@centrifuge/sdk 1.10.0 → 1.12.1

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 (112) hide show
  1. package/dist/Centrifuge.d.ts +38 -0
  2. package/dist/Centrifuge.d.ts.map +1 -1
  3. package/dist/Centrifuge.js +163 -0
  4. package/dist/Centrifuge.js.map +1 -1
  5. package/dist/Centrifuge.test.js +219 -0
  6. package/dist/Centrifuge.test.js.map +1 -1
  7. package/dist/abi/MultiAdapter.abi.d.ts +1 -1
  8. package/dist/abi/MultiAdapter.abi.js +1 -1
  9. package/dist/abi/MultiAdapter.abi.js.map +1 -1
  10. package/dist/abi/OnchainPM.abi.d.ts +3 -0
  11. package/dist/abi/OnchainPM.abi.d.ts.map +1 -0
  12. package/dist/abi/OnchainPM.abi.js +14 -0
  13. package/dist/abi/OnchainPM.abi.js.map +1 -0
  14. package/dist/abi/OnchainPMFactory.abi.d.ts +3 -0
  15. package/dist/abi/OnchainPMFactory.abi.d.ts.map +1 -0
  16. package/dist/abi/OnchainPMFactory.abi.js +6 -0
  17. package/dist/abi/OnchainPMFactory.abi.js.map +1 -0
  18. package/dist/abi/OracleValuation.abi.d.ts +3 -0
  19. package/dist/abi/OracleValuation.abi.d.ts.map +1 -0
  20. package/dist/abi/OracleValuation.abi.js +9 -0
  21. package/dist/abi/OracleValuation.abi.js.map +1 -0
  22. package/dist/abi/ScriptHelpers.abi.d.ts +3 -0
  23. package/dist/abi/ScriptHelpers.abi.d.ts.map +1 -0
  24. package/dist/abi/ScriptHelpers.abi.js +5 -0
  25. package/dist/abi/ScriptHelpers.abi.js.map +1 -0
  26. package/dist/abi/index.d.ts +281 -0
  27. package/dist/abi/index.d.ts.map +1 -1
  28. package/dist/abi/index.js +8 -0
  29. package/dist/abi/index.js.map +1 -1
  30. package/dist/entities/OnchainPM.d.ts +203 -0
  31. package/dist/entities/OnchainPM.d.ts.map +1 -0
  32. package/dist/entities/OnchainPM.js +315 -0
  33. package/dist/entities/OnchainPM.js.map +1 -0
  34. package/dist/entities/OnchainPM.test.d.ts +2 -0
  35. package/dist/entities/OnchainPM.test.d.ts.map +1 -0
  36. package/dist/entities/OnchainPM.test.js +155 -0
  37. package/dist/entities/OnchainPM.test.js.map +1 -0
  38. package/dist/entities/Pool.d.ts +81 -3
  39. package/dist/entities/Pool.d.ts.map +1 -1
  40. package/dist/entities/Pool.js +207 -18
  41. package/dist/entities/Pool.js.map +1 -1
  42. package/dist/entities/PoolNetwork.d.ts +37 -0
  43. package/dist/entities/PoolNetwork.d.ts.map +1 -1
  44. package/dist/entities/PoolNetwork.js +136 -12
  45. package/dist/entities/PoolNetwork.js.map +1 -1
  46. package/dist/entities/PoolNetwork.test.js +134 -1
  47. package/dist/entities/PoolNetwork.test.js.map +1 -1
  48. package/dist/entities/ShareClass.d.ts.map +1 -1
  49. package/dist/entities/ShareClass.js +151 -126
  50. package/dist/entities/ShareClass.js.map +1 -1
  51. package/dist/entities/Vault.test.js +0 -5
  52. package/dist/entities/Vault.test.js.map +1 -1
  53. package/dist/entities/crosschainMessages.d.ts +127 -0
  54. package/dist/entities/crosschainMessages.d.ts.map +1 -0
  55. package/dist/entities/crosschainMessages.js +204 -0
  56. package/dist/entities/crosschainMessages.js.map +1 -0
  57. package/dist/index.d.ts +16 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +8 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/types/index.d.ts +3 -0
  62. package/dist/types/index.d.ts.map +1 -1
  63. package/dist/types/poolMetadata.d.ts +28 -0
  64. package/dist/types/poolMetadata.d.ts.map +1 -1
  65. package/dist/types/transaction.d.ts +5 -1
  66. package/dist/types/transaction.d.ts.map +1 -1
  67. package/dist/types/transaction.js.map +1 -1
  68. package/dist/types/workflow.d.ts +151 -0
  69. package/dist/types/workflow.d.ts.map +1 -0
  70. package/dist/types/workflow.js +5 -0
  71. package/dist/types/workflow.js.map +1 -0
  72. package/dist/utils/catalog.d.ts +29 -0
  73. package/dist/utils/catalog.d.ts.map +1 -0
  74. package/dist/utils/catalog.js +396 -0
  75. package/dist/utils/catalog.js.map +1 -0
  76. package/dist/utils/catalog.test.d.ts +2 -0
  77. package/dist/utils/catalog.test.d.ts.map +1 -0
  78. package/dist/utils/catalog.test.js +291 -0
  79. package/dist/utils/catalog.test.js.map +1 -0
  80. package/dist/utils/scriptHash.d.ts +32 -0
  81. package/dist/utils/scriptHash.d.ts.map +1 -0
  82. package/dist/utils/scriptHash.js +67 -0
  83. package/dist/utils/scriptHash.js.map +1 -0
  84. package/dist/utils/scriptHash.test.d.ts +2 -0
  85. package/dist/utils/scriptHash.test.d.ts.map +1 -0
  86. package/dist/utils/scriptHash.test.js +97 -0
  87. package/dist/utils/scriptHash.test.js.map +1 -0
  88. package/dist/utils/variables.d.ts +78 -0
  89. package/dist/utils/variables.d.ts.map +1 -0
  90. package/dist/utils/variables.js +105 -0
  91. package/dist/utils/variables.js.map +1 -0
  92. package/dist/utils/variables.test.d.ts +2 -0
  93. package/dist/utils/variables.test.d.ts.map +1 -0
  94. package/dist/utils/variables.test.js +110 -0
  95. package/dist/utils/variables.test.js.map +1 -0
  96. package/dist/utils/weiroll.d.ts +176 -0
  97. package/dist/utils/weiroll.d.ts.map +1 -0
  98. package/dist/utils/weiroll.js +341 -0
  99. package/dist/utils/weiroll.js.map +1 -0
  100. package/dist/utils/weiroll.test.d.ts +2 -0
  101. package/dist/utils/weiroll.test.d.ts.map +1 -0
  102. package/dist/utils/weiroll.test.js +430 -0
  103. package/dist/utils/weiroll.test.js.map +1 -0
  104. package/dist/utils/workflowExecute.d.ts +121 -0
  105. package/dist/utils/workflowExecute.d.ts.map +1 -0
  106. package/dist/utils/workflowExecute.js +577 -0
  107. package/dist/utils/workflowExecute.js.map +1 -0
  108. package/dist/utils/workflowExecute.test.d.ts +2 -0
  109. package/dist/utils/workflowExecute.test.d.ts.map +1 -0
  110. package/dist/utils/workflowExecute.test.js +117 -0
  111. package/dist/utils/workflowExecute.test.js.map +1 -0
  112. package/package.json +1 -1
@@ -141,7 +141,11 @@ export class ShareClass extends Entity {
141
141
  if (res.length === 0) {
142
142
  return of([]);
143
143
  }
144
- const items = res.filter((item) => Number(item.centrifugeId) === centrifugeId || !centrifugeId);
144
+ const items = res.filter(
145
+ // Skip holdings whose asset metadata isn't indexed yet (`asset` is null):
146
+ // they can't be priced/rendered, and reading `asset.address` below would
147
+ // otherwise throw and fail the whole `balances()` stream.
148
+ (item) => (Number(item.centrifugeId) === centrifugeId || !centrifugeId) && item.asset != null);
145
149
  if (items.length === 0)
146
150
  return of([]);
147
151
  return combineLatest([
@@ -391,83 +395,73 @@ export class ShareClass extends Entity {
391
395
  self._root._protocolAddresses(self.pool.centrifugeId),
392
396
  self.pool.metadata(),
393
397
  ]);
394
- let data;
395
- if (isLiability) {
396
- const expenseAccount = accounts[AccountType.Expense] || metadata?.shareClasses?.[self.id.raw]?.defaultAccounts?.expense;
397
- const liabilityAccount = accounts[AccountType.Liability] || metadata?.shareClasses?.[self.id.raw]?.defaultAccounts?.liability;
398
- if (liabilityAccount === undefined) {
399
- throw new Error('Missing required accounts for liability creation');
400
- }
401
- if (expenseAccount) {
402
- data = encodeFunctionData({
403
- abi: ABI.Hub,
404
- functionName: 'initializeLiability',
405
- args: [self.pool.id.raw, self.id.raw, assetId.raw, valuation, expenseAccount, liabilityAccount],
406
- });
407
- }
408
- else {
409
- const newExpenseAccount = await self._getFreeAccountId();
410
- const createAccountData = encodeFunctionData({
411
- abi: ABI.Hub,
412
- functionName: 'createAccount',
413
- args: [self.pool.id.raw, newExpenseAccount, true],
414
- });
415
- const initHoldingData = encodeFunctionData({
416
- abi: ABI.Hub,
417
- functionName: 'initializeLiability',
418
- args: [self.pool.id.raw, self.id.raw, assetId.raw, valuation, newExpenseAccount, liabilityAccount],
419
- });
420
- data = [createAccountData, initHoldingData];
398
+ const provided = (accounts ?? {});
399
+ const defaults = metadata?.shareClasses?.[self.id.raw]?.defaultAccounts;
400
+ const createAccountCalls = [];
401
+ const { accounting } = await self._root._protocolAddresses(self.pool.centrifugeId);
402
+ const client = await firstValueFrom(self._root.getClient(self.pool.centrifugeId));
403
+ const allocatedIds = new Set();
404
+ // Resolve an account id from the explicit input or the share class defaults,
405
+ // otherwise allocate a fresh account with the correct normal balance. By
406
+ // double-entry convention asset/loss/expense are debit-normal; equity/gain/
407
+ // liability are credit-normal. This lets a holding be created on a pool that
408
+ // hasn't pre-created its accounts (instead of reverting "Missing required accounts").
409
+ const resolveAccount = async (explicit, fallback, isDebitNormal) => {
410
+ const existing = explicit ?? fallback;
411
+ if (existing !== undefined)
412
+ return BigInt(existing);
413
+ // Generate a fresh id that doesn't already exist on-chain and isn't already
414
+ // allocated in this batch — `initializeHolding` reverts InvalidAccountCombination
415
+ // if any two of the four accounts collide.
416
+ let id = randomUint(256);
417
+ while (allocatedIds.has(id) ||
418
+ (await client.readContract({
419
+ address: accounting,
420
+ abi: ABI.Accounting,
421
+ functionName: 'exists',
422
+ args: [self.pool.id.raw, id],
423
+ }))) {
424
+ id = randomUint(256);
421
425
  }
426
+ allocatedIds.add(id);
427
+ createAccountCalls.push(encodeFunctionData({
428
+ abi: ABI.Hub,
429
+ functionName: 'createAccount',
430
+ args: [self.pool.id.raw, id, isDebitNormal],
431
+ }));
432
+ return id;
433
+ };
434
+ let initData;
435
+ if (isLiability) {
436
+ const expenseAccount = await resolveAccount(provided[AccountType.Expense], defaults?.expense, true);
437
+ const liabilityAccount = await resolveAccount(provided[AccountType.Liability], defaults?.liability, false);
438
+ initData = encodeFunctionData({
439
+ abi: ABI.Hub,
440
+ functionName: 'initializeLiability',
441
+ args: [self.pool.id.raw, self.id.raw, assetId.raw, valuation, expenseAccount, liabilityAccount],
442
+ });
422
443
  }
423
444
  else {
424
- const assetAccount = accounts[AccountType.Asset] || metadata?.shareClasses?.[self.id.raw]?.defaultAccounts?.asset;
425
- const equityAccount = accounts[AccountType.Equity] || metadata?.shareClasses?.[self.id.raw]?.defaultAccounts?.equity;
426
- const gainAccount = accounts[AccountType.Gain] || metadata?.shareClasses?.[self.id.raw]?.defaultAccounts?.gain;
427
- const lossAccount = accounts[AccountType.Loss] || metadata?.shareClasses?.[self.id.raw]?.defaultAccounts?.loss;
428
- if (equityAccount === undefined || gainAccount === undefined || lossAccount === undefined) {
429
- throw new Error('Missing required accounts for holding creation');
430
- }
431
- if (assetAccount) {
432
- data = encodeFunctionData({
433
- abi: ABI.Hub,
434
- functionName: 'initializeHolding',
435
- args: [
436
- self.pool.id.raw,
437
- self.id.raw,
438
- assetId.raw,
439
- valuation,
440
- assetAccount,
441
- equityAccount,
442
- gainAccount,
443
- lossAccount,
444
- ],
445
- });
446
- }
447
- else {
448
- const newAssetAccount = await self._getFreeAccountId();
449
- const createAccountData = encodeFunctionData({
450
- abi: ABI.Hub,
451
- functionName: 'createAccount',
452
- args: [self.pool.id.raw, newAssetAccount, false],
453
- });
454
- const initHoldingData = encodeFunctionData({
455
- abi: ABI.Hub,
456
- functionName: 'initializeHolding',
457
- args: [
458
- self.pool.id.raw,
459
- self.id.raw,
460
- assetId.raw,
461
- valuation,
462
- newAssetAccount,
463
- equityAccount,
464
- gainAccount,
465
- lossAccount,
466
- ],
467
- });
468
- data = [createAccountData, initHoldingData];
469
- }
445
+ const assetAccount = await resolveAccount(provided[AccountType.Asset], defaults?.asset, true);
446
+ const equityAccount = await resolveAccount(provided[AccountType.Equity], defaults?.equity, false);
447
+ const gainAccount = await resolveAccount(provided[AccountType.Gain], defaults?.gain, false);
448
+ const lossAccount = await resolveAccount(provided[AccountType.Loss], defaults?.loss, true);
449
+ initData = encodeFunctionData({
450
+ abi: ABI.Hub,
451
+ functionName: 'initializeHolding',
452
+ args: [
453
+ self.pool.id.raw,
454
+ self.id.raw,
455
+ assetId.raw,
456
+ valuation,
457
+ assetAccount,
458
+ equityAccount,
459
+ gainAccount,
460
+ lossAccount,
461
+ ],
462
+ });
470
463
  }
464
+ const data = createAccountCalls.length > 0 ? [...createAccountCalls, initData] : initData;
471
465
  yield* wrapTransaction('Create holding', ctx, {
472
466
  contract: hub,
473
467
  data,
@@ -2220,19 +2214,6 @@ export class ShareClass extends Entity {
2220
2214
  asset { decimals centrifugeId }
2221
2215
  }
2222
2216
  }
2223
- outstandingInvests(where: { tokenId: $scId }, limit: 1000) {
2224
- items {
2225
- account
2226
- assetId
2227
- tokenId
2228
- approvedAmount
2229
- approvedAt
2230
- depositAmount
2231
- epochIndex
2232
- pendingAmount
2233
- queuedAmount
2234
- }
2235
- }
2236
2217
  }`, { scId: this.id.raw })
2237
2218
  .pipe(map((data) => {
2238
2219
  const maxEpochByAsset = new Map();
@@ -2289,16 +2270,45 @@ export class ShareClass extends Entity {
2289
2270
  createdAtTxHash: item.createdAtTxHash ?? null,
2290
2271
  };
2291
2272
  }),
2292
- outstandingInvests: data.outstandingInvests.items.map((item) => ({
2293
- assetId: new AssetId(item.assetId),
2294
- account: item.account.toLowerCase(),
2295
- investor: item.account.toLowerCase(),
2296
- tokenId: item.tokenId.toLowerCase(),
2297
- approvedAmount: item.approvedAmount,
2298
- depositAmount: item.depositAmount,
2299
- pendingAmount: item.pendingAmount,
2300
- queuedAmount: item.queuedAmount,
2301
- })),
2273
+ // Derived: the deprecated `outstandingInvests` indexer field was removed
2274
+ // (testnet). It enumerated every investor with outstanding activity —
2275
+ // pending OR queued OR approved-but-not-yet-issued. We reconstruct that set
2276
+ // as the union of pendingInvestOrders (pending/queued) and the open
2277
+ // investOrders (issuedAt: null → approved-but-not-issued), keyed by
2278
+ // (investor, assetId). pending/queued amounts come from the pending table
2279
+ // (raw strings, '0' for approved-only investors); consumers wrap them in
2280
+ // Balance with asset decimals and only read assetId/investor/pending/queued.
2281
+ outstandingInvests: (() => {
2282
+ const byKey = new Map();
2283
+ data.pendingInvestOrders.items.forEach((item) => {
2284
+ const assetId = new AssetId(item.assetId);
2285
+ const account = item.account.toLowerCase();
2286
+ byKey.set(`${assetId.toString()}-${account}`, {
2287
+ assetId,
2288
+ account,
2289
+ investor: account,
2290
+ tokenId: this.id.raw,
2291
+ pendingAmount: item.pendingAssetsAmount || '0',
2292
+ queuedAmount: item.queuedAssetsAmount || '0',
2293
+ });
2294
+ });
2295
+ data.investOrders.items.forEach((item) => {
2296
+ const assetId = new AssetId(item.assetId);
2297
+ const account = item.account.toLowerCase();
2298
+ const key = `${assetId.toString()}-${account}`;
2299
+ if (!byKey.has(key)) {
2300
+ byKey.set(key, {
2301
+ assetId,
2302
+ account,
2303
+ investor: account,
2304
+ tokenId: this.id.raw,
2305
+ pendingAmount: '0',
2306
+ queuedAmount: '0',
2307
+ });
2308
+ }
2309
+ });
2310
+ return Array.from(byKey.values());
2311
+ })(),
2302
2312
  };
2303
2313
  }), repeatOnEvents(this._root, {
2304
2314
  address: batchRequestManager,
@@ -2368,20 +2378,6 @@ export class ShareClass extends Entity {
2368
2378
  token { decimals }
2369
2379
  }
2370
2380
  }
2371
- outstandingRedeems(where: {tokenId: $scId}, limit: 1000) {
2372
- items {
2373
- account
2374
- assetId
2375
- tokenId
2376
- epochIndex
2377
- approvedAmount
2378
- approvedAt
2379
- depositAmount
2380
- pendingAmount
2381
- queuedAmount
2382
- token { decimals }
2383
- }
2384
- }
2385
2381
  }`, { scId: this.id.raw })
2386
2382
  .pipe(map((data) => {
2387
2383
  const maxEpochByAsset = new Map();
@@ -2449,21 +2445,50 @@ export class ShareClass extends Entity {
2449
2445
  revokedAt: item.revokedAt ? new Date(item.revokedAt) : null,
2450
2446
  };
2451
2447
  }),
2452
- outstandingRedeems: data.outstandingRedeems.items.map((item) => {
2453
- const tokenDecimals = item.token.decimals;
2454
- return {
2455
- assetId: new AssetId(item.assetId),
2456
- account: item.account.toLowerCase(),
2457
- investor: item.account.toLowerCase(),
2458
- tokenId: item.tokenId.toLowerCase(),
2459
- approvedAt: item.approvedAt ? new Date(item.approvedAt) : null,
2460
- approvedAmount: new Balance(item.approvedAmount, tokenDecimals),
2461
- depositAmount: new Balance(item.depositAmount, tokenDecimals),
2462
- pendingAmount: new Balance(item.pendingAmount, tokenDecimals),
2463
- queuedAmount: new Balance(item.queuedAmount, tokenDecimals),
2464
- tokenDecimals,
2465
- };
2466
- }),
2448
+ // Derived: the deprecated `outstandingRedeems` indexer field was removed
2449
+ // (testnet). It enumerated every investor with outstanding activity —
2450
+ // pending OR queued OR approved-but-not-yet-revoked. We reconstruct that set
2451
+ // as the union of pendingRedeemOrders (pending/queued) and the open
2452
+ // redeemOrders (revokedAt: null → approved-but-not-revoked), keyed by
2453
+ // (investor, assetId). pending/queued amounts come from the pending table as
2454
+ // Balance (already scaled by token decimals; zero for approved-only
2455
+ // investors); consumers only read assetId/investor/pending/queued.
2456
+ outstandingRedeems: (() => {
2457
+ const tokenDecimals = data.epochOutstandingRedeems.items[0]?.token.decimals ??
2458
+ data.epochRedeemOrders.items[0]?.token.decimals ??
2459
+ 18;
2460
+ const byKey = new Map();
2461
+ data.pendingRedeemOrders.items.forEach((item) => {
2462
+ const assetId = new AssetId(item.assetId);
2463
+ const account = item.account.toLowerCase();
2464
+ byKey.set(`${assetId.toString()}-${account}`, {
2465
+ assetId,
2466
+ account,
2467
+ investor: account,
2468
+ tokenId: this.id.raw,
2469
+ pendingAmount: new Balance(item.pendingSharesAmount ?? '0', tokenDecimals),
2470
+ queuedAmount: new Balance(item.queuedSharesAmount ?? '0', tokenDecimals),
2471
+ tokenDecimals,
2472
+ });
2473
+ });
2474
+ data.redeemOrders.items.forEach((item) => {
2475
+ const assetId = new AssetId(item.assetId);
2476
+ const account = item.account.toLowerCase();
2477
+ const key = `${assetId.toString()}-${account}`;
2478
+ if (!byKey.has(key)) {
2479
+ byKey.set(key, {
2480
+ assetId,
2481
+ account,
2482
+ investor: account,
2483
+ tokenId: this.id.raw,
2484
+ pendingAmount: new Balance('0', tokenDecimals),
2485
+ queuedAmount: new Balance('0', tokenDecimals),
2486
+ tokenDecimals,
2487
+ });
2488
+ }
2489
+ });
2490
+ return Array.from(byKey.values());
2491
+ })(),
2467
2492
  };
2468
2493
  }), repeatOnEvents(this._root, {
2469
2494
  address: batchRequestManager,