@centrifuge/sdk 0.0.0-alpha.28 → 0.0.0-alpha.30

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 (53) hide show
  1. package/dist/Centrifuge.d.ts +183 -169
  2. package/dist/Centrifuge.d.ts.map +1 -1
  3. package/dist/Centrifuge.js +169 -79
  4. package/dist/Centrifuge.js.map +1 -1
  5. package/dist/Centrifuge.test.js +33 -15
  6. package/dist/Centrifuge.test.js.map +1 -1
  7. package/dist/abi/index.d.ts +11 -0
  8. package/dist/abi/index.d.ts.map +1 -1
  9. package/dist/abi/index.js +3 -1
  10. package/dist/abi/index.js.map +1 -1
  11. package/dist/config/chains.d.ts +156 -938
  12. package/dist/config/chains.d.ts.map +1 -1
  13. package/dist/config/chains.js +2 -11
  14. package/dist/config/chains.js.map +1 -1
  15. package/dist/entities/BalanceSheet.d.ts +24 -10
  16. package/dist/entities/BalanceSheet.d.ts.map +1 -1
  17. package/dist/entities/BalanceSheet.js +4 -59
  18. package/dist/entities/BalanceSheet.js.map +1 -1
  19. package/dist/entities/Investor.d.ts +1 -5
  20. package/dist/entities/Investor.d.ts.map +1 -1
  21. package/dist/entities/Investor.js +40 -10
  22. package/dist/entities/Investor.js.map +1 -1
  23. package/dist/entities/Pool.d.ts +5 -5
  24. package/dist/entities/Pool.d.ts.map +1 -1
  25. package/dist/entities/Pool.js +18 -5
  26. package/dist/entities/Pool.js.map +1 -1
  27. package/dist/entities/Pool.test.js +5 -5
  28. package/dist/entities/Pool.test.js.map +1 -1
  29. package/dist/entities/PoolNetwork.d.ts +5 -6
  30. package/dist/entities/PoolNetwork.d.ts.map +1 -1
  31. package/dist/entities/PoolNetwork.js +22 -59
  32. package/dist/entities/PoolNetwork.js.map +1 -1
  33. package/dist/entities/ShareClass.d.ts +36 -30
  34. package/dist/entities/ShareClass.d.ts.map +1 -1
  35. package/dist/entities/ShareClass.js +329 -265
  36. package/dist/entities/ShareClass.js.map +1 -1
  37. package/dist/entities/ShareClass.test.js +16 -17
  38. package/dist/entities/ShareClass.test.js.map +1 -1
  39. package/dist/entities/Vault.d.ts +5 -5
  40. package/dist/entities/Vault.d.ts.map +1 -1
  41. package/dist/entities/Vault.js +5 -4
  42. package/dist/entities/Vault.js.map +1 -1
  43. package/dist/entities/Vault.test.js +10 -19
  44. package/dist/entities/Vault.test.js.map +1 -1
  45. package/dist/types/transaction.d.ts +39 -0
  46. package/dist/types/transaction.d.ts.map +1 -1
  47. package/dist/types/transaction.js +40 -1
  48. package/dist/types/transaction.js.map +1 -1
  49. package/dist/utils/transaction.d.ts +10 -3
  50. package/dist/utils/transaction.d.ts.map +1 -1
  51. package/dist/utils/transaction.js +8 -2
  52. package/dist/utils/transaction.js.map +1 -1
  53. package/package.json +3 -3
@@ -2,14 +2,16 @@ import { catchError, combineLatest, defer, EMPTY, expand, filter, map, of, switc
2
2
  import { encodeFunctionData, encodePacked, getContract, parseAbi } from 'viem';
3
3
  import { ABI } from '../abi/index.js';
4
4
  import { AccountType } from '../types/holdings.js';
5
+ import { MessageType } from '../types/transaction.js';
5
6
  import { Balance, Price } from '../utils/BigInt.js';
6
7
  import { addressToBytes32, randomUint } from '../utils/index.js';
7
8
  import { repeatOnEvents } from '../utils/rx.js';
8
- import { doTransaction } from '../utils/transaction.js';
9
+ import { wrapTransaction } from '../utils/transaction.js';
9
10
  import { AssetId, ShareClassId } from '../utils/types.js';
10
11
  import { BalanceSheet } from './BalanceSheet.js';
11
12
  import { Entity } from './Entity.js';
12
13
  import { PoolNetwork } from './PoolNetwork.js';
14
+ import { Vault } from './Vault.js';
13
15
  /**
14
16
  * Query and interact with a share class, which allows querying total issuance, NAV per share,
15
17
  * and allows interactions related to asynchronous deposits and redemptions.
@@ -29,9 +31,8 @@ export class ShareClass extends Entity {
29
31
  * @returns The details of the share class, including name, symbol, total issuance, and NAV per share.
30
32
  */
31
33
  details() {
32
- return this._query(null, () => combineLatest([this._metrics(), this._metadata(), this.navPerNetwork()]).pipe(map(([metrics, metadata, navPerNetwork]) => {
33
- const totalIssuance = navPerNetwork.reduce((acc, item) => acc.add(item.totalIssuance), new Balance(0n, 18) // TODO: Replace with pool currency decimals
34
- );
34
+ return this._query(null, () => combineLatest([this._metrics(), this._metadata(), this.navPerNetwork(), this.pool.currency()]).pipe(map(([metrics, metadata, navPerNetwork, poolCurrency]) => {
35
+ const totalIssuance = navPerNetwork.reduce((acc, item) => acc.add(item.totalIssuance), new Balance(0n, poolCurrency.decimals));
35
36
  return {
36
37
  id: this.id,
37
38
  name: metadata.name,
@@ -53,113 +54,88 @@ export class ShareClass extends Entity {
53
54
  })));
54
55
  }
55
56
  navPerNetwork() {
56
- return this._root._queryIndexer(`query ($scId: String!) {
57
- tokenInstances(where: { tokenId: $scId }) {
58
- items {
59
- totalIssuance
60
- tokenPrice
61
- blockchain {
62
- id
63
- }
64
- }
65
- }
66
- }`, { scId: this.id.raw }, (data) => data.tokenInstances.items.map((item) => ({
57
+ return this._query(null, () => this.pool.currency().pipe(switchMap((poolCurrency) => this._root._queryIndexer(`query ($scId: String!) {
58
+ tokenInstances(where: { tokenId: $scId }) {
59
+ items {
60
+ totalIssuance
61
+ tokenPrice
62
+ blockchain {
63
+ id
64
+ }
65
+ }
66
+ }
67
+ }`, { scId: this.id.raw }, (data) => data.tokenInstances.items.map((item) => ({
67
68
  chainId: Number(item.blockchain.id),
68
- totalIssuance: new Balance(item.totalIssuance, 18), // TODO: Replace with pool currency decimals
69
+ totalIssuance: new Balance(item.totalIssuance, poolCurrency.decimals),
69
70
  pricePerShare: new Price(item.tokenPrice),
70
- nav: new Balance(item.totalIssuance, 18).mul(new Price(item.tokenPrice)),
71
- })));
71
+ nav: new Balance(item.totalIssuance, poolCurrency.decimals).mul(new Price(item.tokenPrice)),
72
+ }))))));
72
73
  }
73
74
  /**
74
75
  * Query the vaults of the share class.
75
- * @param chainId The chain ID to query the vaults on.
76
- * @returns The vaults of the share class on the given chain.
76
+ * @param chainId The optional chain ID to query the vaults on.
77
+ * @returns All vaults of the share class, or filtered by the given chain.
77
78
  */
78
79
  vaults(chainId) {
79
- return this._query(null, () => new PoolNetwork(this._root, this.pool, chainId).vaults(this.id));
80
+ return this._query(null, () => this._allVaults().pipe(map((allVaults) => {
81
+ const vaults = allVaults.filter((vault) => vault.chainId === chainId || !chainId);
82
+ return vaults.map((vault) => new Vault(this._root, new PoolNetwork(this._root, this.pool, vault.chainId), this, vault.assetAddress, vault.address));
83
+ })));
80
84
  }
81
85
  /**
82
- * Query all the holdings of the share class.
86
+ * Query all the balances of the share class (from BalanceSheet and Holdings).
83
87
  */
84
- holdings() {
85
- return this._query(null, () => this._holdings().pipe(switchMap((res) => {
86
- if (res.holdings.items.length === 0) {
88
+ balances(chainId) {
89
+ return this._query(null, () => combineLatest([this._balances(), this.pool.currency()]).pipe(switchMap(([res, poolCurrency]) => {
90
+ if (res.length === 0) {
87
91
  return of([]);
88
92
  }
89
- return combineLatest(res.holdings.items.map((holding) => {
90
- const assetId = new AssetId(holding.assetId);
91
- return this._holding(assetId);
92
- })).pipe(map((holdings) => holdings.map(({ assetDecimals: _, ...holding }, i) => {
93
- const data = res.holdings.items[i];
93
+ const items = res.filter((item) => Number(item.asset.blockchain.id) === chainId || !chainId);
94
+ return combineLatest([
95
+ combineLatest(items.map((holding) => {
96
+ if (!holding.holding)
97
+ return of(null);
98
+ const assetId = new AssetId(holding.assetId);
99
+ return this._holding(assetId);
100
+ })),
101
+ combineLatest(items.map((holding) => {
102
+ const assetId = new AssetId(holding.assetId);
103
+ return this._balance(Number(holding.asset.blockchain.id), {
104
+ address: holding.asset.address,
105
+ assetTokenId: BigInt(holding.asset.assetTokenId),
106
+ id: assetId,
107
+ decimals: holding.asset.decimals,
108
+ });
109
+ })),
110
+ ]).pipe(map(([holdings, balances]) => items.map((data, i) => {
111
+ const holding = holdings[i];
112
+ const balance = balances[i];
113
+ // If the holding hasn't been initialized yet, the price is 1
114
+ const price = holding ? balance.price : Price.fromFloat(1);
115
+ const value = Balance.fromFloat(balance.amount.toDecimal().mul(price.toDecimal()), poolCurrency.decimals);
94
116
  return {
95
- ...holding,
117
+ assetId: new AssetId(data.assetId),
118
+ amount: balance.amount,
119
+ value,
120
+ price,
96
121
  asset: {
97
- decimals: data.holdingEscrow.asset.decimals,
98
- address: data.holdingEscrow.asset.address,
99
- name: data.holdingEscrow.asset.name,
100
- symbol: data.holdingEscrow.asset.symbol,
101
- chainId: Number(data.holdingEscrow.asset.blockchain.id),
122
+ decimals: data.asset.decimals,
123
+ address: data.asset.address,
124
+ name: data.asset.name,
125
+ symbol: data.asset.symbol,
126
+ chainId: Number(data.asset.blockchain.id),
127
+ },
128
+ holding: holding && {
129
+ valuation: holding.valuation,
130
+ amount: holding.amount,
131
+ value: holding.value,
132
+ isLiability: holding.isLiability,
133
+ accounts: holding.accounts,
102
134
  },
103
135
  };
104
136
  })));
105
137
  })));
106
138
  }
107
- /** @internal */
108
- _holding(assetId) {
109
- return this._query(['holding', assetId.toString()], () => this._root._protocolAddresses(this.pool.chainId).pipe(switchMap(({ holdings: holdingsAddr, hubRegistry }) => defer(async () => {
110
- const holdings = getContract({
111
- address: holdingsAddr,
112
- abi: ABI.Holdings,
113
- client: this._root.getClient(this.pool.chainId),
114
- });
115
- const [valuation, amount, value, assetDecimals, isLiability, ...accounts] = await Promise.all([
116
- holdings.read.valuation([this.pool.id.raw, this.id.raw, assetId.raw]),
117
- holdings.read.amount([this.pool.id.raw, this.id.raw, assetId.raw]),
118
- holdings.read.value([this.pool.id.raw, this.id.raw, assetId.raw]),
119
- this._root.getClient(this.pool.chainId).readContract({
120
- address: hubRegistry,
121
- // Use inline ABI because of function overload
122
- abi: parseAbi(['function decimals(uint256) view returns (uint8)']),
123
- functionName: 'decimals',
124
- args: [assetId.raw],
125
- }),
126
- holdings.read.isLiability([this.pool.id.raw, this.id.raw, assetId.raw]),
127
- ...[
128
- AccountType.Asset,
129
- AccountType.Equity,
130
- AccountType.Loss,
131
- AccountType.Gain,
132
- AccountType.Expense,
133
- AccountType.Liability,
134
- ].map((kind) => holdings.read.accountId([this.pool.id.raw, this.id.raw, assetId.raw, kind])),
135
- ]);
136
- return {
137
- assetId,
138
- assetDecimals,
139
- valuation,
140
- amount: new Balance(amount, assetDecimals),
141
- value: new Balance(value, 18), // TODO: Replace with pool currency decimals
142
- isLiability,
143
- accounts: {
144
- [AccountType.Asset]: accounts[0] || null,
145
- [AccountType.Equity]: accounts[1] || null,
146
- [AccountType.Loss]: accounts[2] || null,
147
- [AccountType.Gain]: accounts[3] || null,
148
- [AccountType.Expense]: accounts[4] || null,
149
- [AccountType.Liability]: accounts[5] || null,
150
- },
151
- };
152
- }).pipe(repeatOnEvents(this._root, {
153
- address: holdingsAddr,
154
- abi: ABI.Holdings,
155
- eventName: ['Increase', 'Decrease', 'Update', 'UpdateValuation'],
156
- filter: (events) => {
157
- return events.some((event) => {
158
- return event.args.scId === this.id && event.args.assetId === assetId.raw;
159
- });
160
- },
161
- }, this.pool.chainId)))));
162
- }
163
139
  /**
164
140
  * Get the pending and approved amounts for deposits and redemptions for each asset.
165
141
  */
@@ -253,12 +229,12 @@ export class ShareClass extends Entity {
253
229
  */
254
230
  createHolding(assetId, valuation, isLiability, accounts) {
255
231
  const self = this;
256
- return this._transact(async function* ({ walletClient, publicClient }) {
232
+ return this._transact(async function* (ctx) {
257
233
  const [{ hub }, metadata] = await Promise.all([
258
234
  self._root._protocolAddresses(self.pool.chainId),
259
235
  self.pool.metadata(),
260
236
  ]);
261
- let tx;
237
+ let data;
262
238
  if (isLiability) {
263
239
  const expenseAccount = accounts[AccountType.Expense] || metadata?.shareClasses?.[self.id.raw]?.defaultAccounts?.expense;
264
240
  const liabilityAccount = accounts[AccountType.Liability] || metadata?.shareClasses?.[self.id.raw]?.defaultAccounts?.liability;
@@ -266,8 +242,7 @@ export class ShareClass extends Entity {
266
242
  throw new Error('Missing required accounts for liability creation');
267
243
  }
268
244
  if (expenseAccount) {
269
- tx = walletClient.writeContract({
270
- address: hub,
245
+ data = encodeFunctionData({
271
246
  abi: ABI.Hub,
272
247
  functionName: 'initializeLiability',
273
248
  args: [self.pool.id.raw, self.id.raw, assetId.raw, valuation, expenseAccount, liabilityAccount],
@@ -285,12 +260,7 @@ export class ShareClass extends Entity {
285
260
  functionName: 'initializeLiability',
286
261
  args: [self.pool.id.raw, self.id.raw, assetId.raw, valuation, newExpenseAccount, liabilityAccount],
287
262
  });
288
- tx = walletClient.writeContract({
289
- address: hub,
290
- abi: ABI.Hub,
291
- functionName: 'multicall',
292
- args: [[createAccountData, initHoldingData]],
293
- });
263
+ data = [createAccountData, initHoldingData];
294
264
  }
295
265
  }
296
266
  else {
@@ -302,8 +272,7 @@ export class ShareClass extends Entity {
302
272
  throw new Error('Missing required accounts for holding creation');
303
273
  }
304
274
  if (assetAccount) {
305
- tx = walletClient.writeContract({
306
- address: hub,
275
+ data = encodeFunctionData({
307
276
  abi: ABI.Hub,
308
277
  functionName: 'initializeHolding',
309
278
  args: [
@@ -339,81 +308,101 @@ export class ShareClass extends Entity {
339
308
  lossAccount,
340
309
  ],
341
310
  });
342
- tx = walletClient.writeContract({
343
- address: hub,
344
- abi: ABI.Hub,
345
- functionName: 'multicall',
346
- args: [[createAccountData, initHoldingData]],
347
- });
311
+ data = [createAccountData, initHoldingData];
348
312
  }
349
313
  }
350
- yield* doTransaction('Create holding', publicClient, () => tx);
314
+ yield* wrapTransaction('Create holding', ctx, {
315
+ contract: hub,
316
+ data,
317
+ });
351
318
  }, this.pool.chainId);
352
319
  }
353
320
  updateSharePrice(pricePerShare) {
354
321
  const self = this;
355
- return this._transact(async function* ({ walletClient, publicClient }) {
322
+ return this._transact(async function* (ctx) {
356
323
  const { hub } = await self._root._protocolAddresses(self.pool.chainId);
357
- yield* doTransaction('Update price', publicClient, () => walletClient.writeContract({
358
- address: hub,
359
- abi: ABI.Hub,
360
- functionName: 'updateSharePrice',
361
- args: [self.pool.id.raw, self.id.raw, pricePerShare.toBigInt()],
362
- }));
324
+ yield* wrapTransaction('Update share price', ctx, {
325
+ contract: hub,
326
+ data: encodeFunctionData({
327
+ abi: ABI.Hub,
328
+ functionName: 'updateSharePrice',
329
+ args: [self.pool.id.raw, self.id.raw, pricePerShare.toBigInt()],
330
+ }),
331
+ });
363
332
  }, this.pool.chainId);
364
333
  }
365
334
  setMaxAssetPriceAge(assetId, maxPriceAge) {
366
335
  const self = this;
367
- return this._transact(async function* ({ walletClient, publicClient }) {
336
+ return this._transact(async function* (ctx) {
368
337
  const { hub } = await self._root._protocolAddresses(self.pool.chainId);
369
- yield* doTransaction('Set max asset price age', publicClient, () => walletClient.writeContract({
370
- address: hub,
371
- abi: ABI.Hub,
372
- functionName: 'setMaxAssetPriceAge',
373
- args: [self.pool.id.raw, self.id.raw, assetId.raw, BigInt(maxPriceAge)],
374
- }));
338
+ yield* wrapTransaction('Set max asset price age', ctx, {
339
+ contract: hub,
340
+ data: encodeFunctionData({
341
+ abi: ABI.Hub,
342
+ functionName: 'setMaxAssetPriceAge',
343
+ args: [self.pool.id.raw, self.id.raw, assetId.raw, BigInt(maxPriceAge)],
344
+ }),
345
+ messages: {
346
+ [assetId.centrifugeId]: [MessageType.MaxAssetPriceAge],
347
+ },
348
+ });
375
349
  }, this.pool.chainId);
376
350
  }
377
351
  setMaxSharePriceAge(chainId, maxPriceAge) {
378
352
  const self = this;
379
- return this._transact(async function* ({ walletClient, publicClient }) {
353
+ return this._transact(async function* (ctx) {
380
354
  const [{ hub }, id] = await Promise.all([
381
355
  self._root._protocolAddresses(self.pool.chainId),
382
356
  self._root.id(chainId),
383
357
  ]);
384
- yield* doTransaction('Set max share price age', publicClient, () => walletClient.writeContract({
385
- address: hub,
386
- abi: ABI.Hub,
387
- functionName: 'setMaxSharePriceAge',
388
- args: [id, self.pool.id.raw, self.id.raw, BigInt(maxPriceAge)],
389
- }));
358
+ yield* wrapTransaction('Set max share price age', ctx, {
359
+ contract: hub,
360
+ data: encodeFunctionData({
361
+ abi: ABI.Hub,
362
+ functionName: 'setMaxSharePriceAge',
363
+ args: [id, self.pool.id.raw, self.id.raw, BigInt(maxPriceAge)],
364
+ }),
365
+ messages: {
366
+ [id]: [MessageType.MaxSharePriceAge],
367
+ },
368
+ });
390
369
  }, this.pool.chainId);
391
370
  }
392
371
  notifyAssetPrice(assetId) {
393
372
  const self = this;
394
- return this._transact(async function* ({ walletClient, publicClient }) {
373
+ return this._transact(async function* (ctx) {
395
374
  const { hub } = await self._root._protocolAddresses(self.pool.chainId);
396
- yield* doTransaction('Notify asset price', publicClient, () => walletClient.writeContract({
397
- address: hub,
398
- abi: ABI.Hub,
399
- functionName: 'notifyAssetPrice',
400
- args: [self.pool.id.raw, self.id.raw, assetId.raw],
401
- }));
375
+ yield* wrapTransaction('Notify asset price', ctx, {
376
+ contract: hub,
377
+ data: encodeFunctionData({
378
+ abi: ABI.Hub,
379
+ functionName: 'notifyAssetPrice',
380
+ args: [self.pool.id.raw, self.id.raw, assetId.raw],
381
+ }),
382
+ messages: {
383
+ [assetId.centrifugeId]: [MessageType.NotifyPricePoolPerAsset],
384
+ },
385
+ });
402
386
  }, this.pool.chainId);
403
387
  }
404
388
  notifySharePrice(chainId) {
405
389
  const self = this;
406
- return this._transact(async function* ({ walletClient, publicClient }) {
390
+ return this._transact(async function* (ctx) {
407
391
  const [{ hub }, id] = await Promise.all([
408
392
  self._root._protocolAddresses(self.pool.chainId),
409
393
  self._root.id(chainId),
410
394
  ]);
411
- yield* doTransaction('Notify share price', publicClient, () => walletClient.writeContract({
412
- address: hub,
413
- abi: ABI.Hub,
414
- functionName: 'notifySharePrice',
415
- args: [self.pool.id.raw, self.id.raw, id],
416
- }));
395
+ yield* wrapTransaction('Notify share price', ctx, {
396
+ contract: hub,
397
+ data: encodeFunctionData({
398
+ abi: ABI.Hub,
399
+ functionName: 'notifySharePrice',
400
+ args: [self.pool.id.raw, self.id.raw, id],
401
+ }),
402
+ messages: {
403
+ [id]: [MessageType.NotifyPricePoolPerShare],
404
+ },
405
+ });
417
406
  }, this.pool.chainId);
418
407
  }
419
408
  /**
@@ -423,18 +412,17 @@ export class ShareClass extends Entity {
423
412
  approveDepositsAndIssueShares(assets) {
424
413
  // TODO: Also claim orders
425
414
  const self = this;
426
- return this._transact(async function* ({ walletClient, publicClient }) {
427
- const centIds = [...new Set(assets.map((a) => a.assetId.centrifugeId))];
428
- const [{ hub }, pendingAmounts,
429
- // ...estimates
430
- ] = await Promise.all([
415
+ return this._transact(async function* (ctx) {
416
+ const [{ hub }, pendingAmounts] = await Promise.all([
431
417
  self._root._protocolAddresses(self.pool.chainId),
432
418
  self.pendingAmounts(),
433
- ...centIds.map((centId) => self._root._estimate(self.pool.chainId, { centId })),
434
419
  ]);
435
- // const estimateByCentId: Map<number, bigint> = new Map(centIds.map((centId, i) => [centId, estimates[i]!]))
436
420
  const batch = [];
437
- const estimate = 0n;
421
+ const messages = {};
422
+ // function addMessage(centId: number, message: MessageType) {
423
+ // if (!messages[centId]) messages[centId] = []
424
+ // messages[centId].push(message)
425
+ // }
438
426
  for (const asset of assets) {
439
427
  const pending = pendingAmounts.find((e) => e.assetId.equals(asset.assetId));
440
428
  if (!pending) {
@@ -481,21 +469,10 @@ export class ShareClass extends Entity {
481
469
  if (batch.length === 0) {
482
470
  throw new Error('No approve or issue actions provided');
483
471
  }
484
- yield* doTransaction('Approve and issue', publicClient, () => {
485
- if (batch.length === 1) {
486
- return walletClient.sendTransaction({
487
- data: batch[0],
488
- to: hub,
489
- value: estimate,
490
- });
491
- }
492
- return walletClient.writeContract({
493
- address: hub,
494
- abi: ABI.Hub,
495
- functionName: 'multicall',
496
- args: [batch],
497
- value: estimate,
498
- });
472
+ yield* wrapTransaction('Approve and issue', ctx, {
473
+ contract: hub,
474
+ data: batch,
475
+ messages,
499
476
  });
500
477
  }, this.pool.chainId);
501
478
  }
@@ -506,18 +483,17 @@ export class ShareClass extends Entity {
506
483
  approveRedeemsAndRevokeShares(assets) {
507
484
  // TODO: Also claim orders
508
485
  const self = this;
509
- return this._transact(async function* ({ walletClient, publicClient }) {
510
- const centIds = [...new Set(assets.map((a) => a.assetId.centrifugeId))];
511
- const [{ hub }, pendingAmounts,
512
- // ...estimates
513
- ] = await Promise.all([
486
+ return this._transact(async function* (ctx) {
487
+ const [{ hub }, pendingAmounts] = await Promise.all([
514
488
  self._root._protocolAddresses(self.pool.chainId),
515
489
  self.pendingAmounts(),
516
- ...centIds.map((centId) => self._root._estimate(self.pool.chainId, { centId })),
517
490
  ]);
518
- // const estimateByCentId: Map<number, bigint> = new Map(centIds.map((centId, i) => [centId, estimates[i]!]))
519
491
  const batch = [];
520
- const estimate = 0n;
492
+ const messages = {};
493
+ // function addMessage(centId: number, message: MessageType) {
494
+ // if (!messages[centId]) messages[centId] = []
495
+ // messages[centId].push(message)
496
+ // }
521
497
  for (const asset of assets) {
522
498
  const pending = pendingAmounts.find((e) => e.assetId.equals(asset.assetId));
523
499
  if (!pending) {
@@ -569,21 +545,10 @@ export class ShareClass extends Entity {
569
545
  if (batch.length === 0) {
570
546
  throw new Error('No approve or revoke actions provided');
571
547
  }
572
- yield* doTransaction('Approve and revoke', publicClient, () => {
573
- if (batch.length === 1) {
574
- return walletClient.sendTransaction({
575
- data: batch[0],
576
- to: hub,
577
- value: estimate,
578
- });
579
- }
580
- return walletClient.writeContract({
581
- address: hub,
582
- abi: ABI.Hub,
583
- functionName: 'multicall',
584
- args: [batch],
585
- value: estimate,
586
- });
548
+ yield* wrapTransaction('Approve and revoke', ctx, {
549
+ contract: hub,
550
+ data: batch,
551
+ messages,
587
552
  });
588
553
  }, this.pool.chainId);
589
554
  }
@@ -593,25 +558,26 @@ export class ShareClass extends Entity {
593
558
  */
594
559
  claimDeposit(assetId, investor) {
595
560
  const self = this;
596
- return this._transact(async function* ({ walletClient, publicClient }) {
597
- const [{ hub }, investorOrder, estimate] = await Promise.all([
561
+ return this._transact(async function* (ctx) {
562
+ const [{ hub }, investorOrder] = await Promise.all([
598
563
  self._root._protocolAddresses(self.pool.chainId),
599
564
  self.investorOrder(assetId, investor),
600
- self._root._estimate(self.pool.chainId, { centId: assetId.centrifugeId }),
601
565
  ]);
602
- yield* doTransaction('Claim deposit', publicClient, () => walletClient.writeContract({
603
- address: hub,
604
- abi: ABI.Hub,
605
- functionName: 'notifyDeposit',
606
- args: [
607
- self.pool.id.raw,
608
- self.id.raw,
609
- assetId.raw,
610
- addressToBytes32(investor),
611
- investorOrder.maxDepositClaims,
612
- ],
613
- value: estimate,
614
- }));
566
+ yield* wrapTransaction('Claim deposit', ctx, {
567
+ contract: hub,
568
+ data: encodeFunctionData({
569
+ abi: ABI.Hub,
570
+ functionName: 'notifyDeposit',
571
+ args: [
572
+ self.pool.id.raw,
573
+ self.id.raw,
574
+ assetId.raw,
575
+ addressToBytes32(investor),
576
+ investorOrder.maxDepositClaims,
577
+ ],
578
+ }),
579
+ messages: { [assetId.centrifugeId]: [MessageType.RequestCallback] },
580
+ });
615
581
  }, this.pool.chainId);
616
582
  }
617
583
  /**
@@ -620,19 +586,20 @@ export class ShareClass extends Entity {
620
586
  */
621
587
  claimRedeem(assetId, investor) {
622
588
  const self = this;
623
- return this._transact(async function* ({ walletClient, publicClient }) {
624
- const [{ hub }, investorOrder, estimate] = await Promise.all([
589
+ return this._transact(async function* (ctx) {
590
+ const [{ hub }, investorOrder] = await Promise.all([
625
591
  self._root._protocolAddresses(self.pool.chainId),
626
592
  self.investorOrder(assetId, investor),
627
- self._root._estimate(self.pool.chainId, { centId: assetId.centrifugeId }),
628
593
  ]);
629
- yield* doTransaction('Claim redeem', publicClient, () => walletClient.writeContract({
630
- address: hub,
631
- abi: ABI.Hub,
632
- functionName: 'notifyRedeem',
633
- args: [self.pool.id.raw, self.id.raw, assetId.raw, addressToBytes32(investor), investorOrder.maxRedeemClaims],
634
- value: estimate,
635
- }));
594
+ yield* wrapTransaction('Claim redeem', ctx, {
595
+ contract: hub,
596
+ data: encodeFunctionData({
597
+ abi: ABI.Hub,
598
+ functionName: 'notifyRedeem',
599
+ args: [self.pool.id.raw, self.id.raw, assetId.raw, addressToBytes32(investor), investorOrder.maxRedeemClaims],
600
+ }),
601
+ messages: { [assetId.centrifugeId]: [MessageType.RequestCallback] },
602
+ });
636
603
  }, this.pool.chainId);
637
604
  }
638
605
  /**
@@ -643,45 +610,146 @@ export class ShareClass extends Entity {
643
610
  */
644
611
  updateMember(address, validUntil, chainId) {
645
612
  const self = this;
646
- return this._transact(async function* ({ walletClient, publicClient }) {
647
- const [{ hub }, id, estimate] = await Promise.all([
613
+ return this._transact(async function* (ctx) {
614
+ const [{ hub }, id] = await Promise.all([
648
615
  self._root._protocolAddresses(self.pool.chainId),
649
616
  self._root.id(chainId),
650
- self._root._estimate(self.pool.chainId, { chainId }),
651
617
  ]);
652
618
  const payload = encodePacked(['uint8', 'bytes32', 'uint64'], [/* UpdateRestrictionType.Member */ 1, addressToBytes32(address), BigInt(validUntil)]);
653
- yield* doTransaction('Update restriction', publicClient, () => walletClient.writeContract({
654
- address: hub,
655
- abi: ABI.Hub,
656
- functionName: 'updateRestriction',
657
- args: [self.pool.id.raw, self.id.raw, id, payload, 0n],
658
- value: estimate,
659
- }));
619
+ yield* wrapTransaction('Update member', ctx, {
620
+ contract: hub,
621
+ data: encodeFunctionData({
622
+ abi: ABI.Hub,
623
+ functionName: 'updateRestriction',
624
+ args: [self.pool.id.raw, self.id.raw, id, payload, 0n],
625
+ }),
626
+ messages: { [id]: [MessageType.UpdateRestriction] },
627
+ });
660
628
  }, this.pool.chainId);
661
629
  }
662
630
  /** @internal */
663
- _holdings() {
631
+ _balances() {
664
632
  return this._root._queryIndexer(`query ($scId: String!) {
665
- holdings(where: { tokenId: $scId }) {
633
+ holdingEscrows(where: { tokenId: $scId }) {
666
634
  items {
635
+ holding {
636
+ updatedAt
637
+ }
638
+ assetAmount
639
+ assetPrice
667
640
  assetId
668
- holdingEscrow {
669
- asset {
670
- decimals
671
- assetTokenId
672
- address
673
- name
674
- symbol
675
- blockchain {
676
- id
677
- }
641
+ asset {
642
+ decimals
643
+ assetTokenId
644
+ address
645
+ name
646
+ symbol
647
+ blockchain {
648
+ id
678
649
  }
679
650
  }
680
651
  }
681
652
  }
682
653
  }`, {
683
654
  scId: this.id.raw,
684
- });
655
+ }, (data) => data.holdingEscrows.items);
656
+ }
657
+ /** @internal */
658
+ _holding(assetId) {
659
+ return this._query(['holding', assetId.toString()], () => combineLatest([this._root._protocolAddresses(this.pool.chainId), this.pool.currency()]).pipe(switchMap(([{ holdings: holdingsAddr, hubRegistry }, poolCurrency]) => defer(async () => {
660
+ const holdings = getContract({
661
+ address: holdingsAddr,
662
+ abi: ABI.Holdings,
663
+ client: this._root.getClient(this.pool.chainId),
664
+ });
665
+ const [valuation, amount, value, assetDecimals, isLiability, ...accounts] = await Promise.all([
666
+ holdings.read.valuation([this.pool.id.raw, this.id.raw, assetId.raw]),
667
+ holdings.read.amount([this.pool.id.raw, this.id.raw, assetId.raw]),
668
+ holdings.read.value([this.pool.id.raw, this.id.raw, assetId.raw]),
669
+ this._root.getClient(this.pool.chainId).readContract({
670
+ address: hubRegistry,
671
+ // Use inline ABI because of function overload
672
+ abi: parseAbi(['function decimals(uint256) view returns (uint8)']),
673
+ functionName: 'decimals',
674
+ args: [assetId.raw],
675
+ }),
676
+ holdings.read.isLiability([this.pool.id.raw, this.id.raw, assetId.raw]),
677
+ ...[
678
+ AccountType.Asset,
679
+ AccountType.Equity,
680
+ AccountType.Loss,
681
+ AccountType.Gain,
682
+ AccountType.Expense,
683
+ AccountType.Liability,
684
+ ].map((kind) => holdings.read.accountId([this.pool.id.raw, this.id.raw, assetId.raw, kind])),
685
+ ]);
686
+ return {
687
+ assetId,
688
+ assetDecimals,
689
+ valuation,
690
+ amount: new Balance(amount, assetDecimals),
691
+ value: new Balance(value, poolCurrency.decimals),
692
+ isLiability,
693
+ accounts: {
694
+ [AccountType.Asset]: accounts[0] || null,
695
+ [AccountType.Equity]: accounts[1] || null,
696
+ [AccountType.Loss]: accounts[2] || null,
697
+ [AccountType.Gain]: accounts[3] || null,
698
+ [AccountType.Expense]: accounts[4] || null,
699
+ [AccountType.Liability]: accounts[5] || null,
700
+ },
701
+ };
702
+ }).pipe(repeatOnEvents(this._root, {
703
+ address: holdingsAddr,
704
+ abi: ABI.Holdings,
705
+ eventName: ['Increase', 'Decrease', 'Update', 'UpdateValuation'],
706
+ filter: (events) => {
707
+ return events.some((event) => {
708
+ return event.args.scId === this.id && event.args.assetId === assetId.raw;
709
+ });
710
+ },
711
+ }, this.pool.chainId)))));
712
+ }
713
+ /** @internal */
714
+ _balance(chainId, asset) {
715
+ return this._query(['balance', asset.id.toString()], () => combineLatest([this._root._protocolAddresses(chainId), this.pool.currency()]).pipe(switchMap(([addresses, poolCurrency]) => defer(async () => {
716
+ const client = this._root.getClient(chainId);
717
+ const [amountBn, priceBn] = await Promise.all([
718
+ client.readContract({
719
+ address: addresses.balanceSheet,
720
+ abi: ABI.BalanceSheet,
721
+ functionName: 'availableBalanceOf',
722
+ args: [this.pool.id.raw, this.id.raw, asset.address, BigInt(asset.assetTokenId ?? 0n)],
723
+ }),
724
+ client.readContract({
725
+ address: addresses.spoke,
726
+ abi: ABI.Spoke,
727
+ functionName: 'pricePoolPerAsset',
728
+ args: [this.pool.id.raw, this.id.raw, asset.id.raw, false],
729
+ }),
730
+ ]);
731
+ console.log('addresses.spoke', addresses.spoke);
732
+ console.log('priceBn', priceBn, this.pool.id.raw, this.id.raw, asset.id.raw);
733
+ const amount = new Balance(amountBn, asset.decimals);
734
+ const price = new Price(priceBn);
735
+ const value = Balance.fromFloat(amount.toDecimal().mul(price.toDecimal()), poolCurrency.decimals);
736
+ return {
737
+ amount,
738
+ value,
739
+ price,
740
+ };
741
+ }).pipe(repeatOnEvents(this._root, {
742
+ address: [addresses.balanceSheet, addresses.spoke],
743
+ abi: [ABI.ShareClassManager, ABI.Spoke],
744
+ eventName: ['NoteDeposit', 'Deposit', 'Withdraw', 'UpdateAssetPrice'],
745
+ filter: (events) => {
746
+ return events.some((event) => event.args.scId === this.id.raw &&
747
+ // UpdateAssetPrice event
748
+ (event.args.assetId === asset.id.raw ||
749
+ // NoteDeposit, Deposit, Withdraw events
750
+ event.args.asset?.toLowerCase() === asset.address?.toLowerCase()));
751
+ },
752
+ }, chainId)))));
685
753
  }
686
754
  /** @internal */
687
755
  _allVaults() {
@@ -761,23 +829,20 @@ export class ShareClass extends Entity {
761
829
  }
762
830
  /** @internal */
763
831
  _epoch(assetId) {
764
- return this._query(['epoch', assetId.toString()], () => this._root._protocolAddresses(this.pool.chainId).pipe(switchMap(({ shareClassManager, hubRegistry }) => defer(async () => {
832
+ return this._query(['epoch', assetId.toString()], () => combineLatest([
833
+ this._root._protocolAddresses(this.pool.chainId),
834
+ this.pool.currency(),
835
+ this._root.assetDecimals(assetId, this.pool.chainId),
836
+ ]).pipe(switchMap(([{ shareClassManager }, poolCurrency, assetDecimals]) => defer(async () => {
765
837
  const scm = getContract({
766
838
  address: shareClassManager,
767
839
  abi: ABI.ShareClassManager,
768
840
  client: this._root.getClient(this.pool.chainId),
769
841
  });
770
- const [epoch, pendingDeposit, pendingRedeem, assetDecimals] = await Promise.all([
842
+ const [epoch, pendingDeposit, pendingRedeem] = await Promise.all([
771
843
  scm.read.epochId([this.id.raw, assetId.raw]),
772
844
  scm.read.pendingDeposit([this.id.raw, assetId.raw]),
773
845
  scm.read.pendingRedeem([this.id.raw, assetId.raw]),
774
- this._root.getClient(this.pool.chainId).readContract({
775
- address: hubRegistry,
776
- // Use inline ABI because of function overload
777
- abi: parseAbi(['function decimals(uint256) view returns (uint8)']),
778
- functionName: 'decimals',
779
- args: [assetId.raw],
780
- }),
781
846
  ]);
782
847
  const depositEpoch = epoch[0];
783
848
  const redeemEpoch = epoch[1];
@@ -794,12 +859,10 @@ export class ShareClass extends Entity {
794
859
  redeemEpoch,
795
860
  issueEpoch,
796
861
  revokeEpoch,
797
- // TODO: Replace with assetDecimals()
798
862
  pendingDeposit: new Balance(pendingDeposit, assetDecimals),
799
- // TODO: Replace with assetDecimals()
800
- pendingRedeem: new Balance(pendingRedeem, 18),
863
+ pendingRedeem: new Balance(pendingRedeem, poolCurrency.decimals),
801
864
  approvedDeposit: new Balance(approvedDeposit, assetDecimals),
802
- approvedRedeem: new Balance(approvedRedeem, 18),
865
+ approvedRedeem: new Balance(approvedRedeem, poolCurrency.decimals),
803
866
  };
804
867
  }).pipe(repeatOnEvents(this._root, {
805
868
  address: shareClassManager,
@@ -824,19 +887,20 @@ export class ShareClass extends Entity {
824
887
  /** @internal */
825
888
  _updateContract(chainId, target, payload) {
826
889
  const self = this;
827
- return this._transact(async function* ({ walletClient, publicClient }) {
828
- const id = await self._root.id(chainId);
829
- const [{ hub }, estimate] = await Promise.all([
890
+ return this._transact(async function* (ctx) {
891
+ const [id, { hub }] = await Promise.all([
892
+ self._root.id(chainId),
830
893
  self._root._protocolAddresses(self.pool.chainId),
831
- self._root._estimate(self.pool.chainId, { centId: id }),
832
894
  ]);
833
- yield* doTransaction('Update contract', publicClient, () => walletClient.writeContract({
834
- address: hub,
835
- abi: ABI.Hub,
836
- functionName: 'updateContract',
837
- args: [self.pool.id.raw, self.id.raw, id, addressToBytes32(target), payload, 0n],
838
- value: estimate,
839
- }));
895
+ yield* wrapTransaction('Update contract', ctx, {
896
+ contract: hub,
897
+ data: encodeFunctionData({
898
+ abi: ABI.Hub,
899
+ functionName: 'updateContract',
900
+ args: [self.pool.id.raw, self.id.raw, id, addressToBytes32(target), payload, 0n],
901
+ }),
902
+ messages: { [id]: [MessageType.UpdateContract] },
903
+ });
840
904
  }, this.pool.chainId);
841
905
  }
842
906
  /** @internal */