@forevermoney/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/dist/index.js ADDED
@@ -0,0 +1,1770 @@
1
+ // src/core/addresses.ts
2
+ import { isHex, u8aToHex } from "@polkadot/util";
3
+ import {
4
+ decodeAddress,
5
+ encodeAddress,
6
+ evmToAddress
7
+ } from "@polkadot/util-crypto";
8
+ import { ZeroAddress, getAddress, isAddress } from "ethers";
9
+
10
+ // src/core/errors.ts
11
+ var ForeverMoneyError = class extends Error {
12
+ code;
13
+ details;
14
+ constructor(code, message, details) {
15
+ super(message);
16
+ this.name = "ForeverMoneyError";
17
+ this.code = code;
18
+ this.details = details === void 0 ? void 0 : Object.freeze({ ...details });
19
+ }
20
+ };
21
+
22
+ // src/core/addresses.ts
23
+ var BITTENSOR_SS58_PREFIX = 42;
24
+ function normalizeEvmAddress(value) {
25
+ if (!isAddress(value) || value === ZeroAddress) {
26
+ throw new ForeverMoneyError(
27
+ "INVALID_ADDRESS",
28
+ "Expected a non-zero EVM address."
29
+ );
30
+ }
31
+ return getAddress(value);
32
+ }
33
+ function evmToMirrorSS58(evmAddress) {
34
+ return evmToAddress(normalizeEvmAddress(evmAddress), BITTENSOR_SS58_PREFIX);
35
+ }
36
+ function decodeBittensorAddress(value) {
37
+ try {
38
+ if (!value || isHex(value)) throw new Error("Raw hex is not SS58.");
39
+ const publicKey = decodeAddress(value, false, BITTENSOR_SS58_PREFIX);
40
+ if (publicKey.length !== 32) throw new Error("Invalid key length.");
41
+ return publicKey;
42
+ } catch {
43
+ throw new ForeverMoneyError(
44
+ "INVALID_SS58",
45
+ "Expected a Bittensor SS58 address with prefix 42."
46
+ );
47
+ }
48
+ }
49
+ function normalizeSS58(value) {
50
+ return encodeAddress(decodeBittensorAddress(value), BITTENSOR_SS58_PREFIX);
51
+ }
52
+ function isBittensorSS58(value) {
53
+ try {
54
+ decodeBittensorAddress(value);
55
+ return true;
56
+ } catch {
57
+ return false;
58
+ }
59
+ }
60
+ function ss58ToPublicKey(value) {
61
+ return u8aToHex(decodeBittensorAddress(value));
62
+ }
63
+
64
+ // src/core/amounts.ts
65
+ import { MaxUint256, formatUnits, parseUnits } from "ethers";
66
+
67
+ // src/chains/deployment.ts
68
+ import { getAddress as getAddress2 } from "ethers";
69
+ var BASE_CHAIN_ID = 8453;
70
+ var ROBINHOOD_CHAIN_ID = 4663;
71
+ var SUBTENSOR_CHAIN_ID = 964;
72
+ var BASE_CCIP_SELECTOR = 15971525489660198786n;
73
+ var ROBINHOOD_CCIP_SELECTOR = 6180753054346818345n;
74
+ var SUBTENSOR_CCIP_SELECTOR = 2135107236357186872n;
75
+ var RAO_PER_TAO = 1000000000n;
76
+ var EVM_WEI_PER_RAO = 1000000000n;
77
+ var FOREVERMONEY_DEPLOYMENT_VERSION = "1.1.0";
78
+ var foreverMoneyDeployment = Object.freeze({
79
+ version: FOREVERMONEY_DEPLOYMENT_VERSION,
80
+ base: Object.freeze({
81
+ key: "base",
82
+ name: "Base",
83
+ chainId: BASE_CHAIN_ID,
84
+ ccipSelector: BASE_CCIP_SELECTOR,
85
+ contracts: Object.freeze({
86
+ gateway: getAddress2("0x5EF3d7D19e4b233a1A169DA0d5CB02ec6b160a2C"),
87
+ wrappedTao: getAddress2(
88
+ "0xf3081494b87e8d5fb7960f066e931d1d0e6e3d67"
89
+ ),
90
+ ccipRouter: getAddress2(
91
+ "0x881e3A65B4d4a04dD529061dd0071cf975F58bCD"
92
+ ),
93
+ ccipOffRampFromSubtensor: getAddress2(
94
+ "0xf09AFe78d3c7d359b334d7cB88995751F7eC5E13"
95
+ ),
96
+ vaultFactory: getAddress2(
97
+ "0x9b7F3c7aa335D8BF9f91741E541820466735F868"
98
+ ),
99
+ vaultManagerImplementation: getAddress2(
100
+ "0x235Ad8654fe3a1619a9892eBCaD9361F416168da"
101
+ ),
102
+ batchLiquidityManagerQuery: getAddress2(
103
+ "0xd117176C5E6809b68500995F59589A8c081565A5"
104
+ ),
105
+ weth: getAddress2("0x4200000000000000000000000000000000000006")
106
+ }),
107
+ deploymentBlock: 50098800
108
+ }),
109
+ robinhood: Object.freeze({
110
+ key: "robinhood",
111
+ name: "Robinhood",
112
+ chainId: ROBINHOOD_CHAIN_ID,
113
+ ccipSelector: ROBINHOOD_CCIP_SELECTOR,
114
+ contracts: Object.freeze({
115
+ gateway: getAddress2("0x53Dcc4FE04193e489BE537F722F65317DB1E65d8"),
116
+ wrappedTao: getAddress2(
117
+ "0xf3081494B87e8D5fb7960f066E931D1D0e6E3d67"
118
+ ),
119
+ ccipRouter: getAddress2(
120
+ "0x06fC836cf9839B1cd891C440A0a45242DA6Ae1c9"
121
+ ),
122
+ ccipOffRampFromSubtensor: getAddress2(
123
+ "0xcDca5D374e46A6DDDab50bD2D9acB8c796eC35C3"
124
+ )
125
+ })
126
+ }),
127
+ subtensor: Object.freeze({
128
+ chainId: SUBTENSOR_CHAIN_ID,
129
+ ccipSelector: SUBTENSOR_CCIP_SELECTOR,
130
+ contracts: Object.freeze({
131
+ gateway: getAddress2("0x998f20Fea90bF7792774dECc7f994716442B1705"),
132
+ alphaVault: getAddress2(
133
+ "0x11837459896D96F821a8D88eC93a3C8D152033D4"
134
+ ),
135
+ wrappedTao: getAddress2(
136
+ "0xC5b6C1632d34901239396F5E1BDe54B342900256"
137
+ ),
138
+ ccipRouter: getAddress2(
139
+ "0xD941fBEcD2b971d0F54b4C34286C95faB52B60B8"
140
+ ),
141
+ ccipOffRampFromBase: getAddress2(
142
+ "0x51a6150400ed9F0Ae240F5D1b15E3b45Fc4339C7"
143
+ ),
144
+ ccipOffRampFromRobinhood: getAddress2(
145
+ "0x51a6150400ed9F0Ae240F5D1b15E3b45Fc4339C7"
146
+ ),
147
+ stakingPrecompile: getAddress2(
148
+ "0x0000000000000000000000000000000000000805"
149
+ )
150
+ })
151
+ })
152
+ });
153
+ function getForeverMoneyEvmDeployment(chain = "base") {
154
+ return foreverMoneyDeployment[chain];
155
+ }
156
+
157
+ // src/core/amounts.ts
158
+ var BASIS_POINTS = 10000n;
159
+ var NETWORK_FEE_BUFFER_BPS = 200n;
160
+ var GAS_LIMIT_BUFFER_BPS = 5000n;
161
+ function parseTaoAmount(value) {
162
+ if (typeof value !== "string") {
163
+ throw new ForeverMoneyError(
164
+ "AMOUNT_NOT_WHOLE_RAO",
165
+ "TAO amounts must be decimal strings."
166
+ );
167
+ }
168
+ const decimalPlaces = value.includes(".") ? value.split(".")[1]?.length ?? 0 : 0;
169
+ if (decimalPlaces > 9) {
170
+ throw new ForeverMoneyError(
171
+ "AMOUNT_NOT_WHOLE_RAO",
172
+ "TAO amounts must be positive decimal strings with at most nine decimal places."
173
+ );
174
+ }
175
+ let amount;
176
+ try {
177
+ amount = parseUnits(value, 18);
178
+ } catch {
179
+ throw new ForeverMoneyError(
180
+ "AMOUNT_NOT_WHOLE_RAO",
181
+ "TAO amounts must be positive decimal strings with at most nine decimal places."
182
+ );
183
+ }
184
+ assertWholeRao(amount);
185
+ return amount;
186
+ }
187
+ function assertWholeRao(amountWei) {
188
+ if (typeof amountWei !== "bigint") {
189
+ throw new ForeverMoneyError(
190
+ "INVALID_TRANSACTION_PLAN",
191
+ "The amount must be a bigint."
192
+ );
193
+ }
194
+ if (amountWei === 0n) {
195
+ throw new ForeverMoneyError(
196
+ "AMOUNT_ZERO",
197
+ "The amount must be greater than zero."
198
+ );
199
+ }
200
+ if (amountWei < 0n) {
201
+ throw new ForeverMoneyError(
202
+ "INVALID_TRANSACTION_PLAN",
203
+ "The amount cannot be negative."
204
+ );
205
+ }
206
+ if (amountWei > MaxUint256) {
207
+ throw new ForeverMoneyError(
208
+ "INVALID_TRANSACTION_PLAN",
209
+ "The amount exceeds uint256."
210
+ );
211
+ }
212
+ if (amountWei % EVM_WEI_PER_RAO !== 0n) {
213
+ throw new ForeverMoneyError(
214
+ "AMOUNT_NOT_WHOLE_RAO",
215
+ "TAO amounts must resolve to a whole RAO."
216
+ );
217
+ }
218
+ }
219
+ function formatTaoAmount(amountWei) {
220
+ if (typeof amountWei !== "bigint") {
221
+ throw new ForeverMoneyError(
222
+ "INVALID_TRANSACTION_PLAN",
223
+ "The TAO amount must be a bigint."
224
+ );
225
+ }
226
+ if (amountWei < 0n) {
227
+ throw new ForeverMoneyError(
228
+ "INVALID_TRANSACTION_PLAN",
229
+ "A TAO amount cannot be negative."
230
+ );
231
+ }
232
+ if (amountWei > MaxUint256) {
233
+ throw new ForeverMoneyError(
234
+ "INVALID_TRANSACTION_PLAN",
235
+ "The TAO amount exceeds uint256."
236
+ );
237
+ }
238
+ if (amountWei % EVM_WEI_PER_RAO !== 0n) {
239
+ throw new ForeverMoneyError(
240
+ "AMOUNT_NOT_WHOLE_RAO",
241
+ "TAO amounts must resolve to a whole RAO."
242
+ );
243
+ }
244
+ return formatUnits(amountWei, 18);
245
+ }
246
+ function mulDivCeil(value, numerator, denominator) {
247
+ return (value * numerator + denominator - 1n) / denominator;
248
+ }
249
+ function feeWithBuffer(fee) {
250
+ if (typeof fee !== "bigint") {
251
+ throw new ForeverMoneyError(
252
+ "INVALID_TRANSACTION_PLAN",
253
+ "The network fee must be a bigint."
254
+ );
255
+ }
256
+ if (fee < 0n) {
257
+ throw new ForeverMoneyError(
258
+ "INVALID_TRANSACTION_PLAN",
259
+ "A network fee cannot be negative."
260
+ );
261
+ }
262
+ const buffered = mulDivCeil(
263
+ fee,
264
+ BASIS_POINTS + NETWORK_FEE_BUFFER_BPS,
265
+ BASIS_POINTS
266
+ );
267
+ if (buffered > MaxUint256) {
268
+ throw new ForeverMoneyError(
269
+ "INVALID_TRANSACTION_PLAN",
270
+ "The buffered network fee exceeds uint256."
271
+ );
272
+ }
273
+ return buffered;
274
+ }
275
+ function gasLimitWithBuffer(gasLimit) {
276
+ if (typeof gasLimit !== "bigint") {
277
+ throw new ForeverMoneyError(
278
+ "INVALID_TRANSACTION_PLAN",
279
+ "The gas limit must be a bigint."
280
+ );
281
+ }
282
+ if (gasLimit <= 0n) {
283
+ throw new ForeverMoneyError(
284
+ "INVALID_TRANSACTION_PLAN",
285
+ "A gas limit must be greater than zero."
286
+ );
287
+ }
288
+ const buffered = mulDivCeil(
289
+ gasLimit,
290
+ BASIS_POINTS + GAS_LIMIT_BUFFER_BPS,
291
+ BASIS_POINTS
292
+ );
293
+ if (buffered > MaxUint256) {
294
+ throw new ForeverMoneyError(
295
+ "INVALID_TRANSACTION_PLAN",
296
+ "The buffered gas limit exceeds uint256."
297
+ );
298
+ }
299
+ return buffered;
300
+ }
301
+
302
+ // src/abis/index.ts
303
+ var ERC20_ABI = Object.freeze([
304
+ "function allowance(address owner,address spender) view returns (uint256)",
305
+ "function approve(address spender,uint256 amount) returns (bool)",
306
+ "function balanceOf(address account) view returns (uint256)"
307
+ ]);
308
+ var STAKING_ABI = Object.freeze([
309
+ "function allowance(address sourceAddress,address spenderAddress,uint256 netuid) view returns (uint256)",
310
+ "function approve(address spenderAddress,uint256 netuid,uint256 absoluteAmount)",
311
+ "function getStake(bytes32 hotkey,bytes32 coldkey,uint256 netuid) view returns (uint256)"
312
+ ]);
313
+ var SPOKE_GATEWAY_ABI = Object.freeze([
314
+ "function ROUTER() view returns (address)",
315
+ "function BITTENSOR_SELECTOR() view returns (uint64)",
316
+ "function SUBTENSOR_GATEWAY() view returns (address)",
317
+ "function quoteBridgeToFinney(address token,uint256 amount,(bytes32 ss58,address evmFallback,bool wantLiquid,uint256 minTaoOut) exit) view returns (uint256 fee)",
318
+ "function bridgeToFinney(address token,uint256 amount,(bytes32 ss58,address evmFallback,bool wantLiquid,uint256 minTaoOut) exit) payable returns (bytes32 messageId)",
319
+ "event BridgedToFinney(address indexed token,address indexed sender,bytes32 indexed ss58,uint256 amount,bytes32 messageId)"
320
+ ]);
321
+ var ALPHA_GATEWAY_ABI = Object.freeze([
322
+ "function ROUTER() view returns (address)",
323
+ "function allowedLane(uint64 selector) view returns (bool)",
324
+ "function quoteBridgeOut(uint64 destSelector,address token,address recipient,uint256 mintedAmount) view returns (uint256 fee)",
325
+ "function bridgeOut(uint64 destSelector,address token,address recipient,uint256 taoAmount,uint256 stakedAlphaRao,uint256 minTokenOut) payable returns (bytes32 messageId)",
326
+ "function claimLiquid(address token,uint256 minTaoOut,address to)",
327
+ "function claimNative(address to)",
328
+ "function claimStaked(address token,bytes32 destColdkey,address to)",
329
+ "function claimToken(address token,address to)",
330
+ "function claimableNative(address account) view returns (uint256)",
331
+ "function claimableToken(address account,address token) view returns (uint256)",
332
+ "event BridgedOut(uint64 destChainSelector,address indexed token,address indexed sender,address indexed recipient,uint256 minted,bytes32 messageId)",
333
+ "event Claimable(address indexed token,address indexed user,uint256 native,uint256 wsn)",
334
+ "event DeliveredLiquid(address indexed token,bytes32 indexed ss58,uint256 taoOut)",
335
+ "event DeliveredStaked(address indexed token,bytes32 indexed ss58,uint256 alphaRao)"
336
+ ]);
337
+ var ALPHA_VAULT_ABI = Object.freeze([
338
+ "function isPaused() view returns (bool)",
339
+ "function pausedUntil() view returns (uint256)",
340
+ "function isListed(address token) view returns (bool)",
341
+ "function backing(address token) view returns (uint256 stakedAlpha)",
342
+ "function positionOf(address token) view returns (bytes32 validator,uint256 netuid)"
343
+ ]);
344
+ var VAULT_FACTORY_ABI = Object.freeze([
345
+ "function create(address owner,bytes32 associatedMiner,address akAddress,address poolManager,address poolAddress,address positionManagerImpl,(address token,uint256 amount)[] stashTokens) payable returns (address manager)",
346
+ "event SnLiquidityManagerCreated(address indexed manager,address indexed owner,bytes32 associatedMiner,address akAddress,address poolAddress,address poolManager,address positionManager)"
347
+ ]);
348
+ var VAULT_MANAGER_ABI = Object.freeze([
349
+ "function claimFees(address akAddress)",
350
+ "function topUpAk(address akAddress,address token,uint256 amount) payable",
351
+ "function withdrawFromAkAndPositions(address akAddress,uint256 amount0,uint256 amount1,uint256[] decreaseTokenIds,bool unwrapETH)",
352
+ "function stake(address akAddress,bytes data)",
353
+ "function unstake(address akAddress,bytes data)",
354
+ "function owner() view returns (address)",
355
+ "event AKStashTopUp(address akAddress,address token,uint256 amount)",
356
+ "event AKStashWithdraw(address akAddress,address token,uint256 amount)"
357
+ ]);
358
+ var CCIP_EXECUTION_ABI = Object.freeze([
359
+ "event ExecutionStateChanged(uint64 indexed sourceChainSelector,uint64 indexed sequenceNumber,bytes32 indexed messageId,bytes32 messageHash,uint8 state,bytes returnData,uint256 gasUsed)"
360
+ ]);
361
+ var CCIP_ROUTER_ABI = Object.freeze([
362
+ "function isOffRamp(uint64 sourceChainSelector,address offRamp) view returns (bool)"
363
+ ]);
364
+ var foreverMoneyAbis = Object.freeze({
365
+ alphaGateway: ALPHA_GATEWAY_ABI,
366
+ alphaVault: ALPHA_VAULT_ABI,
367
+ ccipExecution: CCIP_EXECUTION_ABI,
368
+ ccipRouter: CCIP_ROUTER_ABI,
369
+ erc20: ERC20_ABI,
370
+ spokeGateway: SPOKE_GATEWAY_ABI,
371
+ staking: STAKING_ABI,
372
+ vaultFactory: VAULT_FACTORY_ABI,
373
+ vaultManager: VAULT_MANAGER_ABI
374
+ });
375
+
376
+ // src/bridge/plans.ts
377
+ import { Contract, Interface } from "ethers";
378
+
379
+ // src/core/plans.ts
380
+ import { keccak256, toUtf8Bytes } from "ethers";
381
+ function createTransactionPlan(plan) {
382
+ const steps = plan.steps.map(
383
+ (step2) => Object.freeze({
384
+ ...step2,
385
+ transaction: Object.freeze({ ...step2.transaction })
386
+ })
387
+ );
388
+ const versionedPlan = {
389
+ version: "1",
390
+ deploymentVersion: FOREVERMONEY_DEPLOYMENT_VERSION,
391
+ action: plan.action,
392
+ summary: plan.summary,
393
+ steps: Object.freeze(steps)
394
+ };
395
+ return Object.freeze({
396
+ ...versionedPlan,
397
+ hash: keccak256(toUtf8Bytes(JSON.stringify(versionedPlan)))
398
+ });
399
+ }
400
+
401
+ // src/core/validation.ts
402
+ import { MaxUint256 as MaxUint2562, ZeroHash, isHexString } from "ethers";
403
+ function assertBigInt(amount, label) {
404
+ if (typeof amount !== "bigint") {
405
+ throw new ForeverMoneyError(
406
+ "INVALID_TRANSACTION_PLAN",
407
+ `${label} must be a bigint.`
408
+ );
409
+ }
410
+ }
411
+ function assertPositiveAmount(amount, label) {
412
+ assertBigInt(amount, label);
413
+ if (amount === 0n) {
414
+ throw new ForeverMoneyError(
415
+ "AMOUNT_ZERO",
416
+ `${label} must be greater than zero.`
417
+ );
418
+ }
419
+ if (amount < 0n) {
420
+ throw new ForeverMoneyError(
421
+ "INVALID_TRANSACTION_PLAN",
422
+ `${label} cannot be negative.`
423
+ );
424
+ }
425
+ if (amount > MaxUint2562) {
426
+ throw new ForeverMoneyError(
427
+ "INVALID_TRANSACTION_PLAN",
428
+ `${label} exceeds uint256.`
429
+ );
430
+ }
431
+ }
432
+ function assertNonNegativeAmount(amount, label) {
433
+ assertBigInt(amount, label);
434
+ if (amount < 0n) {
435
+ throw new ForeverMoneyError(
436
+ "INVALID_TRANSACTION_PLAN",
437
+ `${label} cannot be negative.`
438
+ );
439
+ }
440
+ if (amount > MaxUint2562) {
441
+ throw new ForeverMoneyError(
442
+ "INVALID_TRANSACTION_PLAN",
443
+ `${label} exceeds uint256.`
444
+ );
445
+ }
446
+ }
447
+ function assertBoolean(value, label) {
448
+ if (typeof value !== "boolean") {
449
+ throw new ForeverMoneyError(
450
+ "INVALID_TRANSACTION_PLAN",
451
+ `${label} must be a boolean.`
452
+ );
453
+ }
454
+ }
455
+ function assertArray(value, label) {
456
+ if (!Array.isArray(value)) {
457
+ throw new ForeverMoneyError(
458
+ "INVALID_TRANSACTION_PLAN",
459
+ `${label} must be an array.`
460
+ );
461
+ }
462
+ }
463
+ function assertRecord(value, label) {
464
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
465
+ throw new ForeverMoneyError(
466
+ "INVALID_TRANSACTION_PLAN",
467
+ `${label} must be an object.`
468
+ );
469
+ }
470
+ }
471
+ function normalizeBytes32(value, label) {
472
+ if (!isHexString(value, 32)) {
473
+ throw new ForeverMoneyError(
474
+ "INVALID_BYTES32",
475
+ `${label} must be a 32-byte hex value.`
476
+ );
477
+ }
478
+ return value.toLowerCase();
479
+ }
480
+ function normalizeOptionalBytes32(value, label) {
481
+ return value === void 0 ? ZeroHash : normalizeBytes32(value, label);
482
+ }
483
+
484
+ // src/bridge/plans.ts
485
+ var erc20Interface = new Interface(ERC20_ABI);
486
+ var spokeInterface = new Interface(SPOKE_GATEWAY_ABI);
487
+ var alphaInterface = new Interface(ALPHA_GATEWAY_ABI);
488
+ var stakingInterface = new Interface(STAKING_ABI);
489
+ var MIN_LIQUID_EVM_TO_SUBTENSOR_WEI = 10000000000000000n;
490
+ var MIN_LIQUID_BASE_TO_SUBTENSOR_WEI = MIN_LIQUID_EVM_TO_SUBTENSOR_WEI;
491
+ function assertDelivery(value) {
492
+ if (value !== "liquid" && value !== "staked") {
493
+ throw new ForeverMoneyError(
494
+ "INVALID_TRANSACTION_PLAN",
495
+ 'Delivery must be "liquid" or "staked".'
496
+ );
497
+ }
498
+ }
499
+ function assertSource(value) {
500
+ if (value !== "liquid" && value !== "staked") {
501
+ throw new ForeverMoneyError(
502
+ "INVALID_TRANSACTION_PLAN",
503
+ 'Source must be "liquid" or "staked".'
504
+ );
505
+ }
506
+ }
507
+ function assertBaseToSubtensorAmount(amountWei, delivery) {
508
+ assertWholeRao(amountWei);
509
+ if (delivery === "liquid" && amountWei < MIN_LIQUID_EVM_TO_SUBTENSOR_WEI) {
510
+ throw new ForeverMoneyError(
511
+ "AMOUNT_BELOW_MINIMUM",
512
+ "Liquid delivery from an EVM chain to Subtensor requires at least 0.01 TAO.",
513
+ {
514
+ amountWei: amountWei.toString(),
515
+ minimumAmountWei: MIN_LIQUID_EVM_TO_SUBTENSOR_WEI.toString()
516
+ }
517
+ );
518
+ }
519
+ }
520
+ function transactionStep(kind, label, chainId, from, to, data, value, gasLimit) {
521
+ return {
522
+ kind,
523
+ label,
524
+ transaction: {
525
+ chainId,
526
+ from,
527
+ to,
528
+ data,
529
+ value: value.toString(),
530
+ ...gasLimit === void 0 ? {} : { gasLimit: gasLimit.toString() }
531
+ }
532
+ };
533
+ }
534
+ function buildEvmToSubtensorPlan(input) {
535
+ const sender = normalizeEvmAddress(input.sender);
536
+ assertDelivery(input.delivery);
537
+ assertBaseToSubtensorAmount(input.amountWei, input.delivery);
538
+ assertNonNegativeAmount(input.allowanceWei, "Token allowance");
539
+ assertNonNegativeAmount(input.exactNetworkFeeWei, "Network fee");
540
+ const destination = normalizeSS58(input.destination);
541
+ const exit = {
542
+ ss58: ss58ToPublicKey(destination),
543
+ evmFallback: sender,
544
+ wantLiquid: input.delivery === "liquid",
545
+ minTaoOut: input.amountWei
546
+ };
547
+ const evm = getForeverMoneyEvmDeployment(input.evmChain);
548
+ const steps = [];
549
+ if (input.allowanceWei < input.amountWei) {
550
+ steps.push(
551
+ transactionStep(
552
+ "approval",
553
+ "Approve wrapped TAO for the ForeverMoney gateway",
554
+ evm.chainId,
555
+ sender,
556
+ evm.contracts.wrappedTao,
557
+ erc20Interface.encodeFunctionData("approve", [
558
+ evm.contracts.gateway,
559
+ input.amountWei
560
+ ]),
561
+ 0n
562
+ )
563
+ );
564
+ }
565
+ const value = feeWithBuffer(input.exactNetworkFeeWei);
566
+ steps.push(
567
+ transactionStep(
568
+ "transaction",
569
+ `Bridge wrapped TAO from ${evm.name} to Subtensor (${input.delivery})`,
570
+ evm.chainId,
571
+ sender,
572
+ evm.contracts.gateway,
573
+ spokeInterface.encodeFunctionData(
574
+ "bridgeToFinney(address,uint256,(bytes32,address,bool,uint256))",
575
+ [evm.contracts.wrappedTao, input.amountWei, exit]
576
+ ),
577
+ value,
578
+ input.estimatedBridgeGas === void 0 ? void 0 : gasLimitWithBuffer(input.estimatedBridgeGas)
579
+ )
580
+ );
581
+ return createTransactionPlan({
582
+ action: evm.key === "base" ? "bridge.base-to-subtensor" : "bridge.robinhood-to-subtensor",
583
+ summary: `Bridge ${input.amountWei} wei of wrapped TAO from ${evm.name} to ${destination}.`,
584
+ steps
585
+ });
586
+ }
587
+ function buildBaseToSubtensorPlan(input) {
588
+ return buildEvmToSubtensorPlan({ ...input, evmChain: "base" });
589
+ }
590
+ function buildSubtensorToEvmPlan(input) {
591
+ const sender = normalizeEvmAddress(input.sender);
592
+ const recipient = normalizeEvmAddress(input.recipient);
593
+ assertSource(input.source);
594
+ assertWholeRao(input.amountWei);
595
+ assertNonNegativeAmount(input.exactNetworkFeeWei, "Network fee");
596
+ const { subtensor } = foreverMoneyDeployment;
597
+ const evm = getForeverMoneyEvmDeployment(input.evmChain);
598
+ const amountRao = input.amountWei / EVM_WEI_PER_RAO;
599
+ const steps = [];
600
+ if (input.source === "staked") {
601
+ if (input.netuid === void 0) {
602
+ throw new ForeverMoneyError(
603
+ "INVALID_TRANSACTION_PLAN",
604
+ "A netuid is required when bridging staked TAO."
605
+ );
606
+ }
607
+ assertNonNegativeAmount(input.netuid, "netuid");
608
+ if (input.stakingAllowanceRao === void 0) {
609
+ throw new ForeverMoneyError(
610
+ "INVALID_TRANSACTION_PLAN",
611
+ "The staking allowance is required when bridging staked TAO."
612
+ );
613
+ }
614
+ assertNonNegativeAmount(input.stakingAllowanceRao, "Staking allowance");
615
+ if (input.stakingAllowanceRao < amountRao) {
616
+ steps.push(
617
+ transactionStep(
618
+ "approval",
619
+ "Approve staked TAO for the ForeverMoney gateway",
620
+ subtensor.chainId,
621
+ sender,
622
+ subtensor.contracts.stakingPrecompile,
623
+ stakingInterface.encodeFunctionData("approve", [
624
+ subtensor.contracts.gateway,
625
+ input.netuid,
626
+ amountRao
627
+ ]),
628
+ 0n
629
+ )
630
+ );
631
+ }
632
+ } else if (input.netuid !== void 0 || input.stakingAllowanceRao !== void 0) {
633
+ throw new ForeverMoneyError(
634
+ "INVALID_TRANSACTION_PLAN",
635
+ "netuid and staking allowance are only valid for staked TAO."
636
+ );
637
+ }
638
+ const taoAmount = input.source === "liquid" ? input.amountWei : 0n;
639
+ const stakedAlphaRao = input.source === "staked" ? amountRao : 0n;
640
+ const value = taoAmount + feeWithBuffer(input.exactNetworkFeeWei);
641
+ assertNonNegativeAmount(value, "Transaction value");
642
+ steps.push(
643
+ transactionStep(
644
+ "transaction",
645
+ `Bridge ${input.source} TAO from Subtensor to ${evm.name}`,
646
+ subtensor.chainId,
647
+ sender,
648
+ subtensor.contracts.gateway,
649
+ alphaInterface.encodeFunctionData("bridgeOut", [
650
+ evm.ccipSelector,
651
+ subtensor.contracts.wrappedTao,
652
+ recipient,
653
+ taoAmount,
654
+ stakedAlphaRao,
655
+ input.amountWei
656
+ ]),
657
+ value,
658
+ input.estimatedBridgeGas === void 0 ? void 0 : gasLimitWithBuffer(input.estimatedBridgeGas)
659
+ )
660
+ );
661
+ return createTransactionPlan({
662
+ action: evm.key === "base" ? "bridge.subtensor-to-base" : "bridge.subtensor-to-robinhood",
663
+ summary: `Bridge ${input.amountWei} wei of ${input.source} TAO from Subtensor to ${recipient} on ${evm.name}.`,
664
+ steps
665
+ });
666
+ }
667
+ function buildSubtensorToBasePlan(input) {
668
+ return buildSubtensorToEvmPlan({ ...input, evmChain: "base" });
669
+ }
670
+ async function prepareEvmToSubtensor(provider, input) {
671
+ const sender = normalizeEvmAddress(input.sender);
672
+ assertDelivery(input.delivery);
673
+ assertBaseToSubtensorAmount(input.amountWei, input.delivery);
674
+ const destination = normalizeSS58(input.destination);
675
+ const evm = getForeverMoneyEvmDeployment(input.evmChain);
676
+ const exit = {
677
+ ss58: ss58ToPublicKey(destination),
678
+ evmFallback: sender,
679
+ wantLiquid: input.delivery === "liquid",
680
+ minTaoOut: input.amountWei
681
+ };
682
+ const token = new Contract(evm.contracts.wrappedTao, ERC20_ABI, provider);
683
+ const gateway = new Contract(
684
+ evm.contracts.gateway,
685
+ SPOKE_GATEWAY_ABI,
686
+ provider
687
+ );
688
+ const [allowanceWei, exactNetworkFeeWei] = await Promise.all([
689
+ token.getFunction("allowance")(
690
+ sender,
691
+ evm.contracts.gateway
692
+ ),
693
+ gateway.getFunction(
694
+ "quoteBridgeToFinney(address,uint256,(bytes32,address,bool,uint256))"
695
+ )(evm.contracts.wrappedTao, input.amountWei, exit)
696
+ ]);
697
+ let estimatedBridgeGas;
698
+ if (allowanceWei >= input.amountWei) {
699
+ const data = spokeInterface.encodeFunctionData(
700
+ "bridgeToFinney(address,uint256,(bytes32,address,bool,uint256))",
701
+ [evm.contracts.wrappedTao, input.amountWei, exit]
702
+ );
703
+ estimatedBridgeGas = await provider.estimateGas({
704
+ from: sender,
705
+ to: evm.contracts.gateway,
706
+ data,
707
+ value: feeWithBuffer(exactNetworkFeeWei)
708
+ });
709
+ }
710
+ const plan = buildEvmToSubtensorPlan({
711
+ ...input,
712
+ allowanceWei,
713
+ exactNetworkFeeWei,
714
+ ...estimatedBridgeGas === void 0 ? {} : { estimatedBridgeGas }
715
+ });
716
+ return Object.freeze({
717
+ exactNetworkFeeWei,
718
+ transactionValueWei: feeWithBuffer(exactNetworkFeeWei),
719
+ plan
720
+ });
721
+ }
722
+ function prepareBaseToSubtensor(provider, input) {
723
+ return prepareEvmToSubtensor(provider, { ...input, evmChain: "base" });
724
+ }
725
+ async function prepareSubtensorToEvm(provider, input) {
726
+ const sender = normalizeEvmAddress(input.sender);
727
+ const recipient = normalizeEvmAddress(input.recipient);
728
+ assertSource(input.source);
729
+ assertWholeRao(input.amountWei);
730
+ const { subtensor } = foreverMoneyDeployment;
731
+ const evm = getForeverMoneyEvmDeployment(input.evmChain);
732
+ const gateway = new Contract(
733
+ subtensor.contracts.gateway,
734
+ ALPHA_GATEWAY_ABI,
735
+ provider
736
+ );
737
+ const exactNetworkFeeWei = await gateway.getFunction("quoteBridgeOut")(
738
+ evm.ccipSelector,
739
+ subtensor.contracts.wrappedTao,
740
+ recipient,
741
+ input.amountWei
742
+ );
743
+ let stakingAllowanceRao;
744
+ if (input.source === "staked") {
745
+ if (input.netuid === void 0) {
746
+ throw new ForeverMoneyError(
747
+ "INVALID_TRANSACTION_PLAN",
748
+ "A netuid is required when bridging staked TAO."
749
+ );
750
+ }
751
+ assertNonNegativeAmount(input.netuid, "netuid");
752
+ const staking = new Contract(
753
+ subtensor.contracts.stakingPrecompile,
754
+ STAKING_ABI,
755
+ provider
756
+ );
757
+ stakingAllowanceRao = await staking.getFunction("allowance")(
758
+ sender,
759
+ subtensor.contracts.gateway,
760
+ input.netuid
761
+ );
762
+ }
763
+ const taoAmount = input.source === "liquid" ? input.amountWei : 0n;
764
+ const stakedAlphaRao = input.source === "staked" ? input.amountWei / EVM_WEI_PER_RAO : 0n;
765
+ const value = taoAmount + feeWithBuffer(exactNetworkFeeWei);
766
+ assertNonNegativeAmount(value, "Transaction value");
767
+ let estimatedBridgeGas;
768
+ if (input.source === "liquid" || stakingAllowanceRao !== void 0 && stakingAllowanceRao >= stakedAlphaRao) {
769
+ estimatedBridgeGas = await provider.estimateGas({
770
+ from: sender,
771
+ to: subtensor.contracts.gateway,
772
+ data: alphaInterface.encodeFunctionData("bridgeOut", [
773
+ evm.ccipSelector,
774
+ subtensor.contracts.wrappedTao,
775
+ recipient,
776
+ taoAmount,
777
+ stakedAlphaRao,
778
+ input.amountWei
779
+ ]),
780
+ value
781
+ });
782
+ }
783
+ const plan = buildSubtensorToEvmPlan({
784
+ ...input,
785
+ exactNetworkFeeWei,
786
+ ...stakingAllowanceRao === void 0 ? {} : { stakingAllowanceRao },
787
+ ...estimatedBridgeGas === void 0 ? {} : { estimatedBridgeGas }
788
+ });
789
+ return Object.freeze({
790
+ exactNetworkFeeWei,
791
+ transactionValueWei: value,
792
+ plan
793
+ });
794
+ }
795
+ function prepareSubtensorToBase(provider, input) {
796
+ return prepareSubtensorToEvm(provider, { ...input, evmChain: "base" });
797
+ }
798
+
799
+ // src/core/transport.ts
800
+ import { BrowserProvider } from "ethers";
801
+ function isRecord(value) {
802
+ return typeof value === "object" && value !== null && !Array.isArray(value);
803
+ }
804
+ function invalidJsonRpcResponse() {
805
+ return new ForeverMoneyError(
806
+ "RPC_ERROR",
807
+ "RPC returned an invalid JSON-RPC response."
808
+ );
809
+ }
810
+ function isLoopback(endpoint) {
811
+ return endpoint.hostname === "localhost" || endpoint.hostname === "127.0.0.1" || endpoint.hostname === "[::1]";
812
+ }
813
+ function jsonRpcResult(body, expectedId) {
814
+ if (!isRecord(body) || body.jsonrpc !== "2.0" || body.id !== expectedId) {
815
+ throw invalidJsonRpcResponse();
816
+ }
817
+ if (body.error !== void 0) {
818
+ if (!isRecord(body.error) || !Number.isInteger(body.error.code) || typeof body.error.message !== "string") {
819
+ throw invalidJsonRpcResponse();
820
+ }
821
+ throw new ForeverMoneyError("RPC_ERROR", body.error.message, {
822
+ rpcCode: body.error.code,
823
+ ...body.error.data === void 0 ? {} : { rpcData: body.error.data }
824
+ });
825
+ }
826
+ if (!Object.prototype.hasOwnProperty.call(body, "result")) {
827
+ throw invalidJsonRpcResponse();
828
+ }
829
+ return body.result;
830
+ }
831
+ function http(url, options = {}) {
832
+ let endpoint;
833
+ try {
834
+ endpoint = new URL(url);
835
+ } catch {
836
+ throw new ForeverMoneyError("RPC_ERROR", "RPC URL is invalid.");
837
+ }
838
+ if (endpoint.protocol !== "https:" && endpoint.protocol !== "http:") {
839
+ throw new ForeverMoneyError(
840
+ "RPC_ERROR",
841
+ "RPC URL must use HTTP or HTTPS."
842
+ );
843
+ }
844
+ if (endpoint.protocol === "http:" && !isLoopback(endpoint)) {
845
+ throw new ForeverMoneyError(
846
+ "RPC_ERROR",
847
+ "Non-local RPC URLs must use HTTPS."
848
+ );
849
+ }
850
+ const timeoutMs = options.timeoutMs ?? 15e3;
851
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs <= 0) {
852
+ throw new ForeverMoneyError(
853
+ "RPC_ERROR",
854
+ "RPC timeout must be a positive integer."
855
+ );
856
+ }
857
+ const fetchImplementation = options.fetch ?? globalThis.fetch;
858
+ if (typeof fetchImplementation !== "function") {
859
+ throw new ForeverMoneyError(
860
+ "RPC_ERROR",
861
+ "This runtime does not provide fetch. Pass an explicit fetch implementation."
862
+ );
863
+ }
864
+ let requestId = 0;
865
+ return Object.freeze({
866
+ async request(request) {
867
+ if (!isRecord(request) || typeof request.method !== "string" || request.method.length === 0) {
868
+ throw new ForeverMoneyError(
869
+ "RPC_ERROR",
870
+ "RPC method must be a non-empty string."
871
+ );
872
+ }
873
+ if (request.params !== void 0 && !Array.isArray(request.params) && !isRecord(request.params)) {
874
+ throw new ForeverMoneyError(
875
+ "RPC_ERROR",
876
+ "RPC params must be an array or object."
877
+ );
878
+ }
879
+ const controller = new AbortController();
880
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
881
+ const id = ++requestId;
882
+ try {
883
+ const response = await fetchImplementation(endpoint, {
884
+ method: "POST",
885
+ headers: { "content-type": "application/json" },
886
+ body: JSON.stringify({
887
+ jsonrpc: "2.0",
888
+ id,
889
+ method: request.method,
890
+ params: request.params ?? []
891
+ }),
892
+ signal: controller.signal
893
+ });
894
+ if (!response.ok) {
895
+ throw new ForeverMoneyError(
896
+ "RPC_ERROR",
897
+ `RPC returned HTTP ${response.status}.`,
898
+ { status: response.status }
899
+ );
900
+ }
901
+ return jsonRpcResult(await response.json(), id);
902
+ } catch (error) {
903
+ if (error instanceof ForeverMoneyError) throw error;
904
+ const message = error instanceof Error && error.name === "AbortError" ? `RPC request timed out after ${timeoutMs}ms.` : "RPC request failed.";
905
+ throw new ForeverMoneyError("RPC_ERROR", message);
906
+ } finally {
907
+ clearTimeout(timer);
908
+ }
909
+ }
910
+ });
911
+ }
912
+ function providerFromTransport(transport) {
913
+ if (!transport || typeof transport.request !== "function") {
914
+ throw new ForeverMoneyError(
915
+ "MISSING_TRANSPORT",
916
+ "An EIP-1193-compatible RPC transport is required."
917
+ );
918
+ }
919
+ return new BrowserProvider(transport);
920
+ }
921
+
922
+ // src/core/provider-errors.ts
923
+ function stringProperty(value, property) {
924
+ if (typeof value !== "object" || value === null || !(property in value)) {
925
+ return void 0;
926
+ }
927
+ const entry = value[property];
928
+ return typeof entry === "string" ? entry : void 0;
929
+ }
930
+ async function providerOperation(operation, action) {
931
+ try {
932
+ return await action();
933
+ } catch (error) {
934
+ if (error instanceof ForeverMoneyError) throw error;
935
+ const causeCode = stringProperty(error, "code");
936
+ const reason = stringProperty(error, "reason");
937
+ const reverted = causeCode === "CALL_EXCEPTION";
938
+ throw new ForeverMoneyError(
939
+ reverted ? "SIMULATION_REVERTED" : "RPC_ERROR",
940
+ reverted ? `${operation} reverted.` : `${operation} failed.`,
941
+ {
942
+ ...causeCode === void 0 ? {} : { causeCode },
943
+ ...reason === void 0 ? {} : { reason }
944
+ }
945
+ );
946
+ }
947
+ }
948
+
949
+ // src/vaults/plans.ts
950
+ import { Contract as Contract2, Interface as Interface2, ZeroAddress as ZeroAddress2 } from "ethers";
951
+ var erc20Interface2 = new Interface2(ERC20_ABI);
952
+ var factoryInterface = new Interface2(VAULT_FACTORY_ABI);
953
+ var managerInterface = new Interface2(VAULT_MANAGER_ABI);
954
+ function step(kind, label, from, to, data, value = 0n) {
955
+ return {
956
+ kind,
957
+ label,
958
+ transaction: {
959
+ chainId: foreverMoneyDeployment.base.chainId,
960
+ from,
961
+ to,
962
+ data,
963
+ value: value.toString()
964
+ }
965
+ };
966
+ }
967
+ function normalizeStashTokens(stashTokens) {
968
+ assertArray(stashTokens, "Stash tokens");
969
+ const seen = /* @__PURE__ */ new Set();
970
+ return stashTokens.map((entry) => {
971
+ assertRecord(entry, "Stash token");
972
+ if (typeof entry.token !== "string") {
973
+ throw new ForeverMoneyError(
974
+ "INVALID_TRANSACTION_PLAN",
975
+ "Stash token address must be a string."
976
+ );
977
+ }
978
+ const normalizedToken = normalizeEvmAddress(entry.token);
979
+ assertPositiveAmount(entry.amount, "Stash amount");
980
+ const key = normalizedToken.toLowerCase();
981
+ if (seen.has(key)) {
982
+ throw new ForeverMoneyError(
983
+ "INVALID_TRANSACTION_PLAN",
984
+ `Duplicate stash token: ${normalizedToken}.`
985
+ );
986
+ }
987
+ seen.add(key);
988
+ return Object.freeze({
989
+ token: normalizedToken,
990
+ amount: entry.amount
991
+ });
992
+ });
993
+ }
994
+ function allowanceMap(allowances) {
995
+ assertArray(allowances, "Token allowances");
996
+ const result = /* @__PURE__ */ new Map();
997
+ for (const entry of allowances) {
998
+ assertRecord(entry, "Token allowance");
999
+ if (typeof entry.token !== "string") {
1000
+ throw new ForeverMoneyError(
1001
+ "INVALID_TRANSACTION_PLAN",
1002
+ "Allowance token address must be a string."
1003
+ );
1004
+ }
1005
+ const token = normalizeEvmAddress(entry.token).toLowerCase();
1006
+ assertNonNegativeAmount(entry.allowance, "Token allowance");
1007
+ if (result.has(token)) {
1008
+ throw new ForeverMoneyError(
1009
+ "INVALID_TRANSACTION_PLAN",
1010
+ `Duplicate allowance for token ${entry.token}.`
1011
+ );
1012
+ }
1013
+ result.set(token, entry.allowance);
1014
+ }
1015
+ return result;
1016
+ }
1017
+ function assertAllowanceSet(allowances, tokens) {
1018
+ const expected = new Set(
1019
+ tokens.filter(
1020
+ ({ token }) => token !== foreverMoneyDeployment.base.contracts.weth
1021
+ ).map(({ token }) => token.toLowerCase())
1022
+ );
1023
+ for (const token of allowances.keys()) {
1024
+ if (!expected.has(token)) {
1025
+ throw new ForeverMoneyError(
1026
+ "INVALID_TRANSACTION_PLAN",
1027
+ `Unexpected allowance for token ${token}.`
1028
+ );
1029
+ }
1030
+ }
1031
+ }
1032
+ function buildCreateVaultPlan(input) {
1033
+ const owner = normalizeEvmAddress(input.owner);
1034
+ const akAddress = normalizeEvmAddress(input.akAddress);
1035
+ const poolManager = normalizeEvmAddress(input.poolManager);
1036
+ const poolAddress = normalizeEvmAddress(input.poolAddress);
1037
+ const positionManagerImplementation = normalizeEvmAddress(
1038
+ input.positionManagerImplementation
1039
+ );
1040
+ const associatedMiner = normalizeOptionalBytes32(
1041
+ input.associatedMiner,
1042
+ "Associated miner"
1043
+ );
1044
+ const stashTokens = normalizeStashTokens(input.stashTokens);
1045
+ const allowances = allowanceMap(input.allowances);
1046
+ assertAllowanceSet(allowances, stashTokens);
1047
+ const { base } = foreverMoneyDeployment;
1048
+ const steps = [];
1049
+ let value = 0n;
1050
+ for (const stash of stashTokens) {
1051
+ if (stash.token === base.contracts.weth) {
1052
+ value += stash.amount;
1053
+ assertNonNegativeAmount(value, "Vault creation value");
1054
+ continue;
1055
+ }
1056
+ const allowance = allowances.get(stash.token.toLowerCase());
1057
+ if (allowance === void 0) {
1058
+ throw new ForeverMoneyError(
1059
+ "INVALID_TRANSACTION_PLAN",
1060
+ `Missing allowance for stash token ${stash.token}.`
1061
+ );
1062
+ }
1063
+ if (allowance < stash.amount) {
1064
+ steps.push(
1065
+ step(
1066
+ "approval",
1067
+ `Approve ${stash.token} for the vault factory`,
1068
+ owner,
1069
+ stash.token,
1070
+ erc20Interface2.encodeFunctionData("approve", [
1071
+ base.contracts.vaultFactory,
1072
+ stash.amount
1073
+ ])
1074
+ )
1075
+ );
1076
+ }
1077
+ }
1078
+ steps.push(
1079
+ step(
1080
+ "transaction",
1081
+ "Create ForeverMoney vault",
1082
+ owner,
1083
+ base.contracts.vaultFactory,
1084
+ factoryInterface.encodeFunctionData("create", [
1085
+ owner,
1086
+ associatedMiner,
1087
+ akAddress,
1088
+ poolManager,
1089
+ poolAddress,
1090
+ positionManagerImplementation,
1091
+ stashTokens
1092
+ ]),
1093
+ value
1094
+ )
1095
+ );
1096
+ return createTransactionPlan({
1097
+ action: "vault.create",
1098
+ summary: `Create a vault owned by ${owner} with ${stashTokens.length} initial stash token(s).`,
1099
+ steps
1100
+ });
1101
+ }
1102
+ async function prepareCreateVault(provider, input) {
1103
+ const owner = normalizeEvmAddress(input.owner);
1104
+ const stashTokens = normalizeStashTokens(input.stashTokens);
1105
+ const erc20Stash = stashTokens.filter(
1106
+ ({ token }) => token !== foreverMoneyDeployment.base.contracts.weth
1107
+ );
1108
+ const allowances = await Promise.all(
1109
+ erc20Stash.map(async ({ token }) => ({
1110
+ token,
1111
+ allowance: await new Contract2(
1112
+ token,
1113
+ ERC20_ABI,
1114
+ provider
1115
+ ).getFunction("allowance")(
1116
+ owner,
1117
+ foreverMoneyDeployment.base.contracts.vaultFactory
1118
+ )
1119
+ }))
1120
+ );
1121
+ return buildCreateVaultPlan({ ...input, allowances });
1122
+ }
1123
+ function buildDepositVaultPlan(input) {
1124
+ const owner = normalizeEvmAddress(input.owner);
1125
+ const manager = normalizeEvmAddress(input.manager);
1126
+ const akAddress = normalizeEvmAddress(input.akAddress);
1127
+ const deposits = normalizeStashTokens(input.deposits);
1128
+ if (deposits.length === 0) {
1129
+ throw new ForeverMoneyError(
1130
+ "INVALID_TRANSACTION_PLAN",
1131
+ "At least one vault deposit is required."
1132
+ );
1133
+ }
1134
+ const allowances = allowanceMap(input.allowances);
1135
+ assertAllowanceSet(allowances, deposits);
1136
+ const steps = [];
1137
+ for (const deposit of deposits) {
1138
+ if (deposit.token === foreverMoneyDeployment.base.contracts.weth) {
1139
+ steps.push(
1140
+ step(
1141
+ "transaction",
1142
+ "Deposit native ETH into the vault as WETH",
1143
+ owner,
1144
+ manager,
1145
+ managerInterface.encodeFunctionData("topUpAk", [
1146
+ akAddress,
1147
+ ZeroAddress2,
1148
+ deposit.amount
1149
+ ]),
1150
+ deposit.amount
1151
+ )
1152
+ );
1153
+ continue;
1154
+ }
1155
+ const allowance = allowances.get(deposit.token.toLowerCase());
1156
+ if (allowance === void 0) {
1157
+ throw new ForeverMoneyError(
1158
+ "INVALID_TRANSACTION_PLAN",
1159
+ `Missing allowance for deposit token ${deposit.token}.`
1160
+ );
1161
+ }
1162
+ if (allowance < deposit.amount) {
1163
+ steps.push(
1164
+ step(
1165
+ "approval",
1166
+ `Approve ${deposit.token} for the vault manager`,
1167
+ owner,
1168
+ deposit.token,
1169
+ erc20Interface2.encodeFunctionData("approve", [
1170
+ manager,
1171
+ deposit.amount
1172
+ ])
1173
+ )
1174
+ );
1175
+ }
1176
+ steps.push(
1177
+ step(
1178
+ "transaction",
1179
+ `Deposit ${deposit.token} into the vault`,
1180
+ owner,
1181
+ manager,
1182
+ managerInterface.encodeFunctionData("topUpAk", [
1183
+ akAddress,
1184
+ deposit.token,
1185
+ deposit.amount
1186
+ ])
1187
+ )
1188
+ );
1189
+ }
1190
+ return createTransactionPlan({
1191
+ action: "vault.deposit",
1192
+ summary: `Deposit ${deposits.length} token amount(s) into vault ${manager}.`,
1193
+ steps
1194
+ });
1195
+ }
1196
+ async function prepareDepositVault(provider, input) {
1197
+ const owner = normalizeEvmAddress(input.owner);
1198
+ const manager = normalizeEvmAddress(input.manager);
1199
+ const deposits = normalizeStashTokens(input.deposits);
1200
+ const erc20Deposits = deposits.filter(
1201
+ ({ token }) => token !== foreverMoneyDeployment.base.contracts.weth
1202
+ );
1203
+ const allowances = await Promise.all(
1204
+ erc20Deposits.map(async ({ token }) => ({
1205
+ token,
1206
+ allowance: await new Contract2(
1207
+ token,
1208
+ ERC20_ABI,
1209
+ provider
1210
+ ).getFunction("allowance")(owner, manager)
1211
+ }))
1212
+ );
1213
+ return buildDepositVaultPlan({ ...input, allowances });
1214
+ }
1215
+ function buildWithdrawVaultPlan(input) {
1216
+ const owner = normalizeEvmAddress(input.owner);
1217
+ const manager = normalizeEvmAddress(input.manager);
1218
+ const akAddress = normalizeEvmAddress(input.akAddress);
1219
+ assertArray(input.decreaseTokenIds, "Position token IDs");
1220
+ assertBoolean(input.unwrapWeth, "unwrapWeth");
1221
+ assertNonNegativeAmount(input.amount0, "Token 0 withdrawal");
1222
+ assertNonNegativeAmount(input.amount1, "Token 1 withdrawal");
1223
+ if (input.amount0 === 0n && input.amount1 === 0n) {
1224
+ throw new ForeverMoneyError(
1225
+ "AMOUNT_ZERO",
1226
+ "At least one withdrawal amount must be greater than zero."
1227
+ );
1228
+ }
1229
+ for (const tokenId of input.decreaseTokenIds) {
1230
+ assertNonNegativeAmount(tokenId, "Position token ID");
1231
+ }
1232
+ return createTransactionPlan({
1233
+ action: "vault.withdraw",
1234
+ summary: `Withdraw token amounts from vault ${manager}.`,
1235
+ steps: [
1236
+ step(
1237
+ "transaction",
1238
+ "Withdraw assets from the vault",
1239
+ owner,
1240
+ manager,
1241
+ managerInterface.encodeFunctionData(
1242
+ "withdrawFromAkAndPositions",
1243
+ [
1244
+ akAddress,
1245
+ input.amount0,
1246
+ input.amount1,
1247
+ input.decreaseTokenIds,
1248
+ input.unwrapWeth
1249
+ ]
1250
+ )
1251
+ )
1252
+ ]
1253
+ });
1254
+ }
1255
+ function buildClaimVaultFeesPlan(input) {
1256
+ const owner = normalizeEvmAddress(input.owner);
1257
+ const manager = normalizeEvmAddress(input.manager);
1258
+ const akAddress = normalizeEvmAddress(input.akAddress);
1259
+ return createTransactionPlan({
1260
+ action: "vault.claim-fees",
1261
+ summary: `Claim all fees for ${akAddress} from vault ${manager}.`,
1262
+ steps: [
1263
+ step(
1264
+ "transaction",
1265
+ "Claim vault fees",
1266
+ owner,
1267
+ manager,
1268
+ managerInterface.encodeFunctionData("claimFees(address)", [
1269
+ akAddress
1270
+ ])
1271
+ )
1272
+ ]
1273
+ });
1274
+ }
1275
+ function buildSetVaultStakingPlan(input) {
1276
+ const owner = normalizeEvmAddress(input.owner);
1277
+ const manager = normalizeEvmAddress(input.manager);
1278
+ const akAddress = normalizeEvmAddress(input.akAddress);
1279
+ assertBoolean(input.staking, "staking");
1280
+ const action = input.staking ? "stake" : "unstake";
1281
+ return createTransactionPlan({
1282
+ action: `vault.${action}`,
1283
+ summary: `${input.staking ? "Stake" : "Unstake"} the active position in vault ${manager}.`,
1284
+ steps: [
1285
+ step(
1286
+ "transaction",
1287
+ `${input.staking ? "Stake" : "Unstake"} the vault position`,
1288
+ owner,
1289
+ manager,
1290
+ managerInterface.encodeFunctionData(
1291
+ `${action}(address,bytes)`,
1292
+ [akAddress, "0x"]
1293
+ )
1294
+ )
1295
+ ]
1296
+ });
1297
+ }
1298
+
1299
+ // src/bridge/tracking.ts
1300
+ import { Interface as Interface4, toBeHex } from "ethers";
1301
+
1302
+ // src/bridge/receipts.ts
1303
+ import { Interface as Interface3, isHexString as isHexString2 } from "ethers";
1304
+
1305
+ // src/core/receipts.ts
1306
+ function isLogFrom(log, address) {
1307
+ return log.address.toLowerCase() === address.toLowerCase();
1308
+ }
1309
+
1310
+ // src/bridge/receipts.ts
1311
+ var alphaGatewayInterface = new Interface3(ALPHA_GATEWAY_ABI);
1312
+ var spokeGatewayInterface = new Interface3(SPOKE_GATEWAY_ABI);
1313
+ function assertBridgeDirection(value) {
1314
+ if (value !== "base-to-subtensor" && value !== "robinhood-to-subtensor" && value !== "subtensor-to-base" && value !== "subtensor-to-robinhood") {
1315
+ throw new ForeverMoneyError(
1316
+ "INVALID_TRANSACTION_PLAN",
1317
+ "Bridge direction must identify a canonical Base or Robinhood lane."
1318
+ );
1319
+ }
1320
+ }
1321
+ function evmChainFromBridgeDirection(direction) {
1322
+ assertBridgeDirection(direction);
1323
+ return direction.includes("robinhood") ? "robinhood" : "base";
1324
+ }
1325
+ function isEvmToSubtensorDirection(direction) {
1326
+ assertBridgeDirection(direction);
1327
+ return direction.endsWith("-to-subtensor");
1328
+ }
1329
+ function bridgeMessageIdFromReceipt(direction, receipt) {
1330
+ assertBridgeDirection(direction);
1331
+ const evm = getForeverMoneyEvmDeployment(
1332
+ evmChainFromBridgeDirection(direction)
1333
+ );
1334
+ const evmToSubtensor = isEvmToSubtensorDirection(direction);
1335
+ const [address, contractInterface, eventName] = evmToSubtensor ? [evm.contracts.gateway, spokeGatewayInterface, "BridgedToFinney"] : [
1336
+ foreverMoneyDeployment.subtensor.contracts.gateway,
1337
+ alphaGatewayInterface,
1338
+ "BridgedOut"
1339
+ ];
1340
+ for (const log of receipt.logs) {
1341
+ if (!isLogFrom(log, address)) continue;
1342
+ try {
1343
+ const parsed = contractInterface.parseLog({
1344
+ data: log.data,
1345
+ topics: [...log.topics]
1346
+ });
1347
+ const messageId = parsed?.args.messageId;
1348
+ if (parsed?.name === eventName && typeof messageId === "string" && isHexString2(messageId, 32) && (evmToSubtensor || parsed.args.destChainSelector === evm.ccipSelector)) {
1349
+ return messageId.toLowerCase();
1350
+ }
1351
+ } catch {
1352
+ }
1353
+ }
1354
+ return null;
1355
+ }
1356
+
1357
+ // src/bridge/tracking.ts
1358
+ var ccipExecutionInterface = new Interface4(CCIP_EXECUTION_ABI);
1359
+ var alphaGatewayInterface2 = new Interface4(ALPHA_GATEWAY_ABI);
1360
+ function destinationChainId(direction) {
1361
+ assertBridgeDirection(direction);
1362
+ const evm = getForeverMoneyEvmDeployment(
1363
+ evmChainFromBridgeDirection(direction)
1364
+ );
1365
+ return isEvmToSubtensorDirection(direction) ? foreverMoneyDeployment.subtensor.chainId : evm.chainId;
1366
+ }
1367
+ function sourceChainId(direction) {
1368
+ assertBridgeDirection(direction);
1369
+ const evm = getForeverMoneyEvmDeployment(
1370
+ evmChainFromBridgeDirection(direction)
1371
+ );
1372
+ return isEvmToSubtensorDirection(direction) ? evm.chainId : foreverMoneyDeployment.subtensor.chainId;
1373
+ }
1374
+ async function assertProviderChain(provider, expectedChainId, label) {
1375
+ const network = await provider.getNetwork();
1376
+ if (network.chainId !== BigInt(expectedChainId)) {
1377
+ throw new ForeverMoneyError(
1378
+ "CHAIN_MISMATCH",
1379
+ `${label} transport reported chain ID ${network.chainId}; expected ${expectedChainId}.`,
1380
+ {
1381
+ expectedChainId,
1382
+ actualChainId: network.chainId.toString()
1383
+ }
1384
+ );
1385
+ }
1386
+ }
1387
+ async function assertDestinationProvider(provider, direction) {
1388
+ const expectedChainId = destinationChainId(direction);
1389
+ await assertProviderChain(provider, expectedChainId, "Bridge destination");
1390
+ return expectedChainId;
1391
+ }
1392
+ async function getBridgeSourceStatus(provider, input) {
1393
+ const expectedChainId = sourceChainId(input.direction);
1394
+ const transactionHash = normalizeBytes32(
1395
+ input.transactionHash,
1396
+ "Transaction hash"
1397
+ );
1398
+ await assertProviderChain(provider, expectedChainId, "Bridge source");
1399
+ const receipt = await provider.getTransactionReceipt(transactionHash);
1400
+ if (receipt === null) {
1401
+ return Object.freeze({
1402
+ direction: input.direction,
1403
+ sourceChainId: expectedChainId,
1404
+ transactionHash,
1405
+ status: "pending",
1406
+ messageId: null
1407
+ });
1408
+ }
1409
+ if (receipt.status === 0) {
1410
+ return Object.freeze({
1411
+ direction: input.direction,
1412
+ sourceChainId: expectedChainId,
1413
+ transactionHash,
1414
+ status: "failed",
1415
+ messageId: null
1416
+ });
1417
+ }
1418
+ if (receipt.status !== 1) {
1419
+ throw new ForeverMoneyError(
1420
+ "INVALID_PROVIDER_RESPONSE",
1421
+ "The bridge source receipt has an invalid status."
1422
+ );
1423
+ }
1424
+ const messageId = bridgeMessageIdFromReceipt(input.direction, receipt);
1425
+ if (messageId === null) {
1426
+ throw new ForeverMoneyError(
1427
+ "INVALID_PROVIDER_RESPONSE",
1428
+ "The confirmed bridge source receipt does not contain its canonical gateway event."
1429
+ );
1430
+ }
1431
+ return Object.freeze({
1432
+ direction: input.direction,
1433
+ sourceChainId: expectedChainId,
1434
+ transactionHash,
1435
+ status: "confirmed",
1436
+ messageId
1437
+ });
1438
+ }
1439
+ async function getCcipDeliveryCheckpoint(provider, direction) {
1440
+ const expectedChainId = await assertDestinationProvider(provider, direction);
1441
+ const fromBlock = await provider.getBlockNumber();
1442
+ if (!Number.isSafeInteger(fromBlock) || fromBlock < 0) {
1443
+ throw new ForeverMoneyError(
1444
+ "INVALID_PROVIDER_RESPONSE",
1445
+ "Bridge destination transport returned an invalid block number."
1446
+ );
1447
+ }
1448
+ return Object.freeze({
1449
+ direction,
1450
+ destinationChainId: expectedChainId,
1451
+ fromBlock
1452
+ });
1453
+ }
1454
+ async function getCcipDeliveryStatus(provider, input) {
1455
+ const expectedChainId = destinationChainId(input.direction);
1456
+ const messageId = normalizeBytes32(input.messageId, "CCIP message ID");
1457
+ if (!Number.isSafeInteger(input.fromBlock) || input.fromBlock < 0) {
1458
+ throw new ForeverMoneyError(
1459
+ "INVALID_TRANSACTION_PLAN",
1460
+ "CCIP fromBlock must be a non-negative safe integer."
1461
+ );
1462
+ }
1463
+ await assertProviderChain(provider, expectedChainId, "Bridge destination");
1464
+ const event = ccipExecutionInterface.getEvent("ExecutionStateChanged");
1465
+ if (event === null) {
1466
+ throw new Error("CCIP execution event is missing from the SDK ABI.");
1467
+ }
1468
+ const evmChain = evmChainFromBridgeDirection(input.direction);
1469
+ const evm = getForeverMoneyEvmDeployment(evmChain);
1470
+ const evmToSubtensor = isEvmToSubtensorDirection(input.direction);
1471
+ const sourceSelector = evmToSubtensor ? evm.ccipSelector : foreverMoneyDeployment.subtensor.ccipSelector;
1472
+ const offRamp = evmToSubtensor ? evmChain === "base" ? foreverMoneyDeployment.subtensor.contracts.ccipOffRampFromBase : foreverMoneyDeployment.subtensor.contracts.ccipOffRampFromRobinhood : evm.contracts.ccipOffRampFromSubtensor;
1473
+ const logs = await provider.getLogs({
1474
+ address: offRamp,
1475
+ fromBlock: input.fromBlock,
1476
+ toBlock: "latest",
1477
+ topics: [event.topicHash, toBeHex(sourceSelector, 32), null, messageId]
1478
+ });
1479
+ const latest = logs.at(-1);
1480
+ if (latest === void 0) return "waiting";
1481
+ const parsed = ccipExecutionInterface.parseLog(latest);
1482
+ const state = Number(parsed?.args.state);
1483
+ if (state === 3) return "failure";
1484
+ if (state !== 2) return "waiting";
1485
+ if (!evmToSubtensor) return "success";
1486
+ const receipt = await provider.getTransactionReceipt(latest.transactionHash);
1487
+ if (receipt === null) {
1488
+ throw new ForeverMoneyError(
1489
+ "INVALID_PROVIDER_RESPONSE",
1490
+ "The CCIP execution receipt is unavailable."
1491
+ );
1492
+ }
1493
+ for (const log of receipt.logs) {
1494
+ if (log.address.toLowerCase() !== foreverMoneyDeployment.subtensor.contracts.gateway.toLowerCase()) {
1495
+ continue;
1496
+ }
1497
+ try {
1498
+ if (alphaGatewayInterface2.parseLog(log)?.name === "Claimable") {
1499
+ return "recovery";
1500
+ }
1501
+ } catch {
1502
+ }
1503
+ }
1504
+ return "success";
1505
+ }
1506
+
1507
+ // src/client.ts
1508
+ async function assertProviderChain2(provider, expectedChainId, name) {
1509
+ const network = await providerOperation(
1510
+ `Reading the ${name} chain ID`,
1511
+ () => provider.getNetwork()
1512
+ );
1513
+ if (network.chainId !== BigInt(expectedChainId)) {
1514
+ throw new ForeverMoneyError(
1515
+ "CHAIN_MISMATCH",
1516
+ `${name} transport reported chain ID ${network.chainId}; expected ${expectedChainId}.`,
1517
+ {
1518
+ expectedChainId,
1519
+ actualChainId: network.chainId.toString()
1520
+ }
1521
+ );
1522
+ }
1523
+ }
1524
+ function createForeverMoneyClient(options) {
1525
+ if (!options?.transports?.base || !options.transports.subtensor) {
1526
+ throw new ForeverMoneyError(
1527
+ "MISSING_TRANSPORT",
1528
+ "Base and Subtensor transports are required."
1529
+ );
1530
+ }
1531
+ const baseProvider = providerFromTransport(options.transports.base);
1532
+ const subtensorProvider = providerFromTransport(
1533
+ options.transports.subtensor
1534
+ );
1535
+ const robinhoodProvider = options.transports.robinhood ? providerFromTransport(options.transports.robinhood) : void 0;
1536
+ const verifyBaseConnection = async () => {
1537
+ await assertProviderChain2(baseProvider, BASE_CHAIN_ID, "Base");
1538
+ };
1539
+ const verifySubtensorConnection = async () => {
1540
+ await assertProviderChain2(
1541
+ subtensorProvider,
1542
+ SUBTENSOR_CHAIN_ID,
1543
+ "Subtensor"
1544
+ );
1545
+ };
1546
+ const verifyRobinhoodConnection = async () => {
1547
+ if (robinhoodProvider === void 0) return;
1548
+ await assertProviderChain2(
1549
+ robinhoodProvider,
1550
+ ROBINHOOD_CHAIN_ID,
1551
+ "Robinhood"
1552
+ );
1553
+ };
1554
+ const verifyConnections = async () => {
1555
+ await Promise.all([
1556
+ verifyBaseConnection(),
1557
+ verifyRobinhoodConnection(),
1558
+ verifySubtensorConnection()
1559
+ ]);
1560
+ };
1561
+ const providerForChain = (chainId) => {
1562
+ if (chainId === BASE_CHAIN_ID) return baseProvider;
1563
+ if (chainId === SUBTENSOR_CHAIN_ID) return subtensorProvider;
1564
+ if (chainId === ROBINHOOD_CHAIN_ID && robinhoodProvider !== void 0) {
1565
+ return robinhoodProvider;
1566
+ }
1567
+ throw new ForeverMoneyError(
1568
+ "MISSING_TRANSPORT",
1569
+ `A transport for chain ID ${chainId} is required for this operation.`
1570
+ );
1571
+ };
1572
+ return Object.freeze({
1573
+ bridge: Object.freeze({
1574
+ async prepareEvmToSubtensor(input) {
1575
+ const evm = getForeverMoneyEvmDeployment(input.evmChain);
1576
+ const provider = providerForChain(evm.chainId);
1577
+ await assertProviderChain2(provider, evm.chainId, evm.name);
1578
+ return providerOperation(
1579
+ `Preparing the ${evm.name} to Subtensor bridge`,
1580
+ () => prepareEvmToSubtensor(provider, input)
1581
+ );
1582
+ },
1583
+ async prepareBaseToSubtensor(input) {
1584
+ await verifyBaseConnection();
1585
+ return providerOperation(
1586
+ "Preparing the Base to Subtensor bridge",
1587
+ () => prepareBaseToSubtensor(baseProvider, input)
1588
+ );
1589
+ },
1590
+ async prepareSubtensorToEvm(input) {
1591
+ const evm = getForeverMoneyEvmDeployment(input.evmChain);
1592
+ await verifySubtensorConnection();
1593
+ return providerOperation(
1594
+ `Preparing the Subtensor to ${evm.name} bridge`,
1595
+ () => prepareSubtensorToEvm(subtensorProvider, input)
1596
+ );
1597
+ },
1598
+ async prepareSubtensorToBase(input) {
1599
+ await verifySubtensorConnection();
1600
+ return providerOperation(
1601
+ "Preparing the Subtensor to Base bridge",
1602
+ () => prepareSubtensorToBase(subtensorProvider, input)
1603
+ );
1604
+ },
1605
+ async getDeliveryCheckpoint(direction) {
1606
+ const expectedChainId = destinationChainId(direction);
1607
+ const provider = providerForChain(expectedChainId);
1608
+ return providerOperation(
1609
+ "Reading the bridge delivery checkpoint",
1610
+ () => getCcipDeliveryCheckpoint(provider, direction)
1611
+ );
1612
+ },
1613
+ async getDeliveryStatus(input) {
1614
+ const expectedChainId = destinationChainId(input.direction);
1615
+ const provider = providerForChain(expectedChainId);
1616
+ return providerOperation(
1617
+ "Reading CCIP delivery status",
1618
+ () => getCcipDeliveryStatus(provider, input)
1619
+ );
1620
+ },
1621
+ async getSourceStatus(input) {
1622
+ const expectedChainId = sourceChainId(input.direction);
1623
+ const provider = providerForChain(expectedChainId);
1624
+ return providerOperation(
1625
+ "Reading the bridge source transaction",
1626
+ () => getBridgeSourceStatus(provider, input)
1627
+ );
1628
+ }
1629
+ }),
1630
+ vaults: Object.freeze({
1631
+ async prepareCreate(input) {
1632
+ await verifyBaseConnection();
1633
+ return providerOperation(
1634
+ "Preparing vault creation",
1635
+ () => prepareCreateVault(baseProvider, input)
1636
+ );
1637
+ },
1638
+ async prepareDeposit(input) {
1639
+ await verifyBaseConnection();
1640
+ return providerOperation(
1641
+ "Preparing the vault deposit",
1642
+ () => prepareDepositVault(baseProvider, input)
1643
+ );
1644
+ }
1645
+ }),
1646
+ verifyConnections
1647
+ });
1648
+ }
1649
+
1650
+ // src/vaults/receipts.ts
1651
+ import { Interface as Interface5 } from "ethers";
1652
+ var vaultFactoryInterface = new Interface5(VAULT_FACTORY_ABI);
1653
+ function vaultManagerFromCreationReceipt(receipt) {
1654
+ for (const log of receipt.logs) {
1655
+ if (!isLogFrom(log, foreverMoneyDeployment.base.contracts.vaultFactory)) {
1656
+ continue;
1657
+ }
1658
+ try {
1659
+ const parsed = vaultFactoryInterface.parseLog({
1660
+ data: log.data,
1661
+ topics: [...log.topics]
1662
+ });
1663
+ if (parsed?.name === "SnLiquidityManagerCreated") {
1664
+ return normalizeEvmAddress(String(parsed.args.manager));
1665
+ }
1666
+ } catch {
1667
+ }
1668
+ }
1669
+ return null;
1670
+ }
1671
+
1672
+ // src/core/transactions.ts
1673
+ import { MaxUint256 as MaxUint2563 } from "ethers";
1674
+ function decimalQuantity(value, label) {
1675
+ if (!/^(0|[1-9][0-9]*)$/.test(value)) {
1676
+ throw new ForeverMoneyError(
1677
+ "INVALID_TRANSACTION_PLAN",
1678
+ `${label} must be an unsigned decimal integer.`
1679
+ );
1680
+ }
1681
+ const quantity = BigInt(value);
1682
+ if (quantity > MaxUint2563) {
1683
+ throw new ForeverMoneyError(
1684
+ "INVALID_TRANSACTION_PLAN",
1685
+ `${label} exceeds uint256.`
1686
+ );
1687
+ }
1688
+ return quantity;
1689
+ }
1690
+ function hexQuantity(value) {
1691
+ return `0x${value.toString(16)}`;
1692
+ }
1693
+ function toEip1193Transaction(transaction) {
1694
+ const value = decimalQuantity(transaction.value, "Transaction value");
1695
+ const gasLimit = transaction.gasLimit === void 0 ? void 0 : decimalQuantity(transaction.gasLimit, "Transaction gas limit");
1696
+ return Object.freeze({
1697
+ from: transaction.from,
1698
+ to: transaction.to,
1699
+ data: transaction.data,
1700
+ value: hexQuantity(value),
1701
+ ...gasLimit === void 0 ? {} : { gas: hexQuantity(gasLimit) }
1702
+ });
1703
+ }
1704
+ function toEthersTransaction(transaction) {
1705
+ return Object.freeze({
1706
+ chainId: transaction.chainId,
1707
+ to: transaction.to,
1708
+ data: transaction.data,
1709
+ value: decimalQuantity(transaction.value, "Transaction value"),
1710
+ ...transaction.gasLimit === void 0 ? {} : {
1711
+ gasLimit: decimalQuantity(
1712
+ transaction.gasLimit,
1713
+ "Transaction gas limit"
1714
+ )
1715
+ }
1716
+ });
1717
+ }
1718
+ export {
1719
+ BASE_CCIP_SELECTOR,
1720
+ BASE_CHAIN_ID,
1721
+ BASIS_POINTS,
1722
+ EVM_WEI_PER_RAO,
1723
+ FOREVERMONEY_DEPLOYMENT_VERSION,
1724
+ ForeverMoneyError,
1725
+ GAS_LIMIT_BUFFER_BPS,
1726
+ MIN_LIQUID_BASE_TO_SUBTENSOR_WEI,
1727
+ MIN_LIQUID_EVM_TO_SUBTENSOR_WEI,
1728
+ NETWORK_FEE_BUFFER_BPS,
1729
+ RAO_PER_TAO,
1730
+ ROBINHOOD_CCIP_SELECTOR,
1731
+ ROBINHOOD_CHAIN_ID,
1732
+ SUBTENSOR_CCIP_SELECTOR,
1733
+ SUBTENSOR_CHAIN_ID,
1734
+ assertWholeRao,
1735
+ bridgeMessageIdFromReceipt,
1736
+ buildBaseToSubtensorPlan,
1737
+ buildClaimVaultFeesPlan,
1738
+ buildCreateVaultPlan,
1739
+ buildDepositVaultPlan,
1740
+ buildEvmToSubtensorPlan,
1741
+ buildSetVaultStakingPlan,
1742
+ buildSubtensorToBasePlan,
1743
+ buildSubtensorToEvmPlan,
1744
+ buildWithdrawVaultPlan,
1745
+ createForeverMoneyClient,
1746
+ destinationChainId,
1747
+ evmChainFromBridgeDirection,
1748
+ evmToMirrorSS58,
1749
+ feeWithBuffer,
1750
+ foreverMoneyAbis,
1751
+ foreverMoneyDeployment,
1752
+ formatTaoAmount,
1753
+ gasLimitWithBuffer,
1754
+ getBridgeSourceStatus,
1755
+ getCcipDeliveryCheckpoint,
1756
+ getCcipDeliveryStatus,
1757
+ getForeverMoneyEvmDeployment,
1758
+ http,
1759
+ isBittensorSS58,
1760
+ isEvmToSubtensorDirection,
1761
+ normalizeEvmAddress,
1762
+ normalizeSS58,
1763
+ parseTaoAmount,
1764
+ sourceChainId,
1765
+ ss58ToPublicKey,
1766
+ toEip1193Transaction,
1767
+ toEthersTransaction,
1768
+ vaultManagerFromCreationReceipt
1769
+ };
1770
+ //# sourceMappingURL=index.js.map