@mantle-rwa/sdk 0.1.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.
Files changed (79) hide show
  1. package/dist/cjs/client.js +198 -0
  2. package/dist/cjs/client.js.map +1 -0
  3. package/dist/cjs/constants/index.js +211 -0
  4. package/dist/cjs/constants/index.js.map +1 -0
  5. package/dist/cjs/errors/index.js +218 -0
  6. package/dist/cjs/errors/index.js.map +1 -0
  7. package/dist/cjs/index.js +51 -0
  8. package/dist/cjs/index.js.map +1 -0
  9. package/dist/cjs/modules/compliance.js +202 -0
  10. package/dist/cjs/modules/compliance.js.map +1 -0
  11. package/dist/cjs/modules/index.js +18 -0
  12. package/dist/cjs/modules/index.js.map +1 -0
  13. package/dist/cjs/modules/kyc.js +278 -0
  14. package/dist/cjs/modules/kyc.js.map +1 -0
  15. package/dist/cjs/modules/token.js +365 -0
  16. package/dist/cjs/modules/token.js.map +1 -0
  17. package/dist/cjs/modules/yield.js +406 -0
  18. package/dist/cjs/modules/yield.js.map +1 -0
  19. package/dist/cjs/package.json +3 -0
  20. package/dist/cjs/types/index.js +20 -0
  21. package/dist/cjs/types/index.js.map +1 -0
  22. package/dist/cjs/utils/index.js +206 -0
  23. package/dist/cjs/utils/index.js.map +1 -0
  24. package/dist/esm/client.js +194 -0
  25. package/dist/esm/client.js.map +1 -0
  26. package/dist/esm/constants/index.js +208 -0
  27. package/dist/esm/constants/index.js.map +1 -0
  28. package/dist/esm/errors/index.js +209 -0
  29. package/dist/esm/errors/index.js.map +1 -0
  30. package/dist/esm/index.js +17 -0
  31. package/dist/esm/index.js.map +1 -0
  32. package/dist/esm/modules/compliance.js +198 -0
  33. package/dist/esm/modules/compliance.js.map +1 -0
  34. package/dist/esm/modules/index.js +8 -0
  35. package/dist/esm/modules/index.js.map +1 -0
  36. package/dist/esm/modules/kyc.js +273 -0
  37. package/dist/esm/modules/kyc.js.map +1 -0
  38. package/dist/esm/modules/token.js +360 -0
  39. package/dist/esm/modules/token.js.map +1 -0
  40. package/dist/esm/modules/yield.js +401 -0
  41. package/dist/esm/modules/yield.js.map +1 -0
  42. package/dist/esm/types/index.js +17 -0
  43. package/dist/esm/types/index.js.map +1 -0
  44. package/dist/esm/utils/index.js +188 -0
  45. package/dist/esm/utils/index.js.map +1 -0
  46. package/dist/types/client.d.ts +93 -0
  47. package/dist/types/client.d.ts.map +1 -0
  48. package/dist/types/constants/index.d.ts +83 -0
  49. package/dist/types/constants/index.d.ts.map +1 -0
  50. package/dist/types/errors/index.d.ts +83 -0
  51. package/dist/types/errors/index.d.ts.map +1 -0
  52. package/dist/types/index.d.ts +13 -0
  53. package/dist/types/index.d.ts.map +1 -0
  54. package/dist/types/modules/compliance.d.ts +29 -0
  55. package/dist/types/modules/compliance.d.ts.map +1 -0
  56. package/dist/types/modules/index.d.ts +8 -0
  57. package/dist/types/modules/index.d.ts.map +1 -0
  58. package/dist/types/modules/kyc.d.ts +131 -0
  59. package/dist/types/modules/kyc.d.ts.map +1 -0
  60. package/dist/types/modules/token.d.ts +145 -0
  61. package/dist/types/modules/token.d.ts.map +1 -0
  62. package/dist/types/modules/yield.d.ts +143 -0
  63. package/dist/types/modules/yield.d.ts.map +1 -0
  64. package/dist/types/types/index.d.ts +254 -0
  65. package/dist/types/types/index.d.ts.map +1 -0
  66. package/dist/types/utils/index.d.ts +80 -0
  67. package/dist/types/utils/index.d.ts.map +1 -0
  68. package/package.json +52 -0
  69. package/src/client.ts +258 -0
  70. package/src/constants/index.ts +226 -0
  71. package/src/errors/index.ts +291 -0
  72. package/src/index.ts +42 -0
  73. package/src/modules/compliance.ts +252 -0
  74. package/src/modules/index.ts +8 -0
  75. package/src/modules/kyc.ts +446 -0
  76. package/src/modules/token.ts +488 -0
  77. package/src/modules/yield.ts +566 -0
  78. package/src/types/index.ts +326 -0
  79. package/src/utils/index.ts +240 -0
@@ -0,0 +1,446 @@
1
+ /**
2
+ * KYCModule - Handles KYC registry interactions and provider integrations
3
+ */
4
+
5
+ import { ethers, type Provider, type Signer } from 'ethers';
6
+ import type {
7
+ InvestorData,
8
+ VerificationSession,
9
+ VerificationResult,
10
+ TransactionResult,
11
+ TransactionOptions,
12
+ AccreditationTier,
13
+ } from '../types';
14
+ import { KYC_REGISTRY_ABI } from '../constants';
15
+ import { RWAError, ErrorCode, parseContractError } from '../errors';
16
+ import {
17
+ isValidAddress,
18
+ normalizeAddress,
19
+ parseEvents,
20
+ createTransactionResult,
21
+ retry,
22
+ estimateGasWithBuffer,
23
+ timestampToDate,
24
+ dateToTimestamp,
25
+ hashIdentityData,
26
+ } from '../utils';
27
+
28
+
29
+ /**
30
+ * Interface for KYC providers
31
+ */
32
+ export interface KYCProvider {
33
+ /** Provider name */
34
+ name: string;
35
+ /** Initiate verification for an investor */
36
+ initiateVerification(investorAddress: string, options?: unknown): Promise<VerificationSession>;
37
+ /** Check verification status */
38
+ checkStatus(sessionId: string): Promise<'pending' | 'in_progress' | 'completed' | 'failed'>;
39
+ /** Get verification result */
40
+ getVerificationResult(sessionId: string): Promise<VerificationResult>;
41
+ }
42
+
43
+ /**
44
+ * Instance of a connected KYC registry
45
+ */
46
+ export class KYCRegistryInstance {
47
+ private readonly _contract: ethers.Contract;
48
+ private readonly _retries: number;
49
+ private readonly _retryDelay: number;
50
+
51
+ /** Registry contract address */
52
+ readonly address: string;
53
+
54
+ constructor(
55
+ address: string,
56
+ provider: Provider,
57
+ signer: Signer | null,
58
+ retries: number,
59
+ retryDelay: number
60
+ ) {
61
+ this.address = normalizeAddress(address);
62
+ this._retries = retries;
63
+ this._retryDelay = retryDelay;
64
+
65
+ this._contract = new ethers.Contract(
66
+ this.address,
67
+ KYC_REGISTRY_ABI,
68
+ signer || provider
69
+ );
70
+ }
71
+
72
+ /*//////////////////////////////////////////////////////////////
73
+ READ FUNCTIONS
74
+ //////////////////////////////////////////////////////////////*/
75
+
76
+ /**
77
+ * Check if an investor is verified
78
+ */
79
+ async isVerified(investor: string): Promise<boolean> {
80
+ return this._contract.isVerified(normalizeAddress(investor));
81
+ }
82
+
83
+ /**
84
+ * Check if an investor is accredited
85
+ */
86
+ async isAccredited(investor: string): Promise<boolean> {
87
+ return this._contract.isAccredited(normalizeAddress(investor));
88
+ }
89
+
90
+ /**
91
+ * Get investor information
92
+ */
93
+ async getInvestorInfo(investor: string): Promise<InvestorData> {
94
+ const [verified, tier, expiry, identityHash] = await this._contract.getInvestorInfo(
95
+ normalizeAddress(investor)
96
+ );
97
+
98
+ return {
99
+ verified,
100
+ tier: tier as AccreditationTier,
101
+ expiry: timestampToDate(expiry),
102
+ identityHash,
103
+ };
104
+ }
105
+
106
+ /*//////////////////////////////////////////////////////////////
107
+ WRITE FUNCTIONS
108
+ //////////////////////////////////////////////////////////////*/
109
+
110
+ /**
111
+ * Add an investor to the registry
112
+ */
113
+ async addInvestor(
114
+ investor: string,
115
+ tier: AccreditationTier,
116
+ expiryDate: Date,
117
+ identityHash: string,
118
+ options?: TransactionOptions
119
+ ): Promise<TransactionResult> {
120
+ const investorAddress = normalizeAddress(investor);
121
+ const expiryTimestamp = dateToTimestamp(expiryDate);
122
+
123
+ try {
124
+ const tx = await retry(
125
+ async () => {
126
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
127
+ () => this._contract.addInvestor.estimateGas(
128
+ investorAddress,
129
+ tier,
130
+ expiryTimestamp,
131
+ identityHash
132
+ )
133
+ );
134
+ return this._contract.addInvestor(
135
+ investorAddress,
136
+ tier,
137
+ expiryTimestamp,
138
+ identityHash,
139
+ { gasLimit }
140
+ );
141
+ },
142
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
143
+ );
144
+
145
+ const receipt = await tx.wait();
146
+ const events = parseEvents(receipt, this._contract.interface);
147
+ return createTransactionResult(receipt, events);
148
+ } catch (error) {
149
+ throw parseContractError(error, this.address, 'addInvestor');
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Update an investor's information
155
+ */
156
+ async updateInvestor(
157
+ investor: string,
158
+ tier: AccreditationTier,
159
+ expiryDate: Date,
160
+ options?: TransactionOptions
161
+ ): Promise<TransactionResult> {
162
+ const investorAddress = normalizeAddress(investor);
163
+ const expiryTimestamp = dateToTimestamp(expiryDate);
164
+
165
+ try {
166
+ const tx = await retry(
167
+ async () => {
168
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
169
+ () => this._contract.updateInvestor.estimateGas(
170
+ investorAddress,
171
+ tier,
172
+ expiryTimestamp
173
+ )
174
+ );
175
+ return this._contract.updateInvestor(
176
+ investorAddress,
177
+ tier,
178
+ expiryTimestamp,
179
+ { gasLimit }
180
+ );
181
+ },
182
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
183
+ );
184
+
185
+ const receipt = await tx.wait();
186
+ const events = parseEvents(receipt, this._contract.interface);
187
+ return createTransactionResult(receipt, events);
188
+ } catch (error) {
189
+ throw parseContractError(error, this.address, 'updateInvestor');
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Remove an investor from the registry
195
+ */
196
+ async removeInvestor(investor: string, options?: TransactionOptions): Promise<TransactionResult> {
197
+ const investorAddress = normalizeAddress(investor);
198
+
199
+ try {
200
+ const tx = await retry(
201
+ async () => {
202
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
203
+ () => this._contract.removeInvestor.estimateGas(investorAddress)
204
+ );
205
+ return this._contract.removeInvestor(investorAddress, { gasLimit });
206
+ },
207
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
208
+ );
209
+
210
+ const receipt = await tx.wait();
211
+ const events = parseEvents(receipt, this._contract.interface);
212
+ return createTransactionResult(receipt, events);
213
+ } catch (error) {
214
+ throw parseContractError(error, this.address, 'removeInvestor');
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Batch add investors
220
+ */
221
+ async batchAddInvestors(
222
+ investors: Array<{
223
+ address: string;
224
+ tier: AccreditationTier;
225
+ expiryDate: Date;
226
+ identityHash: string;
227
+ }>,
228
+ options?: TransactionOptions
229
+ ): Promise<TransactionResult> {
230
+ const addresses = investors.map((i) => normalizeAddress(i.address));
231
+ const tiers = investors.map((i) => i.tier);
232
+ const expiries = investors.map((i) => dateToTimestamp(i.expiryDate));
233
+ const hashes = investors.map((i) => i.identityHash);
234
+
235
+ try {
236
+ const tx = await retry(
237
+ async () => {
238
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
239
+ () => this._contract.batchAddInvestors.estimateGas(addresses, tiers, expiries, hashes)
240
+ );
241
+ return this._contract.batchAddInvestors(addresses, tiers, expiries, hashes, { gasLimit });
242
+ },
243
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
244
+ );
245
+
246
+ const receipt = await tx.wait();
247
+ const events = parseEvents(receipt, this._contract.interface);
248
+ return createTransactionResult(receipt, events);
249
+ } catch (error) {
250
+ throw parseContractError(error, this.address, 'batchAddInvestors');
251
+ }
252
+ }
253
+
254
+ /*//////////////////////////////////////////////////////////////
255
+ EVENT LISTENERS
256
+ //////////////////////////////////////////////////////////////*/
257
+
258
+ /**
259
+ * Listen for InvestorVerified events
260
+ */
261
+ onInvestorVerified(
262
+ callback: (investor: string, tier: AccreditationTier, expiry: bigint) => void
263
+ ): () => void {
264
+ const listener = (investor: string, tier: number, expiry: bigint) => {
265
+ callback(investor, tier as AccreditationTier, expiry);
266
+ };
267
+ this._contract.on('InvestorVerified', listener);
268
+ return () => this._contract.off('InvestorVerified', listener);
269
+ }
270
+
271
+ /**
272
+ * Listen for InvestorRemoved events
273
+ */
274
+ onInvestorRemoved(callback: (investor: string) => void): () => void {
275
+ const listener = (investor: string) => callback(investor);
276
+ this._contract.on('InvestorRemoved', listener);
277
+ return () => this._contract.off('InvestorRemoved', listener);
278
+ }
279
+
280
+ /**
281
+ * Listen for InvestorUpdated events
282
+ */
283
+ onInvestorUpdated(
284
+ callback: (investor: string, newTier: AccreditationTier, newExpiry: bigint) => void
285
+ ): () => void {
286
+ const listener = (investor: string, newTier: number, newExpiry: bigint) => {
287
+ callback(investor, newTier as AccreditationTier, newExpiry);
288
+ };
289
+ this._contract.on('InvestorUpdated', listener);
290
+ return () => this._contract.off('InvestorUpdated', listener);
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Module for KYC operations
296
+ */
297
+ export class KYCModule {
298
+ private readonly _provider: Provider;
299
+ private readonly _signer: Signer | null;
300
+ private readonly _retries: number;
301
+ private readonly _retryDelay: number;
302
+ private _kycProvider: KYCProvider | null = null;
303
+
304
+ constructor(provider: Provider, signer: Signer | null, retries: number, retryDelay: number) {
305
+ this._provider = provider;
306
+ this._signer = signer;
307
+ this._retries = retries;
308
+ this._retryDelay = retryDelay;
309
+ }
310
+
311
+ /**
312
+ * Connect to an existing KYC registry
313
+ */
314
+ connect(address: string): KYCRegistryInstance {
315
+ if (!isValidAddress(address)) {
316
+ throw new RWAError(ErrorCode.INVALID_ADDRESS, `Invalid registry address: ${address}`);
317
+ }
318
+ return new KYCRegistryInstance(
319
+ address,
320
+ this._provider,
321
+ this._signer,
322
+ this._retries,
323
+ this._retryDelay
324
+ );
325
+ }
326
+
327
+ /**
328
+ * Set the KYC provider
329
+ */
330
+ setProvider(provider: 'persona' | 'synaps' | 'jumio' | KYCProvider): void {
331
+ if (typeof provider === 'string') {
332
+ // Create stub provider for built-in providers
333
+ this._kycProvider = this._createBuiltInProvider(provider);
334
+ } else {
335
+ this._kycProvider = provider;
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Get the current KYC provider
341
+ */
342
+ get provider(): KYCProvider | null {
343
+ return this._kycProvider;
344
+ }
345
+
346
+ /**
347
+ * Initiate verification for an investor
348
+ */
349
+ async verifyInvestor(investorAddress: string, options?: unknown): Promise<VerificationSession> {
350
+ if (!this._kycProvider) {
351
+ throw new RWAError(
352
+ ErrorCode.PROVIDER_NOT_CONFIGURED,
353
+ 'KYC provider not configured. Call setProvider() first.'
354
+ );
355
+ }
356
+ return this._kycProvider.initiateVerification(investorAddress, options);
357
+ }
358
+
359
+ /**
360
+ * Check accreditation status
361
+ */
362
+ async checkAccreditation(
363
+ registryAddress: string,
364
+ investorAddress: string
365
+ ): Promise<AccreditationTier> {
366
+ const registry = this.connect(registryAddress);
367
+ const info = await registry.getInvestorInfo(investorAddress);
368
+ return info.tier;
369
+ }
370
+
371
+ /**
372
+ * Update registry with verification result
373
+ */
374
+ async updateRegistry(
375
+ registryAddress: string,
376
+ investorAddress: string,
377
+ result: VerificationResult
378
+ ): Promise<TransactionResult> {
379
+ const registry = this.connect(registryAddress);
380
+ return registry.addInvestor(
381
+ investorAddress,
382
+ result.tier,
383
+ result.expiryDate,
384
+ result.identityHash
385
+ );
386
+ }
387
+
388
+ /**
389
+ * Batch update registry with multiple verification results
390
+ */
391
+ async batchUpdateRegistry(
392
+ registryAddress: string,
393
+ results: Array<{ address: string; result: VerificationResult }>
394
+ ): Promise<TransactionResult> {
395
+ const registry = this.connect(registryAddress);
396
+ const investors = results.map((r) => ({
397
+ address: r.address,
398
+ tier: r.result.tier,
399
+ expiryDate: r.result.expiryDate,
400
+ identityHash: r.result.identityHash,
401
+ }));
402
+ return registry.batchAddInvestors(investors);
403
+ }
404
+
405
+ /**
406
+ * Generate identity hash from data
407
+ */
408
+ generateIdentityHash(data: {
409
+ firstName?: string;
410
+ lastName?: string;
411
+ dateOfBirth?: string;
412
+ documentNumber?: string;
413
+ country?: string;
414
+ }): string {
415
+ return hashIdentityData(data);
416
+ }
417
+
418
+ /**
419
+ * Create a built-in provider stub
420
+ */
421
+ private _createBuiltInProvider(name: 'persona' | 'synaps' | 'jumio'): KYCProvider {
422
+ return {
423
+ name,
424
+ initiateVerification: async (investorAddress: string) => {
425
+ // Stub implementation - in production, this would call the actual provider API
426
+ return {
427
+ sessionId: `${name}-${Date.now()}-${investorAddress.slice(2, 10)}`,
428
+ provider: name,
429
+ status: 'pending' as const,
430
+ redirectUrl: `https://${name}.com/verify?session=${Date.now()}`,
431
+ };
432
+ },
433
+ checkStatus: async () => {
434
+ // Stub implementation
435
+ return 'pending' as const;
436
+ },
437
+ getVerificationResult: async () => {
438
+ // Stub implementation
439
+ throw new RWAError(
440
+ ErrorCode.PROVIDER_NOT_CONFIGURED,
441
+ `${name} provider integration requires API credentials. Configure the provider with your API keys.`
442
+ );
443
+ },
444
+ };
445
+ }
446
+ }