@yallet/rwa-sdk 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +365 -0
- package/dist/credential-nft-server.d.ts +75 -0
- package/dist/credential-nft-server.d.ts.map +1 -0
- package/dist/credential-nft-server.js +122 -0
- package/dist/credential-nft-server.js.map +1 -0
- package/dist/credential-nft.d.ts +31 -0
- package/dist/credential-nft.d.ts.map +1 -0
- package/dist/credential-nft.js +38 -0
- package/dist/credential-nft.js.map +1 -0
- package/dist/encryption.d.ts +107 -0
- package/dist/encryption.d.ts.map +1 -0
- package/dist/encryption.js +176 -0
- package/dist/encryption.js.map +1 -0
- package/dist/index.d.ts +56 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +66 -0
- package/dist/index.js.map +1 -0
- package/dist/metadata.d.ts +161 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +180 -0
- package/dist/metadata.js.map +1 -0
- package/dist/minting.d.ts +63 -0
- package/dist/minting.d.ts.map +1 -0
- package/dist/minting.js +194 -0
- package/dist/minting.js.map +1 -0
- package/dist/payloads.d.ts +120 -0
- package/dist/payloads.d.ts.map +1 -0
- package/dist/payloads.js +118 -0
- package/dist/payloads.js.map +1 -0
- package/dist/registry.d.ts +77 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +173 -0
- package/dist/registry.js.map +1 -0
- package/dist/sdk.d.ts +158 -0
- package/dist/sdk.d.ts.map +1 -0
- package/dist/sdk.js +346 -0
- package/dist/sdk.js.map +1 -0
- package/dist/storage.d.ts +80 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +284 -0
- package/dist/storage.js.map +1 -0
- package/dist/types.d.ts +278 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/package.json +64 -0
- package/src/credential-nft-server.ts +201 -0
- package/src/credential-nft.ts +64 -0
- package/src/encryption.ts +308 -0
- package/src/index.ts +151 -0
- package/src/metadata.ts +336 -0
- package/src/minting.ts +266 -0
- package/src/payloads.ts +238 -0
- package/src/registry.ts +236 -0
- package/src/sdk.ts +507 -0
- package/src/storage.ts +364 -0
- package/src/types.ts +318 -0
- package/wasm/README.md +33 -0
package/src/registry.ts
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Yallet RWA SDK - User Registry Module
|
|
3
|
+
*
|
|
4
|
+
* Manages registered users with their xidentity public keys
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import type { RegisteredUser, UserRegistry } from './types.js';
|
|
8
|
+
|
|
9
|
+
// ======================================
|
|
10
|
+
// In-Memory Registry (For Development)
|
|
11
|
+
// ======================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Simple in-memory user registry
|
|
15
|
+
* For production, implement a database-backed registry
|
|
16
|
+
*/
|
|
17
|
+
export class InMemoryRegistry implements UserRegistry {
|
|
18
|
+
private users: Map<string, RegisteredUser> = new Map();
|
|
19
|
+
|
|
20
|
+
async getUser(solanaAddress: string): Promise<RegisteredUser | null> {
|
|
21
|
+
return this.users.get(solanaAddress) || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async registerUser(user: RegisteredUser): Promise<void> {
|
|
25
|
+
this.users.set(user.solanaAddress, {
|
|
26
|
+
...user,
|
|
27
|
+
registeredAt: user.registeredAt || Date.now(),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async updateUser(
|
|
32
|
+
solanaAddress: string,
|
|
33
|
+
updates: Partial<RegisteredUser>
|
|
34
|
+
): Promise<void> {
|
|
35
|
+
const existing = this.users.get(solanaAddress);
|
|
36
|
+
if (!existing) {
|
|
37
|
+
throw new Error(`User not found: ${solanaAddress}`);
|
|
38
|
+
}
|
|
39
|
+
this.users.set(solanaAddress, { ...existing, ...updates });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async deleteUser(solanaAddress: string): Promise<void> {
|
|
43
|
+
this.users.delete(solanaAddress);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async listUsers(): Promise<RegisteredUser[]> {
|
|
47
|
+
return Array.from(this.users.values());
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// ======================================
|
|
52
|
+
// HTTP Registry (For Production)
|
|
53
|
+
// ======================================
|
|
54
|
+
|
|
55
|
+
export interface HTTPRegistryConfig {
|
|
56
|
+
/** Base URL of the registry API */
|
|
57
|
+
baseUrl: string;
|
|
58
|
+
/** API key for authentication */
|
|
59
|
+
apiKey?: string;
|
|
60
|
+
/** Custom headers */
|
|
61
|
+
headers?: Record<string, string>;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* HTTP-backed user registry
|
|
66
|
+
* Connects to a backend API for user management
|
|
67
|
+
*/
|
|
68
|
+
export class HTTPRegistry implements UserRegistry {
|
|
69
|
+
private config: HTTPRegistryConfig;
|
|
70
|
+
|
|
71
|
+
constructor(config: HTTPRegistryConfig) {
|
|
72
|
+
this.config = config;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
private async fetch<T>(
|
|
76
|
+
path: string,
|
|
77
|
+
options: RequestInit = {}
|
|
78
|
+
): Promise<T> {
|
|
79
|
+
const url = `${this.config.baseUrl}${path}`;
|
|
80
|
+
|
|
81
|
+
const headers: Record<string, string> = {
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
...this.config.headers,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
if (this.config.apiKey) {
|
|
87
|
+
headers['Authorization'] = `Bearer ${this.config.apiKey}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const response = await fetch(url, {
|
|
91
|
+
...options,
|
|
92
|
+
headers: { ...headers, ...options.headers },
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
if (!response.ok) {
|
|
96
|
+
const error = await response.text();
|
|
97
|
+
throw new Error(`Registry API error: ${response.status} - ${error}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return response.json() as Promise<T>;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async getUser(solanaAddress: string): Promise<RegisteredUser | null> {
|
|
104
|
+
try {
|
|
105
|
+
return await this.fetch<RegisteredUser>(
|
|
106
|
+
`/users/${encodeURIComponent(solanaAddress)}`
|
|
107
|
+
);
|
|
108
|
+
} catch (err) {
|
|
109
|
+
if (err instanceof Error && err.message.includes('404')) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
throw err;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async registerUser(user: RegisteredUser): Promise<void> {
|
|
117
|
+
await this.fetch('/users', {
|
|
118
|
+
method: 'POST',
|
|
119
|
+
body: JSON.stringify(user),
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async updateUser(
|
|
124
|
+
solanaAddress: string,
|
|
125
|
+
updates: Partial<RegisteredUser>
|
|
126
|
+
): Promise<void> {
|
|
127
|
+
await this.fetch(`/users/${encodeURIComponent(solanaAddress)}`, {
|
|
128
|
+
method: 'PATCH',
|
|
129
|
+
body: JSON.stringify(updates),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async deleteUser(solanaAddress: string): Promise<void> {
|
|
134
|
+
await this.fetch(`/users/${encodeURIComponent(solanaAddress)}`, {
|
|
135
|
+
method: 'DELETE',
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async listUsers(): Promise<RegisteredUser[]> {
|
|
140
|
+
return this.fetch<RegisteredUser[]>('/users');
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// ======================================
|
|
145
|
+
// Registry Factory
|
|
146
|
+
// ======================================
|
|
147
|
+
|
|
148
|
+
export type RegistryType = 'memory' | 'http';
|
|
149
|
+
|
|
150
|
+
export interface RegistryOptions {
|
|
151
|
+
type: RegistryType;
|
|
152
|
+
httpConfig?: HTTPRegistryConfig;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create a user registry instance
|
|
157
|
+
*
|
|
158
|
+
* @param options - Registry options
|
|
159
|
+
* @returns UserRegistry instance
|
|
160
|
+
*/
|
|
161
|
+
export function createRegistry(options: RegistryOptions): UserRegistry {
|
|
162
|
+
switch (options.type) {
|
|
163
|
+
case 'memory':
|
|
164
|
+
return new InMemoryRegistry();
|
|
165
|
+
case 'http':
|
|
166
|
+
if (!options.httpConfig) {
|
|
167
|
+
throw new Error('httpConfig is required for HTTP registry');
|
|
168
|
+
}
|
|
169
|
+
return new HTTPRegistry(options.httpConfig);
|
|
170
|
+
default:
|
|
171
|
+
throw new Error(`Unknown registry type: ${options.type}`);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// ======================================
|
|
176
|
+
// Validation Helpers
|
|
177
|
+
// ======================================
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Validate a Solana address
|
|
181
|
+
*
|
|
182
|
+
* @param address - Address to validate
|
|
183
|
+
* @returns true if valid
|
|
184
|
+
*/
|
|
185
|
+
export function isValidSolanaAddress(address: string): boolean {
|
|
186
|
+
// Basic validation: 32-44 characters, base58
|
|
187
|
+
const base58Regex = /^[1-9A-HJ-NP-Za-km-z]{32,44}$/;
|
|
188
|
+
return base58Regex.test(address);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Validate an xidentity public key (base64)
|
|
193
|
+
*
|
|
194
|
+
* @param xidentity - xidentity to validate
|
|
195
|
+
* @returns true if valid
|
|
196
|
+
*/
|
|
197
|
+
export function isValidXidentity(xidentity: string): boolean {
|
|
198
|
+
// Check if it's valid base64
|
|
199
|
+
try {
|
|
200
|
+
const decoded = atob(xidentity);
|
|
201
|
+
// X25519 public key should be 32 bytes
|
|
202
|
+
return decoded.length === 32;
|
|
203
|
+
} catch {
|
|
204
|
+
return false;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Validate a registered user
|
|
210
|
+
*
|
|
211
|
+
* @param user - User to validate
|
|
212
|
+
* @returns Validation result
|
|
213
|
+
*/
|
|
214
|
+
export function validateUser(user: Partial<RegisteredUser>): {
|
|
215
|
+
valid: boolean;
|
|
216
|
+
errors: string[];
|
|
217
|
+
} {
|
|
218
|
+
const errors: string[] = [];
|
|
219
|
+
|
|
220
|
+
if (!user.solanaAddress) {
|
|
221
|
+
errors.push('solanaAddress is required');
|
|
222
|
+
} else if (!isValidSolanaAddress(user.solanaAddress)) {
|
|
223
|
+
errors.push('Invalid Solana address format');
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!user.xidentity) {
|
|
227
|
+
errors.push('xidentity is required');
|
|
228
|
+
} else if (!isValidXidentity(user.xidentity)) {
|
|
229
|
+
errors.push('Invalid xidentity format (must be base64-encoded 32-byte key)');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
valid: errors.length === 0,
|
|
234
|
+
errors,
|
|
235
|
+
};
|
|
236
|
+
}
|