@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`
|
|
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
|
-
*
|
|
116
|
-
*
|
|
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
|
|
119
|
-
*
|
|
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
|
|
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,
|
|
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
|
-
|
|
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,
|
|
162
|
-
arguments: [
|
|
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
|
|
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
|
|
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.
|
|
198
|
-
|
|
199
|
-
|
|
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
|
-
|
|
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
|
}
|