@forevermoney/sdk 0.1.0 → 0.2.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 CHANGED
@@ -1,11 +1,18 @@
1
1
  // src/core/addresses.ts
2
- import { isHex, u8aToHex } from "@polkadot/util";
3
2
  import {
4
- decodeAddress,
5
- encodeAddress,
6
- evmToAddress
7
- } from "@polkadot/util-crypto";
8
- import { ZeroAddress, getAddress, isAddress } from "ethers";
3
+ AccountId,
4
+ Blake2256,
5
+ getSs58AddressInfo
6
+ } from "@polkadot-api/substrate-bindings";
7
+ import {
8
+ bytesToHex,
9
+ concat,
10
+ getAddress,
11
+ hexToBytes,
12
+ isAddress,
13
+ stringToBytes,
14
+ zeroAddress
15
+ } from "viem";
9
16
 
10
17
  // src/core/errors.ts
11
18
  var ForeverMoneyError = class extends Error {
@@ -22,22 +29,33 @@ var ForeverMoneyError = class extends Error {
22
29
  // src/core/addresses.ts
23
30
  var BITTENSOR_SS58_PREFIX = 42;
24
31
  function normalizeEvmAddress(value) {
25
- if (!isAddress(value) || value === ZeroAddress) {
32
+ const candidate = typeof value === "string" && /^0x[0-9A-F]{40}$/.test(value) ? value.toLowerCase() : value;
33
+ if (!isAddress(candidate) || candidate === zeroAddress) {
26
34
  throw new ForeverMoneyError(
27
35
  "INVALID_ADDRESS",
28
36
  "Expected a non-zero EVM address."
29
37
  );
30
38
  }
31
- return getAddress(value);
39
+ return getAddress(candidate);
32
40
  }
33
41
  function evmToMirrorSS58(evmAddress) {
34
- return evmToAddress(normalizeEvmAddress(evmAddress), BITTENSOR_SS58_PREFIX);
42
+ return AccountId(BITTENSOR_SS58_PREFIX).dec(
43
+ Blake2256(
44
+ concat([
45
+ stringToBytes("evm:"),
46
+ hexToBytes(normalizeEvmAddress(evmAddress))
47
+ ])
48
+ )
49
+ );
35
50
  }
36
51
  function decodeBittensorAddress(value) {
37
52
  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.");
53
+ if (typeof value !== "string" || !value || value.startsWith("0x"))
54
+ throw new Error("Raw hex is not SS58.");
55
+ const info = getSs58AddressInfo(value);
56
+ if (!info.isValid || info.ss58Format !== BITTENSOR_SS58_PREFIX || info.publicKey.length !== 32)
57
+ throw new Error("Invalid Bittensor address.");
58
+ const publicKey = info.publicKey;
41
59
  return publicKey;
42
60
  } catch {
43
61
  throw new ForeverMoneyError(
@@ -47,7 +65,7 @@ function decodeBittensorAddress(value) {
47
65
  }
48
66
  }
49
67
  function normalizeSS58(value) {
50
- return encodeAddress(decodeBittensorAddress(value), BITTENSOR_SS58_PREFIX);
68
+ return AccountId(BITTENSOR_SS58_PREFIX).dec(decodeBittensorAddress(value));
51
69
  }
52
70
  function isBittensorSS58(value) {
53
71
  try {
@@ -58,14 +76,14 @@ function isBittensorSS58(value) {
58
76
  }
59
77
  }
60
78
  function ss58ToPublicKey(value) {
61
- return u8aToHex(decodeBittensorAddress(value));
79
+ return bytesToHex(decodeBittensorAddress(value));
62
80
  }
63
81
 
64
82
  // src/core/amounts.ts
65
- import { MaxUint256, formatUnits, parseUnits } from "ethers";
83
+ import { maxUint256, formatUnits, parseUnits } from "viem";
66
84
 
67
85
  // src/chains/deployment.ts
68
- import { getAddress as getAddress2 } from "ethers";
86
+ import { getAddress as getAddress2 } from "viem";
69
87
  var BASE_CHAIN_ID = 8453;
70
88
  var ROBINHOOD_CHAIN_ID = 4663;
71
89
  var SUBTENSOR_CHAIN_ID = 964;
@@ -203,7 +221,7 @@ function assertWholeRao(amountWei) {
203
221
  "The amount cannot be negative."
204
222
  );
205
223
  }
206
- if (amountWei > MaxUint256) {
224
+ if (amountWei > maxUint256) {
207
225
  throw new ForeverMoneyError(
208
226
  "INVALID_TRANSACTION_PLAN",
209
227
  "The amount exceeds uint256."
@@ -229,7 +247,7 @@ function formatTaoAmount(amountWei) {
229
247
  "A TAO amount cannot be negative."
230
248
  );
231
249
  }
232
- if (amountWei > MaxUint256) {
250
+ if (amountWei > maxUint256) {
233
251
  throw new ForeverMoneyError(
234
252
  "INVALID_TRANSACTION_PLAN",
235
253
  "The TAO amount exceeds uint256."
@@ -241,7 +259,8 @@ function formatTaoAmount(amountWei) {
241
259
  "TAO amounts must resolve to a whole RAO."
242
260
  );
243
261
  }
244
- return formatUnits(amountWei, 18);
262
+ const formatted = formatUnits(amountWei, 18);
263
+ return formatted.includes(".") ? formatted : `${formatted}.0`;
245
264
  }
246
265
  function mulDivCeil(value, numerator, denominator) {
247
266
  return (value * numerator + denominator - 1n) / denominator;
@@ -264,7 +283,7 @@ function feeWithBuffer(fee) {
264
283
  BASIS_POINTS + NETWORK_FEE_BUFFER_BPS,
265
284
  BASIS_POINTS
266
285
  );
267
- if (buffered > MaxUint256) {
286
+ if (buffered > maxUint256) {
268
287
  throw new ForeverMoneyError(
269
288
  "INVALID_TRANSACTION_PLAN",
270
289
  "The buffered network fee exceeds uint256."
@@ -290,7 +309,7 @@ function gasLimitWithBuffer(gasLimit) {
290
309
  BASIS_POINTS + GAS_LIMIT_BUFFER_BPS,
291
310
  BASIS_POINTS
292
311
  );
293
- if (buffered > MaxUint256) {
312
+ if (buffered > maxUint256) {
294
313
  throw new ForeverMoneyError(
295
314
  "INVALID_TRANSACTION_PLAN",
296
315
  "The buffered gas limit exceeds uint256."
@@ -374,10 +393,10 @@ var foreverMoneyAbis = Object.freeze({
374
393
  });
375
394
 
376
395
  // src/bridge/plans.ts
377
- import { Contract, Interface } from "ethers";
396
+ import { encodeFunctionData, parseAbi } from "viem";
378
397
 
379
398
  // src/core/plans.ts
380
- import { keccak256, toUtf8Bytes } from "ethers";
399
+ import { keccak256, stringToBytes as stringToBytes2 } from "viem";
381
400
  function createTransactionPlan(plan) {
382
401
  const steps = plan.steps.map(
383
402
  (step2) => Object.freeze({
@@ -394,12 +413,12 @@ function createTransactionPlan(plan) {
394
413
  };
395
414
  return Object.freeze({
396
415
  ...versionedPlan,
397
- hash: keccak256(toUtf8Bytes(JSON.stringify(versionedPlan)))
416
+ hash: keccak256(stringToBytes2(JSON.stringify(versionedPlan)))
398
417
  });
399
418
  }
400
419
 
401
420
  // src/core/validation.ts
402
- import { MaxUint256 as MaxUint2562, ZeroHash, isHexString } from "ethers";
421
+ import { maxUint256 as maxUint2562, zeroHash, isHex } from "viem";
403
422
  function assertBigInt(amount, label) {
404
423
  if (typeof amount !== "bigint") {
405
424
  throw new ForeverMoneyError(
@@ -422,7 +441,7 @@ function assertPositiveAmount(amount, label) {
422
441
  `${label} cannot be negative.`
423
442
  );
424
443
  }
425
- if (amount > MaxUint2562) {
444
+ if (amount > maxUint2562) {
426
445
  throw new ForeverMoneyError(
427
446
  "INVALID_TRANSACTION_PLAN",
428
447
  `${label} exceeds uint256.`
@@ -437,7 +456,7 @@ function assertNonNegativeAmount(amount, label) {
437
456
  `${label} cannot be negative.`
438
457
  );
439
458
  }
440
- if (amount > MaxUint2562) {
459
+ if (amount > maxUint2562) {
441
460
  throw new ForeverMoneyError(
442
461
  "INVALID_TRANSACTION_PLAN",
443
462
  `${label} exceeds uint256.`
@@ -469,7 +488,7 @@ function assertRecord(value, label) {
469
488
  }
470
489
  }
471
490
  function normalizeBytes32(value, label) {
472
- if (!isHexString(value, 32)) {
491
+ if (!isHex(value, { strict: true }) || value.length !== 66) {
473
492
  throw new ForeverMoneyError(
474
493
  "INVALID_BYTES32",
475
494
  `${label} must be a 32-byte hex value.`
@@ -478,14 +497,14 @@ function normalizeBytes32(value, label) {
478
497
  return value.toLowerCase();
479
498
  }
480
499
  function normalizeOptionalBytes32(value, label) {
481
- return value === void 0 ? ZeroHash : normalizeBytes32(value, label);
500
+ return value === void 0 ? zeroHash : normalizeBytes32(value, label);
482
501
  }
483
502
 
484
503
  // 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);
504
+ var erc20Abi = parseAbi(ERC20_ABI);
505
+ var spokeAbi = parseAbi(SPOKE_GATEWAY_ABI);
506
+ var alphaAbi = parseAbi(ALPHA_GATEWAY_ABI);
507
+ var stakingAbi = parseAbi(STAKING_ABI);
489
508
  var MIN_LIQUID_EVM_TO_SUBTENSOR_WEI = 10000000000000000n;
490
509
  var MIN_LIQUID_BASE_TO_SUBTENSOR_WEI = MIN_LIQUID_EVM_TO_SUBTENSOR_WEI;
491
510
  function assertDelivery(value) {
@@ -554,10 +573,11 @@ function buildEvmToSubtensorPlan(input) {
554
573
  evm.chainId,
555
574
  sender,
556
575
  evm.contracts.wrappedTao,
557
- erc20Interface.encodeFunctionData("approve", [
558
- evm.contracts.gateway,
559
- input.amountWei
560
- ]),
576
+ encodeFunctionData({
577
+ abi: erc20Abi,
578
+ functionName: "approve",
579
+ args: [evm.contracts.gateway, input.amountWei]
580
+ }),
561
581
  0n
562
582
  )
563
583
  );
@@ -570,10 +590,11 @@ function buildEvmToSubtensorPlan(input) {
570
590
  evm.chainId,
571
591
  sender,
572
592
  evm.contracts.gateway,
573
- spokeInterface.encodeFunctionData(
574
- "bridgeToFinney(address,uint256,(bytes32,address,bool,uint256))",
575
- [evm.contracts.wrappedTao, input.amountWei, exit]
576
- ),
593
+ encodeFunctionData({
594
+ abi: spokeAbi,
595
+ functionName: "bridgeToFinney",
596
+ args: [evm.contracts.wrappedTao, input.amountWei, exit]
597
+ }),
577
598
  value,
578
599
  input.estimatedBridgeGas === void 0 ? void 0 : gasLimitWithBuffer(input.estimatedBridgeGas)
579
600
  )
@@ -620,11 +641,15 @@ function buildSubtensorToEvmPlan(input) {
620
641
  subtensor.chainId,
621
642
  sender,
622
643
  subtensor.contracts.stakingPrecompile,
623
- stakingInterface.encodeFunctionData("approve", [
624
- subtensor.contracts.gateway,
625
- input.netuid,
626
- amountRao
627
- ]),
644
+ encodeFunctionData({
645
+ abi: stakingAbi,
646
+ functionName: "approve",
647
+ args: [
648
+ subtensor.contracts.gateway,
649
+ input.netuid,
650
+ amountRao
651
+ ]
652
+ }),
628
653
  0n
629
654
  )
630
655
  );
@@ -646,14 +671,18 @@ function buildSubtensorToEvmPlan(input) {
646
671
  subtensor.chainId,
647
672
  sender,
648
673
  subtensor.contracts.gateway,
649
- alphaInterface.encodeFunctionData("bridgeOut", [
650
- evm.ccipSelector,
651
- subtensor.contracts.wrappedTao,
652
- recipient,
653
- taoAmount,
654
- stakedAlphaRao,
655
- input.amountWei
656
- ]),
674
+ encodeFunctionData({
675
+ abi: alphaAbi,
676
+ functionName: "bridgeOut",
677
+ args: [
678
+ evm.ccipSelector,
679
+ subtensor.contracts.wrappedTao,
680
+ recipient,
681
+ taoAmount,
682
+ stakedAlphaRao,
683
+ input.amountWei
684
+ ]
685
+ }),
657
686
  value,
658
687
  input.estimatedBridgeGas === void 0 ? void 0 : gasLimitWithBuffer(input.estimatedBridgeGas)
659
688
  )
@@ -679,29 +708,29 @@ async function prepareEvmToSubtensor(provider, input) {
679
708
  wantLiquid: input.delivery === "liquid",
680
709
  minTaoOut: input.amountWei
681
710
  };
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
711
  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)
712
+ provider.readContract({
713
+ address: evm.contracts.wrappedTao,
714
+ abi: erc20Abi,
715
+ functionName: "allowance",
716
+ args: [sender, evm.contracts.gateway]
717
+ }),
718
+ provider.readContract({
719
+ address: evm.contracts.gateway,
720
+ abi: spokeAbi,
721
+ functionName: "quoteBridgeToFinney",
722
+ args: [evm.contracts.wrappedTao, input.amountWei, exit]
723
+ })
696
724
  ]);
697
725
  let estimatedBridgeGas;
698
726
  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
- );
727
+ const data = encodeFunctionData({
728
+ abi: spokeAbi,
729
+ functionName: "bridgeToFinney",
730
+ args: [evm.contracts.wrappedTao, input.amountWei, exit]
731
+ });
703
732
  estimatedBridgeGas = await provider.estimateGas({
704
- from: sender,
733
+ account: sender,
705
734
  to: evm.contracts.gateway,
706
735
  data,
707
736
  value: feeWithBuffer(exactNetworkFeeWei)
@@ -729,17 +758,17 @@ async function prepareSubtensorToEvm(provider, input) {
729
758
  assertWholeRao(input.amountWei);
730
759
  const { subtensor } = foreverMoneyDeployment;
731
760
  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
- );
761
+ const exactNetworkFeeWei = await provider.readContract({
762
+ address: subtensor.contracts.gateway,
763
+ abi: alphaAbi,
764
+ functionName: "quoteBridgeOut",
765
+ args: [
766
+ evm.ccipSelector,
767
+ subtensor.contracts.wrappedTao,
768
+ recipient,
769
+ input.amountWei
770
+ ]
771
+ });
743
772
  let stakingAllowanceRao;
744
773
  if (input.source === "staked") {
745
774
  if (input.netuid === void 0) {
@@ -749,16 +778,12 @@ async function prepareSubtensorToEvm(provider, input) {
749
778
  );
750
779
  }
751
780
  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
- );
781
+ stakingAllowanceRao = await provider.readContract({
782
+ address: subtensor.contracts.stakingPrecompile,
783
+ abi: stakingAbi,
784
+ functionName: "allowance",
785
+ args: [sender, subtensor.contracts.gateway, input.netuid]
786
+ });
762
787
  }
763
788
  const taoAmount = input.source === "liquid" ? input.amountWei : 0n;
764
789
  const stakedAlphaRao = input.source === "staked" ? input.amountWei / EVM_WEI_PER_RAO : 0n;
@@ -767,16 +792,20 @@ async function prepareSubtensorToEvm(provider, input) {
767
792
  let estimatedBridgeGas;
768
793
  if (input.source === "liquid" || stakingAllowanceRao !== void 0 && stakingAllowanceRao >= stakedAlphaRao) {
769
794
  estimatedBridgeGas = await provider.estimateGas({
770
- from: sender,
795
+ account: sender,
771
796
  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
- ]),
797
+ data: encodeFunctionData({
798
+ abi: alphaAbi,
799
+ functionName: "bridgeOut",
800
+ args: [
801
+ evm.ccipSelector,
802
+ subtensor.contracts.wrappedTao,
803
+ recipient,
804
+ taoAmount,
805
+ stakedAlphaRao,
806
+ input.amountWei
807
+ ]
808
+ }),
780
809
  value
781
810
  });
782
811
  }
@@ -797,7 +826,7 @@ function prepareSubtensorToBase(provider, input) {
797
826
  }
798
827
 
799
828
  // src/core/transport.ts
800
- import { BrowserProvider } from "ethers";
829
+ import { createPublicClient, custom } from "viem";
801
830
  function isRecord(value) {
802
831
  return typeof value === "object" && value !== null && !Array.isArray(value);
803
832
  }
@@ -916,10 +945,42 @@ function providerFromTransport(transport) {
916
945
  "An EIP-1193-compatible RPC transport is required."
917
946
  );
918
947
  }
919
- return new BrowserProvider(transport);
948
+ return createPublicClient({
949
+ transport: custom(transport, { retryCount: 0 }),
950
+ cacheTime: 0,
951
+ batch: { multicall: false }
952
+ });
953
+ }
954
+ function trackingClient(provider) {
955
+ if (provider && "getChainId" in provider && typeof provider.getChainId === "function")
956
+ return provider;
957
+ if (provider && "send" in provider && typeof provider.send === "function") {
958
+ return providerFromTransport({
959
+ request: ({ method, params }) => {
960
+ if (params !== void 0 && !Array.isArray(params))
961
+ throw new ForeverMoneyError(
962
+ "RPC_ERROR",
963
+ "Tracking RPC params must be an array."
964
+ );
965
+ return provider.send(
966
+ method,
967
+ params === void 0 ? [] : [...params]
968
+ );
969
+ }
970
+ });
971
+ }
972
+ throw new ForeverMoneyError(
973
+ "MISSING_TRANSPORT",
974
+ "A viem public client or JSON-RPC provider is required."
975
+ );
920
976
  }
921
977
 
922
978
  // src/core/provider-errors.ts
979
+ import {
980
+ BaseError,
981
+ ContractFunctionRevertedError,
982
+ ExecutionRevertedError
983
+ } from "viem";
923
984
  function stringProperty(value, property) {
924
985
  if (typeof value !== "object" || value === null || !(property in value)) {
925
986
  return void 0;
@@ -932,9 +993,13 @@ async function providerOperation(operation, action) {
932
993
  return await action();
933
994
  } catch (error) {
934
995
  if (error instanceof ForeverMoneyError) throw error;
935
- const causeCode = stringProperty(error, "code");
936
- const reason = stringProperty(error, "reason");
937
- const reverted = causeCode === "CALL_EXCEPTION";
996
+ const revert = error instanceof BaseError ? error.walk(
997
+ (cause) => cause instanceof ContractFunctionRevertedError || cause instanceof ExecutionRevertedError
998
+ ) : void 0;
999
+ const viemRevert = revert instanceof ContractFunctionRevertedError || revert instanceof ExecutionRevertedError;
1000
+ const causeCode = viemRevert ? revert.name : stringProperty(error, "code");
1001
+ const reason = viemRevert ? revert instanceof ContractFunctionRevertedError ? revert.data?.errorName ?? revert.reason : void 0 : stringProperty(error, "reason");
1002
+ const reverted = viemRevert || causeCode === "CALL_EXCEPTION";
938
1003
  throw new ForeverMoneyError(
939
1004
  reverted ? "SIMULATION_REVERTED" : "RPC_ERROR",
940
1005
  reverted ? `${operation} reverted.` : `${operation} failed.`,
@@ -947,10 +1012,14 @@ async function providerOperation(operation, action) {
947
1012
  }
948
1013
 
949
1014
  // 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);
1015
+ import {
1016
+ encodeFunctionData as encodeFunctionData2,
1017
+ parseAbi as parseAbi2,
1018
+ zeroAddress as zeroAddress2
1019
+ } from "viem";
1020
+ var erc20Abi2 = parseAbi2(ERC20_ABI);
1021
+ var factoryAbi = parseAbi2(VAULT_FACTORY_ABI);
1022
+ var managerAbi = parseAbi2(VAULT_MANAGER_ABI);
954
1023
  function step(kind, label, from, to, data, value = 0n) {
955
1024
  return {
956
1025
  kind,
@@ -1067,10 +1136,11 @@ function buildCreateVaultPlan(input) {
1067
1136
  `Approve ${stash.token} for the vault factory`,
1068
1137
  owner,
1069
1138
  stash.token,
1070
- erc20Interface2.encodeFunctionData("approve", [
1071
- base.contracts.vaultFactory,
1072
- stash.amount
1073
- ])
1139
+ encodeFunctionData2({
1140
+ abi: erc20Abi2,
1141
+ functionName: "approve",
1142
+ args: [base.contracts.vaultFactory, stash.amount]
1143
+ })
1074
1144
  )
1075
1145
  );
1076
1146
  }
@@ -1081,15 +1151,19 @@ function buildCreateVaultPlan(input) {
1081
1151
  "Create ForeverMoney vault",
1082
1152
  owner,
1083
1153
  base.contracts.vaultFactory,
1084
- factoryInterface.encodeFunctionData("create", [
1085
- owner,
1086
- associatedMiner,
1087
- akAddress,
1088
- poolManager,
1089
- poolAddress,
1090
- positionManagerImplementation,
1091
- stashTokens
1092
- ]),
1154
+ encodeFunctionData2({
1155
+ abi: factoryAbi,
1156
+ functionName: "create",
1157
+ args: [
1158
+ owner,
1159
+ associatedMiner,
1160
+ akAddress,
1161
+ poolManager,
1162
+ poolAddress,
1163
+ positionManagerImplementation,
1164
+ stashTokens
1165
+ ]
1166
+ }),
1093
1167
  value
1094
1168
  )
1095
1169
  );
@@ -1108,14 +1182,15 @@ async function prepareCreateVault(provider, input) {
1108
1182
  const allowances = await Promise.all(
1109
1183
  erc20Stash.map(async ({ token }) => ({
1110
1184
  token,
1111
- allowance: await new Contract2(
1112
- token,
1113
- ERC20_ABI,
1114
- provider
1115
- ).getFunction("allowance")(
1116
- owner,
1117
- foreverMoneyDeployment.base.contracts.vaultFactory
1118
- )
1185
+ allowance: await provider.readContract({
1186
+ address: token,
1187
+ abi: erc20Abi2,
1188
+ functionName: "allowance",
1189
+ args: [
1190
+ owner,
1191
+ foreverMoneyDeployment.base.contracts.vaultFactory
1192
+ ]
1193
+ })
1119
1194
  }))
1120
1195
  );
1121
1196
  return buildCreateVaultPlan({ ...input, allowances });
@@ -1142,11 +1217,11 @@ function buildDepositVaultPlan(input) {
1142
1217
  "Deposit native ETH into the vault as WETH",
1143
1218
  owner,
1144
1219
  manager,
1145
- managerInterface.encodeFunctionData("topUpAk", [
1146
- akAddress,
1147
- ZeroAddress2,
1148
- deposit.amount
1149
- ]),
1220
+ encodeFunctionData2({
1221
+ abi: managerAbi,
1222
+ functionName: "topUpAk",
1223
+ args: [akAddress, zeroAddress2, deposit.amount]
1224
+ }),
1150
1225
  deposit.amount
1151
1226
  )
1152
1227
  );
@@ -1166,10 +1241,11 @@ function buildDepositVaultPlan(input) {
1166
1241
  `Approve ${deposit.token} for the vault manager`,
1167
1242
  owner,
1168
1243
  deposit.token,
1169
- erc20Interface2.encodeFunctionData("approve", [
1170
- manager,
1171
- deposit.amount
1172
- ])
1244
+ encodeFunctionData2({
1245
+ abi: erc20Abi2,
1246
+ functionName: "approve",
1247
+ args: [manager, deposit.amount]
1248
+ })
1173
1249
  )
1174
1250
  );
1175
1251
  }
@@ -1179,11 +1255,11 @@ function buildDepositVaultPlan(input) {
1179
1255
  `Deposit ${deposit.token} into the vault`,
1180
1256
  owner,
1181
1257
  manager,
1182
- managerInterface.encodeFunctionData("topUpAk", [
1183
- akAddress,
1184
- deposit.token,
1185
- deposit.amount
1186
- ])
1258
+ encodeFunctionData2({
1259
+ abi: managerAbi,
1260
+ functionName: "topUpAk",
1261
+ args: [akAddress, deposit.token, deposit.amount]
1262
+ })
1187
1263
  )
1188
1264
  );
1189
1265
  }
@@ -1203,11 +1279,12 @@ async function prepareDepositVault(provider, input) {
1203
1279
  const allowances = await Promise.all(
1204
1280
  erc20Deposits.map(async ({ token }) => ({
1205
1281
  token,
1206
- allowance: await new Contract2(
1207
- token,
1208
- ERC20_ABI,
1209
- provider
1210
- ).getFunction("allowance")(owner, manager)
1282
+ allowance: await provider.readContract({
1283
+ address: token,
1284
+ abi: erc20Abi2,
1285
+ functionName: "allowance",
1286
+ args: [owner, manager]
1287
+ })
1211
1288
  }))
1212
1289
  );
1213
1290
  return buildDepositVaultPlan({ ...input, allowances });
@@ -1238,16 +1315,17 @@ function buildWithdrawVaultPlan(input) {
1238
1315
  "Withdraw assets from the vault",
1239
1316
  owner,
1240
1317
  manager,
1241
- managerInterface.encodeFunctionData(
1242
- "withdrawFromAkAndPositions",
1243
- [
1318
+ encodeFunctionData2({
1319
+ abi: managerAbi,
1320
+ functionName: "withdrawFromAkAndPositions",
1321
+ args: [
1244
1322
  akAddress,
1245
1323
  input.amount0,
1246
1324
  input.amount1,
1247
1325
  input.decreaseTokenIds,
1248
1326
  input.unwrapWeth
1249
1327
  ]
1250
- )
1328
+ })
1251
1329
  )
1252
1330
  ]
1253
1331
  });
@@ -1265,9 +1343,11 @@ function buildClaimVaultFeesPlan(input) {
1265
1343
  "Claim vault fees",
1266
1344
  owner,
1267
1345
  manager,
1268
- managerInterface.encodeFunctionData("claimFees(address)", [
1269
- akAddress
1270
- ])
1346
+ encodeFunctionData2({
1347
+ abi: managerAbi,
1348
+ functionName: "claimFees",
1349
+ args: [akAddress]
1350
+ })
1271
1351
  )
1272
1352
  ]
1273
1353
  });
@@ -1287,20 +1367,25 @@ function buildSetVaultStakingPlan(input) {
1287
1367
  `${input.staking ? "Stake" : "Unstake"} the vault position`,
1288
1368
  owner,
1289
1369
  manager,
1290
- managerInterface.encodeFunctionData(
1291
- `${action}(address,bytes)`,
1292
- [akAddress, "0x"]
1293
- )
1370
+ encodeFunctionData2({
1371
+ abi: managerAbi,
1372
+ functionName: action,
1373
+ args: [akAddress, "0x"]
1374
+ })
1294
1375
  )
1295
1376
  ]
1296
1377
  });
1297
1378
  }
1298
1379
 
1299
1380
  // src/bridge/tracking.ts
1300
- import { Interface as Interface4, toBeHex } from "ethers";
1381
+ import {
1382
+ decodeEventLog as decodeEventLog2,
1383
+ parseAbi as parseAbi4,
1384
+ TransactionReceiptNotFoundError
1385
+ } from "viem";
1301
1386
 
1302
1387
  // src/bridge/receipts.ts
1303
- import { Interface as Interface3, isHexString as isHexString2 } from "ethers";
1388
+ import { decodeEventLog, isHex as isHex2, parseAbi as parseAbi3 } from "viem";
1304
1389
 
1305
1390
  // src/core/receipts.ts
1306
1391
  function isLogFrom(log, address) {
@@ -1308,8 +1393,8 @@ function isLogFrom(log, address) {
1308
1393
  }
1309
1394
 
1310
1395
  // src/bridge/receipts.ts
1311
- var alphaGatewayInterface = new Interface3(ALPHA_GATEWAY_ABI);
1312
- var spokeGatewayInterface = new Interface3(SPOKE_GATEWAY_ABI);
1396
+ var alphaGatewayAbi = parseAbi3(ALPHA_GATEWAY_ABI);
1397
+ var spokeGatewayAbi = parseAbi3(SPOKE_GATEWAY_ABI);
1313
1398
  function assertBridgeDirection(value) {
1314
1399
  if (value !== "base-to-subtensor" && value !== "robinhood-to-subtensor" && value !== "subtensor-to-base" && value !== "subtensor-to-robinhood") {
1315
1400
  throw new ForeverMoneyError(
@@ -1332,20 +1417,21 @@ function bridgeMessageIdFromReceipt(direction, receipt) {
1332
1417
  evmChainFromBridgeDirection(direction)
1333
1418
  );
1334
1419
  const evmToSubtensor = isEvmToSubtensorDirection(direction);
1335
- const [address, contractInterface, eventName] = evmToSubtensor ? [evm.contracts.gateway, spokeGatewayInterface, "BridgedToFinney"] : [
1420
+ const [address, contractAbi, eventName] = evmToSubtensor ? [evm.contracts.gateway, spokeGatewayAbi, "BridgedToFinney"] : [
1336
1421
  foreverMoneyDeployment.subtensor.contracts.gateway,
1337
- alphaGatewayInterface,
1422
+ alphaGatewayAbi,
1338
1423
  "BridgedOut"
1339
1424
  ];
1340
1425
  for (const log of receipt.logs) {
1341
1426
  if (!isLogFrom(log, address)) continue;
1342
1427
  try {
1343
- const parsed = contractInterface.parseLog({
1428
+ const parsed = decodeEventLog({
1429
+ abi: contractAbi,
1344
1430
  data: log.data,
1345
1431
  topics: [...log.topics]
1346
1432
  });
1347
- const messageId = parsed?.args.messageId;
1348
- if (parsed?.name === eventName && typeof messageId === "string" && isHexString2(messageId, 32) && (evmToSubtensor || parsed.args.destChainSelector === evm.ccipSelector)) {
1433
+ const messageId = "messageId" in parsed.args ? parsed.args.messageId : void 0;
1434
+ if (parsed.eventName === eventName && typeof messageId === "string" && isHex2(messageId, { strict: true }) && messageId.length === 66 && (evmToSubtensor || "destChainSelector" in parsed.args && parsed.args.destChainSelector === evm.ccipSelector)) {
1349
1435
  return messageId.toLowerCase();
1350
1436
  }
1351
1437
  } catch {
@@ -1355,8 +1441,8 @@ function bridgeMessageIdFromReceipt(direction, receipt) {
1355
1441
  }
1356
1442
 
1357
1443
  // src/bridge/tracking.ts
1358
- var ccipExecutionInterface = new Interface4(CCIP_EXECUTION_ABI);
1359
- var alphaGatewayInterface2 = new Interface4(ALPHA_GATEWAY_ABI);
1444
+ var ccipExecutionAbi = parseAbi4(CCIP_EXECUTION_ABI);
1445
+ var alphaGatewayAbi2 = parseAbi4(ALPHA_GATEWAY_ABI);
1360
1446
  function destinationChainId(direction) {
1361
1447
  assertBridgeDirection(direction);
1362
1448
  const evm = getForeverMoneyEvmDeployment(
@@ -1372,14 +1458,14 @@ function sourceChainId(direction) {
1372
1458
  return isEvmToSubtensorDirection(direction) ? evm.chainId : foreverMoneyDeployment.subtensor.chainId;
1373
1459
  }
1374
1460
  async function assertProviderChain(provider, expectedChainId, label) {
1375
- const network = await provider.getNetwork();
1376
- if (network.chainId !== BigInt(expectedChainId)) {
1461
+ const chainId = await provider.getChainId();
1462
+ if (chainId !== expectedChainId) {
1377
1463
  throw new ForeverMoneyError(
1378
1464
  "CHAIN_MISMATCH",
1379
- `${label} transport reported chain ID ${network.chainId}; expected ${expectedChainId}.`,
1465
+ `${label} transport reported chain ID ${chainId}; expected ${expectedChainId}.`,
1380
1466
  {
1381
1467
  expectedChainId,
1382
- actualChainId: network.chainId.toString()
1468
+ actualChainId: chainId.toString()
1383
1469
  }
1384
1470
  );
1385
1471
  }
@@ -1390,13 +1476,14 @@ async function assertDestinationProvider(provider, direction) {
1390
1476
  return expectedChainId;
1391
1477
  }
1392
1478
  async function getBridgeSourceStatus(provider, input) {
1479
+ const client = trackingClient(provider);
1393
1480
  const expectedChainId = sourceChainId(input.direction);
1394
1481
  const transactionHash = normalizeBytes32(
1395
1482
  input.transactionHash,
1396
1483
  "Transaction hash"
1397
1484
  );
1398
- await assertProviderChain(provider, expectedChainId, "Bridge source");
1399
- const receipt = await provider.getTransactionReceipt(transactionHash);
1485
+ await assertProviderChain(client, expectedChainId, "Bridge source");
1486
+ const receipt = await receiptOrNull(client, transactionHash);
1400
1487
  if (receipt === null) {
1401
1488
  return Object.freeze({
1402
1489
  direction: input.direction,
@@ -1406,7 +1493,7 @@ async function getBridgeSourceStatus(provider, input) {
1406
1493
  messageId: null
1407
1494
  });
1408
1495
  }
1409
- if (receipt.status === 0) {
1496
+ if (receipt.status === "reverted") {
1410
1497
  return Object.freeze({
1411
1498
  direction: input.direction,
1412
1499
  sourceChainId: expectedChainId,
@@ -1415,7 +1502,7 @@ async function getBridgeSourceStatus(provider, input) {
1415
1502
  messageId: null
1416
1503
  });
1417
1504
  }
1418
- if (receipt.status !== 1) {
1505
+ if (receipt.status !== "success") {
1419
1506
  throw new ForeverMoneyError(
1420
1507
  "INVALID_PROVIDER_RESPONSE",
1421
1508
  "The bridge source receipt has an invalid status."
@@ -1437,8 +1524,10 @@ async function getBridgeSourceStatus(provider, input) {
1437
1524
  });
1438
1525
  }
1439
1526
  async function getCcipDeliveryCheckpoint(provider, direction) {
1440
- const expectedChainId = await assertDestinationProvider(provider, direction);
1441
- const fromBlock = await provider.getBlockNumber();
1527
+ const client = trackingClient(provider);
1528
+ const expectedChainId = await assertDestinationProvider(client, direction);
1529
+ const blockNumber = await client.getBlockNumber({ cacheTime: 0 });
1530
+ const fromBlock = Number(blockNumber);
1442
1531
  if (!Number.isSafeInteger(fromBlock) || fromBlock < 0) {
1443
1532
  throw new ForeverMoneyError(
1444
1533
  "INVALID_PROVIDER_RESPONSE",
@@ -1452,6 +1541,7 @@ async function getCcipDeliveryCheckpoint(provider, direction) {
1452
1541
  });
1453
1542
  }
1454
1543
  async function getCcipDeliveryStatus(provider, input) {
1544
+ const client = trackingClient(provider);
1455
1545
  const expectedChainId = destinationChainId(input.direction);
1456
1546
  const messageId = normalizeBytes32(input.messageId, "CCIP message ID");
1457
1547
  if (!Number.isSafeInteger(input.fromBlock) || input.fromBlock < 0) {
@@ -1460,30 +1550,27 @@ async function getCcipDeliveryStatus(provider, input) {
1460
1550
  "CCIP fromBlock must be a non-negative safe integer."
1461
1551
  );
1462
1552
  }
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
- }
1553
+ await assertProviderChain(client, expectedChainId, "Bridge destination");
1468
1554
  const evmChain = evmChainFromBridgeDirection(input.direction);
1469
1555
  const evm = getForeverMoneyEvmDeployment(evmChain);
1470
1556
  const evmToSubtensor = isEvmToSubtensorDirection(input.direction);
1471
1557
  const sourceSelector = evmToSubtensor ? evm.ccipSelector : foreverMoneyDeployment.subtensor.ccipSelector;
1472
1558
  const offRamp = evmToSubtensor ? evmChain === "base" ? foreverMoneyDeployment.subtensor.contracts.ccipOffRampFromBase : foreverMoneyDeployment.subtensor.contracts.ccipOffRampFromRobinhood : evm.contracts.ccipOffRampFromSubtensor;
1473
- const logs = await provider.getLogs({
1559
+ const logs = await client.getLogs({
1474
1560
  address: offRamp,
1475
- fromBlock: input.fromBlock,
1561
+ fromBlock: BigInt(input.fromBlock),
1476
1562
  toBlock: "latest",
1477
- topics: [event.topicHash, toBeHex(sourceSelector, 32), null, messageId]
1563
+ event: ccipExecutionAbi[0],
1564
+ args: { sourceChainSelector: sourceSelector, messageId },
1565
+ strict: true
1478
1566
  });
1479
1567
  const latest = logs.at(-1);
1480
1568
  if (latest === void 0) return "waiting";
1481
- const parsed = ccipExecutionInterface.parseLog(latest);
1482
- const state = Number(parsed?.args.state);
1569
+ const state = latest.args.state;
1483
1570
  if (state === 3) return "failure";
1484
1571
  if (state !== 2) return "waiting";
1485
1572
  if (!evmToSubtensor) return "success";
1486
- const receipt = await provider.getTransactionReceipt(latest.transactionHash);
1573
+ const receipt = await receiptOrNull(client, latest.transactionHash);
1487
1574
  if (receipt === null) {
1488
1575
  throw new ForeverMoneyError(
1489
1576
  "INVALID_PROVIDER_RESPONSE",
@@ -1495,7 +1582,11 @@ async function getCcipDeliveryStatus(provider, input) {
1495
1582
  continue;
1496
1583
  }
1497
1584
  try {
1498
- if (alphaGatewayInterface2.parseLog(log)?.name === "Claimable") {
1585
+ if (decodeEventLog2({
1586
+ abi: alphaGatewayAbi2,
1587
+ data: log.data,
1588
+ topics: log.topics
1589
+ }).eventName === "Claimable") {
1499
1590
  return "recovery";
1500
1591
  }
1501
1592
  } catch {
@@ -1503,20 +1594,28 @@ async function getCcipDeliveryStatus(provider, input) {
1503
1594
  }
1504
1595
  return "success";
1505
1596
  }
1597
+ async function receiptOrNull(provider, hash) {
1598
+ try {
1599
+ return await provider.getTransactionReceipt({ hash });
1600
+ } catch (error) {
1601
+ if (error instanceof TransactionReceiptNotFoundError) return null;
1602
+ throw error;
1603
+ }
1604
+ }
1506
1605
 
1507
1606
  // src/client.ts
1508
1607
  async function assertProviderChain2(provider, expectedChainId, name) {
1509
- const network = await providerOperation(
1608
+ const chainId = await providerOperation(
1510
1609
  `Reading the ${name} chain ID`,
1511
- () => provider.getNetwork()
1610
+ () => provider.getChainId()
1512
1611
  );
1513
- if (network.chainId !== BigInt(expectedChainId)) {
1612
+ if (chainId !== expectedChainId) {
1514
1613
  throw new ForeverMoneyError(
1515
1614
  "CHAIN_MISMATCH",
1516
- `${name} transport reported chain ID ${network.chainId}; expected ${expectedChainId}.`,
1615
+ `${name} transport reported chain ID ${chainId}; expected ${expectedChainId}.`,
1517
1616
  {
1518
1617
  expectedChainId,
1519
- actualChainId: network.chainId.toString()
1618
+ actualChainId: chainId.toString()
1520
1619
  }
1521
1620
  );
1522
1621
  }
@@ -1648,19 +1747,20 @@ function createForeverMoneyClient(options) {
1648
1747
  }
1649
1748
 
1650
1749
  // src/vaults/receipts.ts
1651
- import { Interface as Interface5 } from "ethers";
1652
- var vaultFactoryInterface = new Interface5(VAULT_FACTORY_ABI);
1750
+ import { decodeEventLog as decodeEventLog3, parseAbi as parseAbi5 } from "viem";
1751
+ var vaultFactoryAbi = parseAbi5(VAULT_FACTORY_ABI);
1653
1752
  function vaultManagerFromCreationReceipt(receipt) {
1654
1753
  for (const log of receipt.logs) {
1655
1754
  if (!isLogFrom(log, foreverMoneyDeployment.base.contracts.vaultFactory)) {
1656
1755
  continue;
1657
1756
  }
1658
1757
  try {
1659
- const parsed = vaultFactoryInterface.parseLog({
1758
+ const parsed = decodeEventLog3({
1759
+ abi: vaultFactoryAbi,
1660
1760
  data: log.data,
1661
1761
  topics: [...log.topics]
1662
1762
  });
1663
- if (parsed?.name === "SnLiquidityManagerCreated") {
1763
+ if (parsed.eventName === "SnLiquidityManagerCreated") {
1664
1764
  return normalizeEvmAddress(String(parsed.args.manager));
1665
1765
  }
1666
1766
  } catch {
@@ -1670,7 +1770,10 @@ function vaultManagerFromCreationReceipt(receipt) {
1670
1770
  }
1671
1771
 
1672
1772
  // src/core/transactions.ts
1673
- import { MaxUint256 as MaxUint2563 } from "ethers";
1773
+ import {
1774
+ defineChain,
1775
+ maxUint256 as maxUint2563
1776
+ } from "viem";
1674
1777
  function decimalQuantity(value, label) {
1675
1778
  if (!/^(0|[1-9][0-9]*)$/.test(value)) {
1676
1779
  throw new ForeverMoneyError(
@@ -1679,7 +1782,7 @@ function decimalQuantity(value, label) {
1679
1782
  );
1680
1783
  }
1681
1784
  const quantity = BigInt(value);
1682
- if (quantity > MaxUint2563) {
1785
+ if (quantity > maxUint2563) {
1683
1786
  throw new ForeverMoneyError(
1684
1787
  "INVALID_TRANSACTION_PLAN",
1685
1788
  `${label} exceeds uint256.`
@@ -1715,6 +1818,52 @@ function toEthersTransaction(transaction) {
1715
1818
  }
1716
1819
  });
1717
1820
  }
1821
+ var transactionChains = new Map(
1822
+ [
1823
+ [BASE_CHAIN_ID, "Base", "Ether", "ETH"],
1824
+ [ROBINHOOD_CHAIN_ID, "Robinhood", "Ether", "ETH"],
1825
+ [SUBTENSOR_CHAIN_ID, "Subtensor", "TAO", "TAO"]
1826
+ ].map(([id, name, currency, symbol]) => [
1827
+ id,
1828
+ Object.freeze(
1829
+ defineChain({
1830
+ id,
1831
+ name,
1832
+ nativeCurrency: Object.freeze({
1833
+ name: currency,
1834
+ symbol,
1835
+ decimals: 18
1836
+ }),
1837
+ // Chain identity only. The caller retains ownership of the wallet transport.
1838
+ rpcUrls: Object.freeze({
1839
+ default: Object.freeze({ http: Object.freeze([]) })
1840
+ })
1841
+ })
1842
+ )
1843
+ ])
1844
+ );
1845
+ function toViemTransaction(transaction) {
1846
+ const chain = transactionChains.get(transaction.chainId);
1847
+ if (!chain)
1848
+ throw new ForeverMoneyError(
1849
+ "INVALID_TRANSACTION_PLAN",
1850
+ "Unsupported transaction chain."
1851
+ );
1852
+ return Object.freeze({
1853
+ chain,
1854
+ account: transaction.from,
1855
+ chainId: transaction.chainId,
1856
+ to: transaction.to,
1857
+ data: transaction.data,
1858
+ value: decimalQuantity(transaction.value, "Transaction value"),
1859
+ ...transaction.gasLimit === void 0 ? {} : {
1860
+ gas: decimalQuantity(
1861
+ transaction.gasLimit,
1862
+ "Transaction gas limit"
1863
+ )
1864
+ }
1865
+ });
1866
+ }
1718
1867
  export {
1719
1868
  BASE_CCIP_SELECTOR,
1720
1869
  BASE_CHAIN_ID,
@@ -1765,6 +1914,7 @@ export {
1765
1914
  ss58ToPublicKey,
1766
1915
  toEip1193Transaction,
1767
1916
  toEthersTransaction,
1917
+ toViemTransaction,
1768
1918
  vaultManagerFromCreationReceipt
1769
1919
  };
1770
1920
  //# sourceMappingURL=index.js.map