@ember-finance/sdk 2.25.0 → 2.27.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.
- package/dist/src/atomic/instant-withdraw.d.ts +1 -2
- package/dist/src/sui-vaults/on-chain-calls/atomic-liquidity.d.ts +23 -0
- package/dist/src/sui-vaults/on-chain-calls/atomic-liquidity.js +53 -19
- package/dist/src/sui-vaults/on-chain-calls/user.d.ts +4 -2
- package/dist/src/sui-vaults/on-chain-calls/user.js +37 -7
- package/package.json +1 -1
|
@@ -39,13 +39,12 @@ export interface EmberShareSwapConfig {
|
|
|
39
39
|
/**
|
|
40
40
|
* Minimum gross collateral value per swap (collateral units). A swap with
|
|
41
41
|
* `gross < minSwapGross` reverts `SwapAmountTooSmall`. 0/undefined disables the floor.
|
|
42
|
-
* EVM-only (no Sui equivalent yet) — omit on Sui.
|
|
43
42
|
*/
|
|
44
43
|
minSwapGross?: bigint;
|
|
45
44
|
/**
|
|
46
45
|
* TVL-independent absolute ceiling on this source share's post-swap held value
|
|
47
46
|
* (collateral units). A swap pushing `currentHeldValue + gross` above it reverts
|
|
48
|
-
* `EmberShareHeldValueCapExceeded`. 0/undefined disables it.
|
|
47
|
+
* `EmberShareHeldValueCapExceeded`. 0/undefined disables it.
|
|
49
48
|
*/
|
|
50
49
|
maxHeldValueAbsolute?: bigint;
|
|
51
50
|
}
|
|
@@ -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
|
*
|
|
@@ -39,9 +51,20 @@ export declare class SuiAtomicLiquidityReader {
|
|
|
39
51
|
epochStartedAt: bigint;
|
|
40
52
|
epochUsedAmount: bigint;
|
|
41
53
|
feeMultiplierCurve: FeeMultiplierTier[];
|
|
54
|
+
minSwapGross: bigint;
|
|
55
|
+
maxHeldValueAbsolute: bigint;
|
|
42
56
|
} | null>;
|
|
43
57
|
/** Caller's ALV (non-transferable) share balance from the `shares` table. */
|
|
44
58
|
shareOf(vaultId: string, account: string): Promise<bigint>;
|
|
59
|
+
/**
|
|
60
|
+
* The strategy an atomic vault is on, and for Suilend the market it supplies to.
|
|
61
|
+
* Plain object reads, no simulation.
|
|
62
|
+
* @param vaultId Object id of the atomic vault
|
|
63
|
+
* @returns The strategy, the vault's collateral coin type and idle collateral, and
|
|
64
|
+
* for Suilend the market id and its pool type (`P`)
|
|
65
|
+
*/
|
|
66
|
+
getStrategyTarget(vaultId: string): Promise<SuiAtomicStrategyTarget>;
|
|
67
|
+
private readStrategyTarget;
|
|
45
68
|
/**
|
|
46
69
|
* Where an atomic vault's collateral sits: idle in the vault, or supplied to its
|
|
47
70
|
* strategy.
|
|
@@ -99,7 +99,9 @@ export class SuiAtomicLiquidityReader {
|
|
|
99
99
|
maxUtilizationPercentage: toBig(cfg.max_utilization_percentage),
|
|
100
100
|
epochStartedAt: toBig(cfg.epoch_started_at),
|
|
101
101
|
epochUsedAmount: toBig(cfg.epoch_used_amount),
|
|
102
|
-
feeMultiplierCurve: curve
|
|
102
|
+
feeMultiplierCurve: curve,
|
|
103
|
+
minSwapGross: toBig(cfg.min_swap_gross),
|
|
104
|
+
maxHeldValueAbsolute: toBig(cfg.max_held_value_absolute)
|
|
103
105
|
};
|
|
104
106
|
}
|
|
105
107
|
}
|
|
@@ -112,14 +114,18 @@ export class SuiAtomicLiquidityReader {
|
|
|
112
114
|
return toBig(await this.readTableValue(tableId, "address", account));
|
|
113
115
|
}
|
|
114
116
|
/**
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
+
* The strategy an atomic vault is on, and for Suilend the market it supplies to.
|
|
118
|
+
* Plain object reads, no simulation.
|
|
117
119
|
* @param vaultId Object id of the atomic vault
|
|
118
|
-
* @returns The
|
|
119
|
-
*
|
|
120
|
-
* the strategy is Suilend.
|
|
120
|
+
* @returns The strategy, the vault's collateral coin type and idle collateral, and
|
|
121
|
+
* for Suilend the market id and its pool type (`P`)
|
|
121
122
|
*/
|
|
122
|
-
async
|
|
123
|
+
async getStrategyTarget(vaultId) {
|
|
124
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
125
|
+
const { packageId, ...target } = await this.readStrategyTarget(vaultId);
|
|
126
|
+
return target;
|
|
127
|
+
}
|
|
128
|
+
async readStrategyTarget(vaultId) {
|
|
123
129
|
const res = await this.suiClient.core.getObject({
|
|
124
130
|
objectId: vaultId,
|
|
125
131
|
include: { json: true }
|
|
@@ -131,16 +137,14 @@ export class SuiAtomicLiquidityReader {
|
|
|
131
137
|
if (!strategy) {
|
|
132
138
|
throw new Error(`Unknown strategy kind ${fields.strategy_kind} on ${vaultId}`);
|
|
133
139
|
}
|
|
140
|
+
const vaultType = res.object.type;
|
|
141
|
+
// The package that defines the vault type is the one with the views.
|
|
142
|
+
const packageId = vaultType.split("::")[0];
|
|
143
|
+
const collateralType = vaultType.slice(vaultType.indexOf("<") + 1, -1);
|
|
134
144
|
const idleCollateral = toBig(fields.balance);
|
|
135
145
|
if (strategy === "none") {
|
|
136
|
-
return { strategy, idleCollateral,
|
|
146
|
+
return { strategy, collateralType, idleCollateral, packageId };
|
|
137
147
|
}
|
|
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
148
|
const marketField = await this.suiClient.core.getDynamicField({
|
|
145
149
|
parentId: vaultId,
|
|
146
150
|
name: {
|
|
@@ -154,13 +158,41 @@ export class SuiAtomicLiquidityReader {
|
|
|
154
158
|
const suilendMarketId = bcs.Address.parse(marketField.dynamicField.value.bcs);
|
|
155
159
|
const market = await this.suiClient.core.getObject({ objectId: suilendMarketId });
|
|
156
160
|
const marketType = market.object.type;
|
|
157
|
-
|
|
161
|
+
return {
|
|
162
|
+
strategy,
|
|
163
|
+
collateralType,
|
|
164
|
+
idleCollateral,
|
|
165
|
+
packageId,
|
|
166
|
+
suilendMarketId,
|
|
167
|
+
suilendPoolType: marketType.slice(marketType.indexOf("<") + 1, -1)
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Where an atomic vault's collateral sits: idle in the vault, or supplied to its
|
|
172
|
+
* strategy.
|
|
173
|
+
* @param vaultId Object id of the atomic vault
|
|
174
|
+
* @returns The active strategy, idle collateral and strategy assets, in collateral
|
|
175
|
+
* base units. `suilendMarketId` and `suilendPoolType` (the market's `P`) are set when
|
|
176
|
+
* the strategy is Suilend.
|
|
177
|
+
*/
|
|
178
|
+
async getStrategyState(vaultId) {
|
|
179
|
+
const target = await this.readStrategyTarget(vaultId);
|
|
180
|
+
const { strategy, idleCollateral, suilendMarketId, suilendPoolType, packageId } = target;
|
|
181
|
+
if (strategy === "none") {
|
|
182
|
+
return { strategy, idleCollateral, strategyAssets: 0n };
|
|
183
|
+
}
|
|
184
|
+
// Suilend values the position against the reserve's current interest, so it is
|
|
185
|
+
// simulated rather than read from fields.
|
|
158
186
|
const tx = new Transaction();
|
|
159
187
|
tx.setSender(SUI_ZERO_ADDRESS);
|
|
160
188
|
tx.moveCall({
|
|
161
189
|
target: `${packageId}::atomic_liquidity_vault::get_suilend_strategy_assets`,
|
|
162
|
-
typeArguments: [collateralType,
|
|
163
|
-
arguments: [
|
|
190
|
+
typeArguments: [target.collateralType, suilendPoolType],
|
|
191
|
+
arguments: [
|
|
192
|
+
tx.object(vaultId),
|
|
193
|
+
tx.object(suilendMarketId),
|
|
194
|
+
tx.object.clock()
|
|
195
|
+
]
|
|
164
196
|
});
|
|
165
197
|
const sim = await this.suiClient.core.simulateTransaction({
|
|
166
198
|
transaction: tx,
|
|
@@ -176,7 +208,7 @@ export class SuiAtomicLiquidityReader {
|
|
|
176
208
|
idleCollateral,
|
|
177
209
|
strategyAssets: BigInt(bcs.u64().parse(returned)),
|
|
178
210
|
suilendMarketId,
|
|
179
|
-
suilendPoolType
|
|
211
|
+
suilendPoolType
|
|
180
212
|
};
|
|
181
213
|
}
|
|
182
214
|
/**
|
|
@@ -218,7 +250,9 @@ export class SuiAtomicLiquidityReader {
|
|
|
218
250
|
feePercentage: config.feePercentage,
|
|
219
251
|
holdbackPercentage: config.holdbackPercentage,
|
|
220
252
|
maxUtilizationPercentage: config.maxUtilizationPercentage,
|
|
221
|
-
feeMultiplierCurve: config.feeMultiplierCurve
|
|
253
|
+
feeMultiplierCurve: config.feeMultiplierCurve,
|
|
254
|
+
minSwapGross: config.minSwapGross,
|
|
255
|
+
maxHeldValueAbsolute: config.maxHeldValueAbsolute
|
|
222
256
|
},
|
|
223
257
|
feeWaived: feeWaived.includes(owner),
|
|
224
258
|
holdbackWaived: holdbackWaived.includes(owner)
|
|
@@ -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
|
}
|