@ember-finance/sdk 2.24.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,18 +54,29 @@ 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.
48
69
  * @param vaultId Object id of the atomic vault
49
70
  * @returns The active strategy, idle collateral and strategy assets, in collateral
50
- * base units. `suilendMarketId` is set when the strategy is Suilend.
71
+ * base units. `suilendMarketId` and `suilendPoolType` (the market's `P`) are set when
72
+ * the strategy is Suilend.
51
73
  */
52
74
  getStrategyState(vaultId: string): Promise<{
53
75
  strategy: SuiAtomicStrategy;
54
76
  idleCollateral: bigint;
55
77
  strategyAssets: bigint;
56
78
  suilendMarketId?: string;
79
+ suilendPoolType?: string;
57
80
  }>;
58
81
  /**
59
82
  * Live quote for the EmberShare swap-with-holdback "instant withdraw" on Sui.
@@ -112,13 +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` is set when 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`)
120
120
  */
121
- 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) {
122
127
  const res = await this.suiClient.core.getObject({
123
128
  objectId: vaultId,
124
129
  include: { json: true }
@@ -130,16 +135,14 @@ export class SuiAtomicLiquidityReader {
130
135
  if (!strategy) {
131
136
  throw new Error(`Unknown strategy kind ${fields.strategy_kind} on ${vaultId}`);
132
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);
133
142
  const idleCollateral = toBig(fields.balance);
134
143
  if (strategy === "none") {
135
- return { strategy, idleCollateral, strategyAssets: 0n };
144
+ return { strategy, collateralType, idleCollateral, packageId };
136
145
  }
137
- // The vault's type names the package that defines it, which is the one with
138
- // the views. The position is valued by Suilend against the reserve's current
139
- // interest, so it is simulated rather than read from fields.
140
- const vaultType = res.object.type;
141
- const collateralType = vaultType.slice(vaultType.indexOf("<") + 1, -1);
142
- const packageId = vaultType.split("::")[0];
143
146
  const marketField = await this.suiClient.core.getDynamicField({
144
147
  parentId: vaultId,
145
148
  name: {
@@ -153,13 +156,41 @@ export class SuiAtomicLiquidityReader {
153
156
  const suilendMarketId = bcs.Address.parse(marketField.dynamicField.value.bcs);
154
157
  const market = await this.suiClient.core.getObject({ objectId: suilendMarketId });
155
158
  const marketType = market.object.type;
156
- 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.
157
184
  const tx = new Transaction();
158
185
  tx.setSender(SUI_ZERO_ADDRESS);
159
186
  tx.moveCall({
160
187
  target: `${packageId}::atomic_liquidity_vault::get_suilend_strategy_assets`,
161
- typeArguments: [collateralType, poolType],
162
- 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
+ ]
163
194
  });
164
195
  const sim = await this.suiClient.core.simulateTransaction({
165
196
  transaction: tx,
@@ -174,7 +205,8 @@ export class SuiAtomicLiquidityReader {
174
205
  strategy,
175
206
  idleCollateral,
176
207
  strategyAssets: BigInt(bcs.u64().parse(returned)),
177
- suilendMarketId
208
+ suilendMarketId,
209
+ suilendPoolType
178
210
  };
179
211
  }
180
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.24.0",
4
+ "version": "2.26.0",
5
5
  "type": "module",
6
6
  "module": "./dist/index.js",
7
7
  "main": "./dist/index.js",