@forevermoney/sdk 0.1.0 → 0.4.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.cjs CHANGED
@@ -27,12 +27,15 @@ __export(index_exports, {
27
27
  FOREVERMONEY_DEPLOYMENT_VERSION: () => FOREVERMONEY_DEPLOYMENT_VERSION,
28
28
  ForeverMoneyError: () => ForeverMoneyError,
29
29
  GAS_LIMIT_BUFFER_BPS: () => GAS_LIMIT_BUFFER_BPS,
30
+ MAX_PARTNER_FEE_BPS: () => MAX_PARTNER_FEE_BPS,
30
31
  MIN_LIQUID_BASE_TO_SUBTENSOR_WEI: () => MIN_LIQUID_BASE_TO_SUBTENSOR_WEI,
31
32
  MIN_LIQUID_EVM_TO_SUBTENSOR_WEI: () => MIN_LIQUID_EVM_TO_SUBTENSOR_WEI,
33
+ MIN_LIQUID_SUBTENSOR_TO_EVM_WEI: () => MIN_LIQUID_SUBTENSOR_TO_EVM_WEI,
32
34
  NETWORK_FEE_BUFFER_BPS: () => NETWORK_FEE_BUFFER_BPS,
33
35
  RAO_PER_TAO: () => RAO_PER_TAO,
34
36
  ROBINHOOD_CCIP_SELECTOR: () => ROBINHOOD_CCIP_SELECTOR,
35
37
  ROBINHOOD_CHAIN_ID: () => ROBINHOOD_CHAIN_ID,
38
+ SN80_NETUID: () => SN80_NETUID,
36
39
  SUBTENSOR_CCIP_SELECTOR: () => SUBTENSOR_CCIP_SELECTOR,
37
40
  SUBTENSOR_CHAIN_ID: () => SUBTENSOR_CHAIN_ID,
38
41
  assertWholeRao: () => assertWholeRao,
@@ -65,18 +68,20 @@ __export(index_exports, {
65
68
  normalizeEvmAddress: () => normalizeEvmAddress,
66
69
  normalizeSS58: () => normalizeSS58,
67
70
  parseTaoAmount: () => parseTaoAmount,
71
+ partnerFeeCut: () => partnerFeeCut,
72
+ partnerFeeTaoTopUp: () => partnerFeeTaoTopUp,
68
73
  sourceChainId: () => sourceChainId,
69
74
  ss58ToPublicKey: () => ss58ToPublicKey,
70
75
  toEip1193Transaction: () => toEip1193Transaction,
71
76
  toEthersTransaction: () => toEthersTransaction,
77
+ toViemTransaction: () => toViemTransaction,
72
78
  vaultManagerFromCreationReceipt: () => vaultManagerFromCreationReceipt
73
79
  });
74
80
  module.exports = __toCommonJS(index_exports);
75
81
 
76
82
  // src/core/addresses.ts
77
- var import_util = require("@polkadot/util");
78
- var import_util_crypto = require("@polkadot/util-crypto");
79
- var import_ethers = require("ethers");
83
+ var import_substrate_bindings = require("@polkadot-api/substrate-bindings");
84
+ var import_viem = require("viem");
80
85
 
81
86
  // src/core/errors.ts
82
87
  var ForeverMoneyError = class extends Error {
@@ -93,22 +98,33 @@ var ForeverMoneyError = class extends Error {
93
98
  // src/core/addresses.ts
94
99
  var BITTENSOR_SS58_PREFIX = 42;
95
100
  function normalizeEvmAddress(value) {
96
- if (!(0, import_ethers.isAddress)(value) || value === import_ethers.ZeroAddress) {
101
+ const candidate = typeof value === "string" && /^0x[0-9A-F]{40}$/.test(value) ? value.toLowerCase() : value;
102
+ if (!(0, import_viem.isAddress)(candidate) || candidate === import_viem.zeroAddress) {
97
103
  throw new ForeverMoneyError(
98
104
  "INVALID_ADDRESS",
99
105
  "Expected a non-zero EVM address."
100
106
  );
101
107
  }
102
- return (0, import_ethers.getAddress)(value);
108
+ return (0, import_viem.getAddress)(candidate);
103
109
  }
104
110
  function evmToMirrorSS58(evmAddress) {
105
- return (0, import_util_crypto.evmToAddress)(normalizeEvmAddress(evmAddress), BITTENSOR_SS58_PREFIX);
111
+ return (0, import_substrate_bindings.AccountId)(BITTENSOR_SS58_PREFIX).dec(
112
+ (0, import_substrate_bindings.Blake2256)(
113
+ (0, import_viem.concat)([
114
+ (0, import_viem.stringToBytes)("evm:"),
115
+ (0, import_viem.hexToBytes)(normalizeEvmAddress(evmAddress))
116
+ ])
117
+ )
118
+ );
106
119
  }
107
120
  function decodeBittensorAddress(value) {
108
121
  try {
109
- if (!value || (0, import_util.isHex)(value)) throw new Error("Raw hex is not SS58.");
110
- const publicKey = (0, import_util_crypto.decodeAddress)(value, false, BITTENSOR_SS58_PREFIX);
111
- if (publicKey.length !== 32) throw new Error("Invalid key length.");
122
+ if (typeof value !== "string" || !value || value.startsWith("0x"))
123
+ throw new Error("Raw hex is not SS58.");
124
+ const info = (0, import_substrate_bindings.getSs58AddressInfo)(value);
125
+ if (!info.isValid || info.ss58Format !== BITTENSOR_SS58_PREFIX || info.publicKey.length !== 32)
126
+ throw new Error("Invalid Bittensor address.");
127
+ const publicKey = info.publicKey;
112
128
  return publicKey;
113
129
  } catch {
114
130
  throw new ForeverMoneyError(
@@ -118,7 +134,7 @@ function decodeBittensorAddress(value) {
118
134
  }
119
135
  }
120
136
  function normalizeSS58(value) {
121
- return (0, import_util_crypto.encodeAddress)(decodeBittensorAddress(value), BITTENSOR_SS58_PREFIX);
137
+ return (0, import_substrate_bindings.AccountId)(BITTENSOR_SS58_PREFIX).dec(decodeBittensorAddress(value));
122
138
  }
123
139
  function isBittensorSS58(value) {
124
140
  try {
@@ -129,14 +145,14 @@ function isBittensorSS58(value) {
129
145
  }
130
146
  }
131
147
  function ss58ToPublicKey(value) {
132
- return (0, import_util.u8aToHex)(decodeBittensorAddress(value));
148
+ return (0, import_viem.bytesToHex)(decodeBittensorAddress(value));
133
149
  }
134
150
 
135
151
  // src/core/amounts.ts
136
- var import_ethers3 = require("ethers");
152
+ var import_viem3 = require("viem");
137
153
 
138
154
  // src/chains/deployment.ts
139
- var import_ethers2 = require("ethers");
155
+ var import_viem2 = require("viem");
140
156
  var BASE_CHAIN_ID = 8453;
141
157
  var ROBINHOOD_CHAIN_ID = 4663;
142
158
  var SUBTENSOR_CHAIN_ID = 964;
@@ -145,7 +161,8 @@ var ROBINHOOD_CCIP_SELECTOR = 6180753054346818345n;
145
161
  var SUBTENSOR_CCIP_SELECTOR = 2135107236357186872n;
146
162
  var RAO_PER_TAO = 1000000000n;
147
163
  var EVM_WEI_PER_RAO = 1000000000n;
148
- var FOREVERMONEY_DEPLOYMENT_VERSION = "1.1.0";
164
+ var SN80_NETUID = 80n;
165
+ var FOREVERMONEY_DEPLOYMENT_VERSION = "1.2.0";
149
166
  var foreverMoneyDeployment = Object.freeze({
150
167
  version: FOREVERMONEY_DEPLOYMENT_VERSION,
151
168
  base: Object.freeze({
@@ -154,26 +171,32 @@ var foreverMoneyDeployment = Object.freeze({
154
171
  chainId: BASE_CHAIN_ID,
155
172
  ccipSelector: BASE_CCIP_SELECTOR,
156
173
  contracts: Object.freeze({
157
- gateway: (0, import_ethers2.getAddress)("0x5EF3d7D19e4b233a1A169DA0d5CB02ec6b160a2C"),
158
- wrappedTao: (0, import_ethers2.getAddress)(
174
+ gateway: (0, import_viem2.getAddress)("0x1da2415229b614C787e145D1D7346eb496319C52"),
175
+ legacyGateway: (0, import_viem2.getAddress)(
176
+ "0x5EF3d7D19e4b233a1A169DA0d5CB02ec6b160a2C"
177
+ ),
178
+ wrappedTao: (0, import_viem2.getAddress)(
159
179
  "0xf3081494b87e8d5fb7960f066e931d1d0e6e3d67"
160
180
  ),
161
- ccipRouter: (0, import_ethers2.getAddress)(
181
+ wrappedSn80: (0, import_viem2.getAddress)(
182
+ "0x6f63d869011f95274498023b4abfc00b30c34378"
183
+ ),
184
+ ccipRouter: (0, import_viem2.getAddress)(
162
185
  "0x881e3A65B4d4a04dD529061dd0071cf975F58bCD"
163
186
  ),
164
- ccipOffRampFromSubtensor: (0, import_ethers2.getAddress)(
187
+ ccipOffRampFromSubtensor: (0, import_viem2.getAddress)(
165
188
  "0xf09AFe78d3c7d359b334d7cB88995751F7eC5E13"
166
189
  ),
167
- vaultFactory: (0, import_ethers2.getAddress)(
190
+ vaultFactory: (0, import_viem2.getAddress)(
168
191
  "0x9b7F3c7aa335D8BF9f91741E541820466735F868"
169
192
  ),
170
- vaultManagerImplementation: (0, import_ethers2.getAddress)(
193
+ vaultManagerImplementation: (0, import_viem2.getAddress)(
171
194
  "0x235Ad8654fe3a1619a9892eBCaD9361F416168da"
172
195
  ),
173
- batchLiquidityManagerQuery: (0, import_ethers2.getAddress)(
196
+ batchLiquidityManagerQuery: (0, import_viem2.getAddress)(
174
197
  "0xd117176C5E6809b68500995F59589A8c081565A5"
175
198
  ),
176
- weth: (0, import_ethers2.getAddress)("0x4200000000000000000000000000000000000006")
199
+ weth: (0, import_viem2.getAddress)("0x4200000000000000000000000000000000000006")
177
200
  }),
178
201
  deploymentBlock: 50098800
179
202
  }),
@@ -183,14 +206,17 @@ var foreverMoneyDeployment = Object.freeze({
183
206
  chainId: ROBINHOOD_CHAIN_ID,
184
207
  ccipSelector: ROBINHOOD_CCIP_SELECTOR,
185
208
  contracts: Object.freeze({
186
- gateway: (0, import_ethers2.getAddress)("0x53Dcc4FE04193e489BE537F722F65317DB1E65d8"),
187
- wrappedTao: (0, import_ethers2.getAddress)(
209
+ gateway: (0, import_viem2.getAddress)("0xf27fdA637131E25B2A1b4865ED9597d881980c7E"),
210
+ legacyGateway: (0, import_viem2.getAddress)(
211
+ "0x53Dcc4FE04193e489BE537F722F65317DB1E65d8"
212
+ ),
213
+ wrappedTao: (0, import_viem2.getAddress)(
188
214
  "0xf3081494B87e8D5fb7960f066E931D1D0e6E3d67"
189
215
  ),
190
- ccipRouter: (0, import_ethers2.getAddress)(
216
+ ccipRouter: (0, import_viem2.getAddress)(
191
217
  "0x06fC836cf9839B1cd891C440A0a45242DA6Ae1c9"
192
218
  ),
193
- ccipOffRampFromSubtensor: (0, import_ethers2.getAddress)(
219
+ ccipOffRampFromSubtensor: (0, import_viem2.getAddress)(
194
220
  "0xcDca5D374e46A6DDDab50bD2D9acB8c796eC35C3"
195
221
  )
196
222
  })
@@ -199,23 +225,29 @@ var foreverMoneyDeployment = Object.freeze({
199
225
  chainId: SUBTENSOR_CHAIN_ID,
200
226
  ccipSelector: SUBTENSOR_CCIP_SELECTOR,
201
227
  contracts: Object.freeze({
202
- gateway: (0, import_ethers2.getAddress)("0x998f20Fea90bF7792774dECc7f994716442B1705"),
203
- alphaVault: (0, import_ethers2.getAddress)(
228
+ gateway: (0, import_viem2.getAddress)("0xcd0C6d98D0A126B1c113d15b4c28F38321437787"),
229
+ legacyGateway: (0, import_viem2.getAddress)(
230
+ "0x998f20Fea90bF7792774dECc7f994716442B1705"
231
+ ),
232
+ alphaVault: (0, import_viem2.getAddress)(
204
233
  "0x11837459896D96F821a8D88eC93a3C8D152033D4"
205
234
  ),
206
- wrappedTao: (0, import_ethers2.getAddress)(
235
+ wrappedTao: (0, import_viem2.getAddress)(
207
236
  "0xC5b6C1632d34901239396F5E1BDe54B342900256"
208
237
  ),
209
- ccipRouter: (0, import_ethers2.getAddress)(
238
+ wrappedSn80: (0, import_viem2.getAddress)(
239
+ "0xfD628dE75EF96f0A5C59659159C6cA81E0DC2222"
240
+ ),
241
+ ccipRouter: (0, import_viem2.getAddress)(
210
242
  "0xD941fBEcD2b971d0F54b4C34286C95faB52B60B8"
211
243
  ),
212
- ccipOffRampFromBase: (0, import_ethers2.getAddress)(
244
+ ccipOffRampFromBase: (0, import_viem2.getAddress)(
213
245
  "0x51a6150400ed9F0Ae240F5D1b15E3b45Fc4339C7"
214
246
  ),
215
- ccipOffRampFromRobinhood: (0, import_ethers2.getAddress)(
247
+ ccipOffRampFromRobinhood: (0, import_viem2.getAddress)(
216
248
  "0x51a6150400ed9F0Ae240F5D1b15E3b45Fc4339C7"
217
249
  ),
218
- stakingPrecompile: (0, import_ethers2.getAddress)(
250
+ stakingPrecompile: (0, import_viem2.getAddress)(
219
251
  "0x0000000000000000000000000000000000000805"
220
252
  )
221
253
  })
@@ -245,7 +277,7 @@ function parseTaoAmount(value) {
245
277
  }
246
278
  let amount;
247
279
  try {
248
- amount = (0, import_ethers3.parseUnits)(value, 18);
280
+ amount = (0, import_viem3.parseUnits)(value, 18);
249
281
  } catch {
250
282
  throw new ForeverMoneyError(
251
283
  "AMOUNT_NOT_WHOLE_RAO",
@@ -274,7 +306,7 @@ function assertWholeRao(amountWei) {
274
306
  "The amount cannot be negative."
275
307
  );
276
308
  }
277
- if (amountWei > import_ethers3.MaxUint256) {
309
+ if (amountWei > import_viem3.maxUint256) {
278
310
  throw new ForeverMoneyError(
279
311
  "INVALID_TRANSACTION_PLAN",
280
312
  "The amount exceeds uint256."
@@ -300,7 +332,7 @@ function formatTaoAmount(amountWei) {
300
332
  "A TAO amount cannot be negative."
301
333
  );
302
334
  }
303
- if (amountWei > import_ethers3.MaxUint256) {
335
+ if (amountWei > import_viem3.maxUint256) {
304
336
  throw new ForeverMoneyError(
305
337
  "INVALID_TRANSACTION_PLAN",
306
338
  "The TAO amount exceeds uint256."
@@ -312,7 +344,8 @@ function formatTaoAmount(amountWei) {
312
344
  "TAO amounts must resolve to a whole RAO."
313
345
  );
314
346
  }
315
- return (0, import_ethers3.formatUnits)(amountWei, 18);
347
+ const formatted = (0, import_viem3.formatUnits)(amountWei, 18);
348
+ return formatted.includes(".") ? formatted : `${formatted}.0`;
316
349
  }
317
350
  function mulDivCeil(value, numerator, denominator) {
318
351
  return (value * numerator + denominator - 1n) / denominator;
@@ -335,7 +368,7 @@ function feeWithBuffer(fee) {
335
368
  BASIS_POINTS + NETWORK_FEE_BUFFER_BPS,
336
369
  BASIS_POINTS
337
370
  );
338
- if (buffered > import_ethers3.MaxUint256) {
371
+ if (buffered > import_viem3.maxUint256) {
339
372
  throw new ForeverMoneyError(
340
373
  "INVALID_TRANSACTION_PLAN",
341
374
  "The buffered network fee exceeds uint256."
@@ -361,7 +394,7 @@ function gasLimitWithBuffer(gasLimit) {
361
394
  BASIS_POINTS + GAS_LIMIT_BUFFER_BPS,
362
395
  BASIS_POINTS
363
396
  );
364
- if (buffered > import_ethers3.MaxUint256) {
397
+ if (buffered > import_viem3.maxUint256) {
365
398
  throw new ForeverMoneyError(
366
399
  "INVALID_TRANSACTION_PLAN",
367
400
  "The buffered gas limit exceeds uint256."
@@ -385,23 +418,34 @@ var SPOKE_GATEWAY_ABI = Object.freeze([
385
418
  "function ROUTER() view returns (address)",
386
419
  "function BITTENSOR_SELECTOR() view returns (uint64)",
387
420
  "function SUBTENSOR_GATEWAY() view returns (address)",
421
+ "function maxIntegratorFeeBps() view returns (uint16)",
422
+ "function bridgeFeeBps() view returns (uint16)",
388
423
  "function quoteBridgeToFinney(address token,uint256 amount,(bytes32 ss58,address evmFallback,bool wantLiquid,uint256 minTaoOut) exit) view returns (uint256 fee)",
389
424
  "function bridgeToFinney(address token,uint256 amount,(bytes32 ss58,address evmFallback,bool wantLiquid,uint256 minTaoOut) exit) payable returns (bytes32 messageId)",
425
+ "function quoteBridgeToFinneyWithFee(address token,uint256 amount,(bytes32 ss58,address evmFallback,bool wantLiquid,uint256 minTaoOut) exit,uint256 gasLimit,(address recipient,uint16 bps) integrator) view returns (uint256 fee,uint256 cut,uint256 amountCrossing)",
426
+ "function bridgeToFinneyWithFee(address token,uint256 amount,(bytes32 ss58,address evmFallback,bool wantLiquid,uint256 minTaoOut) exit,uint256 gasLimit,(address recipient,uint16 bps) integrator) payable returns (bytes32 messageId)",
390
427
  "event BridgedToFinney(address indexed token,address indexed sender,bytes32 indexed ss58,uint256 amount,bytes32 messageId)"
391
428
  ]);
392
429
  var ALPHA_GATEWAY_ABI = Object.freeze([
393
430
  "function ROUTER() view returns (address)",
394
431
  "function allowedLane(uint64 selector) view returns (bool)",
432
+ "function maxIntegratorFeeBps() view returns (uint16)",
433
+ "function bridgeFeeBps() view returns (uint16)",
434
+ "function integratorTaoTopUp(uint256 taoAmount,uint16 bps) pure returns (uint256)",
395
435
  "function quoteBridgeOut(uint64 destSelector,address token,address recipient,uint256 mintedAmount) view returns (uint256 fee)",
396
436
  "function bridgeOut(uint64 destSelector,address token,address recipient,uint256 taoAmount,uint256 stakedAlphaRao,uint256 minTokenOut) payable returns (bytes32 messageId)",
437
+ "function integratorCut(uint256 amount,uint16 bps) pure returns (uint256)",
438
+ "function quoteBridgeOutWithFee(uint64 destSelector,address token,address recipient,uint256 mintedAmount,uint256 taoAmount,uint256 stakedAlphaRao,(address recipient,uint16 bps) integrator) view returns (uint256 fee,uint256 nativeTopUp,uint256 alphaTopUp,uint256 amountCrossing)",
439
+ "function bridgeOutWithFee(uint64 destSelector,address token,address recipient,uint256 taoAmount,uint256 stakedAlphaRao,uint256 minTokenOut,(address recipient,uint16 bps) integrator) payable returns (bytes32 messageId)",
397
440
  "function claimLiquid(address token,uint256 minTaoOut,address to)",
398
441
  "function claimNative(address to)",
399
442
  "function claimStaked(address token,bytes32 destColdkey,address to)",
400
443
  "function claimToken(address token,address to)",
401
444
  "function claimableNative(address account) view returns (uint256)",
402
- "function claimableToken(address account,address token) view returns (uint256)",
445
+ "function claimableToken(address token,address account) view returns (uint256)",
403
446
  "event BridgedOut(uint64 destChainSelector,address indexed token,address indexed sender,address indexed recipient,uint256 minted,bytes32 messageId)",
404
447
  "event Claimable(address indexed token,address indexed user,uint256 native,uint256 wsn)",
448
+ "event NotDelivered(uint64 indexed sourceChainSelector,address indexed token,uint256 amount,uint8 reason)",
405
449
  "event DeliveredLiquid(address indexed token,bytes32 indexed ss58,uint256 taoOut)",
406
450
  "event DeliveredStaked(address indexed token,bytes32 indexed ss58,uint256 alphaRao)"
407
451
  ]);
@@ -445,10 +489,10 @@ var foreverMoneyAbis = Object.freeze({
445
489
  });
446
490
 
447
491
  // src/bridge/plans.ts
448
- var import_ethers6 = require("ethers");
492
+ var import_viem6 = require("viem");
449
493
 
450
494
  // src/core/plans.ts
451
- var import_ethers4 = require("ethers");
495
+ var import_viem4 = require("viem");
452
496
  function createTransactionPlan(plan) {
453
497
  const steps = plan.steps.map(
454
498
  (step2) => Object.freeze({
@@ -465,12 +509,12 @@ function createTransactionPlan(plan) {
465
509
  };
466
510
  return Object.freeze({
467
511
  ...versionedPlan,
468
- hash: (0, import_ethers4.keccak256)((0, import_ethers4.toUtf8Bytes)(JSON.stringify(versionedPlan)))
512
+ hash: (0, import_viem4.keccak256)((0, import_viem4.stringToBytes)(JSON.stringify(versionedPlan)))
469
513
  });
470
514
  }
471
515
 
472
516
  // src/core/validation.ts
473
- var import_ethers5 = require("ethers");
517
+ var import_viem5 = require("viem");
474
518
  function assertBigInt(amount, label) {
475
519
  if (typeof amount !== "bigint") {
476
520
  throw new ForeverMoneyError(
@@ -493,7 +537,7 @@ function assertPositiveAmount(amount, label) {
493
537
  `${label} cannot be negative.`
494
538
  );
495
539
  }
496
- if (amount > import_ethers5.MaxUint256) {
540
+ if (amount > import_viem5.maxUint256) {
497
541
  throw new ForeverMoneyError(
498
542
  "INVALID_TRANSACTION_PLAN",
499
543
  `${label} exceeds uint256.`
@@ -508,7 +552,7 @@ function assertNonNegativeAmount(amount, label) {
508
552
  `${label} cannot be negative.`
509
553
  );
510
554
  }
511
- if (amount > import_ethers5.MaxUint256) {
555
+ if (amount > import_viem5.maxUint256) {
512
556
  throw new ForeverMoneyError(
513
557
  "INVALID_TRANSACTION_PLAN",
514
558
  `${label} exceeds uint256.`
@@ -540,7 +584,7 @@ function assertRecord(value, label) {
540
584
  }
541
585
  }
542
586
  function normalizeBytes32(value, label) {
543
- if (!(0, import_ethers5.isHexString)(value, 32)) {
587
+ if (!(0, import_viem5.isHex)(value, { strict: true }) || value.length !== 66) {
544
588
  throw new ForeverMoneyError(
545
589
  "INVALID_BYTES32",
546
590
  `${label} must be a 32-byte hex value.`
@@ -549,16 +593,111 @@ function normalizeBytes32(value, label) {
549
593
  return value.toLowerCase();
550
594
  }
551
595
  function normalizeOptionalBytes32(value, label) {
552
- return value === void 0 ? import_ethers5.ZeroHash : normalizeBytes32(value, label);
596
+ return value === void 0 ? import_viem5.zeroHash : normalizeBytes32(value, label);
553
597
  }
554
598
 
555
599
  // src/bridge/plans.ts
556
- var erc20Interface = new import_ethers6.Interface(ERC20_ABI);
557
- var spokeInterface = new import_ethers6.Interface(SPOKE_GATEWAY_ABI);
558
- var alphaInterface = new import_ethers6.Interface(ALPHA_GATEWAY_ABI);
559
- var stakingInterface = new import_ethers6.Interface(STAKING_ABI);
600
+ var erc20Abi = (0, import_viem6.parseAbi)(ERC20_ABI);
601
+ var spokeAbi = (0, import_viem6.parseAbi)(SPOKE_GATEWAY_ABI);
602
+ var alphaAbi = (0, import_viem6.parseAbi)(ALPHA_GATEWAY_ABI);
603
+ var stakingAbi = (0, import_viem6.parseAbi)(STAKING_ABI);
560
604
  var MIN_LIQUID_EVM_TO_SUBTENSOR_WEI = 10000000000000000n;
561
605
  var MIN_LIQUID_BASE_TO_SUBTENSOR_WEI = MIN_LIQUID_EVM_TO_SUBTENSOR_WEI;
606
+ var MIN_LIQUID_SUBTENSOR_TO_EVM_WEI = 2000000n * EVM_WEI_PER_RAO;
607
+ var MAX_PARTNER_FEE_BPS = 1e4;
608
+ var NO_PARTNER_FEE = {
609
+ recipient: "0x0000000000000000000000000000000000000000",
610
+ bps: 0
611
+ };
612
+ function resolvePartnerFee(fee, gateway) {
613
+ if (fee === void 0) return NO_PARTNER_FEE;
614
+ if (typeof fee.bps !== "number" || !Number.isInteger(fee.bps) || fee.bps < 0 || fee.bps > MAX_PARTNER_FEE_BPS) {
615
+ throw new ForeverMoneyError(
616
+ "INVALID_PARTNER_FEE",
617
+ `Partner fee bps must be an integer between 0 and ${MAX_PARTNER_FEE_BPS}.`,
618
+ { bps: fee.bps }
619
+ );
620
+ }
621
+ if (fee.bps === 0) return NO_PARTNER_FEE;
622
+ const recipient = normalizeEvmAddress(fee.recipient);
623
+ if (recipient === NO_PARTNER_FEE.recipient || recipient.toLowerCase() === gateway.toLowerCase()) {
624
+ throw new ForeverMoneyError(
625
+ "INVALID_PARTNER_FEE",
626
+ "Partner fee recipient must not be the zero address or the gateway.",
627
+ { recipient }
628
+ );
629
+ }
630
+ return { recipient, bps: fee.bps };
631
+ }
632
+ function partnerFeeCut(amount, bps) {
633
+ return amount * BigInt(bps) / 10000n;
634
+ }
635
+ function partnerFeeTaoTopUp(taoAmount, bps) {
636
+ const raw = partnerFeeCut(taoAmount, bps);
637
+ if (raw === 0n) return 0n;
638
+ return (raw + EVM_WEI_PER_RAO - 1n) / EVM_WEI_PER_RAO * EVM_WEI_PER_RAO;
639
+ }
640
+ function assertPartnerFeeAllowed(fee, maxBps) {
641
+ if (fee.bps > maxBps) {
642
+ throw new ForeverMoneyError(
643
+ "INVALID_PARTNER_FEE",
644
+ `Partner fee of ${fee.bps} bps exceeds the gateway maximum of ${maxBps} bps.`,
645
+ { bps: fee.bps, maxBps }
646
+ );
647
+ }
648
+ }
649
+ function bridgeAsset(evmChain, asset = "tao") {
650
+ if (asset !== "tao" && asset !== "sn80") {
651
+ throw new ForeverMoneyError(
652
+ "INVALID_TRANSACTION_PLAN",
653
+ 'Asset must be "tao" or "sn80".'
654
+ );
655
+ }
656
+ if (asset === "sn80" && evmChain !== "base") {
657
+ throw new ForeverMoneyError(
658
+ "INVALID_TRANSACTION_PLAN",
659
+ "SN80 bridging is supported between Base and Subtensor."
660
+ );
661
+ }
662
+ const evm = getForeverMoneyEvmDeployment(evmChain);
663
+ return asset === "sn80" ? {
664
+ evmToken: foreverMoneyDeployment.base.contracts.wrappedSn80,
665
+ subtensorToken: foreverMoneyDeployment.subtensor.contracts.wrappedSn80,
666
+ label: "SN80",
667
+ wrappedLabel: "SN80"
668
+ } : {
669
+ evmToken: evm.contracts.wrappedTao,
670
+ subtensorToken: foreverMoneyDeployment.subtensor.contracts.wrappedTao,
671
+ label: "TAO",
672
+ wrappedLabel: "wrapped TAO"
673
+ };
674
+ }
675
+ function assertAssetMode(asset, mode) {
676
+ if (asset === "sn80" && mode !== "staked") {
677
+ throw new ForeverMoneyError(
678
+ "INVALID_TRANSACTION_PLAN",
679
+ "SN80 bridging requires staked subnet 80 input or delivery; liquid TAO conversion is not supported."
680
+ );
681
+ }
682
+ }
683
+ function sourceNetuid(input) {
684
+ if (input.source !== "staked") return void 0;
685
+ const netuid = input.netuid ?? (input.asset === "sn80" ? SN80_NETUID : void 0);
686
+ if (netuid === void 0) {
687
+ throw new ForeverMoneyError(
688
+ "INVALID_TRANSACTION_PLAN",
689
+ "A netuid is required when bridging staked TAO."
690
+ );
691
+ }
692
+ assertNonNegativeAmount(netuid, "netuid");
693
+ if (input.asset === "sn80" && netuid !== SN80_NETUID) {
694
+ throw new ForeverMoneyError(
695
+ "INVALID_TRANSACTION_PLAN",
696
+ "SN80 staking approvals must use netuid 80."
697
+ );
698
+ }
699
+ return netuid;
700
+ }
562
701
  function assertDelivery(value) {
563
702
  if (value !== "liquid" && value !== "staked") {
564
703
  throw new ForeverMoneyError(
@@ -588,6 +727,19 @@ function assertBaseToSubtensorAmount(amountWei, delivery) {
588
727
  );
589
728
  }
590
729
  }
730
+ function assertSubtensorToEvmAmount(amountWei, source) {
731
+ assertWholeRao(amountWei);
732
+ if (source === "liquid" && amountWei < MIN_LIQUID_SUBTENSOR_TO_EVM_WEI) {
733
+ throw new ForeverMoneyError(
734
+ "AMOUNT_BELOW_MINIMUM",
735
+ "Bridging liquid TAO from Subtensor requires at least 0.002 TAO.",
736
+ {
737
+ amountWei: amountWei.toString(),
738
+ minimumAmountWei: MIN_LIQUID_SUBTENSOR_TO_EVM_WEI.toString()
739
+ }
740
+ );
741
+ }
742
+ }
591
743
  function transactionStep(kind, label, chainId, from, to, data, value, gasLimit) {
592
744
  return {
593
745
  kind,
@@ -602,9 +754,94 @@ function transactionStep(kind, label, chainId, from, to, data, value, gasLimit)
602
754
  }
603
755
  };
604
756
  }
757
+ function partnerFeeSummary(charged, unit, fee) {
758
+ if (fee.bps === 0) return "";
759
+ return ` Partner fee: ${charged} wei of ${unit} (${fee.bps} bps) on top, paid to ${fee.recipient}.`;
760
+ }
761
+ function encodeSpokeBridge(token, amountWei, exit, fee) {
762
+ return fee.bps === 0 ? (0, import_viem6.encodeFunctionData)({
763
+ abi: spokeAbi,
764
+ functionName: "bridgeToFinney",
765
+ args: [token, amountWei, exit]
766
+ }) : (0, import_viem6.encodeFunctionData)({
767
+ abi: spokeAbi,
768
+ functionName: "bridgeToFinneyWithFee",
769
+ args: [token, amountWei, exit, 0n, fee]
770
+ });
771
+ }
772
+ function encodeHubBridge(destSelector, token, recipient, taoAmount, stakedAlphaRao, minTokenOut, fee) {
773
+ const args = [
774
+ destSelector,
775
+ token,
776
+ recipient,
777
+ taoAmount,
778
+ stakedAlphaRao,
779
+ minTokenOut
780
+ ];
781
+ return fee.bps === 0 ? (0, import_viem6.encodeFunctionData)({
782
+ abi: alphaAbi,
783
+ functionName: "bridgeOut",
784
+ args
785
+ }) : (0, import_viem6.encodeFunctionData)({
786
+ abi: alphaAbi,
787
+ functionName: "bridgeOutWithFee",
788
+ args: [...args, fee]
789
+ });
790
+ }
791
+ async function quoteSpokeWithFee(provider, gateway, token, amountWei, exit, fee) {
792
+ const maxBps = await provider.readContract({
793
+ address: gateway,
794
+ abi: spokeAbi,
795
+ functionName: "maxIntegratorFeeBps"
796
+ });
797
+ assertPartnerFeeAllowed(fee, maxBps);
798
+ const [networkFee] = await provider.readContract({
799
+ address: gateway,
800
+ abi: spokeAbi,
801
+ functionName: "quoteBridgeToFinneyWithFee",
802
+ args: [token, amountWei, exit, 0n, fee]
803
+ });
804
+ return networkFee;
805
+ }
806
+ async function quoteHubWithFee(provider, gateway, destSelector, token, recipient, mintedAmount, taoAmount, stakedAlphaRao, fee) {
807
+ const maxBps = await provider.readContract({
808
+ address: gateway,
809
+ abi: alphaAbi,
810
+ functionName: "maxIntegratorFeeBps"
811
+ });
812
+ assertPartnerFeeAllowed(fee, maxBps);
813
+ const [networkFee, taoTopUp, alphaTopUpRao, crossing] = await provider.readContract({
814
+ address: gateway,
815
+ abi: alphaAbi,
816
+ functionName: "quoteBridgeOutWithFee",
817
+ args: [
818
+ destSelector,
819
+ token,
820
+ recipient,
821
+ mintedAmount,
822
+ taoAmount,
823
+ stakedAlphaRao,
824
+ fee
825
+ ]
826
+ });
827
+ if (crossing !== mintedAmount || taoTopUp !== partnerFeeTaoTopUp(taoAmount, fee.bps) || alphaTopUpRao !== partnerFeeCut(stakedAlphaRao, fee.bps)) {
828
+ throw new ForeverMoneyError(
829
+ "INVALID_PROVIDER_RESPONSE",
830
+ "The gateway partner fee quote does not match the SDK fee calculation.",
831
+ {
832
+ crossing: crossing.toString(),
833
+ taoTopUp: taoTopUp.toString(),
834
+ alphaTopUpRao: alphaTopUpRao.toString()
835
+ }
836
+ );
837
+ }
838
+ return { networkFee, taoTopUp, alphaTopUpRao };
839
+ }
605
840
  function buildEvmToSubtensorPlan(input) {
606
841
  const sender = normalizeEvmAddress(input.sender);
842
+ const asset = bridgeAsset(input.evmChain, input.asset);
607
843
  assertDelivery(input.delivery);
844
+ assertAssetMode(input.asset, input.delivery);
608
845
  assertBaseToSubtensorAmount(input.amountWei, input.delivery);
609
846
  assertNonNegativeAmount(input.allowanceWei, "Token allowance");
610
847
  assertNonNegativeAmount(input.exactNetworkFeeWei, "Network fee");
@@ -616,19 +853,25 @@ function buildEvmToSubtensorPlan(input) {
616
853
  minTaoOut: input.amountWei
617
854
  };
618
855
  const evm = getForeverMoneyEvmDeployment(input.evmChain);
856
+ const partnerFee = resolvePartnerFee(
857
+ input.partnerFee,
858
+ evm.contracts.gateway
859
+ );
860
+ const cut = partnerFeeCut(input.amountWei, partnerFee.bps);
619
861
  const steps = [];
620
- if (input.allowanceWei < input.amountWei) {
862
+ if (input.allowanceWei < input.amountWei + cut) {
621
863
  steps.push(
622
864
  transactionStep(
623
865
  "approval",
624
- "Approve wrapped TAO for the ForeverMoney gateway",
866
+ `Approve ${asset.wrappedLabel} for the ForeverMoney gateway`,
625
867
  evm.chainId,
626
868
  sender,
627
- evm.contracts.wrappedTao,
628
- erc20Interface.encodeFunctionData("approve", [
629
- evm.contracts.gateway,
630
- input.amountWei
631
- ]),
869
+ asset.evmToken,
870
+ (0, import_viem6.encodeFunctionData)({
871
+ abi: erc20Abi,
872
+ functionName: "approve",
873
+ args: [evm.contracts.gateway, input.amountWei + cut]
874
+ }),
632
875
  0n
633
876
  )
634
877
  );
@@ -637,13 +880,15 @@ function buildEvmToSubtensorPlan(input) {
637
880
  steps.push(
638
881
  transactionStep(
639
882
  "transaction",
640
- `Bridge wrapped TAO from ${evm.name} to Subtensor (${input.delivery})`,
883
+ `Bridge ${asset.wrappedLabel} from ${evm.name} to Subtensor (${input.delivery})`,
641
884
  evm.chainId,
642
885
  sender,
643
886
  evm.contracts.gateway,
644
- spokeInterface.encodeFunctionData(
645
- "bridgeToFinney(address,uint256,(bytes32,address,bool,uint256))",
646
- [evm.contracts.wrappedTao, input.amountWei, exit]
887
+ encodeSpokeBridge(
888
+ asset.evmToken,
889
+ input.amountWei,
890
+ exit,
891
+ partnerFee
647
892
  ),
648
893
  value,
649
894
  input.estimatedBridgeGas === void 0 ? void 0 : gasLimitWithBuffer(input.estimatedBridgeGas)
@@ -651,7 +896,7 @@ function buildEvmToSubtensorPlan(input) {
651
896
  );
652
897
  return createTransactionPlan({
653
898
  action: evm.key === "base" ? "bridge.base-to-subtensor" : "bridge.robinhood-to-subtensor",
654
- summary: `Bridge ${input.amountWei} wei of wrapped TAO from ${evm.name} to ${destination}.`,
899
+ summary: `Bridge ${input.amountWei} wei of ${asset.wrappedLabel} from ${evm.name} to ${destination}.${partnerFeeSummary(cut, asset.wrappedLabel, partnerFee)}`,
655
900
  steps
656
901
  });
657
902
  }
@@ -661,21 +906,23 @@ function buildBaseToSubtensorPlan(input) {
661
906
  function buildSubtensorToEvmPlan(input) {
662
907
  const sender = normalizeEvmAddress(input.sender);
663
908
  const recipient = normalizeEvmAddress(input.recipient);
909
+ const asset = bridgeAsset(input.evmChain, input.asset);
664
910
  assertSource(input.source);
665
- assertWholeRao(input.amountWei);
911
+ assertAssetMode(input.asset, input.source);
912
+ const netuid = sourceNetuid(input);
913
+ assertSubtensorToEvmAmount(input.amountWei, input.source);
666
914
  assertNonNegativeAmount(input.exactNetworkFeeWei, "Network fee");
667
915
  const { subtensor } = foreverMoneyDeployment;
668
916
  const evm = getForeverMoneyEvmDeployment(input.evmChain);
917
+ const partnerFee = resolvePartnerFee(
918
+ input.partnerFee,
919
+ subtensor.contracts.gateway
920
+ );
669
921
  const amountRao = input.amountWei / EVM_WEI_PER_RAO;
922
+ const alphaTopUpRao = input.source === "staked" ? partnerFeeCut(amountRao, partnerFee.bps) : 0n;
923
+ const taoTopUp = input.source === "liquid" ? partnerFeeTaoTopUp(input.amountWei, partnerFee.bps) : 0n;
670
924
  const steps = [];
671
925
  if (input.source === "staked") {
672
- if (input.netuid === void 0) {
673
- throw new ForeverMoneyError(
674
- "INVALID_TRANSACTION_PLAN",
675
- "A netuid is required when bridging staked TAO."
676
- );
677
- }
678
- assertNonNegativeAmount(input.netuid, "netuid");
679
926
  if (input.stakingAllowanceRao === void 0) {
680
927
  throw new ForeverMoneyError(
681
928
  "INVALID_TRANSACTION_PLAN",
@@ -683,19 +930,23 @@ function buildSubtensorToEvmPlan(input) {
683
930
  );
684
931
  }
685
932
  assertNonNegativeAmount(input.stakingAllowanceRao, "Staking allowance");
686
- if (input.stakingAllowanceRao < amountRao) {
933
+ if (input.stakingAllowanceRao < amountRao + alphaTopUpRao) {
687
934
  steps.push(
688
935
  transactionStep(
689
936
  "approval",
690
- "Approve staked TAO for the ForeverMoney gateway",
937
+ `Approve staked ${asset.label} for the ForeverMoney gateway`,
691
938
  subtensor.chainId,
692
939
  sender,
693
940
  subtensor.contracts.stakingPrecompile,
694
- stakingInterface.encodeFunctionData("approve", [
695
- subtensor.contracts.gateway,
696
- input.netuid,
697
- amountRao
698
- ]),
941
+ (0, import_viem6.encodeFunctionData)({
942
+ abi: stakingAbi,
943
+ functionName: "approve",
944
+ args: [
945
+ subtensor.contracts.gateway,
946
+ netuid,
947
+ amountRao + alphaTopUpRao
948
+ ]
949
+ }),
699
950
  0n
700
951
  )
701
952
  );
@@ -708,30 +959,35 @@ function buildSubtensorToEvmPlan(input) {
708
959
  }
709
960
  const taoAmount = input.source === "liquid" ? input.amountWei : 0n;
710
961
  const stakedAlphaRao = input.source === "staked" ? amountRao : 0n;
711
- const value = taoAmount + feeWithBuffer(input.exactNetworkFeeWei);
962
+ const value = taoAmount + taoTopUp + feeWithBuffer(input.exactNetworkFeeWei);
712
963
  assertNonNegativeAmount(value, "Transaction value");
713
964
  steps.push(
714
965
  transactionStep(
715
966
  "transaction",
716
- `Bridge ${input.source} TAO from Subtensor to ${evm.name}`,
967
+ `Bridge ${input.source} ${asset.label} from Subtensor to ${evm.name}`,
717
968
  subtensor.chainId,
718
969
  sender,
719
970
  subtensor.contracts.gateway,
720
- alphaInterface.encodeFunctionData("bridgeOut", [
971
+ encodeHubBridge(
721
972
  evm.ccipSelector,
722
- subtensor.contracts.wrappedTao,
973
+ asset.subtensorToken,
723
974
  recipient,
724
975
  taoAmount,
725
976
  stakedAlphaRao,
726
- input.amountWei
727
- ]),
977
+ input.amountWei,
978
+ partnerFee
979
+ ),
728
980
  value,
729
981
  input.estimatedBridgeGas === void 0 ? void 0 : gasLimitWithBuffer(input.estimatedBridgeGas)
730
982
  )
731
983
  );
732
984
  return createTransactionPlan({
733
985
  action: evm.key === "base" ? "bridge.subtensor-to-base" : "bridge.subtensor-to-robinhood",
734
- summary: `Bridge ${input.amountWei} wei of ${input.source} TAO from Subtensor to ${recipient} on ${evm.name}.`,
986
+ summary: `Bridge ${input.amountWei} wei of ${input.source} ${asset.label} from Subtensor to ${recipient} on ${evm.name}.${partnerFeeSummary(
987
+ input.source === "liquid" ? taoTopUp : alphaTopUpRao * EVM_WEI_PER_RAO,
988
+ input.source === "liquid" ? "TAO" : `staked ${asset.label}`,
989
+ partnerFee
990
+ )}`,
735
991
  steps
736
992
  });
737
993
  }
@@ -740,7 +996,9 @@ function buildSubtensorToBasePlan(input) {
740
996
  }
741
997
  async function prepareEvmToSubtensor(provider, input) {
742
998
  const sender = normalizeEvmAddress(input.sender);
999
+ const asset = bridgeAsset(input.evmChain, input.asset);
743
1000
  assertDelivery(input.delivery);
1001
+ assertAssetMode(input.asset, input.delivery);
744
1002
  assertBaseToSubtensorAmount(input.amountWei, input.delivery);
745
1003
  const destination = normalizeSS58(input.destination);
746
1004
  const evm = getForeverMoneyEvmDeployment(input.evmChain);
@@ -750,29 +1008,42 @@ async function prepareEvmToSubtensor(provider, input) {
750
1008
  wantLiquid: input.delivery === "liquid",
751
1009
  minTaoOut: input.amountWei
752
1010
  };
753
- const token = new import_ethers6.Contract(evm.contracts.wrappedTao, ERC20_ABI, provider);
754
- const gateway = new import_ethers6.Contract(
755
- evm.contracts.gateway,
756
- SPOKE_GATEWAY_ABI,
757
- provider
1011
+ const partnerFee = resolvePartnerFee(
1012
+ input.partnerFee,
1013
+ evm.contracts.gateway
758
1014
  );
1015
+ const cut = partnerFeeCut(input.amountWei, partnerFee.bps);
759
1016
  const [allowanceWei, exactNetworkFeeWei] = await Promise.all([
760
- token.getFunction("allowance")(
761
- sender,
762
- evm.contracts.gateway
763
- ),
764
- gateway.getFunction(
765
- "quoteBridgeToFinney(address,uint256,(bytes32,address,bool,uint256))"
766
- )(evm.contracts.wrappedTao, input.amountWei, exit)
1017
+ provider.readContract({
1018
+ address: asset.evmToken,
1019
+ abi: erc20Abi,
1020
+ functionName: "allowance",
1021
+ args: [sender, evm.contracts.gateway]
1022
+ }),
1023
+ partnerFee.bps === 0 ? provider.readContract({
1024
+ address: evm.contracts.gateway,
1025
+ abi: spokeAbi,
1026
+ functionName: "quoteBridgeToFinney",
1027
+ args: [asset.evmToken, input.amountWei, exit]
1028
+ }) : quoteSpokeWithFee(
1029
+ provider,
1030
+ evm.contracts.gateway,
1031
+ asset.evmToken,
1032
+ input.amountWei,
1033
+ exit,
1034
+ partnerFee
1035
+ )
767
1036
  ]);
768
1037
  let estimatedBridgeGas;
769
- if (allowanceWei >= input.amountWei) {
770
- const data = spokeInterface.encodeFunctionData(
771
- "bridgeToFinney(address,uint256,(bytes32,address,bool,uint256))",
772
- [evm.contracts.wrappedTao, input.amountWei, exit]
1038
+ if (allowanceWei >= input.amountWei + cut) {
1039
+ const data = encodeSpokeBridge(
1040
+ asset.evmToken,
1041
+ input.amountWei,
1042
+ exit,
1043
+ partnerFee
773
1044
  );
774
1045
  estimatedBridgeGas = await provider.estimateGas({
775
- from: sender,
1046
+ account: sender,
776
1047
  to: evm.contracts.gateway,
777
1048
  data,
778
1049
  value: feeWithBuffer(exactNetworkFeeWei)
@@ -787,6 +1058,7 @@ async function prepareEvmToSubtensor(provider, input) {
787
1058
  return Object.freeze({
788
1059
  exactNetworkFeeWei,
789
1060
  transactionValueWei: feeWithBuffer(exactNetworkFeeWei),
1061
+ partnerFeeWei: cut,
790
1062
  plan
791
1063
  });
792
1064
  }
@@ -796,58 +1068,67 @@ function prepareBaseToSubtensor(provider, input) {
796
1068
  async function prepareSubtensorToEvm(provider, input) {
797
1069
  const sender = normalizeEvmAddress(input.sender);
798
1070
  const recipient = normalizeEvmAddress(input.recipient);
1071
+ const asset = bridgeAsset(input.evmChain, input.asset);
799
1072
  assertSource(input.source);
800
- assertWholeRao(input.amountWei);
1073
+ assertAssetMode(input.asset, input.source);
1074
+ const netuid = sourceNetuid(input);
1075
+ assertSubtensorToEvmAmount(input.amountWei, input.source);
801
1076
  const { subtensor } = foreverMoneyDeployment;
802
1077
  const evm = getForeverMoneyEvmDeployment(input.evmChain);
803
- const gateway = new import_ethers6.Contract(
804
- subtensor.contracts.gateway,
805
- ALPHA_GATEWAY_ABI,
806
- provider
1078
+ const partnerFee = resolvePartnerFee(
1079
+ input.partnerFee,
1080
+ subtensor.contracts.gateway
807
1081
  );
808
- const exactNetworkFeeWei = await gateway.getFunction("quoteBridgeOut")(
1082
+ const taoAmount = input.source === "liquid" ? input.amountWei : 0n;
1083
+ const stakedAlphaRao = input.source === "staked" ? input.amountWei / EVM_WEI_PER_RAO : 0n;
1084
+ const exactNetworkFeeWei = partnerFee.bps === 0 ? await provider.readContract({
1085
+ address: subtensor.contracts.gateway,
1086
+ abi: alphaAbi,
1087
+ functionName: "quoteBridgeOut",
1088
+ args: [
1089
+ evm.ccipSelector,
1090
+ asset.subtensorToken,
1091
+ recipient,
1092
+ input.amountWei
1093
+ ]
1094
+ }) : (await quoteHubWithFee(
1095
+ provider,
1096
+ subtensor.contracts.gateway,
809
1097
  evm.ccipSelector,
810
- subtensor.contracts.wrappedTao,
1098
+ asset.subtensorToken,
811
1099
  recipient,
812
- input.amountWei
813
- );
1100
+ input.amountWei,
1101
+ taoAmount,
1102
+ stakedAlphaRao,
1103
+ partnerFee
1104
+ )).networkFee;
814
1105
  let stakingAllowanceRao;
815
1106
  if (input.source === "staked") {
816
- if (input.netuid === void 0) {
817
- throw new ForeverMoneyError(
818
- "INVALID_TRANSACTION_PLAN",
819
- "A netuid is required when bridging staked TAO."
820
- );
821
- }
822
- assertNonNegativeAmount(input.netuid, "netuid");
823
- const staking = new import_ethers6.Contract(
824
- subtensor.contracts.stakingPrecompile,
825
- STAKING_ABI,
826
- provider
827
- );
828
- stakingAllowanceRao = await staking.getFunction("allowance")(
829
- sender,
830
- subtensor.contracts.gateway,
831
- input.netuid
832
- );
1107
+ stakingAllowanceRao = await provider.readContract({
1108
+ address: subtensor.contracts.stakingPrecompile,
1109
+ abi: stakingAbi,
1110
+ functionName: "allowance",
1111
+ args: [sender, subtensor.contracts.gateway, netuid]
1112
+ });
833
1113
  }
834
- const taoAmount = input.source === "liquid" ? input.amountWei : 0n;
835
- const stakedAlphaRao = input.source === "staked" ? input.amountWei / EVM_WEI_PER_RAO : 0n;
836
- const value = taoAmount + feeWithBuffer(exactNetworkFeeWei);
1114
+ const alphaTopUpRao = partnerFeeCut(stakedAlphaRao, partnerFee.bps);
1115
+ const taoTopUp = partnerFeeTaoTopUp(taoAmount, partnerFee.bps);
1116
+ const value = taoAmount + taoTopUp + feeWithBuffer(exactNetworkFeeWei);
837
1117
  assertNonNegativeAmount(value, "Transaction value");
838
1118
  let estimatedBridgeGas;
839
- if (input.source === "liquid" || stakingAllowanceRao !== void 0 && stakingAllowanceRao >= stakedAlphaRao) {
1119
+ if (input.source === "liquid" || stakingAllowanceRao !== void 0 && stakingAllowanceRao >= stakedAlphaRao + alphaTopUpRao) {
840
1120
  estimatedBridgeGas = await provider.estimateGas({
841
- from: sender,
1121
+ account: sender,
842
1122
  to: subtensor.contracts.gateway,
843
- data: alphaInterface.encodeFunctionData("bridgeOut", [
1123
+ data: encodeHubBridge(
844
1124
  evm.ccipSelector,
845
- subtensor.contracts.wrappedTao,
1125
+ asset.subtensorToken,
846
1126
  recipient,
847
1127
  taoAmount,
848
1128
  stakedAlphaRao,
849
- input.amountWei
850
- ]),
1129
+ input.amountWei,
1130
+ partnerFee
1131
+ ),
851
1132
  value
852
1133
  });
853
1134
  }
@@ -860,6 +1141,7 @@ async function prepareSubtensorToEvm(provider, input) {
860
1141
  return Object.freeze({
861
1142
  exactNetworkFeeWei,
862
1143
  transactionValueWei: value,
1144
+ partnerFeeWei: input.source === "liquid" ? taoTopUp : alphaTopUpRao * EVM_WEI_PER_RAO,
863
1145
  plan
864
1146
  });
865
1147
  }
@@ -868,7 +1150,7 @@ function prepareSubtensorToBase(provider, input) {
868
1150
  }
869
1151
 
870
1152
  // src/core/transport.ts
871
- var import_ethers7 = require("ethers");
1153
+ var import_viem7 = require("viem");
872
1154
  function isRecord(value) {
873
1155
  return typeof value === "object" && value !== null && !Array.isArray(value);
874
1156
  }
@@ -987,10 +1269,38 @@ function providerFromTransport(transport) {
987
1269
  "An EIP-1193-compatible RPC transport is required."
988
1270
  );
989
1271
  }
990
- return new import_ethers7.BrowserProvider(transport);
1272
+ return (0, import_viem7.createPublicClient)({
1273
+ transport: (0, import_viem7.custom)(transport, { retryCount: 0 }),
1274
+ cacheTime: 0,
1275
+ batch: { multicall: false }
1276
+ });
1277
+ }
1278
+ function trackingClient(provider) {
1279
+ if (provider && "getChainId" in provider && typeof provider.getChainId === "function")
1280
+ return provider;
1281
+ if (provider && "send" in provider && typeof provider.send === "function") {
1282
+ return providerFromTransport({
1283
+ request: ({ method, params }) => {
1284
+ if (params !== void 0 && !Array.isArray(params))
1285
+ throw new ForeverMoneyError(
1286
+ "RPC_ERROR",
1287
+ "Tracking RPC params must be an array."
1288
+ );
1289
+ return provider.send(
1290
+ method,
1291
+ params === void 0 ? [] : [...params]
1292
+ );
1293
+ }
1294
+ });
1295
+ }
1296
+ throw new ForeverMoneyError(
1297
+ "MISSING_TRANSPORT",
1298
+ "A viem public client or JSON-RPC provider is required."
1299
+ );
991
1300
  }
992
1301
 
993
1302
  // src/core/provider-errors.ts
1303
+ var import_viem8 = require("viem");
994
1304
  function stringProperty(value, property) {
995
1305
  if (typeof value !== "object" || value === null || !(property in value)) {
996
1306
  return void 0;
@@ -1003,9 +1313,13 @@ async function providerOperation(operation, action) {
1003
1313
  return await action();
1004
1314
  } catch (error) {
1005
1315
  if (error instanceof ForeverMoneyError) throw error;
1006
- const causeCode = stringProperty(error, "code");
1007
- const reason = stringProperty(error, "reason");
1008
- const reverted = causeCode === "CALL_EXCEPTION";
1316
+ const revert = error instanceof import_viem8.BaseError ? error.walk(
1317
+ (cause) => cause instanceof import_viem8.ContractFunctionRevertedError || cause instanceof import_viem8.ExecutionRevertedError
1318
+ ) : void 0;
1319
+ const viemRevert = revert instanceof import_viem8.ContractFunctionRevertedError || revert instanceof import_viem8.ExecutionRevertedError;
1320
+ const causeCode = viemRevert ? revert.name : stringProperty(error, "code");
1321
+ const reason = viemRevert ? revert instanceof import_viem8.ContractFunctionRevertedError ? revert.data?.errorName ?? revert.reason : void 0 : stringProperty(error, "reason");
1322
+ const reverted = viemRevert || causeCode === "CALL_EXCEPTION";
1009
1323
  throw new ForeverMoneyError(
1010
1324
  reverted ? "SIMULATION_REVERTED" : "RPC_ERROR",
1011
1325
  reverted ? `${operation} reverted.` : `${operation} failed.`,
@@ -1018,10 +1332,10 @@ async function providerOperation(operation, action) {
1018
1332
  }
1019
1333
 
1020
1334
  // src/vaults/plans.ts
1021
- var import_ethers8 = require("ethers");
1022
- var erc20Interface2 = new import_ethers8.Interface(ERC20_ABI);
1023
- var factoryInterface = new import_ethers8.Interface(VAULT_FACTORY_ABI);
1024
- var managerInterface = new import_ethers8.Interface(VAULT_MANAGER_ABI);
1335
+ var import_viem9 = require("viem");
1336
+ var erc20Abi2 = (0, import_viem9.parseAbi)(ERC20_ABI);
1337
+ var factoryAbi = (0, import_viem9.parseAbi)(VAULT_FACTORY_ABI);
1338
+ var managerAbi = (0, import_viem9.parseAbi)(VAULT_MANAGER_ABI);
1025
1339
  function step(kind, label, from, to, data, value = 0n) {
1026
1340
  return {
1027
1341
  kind,
@@ -1138,10 +1452,11 @@ function buildCreateVaultPlan(input) {
1138
1452
  `Approve ${stash.token} for the vault factory`,
1139
1453
  owner,
1140
1454
  stash.token,
1141
- erc20Interface2.encodeFunctionData("approve", [
1142
- base.contracts.vaultFactory,
1143
- stash.amount
1144
- ])
1455
+ (0, import_viem9.encodeFunctionData)({
1456
+ abi: erc20Abi2,
1457
+ functionName: "approve",
1458
+ args: [base.contracts.vaultFactory, stash.amount]
1459
+ })
1145
1460
  )
1146
1461
  );
1147
1462
  }
@@ -1152,15 +1467,19 @@ function buildCreateVaultPlan(input) {
1152
1467
  "Create ForeverMoney vault",
1153
1468
  owner,
1154
1469
  base.contracts.vaultFactory,
1155
- factoryInterface.encodeFunctionData("create", [
1156
- owner,
1157
- associatedMiner,
1158
- akAddress,
1159
- poolManager,
1160
- poolAddress,
1161
- positionManagerImplementation,
1162
- stashTokens
1163
- ]),
1470
+ (0, import_viem9.encodeFunctionData)({
1471
+ abi: factoryAbi,
1472
+ functionName: "create",
1473
+ args: [
1474
+ owner,
1475
+ associatedMiner,
1476
+ akAddress,
1477
+ poolManager,
1478
+ poolAddress,
1479
+ positionManagerImplementation,
1480
+ stashTokens
1481
+ ]
1482
+ }),
1164
1483
  value
1165
1484
  )
1166
1485
  );
@@ -1179,14 +1498,15 @@ async function prepareCreateVault(provider, input) {
1179
1498
  const allowances = await Promise.all(
1180
1499
  erc20Stash.map(async ({ token }) => ({
1181
1500
  token,
1182
- allowance: await new import_ethers8.Contract(
1183
- token,
1184
- ERC20_ABI,
1185
- provider
1186
- ).getFunction("allowance")(
1187
- owner,
1188
- foreverMoneyDeployment.base.contracts.vaultFactory
1189
- )
1501
+ allowance: await provider.readContract({
1502
+ address: token,
1503
+ abi: erc20Abi2,
1504
+ functionName: "allowance",
1505
+ args: [
1506
+ owner,
1507
+ foreverMoneyDeployment.base.contracts.vaultFactory
1508
+ ]
1509
+ })
1190
1510
  }))
1191
1511
  );
1192
1512
  return buildCreateVaultPlan({ ...input, allowances });
@@ -1213,11 +1533,11 @@ function buildDepositVaultPlan(input) {
1213
1533
  "Deposit native ETH into the vault as WETH",
1214
1534
  owner,
1215
1535
  manager,
1216
- managerInterface.encodeFunctionData("topUpAk", [
1217
- akAddress,
1218
- import_ethers8.ZeroAddress,
1219
- deposit.amount
1220
- ]),
1536
+ (0, import_viem9.encodeFunctionData)({
1537
+ abi: managerAbi,
1538
+ functionName: "topUpAk",
1539
+ args: [akAddress, import_viem9.zeroAddress, deposit.amount]
1540
+ }),
1221
1541
  deposit.amount
1222
1542
  )
1223
1543
  );
@@ -1237,10 +1557,11 @@ function buildDepositVaultPlan(input) {
1237
1557
  `Approve ${deposit.token} for the vault manager`,
1238
1558
  owner,
1239
1559
  deposit.token,
1240
- erc20Interface2.encodeFunctionData("approve", [
1241
- manager,
1242
- deposit.amount
1243
- ])
1560
+ (0, import_viem9.encodeFunctionData)({
1561
+ abi: erc20Abi2,
1562
+ functionName: "approve",
1563
+ args: [manager, deposit.amount]
1564
+ })
1244
1565
  )
1245
1566
  );
1246
1567
  }
@@ -1250,11 +1571,11 @@ function buildDepositVaultPlan(input) {
1250
1571
  `Deposit ${deposit.token} into the vault`,
1251
1572
  owner,
1252
1573
  manager,
1253
- managerInterface.encodeFunctionData("topUpAk", [
1254
- akAddress,
1255
- deposit.token,
1256
- deposit.amount
1257
- ])
1574
+ (0, import_viem9.encodeFunctionData)({
1575
+ abi: managerAbi,
1576
+ functionName: "topUpAk",
1577
+ args: [akAddress, deposit.token, deposit.amount]
1578
+ })
1258
1579
  )
1259
1580
  );
1260
1581
  }
@@ -1274,11 +1595,12 @@ async function prepareDepositVault(provider, input) {
1274
1595
  const allowances = await Promise.all(
1275
1596
  erc20Deposits.map(async ({ token }) => ({
1276
1597
  token,
1277
- allowance: await new import_ethers8.Contract(
1278
- token,
1279
- ERC20_ABI,
1280
- provider
1281
- ).getFunction("allowance")(owner, manager)
1598
+ allowance: await provider.readContract({
1599
+ address: token,
1600
+ abi: erc20Abi2,
1601
+ functionName: "allowance",
1602
+ args: [owner, manager]
1603
+ })
1282
1604
  }))
1283
1605
  );
1284
1606
  return buildDepositVaultPlan({ ...input, allowances });
@@ -1309,16 +1631,17 @@ function buildWithdrawVaultPlan(input) {
1309
1631
  "Withdraw assets from the vault",
1310
1632
  owner,
1311
1633
  manager,
1312
- managerInterface.encodeFunctionData(
1313
- "withdrawFromAkAndPositions",
1314
- [
1634
+ (0, import_viem9.encodeFunctionData)({
1635
+ abi: managerAbi,
1636
+ functionName: "withdrawFromAkAndPositions",
1637
+ args: [
1315
1638
  akAddress,
1316
1639
  input.amount0,
1317
1640
  input.amount1,
1318
1641
  input.decreaseTokenIds,
1319
1642
  input.unwrapWeth
1320
1643
  ]
1321
- )
1644
+ })
1322
1645
  )
1323
1646
  ]
1324
1647
  });
@@ -1336,9 +1659,11 @@ function buildClaimVaultFeesPlan(input) {
1336
1659
  "Claim vault fees",
1337
1660
  owner,
1338
1661
  manager,
1339
- managerInterface.encodeFunctionData("claimFees(address)", [
1340
- akAddress
1341
- ])
1662
+ (0, import_viem9.encodeFunctionData)({
1663
+ abi: managerAbi,
1664
+ functionName: "claimFees",
1665
+ args: [akAddress]
1666
+ })
1342
1667
  )
1343
1668
  ]
1344
1669
  });
@@ -1358,20 +1683,21 @@ function buildSetVaultStakingPlan(input) {
1358
1683
  `${input.staking ? "Stake" : "Unstake"} the vault position`,
1359
1684
  owner,
1360
1685
  manager,
1361
- managerInterface.encodeFunctionData(
1362
- `${action}(address,bytes)`,
1363
- [akAddress, "0x"]
1364
- )
1686
+ (0, import_viem9.encodeFunctionData)({
1687
+ abi: managerAbi,
1688
+ functionName: action,
1689
+ args: [akAddress, "0x"]
1690
+ })
1365
1691
  )
1366
1692
  ]
1367
1693
  });
1368
1694
  }
1369
1695
 
1370
1696
  // src/bridge/tracking.ts
1371
- var import_ethers10 = require("ethers");
1697
+ var import_viem11 = require("viem");
1372
1698
 
1373
1699
  // src/bridge/receipts.ts
1374
- var import_ethers9 = require("ethers");
1700
+ var import_viem10 = require("viem");
1375
1701
 
1376
1702
  // src/core/receipts.ts
1377
1703
  function isLogFrom(log, address) {
@@ -1379,8 +1705,8 @@ function isLogFrom(log, address) {
1379
1705
  }
1380
1706
 
1381
1707
  // src/bridge/receipts.ts
1382
- var alphaGatewayInterface = new import_ethers9.Interface(ALPHA_GATEWAY_ABI);
1383
- var spokeGatewayInterface = new import_ethers9.Interface(SPOKE_GATEWAY_ABI);
1708
+ var alphaGatewayAbi = (0, import_viem10.parseAbi)(ALPHA_GATEWAY_ABI);
1709
+ var spokeGatewayAbi = (0, import_viem10.parseAbi)(SPOKE_GATEWAY_ABI);
1384
1710
  function assertBridgeDirection(value) {
1385
1711
  if (value !== "base-to-subtensor" && value !== "robinhood-to-subtensor" && value !== "subtensor-to-base" && value !== "subtensor-to-robinhood") {
1386
1712
  throw new ForeverMoneyError(
@@ -1403,20 +1729,28 @@ function bridgeMessageIdFromReceipt(direction, receipt) {
1403
1729
  evmChainFromBridgeDirection(direction)
1404
1730
  );
1405
1731
  const evmToSubtensor = isEvmToSubtensorDirection(direction);
1406
- const [address, contractInterface, eventName] = evmToSubtensor ? [evm.contracts.gateway, spokeGatewayInterface, "BridgedToFinney"] : [
1407
- foreverMoneyDeployment.subtensor.contracts.gateway,
1408
- alphaGatewayInterface,
1732
+ const [addresses, contractAbi, eventName] = evmToSubtensor ? [
1733
+ [evm.contracts.legacyGateway, evm.contracts.gateway],
1734
+ spokeGatewayAbi,
1735
+ "BridgedToFinney"
1736
+ ] : [
1737
+ [
1738
+ foreverMoneyDeployment.subtensor.contracts.legacyGateway,
1739
+ foreverMoneyDeployment.subtensor.contracts.gateway
1740
+ ],
1741
+ alphaGatewayAbi,
1409
1742
  "BridgedOut"
1410
1743
  ];
1411
1744
  for (const log of receipt.logs) {
1412
- if (!isLogFrom(log, address)) continue;
1745
+ if (!addresses.some((address) => isLogFrom(log, address))) continue;
1413
1746
  try {
1414
- const parsed = contractInterface.parseLog({
1747
+ const parsed = (0, import_viem10.decodeEventLog)({
1748
+ abi: contractAbi,
1415
1749
  data: log.data,
1416
1750
  topics: [...log.topics]
1417
1751
  });
1418
- const messageId = parsed?.args.messageId;
1419
- if (parsed?.name === eventName && typeof messageId === "string" && (0, import_ethers9.isHexString)(messageId, 32) && (evmToSubtensor || parsed.args.destChainSelector === evm.ccipSelector)) {
1752
+ const messageId = "messageId" in parsed.args ? parsed.args.messageId : void 0;
1753
+ if (parsed.eventName === eventName && typeof messageId === "string" && (0, import_viem10.isHex)(messageId, { strict: true }) && messageId.length === 66 && (evmToSubtensor || "destChainSelector" in parsed.args && parsed.args.destChainSelector === evm.ccipSelector)) {
1420
1754
  return messageId.toLowerCase();
1421
1755
  }
1422
1756
  } catch {
@@ -1426,8 +1760,8 @@ function bridgeMessageIdFromReceipt(direction, receipt) {
1426
1760
  }
1427
1761
 
1428
1762
  // src/bridge/tracking.ts
1429
- var ccipExecutionInterface = new import_ethers10.Interface(CCIP_EXECUTION_ABI);
1430
- var alphaGatewayInterface2 = new import_ethers10.Interface(ALPHA_GATEWAY_ABI);
1763
+ var ccipExecutionAbi = (0, import_viem11.parseAbi)(CCIP_EXECUTION_ABI);
1764
+ var alphaGatewayAbi2 = (0, import_viem11.parseAbi)(ALPHA_GATEWAY_ABI);
1431
1765
  function destinationChainId(direction) {
1432
1766
  assertBridgeDirection(direction);
1433
1767
  const evm = getForeverMoneyEvmDeployment(
@@ -1443,14 +1777,14 @@ function sourceChainId(direction) {
1443
1777
  return isEvmToSubtensorDirection(direction) ? evm.chainId : foreverMoneyDeployment.subtensor.chainId;
1444
1778
  }
1445
1779
  async function assertProviderChain(provider, expectedChainId, label) {
1446
- const network = await provider.getNetwork();
1447
- if (network.chainId !== BigInt(expectedChainId)) {
1780
+ const chainId = await provider.getChainId();
1781
+ if (chainId !== expectedChainId) {
1448
1782
  throw new ForeverMoneyError(
1449
1783
  "CHAIN_MISMATCH",
1450
- `${label} transport reported chain ID ${network.chainId}; expected ${expectedChainId}.`,
1784
+ `${label} transport reported chain ID ${chainId}; expected ${expectedChainId}.`,
1451
1785
  {
1452
1786
  expectedChainId,
1453
- actualChainId: network.chainId.toString()
1787
+ actualChainId: chainId.toString()
1454
1788
  }
1455
1789
  );
1456
1790
  }
@@ -1461,13 +1795,14 @@ async function assertDestinationProvider(provider, direction) {
1461
1795
  return expectedChainId;
1462
1796
  }
1463
1797
  async function getBridgeSourceStatus(provider, input) {
1798
+ const client = trackingClient(provider);
1464
1799
  const expectedChainId = sourceChainId(input.direction);
1465
1800
  const transactionHash = normalizeBytes32(
1466
1801
  input.transactionHash,
1467
1802
  "Transaction hash"
1468
1803
  );
1469
- await assertProviderChain(provider, expectedChainId, "Bridge source");
1470
- const receipt = await provider.getTransactionReceipt(transactionHash);
1804
+ await assertProviderChain(client, expectedChainId, "Bridge source");
1805
+ const receipt = await receiptOrNull(client, transactionHash);
1471
1806
  if (receipt === null) {
1472
1807
  return Object.freeze({
1473
1808
  direction: input.direction,
@@ -1477,7 +1812,7 @@ async function getBridgeSourceStatus(provider, input) {
1477
1812
  messageId: null
1478
1813
  });
1479
1814
  }
1480
- if (receipt.status === 0) {
1815
+ if (receipt.status === "reverted") {
1481
1816
  return Object.freeze({
1482
1817
  direction: input.direction,
1483
1818
  sourceChainId: expectedChainId,
@@ -1486,7 +1821,7 @@ async function getBridgeSourceStatus(provider, input) {
1486
1821
  messageId: null
1487
1822
  });
1488
1823
  }
1489
- if (receipt.status !== 1) {
1824
+ if (receipt.status !== "success") {
1490
1825
  throw new ForeverMoneyError(
1491
1826
  "INVALID_PROVIDER_RESPONSE",
1492
1827
  "The bridge source receipt has an invalid status."
@@ -1508,8 +1843,10 @@ async function getBridgeSourceStatus(provider, input) {
1508
1843
  });
1509
1844
  }
1510
1845
  async function getCcipDeliveryCheckpoint(provider, direction) {
1511
- const expectedChainId = await assertDestinationProvider(provider, direction);
1512
- const fromBlock = await provider.getBlockNumber();
1846
+ const client = trackingClient(provider);
1847
+ const expectedChainId = await assertDestinationProvider(client, direction);
1848
+ const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
1849
+ const fromBlock = Number(blockNumber);
1513
1850
  if (!Number.isSafeInteger(fromBlock) || fromBlock < 0) {
1514
1851
  throw new ForeverMoneyError(
1515
1852
  "INVALID_PROVIDER_RESPONSE",
@@ -1523,6 +1860,7 @@ async function getCcipDeliveryCheckpoint(provider, direction) {
1523
1860
  });
1524
1861
  }
1525
1862
  async function getCcipDeliveryStatus(provider, input) {
1863
+ const client = trackingClient(provider);
1526
1864
  const expectedChainId = destinationChainId(input.direction);
1527
1865
  const messageId = normalizeBytes32(input.messageId, "CCIP message ID");
1528
1866
  if (!Number.isSafeInteger(input.fromBlock) || input.fromBlock < 0) {
@@ -1531,42 +1869,50 @@ async function getCcipDeliveryStatus(provider, input) {
1531
1869
  "CCIP fromBlock must be a non-negative safe integer."
1532
1870
  );
1533
1871
  }
1534
- await assertProviderChain(provider, expectedChainId, "Bridge destination");
1535
- const event = ccipExecutionInterface.getEvent("ExecutionStateChanged");
1536
- if (event === null) {
1537
- throw new Error("CCIP execution event is missing from the SDK ABI.");
1538
- }
1872
+ await assertProviderChain(client, expectedChainId, "Bridge destination");
1539
1873
  const evmChain = evmChainFromBridgeDirection(input.direction);
1540
1874
  const evm = getForeverMoneyEvmDeployment(evmChain);
1541
1875
  const evmToSubtensor = isEvmToSubtensorDirection(input.direction);
1542
1876
  const sourceSelector = evmToSubtensor ? evm.ccipSelector : foreverMoneyDeployment.subtensor.ccipSelector;
1543
1877
  const offRamp = evmToSubtensor ? evmChain === "base" ? foreverMoneyDeployment.subtensor.contracts.ccipOffRampFromBase : foreverMoneyDeployment.subtensor.contracts.ccipOffRampFromRobinhood : evm.contracts.ccipOffRampFromSubtensor;
1544
- const logs = await provider.getLogs({
1878
+ const logs = await client.getLogs({
1545
1879
  address: offRamp,
1546
- fromBlock: input.fromBlock,
1880
+ fromBlock: BigInt(input.fromBlock),
1547
1881
  toBlock: "latest",
1548
- topics: [event.topicHash, (0, import_ethers10.toBeHex)(sourceSelector, 32), null, messageId]
1882
+ event: ccipExecutionAbi[0],
1883
+ args: { sourceChainSelector: sourceSelector, messageId },
1884
+ strict: true
1549
1885
  });
1550
1886
  const latest = logs.at(-1);
1551
1887
  if (latest === void 0) return "waiting";
1552
- const parsed = ccipExecutionInterface.parseLog(latest);
1553
- const state = Number(parsed?.args.state);
1888
+ const state = latest.args.state;
1554
1889
  if (state === 3) return "failure";
1555
1890
  if (state !== 2) return "waiting";
1556
1891
  if (!evmToSubtensor) return "success";
1557
- const receipt = await provider.getTransactionReceipt(latest.transactionHash);
1892
+ const receipt = await receiptOrNull(client, latest.transactionHash);
1558
1893
  if (receipt === null) {
1559
1894
  throw new ForeverMoneyError(
1560
1895
  "INVALID_PROVIDER_RESPONSE",
1561
1896
  "The CCIP execution receipt is unavailable."
1562
1897
  );
1563
1898
  }
1899
+ const subtensorGateways = [
1900
+ foreverMoneyDeployment.subtensor.contracts.legacyGateway,
1901
+ foreverMoneyDeployment.subtensor.contracts.gateway
1902
+ ];
1564
1903
  for (const log of receipt.logs) {
1565
- if (log.address.toLowerCase() !== foreverMoneyDeployment.subtensor.contracts.gateway.toLowerCase()) {
1904
+ if (!subtensorGateways.some(
1905
+ (gateway) => log.address.toLowerCase() === gateway.toLowerCase()
1906
+ )) {
1566
1907
  continue;
1567
1908
  }
1568
1909
  try {
1569
- if (alphaGatewayInterface2.parseLog(log)?.name === "Claimable") {
1910
+ const { eventName } = (0, import_viem11.decodeEventLog)({
1911
+ abi: alphaGatewayAbi2,
1912
+ data: log.data,
1913
+ topics: log.topics
1914
+ });
1915
+ if (eventName === "Claimable" || eventName === "NotDelivered") {
1570
1916
  return "recovery";
1571
1917
  }
1572
1918
  } catch {
@@ -1574,20 +1920,28 @@ async function getCcipDeliveryStatus(provider, input) {
1574
1920
  }
1575
1921
  return "success";
1576
1922
  }
1923
+ async function receiptOrNull(provider, hash) {
1924
+ try {
1925
+ return await provider.getTransactionReceipt({ hash });
1926
+ } catch (error) {
1927
+ if (error instanceof import_viem11.TransactionReceiptNotFoundError) return null;
1928
+ throw error;
1929
+ }
1930
+ }
1577
1931
 
1578
1932
  // src/client.ts
1579
1933
  async function assertProviderChain2(provider, expectedChainId, name) {
1580
- const network = await providerOperation(
1934
+ const chainId = await providerOperation(
1581
1935
  `Reading the ${name} chain ID`,
1582
- () => provider.getNetwork()
1936
+ () => provider.getChainId()
1583
1937
  );
1584
- if (network.chainId !== BigInt(expectedChainId)) {
1938
+ if (chainId !== expectedChainId) {
1585
1939
  throw new ForeverMoneyError(
1586
1940
  "CHAIN_MISMATCH",
1587
- `${name} transport reported chain ID ${network.chainId}; expected ${expectedChainId}.`,
1941
+ `${name} transport reported chain ID ${chainId}; expected ${expectedChainId}.`,
1588
1942
  {
1589
1943
  expectedChainId,
1590
- actualChainId: network.chainId.toString()
1944
+ actualChainId: chainId.toString()
1591
1945
  }
1592
1946
  );
1593
1947
  }
@@ -1719,19 +2073,20 @@ function createForeverMoneyClient(options) {
1719
2073
  }
1720
2074
 
1721
2075
  // src/vaults/receipts.ts
1722
- var import_ethers11 = require("ethers");
1723
- var vaultFactoryInterface = new import_ethers11.Interface(VAULT_FACTORY_ABI);
2076
+ var import_viem12 = require("viem");
2077
+ var vaultFactoryAbi = (0, import_viem12.parseAbi)(VAULT_FACTORY_ABI);
1724
2078
  function vaultManagerFromCreationReceipt(receipt) {
1725
2079
  for (const log of receipt.logs) {
1726
2080
  if (!isLogFrom(log, foreverMoneyDeployment.base.contracts.vaultFactory)) {
1727
2081
  continue;
1728
2082
  }
1729
2083
  try {
1730
- const parsed = vaultFactoryInterface.parseLog({
2084
+ const parsed = (0, import_viem12.decodeEventLog)({
2085
+ abi: vaultFactoryAbi,
1731
2086
  data: log.data,
1732
2087
  topics: [...log.topics]
1733
2088
  });
1734
- if (parsed?.name === "SnLiquidityManagerCreated") {
2089
+ if (parsed.eventName === "SnLiquidityManagerCreated") {
1735
2090
  return normalizeEvmAddress(String(parsed.args.manager));
1736
2091
  }
1737
2092
  } catch {
@@ -1741,7 +2096,7 @@ function vaultManagerFromCreationReceipt(receipt) {
1741
2096
  }
1742
2097
 
1743
2098
  // src/core/transactions.ts
1744
- var import_ethers12 = require("ethers");
2099
+ var import_viem13 = require("viem");
1745
2100
  function decimalQuantity(value, label) {
1746
2101
  if (!/^(0|[1-9][0-9]*)$/.test(value)) {
1747
2102
  throw new ForeverMoneyError(
@@ -1750,7 +2105,7 @@ function decimalQuantity(value, label) {
1750
2105
  );
1751
2106
  }
1752
2107
  const quantity = BigInt(value);
1753
- if (quantity > import_ethers12.MaxUint256) {
2108
+ if (quantity > import_viem13.maxUint256) {
1754
2109
  throw new ForeverMoneyError(
1755
2110
  "INVALID_TRANSACTION_PLAN",
1756
2111
  `${label} exceeds uint256.`
@@ -1786,4 +2141,50 @@ function toEthersTransaction(transaction) {
1786
2141
  }
1787
2142
  });
1788
2143
  }
2144
+ var transactionChains = new Map(
2145
+ [
2146
+ [BASE_CHAIN_ID, "Base", "Ether", "ETH"],
2147
+ [ROBINHOOD_CHAIN_ID, "Robinhood", "Ether", "ETH"],
2148
+ [SUBTENSOR_CHAIN_ID, "Subtensor", "TAO", "TAO"]
2149
+ ].map(([id, name, currency, symbol]) => [
2150
+ id,
2151
+ Object.freeze(
2152
+ (0, import_viem13.defineChain)({
2153
+ id,
2154
+ name,
2155
+ nativeCurrency: Object.freeze({
2156
+ name: currency,
2157
+ symbol,
2158
+ decimals: 18
2159
+ }),
2160
+ // Chain identity only. The caller retains ownership of the wallet transport.
2161
+ rpcUrls: Object.freeze({
2162
+ default: Object.freeze({ http: Object.freeze([]) })
2163
+ })
2164
+ })
2165
+ )
2166
+ ])
2167
+ );
2168
+ function toViemTransaction(transaction) {
2169
+ const chain = transactionChains.get(transaction.chainId);
2170
+ if (!chain)
2171
+ throw new ForeverMoneyError(
2172
+ "INVALID_TRANSACTION_PLAN",
2173
+ "Unsupported transaction chain."
2174
+ );
2175
+ return Object.freeze({
2176
+ chain,
2177
+ account: transaction.from,
2178
+ chainId: transaction.chainId,
2179
+ to: transaction.to,
2180
+ data: transaction.data,
2181
+ value: decimalQuantity(transaction.value, "Transaction value"),
2182
+ ...transaction.gasLimit === void 0 ? {} : {
2183
+ gas: decimalQuantity(
2184
+ transaction.gasLimit,
2185
+ "Transaction gas limit"
2186
+ )
2187
+ }
2188
+ });
2189
+ }
1789
2190
  //# sourceMappingURL=index.cjs.map