@forevermoney/sdk 0.5.4 → 0.5.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.5.6 — 2026-09-22
4
+
5
+ - Add Pareton (SN10) deployment metadata on Base, Robinhood and Subtensor.
6
+ - Support staked-only SN10 bridge plans and preparation in both directions
7
+ on both EVM spokes, enforcing netuid 10. Deployment version is 1.5.0.
8
+
9
+ ## 0.5.5 — 2026-09-21
10
+
11
+ - Discover destination off-ramps from the canonical CCIP router for the source
12
+ chain, retaining the configured legacy off-ramp for historical transfers.
13
+ Cache discovery for five minutes per provider and coalesce concurrent reads.
14
+ - Recognise both legacy and CCIP v2 execution events and select the latest
15
+ execution across off-ramps. This fixes completed Finney-to-Robinhood SN80
16
+ deliveries remaining in the waiting state. Verified against a real delivery.
17
+
3
18
  ## 0.5.4 — 2026-09-21
4
19
 
5
20
  - Added the canonical Robinhood SN80 token to deployment metadata and enabled
package/README.md CHANGED
@@ -134,6 +134,21 @@ The bridge does not deduct its fee from the destination amount. For both
134
134
  directions, the SDK encodes the bridge amount itself as the contract's minimum
135
135
  output; callers cannot weaken that invariant.
136
136
 
137
+ ## Bridge Pareton (SN10)
138
+
139
+ Use `asset: 'sn10'` with `evmChain: 'base'` or `'robinhood'` in the
140
+ bridge plan and preparation APIs. Finney input and delivery must be staked;
141
+ netuid defaults to 10 and any other netuid is rejected. Amounts use 18 decimals
142
+ and must be whole alpha RAO. Approval, quoting and tracking use the same APIs
143
+ as SN80 below.
144
+
145
+ Canonical `contracts.wrappedSn10` addresses:
146
+
147
+ - Base and Robinhood: `0xDdDE5965D49e69b6362BD8743e139a45dBFEb7b3`.
148
+ - Subtensor (964): `0xcd0836D1ecE4AEDf5B39f2792e0790BBE79449b3`.
149
+
150
+ The Subtensor ERC-20 remains named "Subnet 10"; the application displays Pareton.
151
+
137
152
  ## Bridge SN80 on Base and Robinhood
138
153
 
139
154
  Pass `asset: 'sn80'` to bridge Base or Robinhood SN80 to a Finney subnet-80
package/dist/index.cjs CHANGED
@@ -37,6 +37,7 @@ __export(index_exports, {
37
37
  RAO_PER_TAO: () => RAO_PER_TAO,
38
38
  ROBINHOOD_CCIP_SELECTOR: () => ROBINHOOD_CCIP_SELECTOR,
39
39
  ROBINHOOD_CHAIN_ID: () => ROBINHOOD_CHAIN_ID,
40
+ SN10_NETUID: () => SN10_NETUID,
40
41
  SN80_NETUID: () => SN80_NETUID,
41
42
  SUBTENSOR_CCIP_SELECTOR: () => SUBTENSOR_CCIP_SELECTOR,
42
43
  SUBTENSOR_CHAIN_ID: () => SUBTENSOR_CHAIN_ID,
@@ -94,8 +95,9 @@ var ROBINHOOD_CCIP_SELECTOR = 6180753054346818345n;
94
95
  var SUBTENSOR_CCIP_SELECTOR = 2135107236357186872n;
95
96
  var RAO_PER_TAO = 1000000000n;
96
97
  var EVM_WEI_PER_RAO = 1000000000n;
98
+ var SN10_NETUID = 10n;
97
99
  var SN80_NETUID = 80n;
98
- var FOREVERMONEY_DEPLOYMENT_VERSION = "1.4.0";
100
+ var FOREVERMONEY_DEPLOYMENT_VERSION = "1.5.0";
99
101
  var foreverMoneyDeployment = Object.freeze({
100
102
  version: FOREVERMONEY_DEPLOYMENT_VERSION,
101
103
  base: Object.freeze({
@@ -113,6 +115,9 @@ var foreverMoneyDeployment = Object.freeze({
113
115
  wrappedTao: (0, import_viem.getAddress)(
114
116
  "0xf3081494b87e8d5fb7960f066e931d1d0e6e3d67"
115
117
  ),
118
+ wrappedSn10: (0, import_viem.getAddress)(
119
+ "0xDdDE5965D49e69b6362BD8743e139a45dBFEb7b3"
120
+ ),
116
121
  wrappedSn80: (0, import_viem.getAddress)(
117
122
  "0x6f63d869011f95274498023b4abfc00b30c34378"
118
123
  ),
@@ -148,6 +153,9 @@ var foreverMoneyDeployment = Object.freeze({
148
153
  wrappedTao: (0, import_viem.getAddress)(
149
154
  "0xf3081494B87e8D5fb7960f066E931D1D0e6E3d67"
150
155
  ),
156
+ wrappedSn10: (0, import_viem.getAddress)(
157
+ "0xDdDE5965D49e69b6362BD8743e139a45dBFEb7b3"
158
+ ),
151
159
  wrappedSn80: (0, import_viem.getAddress)(
152
160
  "0x6f63d869011f95274498023b4abfc00b30c34378"
153
161
  ),
@@ -177,6 +185,9 @@ var foreverMoneyDeployment = Object.freeze({
177
185
  wrappedTao: (0, import_viem.getAddress)(
178
186
  "0xC5b6C1632d34901239396F5E1BDe54B342900256"
179
187
  ),
188
+ wrappedSn10: (0, import_viem.getAddress)(
189
+ "0xcd0836D1ecE4AEDf5B39f2792e0790BBE79449b3"
190
+ ),
180
191
  wrappedSn80: (0, import_viem.getAddress)(
181
192
  "0xfD628dE75EF96f0A5C59659159C6cA81E0DC2222"
182
193
  ),
@@ -708,7 +719,8 @@ var CCIP_EXECUTION_ABI = Object.freeze([
708
719
  "event ExecutionStateChanged(uint64 indexed sourceChainSelector,uint64 indexed sequenceNumber,bytes32 indexed messageId,bytes32 messageHash,uint8 state,bytes returnData,uint256 gasUsed)"
709
720
  ]);
710
721
  var CCIP_ROUTER_ABI = Object.freeze([
711
- "function isOffRamp(uint64 sourceChainSelector,address offRamp) view returns (bool)"
722
+ "function isOffRamp(uint64 sourceChainSelector,address offRamp) view returns (bool)",
723
+ "function getOffRamps() view returns ((uint64 sourceChainSelector,address offRamp)[])"
712
724
  ]);
713
725
  var foreverMoneyAbis = Object.freeze({
714
726
  alphaGateway: ALPHA_GATEWAY_ABI,
@@ -811,18 +823,19 @@ function assertPartnerFeeAllowed(fee, maxBps) {
811
823
  }
812
824
  }
813
825
  function bridgeAsset(evmChain, asset = "tao") {
814
- if (asset !== "tao" && asset !== "sn80") {
826
+ if (asset !== "tao" && asset !== "sn80" && asset !== "sn10") {
815
827
  throw new ForeverMoneyError(
816
828
  "INVALID_TRANSACTION_PLAN",
817
- 'Asset must be "tao" or "sn80".'
829
+ 'Asset must be "tao", "sn80", or "sn10".'
818
830
  );
819
831
  }
820
832
  const evm = getForeverMoneyEvmDeployment(evmChain);
821
- return asset === "sn80" ? {
822
- evmToken: evm.contracts.wrappedSn80,
823
- subtensorToken: foreverMoneyDeployment.subtensor.contracts.wrappedSn80,
824
- label: "SN80",
825
- wrappedLabel: "SN80"
833
+ const tokenKey = asset === "sn10" ? "wrappedSn10" : "wrappedSn80";
834
+ return asset !== "tao" ? {
835
+ evmToken: evm.contracts[tokenKey],
836
+ subtensorToken: foreverMoneyDeployment.subtensor.contracts[tokenKey],
837
+ label: asset.toUpperCase(),
838
+ wrappedLabel: asset.toUpperCase()
826
839
  } : {
827
840
  evmToken: evm.contracts.wrappedTao,
828
841
  subtensorToken: foreverMoneyDeployment.subtensor.contracts.wrappedTao,
@@ -831,16 +844,17 @@ function bridgeAsset(evmChain, asset = "tao") {
831
844
  };
832
845
  }
833
846
  function assertAssetMode(asset, mode) {
834
- if (asset === "sn80" && mode !== "staked") {
847
+ if ((asset === "sn80" || asset === "sn10") && mode !== "staked") {
835
848
  throw new ForeverMoneyError(
836
849
  "INVALID_TRANSACTION_PLAN",
837
- "SN80 bridging requires staked subnet 80 input or delivery; liquid TAO conversion is not supported."
850
+ `${asset.toUpperCase()} bridging requires staked subnet ${asset === "sn10" ? SN10_NETUID : SN80_NETUID} input or delivery; liquid TAO conversion is not supported.`
838
851
  );
839
852
  }
840
853
  }
841
854
  function sourceNetuid(input) {
842
855
  if (input.source !== "staked") return void 0;
843
- const netuid = input.netuid ?? (input.asset === "sn80" ? SN80_NETUID : void 0);
856
+ const assetNetuid = input.asset === "sn10" ? SN10_NETUID : input.asset === "sn80" ? SN80_NETUID : void 0;
857
+ const netuid = input.netuid ?? assetNetuid;
844
858
  if (netuid === void 0) {
845
859
  throw new ForeverMoneyError(
846
860
  "INVALID_TRANSACTION_PLAN",
@@ -848,10 +862,10 @@ function sourceNetuid(input) {
848
862
  );
849
863
  }
850
864
  assertNonNegativeAmount(netuid, "netuid");
851
- if (input.asset === "sn80" && netuid !== SN80_NETUID) {
865
+ if (assetNetuid !== void 0 && netuid !== assetNetuid) {
852
866
  throw new ForeverMoneyError(
853
867
  "INVALID_TRANSACTION_PLAN",
854
- "SN80 staking approvals must use netuid 80."
868
+ `${input.asset.toUpperCase()} staking approvals must use netuid ${assetNetuid}.`
855
869
  );
856
870
  }
857
871
  return netuid;
@@ -2072,6 +2086,41 @@ function bridgeMessageIdFromReceipt(direction, receipt) {
2072
2086
  // src/bridge/tracking.ts
2073
2087
  var ccipExecutionAbi = (0, import_viem11.parseAbi)(CCIP_EXECUTION_ABI);
2074
2088
  var alphaGatewayAbi2 = (0, import_viem11.parseAbi)(ALPHA_GATEWAY_ABI);
2089
+ var routerAbi = (0, import_viem11.parseAbi)(CCIP_ROUTER_ABI);
2090
+ var executionV2Abi = (0, import_viem11.parseAbi)([
2091
+ "event ExecutionStateChanged(uint64 indexed sourceChainSelector,uint64 indexed sequenceNumber,bytes32 indexed messageId,uint8 state,bytes returnData)"
2092
+ ]);
2093
+ var rampCache = /* @__PURE__ */ new WeakMap();
2094
+ function deliveryOffRamps(provider, client, router, selector, legacy) {
2095
+ let cache = rampCache.get(provider);
2096
+ if (!cache) {
2097
+ cache = /* @__PURE__ */ new Map();
2098
+ rampCache.set(provider, cache);
2099
+ }
2100
+ const key = `${router}:${selector}:${legacy}`;
2101
+ const cached = cache.get(key);
2102
+ if (cached && cached.expires > Date.now()) return cached.request;
2103
+ const request = client.readContract({
2104
+ address: router,
2105
+ abi: routerAbi,
2106
+ functionName: "getOffRamps"
2107
+ }).then((ramps) => {
2108
+ const addresses = /* @__PURE__ */ new Map([
2109
+ [legacy.toLowerCase(), legacy]
2110
+ ]);
2111
+ for (const ramp of ramps) {
2112
+ if (ramp.sourceChainSelector === selector)
2113
+ addresses.set(ramp.offRamp.toLowerCase(), ramp.offRamp);
2114
+ }
2115
+ return [...addresses.values()];
2116
+ });
2117
+ const entry = { expires: Date.now() + 3e5, request };
2118
+ cache.set(key, entry);
2119
+ void request.catch(() => {
2120
+ if (cache.get(key) === entry) cache.delete(key);
2121
+ });
2122
+ return request;
2123
+ }
2075
2124
  function destinationChainId(direction) {
2076
2125
  assertBridgeDirection(direction);
2077
2126
  const evm = getForeverMoneyEvmDeployment(
@@ -2185,13 +2234,29 @@ async function getCcipDeliveryStatus(provider, input) {
2185
2234
  const evmToSubtensor = isEvmToSubtensorDirection(input.direction);
2186
2235
  const sourceSelector = evmToSubtensor ? evm.ccipSelector : foreverMoneyDeployment.subtensor.ccipSelector;
2187
2236
  const offRamp = evmToSubtensor ? evmChain === "base" ? foreverMoneyDeployment.subtensor.contracts.ccipOffRampFromBase : foreverMoneyDeployment.subtensor.contracts.ccipOffRampFromRobinhood : evm.contracts.ccipOffRampFromSubtensor;
2188
- const logs = await client.getLogs({
2189
- address: offRamp,
2190
- fromBlock: BigInt(input.fromBlock),
2191
- toBlock: "latest",
2192
- event: ccipExecutionAbi[0],
2193
- args: { sourceChainSelector: sourceSelector, messageId },
2194
- strict: true
2237
+ const offRamps = await deliveryOffRamps(
2238
+ provider,
2239
+ client,
2240
+ evmToSubtensor ? foreverMoneyDeployment.subtensor.contracts.ccipRouter : evm.contracts.ccipRouter,
2241
+ sourceSelector,
2242
+ offRamp
2243
+ );
2244
+ const batches = await Promise.all(
2245
+ [executionV2Abi[0], ccipExecutionAbi[0]].map(
2246
+ (event) => client.getLogs({
2247
+ address: offRamps.length === 1 ? offRamps[0] : [...offRamps],
2248
+ fromBlock: BigInt(input.fromBlock),
2249
+ toBlock: "latest",
2250
+ event,
2251
+ args: { sourceChainSelector: sourceSelector, messageId },
2252
+ strict: true
2253
+ })
2254
+ )
2255
+ );
2256
+ const logs = batches.flat().sort((a, b) => {
2257
+ const blockA = a.blockNumber ?? 0n;
2258
+ const blockB = b.blockNumber ?? 0n;
2259
+ return blockA === blockB ? (a.logIndex ?? 0) - (b.logIndex ?? 0) : blockA < blockB ? -1 : 1;
2195
2260
  });
2196
2261
  const latest = logs.at(-1);
2197
2262
  if (latest === void 0) return "waiting";