Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/account-alias-storage-plugin.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
'@openzeppelin/ui-storage': minor
---

Add Account Alias Storage plugin for mapping blockchain addresses to human-readable names

- New `AliasStorage` class with full CRUD operations for address aliases
- Support for multi-network aliases (same address can have different aliases per network)
- Configurable duplicate handling modes: 'strict', 'warn', or 'allow'
- React hook integration via `createUseAliasStorage` with live reactive updates
- Import/export functionality for backup and migration
- Comprehensive error handling with typed error codes
- Full TypeScript support with JSDoc documentation
145 changes: 145 additions & 0 deletions packages/storage/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ This package provides a generic storage infrastructure built on top of IndexedDB
- **Bulk Operations**: Efficient bulk add/put/delete
- **Index Queries**: Query by indexed fields
- **Quota Handling**: Cross-browser quota exceeded error detection
- **Account Alias Plugin**: Map blockchain addresses to human-readable names

## Quick Start

Expand Down Expand Up @@ -235,6 +236,150 @@ await settingsStorage.set('language', 'en');
const theme = await settingsStorage.get<string>('theme'); // 'dark'
```

## Account Alias Plugin

The Account Alias plugin provides address-to-alias mapping for blockchain addresses. It enables developers to map addresses to human-readable names with configurable duplicate handling, optional metadata, and React hook integration.

### Basic Usage

```typescript
import { ALIAS_SCHEMA, createAliasStorage, createDexieDatabase } from '@openzeppelin/ui-storage';

// Create database with alias schema
const db = createDexieDatabase('MyApp', [{ version: 1, stores: ALIAS_SCHEMA }]);

// Create alias storage
const aliasStorage = createAliasStorage(db);

// Save an alias
await aliasStorage.save({ address: '0x742d35Cc...', alias: 'Treasury' });

// Lookup by address
const record = await aliasStorage.getByAddress('0x742d35Cc...');
console.log(record?.alias); // 'Treasury'

// Resolve alias to address
const address = await aliasStorage.resolveAlias('Treasury');
console.log(address); // '0x742d35Cc...'
```

### Multi-Network Support

The plugin supports network-specific aliases where the same address can have different aliases on different networks:

```typescript
// Global alias (no networkId)
await aliasStorage.save({ address: '0x123...', alias: 'Treasury' });

// Network-specific aliases
await aliasStorage.save({
address: '0x123...',
networkId: 'ethereum-mainnet',
alias: 'ETH Treasury',
});
await aliasStorage.save({
address: '0x123...',
networkId: 'polygon-mainnet',
alias: 'Polygon Treasury',
});

// Lookup by address and network
const ethRecord = await aliasStorage.getByAddressAndNetwork('0x123...', 'ethereum-mainnet');
console.log(ethRecord?.alias); // 'ETH Treasury'

// Get all aliases for an address
const allAliases = await aliasStorage.findByAddress('0x123...');
console.log(allAliases.length); // 3
```

### Configuration Options

```typescript
const aliasStorage = createAliasStorage(db, {
duplicateMode: 'strict', // 'strict' | 'warn' | 'allow'
maxAliasLength: 64, // Max alias length (undefined to disable)
enableLogging: true, // Enable/disable logging
logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error'
onDuplicate: (alias, existingAddr) => {
console.warn(`Duplicate alias: ${alias}`);
},
});
```

### React Hook Integration

```tsx
import { createUseAliasStorage } from '@openzeppelin/ui-storage';

const useAliasStorage = createUseAliasStorage(db, {
onError: (title, error) => toast.error(title),
});

function AddressBook() {
const { records, isLoading, save, remove } = useAliasStorage();

if (isLoading) return <div>Loading...</div>;

return (
<ul>
{records?.map((r) => (
<li key={r.id}>
{r.alias}: {r.address}
</li>
))}
</ul>
);
}
```

### Import/Export

```typescript
// Export all aliases to JSON
const json = await aliasStorage.exportJson();

// Import aliases from JSON
const result = await aliasStorage.importJson(json);
console.log(`Imported: ${result.imported}, Skipped: ${result.skipped}`);
```

### Error Handling

```typescript
import { AliasStorageError } from '@openzeppelin/ui-storage';

try {
await aliasStorage.save({ address: '0x...', alias: 'Treasury' });
} catch (error) {
if (error instanceof AliasStorageError) {
switch (error.code) {
case 'DUPLICATE_ALIAS':
console.error('Alias already in use');
break;
case 'ALIAS_TOO_LONG':
console.error('Alias exceeds max length');
break;
case 'STORAGE_QUOTA_EXCEEDED':
console.error('Storage quota exceeded');
break;
}
}
}
```

### Adding to Existing Projects

Add the alias schema in a new database version:

```typescript
import { ALIAS_SCHEMA, createDexieDatabase } from '@openzeppelin/ui-storage';

const db = createDexieDatabase('MyApp', [
{ version: 1, stores: { users: '++id, email' } },
{ version: 2, stores: { users: '++id, email', ...ALIAS_SCHEMA } },
]);
```

## Development

```bash
Expand Down
3 changes: 3 additions & 0 deletions packages/storage/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,10 @@
"react": "^19.0.0"
},
"devDependencies": {
"@testing-library/react": "^16.3.1",
"@types/react": "^19.2.8",
"fake-indexeddb": "^6.0.1",
"jsdom": "^26.1.0",
"typescript": "^5.8.3",
"vitest": "^3.2.4"
}
Expand Down
38 changes: 38 additions & 0 deletions packages/storage/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,41 @@ export {
createJsonFileIO,
createRepositoryHook,
} from './react';

// Account Alias Storage Plugin
export {
// Types
type AliasRecord,
type AliasInput,
type AliasUpdate,
type DuplicateMode,
type LogLevel,
type AliasStorageOptions,
type ImportResult,
type AliasExport,
type AliasStorageErrorCode,
type UseAliasStorageOptions,
type UseAliasStorageReturn,
// Constants
DEFAULT_OPTIONS,
ALIAS_SCHEMA,
ERROR_MESSAGES,
// Classes
AliasStorage,
AliasStorageError,
// Factory functions
createAliasStorage,
createAliasSchema,
getAliasSchema,
createUseAliasStorage,
getAliasStorageInstance,
// Error utilities
isAliasStorageError,
duplicateAliasError,
aliasTooLongError,
invalidAliasError,
invalidAddressError,
aliasNotFoundError,
invalidImportFormatError,
storageQuotaExceededError,
} from './plugins/account-alias';
Loading
Loading