@ember-finance/sdk 2.15.0-ccip.0 → 2.16.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/abis/index.d.ts +1 -3
- package/dist/src/abis/index.js +1 -4
- package/dist/src/api/v2/models/account-transaction-transaction-data.d.ts +1 -2
- package/dist/src/api/v2/models/account-transaction.d.ts +0 -2
- package/dist/src/api/v2/models/account-transaction.js +1 -3
- package/dist/src/api/v2/models/chain.d.ts +0 -6
- package/dist/src/api/v2/models/evm-contracts.d.ts +0 -9
- package/dist/src/api/v2/models/evm-oftadapter.d.ts +0 -11
- package/dist/src/api/v2/models/evm-oftadapter.js +1 -4
- package/dist/src/api/v2/models/evm-oftpeer.d.ts +0 -12
- package/dist/src/api/v2/models/index.d.ts +0 -4
- package/dist/src/api/v2/models/index.js +0 -4
- package/dist/src/evm-vaults/interfaces/index.d.ts +0 -39
- package/dist/src/evm-vaults/on-chain-calls/tx-builder.d.ts +1 -15
- package/dist/src/evm-vaults/on-chain-calls/tx-builder.js +0 -36
- package/dist/src/evm-vaults/on-chain-calls/user.d.ts +1 -40
- package/dist/src/evm-vaults/on-chain-calls/user.js +0 -75
- package/dist/src/evm-vaults/on-chain-calls/vault-reader.d.ts +1 -14
- package/dist/src/evm-vaults/on-chain-calls/vault-reader.js +0 -30
- package/dist/src/evm-vaults/utils/deployment-parser.d.ts +1 -46
- package/dist/src/evm-vaults/utils/deployment-parser.js +0 -74
- package/dist/src/evm-vaults/utils/index.d.ts +0 -1
- package/dist/src/evm-vaults/utils/index.js +0 -1
- package/dist/src/placements/pointers.js +129 -45
- package/dist/src/sui-vaults/on-chain-calls/atomic-liquidity.js +3 -3
- package/dist/src/sui-vaults/on-chain-calls/tx-builder.d.ts +2 -2
- package/dist/src/sui-vaults/on-chain-calls/tx-builder.js +11 -4
- package/dist/src/sui-vaults/on-chain-calls/vault-admin.d.ts +10 -2
- package/dist/src/sui-vaults/on-chain-calls/vault-admin.js +12 -7
- package/dist/src/sui-vaults/on-chain-calls/vault.js +2 -4
- package/dist/src/sui-vaults/utils/common.d.ts +20 -0
- package/dist/src/sui-vaults/utils/common.js +24 -0
- package/package.json +1 -1
- package/dist/src/abis/CcipRouter.json +0 -58
- package/dist/src/api/v2/models/bridge-transaction.d.ts +0 -50
- package/dist/src/api/v2/models/bridge-transaction.js +0 -14
- package/dist/src/api/v2/models/evm-ccip-lane.d.ts +0 -73
- package/dist/src/api/v2/models/evm-ccip-lane.js +0 -14
- package/dist/src/api/v2/models/evm-ccip-rate-limits.d.ts +0 -36
- package/dist/src/api/v2/models/evm-ccip-rate-limits.js +0 -14
- package/dist/src/api/v2/models/evm-ccip-token-pool.d.ts +0 -122
- package/dist/src/api/v2/models/evm-ccip-token-pool.js +0 -17
- package/dist/src/evm-vaults/utils/ccip.d.ts +0 -23
- package/dist/src/evm-vaults/utils/ccip.js +0 -32
|
@@ -206,78 +206,4 @@ export class EVMDeploymentParser {
|
|
|
206
206
|
}
|
|
207
207
|
return entry;
|
|
208
208
|
}
|
|
209
|
-
/**
|
|
210
|
-
* Finds the CCIP token pool entry for a given vault address.
|
|
211
|
-
* @param vaultAddress The vault address (receipt token address in EVM deployment data)
|
|
212
|
-
*/
|
|
213
|
-
getCcipTokenPoolByVaultAddress(vaultAddress) {
|
|
214
|
-
return Object.values(this.deployment.contracts?.ccipTokenPools ?? {}).find(pool => pool.vaultAddress?.toLowerCase() === vaultAddress.toLowerCase());
|
|
215
|
-
}
|
|
216
|
-
/**
|
|
217
|
-
* Reports which bridge a vault uses, or undefined if it has none.
|
|
218
|
-
*
|
|
219
|
-
* A vault has exactly one active bridge, but different vaults use different
|
|
220
|
-
* ones, so call this before choosing between the LayerZero and CCIP send
|
|
221
|
-
* methods rather than assuming either.
|
|
222
|
-
*
|
|
223
|
-
* @param vaultAddress The vault address (receipt token address in EVM deployment data)
|
|
224
|
-
*/
|
|
225
|
-
getBridgeTransportByVaultAddress(vaultAddress) {
|
|
226
|
-
if (this.getCcipTokenPoolByVaultAddress(vaultAddress))
|
|
227
|
-
return "CCIP";
|
|
228
|
-
if (this.getOFTAdapterObjectByVaultAddress(vaultAddress))
|
|
229
|
-
return "LayerZero";
|
|
230
|
-
return undefined;
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Lists the networks a vault can bridge to, whichever transport it uses.
|
|
234
|
-
*
|
|
235
|
-
* Returns an empty array for a vault with no bridge, so it is safe to render
|
|
236
|
-
* directly. Names are as the deployment records them, which is what
|
|
237
|
-
* bridgeSendTokenViaCcip takes as its destination.
|
|
238
|
-
*
|
|
239
|
-
* @param vaultAddress The vault address (receipt token address in EVM deployment data)
|
|
240
|
-
*/
|
|
241
|
-
getBridgeDestinationsByVaultAddress(vaultAddress) {
|
|
242
|
-
// Both transports record the far end the same way, under remoteNetwork,
|
|
243
|
-
// but one keys its map by CCIP chain selector and the other by LayerZero
|
|
244
|
-
// endpoint id — neither of which a caller has any way to resolve. Reading
|
|
245
|
-
// the values rather than the keys is what keeps the two symmetric.
|
|
246
|
-
const pool = this.getCcipTokenPoolByVaultAddress(vaultAddress);
|
|
247
|
-
const adapter = this.getOFTAdapterObjectByVaultAddress(vaultAddress);
|
|
248
|
-
const networks = [
|
|
249
|
-
...Object.values(pool?.lanes ?? {}),
|
|
250
|
-
...Object.values(adapter?.peers ?? {})
|
|
251
|
-
]
|
|
252
|
-
.map(entry => entry.remoteNetwork)
|
|
253
|
-
.filter((network) => !!network);
|
|
254
|
-
return [...new Set(networks)];
|
|
255
|
-
}
|
|
256
|
-
/**
|
|
257
|
-
* Finds the configured CCIP lane from a vault to a destination chain selector.
|
|
258
|
-
*
|
|
259
|
-
* A missing lane means the route is not configured and a send would revert.
|
|
260
|
-
*
|
|
261
|
-
* @param vaultAddress The vault address on the source chain
|
|
262
|
-
* @param remoteChainSelector The destination CCIP chain selector
|
|
263
|
-
*/
|
|
264
|
-
getCcipLaneByVaultAndSelector(vaultAddress, remoteChainSelector) {
|
|
265
|
-
// Lanes are keyed by selector, so this is a direct hit rather than the
|
|
266
|
-
// scan the by-network lookup has to do.
|
|
267
|
-
return this.getCcipTokenPoolByVaultAddress(vaultAddress)?.lanes?.[remoteChainSelector];
|
|
268
|
-
}
|
|
269
|
-
/**
|
|
270
|
-
* Finds the configured CCIP lane from a vault to a destination network.
|
|
271
|
-
*
|
|
272
|
-
* Lanes are keyed by the destination's CCIP chain selector, which nothing
|
|
273
|
-
* outside CCIP knows, so this looks the lane up by network name instead.
|
|
274
|
-
* A missing lane means the route is not configured — sending would revert.
|
|
275
|
-
*
|
|
276
|
-
* @param vaultAddress The vault address on the source chain
|
|
277
|
-
* @param destinationNetwork The destination network name, e.g. "atlantic"
|
|
278
|
-
*/
|
|
279
|
-
getCcipLaneByVaultAndNetwork(vaultAddress, destinationNetwork) {
|
|
280
|
-
const pool = this.getCcipTokenPoolByVaultAddress(vaultAddress);
|
|
281
|
-
return Object.values(pool?.lanes ?? {}).find(lane => lane.remoteNetwork?.toLowerCase() === destinationNetwork.toLowerCase());
|
|
282
|
-
}
|
|
283
209
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { environmentConfig } from "../common/constants.js";
|
|
2
|
-
import {
|
|
2
|
+
import { isAbortError, isTransientStatus, retryAfterMs } from "./batch.js";
|
|
3
|
+
import { coinTypesMatch, normalizeCoinType } from "./sui.js";
|
|
3
4
|
/**
|
|
4
5
|
* Pointers held in memory. Useful for tests, and for pinning a known venue set
|
|
5
6
|
* without a round-trip.
|
|
@@ -35,69 +36,152 @@ export function httpPointerSource(options = {}) {
|
|
|
35
36
|
const path = options.path ?? "/api/v2/vaults/placements";
|
|
36
37
|
const cacheMs = options.cacheMs ?? 30000;
|
|
37
38
|
/**
|
|
38
|
-
* One request
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* fetched the same list five times and threw four of them away. Reading one
|
|
42
|
-
* vault meant eight requests where two would do, and a caller sweeping every
|
|
43
|
-
* vault made hundreds — enough to be rate-limited, and a rejected discover
|
|
44
|
-
* drops that venue from the answer.
|
|
39
|
+
* One request for every coin. The endpoint answers with the whole registry
|
|
40
|
+
* when `coin` is omitted, so reading every vault costs one call rather than
|
|
41
|
+
* one per receipt coin.
|
|
45
42
|
*
|
|
46
43
|
* In-flight requests are shared rather than merely cached, so the adapters
|
|
47
44
|
* racing each other at the start of a `forAccount` collapse into one call.
|
|
48
45
|
*/
|
|
49
46
|
const inFlight = new Map();
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
// Set when the server rejects the unfiltered call, i.e. it predates it.
|
|
48
|
+
// Rechecked after a while, so a long-lived source picks up a server that
|
|
49
|
+
// has since been upgraded.
|
|
50
|
+
let perCoinUntil = 0;
|
|
51
|
+
const shared = (key, load) => {
|
|
52
52
|
const hit = inFlight.get(key);
|
|
53
53
|
if (hit && Date.now() - hit.at < cacheMs)
|
|
54
54
|
return hit.rows;
|
|
55
|
-
const rows = (
|
|
56
|
-
const url = new URL(`${baseUrl.replace(/\/$/, "")}${path}`);
|
|
57
|
-
url.searchParams.set("coin", coin.coin);
|
|
58
|
-
const response = await fetch(url.toString(), {
|
|
59
|
-
headers: { Accept: "application/json" }
|
|
60
|
-
});
|
|
61
|
-
if (!response.ok) {
|
|
62
|
-
throw new Error(`pointer source ${response.status}`);
|
|
63
|
-
}
|
|
64
|
-
const body = (await response.json());
|
|
65
|
-
// The chain type comes from the coin, not the wire: an adapter only
|
|
66
|
-
// ever runs on its own chain, so a server that named it again could
|
|
67
|
-
// only ever disagree.
|
|
68
|
-
return (body.placements ?? []).map(placement => ({
|
|
69
|
-
id: placement.id,
|
|
70
|
-
protocol: placement.protocol,
|
|
71
|
-
chain: {
|
|
72
|
-
type: coin.chain.type,
|
|
73
|
-
chainId: placement.chainId,
|
|
74
|
-
identifier: placement.chainIdentifier
|
|
75
|
-
},
|
|
76
|
-
venue: placement.venue,
|
|
77
|
-
coin: placement.coin,
|
|
78
|
-
decimals: requireDecimals(placement),
|
|
79
|
-
protocolName: placement.protocolName,
|
|
80
|
-
protocolLogoUrl: placement.protocolLogoUrl,
|
|
81
|
-
source: placement.source,
|
|
82
|
-
label: placement.label,
|
|
83
|
-
shareToken: placement.shareToken,
|
|
84
|
-
url: placement.url,
|
|
85
|
-
rpcUrl: placement.rpcUrl
|
|
86
|
-
}));
|
|
87
|
-
})();
|
|
55
|
+
const rows = load();
|
|
88
56
|
// A failure is not cached: the next adapter should retry rather than
|
|
89
57
|
// inherit a rejection it never caused.
|
|
90
58
|
rows.catch(() => inFlight.delete(key));
|
|
91
59
|
inFlight.set(key, { at: Date.now(), rows });
|
|
92
60
|
return rows;
|
|
93
61
|
};
|
|
62
|
+
const request = (coin) => {
|
|
63
|
+
const url = new URL(`${baseUrl.replace(/\/$/, "")}${path}`);
|
|
64
|
+
if (coin !== undefined)
|
|
65
|
+
url.searchParams.set("coin", coin);
|
|
66
|
+
return fetchPlacements(url.toString());
|
|
67
|
+
};
|
|
68
|
+
const rowsFor = async (coin) => {
|
|
69
|
+
if (Date.now() >= perCoinUntil) {
|
|
70
|
+
try {
|
|
71
|
+
return await shared("*", () => request());
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
if (!(error instanceof PointerSourceError) || error.status !== 400) {
|
|
75
|
+
throw error;
|
|
76
|
+
}
|
|
77
|
+
perCoinUntil = Date.now() + FALLBACK_RECHECK_MS;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return shared(coin.coin.toLowerCase(), () => request(coin.coin));
|
|
81
|
+
};
|
|
94
82
|
return {
|
|
95
83
|
async placements(protocol, coin) {
|
|
96
|
-
const rows = await
|
|
97
|
-
|
|
84
|
+
const rows = await rowsFor(coin);
|
|
85
|
+
const wanted = coinKey(coin.coin);
|
|
86
|
+
// One venue stored under two spellings of the coin would otherwise
|
|
87
|
+
// be read twice and double-count the position.
|
|
88
|
+
const seen = new Set();
|
|
89
|
+
return (rows
|
|
90
|
+
.filter(row => {
|
|
91
|
+
if (row.protocol !== protocol || coinKey(row.coin) !== wanted) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const key = [row.chainIdentifier, row.venue, row.shareToken ?? ""]
|
|
95
|
+
.join(":")
|
|
96
|
+
.toLowerCase();
|
|
97
|
+
if (seen.has(key))
|
|
98
|
+
return false;
|
|
99
|
+
seen.add(key);
|
|
100
|
+
return true;
|
|
101
|
+
})
|
|
102
|
+
// The chain type comes from the coin, not the wire: an adapter
|
|
103
|
+
// only ever runs on its own chain, so a server that named it
|
|
104
|
+
// again could only ever disagree.
|
|
105
|
+
.map(row => ({
|
|
106
|
+
id: row.id,
|
|
107
|
+
protocol: row.protocol,
|
|
108
|
+
chain: {
|
|
109
|
+
type: coin.chain.type,
|
|
110
|
+
chainId: row.chainId,
|
|
111
|
+
identifier: row.chainIdentifier
|
|
112
|
+
},
|
|
113
|
+
venue: row.venue,
|
|
114
|
+
coin: row.coin,
|
|
115
|
+
decimals: requireDecimals(row),
|
|
116
|
+
protocolName: row.protocolName,
|
|
117
|
+
protocolLogoUrl: row.protocolLogoUrl,
|
|
118
|
+
source: row.source,
|
|
119
|
+
label: row.label,
|
|
120
|
+
shareToken: row.shareToken,
|
|
121
|
+
url: row.url,
|
|
122
|
+
rpcUrl: row.rpcUrl
|
|
123
|
+
})));
|
|
98
124
|
}
|
|
99
125
|
};
|
|
100
126
|
}
|
|
127
|
+
class PointerSourceError extends Error {
|
|
128
|
+
constructor(status) {
|
|
129
|
+
super(`pointer source ${status}`);
|
|
130
|
+
this.status = status;
|
|
131
|
+
this.name = "PointerSourceError";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const FETCH_ATTEMPTS = 3;
|
|
135
|
+
const RETRY_BASE_MS = 250;
|
|
136
|
+
const RETRY_MAX_MS = 2000;
|
|
137
|
+
const FALLBACK_RECHECK_MS = 5 * 60000;
|
|
138
|
+
/**
|
|
139
|
+
* Retries transient failures. Every vault's venues now ride on this one
|
|
140
|
+
* request, and callers keep a failed discover rather than retrying it.
|
|
141
|
+
*/
|
|
142
|
+
async function fetchPlacements(url) {
|
|
143
|
+
for (let attempt = 1;; attempt++) {
|
|
144
|
+
let waitMs;
|
|
145
|
+
try {
|
|
146
|
+
const response = await fetch(url, {
|
|
147
|
+
headers: { Accept: "application/json" }
|
|
148
|
+
});
|
|
149
|
+
if (response.ok) {
|
|
150
|
+
const body = (await response.json());
|
|
151
|
+
return body.placements ?? [];
|
|
152
|
+
}
|
|
153
|
+
if (!isTransientStatus(response.status) || attempt >= FETCH_ATTEMPTS) {
|
|
154
|
+
throw new PointerSourceError(response.status);
|
|
155
|
+
}
|
|
156
|
+
waitMs = retryAfterMs(response.headers?.get("retry-after"));
|
|
157
|
+
// Retrying before the server asked would only add to the load it
|
|
158
|
+
// is shedding and fail again.
|
|
159
|
+
if (waitMs !== undefined && waitMs > RETRY_MAX_MS) {
|
|
160
|
+
throw new PointerSourceError(response.status);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
// fetch rejects only on network failure; retry that, not our own
|
|
165
|
+
// errors or an abort.
|
|
166
|
+
if (error instanceof PointerSourceError ||
|
|
167
|
+
isAbortError(error) ||
|
|
168
|
+
attempt >= FETCH_ATTEMPTS) {
|
|
169
|
+
throw error;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
const backoff = Math.min(RETRY_BASE_MS * 2 ** (attempt - 1), RETRY_MAX_MS);
|
|
173
|
+
await new Promise(resolve => setTimeout(resolve, waitMs ?? backoff));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Coin identity across spellings. `coinTypesMatch` folds a Sui address; EVM
|
|
178
|
+
* addresses are compared case-insensitively and with or without `0x`, since
|
|
179
|
+
* the API returns them lowercased and the vaults list checksums them.
|
|
180
|
+
*/
|
|
181
|
+
function coinKey(coin) {
|
|
182
|
+
const normalized = normalizeCoinType(coin);
|
|
183
|
+
return normalized.includes("::") ? normalized : normalized.replace(/^0x/, "");
|
|
184
|
+
}
|
|
101
185
|
/**
|
|
102
186
|
* Merges what every source returns, de-duplicated by venue. Earlier sources win,
|
|
103
187
|
* so a caller can pin rows ahead of a remote set. A source that throws is
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { bcs } from "@mysten/sui/bcs";
|
|
2
|
+
import { tableIdOf, unwrapMoveFields } from "../utils/common.js";
|
|
2
3
|
import { computeSwapQuote, computePlainInstantQuote } from "../../atomic/instant-withdraw.js";
|
|
3
4
|
/** Sui fixed-point scale (1e9) used by the Atomic Liquidity Vault Move math. */
|
|
4
5
|
const SUI_SCALE = 1000000000n;
|
|
5
|
-
const unwrapMoveFields = (value) => value?.fields ?? value;
|
|
6
6
|
const toBig = (v) => v === undefined || v === null ? 0n : BigInt(v);
|
|
7
7
|
/** `math::div(a, b)` = floor(a * scale / b). */
|
|
8
8
|
const divFloor = (a, b) => (b === 0n ? 0n : (a * SUI_SCALE) / b);
|
|
@@ -73,7 +73,7 @@ export class SuiAtomicLiquidityReader {
|
|
|
73
73
|
/** Per-source-share EmberShare config from the `ember_share_configs` table. */
|
|
74
74
|
async getEmberShareConfig(vaultId, sourceVaultId) {
|
|
75
75
|
const fields = await this.vaultFields(vaultId);
|
|
76
|
-
const tableId =
|
|
76
|
+
const tableId = tableIdOf(fields.ember_share_configs);
|
|
77
77
|
if (!tableId)
|
|
78
78
|
return null;
|
|
79
79
|
{
|
|
@@ -102,7 +102,7 @@ export class SuiAtomicLiquidityReader {
|
|
|
102
102
|
/** Caller's ALV (non-transferable) share balance from the `shares` table. */
|
|
103
103
|
async shareOf(vaultId, account) {
|
|
104
104
|
const fields = await this.vaultFields(vaultId);
|
|
105
|
-
const tableId =
|
|
105
|
+
const tableId = tableIdOf(fields.shares);
|
|
106
106
|
if (!tableId)
|
|
107
107
|
return 0n;
|
|
108
108
|
return toBig(await this.readTableValue(tableId, "address", account));
|
|
@@ -466,7 +466,7 @@ export declare class TxBuilder {
|
|
|
466
466
|
* @param options Optional tx building params
|
|
467
467
|
* @returns TransactionBlock
|
|
468
468
|
*/
|
|
469
|
-
atomicSetEmberShareWhitelist(vaultId: string, shareVaultId: string, whitelisted: boolean, feePercentage: NumStr, maxCapacityPercentage: NumStr, holdbackPercentage: NumStr, maxUtilizationPercentage: NumStr, options?: ITxBuilderOptionalParams): TransactionBlock;
|
|
469
|
+
atomicSetEmberShareWhitelist(vaultId: string, shareVaultId: string, whitelisted: boolean, feePercentage: NumStr, maxCapacityPercentage: NumStr, holdbackPercentage: NumStr, maxUtilizationPercentage: NumStr, minSwapGross: NumStr, maxHeldValueAbsolute: NumStr, options?: ITxBuilderOptionalParams): TransactionBlock;
|
|
470
470
|
/**
|
|
471
471
|
* Sets the max utilization percentage of an ember share source vault
|
|
472
472
|
* @param vaultId Id of the atomic vault
|
|
@@ -556,7 +556,7 @@ export declare class TxBuilder {
|
|
|
556
556
|
* @param options Optional tx building params
|
|
557
557
|
* @returns TransactionBlock
|
|
558
558
|
*/
|
|
559
|
-
atomicSetStrategyNoneFromAlphalend(vaultId: string, lendingProtocolId: string, options?: ITxBuilderOptionalParams): TransactionBlock;
|
|
559
|
+
atomicSetStrategyNoneFromAlphalend(vaultId: string, lendingProtocolId: string, acceptResidual: boolean, options?: ITxBuilderOptionalParams): TransactionBlock;
|
|
560
560
|
/**
|
|
561
561
|
* Step 1 of 2 for Suilend activation: commits the reserve index, the market
|
|
562
562
|
* object and the collateral type. Step 2 is `atomicOpenSuilendObligation`,
|
|
@@ -1452,7 +1452,7 @@ export class TxBuilder {
|
|
|
1452
1452
|
* @param options Optional tx building params
|
|
1453
1453
|
* @returns TransactionBlock
|
|
1454
1454
|
*/
|
|
1455
|
-
atomicSetEmberShareWhitelist(vaultId, shareVaultId, whitelisted, feePercentage, maxCapacityPercentage, holdbackPercentage, maxUtilizationPercentage, options) {
|
|
1455
|
+
atomicSetEmberShareWhitelist(vaultId, shareVaultId, whitelisted, feePercentage, maxCapacityPercentage, holdbackPercentage, maxUtilizationPercentage, minSwapGross, maxHeldValueAbsolute, options) {
|
|
1456
1456
|
const txb = options?.txBlock || new TransactionBlock();
|
|
1457
1457
|
txb.moveCall({
|
|
1458
1458
|
arguments: [
|
|
@@ -1463,7 +1463,9 @@ export class TxBuilder {
|
|
|
1463
1463
|
txb.pure.u64(feePercentage),
|
|
1464
1464
|
txb.pure.u64(maxCapacityPercentage),
|
|
1465
1465
|
txb.pure.u64(holdbackPercentage),
|
|
1466
|
-
txb.pure.u64(maxUtilizationPercentage)
|
|
1466
|
+
txb.pure.u64(maxUtilizationPercentage),
|
|
1467
|
+
txb.pure.u64(minSwapGross),
|
|
1468
|
+
txb.pure.u64(maxHeldValueAbsolute)
|
|
1467
1469
|
],
|
|
1468
1470
|
typeArguments: [this.parser.getAtomicCollateralType(vaultId)],
|
|
1469
1471
|
target: `${this.parser.getPackageId()}::gateway::atomic_set_ember_share_whitelist`
|
|
@@ -1715,13 +1717,14 @@ export class TxBuilder {
|
|
|
1715
1717
|
* @param options Optional tx building params
|
|
1716
1718
|
* @returns TransactionBlock
|
|
1717
1719
|
*/
|
|
1718
|
-
atomicSetStrategyNoneFromAlphalend(vaultId, lendingProtocolId, options) {
|
|
1720
|
+
atomicSetStrategyNoneFromAlphalend(vaultId, lendingProtocolId, acceptResidual, options) {
|
|
1719
1721
|
const txb = options?.txBlock || new TransactionBlock();
|
|
1720
1722
|
txb.moveCall({
|
|
1721
1723
|
arguments: [
|
|
1722
1724
|
txb.object(vaultId),
|
|
1723
1725
|
txb.object(this.parser.getProtocolConfig()),
|
|
1724
1726
|
txb.object(lendingProtocolId),
|
|
1727
|
+
txb.pure.bool(acceptResidual),
|
|
1725
1728
|
txb.object(SUI_CLOCK_OBJECT_ID)
|
|
1726
1729
|
],
|
|
1727
1730
|
typeArguments: [this.parser.getAtomicCollateralType(vaultId)],
|
|
@@ -2383,7 +2386,11 @@ export class TxBuilder {
|
|
|
2383
2386
|
arguments: [
|
|
2384
2387
|
txb.object(vaultId),
|
|
2385
2388
|
txb.object(this.parser.getProtocolConfig()),
|
|
2386
|
-
|
|
2389
|
+
// `source_vault: &Vault<T, R>` — a shared-object reference, not an
|
|
2390
|
+
// id. Its sibling `atomic_redeem_collected_ember_shares` takes the
|
|
2391
|
+
// same vault the same way; sending a pure ID here cannot resolve to
|
|
2392
|
+
// the object the Move side wants.
|
|
2393
|
+
txb.object(sourceVaultId),
|
|
2387
2394
|
txb.object(tokenId)
|
|
2388
2395
|
],
|
|
2389
2396
|
typeArguments: [
|
|
@@ -193,10 +193,15 @@ export declare class VaultAdminCalls extends OnChainCalls {
|
|
|
193
193
|
* @param maxCapacityPercentage The max capacity percentage
|
|
194
194
|
* @param holdbackPercentage The holdback percentage
|
|
195
195
|
* @param maxUtilizationPercentage The max utilization percentage
|
|
196
|
+
* @param minSwapGross Minimum gross swap size, in collateral base units. 0 means
|
|
197
|
+
* no floor. Below it `swap_ember_shares` aborts, which is what keeps dust swaps
|
|
198
|
+
* from walking the fee curve for free.
|
|
199
|
+
* @param maxHeldValueAbsolute Absolute ceiling on the collateral value this vault
|
|
200
|
+
* will custody for the source. 0 means no cap — the percentage caps still apply.
|
|
196
201
|
* @param options Optional tx execution params
|
|
197
202
|
* @returns OnChainCallResponse
|
|
198
203
|
*/
|
|
199
|
-
atomicSetEmberShareWhitelist(vaultId: string, shareVaultId: string, whitelisted: boolean, feePercentage: NumStr, maxCapacityPercentage: NumStr, holdbackPercentage: NumStr, maxUtilizationPercentage: NumStr, options?: IVaultOptionalParams & SuiTransactionBlockResponseOptions): Promise<OnChainCallResponse>;
|
|
204
|
+
atomicSetEmberShareWhitelist(vaultId: string, shareVaultId: string, whitelisted: boolean, feePercentage: NumStr, maxCapacityPercentage: NumStr, holdbackPercentage: NumStr, maxUtilizationPercentage: NumStr, minSwapGross: NumStr, maxHeldValueAbsolute: NumStr, options?: IVaultOptionalParams & SuiTransactionBlockResponseOptions): Promise<OnChainCallResponse>;
|
|
200
205
|
/**
|
|
201
206
|
* Set the max utilization percentage of an ember share source vault
|
|
202
207
|
* @param vaultId The id of the atomic vault
|
|
@@ -317,8 +322,11 @@ export declare class VaultAdminCalls extends OnChainCalls {
|
|
|
317
322
|
* draining the full position collateral back into the vault balance
|
|
318
323
|
* @param vaultId The id of the atomic vault
|
|
319
324
|
* @param lendingProtocolId Object id of the AlphaLend LendingProtocol
|
|
325
|
+
* @param acceptResidual Abandon a dust residual rather than aborting the exit.
|
|
326
|
+
* Only pass true deliberately: the residual is forfeited, and the exit burns the
|
|
327
|
+
* PositionCap that could otherwise reach it.
|
|
320
328
|
* @param options Optional tx execution params
|
|
321
329
|
* @returns OnChainCallResponse
|
|
322
330
|
*/
|
|
323
|
-
atomicSetStrategyNoneFromAlphalend(vaultId: string, lendingProtocolId: string, options?: IVaultOptionalParams & SuiTransactionBlockResponseOptions): Promise<OnChainCallResponse>;
|
|
331
|
+
atomicSetStrategyNoneFromAlphalend(vaultId: string, lendingProtocolId: string, acceptResidual: boolean, options?: IVaultOptionalParams & SuiTransactionBlockResponseOptions): Promise<OnChainCallResponse>;
|
|
324
332
|
}
|
|
@@ -340,12 +340,17 @@ export class VaultAdminCalls extends OnChainCalls {
|
|
|
340
340
|
* @param maxCapacityPercentage The max capacity percentage
|
|
341
341
|
* @param holdbackPercentage The holdback percentage
|
|
342
342
|
* @param maxUtilizationPercentage The max utilization percentage
|
|
343
|
+
* @param minSwapGross Minimum gross swap size, in collateral base units. 0 means
|
|
344
|
+
* no floor. Below it `swap_ember_shares` aborts, which is what keeps dust swaps
|
|
345
|
+
* from walking the fee curve for free.
|
|
346
|
+
* @param maxHeldValueAbsolute Absolute ceiling on the collateral value this vault
|
|
347
|
+
* will custody for the source. 0 means no cap — the percentage caps still apply.
|
|
343
348
|
* @param options Optional tx execution params
|
|
344
349
|
* @returns OnChainCallResponse
|
|
345
350
|
*/
|
|
346
|
-
async atomicSetEmberShareWhitelist(vaultId, shareVaultId, whitelisted, feePercentage, maxCapacityPercentage, holdbackPercentage, maxUtilizationPercentage, options) {
|
|
351
|
+
async atomicSetEmberShareWhitelist(vaultId, shareVaultId, whitelisted, feePercentage, maxCapacityPercentage, holdbackPercentage, maxUtilizationPercentage, minSwapGross, maxHeldValueAbsolute, options) {
|
|
347
352
|
const txb = options?.txBlock || new TransactionBlock();
|
|
348
|
-
this.txBuilder.atomicSetEmberShareWhitelist(vaultId, shareVaultId, whitelisted, feePercentage, maxCapacityPercentage, holdbackPercentage, maxUtilizationPercentage, { ...options, txBlock: txb });
|
|
353
|
+
this.txBuilder.atomicSetEmberShareWhitelist(vaultId, shareVaultId, whitelisted, feePercentage, maxCapacityPercentage, holdbackPercentage, maxUtilizationPercentage, minSwapGross, maxHeldValueAbsolute, { ...options, txBlock: txb });
|
|
349
354
|
return this.execCall(txb, options);
|
|
350
355
|
}
|
|
351
356
|
/**
|
|
@@ -537,15 +542,15 @@ export class VaultAdminCalls extends OnChainCalls {
|
|
|
537
542
|
* draining the full position collateral back into the vault balance
|
|
538
543
|
* @param vaultId The id of the atomic vault
|
|
539
544
|
* @param lendingProtocolId Object id of the AlphaLend LendingProtocol
|
|
545
|
+
* @param acceptResidual Abandon a dust residual rather than aborting the exit.
|
|
546
|
+
* Only pass true deliberately: the residual is forfeited, and the exit burns the
|
|
547
|
+
* PositionCap that could otherwise reach it.
|
|
540
548
|
* @param options Optional tx execution params
|
|
541
549
|
* @returns OnChainCallResponse
|
|
542
550
|
*/
|
|
543
|
-
async atomicSetStrategyNoneFromAlphalend(vaultId, lendingProtocolId, options) {
|
|
551
|
+
async atomicSetStrategyNoneFromAlphalend(vaultId, lendingProtocolId, acceptResidual, options) {
|
|
544
552
|
const txb = options?.txBlock || new TransactionBlock();
|
|
545
|
-
this.txBuilder.atomicSetStrategyNoneFromAlphalend(vaultId, lendingProtocolId, {
|
|
546
|
-
...options,
|
|
547
|
-
txBlock: txb
|
|
548
|
-
});
|
|
553
|
+
this.txBuilder.atomicSetStrategyNoneFromAlphalend(vaultId, lendingProtocolId, acceptResidual, { ...options, txBlock: txb });
|
|
549
554
|
return this.execCall(txb, options);
|
|
550
555
|
}
|
|
551
556
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { bcs } from "@mysten/sui/bcs";
|
|
2
2
|
import { BcsUpdateVaultStrategyRequest } from "../interfaces/bcs.js";
|
|
3
|
-
|
|
3
|
+
import { tableIdOf, unwrapMoveFields } from "../utils/common.js";
|
|
4
4
|
export class Vault {
|
|
5
5
|
/**
|
|
6
6
|
* Fetches the vault rate from on-chain
|
|
@@ -65,9 +65,7 @@ export class Vault {
|
|
|
65
65
|
});
|
|
66
66
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
67
|
const fields = unwrapMoveFields(objDetails.object?.json);
|
|
68
|
-
const
|
|
69
|
-
const accountTableId = unwrapMoveFields(accounts?.id);
|
|
70
|
-
const userTable = accountTableId?.id || accounts?.id;
|
|
68
|
+
const userTable = tableIdOf(fields?.accounts);
|
|
71
69
|
try {
|
|
72
70
|
const userData = await suiClient.core.getDynamicObjectField({
|
|
73
71
|
parentId: userTable,
|
|
@@ -1,3 +1,23 @@
|
|
|
1
1
|
import { BaseClient as SuiClient } from "@mysten/sui/client";
|
|
2
2
|
import { TransactionBlock } from "@firefly-exchange/library-sui";
|
|
3
3
|
export declare const getTxBytes: (tx: TransactionBlock, client: SuiClient) => Promise<string>;
|
|
4
|
+
/**
|
|
5
|
+
* Strip the JSON-RPC `{ fields: {...} }` wrapper from a Move value.
|
|
6
|
+
*
|
|
7
|
+
* gRPC returns the fields directly, JSON-RPC nests them one level down, so this
|
|
8
|
+
* makes a read work on either transport.
|
|
9
|
+
*/
|
|
10
|
+
export declare const unwrapMoveFields: (value: any) => any;
|
|
11
|
+
/**
|
|
12
|
+
* The object id of a `Table` / `Bag` / `ObjectTable` field, whichever way the
|
|
13
|
+
* transport rendered its `UID`.
|
|
14
|
+
*
|
|
15
|
+
* The two disagree: over gRPC a UID comes back flattened to a string, so the
|
|
16
|
+
* field reads `{ id: "0x...", size: "1" }`, while JSON-RPC keeps the nesting and
|
|
17
|
+
* gives `{ id: { id: "0x..." }, size: "1" }`. Reaching straight for `.id.id`
|
|
18
|
+
* therefore yields `undefined` on gRPC — and because every caller treats a
|
|
19
|
+
* missing table id as "no such table", the read fails CLOSED: a whitelisted
|
|
20
|
+
* source vault reads as not whitelisted, a real share balance reads as zero.
|
|
21
|
+
* Silent, and wrong in the direction that looks like ordinary empty state.
|
|
22
|
+
*/
|
|
23
|
+
export declare const tableIdOf: (field: any) => string | undefined;
|
|
@@ -2,3 +2,27 @@ import { toBase64 } from "@mysten/sui/utils";
|
|
|
2
2
|
export const getTxBytes = async (tx, client) => {
|
|
3
3
|
return toBase64(await tx.build({ client: client, onlyTransactionKind: false }));
|
|
4
4
|
};
|
|
5
|
+
/**
|
|
6
|
+
* Strip the JSON-RPC `{ fields: {...} }` wrapper from a Move value.
|
|
7
|
+
*
|
|
8
|
+
* gRPC returns the fields directly, JSON-RPC nests them one level down, so this
|
|
9
|
+
* makes a read work on either transport.
|
|
10
|
+
*/
|
|
11
|
+
export const unwrapMoveFields = (value) => value?.fields ?? value;
|
|
12
|
+
/**
|
|
13
|
+
* The object id of a `Table` / `Bag` / `ObjectTable` field, whichever way the
|
|
14
|
+
* transport rendered its `UID`.
|
|
15
|
+
*
|
|
16
|
+
* The two disagree: over gRPC a UID comes back flattened to a string, so the
|
|
17
|
+
* field reads `{ id: "0x...", size: "1" }`, while JSON-RPC keeps the nesting and
|
|
18
|
+
* gives `{ id: { id: "0x..." }, size: "1" }`. Reaching straight for `.id.id`
|
|
19
|
+
* therefore yields `undefined` on gRPC — and because every caller treats a
|
|
20
|
+
* missing table id as "no such table", the read fails CLOSED: a whitelisted
|
|
21
|
+
* source vault reads as not whitelisted, a real share balance reads as zero.
|
|
22
|
+
* Silent, and wrong in the direction that looks like ordinary empty state.
|
|
23
|
+
*/
|
|
24
|
+
export const tableIdOf = (field) => {
|
|
25
|
+
const table = unwrapMoveFields(field);
|
|
26
|
+
const id = unwrapMoveFields(table?.id);
|
|
27
|
+
return (typeof id === "string" ? id : id?.id) ?? undefined;
|
|
28
|
+
};
|
package/package.json
CHANGED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"abi": [
|
|
3
|
-
{
|
|
4
|
-
"type": "function",
|
|
5
|
-
"name": "getFee",
|
|
6
|
-
"stateMutability": "view",
|
|
7
|
-
"inputs": [
|
|
8
|
-
{ "name": "destChainSelector", "type": "uint64" },
|
|
9
|
-
{
|
|
10
|
-
"name": "message",
|
|
11
|
-
"type": "tuple",
|
|
12
|
-
"components": [
|
|
13
|
-
{ "name": "receiver", "type": "bytes" },
|
|
14
|
-
{ "name": "data", "type": "bytes" },
|
|
15
|
-
{
|
|
16
|
-
"name": "tokenAmounts",
|
|
17
|
-
"type": "tuple[]",
|
|
18
|
-
"components": [
|
|
19
|
-
{ "name": "token", "type": "address" },
|
|
20
|
-
{ "name": "amount", "type": "uint256" }
|
|
21
|
-
]
|
|
22
|
-
},
|
|
23
|
-
{ "name": "feeToken", "type": "address" },
|
|
24
|
-
{ "name": "extraArgs", "type": "bytes" }
|
|
25
|
-
]
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"outputs": [{ "name": "fee", "type": "uint256" }]
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"type": "function",
|
|
32
|
-
"name": "ccipSend",
|
|
33
|
-
"stateMutability": "payable",
|
|
34
|
-
"inputs": [
|
|
35
|
-
{ "name": "destinationChainSelector", "type": "uint64" },
|
|
36
|
-
{
|
|
37
|
-
"name": "message",
|
|
38
|
-
"type": "tuple",
|
|
39
|
-
"components": [
|
|
40
|
-
{ "name": "receiver", "type": "bytes" },
|
|
41
|
-
{ "name": "data", "type": "bytes" },
|
|
42
|
-
{
|
|
43
|
-
"name": "tokenAmounts",
|
|
44
|
-
"type": "tuple[]",
|
|
45
|
-
"components": [
|
|
46
|
-
{ "name": "token", "type": "address" },
|
|
47
|
-
{ "name": "amount", "type": "uint256" }
|
|
48
|
-
]
|
|
49
|
-
},
|
|
50
|
-
{ "name": "feeToken", "type": "address" },
|
|
51
|
-
{ "name": "extraArgs", "type": "bytes" }
|
|
52
|
-
]
|
|
53
|
-
}
|
|
54
|
-
],
|
|
55
|
-
"outputs": [{ "name": "messageId", "type": "bytes32" }]
|
|
56
|
-
}
|
|
57
|
-
]
|
|
58
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Ember Protocol Vaults API
|
|
3
|
-
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
|
|
4
|
-
*
|
|
5
|
-
* The version of the OpenAPI document: 2.0.0
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
|
|
9
|
-
* https://openapi-generator.tech
|
|
10
|
-
* Do not edit the class manually.
|
|
11
|
-
*/
|
|
12
|
-
import type { Asset } from "./asset.js";
|
|
13
|
-
import type { VaultSlice } from "./vault-slice.js";
|
|
14
|
-
/**
|
|
15
|
-
* Vault shares moving between chains. BridgeOut burns them on the origin chain, BridgeIn mints them on the destination; the two are separate transactions on separate chains rather than one record, because each chain reports its own half. Carries no transport discriminator: a vault has exactly one bridge, so which one carried a given transfer is a property of the vault rather than of the transaction. Read it from the vault\'s own bridge config.
|
|
16
|
-
* @export
|
|
17
|
-
* @interface BridgeTransaction
|
|
18
|
-
*/
|
|
19
|
-
export interface BridgeTransaction {
|
|
20
|
-
/**
|
|
21
|
-
* The timestamp of the event
|
|
22
|
-
* @type {number}
|
|
23
|
-
* @memberof BridgeTransaction
|
|
24
|
-
*/
|
|
25
|
-
timestamp?: number;
|
|
26
|
-
/**
|
|
27
|
-
* The amount of shares bridged
|
|
28
|
-
* @type {string}
|
|
29
|
-
* @memberof BridgeTransaction
|
|
30
|
-
*/
|
|
31
|
-
sharesAmount?: string;
|
|
32
|
-
/**
|
|
33
|
-
*
|
|
34
|
-
* @type {Asset}
|
|
35
|
-
* @memberof BridgeTransaction
|
|
36
|
-
*/
|
|
37
|
-
sharesCoin?: Asset;
|
|
38
|
-
/**
|
|
39
|
-
*
|
|
40
|
-
* @type {VaultSlice}
|
|
41
|
-
* @memberof BridgeTransaction
|
|
42
|
-
*/
|
|
43
|
-
vault?: VaultSlice;
|
|
44
|
-
/**
|
|
45
|
-
* The transaction hash on this chain
|
|
46
|
-
* @type {string}
|
|
47
|
-
* @memberof BridgeTransaction
|
|
48
|
-
*/
|
|
49
|
-
txDigest?: string;
|
|
50
|
-
}
|