@ember-finance/sdk 2.31.0 → 2.34.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.
@@ -62,6 +62,12 @@ export declare class SuiAtomicLiquidityReader {
62
62
  private readTableValue;
63
63
  /** Rate value (1e9) of any vault object (ALV or source EmberVault). */
64
64
  private rateValue;
65
+ /**
66
+ * Platform fee a collection would settle at `atMs` (epoch ms), in collateral base units.
67
+ * @param vaultId The id of the atomic vault
68
+ * @param atMs Time to project the fee to
69
+ */
70
+ getPlatformFeeDue(vaultId: string, atMs: bigint): Promise<bigint>;
65
71
  /** Per-source-share EmberShare config from the `ember_share_configs` table. */
66
72
  getEmberShareConfig(vaultId: string, sourceVaultId: string): Promise<{
67
73
  whitelisted: boolean;
@@ -131,6 +131,27 @@ export class SuiAtomicLiquidityReader {
131
131
  const fields = unwrapMoveFields(res.object?.json);
132
132
  return toBig(unwrapMoveFields(fields?.rate)?.value);
133
133
  }
134
+ /**
135
+ * Platform fee a collection would settle at `atMs` (epoch ms), in collateral base units.
136
+ * @param vaultId The id of the atomic vault
137
+ * @param atMs Time to project the fee to
138
+ */
139
+ async getPlatformFeeDue(vaultId, atMs) {
140
+ // collect_platform_fee charges the time since last_charged_at before settling:
141
+ // accrued + tvl' * fee_percentage * elapsed_ms / 31_536_000e12, where tvl' is
142
+ // TVL (div(total_shares, rate)) net of the already-accrued fee.
143
+ const fields = await this.vaultFields(vaultId);
144
+ const fee = unwrapMoveFields(fields.fee) ?? {};
145
+ const accrued = toBig(fee.accrued);
146
+ const lastChargedAt = toBig(fee.last_charged_at);
147
+ const feePercentage = toBig(fields.fee_percentage);
148
+ if (atMs <= lastChargedAt || feePercentage === 0n)
149
+ return accrued;
150
+ const tvl = divFloor(toBig(fields.total_shares), toBig(unwrapMoveFields(fields.rate)?.value));
151
+ const base = tvl > accrued ? tvl - accrued : 0n;
152
+ return (accrued +
153
+ (base * feePercentage * (atMs - lastChargedAt)) / 31536000000000000000n);
154
+ }
134
155
  /** Per-source-share EmberShare config from the `ember_share_configs` table. */
135
156
  async getEmberShareConfig(vaultId, sourceVaultId) {
136
157
  const fields = await this.vaultFields(vaultId);
@@ -139,7 +139,8 @@ export declare class OperatorCalls extends OnChainCalls {
139
139
  */
140
140
  atomicProcessWithdrawalRequestsUpToTimestamp(vaultId: string, timestamp: NumStr, options?: IVaultOptionalParams & SuiTransactionBlockResponseOptions): Promise<OnChainCallResponse>;
141
141
  /**
142
- * Collect the platform fee of an atomic vault
142
+ * Collect the platform fee of an atomic vault. On a strategy vault, pulls any
143
+ * shortfall from the strategy first, in the same transaction.
143
144
  * @param vaultId The id of the atomic vault
144
145
  * @param loss The reported loss amount
145
146
  * @param options Optional tx execution params
@@ -244,6 +245,16 @@ export declare class OperatorCalls extends OnChainCalls {
244
245
  * @returns OnChainCallResponse
245
246
  */
246
247
  atomicDepositFromSubAccount(vaultId: string, tokenId: string, subAccount: string, options?: IVaultOptionalParams & SuiTransactionBlockResponseOptions): Promise<OnChainCallResponse>;
248
+ /**
249
+ * Deposit collateral back into an atomic vault from its accumulator
250
+ * (address) balance, where `send_funds` transfers land
251
+ * @param vaultId The id of the atomic vault
252
+ * @param amount The amount to redeem from the vault's accumulator balance
253
+ * @param subAccount The sub account address
254
+ * @param options Optional tx execution params
255
+ * @returns OnChainCallResponse
256
+ */
257
+ atomicDepositFromSubAccountV2(vaultId: string, amount: NumStr, subAccount: string, options?: IVaultOptionalParams & SuiTransactionBlockResponseOptions): Promise<OnChainCallResponse>;
247
258
  /**
248
259
  * Redeem collected ember shares from an atomic vault against a source vault
249
260
  * @param vaultId The id of the atomic vault
@@ -252,7 +252,8 @@ export class OperatorCalls extends OnChainCalls {
252
252
  return this.execCall(txb, options);
253
253
  }
254
254
  /**
255
- * Collect the platform fee of an atomic vault
255
+ * Collect the platform fee of an atomic vault. On a strategy vault, pulls any
256
+ * shortfall from the strategy first, in the same transaction.
256
257
  * @param vaultId The id of the atomic vault
257
258
  * @param loss The reported loss amount
258
259
  * @param options Optional tx execution params
@@ -260,6 +261,24 @@ export class OperatorCalls extends OnChainCalls {
260
261
  */
261
262
  async atomicCollectPlatformFee(vaultId, loss, options) {
262
263
  const txb = options?.txBlock || new TransactionBlock();
264
+ // Unlike EVM's collectPlatformFee, the Sui collection pays from idle balance only
265
+ // and aborts when it is short, so the strategy is drawn on first. The fee keeps
266
+ // accruing until the tx lands, so it is projected a few minutes ahead; whatever
267
+ // is pulled beyond the payout stays idle in the vault.
268
+ const reader = new SuiAtomicLiquidityReader(this.suiClient);
269
+ const target = await reader.getStrategyTarget(vaultId);
270
+ if (target.strategy === "suilend") {
271
+ const landsBy = BigInt(Date.now() + 10 * 60 * 1000);
272
+ const due = await reader.getPlatformFeeDue(vaultId, landsBy);
273
+ const payout = due > BigInt(loss) ? due - BigInt(loss) : 0n;
274
+ const shortfall = payout - target.idleCollateral;
275
+ if (shortfall > 0n) {
276
+ const withdraw = isSuiCoinType(target.collateralType)
277
+ ? this.txBuilder.atomicWithdrawFromSuilendStrategySui.bind(this.txBuilder)
278
+ : this.txBuilder.atomicWithdrawFromSuilendStrategy.bind(this.txBuilder);
279
+ withdraw(vaultId, target.suilendPoolType, target.suilendMarketId, shortfall.toString(), { ...options, txBlock: txb });
280
+ }
281
+ }
263
282
  this.txBuilder.atomicCollectPlatformFee(vaultId, loss, {
264
283
  ...options,
265
284
  txBlock: txb
@@ -405,6 +424,23 @@ export class OperatorCalls extends OnChainCalls {
405
424
  });
406
425
  return this.execCall(txb, options);
407
426
  }
427
+ /**
428
+ * Deposit collateral back into an atomic vault from its accumulator
429
+ * (address) balance, where `send_funds` transfers land
430
+ * @param vaultId The id of the atomic vault
431
+ * @param amount The amount to redeem from the vault's accumulator balance
432
+ * @param subAccount The sub account address
433
+ * @param options Optional tx execution params
434
+ * @returns OnChainCallResponse
435
+ */
436
+ async atomicDepositFromSubAccountV2(vaultId, amount, subAccount, options) {
437
+ const txb = options?.txBlock || new TransactionBlock();
438
+ this.txBuilder.atomicDepositFromSubAccountV2(vaultId, amount, subAccount, {
439
+ ...options,
440
+ txBlock: txb
441
+ });
442
+ return this.execCall(txb, options);
443
+ }
408
444
  /**
409
445
  * Redeem collected ember shares from an atomic vault against a source vault
410
446
  * @param vaultId The id of the atomic vault
@@ -758,6 +758,16 @@ export declare class TxBuilder {
758
758
  * @returns TransactionBlock
759
759
  */
760
760
  atomicDepositFromSubAccount(vaultId: string, tokenId: string, subAccount: string, options?: ITxBuilderOptionalParams): TransactionBlock;
761
+ /**
762
+ * Deposits collateral back into an atomic vault from its accumulator
763
+ * (address) balance, where `send_funds` transfers land
764
+ * @param vaultId Id of the atomic vault
765
+ * @param amount Amount to redeem from the vault's accumulator balance
766
+ * @param subAccount Sub account address
767
+ * @param options Optional tx building params
768
+ * @returns TransactionBlock
769
+ */
770
+ atomicDepositFromSubAccountV2(vaultId: string, amount: NumStr, subAccount: string, options?: ITxBuilderOptionalParams): TransactionBlock;
761
771
  /**
762
772
  * Collects the platform fee of an atomic vault
763
773
  * @param vaultId Id of the atomic vault
@@ -2184,6 +2184,33 @@ export class TxBuilder {
2184
2184
  txb.setSenderIfNotSet(options.sender);
2185
2185
  return txb;
2186
2186
  }
2187
+ /**
2188
+ * Deposits collateral back into an atomic vault from its accumulator
2189
+ * (address) balance, where `send_funds` transfers land
2190
+ * @param vaultId Id of the atomic vault
2191
+ * @param amount Amount to redeem from the vault's accumulator balance
2192
+ * @param subAccount Sub account address
2193
+ * @param options Optional tx building params
2194
+ * @returns TransactionBlock
2195
+ */
2196
+ atomicDepositFromSubAccountV2(vaultId, amount, subAccount, options) {
2197
+ const txb = options?.txBlock || new TransactionBlock();
2198
+ txb.moveCall({
2199
+ arguments: [
2200
+ txb.object(vaultId),
2201
+ txb.object(this.parser.getProtocolConfig()),
2202
+ txb.pure.u64(amount),
2203
+ txb.pure.address(subAccount)
2204
+ ],
2205
+ typeArguments: [this.parser.getAtomicCollateralType(vaultId)],
2206
+ target: `${this.parser.getPackageId()}::gateway::atomic_deposit_from_sub_account_v2`
2207
+ });
2208
+ if (options?.gasBudget)
2209
+ txb.setGasBudget(options.gasBudget);
2210
+ if (options?.sender)
2211
+ txb.setSenderIfNotSet(options.sender);
2212
+ return txb;
2213
+ }
2187
2214
  /**
2188
2215
  * Collects the platform fee of an atomic vault
2189
2216
  * @param vaultId Id of the atomic vault
@@ -5,6 +5,7 @@ import { OnChainCallResponse, Signer, SuiAddress, SuiTransactionBlockResponseOpt
5
5
  import { TransactionObjectArgument } from "@mysten/sui/transactions";
6
6
  export declare class UserCalls extends OnChainCalls {
7
7
  constructor(_network: string, _suiClient: SuiClient, _deployment: ISuiDeployment, _signer?: Signer, _walletAddress?: string, _isUIWallet?: boolean);
8
+ private walletCoin;
8
9
  /**
9
10
  * Deposit asset into a vault
10
11
  * @param vaultId The id of the vault to deposit into
@@ -1,12 +1,23 @@
1
1
  import { OnChainCalls } from "./onchain-calls.js";
2
2
  import { TransactionBlock, bigNumber, CoinUtils, SUI_NATIVE_BASE } from "@firefly-exchange/library-sui";
3
3
  import { Vault } from "./vault.js";
4
+ import { coinWithBalance } from "@mysten/sui/transactions";
4
5
  import { SuiAtomicLiquidityReader } from "./atomic-liquidity.js";
5
6
  import { isSuiCoinType } from "../utils/common.js";
6
7
  export class UserCalls extends OnChainCalls {
7
8
  constructor(_network, _suiClient, _deployment, _signer, _walletAddress, _isUIWallet) {
8
9
  super(_network, _suiClient, _deployment, _signer, _walletAddress, _isUIWallet);
9
10
  }
11
+ walletCoin(txb, coinType, amount) {
12
+ // `send_funds` credits the wallet's address balance, which coin
13
+ // enumeration cannot see. The intent draws on coin objects and that
14
+ // balance together, resolved when the tx is built.
15
+ txb.setSenderIfNotSet(this.walletAddress);
16
+ return txb.add(coinWithBalance({
17
+ type: coinType,
18
+ balance: BigInt(bigNumber(amount).toFixed(0))
19
+ }));
20
+ }
10
21
  /**
11
22
  * Deposit asset into a vault
12
23
  * @param vaultId The id of the vault to deposit into
@@ -21,7 +32,7 @@ export class UserCalls extends OnChainCalls {
21
32
  let merged;
22
33
  if (!coinId) {
23
34
  const coinType = this.parser.getDepositCoinType(vaultId);
24
- [split, merged] = await CoinUtils.createCoinWithBalance(this.suiClient, txb, bigNumber(amount).toFixed(0), coinType, this.walletAddress);
35
+ split = this.walletCoin(txb, coinType, bigNumber(amount).toFixed(0));
25
36
  coinId = split;
26
37
  }
27
38
  else {
@@ -71,7 +82,7 @@ export class UserCalls extends OnChainCalls {
71
82
  let merged;
72
83
  if (!coinId) {
73
84
  const coinType = this.parser.getDepositCoinType(vaultId);
74
- [split, merged] = await CoinUtils.createCoinWithBalance(this.suiClient, txb, assetAmountRequired, coinType, this.walletAddress);
85
+ split = this.walletCoin(txb, coinType, assetAmountRequired);
75
86
  coinId = split;
76
87
  }
77
88
  else {
@@ -98,7 +109,7 @@ export class UserCalls extends OnChainCalls {
98
109
  let split;
99
110
  if (!coinId) {
100
111
  const coinType = this.parser.getReceiptCoinType(vaultId);
101
- [split, merged] = await CoinUtils.createCoinWithBalance(this.suiClient, txb, bigNumber(amountOfShares).toFixed(0), coinType, this.walletAddress);
112
+ split = this.walletCoin(txb, coinType, bigNumber(amountOfShares).toFixed(0));
102
113
  coinId = split;
103
114
  }
104
115
  else {
@@ -144,7 +155,7 @@ export class UserCalls extends OnChainCalls {
144
155
  let merged;
145
156
  if (!coinId) {
146
157
  const coinType = this.parser.getAtomicCollateralType(vaultId);
147
- [split, merged] = await CoinUtils.createCoinWithBalance(this.suiClient, txb, bigNumber(amount).toFixed(0), coinType, this.walletAddress);
158
+ split = this.walletCoin(txb, coinType, bigNumber(amount).toFixed(0));
148
159
  coinId = split;
149
160
  }
150
161
  else {
@@ -249,7 +260,7 @@ export class UserCalls extends OnChainCalls {
249
260
  let split;
250
261
  let merged;
251
262
  if (!coinId) {
252
- [split, merged] = await CoinUtils.createCoinWithBalance(this.suiClient, txb, bigNumber(amount).toFixed(0), sourceReceiptType, this.walletAddress);
263
+ split = this.walletCoin(txb, sourceReceiptType, bigNumber(amount).toFixed(0));
253
264
  coinId = split;
254
265
  }
255
266
  else {
@@ -298,7 +309,7 @@ export class UserCalls extends OnChainCalls {
298
309
  let split;
299
310
  let merged;
300
311
  if (!coinId) {
301
- [split, merged] = await CoinUtils.createCoinWithBalance(this.suiClient, txb, bigNumber(amount).toFixed(0), sourceReceiptType, this.walletAddress);
312
+ split = this.walletCoin(txb, sourceReceiptType, bigNumber(amount).toFixed(0));
302
313
  coinId = split;
303
314
  }
304
315
  else {
@@ -343,7 +354,7 @@ export class UserCalls extends OnChainCalls {
343
354
  let split;
344
355
  let merged;
345
356
  if (!coinId) {
346
- [split, merged] = await CoinUtils.createCoinWithBalance(this.suiClient, txb, bigNumber(amount).toFixed(0), sourceReceiptType, this.walletAddress);
357
+ split = this.walletCoin(txb, sourceReceiptType, bigNumber(amount).toFixed(0));
347
358
  coinId = split;
348
359
  }
349
360
  else {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ember-finance/sdk",
3
3
  "description": "Ember Protocol SDK",
4
- "version": "2.31.0",
4
+ "version": "2.34.0",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
7
7
  "main": "./dist/index.js",