@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,488 @@
1
+ /**
2
+ * TokenModule - Handles RWA token deployment and interactions
3
+ */
4
+
5
+ import { ethers, type Provider, type Signer } from 'ethers';
6
+ import type {
7
+ TokenDeployConfig,
8
+ TokenInfo,
9
+ TransactionResult,
10
+ TransactionOptions,
11
+ } from '../types';
12
+ import { RWA_TOKEN_ABI } from '../constants';
13
+ import { RWAError, ErrorCode, parseContractError } from '../errors';
14
+ import {
15
+ isValidAddress,
16
+ normalizeAddress,
17
+ parseAmount,
18
+ parseEvents,
19
+ createTransactionResult,
20
+ retry,
21
+ estimateGasWithBuffer,
22
+ } from '../utils';
23
+
24
+ /**
25
+ * Instance of a connected RWA token
26
+ */
27
+ export class TokenInstance {
28
+ private readonly _contract: ethers.Contract;
29
+ private readonly _retries: number;
30
+ private readonly _retryDelay: number;
31
+
32
+ /** Token contract address */
33
+ readonly address: string;
34
+
35
+ constructor(
36
+ address: string,
37
+ provider: Provider,
38
+ signer: Signer | null,
39
+ retries: number,
40
+ retryDelay: number
41
+ ) {
42
+ this.address = normalizeAddress(address);
43
+ this._retries = retries;
44
+ this._retryDelay = retryDelay;
45
+
46
+ this._contract = new ethers.Contract(
47
+ this.address,
48
+ RWA_TOKEN_ABI,
49
+ signer || provider
50
+ );
51
+ }
52
+
53
+ /*//////////////////////////////////////////////////////////////
54
+ READ FUNCTIONS
55
+ //////////////////////////////////////////////////////////////*/
56
+
57
+ /**
58
+ * Get token name
59
+ */
60
+ async name(): Promise<string> {
61
+ return this._contract.name();
62
+ }
63
+
64
+ /**
65
+ * Get token symbol
66
+ */
67
+ async symbol(): Promise<string> {
68
+ return this._contract.symbol();
69
+ }
70
+
71
+ /**
72
+ * Get token decimals
73
+ */
74
+ async decimals(): Promise<number> {
75
+ return this._contract.decimals();
76
+ }
77
+
78
+ /**
79
+ * Get total supply
80
+ */
81
+ async totalSupply(): Promise<bigint> {
82
+ return this._contract.totalSupply();
83
+ }
84
+
85
+ /**
86
+ * Get balance of an account
87
+ */
88
+ async balanceOf(account: string): Promise<bigint> {
89
+ return this._contract.balanceOf(normalizeAddress(account));
90
+ }
91
+
92
+ /**
93
+ * Check if token is paused
94
+ */
95
+ async paused(): Promise<boolean> {
96
+ return this._contract.paused();
97
+ }
98
+
99
+ /**
100
+ * Get the KYC registry address
101
+ */
102
+ async kycRegistry(): Promise<string> {
103
+ return this._contract.kycRegistry();
104
+ }
105
+
106
+ /**
107
+ * Get all compliance modules
108
+ */
109
+ async getComplianceModules(): Promise<string[]> {
110
+ return this._contract.getComplianceModules();
111
+ }
112
+
113
+ /**
114
+ * Check if an address is a compliance module
115
+ */
116
+ async isComplianceModule(module: string): Promise<boolean> {
117
+ return this._contract.isComplianceModule(normalizeAddress(module));
118
+ }
119
+
120
+ /**
121
+ * Check if a transfer is allowed
122
+ */
123
+ async isTransferAllowed(
124
+ from: string,
125
+ to: string,
126
+ amount: string
127
+ ): Promise<{ allowed: boolean; reason: string }> {
128
+ const [allowed, reason] = await this._contract.isTransferAllowed(
129
+ normalizeAddress(from),
130
+ normalizeAddress(to),
131
+ parseAmount(amount)
132
+ );
133
+ return { allowed, reason };
134
+ }
135
+
136
+ /**
137
+ * Get token info
138
+ */
139
+ async getInfo(): Promise<TokenInfo> {
140
+ const [name, symbol, decimals, totalSupply, paused] = await Promise.all([
141
+ this.name(),
142
+ this.symbol(),
143
+ this.decimals(),
144
+ this.totalSupply(),
145
+ this.paused(),
146
+ ]);
147
+
148
+ return {
149
+ address: this.address,
150
+ name,
151
+ symbol,
152
+ decimals,
153
+ totalSupply,
154
+ paused,
155
+ };
156
+ }
157
+
158
+ /**
159
+ * Get balance at a specific snapshot
160
+ */
161
+ async balanceOfAt(account: string, snapshotId: bigint): Promise<bigint> {
162
+ return this._contract.balanceOfAt(normalizeAddress(account), snapshotId);
163
+ }
164
+
165
+ /**
166
+ * Get total supply at a specific snapshot
167
+ */
168
+ async totalSupplyAt(snapshotId: bigint): Promise<bigint> {
169
+ return this._contract.totalSupplyAt(snapshotId);
170
+ }
171
+
172
+ /*//////////////////////////////////////////////////////////////
173
+ WRITE FUNCTIONS
174
+ //////////////////////////////////////////////////////////////*/
175
+
176
+ /**
177
+ * Mint tokens to an address
178
+ */
179
+ async mint(to: string, amount: string, options?: TransactionOptions): Promise<TransactionResult> {
180
+ const toAddress = normalizeAddress(to);
181
+ const amountWei = parseAmount(amount);
182
+
183
+ try {
184
+ const tx = await retry(
185
+ async () => {
186
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
187
+ () => this._contract.mint.estimateGas(toAddress, amountWei)
188
+ );
189
+ return this._contract.mint(toAddress, amountWei, { gasLimit });
190
+ },
191
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
192
+ );
193
+
194
+ const receipt = await tx.wait();
195
+ const events = parseEvents(receipt, this._contract.interface);
196
+ return createTransactionResult(receipt, events);
197
+ } catch (error) {
198
+ throw parseContractError(error, this.address, 'mint');
199
+ }
200
+ }
201
+
202
+ /**
203
+ * Burn tokens from an address
204
+ */
205
+ async burn(from: string, amount: string, options?: TransactionOptions): Promise<TransactionResult> {
206
+ const fromAddress = normalizeAddress(from);
207
+ const amountWei = parseAmount(amount);
208
+
209
+ try {
210
+ const tx = await retry(
211
+ async () => {
212
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
213
+ () => this._contract.burn.estimateGas(fromAddress, amountWei)
214
+ );
215
+ return this._contract.burn(fromAddress, amountWei, { gasLimit });
216
+ },
217
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
218
+ );
219
+
220
+ const receipt = await tx.wait();
221
+ const events = parseEvents(receipt, this._contract.interface);
222
+ return createTransactionResult(receipt, events);
223
+ } catch (error) {
224
+ throw parseContractError(error, this.address, 'burn');
225
+ }
226
+ }
227
+
228
+ /**
229
+ * Transfer tokens
230
+ */
231
+ async transfer(to: string, amount: string, options?: TransactionOptions): Promise<TransactionResult> {
232
+ const toAddress = normalizeAddress(to);
233
+ const amountWei = parseAmount(amount);
234
+
235
+ try {
236
+ const tx = await retry(
237
+ async () => {
238
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
239
+ () => this._contract.transfer.estimateGas(toAddress, amountWei)
240
+ );
241
+ return this._contract.transfer(toAddress, amountWei, { 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, 'transfer');
251
+ }
252
+ }
253
+
254
+ /**
255
+ * Pause token transfers
256
+ */
257
+ async pause(options?: TransactionOptions): Promise<TransactionResult> {
258
+ try {
259
+ const tx = await retry(
260
+ async () => {
261
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
262
+ () => this._contract.pause.estimateGas()
263
+ );
264
+ return this._contract.pause({ gasLimit });
265
+ },
266
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
267
+ );
268
+
269
+ const receipt = await tx.wait();
270
+ const events = parseEvents(receipt, this._contract.interface);
271
+ return createTransactionResult(receipt, events);
272
+ } catch (error) {
273
+ throw parseContractError(error, this.address, 'pause');
274
+ }
275
+ }
276
+
277
+ /**
278
+ * Unpause token transfers
279
+ */
280
+ async unpause(options?: TransactionOptions): Promise<TransactionResult> {
281
+ try {
282
+ const tx = await retry(
283
+ async () => {
284
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
285
+ () => this._contract.unpause.estimateGas()
286
+ );
287
+ return this._contract.unpause({ gasLimit });
288
+ },
289
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
290
+ );
291
+
292
+ const receipt = await tx.wait();
293
+ const events = parseEvents(receipt, this._contract.interface);
294
+ return createTransactionResult(receipt, events);
295
+ } catch (error) {
296
+ throw parseContractError(error, this.address, 'unpause');
297
+ }
298
+ }
299
+
300
+ /**
301
+ * Create a snapshot
302
+ */
303
+ async snapshot(options?: TransactionOptions): Promise<{ result: TransactionResult; snapshotId: bigint }> {
304
+ try {
305
+ const tx = await retry(
306
+ async () => {
307
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
308
+ () => this._contract.snapshot.estimateGas()
309
+ );
310
+ return this._contract.snapshot({ gasLimit });
311
+ },
312
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
313
+ );
314
+
315
+ const receipt = await tx.wait();
316
+ const events = parseEvents(receipt, this._contract.interface);
317
+ const result = createTransactionResult(receipt, events);
318
+
319
+ // Extract snapshot ID from event
320
+ const snapshotEvent = events.find((e) => e.name === 'Snapshot');
321
+ const snapshotId = snapshotEvent?.args.id as bigint || 0n;
322
+
323
+ return { result, snapshotId };
324
+ } catch (error) {
325
+ throw parseContractError(error, this.address, 'snapshot');
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Set the KYC registry
331
+ */
332
+ async setKYCRegistry(registry: string, options?: TransactionOptions): Promise<TransactionResult> {
333
+ const registryAddress = normalizeAddress(registry);
334
+
335
+ try {
336
+ const tx = await retry(
337
+ async () => {
338
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
339
+ () => this._contract.setKYCRegistry.estimateGas(registryAddress)
340
+ );
341
+ return this._contract.setKYCRegistry(registryAddress, { gasLimit });
342
+ },
343
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
344
+ );
345
+
346
+ const receipt = await tx.wait();
347
+ const events = parseEvents(receipt, this._contract.interface);
348
+ return createTransactionResult(receipt, events);
349
+ } catch (error) {
350
+ throw parseContractError(error, this.address, 'setKYCRegistry');
351
+ }
352
+ }
353
+
354
+ /**
355
+ * Add a compliance module
356
+ */
357
+ async addComplianceModule(module: string, options?: TransactionOptions): Promise<TransactionResult> {
358
+ const moduleAddress = normalizeAddress(module);
359
+
360
+ try {
361
+ const tx = await retry(
362
+ async () => {
363
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
364
+ () => this._contract.addComplianceModule.estimateGas(moduleAddress)
365
+ );
366
+ return this._contract.addComplianceModule(moduleAddress, { gasLimit });
367
+ },
368
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
369
+ );
370
+
371
+ const receipt = await tx.wait();
372
+ const events = parseEvents(receipt, this._contract.interface);
373
+ return createTransactionResult(receipt, events);
374
+ } catch (error) {
375
+ throw parseContractError(error, this.address, 'addComplianceModule');
376
+ }
377
+ }
378
+
379
+ /**
380
+ * Remove a compliance module
381
+ */
382
+ async removeComplianceModule(module: string, options?: TransactionOptions): Promise<TransactionResult> {
383
+ const moduleAddress = normalizeAddress(module);
384
+
385
+ try {
386
+ const tx = await retry(
387
+ async () => {
388
+ const gasLimit = options?.gasLimit || await estimateGasWithBuffer(
389
+ () => this._contract.removeComplianceModule.estimateGas(moduleAddress)
390
+ );
391
+ return this._contract.removeComplianceModule(moduleAddress, { gasLimit });
392
+ },
393
+ { retries: options?.retries ?? this._retries, delay: options?.retryDelay ?? this._retryDelay }
394
+ );
395
+
396
+ const receipt = await tx.wait();
397
+ const events = parseEvents(receipt, this._contract.interface);
398
+ return createTransactionResult(receipt, events);
399
+ } catch (error) {
400
+ throw parseContractError(error, this.address, 'removeComplianceModule');
401
+ }
402
+ }
403
+
404
+ /*//////////////////////////////////////////////////////////////
405
+ EVENT LISTENERS
406
+ //////////////////////////////////////////////////////////////*/
407
+
408
+ /**
409
+ * Listen for Transfer events
410
+ */
411
+ onTransfer(callback: (from: string, to: string, amount: bigint) => void): () => void {
412
+ const listener = (from: string, to: string, amount: bigint) => {
413
+ callback(from, to, amount);
414
+ };
415
+ this._contract.on('Transfer', listener);
416
+ return () => this._contract.off('Transfer', listener);
417
+ }
418
+
419
+ /**
420
+ * Listen for TransferRestricted events
421
+ */
422
+ onTransferRestricted(
423
+ callback: (from: string, to: string, amount: bigint, reason: string) => void
424
+ ): () => void {
425
+ const listener = (from: string, to: string, amount: bigint, reason: string) => {
426
+ callback(from, to, amount, reason);
427
+ };
428
+ this._contract.on('TransferRestricted', listener);
429
+ return () => this._contract.off('TransferRestricted', listener);
430
+ }
431
+
432
+ /**
433
+ * Listen for Paused events
434
+ */
435
+ onPaused(callback: (by: string) => void): () => void {
436
+ const listener = (by: string) => callback(by);
437
+ this._contract.on('TokensPaused', listener);
438
+ return () => this._contract.off('TokensPaused', listener);
439
+ }
440
+
441
+ /**
442
+ * Listen for Unpaused events
443
+ */
444
+ onUnpaused(callback: (by: string) => void): () => void {
445
+ const listener = (by: string) => callback(by);
446
+ this._contract.on('TokensUnpaused', listener);
447
+ return () => this._contract.off('TokensUnpaused', listener);
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Module for token operations
453
+ */
454
+ export class TokenModule {
455
+ private readonly _provider: Provider;
456
+ private readonly _signer: Signer | null;
457
+ private readonly _retries: number;
458
+ private readonly _retryDelay: number;
459
+
460
+ constructor(provider: Provider, signer: Signer | null, retries: number, retryDelay: number) {
461
+ this._provider = provider;
462
+ this._signer = signer;
463
+ this._retries = retries;
464
+ this._retryDelay = retryDelay;
465
+ }
466
+
467
+ /**
468
+ * Connect to an existing token contract
469
+ */
470
+ connect(address: string): TokenInstance {
471
+ if (!isValidAddress(address)) {
472
+ throw new RWAError(ErrorCode.INVALID_ADDRESS, `Invalid token address: ${address}`);
473
+ }
474
+ return new TokenInstance(address, this._provider, this._signer, this._retries, this._retryDelay);
475
+ }
476
+
477
+ /**
478
+ * Estimate gas for deploying a token
479
+ */
480
+ async estimateDeployGas(_config: TokenDeployConfig): Promise<bigint> {
481
+ // This would require the token bytecode which we don't have in the SDK
482
+ // In practice, deployment is done through the factory
483
+ throw new RWAError(
484
+ ErrorCode.INVALID_CONFIGURATION,
485
+ 'Direct token deployment is not supported. Use RWAClient.deployRWASystem() instead.'
486
+ );
487
+ }
488
+ }