@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 +15 -0
- package/README.md +15 -0
- package/dist/index.cjs +86 -21
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +9 -5
- package/dist/index.d.ts +9 -5
- package/dist/index.js +86 -21
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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 "
|
|
829
|
+
'Asset must be "tao", "sn80", or "sn10".'
|
|
818
830
|
);
|
|
819
831
|
}
|
|
820
832
|
const evm = getForeverMoneyEvmDeployment(evmChain);
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
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
|
-
|
|
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
|
|
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 (
|
|
865
|
+
if (assetNetuid !== void 0 && netuid !== assetNetuid) {
|
|
852
866
|
throw new ForeverMoneyError(
|
|
853
867
|
"INVALID_TRANSACTION_PLAN",
|
|
854
|
-
|
|
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
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
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";
|