@oydual31/more-vaults-sdk 0.1.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/README.md +283 -0
- package/dist/ethers/index.cjs +973 -0
- package/dist/ethers/index.cjs.map +1 -0
- package/dist/ethers/index.d.cts +671 -0
- package/dist/ethers/index.d.ts +671 -0
- package/dist/ethers/index.js +924 -0
- package/dist/ethers/index.js.map +1 -0
- package/dist/viem/index.cjs +1426 -0
- package/dist/viem/index.cjs.map +1 -0
- package/dist/viem/index.d.cts +1291 -0
- package/dist/viem/index.d.ts +1291 -0
- package/dist/viem/index.js +1377 -0
- package/dist/viem/index.js.map +1 -0
- package/package.json +46 -0
- package/src/ethers/abis.ts +82 -0
- package/src/ethers/crossChainFlows.ts +206 -0
- package/src/ethers/depositFlows.ts +347 -0
- package/src/ethers/errors.ts +81 -0
- package/src/ethers/index.ts +103 -0
- package/src/ethers/preflight.ts +156 -0
- package/src/ethers/redeemFlows.ts +286 -0
- package/src/ethers/types.ts +67 -0
- package/src/ethers/userHelpers.ts +480 -0
- package/src/ethers/utils.ts +377 -0
- package/src/viem/abis.ts +392 -0
- package/src/viem/crossChainFlows.ts +220 -0
- package/src/viem/depositFlows.ts +331 -0
- package/src/viem/errors.ts +81 -0
- package/src/viem/index.ts +100 -0
- package/src/viem/preflight.ts +204 -0
- package/src/viem/redeemFlows.ts +337 -0
- package/src/viem/types.ts +56 -0
- package/src/viem/userHelpers.ts +489 -0
- package/src/viem/utils.ts +421 -0
|
@@ -0,0 +1,973 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ethers = require('ethers');
|
|
4
|
+
|
|
5
|
+
// src/ethers/types.ts
|
|
6
|
+
var ActionType = {
|
|
7
|
+
DEPOSIT: 0,
|
|
8
|
+
MINT: 1,
|
|
9
|
+
WITHDRAW: 2,
|
|
10
|
+
REDEEM: 3,
|
|
11
|
+
MULTI_ASSETS_DEPOSIT: 4,
|
|
12
|
+
ACCRUE_FEES: 5
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
// src/ethers/abis.ts
|
|
16
|
+
var VAULT_ABI = [
|
|
17
|
+
// ERC4626 core
|
|
18
|
+
"function deposit(uint256 assets, address receiver) returns (uint256 shares)",
|
|
19
|
+
"function mint(uint256 shares, address receiver) returns (uint256 assets)",
|
|
20
|
+
"function withdraw(uint256 assets, address receiver, address owner) returns (uint256 shares)",
|
|
21
|
+
"function redeem(uint256 shares, address receiver, address owner) returns (uint256 assets)",
|
|
22
|
+
// Multi-asset deposit
|
|
23
|
+
"function deposit(address[] tokens, uint256[] assets, address receiver, uint256 minAmountOut) payable returns (uint256 shares)",
|
|
24
|
+
// Withdrawal queue
|
|
25
|
+
"function requestRedeem(uint256 shares, address onBehalfOf)",
|
|
26
|
+
"function requestWithdraw(uint256 assets, address onBehalfOf)",
|
|
27
|
+
"function clearRequest()",
|
|
28
|
+
"function getWithdrawalRequest(address _owner) view returns (uint256 shares, uint256 timelockEndsAt)",
|
|
29
|
+
// Views
|
|
30
|
+
"function totalAssets() view returns (uint256)",
|
|
31
|
+
"function totalSupply() view returns (uint256)",
|
|
32
|
+
"function balanceOf(address account) view returns (uint256)",
|
|
33
|
+
"function asset() view returns (address)",
|
|
34
|
+
"function convertToShares(uint256 assets) view returns (uint256)",
|
|
35
|
+
"function convertToAssets(uint256 shares) view returns (uint256)",
|
|
36
|
+
"function previewDeposit(uint256 assets) view returns (uint256)",
|
|
37
|
+
"function previewRedeem(uint256 shares) view returns (uint256)",
|
|
38
|
+
"function paused() view returns (bool)",
|
|
39
|
+
// Events
|
|
40
|
+
"event Deposit(address indexed sender, address indexed owner, address[] tokens, uint256[] assets, uint256 shares)",
|
|
41
|
+
"event Transfer(address indexed from, address indexed to, uint256 value)",
|
|
42
|
+
"event WithdrawRequestCreated(address requester, uint256 sharesAmount, uint256 endsAt)",
|
|
43
|
+
"event WithdrawRequestFulfilled(address requester, address receiver, uint256 sharesAmount, uint256 assetAmount)"
|
|
44
|
+
];
|
|
45
|
+
var BRIDGE_ABI = [
|
|
46
|
+
"function initVaultActionRequest(uint8 actionType, bytes actionCallData, uint256 amountLimit, bytes extraOptions) payable returns (bytes32 guid)",
|
|
47
|
+
"function getRequestInfo(bytes32 guid) view returns (tuple(address initiator, uint64 timestamp, uint8 actionType, bytes actionCallData, bool fulfilled, bool finalized, bool refunded, uint256 totalAssets, uint256 finalizationResult, uint256 amountLimit))",
|
|
48
|
+
"function getFinalizationResult(bytes32 guid) view returns (uint256 result)",
|
|
49
|
+
"function oraclesCrossChainAccounting() view returns (bool)",
|
|
50
|
+
"function quoteAccountingFee(bytes extraOptions) view returns (uint256 nativeFee)",
|
|
51
|
+
"function accountingBridgeFacet() view returns (uint256 sum, bool isPositive)"
|
|
52
|
+
];
|
|
53
|
+
var CONFIG_ABI = [
|
|
54
|
+
"function getEscrow() view returns (address escrow)",
|
|
55
|
+
"function getCrossChainAccountingManager() view returns (address)",
|
|
56
|
+
"function isHub() view returns (bool)",
|
|
57
|
+
"function getWithdrawalQueueStatus() view returns (bool)",
|
|
58
|
+
"function getWithdrawalTimelock() view returns (uint64)",
|
|
59
|
+
"function getWithdrawalFee() view returns (uint96)",
|
|
60
|
+
"function getMaxWithdrawalDelay() view returns (uint32)",
|
|
61
|
+
"function getAvailableAssets() view returns (address[])",
|
|
62
|
+
"function getDepositableAssets() view returns (address[])",
|
|
63
|
+
"function depositCapacity() view returns (uint256)",
|
|
64
|
+
"function fee() view returns (uint96)",
|
|
65
|
+
"function feeRecipient() view returns (address)",
|
|
66
|
+
"function paused() view returns (bool)",
|
|
67
|
+
"function maxDeposit(address receiver) view returns (uint256)"
|
|
68
|
+
];
|
|
69
|
+
var METADATA_ABI = [
|
|
70
|
+
"function name() view returns (string)",
|
|
71
|
+
"function symbol() view returns (string)",
|
|
72
|
+
"function decimals() view returns (uint8)"
|
|
73
|
+
];
|
|
74
|
+
var ERC20_ABI = [
|
|
75
|
+
"function approve(address spender, uint256 amount) returns (bool)",
|
|
76
|
+
"function allowance(address owner, address spender) view returns (uint256)",
|
|
77
|
+
"function balanceOf(address account) view returns (uint256)",
|
|
78
|
+
"function transfer(address to, uint256 amount) returns (bool)"
|
|
79
|
+
];
|
|
80
|
+
var OFT_ABI = [
|
|
81
|
+
"function send(tuple(uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam, tuple(uint256 nativeFee, uint256 lzTokenFee) fee, address refundAddress) payable returns (tuple(bytes32 guid, uint64 nonce, uint256 amountSentLD, uint256 amountReceivedLD) receipt, tuple(uint256 nativeFee, uint256 lzTokenFee) fee)",
|
|
82
|
+
"function quoteSend(tuple(uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd) sendParam, bool payInLzToken) view returns (tuple(uint256 nativeFee, uint256 lzTokenFee))"
|
|
83
|
+
];
|
|
84
|
+
|
|
85
|
+
// src/ethers/errors.ts
|
|
86
|
+
var MoreVaultsError = class extends Error {
|
|
87
|
+
constructor(message) {
|
|
88
|
+
super(message);
|
|
89
|
+
this.name = "MoreVaultsError";
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
var VaultPausedError = class extends MoreVaultsError {
|
|
93
|
+
constructor(vault) {
|
|
94
|
+
super(`[MoreVaults] Vault ${vault} is paused. Cannot perform any actions.`);
|
|
95
|
+
this.name = "VaultPausedError";
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
var CapacityFullError = class extends MoreVaultsError {
|
|
99
|
+
constructor(vault) {
|
|
100
|
+
super(`[MoreVaults] Vault ${vault} has reached deposit capacity. No more deposits accepted.`);
|
|
101
|
+
this.name = "CapacityFullError";
|
|
102
|
+
}
|
|
103
|
+
};
|
|
104
|
+
var NotWhitelistedError = class extends MoreVaultsError {
|
|
105
|
+
constructor(vault, user) {
|
|
106
|
+
super(`[MoreVaults] Address ${user} is not whitelisted to deposit in vault ${vault}.`);
|
|
107
|
+
this.name = "NotWhitelistedError";
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
var InsufficientLiquidityError = class extends MoreVaultsError {
|
|
111
|
+
hubLiquid;
|
|
112
|
+
required;
|
|
113
|
+
constructor(vault, hubLiquid, required) {
|
|
114
|
+
super(
|
|
115
|
+
`[MoreVaults] Insufficient hub liquidity for redeem.
|
|
116
|
+
Hub liquid balance : ${hubLiquid}
|
|
117
|
+
Estimated required : ${required}
|
|
118
|
+
Submitting this redeem will waste the LayerZero fee \u2014 the request will be auto-refunded.
|
|
119
|
+
Ask the vault curator to repatriate liquidity from spoke chains first.`
|
|
120
|
+
);
|
|
121
|
+
this.name = "InsufficientLiquidityError";
|
|
122
|
+
this.hubLiquid = hubLiquid;
|
|
123
|
+
this.required = required;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
var CCManagerNotConfiguredError = class extends MoreVaultsError {
|
|
127
|
+
constructor(vault) {
|
|
128
|
+
super(`[MoreVaults] CCManager not configured on vault ${vault}. Call setCrossChainAccountingManager(ccManagerAddress) as vault owner first.`);
|
|
129
|
+
this.name = "CCManagerNotConfiguredError";
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
var EscrowNotConfiguredError = class extends MoreVaultsError {
|
|
133
|
+
constructor(vault) {
|
|
134
|
+
super(`[MoreVaults] Escrow not configured for vault ${vault}. The registry must have an escrow set for this vault.`);
|
|
135
|
+
this.name = "EscrowNotConfiguredError";
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
var NotHubVaultError = class extends MoreVaultsError {
|
|
139
|
+
constructor(vault) {
|
|
140
|
+
super(`[MoreVaults] Vault ${vault} is not a hub vault. Async flows (D4/D5/R5) only work on hub vaults.`);
|
|
141
|
+
this.name = "NotHubVaultError";
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
var MissingEscrowAddressError = class extends MoreVaultsError {
|
|
145
|
+
constructor() {
|
|
146
|
+
super(`[MoreVaults] This flow requires an escrow address. Set VaultAddresses.escrow before calling async deposit/redeem flows.`);
|
|
147
|
+
this.name = "MissingEscrowAddressError";
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
async function preflightAsync(provider, vault, escrow) {
|
|
151
|
+
const config = new ethers.Contract(vault, CONFIG_ABI, provider);
|
|
152
|
+
const bridge = new ethers.Contract(vault, BRIDGE_ABI, provider);
|
|
153
|
+
const [ccManager, registeredEscrow, isHub, oraclesEnabled, isPaused] = await Promise.all([
|
|
154
|
+
config.getCrossChainAccountingManager(),
|
|
155
|
+
config.getEscrow(),
|
|
156
|
+
config.isHub(),
|
|
157
|
+
bridge.oraclesCrossChainAccounting(),
|
|
158
|
+
config.paused()
|
|
159
|
+
]);
|
|
160
|
+
if (ccManager === ethers.ZeroAddress) {
|
|
161
|
+
throw new Error(
|
|
162
|
+
`[MoreVaults] CCManager not configured on vault ${vault}. Call setCrossChainAccountingManager(ccManagerAddress) as vault owner first.`
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
if (registeredEscrow === ethers.ZeroAddress) {
|
|
166
|
+
throw new Error(
|
|
167
|
+
`[MoreVaults] Escrow not configured for vault ${vault}. The registry must have an escrow set for this vault.`
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
if (!isHub) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
`[MoreVaults] Vault ${vault} is not a hub vault. Async flows (D4/D5/R5) only work on hub vaults.`
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
if (oraclesEnabled) {
|
|
176
|
+
throw new Error(
|
|
177
|
+
`[MoreVaults] Vault ${vault} has oracle-based cross-chain accounting enabled. Use depositSimple/depositCrossChainOracleOn instead of async flows.`
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
if (isPaused) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
`[MoreVaults] Vault ${vault} is paused. Cannot perform any actions.`
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
async function preflightRedeemLiquidity(provider, vault, shares) {
|
|
187
|
+
const vaultContract = new ethers.Contract(vault, VAULT_ABI, provider);
|
|
188
|
+
const underlying = await vaultContract.asset();
|
|
189
|
+
const underlyingContract = new ethers.Contract(underlying, ERC20_ABI, provider);
|
|
190
|
+
const [hubLiquid, assetsNeeded] = await Promise.all([
|
|
191
|
+
underlyingContract.balanceOf(vault),
|
|
192
|
+
vaultContract.convertToAssets(shares)
|
|
193
|
+
]);
|
|
194
|
+
if (hubLiquid < assetsNeeded) {
|
|
195
|
+
throw new Error(
|
|
196
|
+
`[MoreVaults] Insufficient hub liquidity for redeem.
|
|
197
|
+
Hub liquid balance : ${hubLiquid}
|
|
198
|
+
Estimated required : ${assetsNeeded}
|
|
199
|
+
Submitting this redeem will waste the LayerZero fee \u2014 the request will be auto-refunded.
|
|
200
|
+
Ask the vault curator to repatriate liquidity from spoke chains first.`
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async function preflightSync(provider, vault) {
|
|
205
|
+
const config = new ethers.Contract(vault, CONFIG_ABI, provider);
|
|
206
|
+
const [isPaused, depositCapResult] = await Promise.all([
|
|
207
|
+
config.paused(),
|
|
208
|
+
config.maxDeposit(ethers.ZeroAddress).catch(() => null)
|
|
209
|
+
]);
|
|
210
|
+
if (isPaused) {
|
|
211
|
+
throw new Error(
|
|
212
|
+
`[MoreVaults] Vault ${vault} is paused. Cannot perform any actions.`
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (depositCapResult !== null && depositCapResult === 0n) {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`[MoreVaults] Vault ${vault} has reached deposit capacity. No more deposits accepted.`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
var MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
222
|
+
var MULTICALL3_ABI = [
|
|
223
|
+
"function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
|
|
224
|
+
];
|
|
225
|
+
async function ensureAllowance(signer, provider, token, spender, amount) {
|
|
226
|
+
const owner = await signer.getAddress();
|
|
227
|
+
const erc20Read = new ethers.Contract(token, ERC20_ABI, provider);
|
|
228
|
+
const current = await erc20Read.allowance(owner, spender);
|
|
229
|
+
if (current < amount) {
|
|
230
|
+
const erc20Write = new ethers.Contract(token, ERC20_ABI, signer);
|
|
231
|
+
const tx = await erc20Write.approve(spender, amount);
|
|
232
|
+
await tx.wait();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
async function quoteLzFee(provider, vault, extraOptions = "0x") {
|
|
236
|
+
const bridge = new ethers.Contract(vault, BRIDGE_ABI, provider);
|
|
237
|
+
const fee = await bridge.quoteAccountingFee(extraOptions);
|
|
238
|
+
return fee;
|
|
239
|
+
}
|
|
240
|
+
async function isAsyncMode(provider, vault) {
|
|
241
|
+
const config = new ethers.Contract(vault, CONFIG_ABI, provider);
|
|
242
|
+
const bridge = new ethers.Contract(vault, BRIDGE_ABI, provider);
|
|
243
|
+
const [isHub, oraclesEnabled] = await Promise.all([
|
|
244
|
+
config.isHub(),
|
|
245
|
+
bridge.oraclesCrossChainAccounting()
|
|
246
|
+
]);
|
|
247
|
+
if (!isHub) return false;
|
|
248
|
+
return !oraclesEnabled;
|
|
249
|
+
}
|
|
250
|
+
async function getAsyncRequestStatus(provider, vault, guid) {
|
|
251
|
+
const bridge = new ethers.Contract(vault, BRIDGE_ABI, provider);
|
|
252
|
+
const [info, finalizationResult] = await Promise.all([
|
|
253
|
+
bridge.getRequestInfo(guid),
|
|
254
|
+
bridge.getFinalizationResult(guid)
|
|
255
|
+
]);
|
|
256
|
+
return {
|
|
257
|
+
fulfilled: info.fulfilled,
|
|
258
|
+
finalized: info.finalized,
|
|
259
|
+
result: finalizationResult
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
async function getVaultStatus(provider, vault) {
|
|
263
|
+
const mc = new ethers.Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, provider);
|
|
264
|
+
const configIface = new ethers.Interface(CONFIG_ABI);
|
|
265
|
+
const bridgeIface = new ethers.Interface(BRIDGE_ABI);
|
|
266
|
+
const vaultIface = new ethers.Interface(VAULT_ABI);
|
|
267
|
+
const decimalsIface = new ethers.Interface(["function decimals() view returns (uint8)"]);
|
|
268
|
+
const b1Calls = [
|
|
269
|
+
{ target: vault, allowFailure: false, callData: configIface.encodeFunctionData("isHub") },
|
|
270
|
+
{ target: vault, allowFailure: false, callData: configIface.encodeFunctionData("paused") },
|
|
271
|
+
{ target: vault, allowFailure: false, callData: bridgeIface.encodeFunctionData("oraclesCrossChainAccounting") },
|
|
272
|
+
{ target: vault, allowFailure: false, callData: configIface.encodeFunctionData("getCrossChainAccountingManager") },
|
|
273
|
+
{ target: vault, allowFailure: false, callData: configIface.encodeFunctionData("getEscrow") },
|
|
274
|
+
{ target: vault, allowFailure: false, callData: configIface.encodeFunctionData("getWithdrawalQueueStatus") },
|
|
275
|
+
{ target: vault, allowFailure: false, callData: configIface.encodeFunctionData("getWithdrawalTimelock") },
|
|
276
|
+
// allowFailure=true: maxDeposit reverts on whitelisted vaults with address(0)
|
|
277
|
+
{ target: vault, allowFailure: true, callData: configIface.encodeFunctionData("maxDeposit", [ethers.ZeroAddress]) },
|
|
278
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("asset") },
|
|
279
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("totalAssets") },
|
|
280
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("totalSupply") },
|
|
281
|
+
{ target: vault, allowFailure: false, callData: decimalsIface.encodeFunctionData("decimals") }
|
|
282
|
+
];
|
|
283
|
+
const b1 = await mc.aggregate3.staticCall(b1Calls);
|
|
284
|
+
const isHub = configIface.decodeFunctionResult("isHub", b1[0].returnData)[0];
|
|
285
|
+
const isPaused = configIface.decodeFunctionResult("paused", b1[1].returnData)[0];
|
|
286
|
+
const oraclesEnabled = bridgeIface.decodeFunctionResult("oraclesCrossChainAccounting", b1[2].returnData)[0];
|
|
287
|
+
const ccManager = configIface.decodeFunctionResult("getCrossChainAccountingManager", b1[3].returnData)[0];
|
|
288
|
+
const escrow = configIface.decodeFunctionResult("getEscrow", b1[4].returnData)[0];
|
|
289
|
+
const withdrawalQueueEnabled = configIface.decodeFunctionResult("getWithdrawalQueueStatus", b1[5].returnData)[0];
|
|
290
|
+
const withdrawalTimelockSeconds = configIface.decodeFunctionResult("getWithdrawalTimelock", b1[6].returnData)[0];
|
|
291
|
+
const maxDepositRaw = b1[7].success ? configIface.decodeFunctionResult("maxDeposit", b1[7].returnData)[0] : null;
|
|
292
|
+
const underlying = vaultIface.decodeFunctionResult("asset", b1[8].returnData)[0];
|
|
293
|
+
const totalAssets = vaultIface.decodeFunctionResult("totalAssets", b1[9].returnData)[0];
|
|
294
|
+
const totalSupply = vaultIface.decodeFunctionResult("totalSupply", b1[10].returnData)[0];
|
|
295
|
+
const decimalsRaw = decimalsIface.decodeFunctionResult("decimals", b1[11].returnData)[0];
|
|
296
|
+
const decimalsNum = Number(decimalsRaw);
|
|
297
|
+
const oneShare = 10n ** BigInt(decimalsNum);
|
|
298
|
+
const erc20Iface = new ethers.Interface(ERC20_ABI);
|
|
299
|
+
const b2Calls = [
|
|
300
|
+
{ target: underlying, allowFailure: false, callData: erc20Iface.encodeFunctionData("balanceOf", [vault]) },
|
|
301
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("convertToAssets", [oneShare]) }
|
|
302
|
+
];
|
|
303
|
+
const b2 = await mc.aggregate3.staticCall(b2Calls);
|
|
304
|
+
const hubLiquidBalance = erc20Iface.decodeFunctionResult("balanceOf", b2[0].returnData)[0];
|
|
305
|
+
const sharePrice = vaultIface.decodeFunctionResult("convertToAssets", b2[1].returnData)[0];
|
|
306
|
+
const spokesDeployedBalance = totalAssets > hubLiquidBalance ? totalAssets - hubLiquidBalance : 0n;
|
|
307
|
+
const MAX_UINT256 = BigInt("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
|
|
308
|
+
const depositAccessRestricted = maxDepositRaw === null;
|
|
309
|
+
const effectiveCapacity = depositAccessRestricted ? MAX_UINT256 : maxDepositRaw;
|
|
310
|
+
let mode;
|
|
311
|
+
if (isPaused) {
|
|
312
|
+
mode = "paused";
|
|
313
|
+
} else if (effectiveCapacity === 0n) {
|
|
314
|
+
mode = "full";
|
|
315
|
+
} else if (!isHub) {
|
|
316
|
+
mode = "local";
|
|
317
|
+
} else if (oraclesEnabled) {
|
|
318
|
+
mode = "cross-chain-oracle";
|
|
319
|
+
} else {
|
|
320
|
+
mode = "cross-chain-async";
|
|
321
|
+
}
|
|
322
|
+
let recommendedDepositFlow;
|
|
323
|
+
let recommendedRedeemFlow;
|
|
324
|
+
if (mode === "paused" || mode === "full") {
|
|
325
|
+
recommendedDepositFlow = "none";
|
|
326
|
+
recommendedRedeemFlow = mode === "paused" ? "none" : "redeemShares";
|
|
327
|
+
} else if (mode === "cross-chain-async") {
|
|
328
|
+
recommendedDepositFlow = "depositAsync";
|
|
329
|
+
recommendedRedeemFlow = "redeemAsync";
|
|
330
|
+
} else {
|
|
331
|
+
recommendedDepositFlow = "depositSimple";
|
|
332
|
+
recommendedRedeemFlow = "redeemShares";
|
|
333
|
+
}
|
|
334
|
+
const issues = [];
|
|
335
|
+
if (isPaused) {
|
|
336
|
+
issues.push("Vault is paused \u2014 no deposits or redeems are possible.");
|
|
337
|
+
}
|
|
338
|
+
if (effectiveCapacity === 0n && !isPaused) {
|
|
339
|
+
issues.push(
|
|
340
|
+
"Deposit capacity is full \u2014 increase depositCapacity via setDepositCapacity()."
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
if (isHub && !oraclesEnabled && ccManager === ethers.ZeroAddress) {
|
|
344
|
+
issues.push(
|
|
345
|
+
"CCManager not configured \u2014 async flows will revert. Call setCrossChainAccountingManager(address) as vault owner."
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
if (isHub && !oraclesEnabled && escrow === ethers.ZeroAddress) {
|
|
349
|
+
issues.push(
|
|
350
|
+
"Escrow not configured in registry \u2014 async flows will revert. Set the escrow via the MoreVaultsRegistry."
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
if (depositAccessRestricted) {
|
|
354
|
+
issues.push("Deposit access is restricted (whitelist or other access control). Only approved addresses can deposit.");
|
|
355
|
+
}
|
|
356
|
+
const maxImmediateRedeemAssets = isHub && !oraclesEnabled ? hubLiquidBalance : totalAssets;
|
|
357
|
+
if (isHub) {
|
|
358
|
+
if (hubLiquidBalance === 0n) {
|
|
359
|
+
issues.push(
|
|
360
|
+
`Hub has no liquid assets (hubLiquidBalance = 0). All redeems will be auto-refunded until the curator repatriates funds from spokes via executeBridging().`
|
|
361
|
+
);
|
|
362
|
+
} else if (totalAssets > 0n && hubLiquidBalance * 10n < totalAssets) {
|
|
363
|
+
const pct = Number(hubLiquidBalance * 10000n / totalAssets) / 100;
|
|
364
|
+
issues.push(
|
|
365
|
+
`Low hub liquidity: ${hubLiquidBalance} units liquid on hub (${pct.toFixed(1)}% of TVL). Redeems above ${hubLiquidBalance} underlying units will be auto-refunded. Curator must call executeBridging() to repatriate from spokes.`
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
if (spokesDeployedBalance > 0n) {
|
|
369
|
+
const total = totalAssets;
|
|
370
|
+
issues.push(
|
|
371
|
+
`${spokesDeployedBalance} units (~${(Number(spokesDeployedBalance) / Number(total || 1n) * 100).toFixed(1)}% of TVL) are deployed on spoke chains earning yield. These are NOT immediately redeemable \u2014 they require a curator repatriation (executeBridging) before users can withdraw them.`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
mode,
|
|
377
|
+
recommendedDepositFlow,
|
|
378
|
+
recommendedRedeemFlow,
|
|
379
|
+
isHub,
|
|
380
|
+
isPaused,
|
|
381
|
+
oracleAccountingEnabled: oraclesEnabled,
|
|
382
|
+
ccManager,
|
|
383
|
+
escrow,
|
|
384
|
+
withdrawalQueueEnabled,
|
|
385
|
+
withdrawalTimelockSeconds: BigInt(withdrawalTimelockSeconds),
|
|
386
|
+
remainingDepositCapacity: effectiveCapacity,
|
|
387
|
+
depositAccessRestricted,
|
|
388
|
+
underlying,
|
|
389
|
+
totalAssets,
|
|
390
|
+
totalSupply,
|
|
391
|
+
decimals: decimalsNum,
|
|
392
|
+
sharePrice,
|
|
393
|
+
hubLiquidBalance,
|
|
394
|
+
spokesDeployedBalance,
|
|
395
|
+
maxImmediateRedeemAssets,
|
|
396
|
+
issues
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// src/ethers/depositFlows.ts
|
|
401
|
+
async function ensureAllowance2(signer, token, spender, amount) {
|
|
402
|
+
const owner = await signer.getAddress();
|
|
403
|
+
const erc20 = new ethers.Contract(token, ERC20_ABI, signer);
|
|
404
|
+
const current = await erc20.allowance(owner, spender);
|
|
405
|
+
if (current < amount) {
|
|
406
|
+
const tx = await erc20.approve(spender, amount);
|
|
407
|
+
await tx.wait();
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
async function depositSimple(signer, addresses, assets, receiver) {
|
|
411
|
+
const provider = signer.provider;
|
|
412
|
+
await preflightSync(provider, addresses.vault);
|
|
413
|
+
const vault = new ethers.Contract(addresses.vault, VAULT_ABI, signer);
|
|
414
|
+
const underlying = await vault.asset();
|
|
415
|
+
await ensureAllowance2(signer, underlying, addresses.vault, assets);
|
|
416
|
+
const tx = await vault["deposit(uint256,address)"](assets, receiver);
|
|
417
|
+
const receipt = await tx.wait();
|
|
418
|
+
let shares = 0n;
|
|
419
|
+
for (const log of receipt.logs) {
|
|
420
|
+
try {
|
|
421
|
+
const parsed = vault.interface.parseLog({
|
|
422
|
+
topics: log.topics,
|
|
423
|
+
data: log.data
|
|
424
|
+
});
|
|
425
|
+
if (parsed && parsed.name === "Transfer" && parsed.args[0] === "0x0000000000000000000000000000000000000000") {
|
|
426
|
+
shares = parsed.args[2];
|
|
427
|
+
break;
|
|
428
|
+
}
|
|
429
|
+
} catch {
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return { receipt, shares };
|
|
433
|
+
}
|
|
434
|
+
async function depositMultiAsset(signer, addresses, tokens, amounts, receiver, minShares) {
|
|
435
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
436
|
+
await ensureAllowance2(signer, tokens[i], addresses.vault, amounts[i]);
|
|
437
|
+
}
|
|
438
|
+
const vault = new ethers.Contract(addresses.vault, VAULT_ABI, signer);
|
|
439
|
+
const tx = await vault["deposit(address[],uint256[],address,uint256)"](tokens, amounts, receiver, minShares);
|
|
440
|
+
const receipt = await tx.wait();
|
|
441
|
+
let shares = 0n;
|
|
442
|
+
for (const log of receipt.logs) {
|
|
443
|
+
try {
|
|
444
|
+
const parsed = vault.interface.parseLog({
|
|
445
|
+
topics: log.topics,
|
|
446
|
+
data: log.data
|
|
447
|
+
});
|
|
448
|
+
if (parsed && parsed.name === "Deposit") {
|
|
449
|
+
shares = parsed.args[4];
|
|
450
|
+
break;
|
|
451
|
+
}
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
return { receipt, shares };
|
|
456
|
+
}
|
|
457
|
+
var depositCrossChainOracleOn = depositSimple;
|
|
458
|
+
async function depositAsync(signer, addresses, assets, receiver, lzFee, extraOptions = "0x") {
|
|
459
|
+
const provider = signer.provider;
|
|
460
|
+
if (!addresses.escrow) throw new MissingEscrowAddressError();
|
|
461
|
+
const escrow = addresses.escrow;
|
|
462
|
+
await preflightAsync(provider, addresses.vault);
|
|
463
|
+
const vault = new ethers.Contract(addresses.vault, VAULT_ABI, signer);
|
|
464
|
+
const underlying = await vault.asset();
|
|
465
|
+
await ensureAllowance2(signer, underlying, escrow, assets);
|
|
466
|
+
const coder = ethers.AbiCoder.defaultAbiCoder();
|
|
467
|
+
const actionCallData = coder.encode(
|
|
468
|
+
["uint256", "address"],
|
|
469
|
+
[assets, receiver]
|
|
470
|
+
);
|
|
471
|
+
const bridge = new ethers.Contract(addresses.vault, BRIDGE_ABI, signer);
|
|
472
|
+
const guid = await bridge.initVaultActionRequest.staticCall(
|
|
473
|
+
ActionType.DEPOSIT,
|
|
474
|
+
actionCallData,
|
|
475
|
+
0,
|
|
476
|
+
extraOptions,
|
|
477
|
+
{ value: lzFee }
|
|
478
|
+
);
|
|
479
|
+
const tx = await bridge.initVaultActionRequest(
|
|
480
|
+
ActionType.DEPOSIT,
|
|
481
|
+
actionCallData,
|
|
482
|
+
0,
|
|
483
|
+
// amountLimit = 0 for deposits (minAmountOut handled by cross-chain manager)
|
|
484
|
+
extraOptions,
|
|
485
|
+
{ value: lzFee }
|
|
486
|
+
);
|
|
487
|
+
const receipt = await tx.wait();
|
|
488
|
+
return { receipt, guid };
|
|
489
|
+
}
|
|
490
|
+
async function mintAsync(signer, addresses, shares, maxAssets, receiver, lzFee, extraOptions = "0x") {
|
|
491
|
+
const provider = signer.provider;
|
|
492
|
+
if (!addresses.escrow) throw new MissingEscrowAddressError();
|
|
493
|
+
const escrow = addresses.escrow;
|
|
494
|
+
await preflightAsync(provider, addresses.vault);
|
|
495
|
+
const vault = new ethers.Contract(addresses.vault, VAULT_ABI, signer);
|
|
496
|
+
const underlying = await vault.asset();
|
|
497
|
+
await ensureAllowance2(signer, underlying, escrow, maxAssets);
|
|
498
|
+
const coder = ethers.AbiCoder.defaultAbiCoder();
|
|
499
|
+
const actionCallData = coder.encode(
|
|
500
|
+
["uint256", "address"],
|
|
501
|
+
[shares, receiver]
|
|
502
|
+
);
|
|
503
|
+
const bridge = new ethers.Contract(addresses.vault, BRIDGE_ABI, signer);
|
|
504
|
+
const guid = await bridge.initVaultActionRequest.staticCall(
|
|
505
|
+
ActionType.MINT,
|
|
506
|
+
actionCallData,
|
|
507
|
+
maxAssets,
|
|
508
|
+
extraOptions,
|
|
509
|
+
{ value: lzFee }
|
|
510
|
+
);
|
|
511
|
+
const tx = await bridge.initVaultActionRequest(
|
|
512
|
+
ActionType.MINT,
|
|
513
|
+
actionCallData,
|
|
514
|
+
maxAssets,
|
|
515
|
+
extraOptions,
|
|
516
|
+
{ value: lzFee }
|
|
517
|
+
);
|
|
518
|
+
const receipt = await tx.wait();
|
|
519
|
+
return { receipt, guid };
|
|
520
|
+
}
|
|
521
|
+
async function smartDeposit(signer, provider, addresses, assets, receiver, extraOptions = "0x") {
|
|
522
|
+
const vault = addresses.vault;
|
|
523
|
+
const status = await getVaultStatus(provider, vault);
|
|
524
|
+
if (status.mode === "paused") {
|
|
525
|
+
throw new VaultPausedError(vault);
|
|
526
|
+
}
|
|
527
|
+
if (status.mode === "full") {
|
|
528
|
+
throw new CapacityFullError(vault);
|
|
529
|
+
}
|
|
530
|
+
if (status.recommendedDepositFlow === "depositAsync") {
|
|
531
|
+
const lzFee = await quoteLzFee(provider, vault, extraOptions);
|
|
532
|
+
return depositAsync(signer, addresses, assets, receiver, lzFee, extraOptions);
|
|
533
|
+
}
|
|
534
|
+
return depositSimple(signer, addresses, assets, receiver);
|
|
535
|
+
}
|
|
536
|
+
async function ensureAllowance3(signer, token, spender, amount) {
|
|
537
|
+
const owner = await signer.getAddress();
|
|
538
|
+
const erc20 = new ethers.Contract(token, ERC20_ABI, signer);
|
|
539
|
+
const current = await erc20.allowance(owner, spender);
|
|
540
|
+
if (current < amount) {
|
|
541
|
+
const tx = await erc20.approve(spender, amount);
|
|
542
|
+
await tx.wait();
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
async function depositFromSpoke(signer, spokeOFT, hubEid, spokeEid, amount, receiver, lzFee, minMsgValue = 0n, minSharesOut = 0n, minAmountLD, extraOptions = "0x") {
|
|
546
|
+
await ensureAllowance3(signer, spokeOFT, spokeOFT, amount);
|
|
547
|
+
const oft = new ethers.Contract(spokeOFT, OFT_ABI, signer);
|
|
548
|
+
const refundAddress = await signer.getAddress();
|
|
549
|
+
const receiverBytes32 = ethers.zeroPadValue(receiver, 32);
|
|
550
|
+
const hopSendParam = {
|
|
551
|
+
dstEid: spokeEid,
|
|
552
|
+
to: receiverBytes32,
|
|
553
|
+
amountLD: 0n,
|
|
554
|
+
minAmountLD: minSharesOut,
|
|
555
|
+
extraOptions: "0x",
|
|
556
|
+
composeMsg: "0x",
|
|
557
|
+
oftCmd: "0x"
|
|
558
|
+
};
|
|
559
|
+
const coder = ethers.AbiCoder.defaultAbiCoder();
|
|
560
|
+
const composeMsg = coder.encode(
|
|
561
|
+
[
|
|
562
|
+
"tuple(uint32 dstEid, bytes32 to, uint256 amountLD, uint256 minAmountLD, bytes extraOptions, bytes composeMsg, bytes oftCmd)",
|
|
563
|
+
"uint256"
|
|
564
|
+
],
|
|
565
|
+
[hopSendParam, minMsgValue]
|
|
566
|
+
);
|
|
567
|
+
const sendParam = {
|
|
568
|
+
dstEid: hubEid,
|
|
569
|
+
to: receiverBytes32,
|
|
570
|
+
amountLD: amount,
|
|
571
|
+
minAmountLD: minAmountLD ?? amount,
|
|
572
|
+
extraOptions,
|
|
573
|
+
composeMsg,
|
|
574
|
+
oftCmd: "0x"
|
|
575
|
+
};
|
|
576
|
+
const msgFee = { nativeFee: lzFee, lzTokenFee: 0n };
|
|
577
|
+
const tx = await oft.send(sendParam, msgFee, refundAddress, {
|
|
578
|
+
value: lzFee
|
|
579
|
+
});
|
|
580
|
+
const receipt = await tx.wait();
|
|
581
|
+
return { receipt };
|
|
582
|
+
}
|
|
583
|
+
var depositFromSpokeAsync = depositFromSpoke;
|
|
584
|
+
async function quoteDepositFromSpokeFee(provider, spokeOFT, hubEid, spokeEid, amount, receiver, minMsgValue = 0n, minSharesOut = 0n, minAmountLD, extraOptions = "0x") {
|
|
585
|
+
const receiverBytes32 = ethers.zeroPadValue(receiver, 32);
|
|
586
|
+
const hopSendParam = {
|
|
587
|
+
dstEid: spokeEid,
|
|
588
|
+
to: receiverBytes32,
|
|
589
|
+
amountLD: 0n,
|
|
590
|
+
minAmountLD: minSharesOut,
|
|
591
|
+
extraOptions: "0x",
|
|
592
|
+
composeMsg: "0x",
|
|
593
|
+
oftCmd: "0x"
|
|
594
|
+
};
|
|
595
|
+
const coder = ethers.AbiCoder.defaultAbiCoder();
|
|
596
|
+
const composeMsgBytes = coder.encode(
|
|
597
|
+
["tuple(uint32,bytes32,uint256,uint256,bytes,bytes,bytes)", "uint256"],
|
|
598
|
+
[
|
|
599
|
+
[
|
|
600
|
+
hopSendParam.dstEid,
|
|
601
|
+
hopSendParam.to,
|
|
602
|
+
hopSendParam.amountLD,
|
|
603
|
+
hopSendParam.minAmountLD,
|
|
604
|
+
hopSendParam.extraOptions,
|
|
605
|
+
hopSendParam.composeMsg,
|
|
606
|
+
hopSendParam.oftCmd
|
|
607
|
+
],
|
|
608
|
+
minMsgValue
|
|
609
|
+
]
|
|
610
|
+
);
|
|
611
|
+
const sendParam = {
|
|
612
|
+
dstEid: hubEid,
|
|
613
|
+
to: receiverBytes32,
|
|
614
|
+
amountLD: amount,
|
|
615
|
+
minAmountLD: minAmountLD ?? amount,
|
|
616
|
+
extraOptions,
|
|
617
|
+
composeMsg: composeMsgBytes,
|
|
618
|
+
oftCmd: "0x"
|
|
619
|
+
};
|
|
620
|
+
const oft = new ethers.Contract(spokeOFT, OFT_ABI, provider);
|
|
621
|
+
const fee = await oft.quoteSend(sendParam, false);
|
|
622
|
+
return fee.nativeFee;
|
|
623
|
+
}
|
|
624
|
+
async function ensureAllowance4(signer, token, spender, amount) {
|
|
625
|
+
const owner = await signer.getAddress();
|
|
626
|
+
const erc20 = new ethers.Contract(token, ERC20_ABI, signer);
|
|
627
|
+
const current = await erc20.allowance(owner, spender);
|
|
628
|
+
if (current < amount) {
|
|
629
|
+
const tx = await erc20.approve(spender, amount);
|
|
630
|
+
await tx.wait();
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
async function redeemShares(signer, addresses, shares, receiver, owner) {
|
|
634
|
+
const vault = new ethers.Contract(addresses.vault, VAULT_ABI, signer);
|
|
635
|
+
const assets = await vault.redeem.staticCall(shares, receiver, owner);
|
|
636
|
+
const tx = await vault.redeem(shares, receiver, owner);
|
|
637
|
+
const receipt = await tx.wait();
|
|
638
|
+
return { receipt, assets };
|
|
639
|
+
}
|
|
640
|
+
async function withdrawAssets(signer, addresses, assets, receiver, owner) {
|
|
641
|
+
const vault = new ethers.Contract(addresses.vault, VAULT_ABI, signer);
|
|
642
|
+
const tx = await vault.withdraw(assets, receiver, owner);
|
|
643
|
+
const receipt = await tx.wait();
|
|
644
|
+
return { receipt, assets };
|
|
645
|
+
}
|
|
646
|
+
async function requestRedeem(signer, addresses, shares, owner) {
|
|
647
|
+
const vault = new ethers.Contract(addresses.vault, VAULT_ABI, signer);
|
|
648
|
+
const tx = await vault.requestRedeem(shares, owner);
|
|
649
|
+
const receipt = await tx.wait();
|
|
650
|
+
return { receipt };
|
|
651
|
+
}
|
|
652
|
+
async function getWithdrawalRequest(provider, vault, owner) {
|
|
653
|
+
const vaultContract = new ethers.Contract(vault, VAULT_ABI, provider);
|
|
654
|
+
const [shares, timelockEndsAt] = await vaultContract.getWithdrawalRequest(owner);
|
|
655
|
+
if (shares === 0n) {
|
|
656
|
+
return null;
|
|
657
|
+
}
|
|
658
|
+
return { shares, timelockEndsAt };
|
|
659
|
+
}
|
|
660
|
+
async function redeemAsync(signer, addresses, shares, receiver, owner, lzFee, extraOptions = "0x") {
|
|
661
|
+
const provider = signer.provider;
|
|
662
|
+
if (!addresses.escrow) throw new MissingEscrowAddressError();
|
|
663
|
+
const escrow = addresses.escrow;
|
|
664
|
+
await preflightAsync(provider, addresses.vault);
|
|
665
|
+
await preflightRedeemLiquidity(provider, addresses.vault, shares);
|
|
666
|
+
await ensureAllowance4(signer, addresses.vault, escrow, shares);
|
|
667
|
+
const coder = ethers.AbiCoder.defaultAbiCoder();
|
|
668
|
+
const actionCallData = coder.encode(
|
|
669
|
+
["uint256", "address", "address"],
|
|
670
|
+
[shares, receiver, owner]
|
|
671
|
+
);
|
|
672
|
+
const bridge = new ethers.Contract(addresses.vault, BRIDGE_ABI, signer);
|
|
673
|
+
const guid = await bridge.initVaultActionRequest.staticCall(
|
|
674
|
+
ActionType.REDEEM,
|
|
675
|
+
actionCallData,
|
|
676
|
+
0,
|
|
677
|
+
extraOptions,
|
|
678
|
+
{ value: lzFee }
|
|
679
|
+
);
|
|
680
|
+
const tx = await bridge.initVaultActionRequest(
|
|
681
|
+
ActionType.REDEEM,
|
|
682
|
+
actionCallData,
|
|
683
|
+
0,
|
|
684
|
+
// amountLimit MUST be 0 for redeems
|
|
685
|
+
extraOptions,
|
|
686
|
+
{ value: lzFee }
|
|
687
|
+
);
|
|
688
|
+
const receipt = await tx.wait();
|
|
689
|
+
return { receipt, guid };
|
|
690
|
+
}
|
|
691
|
+
async function bridgeSharesToHub(signer, shareOFT, hubChainEid, shares, receiver, lzFee) {
|
|
692
|
+
await ensureAllowance4(signer, shareOFT, shareOFT, shares);
|
|
693
|
+
const oft = new ethers.Contract(shareOFT, OFT_ABI, signer);
|
|
694
|
+
const refundAddress = await signer.getAddress();
|
|
695
|
+
const toBytes32 = ethers.zeroPadValue(receiver, 32);
|
|
696
|
+
const sendParam = {
|
|
697
|
+
dstEid: hubChainEid,
|
|
698
|
+
to: toBytes32,
|
|
699
|
+
amountLD: shares,
|
|
700
|
+
minAmountLD: shares,
|
|
701
|
+
// no slippage on share bridging
|
|
702
|
+
extraOptions: "0x",
|
|
703
|
+
composeMsg: "0x",
|
|
704
|
+
oftCmd: "0x"
|
|
705
|
+
};
|
|
706
|
+
const msgFee = { nativeFee: lzFee, lzTokenFee: 0n };
|
|
707
|
+
const tx = await oft.send(sendParam, msgFee, refundAddress, {
|
|
708
|
+
value: lzFee
|
|
709
|
+
});
|
|
710
|
+
const receipt = await tx.wait();
|
|
711
|
+
return { receipt };
|
|
712
|
+
}
|
|
713
|
+
var MULTICALL3_ADDRESS2 = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
714
|
+
var MULTICALL3_ABI2 = [
|
|
715
|
+
"function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
|
|
716
|
+
];
|
|
717
|
+
async function getUserPosition(provider, vault, user) {
|
|
718
|
+
const mc = new ethers.Contract(MULTICALL3_ADDRESS2, MULTICALL3_ABI2, provider);
|
|
719
|
+
const vaultIface = new ethers.Interface(VAULT_ABI);
|
|
720
|
+
const decimalsIface = new ethers.Interface(["function decimals() view returns (uint8)"]);
|
|
721
|
+
const b1Calls = [
|
|
722
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("balanceOf", [user]) },
|
|
723
|
+
{ target: vault, allowFailure: false, callData: decimalsIface.encodeFunctionData("decimals") },
|
|
724
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("getWithdrawalRequest", [user]) }
|
|
725
|
+
];
|
|
726
|
+
const [b1Raw, block] = await Promise.all([
|
|
727
|
+
mc.aggregate3.staticCall(b1Calls),
|
|
728
|
+
provider.getBlock("latest")
|
|
729
|
+
]);
|
|
730
|
+
const shares = vaultIface.decodeFunctionResult("balanceOf", b1Raw[0].returnData)[0];
|
|
731
|
+
const decimalsRaw = decimalsIface.decodeFunctionResult("decimals", b1Raw[1].returnData)[0];
|
|
732
|
+
const decimals = Number(decimalsRaw);
|
|
733
|
+
const withdrawalResult = vaultIface.decodeFunctionResult("getWithdrawalRequest", b1Raw[2].returnData);
|
|
734
|
+
const withdrawalRequest = [withdrawalResult[0], withdrawalResult[1]];
|
|
735
|
+
const [withdrawShares, timelockEndsAt] = withdrawalRequest;
|
|
736
|
+
const vaultContract = new ethers.Contract(vault, VAULT_ABI, provider);
|
|
737
|
+
const oneShare = 10n ** BigInt(decimals);
|
|
738
|
+
const [estimatedAssets, sharePrice] = await Promise.all([
|
|
739
|
+
shares === 0n ? Promise.resolve(0n) : vaultContract.convertToAssets(shares),
|
|
740
|
+
vaultContract.convertToAssets(oneShare)
|
|
741
|
+
]);
|
|
742
|
+
const currentTimestamp = BigInt(block.timestamp);
|
|
743
|
+
const pendingWithdrawal = withdrawShares === 0n ? null : {
|
|
744
|
+
shares: withdrawShares,
|
|
745
|
+
timelockEndsAt,
|
|
746
|
+
canRedeemNow: timelockEndsAt === 0n || currentTimestamp >= timelockEndsAt
|
|
747
|
+
};
|
|
748
|
+
return {
|
|
749
|
+
shares,
|
|
750
|
+
estimatedAssets,
|
|
751
|
+
sharePrice,
|
|
752
|
+
decimals,
|
|
753
|
+
pendingWithdrawal
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
async function previewDeposit(provider, vault, assets) {
|
|
757
|
+
const vaultContract = new ethers.Contract(vault, VAULT_ABI, provider);
|
|
758
|
+
return vaultContract.previewDeposit(assets);
|
|
759
|
+
}
|
|
760
|
+
async function previewRedeem(provider, vault, shares) {
|
|
761
|
+
const vaultContract = new ethers.Contract(vault, VAULT_ABI, provider);
|
|
762
|
+
return vaultContract.previewRedeem(shares);
|
|
763
|
+
}
|
|
764
|
+
async function canDeposit(provider, vault, user) {
|
|
765
|
+
const config = new ethers.Contract(vault, CONFIG_ABI, provider);
|
|
766
|
+
const isPaused = await config.paused();
|
|
767
|
+
if (isPaused) {
|
|
768
|
+
return { allowed: false, reason: "paused" };
|
|
769
|
+
}
|
|
770
|
+
let maxDepositAmount;
|
|
771
|
+
try {
|
|
772
|
+
maxDepositAmount = await config.maxDeposit(user);
|
|
773
|
+
} catch {
|
|
774
|
+
return { allowed: false, reason: "not-whitelisted" };
|
|
775
|
+
}
|
|
776
|
+
if (maxDepositAmount === 0n) {
|
|
777
|
+
return { allowed: false, reason: "capacity-full" };
|
|
778
|
+
}
|
|
779
|
+
return { allowed: true, reason: "ok" };
|
|
780
|
+
}
|
|
781
|
+
async function getVaultMetadata(provider, vault) {
|
|
782
|
+
const MULTICALL3_ADDRESS3 = "0xcA11bde05977b3631167028862bE2a173976CA11";
|
|
783
|
+
const MULTICALL3_ABI3 = [
|
|
784
|
+
"function aggregate3(tuple(address target, bool allowFailure, bytes callData)[] calls) payable returns (tuple(bool success, bytes returnData)[] returnData)"
|
|
785
|
+
];
|
|
786
|
+
const mc = new ethers.Contract(MULTICALL3_ADDRESS3, MULTICALL3_ABI3, provider);
|
|
787
|
+
const metaIface = new ethers.Interface(METADATA_ABI);
|
|
788
|
+
const vaultIface = new ethers.Interface(VAULT_ABI);
|
|
789
|
+
const b1Calls = [
|
|
790
|
+
{ target: vault, allowFailure: false, callData: metaIface.encodeFunctionData("name") },
|
|
791
|
+
{ target: vault, allowFailure: false, callData: metaIface.encodeFunctionData("symbol") },
|
|
792
|
+
{ target: vault, allowFailure: false, callData: metaIface.encodeFunctionData("decimals") },
|
|
793
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("asset") }
|
|
794
|
+
];
|
|
795
|
+
const b1 = await mc.aggregate3.staticCall(b1Calls);
|
|
796
|
+
const name = metaIface.decodeFunctionResult("name", b1[0].returnData)[0];
|
|
797
|
+
const symbol = metaIface.decodeFunctionResult("symbol", b1[1].returnData)[0];
|
|
798
|
+
const decimals = Number(metaIface.decodeFunctionResult("decimals", b1[2].returnData)[0]);
|
|
799
|
+
const underlying = vaultIface.decodeFunctionResult("asset", b1[3].returnData)[0];
|
|
800
|
+
const b2Calls = [
|
|
801
|
+
{ target: underlying, allowFailure: false, callData: metaIface.encodeFunctionData("symbol") },
|
|
802
|
+
{ target: underlying, allowFailure: false, callData: metaIface.encodeFunctionData("decimals") }
|
|
803
|
+
];
|
|
804
|
+
const b2 = await mc.aggregate3.staticCall(b2Calls);
|
|
805
|
+
const underlyingSymbol = metaIface.decodeFunctionResult("symbol", b2[0].returnData)[0];
|
|
806
|
+
const underlyingDecimals = Number(metaIface.decodeFunctionResult("decimals", b2[1].returnData)[0]);
|
|
807
|
+
return {
|
|
808
|
+
name,
|
|
809
|
+
symbol,
|
|
810
|
+
decimals,
|
|
811
|
+
underlying,
|
|
812
|
+
underlyingSymbol,
|
|
813
|
+
underlyingDecimals
|
|
814
|
+
};
|
|
815
|
+
}
|
|
816
|
+
async function getAsyncRequestStatusLabel(provider, vault, guid) {
|
|
817
|
+
const bridge = new ethers.Contract(vault, BRIDGE_ABI, provider);
|
|
818
|
+
const [info, finalizationResult] = await Promise.all([
|
|
819
|
+
bridge.getRequestInfo(guid),
|
|
820
|
+
bridge.getFinalizationResult(guid)
|
|
821
|
+
]);
|
|
822
|
+
if (info.refunded) {
|
|
823
|
+
return {
|
|
824
|
+
status: "refunded",
|
|
825
|
+
label: "Request refunded \u2014 tokens returned to initiator",
|
|
826
|
+
result: 0n
|
|
827
|
+
};
|
|
828
|
+
}
|
|
829
|
+
if (info.finalized) {
|
|
830
|
+
return {
|
|
831
|
+
status: "completed",
|
|
832
|
+
label: "Completed",
|
|
833
|
+
result: finalizationResult
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
if (info.fulfilled) {
|
|
837
|
+
return {
|
|
838
|
+
status: "ready-to-execute",
|
|
839
|
+
label: "Oracle responded \u2014 ready to execute",
|
|
840
|
+
result: 0n
|
|
841
|
+
};
|
|
842
|
+
}
|
|
843
|
+
return {
|
|
844
|
+
status: "pending",
|
|
845
|
+
label: "Waiting for cross-chain oracle response...",
|
|
846
|
+
result: 0n
|
|
847
|
+
};
|
|
848
|
+
}
|
|
849
|
+
async function getUserBalances(provider, vault, user) {
|
|
850
|
+
const mc = new ethers.Contract(MULTICALL3_ADDRESS2, MULTICALL3_ABI2, provider);
|
|
851
|
+
const vaultIface = new ethers.Interface(VAULT_ABI);
|
|
852
|
+
const decimalsIface = new ethers.Interface(["function decimals() view returns (uint8)"]);
|
|
853
|
+
const b1Calls = [
|
|
854
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("balanceOf", [user]) },
|
|
855
|
+
{ target: vault, allowFailure: false, callData: decimalsIface.encodeFunctionData("decimals") },
|
|
856
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("asset") }
|
|
857
|
+
];
|
|
858
|
+
const b1 = await mc.aggregate3.staticCall(b1Calls);
|
|
859
|
+
const shareBalance = vaultIface.decodeFunctionResult("balanceOf", b1[0].returnData)[0];
|
|
860
|
+
const underlying = vaultIface.decodeFunctionResult("asset", b1[2].returnData)[0];
|
|
861
|
+
const [underlyingBalance, estimatedAssets] = await Promise.all([
|
|
862
|
+
new ethers.Contract(underlying, ERC20_ABI, provider).balanceOf(user),
|
|
863
|
+
shareBalance === 0n ? Promise.resolve(0n) : new ethers.Contract(vault, VAULT_ABI, provider).convertToAssets(shareBalance)
|
|
864
|
+
]);
|
|
865
|
+
return {
|
|
866
|
+
shareBalance,
|
|
867
|
+
underlyingBalance,
|
|
868
|
+
estimatedAssets
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
async function getMaxWithdrawable(provider, vault, user) {
|
|
872
|
+
const mc = new ethers.Contract(MULTICALL3_ADDRESS2, MULTICALL3_ABI2, provider);
|
|
873
|
+
const configIface = new ethers.Interface(CONFIG_ABI);
|
|
874
|
+
const bridgeIface = new ethers.Interface(BRIDGE_ABI);
|
|
875
|
+
const vaultIface = new ethers.Interface(VAULT_ABI);
|
|
876
|
+
const erc20Iface = new ethers.Interface(ERC20_ABI);
|
|
877
|
+
const b1Calls = [
|
|
878
|
+
{ target: vault, allowFailure: false, callData: configIface.encodeFunctionData("isHub") },
|
|
879
|
+
{ target: vault, allowFailure: false, callData: bridgeIface.encodeFunctionData("oraclesCrossChainAccounting") },
|
|
880
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("balanceOf", [user]) },
|
|
881
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("asset") }
|
|
882
|
+
];
|
|
883
|
+
const b1 = await mc.aggregate3.staticCall(b1Calls);
|
|
884
|
+
const isHub = configIface.decodeFunctionResult("isHub", b1[0].returnData)[0];
|
|
885
|
+
const oraclesEnabled = bridgeIface.decodeFunctionResult("oraclesCrossChainAccounting", b1[1].returnData)[0];
|
|
886
|
+
const userShares = vaultIface.decodeFunctionResult("balanceOf", b1[2].returnData)[0];
|
|
887
|
+
const underlying = vaultIface.decodeFunctionResult("asset", b1[3].returnData)[0];
|
|
888
|
+
if (userShares === 0n) {
|
|
889
|
+
return { shares: 0n, assets: 0n };
|
|
890
|
+
}
|
|
891
|
+
const b2Calls = [
|
|
892
|
+
{ target: vault, allowFailure: false, callData: vaultIface.encodeFunctionData("convertToAssets", [userShares]) },
|
|
893
|
+
{ target: underlying, allowFailure: false, callData: erc20Iface.encodeFunctionData("balanceOf", [vault]) }
|
|
894
|
+
];
|
|
895
|
+
const b2 = await mc.aggregate3.staticCall(b2Calls);
|
|
896
|
+
const estimatedAssets = vaultIface.decodeFunctionResult("convertToAssets", b2[0].returnData)[0];
|
|
897
|
+
const hubLiquidBalance = erc20Iface.decodeFunctionResult("balanceOf", b2[1].returnData)[0];
|
|
898
|
+
let maxAssets;
|
|
899
|
+
if (isHub && !oraclesEnabled) {
|
|
900
|
+
maxAssets = estimatedAssets < hubLiquidBalance ? estimatedAssets : hubLiquidBalance;
|
|
901
|
+
} else {
|
|
902
|
+
maxAssets = estimatedAssets;
|
|
903
|
+
}
|
|
904
|
+
let maxShares;
|
|
905
|
+
if (maxAssets < estimatedAssets) {
|
|
906
|
+
const vaultContract = new ethers.Contract(vault, VAULT_ABI, provider);
|
|
907
|
+
maxShares = await vaultContract.convertToShares(maxAssets);
|
|
908
|
+
} else {
|
|
909
|
+
maxShares = userShares;
|
|
910
|
+
}
|
|
911
|
+
return {
|
|
912
|
+
shares: maxShares,
|
|
913
|
+
assets: maxAssets
|
|
914
|
+
};
|
|
915
|
+
}
|
|
916
|
+
async function getVaultSummary(provider, vault) {
|
|
917
|
+
const [status, metadata] = await Promise.all([
|
|
918
|
+
getVaultStatus(provider, vault),
|
|
919
|
+
getVaultMetadata(provider, vault)
|
|
920
|
+
]);
|
|
921
|
+
return { ...status, ...metadata };
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
exports.ActionType = ActionType;
|
|
925
|
+
exports.BRIDGE_ABI = BRIDGE_ABI;
|
|
926
|
+
exports.CCManagerNotConfiguredError = CCManagerNotConfiguredError;
|
|
927
|
+
exports.CONFIG_ABI = CONFIG_ABI;
|
|
928
|
+
exports.CapacityFullError = CapacityFullError;
|
|
929
|
+
exports.ERC20_ABI = ERC20_ABI;
|
|
930
|
+
exports.EscrowNotConfiguredError = EscrowNotConfiguredError;
|
|
931
|
+
exports.InsufficientLiquidityError = InsufficientLiquidityError;
|
|
932
|
+
exports.METADATA_ABI = METADATA_ABI;
|
|
933
|
+
exports.MissingEscrowAddressError = MissingEscrowAddressError;
|
|
934
|
+
exports.MoreVaultsError = MoreVaultsError;
|
|
935
|
+
exports.NotHubVaultError = NotHubVaultError;
|
|
936
|
+
exports.NotWhitelistedError = NotWhitelistedError;
|
|
937
|
+
exports.OFT_ABI = OFT_ABI;
|
|
938
|
+
exports.VAULT_ABI = VAULT_ABI;
|
|
939
|
+
exports.VaultPausedError = VaultPausedError;
|
|
940
|
+
exports.bridgeSharesToHub = bridgeSharesToHub;
|
|
941
|
+
exports.canDeposit = canDeposit;
|
|
942
|
+
exports.depositAsync = depositAsync;
|
|
943
|
+
exports.depositCrossChainOracleOn = depositCrossChainOracleOn;
|
|
944
|
+
exports.depositFromSpoke = depositFromSpoke;
|
|
945
|
+
exports.depositFromSpokeAsync = depositFromSpokeAsync;
|
|
946
|
+
exports.depositMultiAsset = depositMultiAsset;
|
|
947
|
+
exports.depositSimple = depositSimple;
|
|
948
|
+
exports.ensureAllowance = ensureAllowance;
|
|
949
|
+
exports.getAsyncRequestStatus = getAsyncRequestStatus;
|
|
950
|
+
exports.getAsyncRequestStatusLabel = getAsyncRequestStatusLabel;
|
|
951
|
+
exports.getMaxWithdrawable = getMaxWithdrawable;
|
|
952
|
+
exports.getUserBalances = getUserBalances;
|
|
953
|
+
exports.getUserPosition = getUserPosition;
|
|
954
|
+
exports.getVaultMetadata = getVaultMetadata;
|
|
955
|
+
exports.getVaultStatus = getVaultStatus;
|
|
956
|
+
exports.getVaultSummary = getVaultSummary;
|
|
957
|
+
exports.getWithdrawalRequest = getWithdrawalRequest;
|
|
958
|
+
exports.isAsyncMode = isAsyncMode;
|
|
959
|
+
exports.mintAsync = mintAsync;
|
|
960
|
+
exports.preflightAsync = preflightAsync;
|
|
961
|
+
exports.preflightRedeemLiquidity = preflightRedeemLiquidity;
|
|
962
|
+
exports.preflightSync = preflightSync;
|
|
963
|
+
exports.previewDeposit = previewDeposit;
|
|
964
|
+
exports.previewRedeem = previewRedeem;
|
|
965
|
+
exports.quoteDepositFromSpokeFee = quoteDepositFromSpokeFee;
|
|
966
|
+
exports.quoteLzFee = quoteLzFee;
|
|
967
|
+
exports.redeemAsync = redeemAsync;
|
|
968
|
+
exports.redeemShares = redeemShares;
|
|
969
|
+
exports.requestRedeem = requestRedeem;
|
|
970
|
+
exports.smartDeposit = smartDeposit;
|
|
971
|
+
exports.withdrawAssets = withdrawAssets;
|
|
972
|
+
//# sourceMappingURL=index.cjs.map
|
|
973
|
+
//# sourceMappingURL=index.cjs.map
|