@ember-finance/sdk 2.25.0 → 2.26.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.
@@ -2,6 +2,18 @@ import { BaseClient as SuiClient } from "@mysten/sui/client";
2
2
  import { FeeMultiplierTier, InstantWithdrawQuote } from "../../atomic/instant-withdraw.js";
3
3
  /** Strategy an atomic vault's idle collateral can be supplied to. */
4
4
  export type SuiAtomicStrategy = "none" | "suilend";
5
+ /** An atomic vault's strategy and, for Suilend, the market it supplies to. */
6
+ export interface SuiAtomicStrategyTarget {
7
+ strategy: SuiAtomicStrategy;
8
+ /** The vault's collateral coin type (`T`). */
9
+ collateralType: string;
10
+ /** Collateral held in the vault, in base units. */
11
+ idleCollateral: bigint;
12
+ /** Suilend only: the LendingMarket object id. */
13
+ suilendMarketId?: string;
14
+ /** Suilend only: the market's pool type (`P`). */
15
+ suilendPoolType?: string;
16
+ }
5
17
  /**
6
18
  * Read-only views + instant-withdraw quotes for the Sui Atomic Liquidity Vault (BET-4111).
7
19
  *
@@ -42,6 +54,15 @@ export declare class SuiAtomicLiquidityReader {
42
54
  } | null>;
43
55
  /** Caller's ALV (non-transferable) share balance from the `shares` table. */
44
56
  shareOf(vaultId: string, account: string): Promise<bigint>;
57
+ /**
58
+ * The strategy an atomic vault is on, and for Suilend the market it supplies to.
59
+ * Plain object reads, no simulation.
60
+ * @param vaultId Object id of the atomic vault
61
+ * @returns The strategy, the vault's collateral coin type and idle collateral, and
62
+ * for Suilend the market id and its pool type (`P`)
63
+ */
64
+ getStrategyTarget(vaultId: string): Promise<SuiAtomicStrategyTarget>;
65
+ private readStrategyTarget;
45
66
  /**
46
67
  * Where an atomic vault's collateral sits: idle in the vault, or supplied to its
47
68
  * strategy.
@@ -112,14 +112,18 @@ export class SuiAtomicLiquidityReader {
112
112
  return toBig(await this.readTableValue(tableId, "address", account));
113
113
  }
114
114
  /**
115
- * Where an atomic vault's collateral sits: idle in the vault, or supplied to its
116
- * strategy.
115
+ * The strategy an atomic vault is on, and for Suilend the market it supplies to.
116
+ * Plain object reads, no simulation.
117
117
  * @param vaultId Object id of the atomic vault
118
- * @returns The active strategy, idle collateral and strategy assets, in collateral
119
- * base units. `suilendMarketId` and `suilendPoolType` (the market's `P`) are set when
120
- * the strategy is Suilend.
118
+ * @returns The strategy, the vault's collateral coin type and idle collateral, and
119
+ * for Suilend the market id and its pool type (`P`)
121
120
  */
122
- async getStrategyState(vaultId) {
121
+ async getStrategyTarget(vaultId) {
122
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
123
+ const { packageId, ...target } = await this.readStrategyTarget(vaultId);
124
+ return target;
125
+ }
126
+ async readStrategyTarget(vaultId) {
123
127
  const res = await this.suiClient.core.getObject({
124
128
  objectId: vaultId,
125
129
  include: { json: true }
@@ -131,16 +135,14 @@ export class SuiAtomicLiquidityReader {
131
135
  if (!strategy) {
132
136
  throw new Error(`Unknown strategy kind ${fields.strategy_kind} on ${vaultId}`);
133
137
  }
138
+ const vaultType = res.object.type;
139
+ // The package that defines the vault type is the one with the views.
140
+ const packageId = vaultType.split("::")[0];
141
+ const collateralType = vaultType.slice(vaultType.indexOf("<") + 1, -1);
134
142
  const idleCollateral = toBig(fields.balance);
135
143
  if (strategy === "none") {
136
- return { strategy, idleCollateral, strategyAssets: 0n };
144
+ return { strategy, collateralType, idleCollateral, packageId };
137
145
  }
138
- // The vault's type names the package that defines it, which is the one with
139
- // the views. The position is valued by Suilend against the reserve's current
140
- // interest, so it is simulated rather than read from fields.
141
- const vaultType = res.object.type;
142
- const collateralType = vaultType.slice(vaultType.indexOf("<") + 1, -1);
143
- const packageId = vaultType.split("::")[0];
144
146
  const marketField = await this.suiClient.core.getDynamicField({
145
147
  parentId: vaultId,
146
148
  name: {
@@ -154,13 +156,41 @@ export class SuiAtomicLiquidityReader {
154
156
  const suilendMarketId = bcs.Address.parse(marketField.dynamicField.value.bcs);
155
157
  const market = await this.suiClient.core.getObject({ objectId: suilendMarketId });
156
158
  const marketType = market.object.type;
157
- const poolType = marketType.slice(marketType.indexOf("<") + 1, -1);
159
+ return {
160
+ strategy,
161
+ collateralType,
162
+ idleCollateral,
163
+ packageId,
164
+ suilendMarketId,
165
+ suilendPoolType: marketType.slice(marketType.indexOf("<") + 1, -1)
166
+ };
167
+ }
168
+ /**
169
+ * Where an atomic vault's collateral sits: idle in the vault, or supplied to its
170
+ * strategy.
171
+ * @param vaultId Object id of the atomic vault
172
+ * @returns The active strategy, idle collateral and strategy assets, in collateral
173
+ * base units. `suilendMarketId` and `suilendPoolType` (the market's `P`) are set when
174
+ * the strategy is Suilend.
175
+ */
176
+ async getStrategyState(vaultId) {
177
+ const target = await this.readStrategyTarget(vaultId);
178
+ const { strategy, idleCollateral, suilendMarketId, suilendPoolType, packageId } = target;
179
+ if (strategy === "none") {
180
+ return { strategy, idleCollateral, strategyAssets: 0n };
181
+ }
182
+ // Suilend values the position against the reserve's current interest, so it is
183
+ // simulated rather than read from fields.
158
184
  const tx = new Transaction();
159
185
  tx.setSender(SUI_ZERO_ADDRESS);
160
186
  tx.moveCall({
161
187
  target: `${packageId}::atomic_liquidity_vault::get_suilend_strategy_assets`,
162
- typeArguments: [collateralType, poolType],
163
- arguments: [tx.object(vaultId), tx.object(suilendMarketId), tx.object.clock()]
188
+ typeArguments: [target.collateralType, suilendPoolType],
189
+ arguments: [
190
+ tx.object(vaultId),
191
+ tx.object(suilendMarketId),
192
+ tx.object.clock()
193
+ ]
164
194
  });
165
195
  const sim = await this.suiClient.core.simulateTransaction({
166
196
  transaction: tx,
@@ -176,7 +206,7 @@ export class SuiAtomicLiquidityReader {
176
206
  idleCollateral,
177
207
  strategyAssets: BigInt(bcs.u64().parse(returned)),
178
208
  suilendMarketId,
179
- suilendPoolType: poolType
209
+ suilendPoolType
180
210
  };
181
211
  }
182
212
  /**
@@ -87,7 +87,8 @@ export declare class UserCalls extends OnChainCalls {
87
87
  */
88
88
  atomicCancelPendingWithdrawalRequest(vaultId: string, sequenceNumber: NumStr, options?: IVaultOptionalParams & SuiTransactionBlockResponseOptions): Promise<OnChainCallResponse>;
89
89
  /**
90
- * Instantly withdraw collateral from an atomic liquidity vault (no strategy)
90
+ * Instantly withdraw collateral from an atomic liquidity vault. Routes to the
91
+ * entry point for the vault's current strategy.
91
92
  * @param vaultId The id of the atomic vault
92
93
  * @param shares The number of shares to withdraw
93
94
  * @param options Optional tx execution params
@@ -109,7 +110,8 @@ export declare class UserCalls extends OnChainCalls {
109
110
  receiver?: SuiAddress;
110
111
  } & SuiTransactionBlockResponseOptions): Promise<OnChainCallResponse>;
111
112
  /**
112
- * Swap ember shares (source vault receipt coin) into an atomic vault
113
+ * Swap ember shares (source vault receipt coin) into an atomic vault. Routes to the
114
+ * entry point for the vault's current strategy.
113
115
  * @param vaultId The id of the atomic vault
114
116
  * @param sourceReceiptType The receipt coin type of the source vault (R)
115
117
  * @param sourceVaultId Object id of the source Vault<T, R>
@@ -1,6 +1,11 @@
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 { SuiAtomicLiquidityReader } from "./atomic-liquidity.js";
5
+ const isSuiCoinType = (coinType) => {
6
+ const [address, module, name] = coinType.split("::");
7
+ return module === "sui" && name === "SUI" && /^0x0*2$/.test(address);
8
+ };
4
9
  export class UserCalls extends OnChainCalls {
5
10
  constructor(_network, _suiClient, _deployment, _signer, _walletAddress, _isUIWallet) {
6
11
  super(_network, _suiClient, _deployment, _signer, _walletAddress, _isUIWallet);
@@ -186,7 +191,8 @@ export class UserCalls extends OnChainCalls {
186
191
  return this.execCall(txb, options);
187
192
  }
188
193
  /**
189
- * Instantly withdraw collateral from an atomic liquidity vault (no strategy)
194
+ * Instantly withdraw collateral from an atomic liquidity vault. Routes to the
195
+ * entry point for the vault's current strategy.
190
196
  * @param vaultId The id of the atomic vault
191
197
  * @param shares The number of shares to withdraw
192
198
  * @param options Optional tx execution params
@@ -194,10 +200,20 @@ export class UserCalls extends OnChainCalls {
194
200
  */
195
201
  async atomicInstantWithdraw(vaultId, shares, options) {
196
202
  const txb = options?.txBlock || new TransactionBlock();
197
- this.txBuilder.atomicInstantWithdraw(vaultId, shares, options?.receiver ?? null, {
198
- ...options,
199
- txBlock: txb
200
- });
203
+ const target = await new SuiAtomicLiquidityReader(this.suiClient).getStrategyTarget(vaultId);
204
+ // The plain entry point asserts the vault has no strategy (EStrategyMismatch);
205
+ // a strategy vault must be called with its market so liquidity can be pulled.
206
+ if (target.strategy === "suilend") {
207
+ if (isSuiCoinType(target.collateralType)) {
208
+ this.txBuilder.atomicInstantWithdrawWithSuilendSui(vaultId, target.suilendPoolType, target.suilendMarketId, shares, options?.receiver ?? this.walletAddress, { ...options, txBlock: txb });
209
+ }
210
+ else {
211
+ this.txBuilder.atomicInstantWithdrawWithSuilend(vaultId, target.suilendPoolType, target.suilendMarketId, shares, options?.receiver ?? null, { ...options, txBlock: txb });
212
+ }
213
+ }
214
+ else {
215
+ this.txBuilder.atomicInstantWithdraw(vaultId, shares, options?.receiver ?? null, { ...options, txBlock: txb });
216
+ }
201
217
  return this.execCall(txb, options);
202
218
  }
203
219
  /**
@@ -215,7 +231,8 @@ export class UserCalls extends OnChainCalls {
215
231
  return this.execCall(txb, options);
216
232
  }
217
233
  /**
218
- * Swap ember shares (source vault receipt coin) into an atomic vault
234
+ * Swap ember shares (source vault receipt coin) into an atomic vault. Routes to the
235
+ * entry point for the vault's current strategy.
219
236
  * @param vaultId The id of the atomic vault
220
237
  * @param sourceReceiptType The receipt coin type of the source vault (R)
221
238
  * @param sourceVaultId Object id of the source Vault<T, R>
@@ -242,7 +259,20 @@ export class UserCalls extends OnChainCalls {
242
259
  [split, merged] = await CoinUtils.validateCoinBalance(this.suiClient, txb, bigNumber(amount).toFixed(0), coinId);
243
260
  coinId = split;
244
261
  }
245
- this.txBuilder.atomicSwapEmberShares(vaultId, sourceReceiptType, sourceVaultId, coinId, options?.receiver ?? null, minNetOut, { ...options, txBlock: txb });
262
+ const target = await new SuiAtomicLiquidityReader(this.suiClient).getStrategyTarget(vaultId);
263
+ // The plain entry point asserts the vault has no strategy (EStrategyMismatch);
264
+ // a strategy vault must be called with its market so liquidity can be pulled.
265
+ if (target.strategy === "suilend") {
266
+ if (isSuiCoinType(target.collateralType)) {
267
+ this.txBuilder.atomicSwapEmberSharesWithSuilendSui(vaultId, sourceReceiptType, target.suilendPoolType, sourceVaultId, target.suilendMarketId, coinId, options?.receiver ?? this.walletAddress, minNetOut, { ...options, txBlock: txb });
268
+ }
269
+ else {
270
+ this.txBuilder.atomicSwapEmberSharesWithSuilend(vaultId, sourceReceiptType, target.suilendPoolType, sourceVaultId, target.suilendMarketId, coinId, options?.receiver ?? null, minNetOut, { ...options, txBlock: txb });
271
+ }
272
+ }
273
+ else {
274
+ this.txBuilder.atomicSwapEmberShares(vaultId, sourceReceiptType, sourceVaultId, coinId, options?.receiver ?? null, minNetOut, { ...options, txBlock: txb });
275
+ }
246
276
  if (merged) {
247
277
  txb.transferObjects([merged], this.walletAddress);
248
278
  }
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.25.0",
4
+ "version": "2.26.0",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
7
7
  "main": "./dist/index.js",