@forevermoney/sdk 0.2.0 → 0.5.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/CHANGELOG.md +61 -0
- package/README.md +119 -0
- package/dist/index.cjs +452 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +59 -6
- package/dist/index.d.ts +59 -6
- package/dist/index.js +452 -87
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -27,12 +27,16 @@ __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,
|
|
31
|
+
MAX_STAKE_PULLS: () => MAX_STAKE_PULLS,
|
|
30
32
|
MIN_LIQUID_BASE_TO_SUBTENSOR_WEI: () => MIN_LIQUID_BASE_TO_SUBTENSOR_WEI,
|
|
31
33
|
MIN_LIQUID_EVM_TO_SUBTENSOR_WEI: () => MIN_LIQUID_EVM_TO_SUBTENSOR_WEI,
|
|
34
|
+
MIN_LIQUID_SUBTENSOR_TO_EVM_WEI: () => MIN_LIQUID_SUBTENSOR_TO_EVM_WEI,
|
|
32
35
|
NETWORK_FEE_BUFFER_BPS: () => NETWORK_FEE_BUFFER_BPS,
|
|
33
36
|
RAO_PER_TAO: () => RAO_PER_TAO,
|
|
34
37
|
ROBINHOOD_CCIP_SELECTOR: () => ROBINHOOD_CCIP_SELECTOR,
|
|
35
38
|
ROBINHOOD_CHAIN_ID: () => ROBINHOOD_CHAIN_ID,
|
|
39
|
+
SN80_NETUID: () => SN80_NETUID,
|
|
36
40
|
SUBTENSOR_CCIP_SELECTOR: () => SUBTENSOR_CCIP_SELECTOR,
|
|
37
41
|
SUBTENSOR_CHAIN_ID: () => SUBTENSOR_CHAIN_ID,
|
|
38
42
|
assertWholeRao: () => assertWholeRao,
|
|
@@ -65,6 +69,8 @@ __export(index_exports, {
|
|
|
65
69
|
normalizeEvmAddress: () => normalizeEvmAddress,
|
|
66
70
|
normalizeSS58: () => normalizeSS58,
|
|
67
71
|
parseTaoAmount: () => parseTaoAmount,
|
|
72
|
+
partnerFeeCut: () => partnerFeeCut,
|
|
73
|
+
partnerFeeTaoTopUp: () => partnerFeeTaoTopUp,
|
|
68
74
|
sourceChainId: () => sourceChainId,
|
|
69
75
|
ss58ToPublicKey: () => ss58ToPublicKey,
|
|
70
76
|
toEip1193Transaction: () => toEip1193Transaction,
|
|
@@ -156,7 +162,8 @@ var ROBINHOOD_CCIP_SELECTOR = 6180753054346818345n;
|
|
|
156
162
|
var SUBTENSOR_CCIP_SELECTOR = 2135107236357186872n;
|
|
157
163
|
var RAO_PER_TAO = 1000000000n;
|
|
158
164
|
var EVM_WEI_PER_RAO = 1000000000n;
|
|
159
|
-
var
|
|
165
|
+
var SN80_NETUID = 80n;
|
|
166
|
+
var FOREVERMONEY_DEPLOYMENT_VERSION = "1.3.0";
|
|
160
167
|
var foreverMoneyDeployment = Object.freeze({
|
|
161
168
|
version: FOREVERMONEY_DEPLOYMENT_VERSION,
|
|
162
169
|
base: Object.freeze({
|
|
@@ -165,10 +172,18 @@ var foreverMoneyDeployment = Object.freeze({
|
|
|
165
172
|
chainId: BASE_CHAIN_ID,
|
|
166
173
|
ccipSelector: BASE_CCIP_SELECTOR,
|
|
167
174
|
contracts: Object.freeze({
|
|
168
|
-
gateway: (0, import_viem2.getAddress)("
|
|
175
|
+
gateway: (0, import_viem2.getAddress)("0x1da2415229b614C787e145D1D7346eb496319C52"),
|
|
176
|
+
// Retired gateways, oldest first; kept only to track transfers sent
|
|
177
|
+
// through them.
|
|
178
|
+
legacyGateways: [
|
|
179
|
+
(0, import_viem2.getAddress)("0x5EF3d7D19e4b233a1A169DA0d5CB02ec6b160a2C")
|
|
180
|
+
],
|
|
169
181
|
wrappedTao: (0, import_viem2.getAddress)(
|
|
170
182
|
"0xf3081494b87e8d5fb7960f066e931d1d0e6e3d67"
|
|
171
183
|
),
|
|
184
|
+
wrappedSn80: (0, import_viem2.getAddress)(
|
|
185
|
+
"0x6f63d869011f95274498023b4abfc00b30c34378"
|
|
186
|
+
),
|
|
172
187
|
ccipRouter: (0, import_viem2.getAddress)(
|
|
173
188
|
"0x881e3A65B4d4a04dD529061dd0071cf975F58bCD"
|
|
174
189
|
),
|
|
@@ -194,7 +209,10 @@ var foreverMoneyDeployment = Object.freeze({
|
|
|
194
209
|
chainId: ROBINHOOD_CHAIN_ID,
|
|
195
210
|
ccipSelector: ROBINHOOD_CCIP_SELECTOR,
|
|
196
211
|
contracts: Object.freeze({
|
|
197
|
-
gateway: (0, import_viem2.getAddress)("
|
|
212
|
+
gateway: (0, import_viem2.getAddress)("0xf27fdA637131E25B2A1b4865ED9597d881980c7E"),
|
|
213
|
+
legacyGateways: [
|
|
214
|
+
(0, import_viem2.getAddress)("0x53Dcc4FE04193e489BE537F722F65317DB1E65d8")
|
|
215
|
+
],
|
|
198
216
|
wrappedTao: (0, import_viem2.getAddress)(
|
|
199
217
|
"0xf3081494B87e8D5fb7960f066E931D1D0e6E3d67"
|
|
200
218
|
),
|
|
@@ -210,13 +228,23 @@ var foreverMoneyDeployment = Object.freeze({
|
|
|
210
228
|
chainId: SUBTENSOR_CHAIN_ID,
|
|
211
229
|
ccipSelector: SUBTENSOR_CCIP_SELECTOR,
|
|
212
230
|
contracts: Object.freeze({
|
|
213
|
-
|
|
231
|
+
// V5.1 AlphaGateway: multi-validator staked input (tao-bridge 485c898).
|
|
232
|
+
gateway: (0, import_viem2.getAddress)("0xd5Fa238aa4177f6c1341491969d9cBeec94EEd69"),
|
|
233
|
+
// V4 and V5 hubs. The spokes still deliver to the V5 hub (their hub
|
|
234
|
+
// pointer is immutable), so it stays here for delivery tracking.
|
|
235
|
+
legacyGateways: [
|
|
236
|
+
(0, import_viem2.getAddress)("0x998f20Fea90bF7792774dECc7f994716442B1705"),
|
|
237
|
+
(0, import_viem2.getAddress)("0xcd0C6d98D0A126B1c113d15b4c28F38321437787")
|
|
238
|
+
],
|
|
214
239
|
alphaVault: (0, import_viem2.getAddress)(
|
|
215
240
|
"0x11837459896D96F821a8D88eC93a3C8D152033D4"
|
|
216
241
|
),
|
|
217
242
|
wrappedTao: (0, import_viem2.getAddress)(
|
|
218
243
|
"0xC5b6C1632d34901239396F5E1BDe54B342900256"
|
|
219
244
|
),
|
|
245
|
+
wrappedSn80: (0, import_viem2.getAddress)(
|
|
246
|
+
"0xfD628dE75EF96f0A5C59659159C6cA81E0DC2222"
|
|
247
|
+
),
|
|
220
248
|
ccipRouter: (0, import_viem2.getAddress)(
|
|
221
249
|
"0xD941fBEcD2b971d0F54b4C34286C95faB52B60B8"
|
|
222
250
|
),
|
|
@@ -397,23 +425,39 @@ var SPOKE_GATEWAY_ABI = Object.freeze([
|
|
|
397
425
|
"function ROUTER() view returns (address)",
|
|
398
426
|
"function BITTENSOR_SELECTOR() view returns (uint64)",
|
|
399
427
|
"function SUBTENSOR_GATEWAY() view returns (address)",
|
|
428
|
+
"function maxIntegratorFeeBps() view returns (uint16)",
|
|
429
|
+
"function bridgeFeeBps() view returns (uint16)",
|
|
400
430
|
"function quoteBridgeToFinney(address token,uint256 amount,(bytes32 ss58,address evmFallback,bool wantLiquid,uint256 minTaoOut) exit) view returns (uint256 fee)",
|
|
401
431
|
"function bridgeToFinney(address token,uint256 amount,(bytes32 ss58,address evmFallback,bool wantLiquid,uint256 minTaoOut) exit) payable returns (bytes32 messageId)",
|
|
432
|
+
"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)",
|
|
433
|
+
"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)",
|
|
402
434
|
"event BridgedToFinney(address indexed token,address indexed sender,bytes32 indexed ss58,uint256 amount,bytes32 messageId)"
|
|
403
435
|
]);
|
|
404
436
|
var ALPHA_GATEWAY_ABI = Object.freeze([
|
|
405
437
|
"function ROUTER() view returns (address)",
|
|
406
438
|
"function allowedLane(uint64 selector) view returns (bool)",
|
|
439
|
+
"function maxIntegratorFeeBps() view returns (uint16)",
|
|
440
|
+
"function bridgeFeeBps() view returns (uint16)",
|
|
441
|
+
"function integratorTaoTopUp(uint256 taoAmount,uint16 bps) pure returns (uint256)",
|
|
407
442
|
"function quoteBridgeOut(uint64 destSelector,address token,address recipient,uint256 mintedAmount) view returns (uint256 fee)",
|
|
408
443
|
"function bridgeOut(uint64 destSelector,address token,address recipient,uint256 taoAmount,uint256 stakedAlphaRao,uint256 minTokenOut) payable returns (bytes32 messageId)",
|
|
444
|
+
"function integratorCut(uint256 amount,uint16 bps) pure returns (uint256)",
|
|
445
|
+
"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)",
|
|
446
|
+
"function bridgeOutWithFee(uint64 destSelector,address token,address recipient,uint256 taoAmount,uint256 stakedAlphaRao,uint256 minTokenOut,(address recipient,uint16 bps) integrator) payable returns (bytes32 messageId)",
|
|
447
|
+
"function bridgeOutFromValidators(uint64 destSelector,address token,address recipient,uint256 taoAmount,(bytes32 validator,uint256 alphaRao)[] sources,uint256 minTokenOut) payable returns (bytes32 messageId)",
|
|
448
|
+
"function bridgeOutFromValidatorsWithFee(uint64 destSelector,address token,address recipient,uint256 taoAmount,(bytes32 validator,uint256 alphaRao)[] sources,uint256 minTokenOut,(address recipient,uint16 bps) integrator) payable returns (bytes32 messageId)",
|
|
449
|
+
"function minStakeRequired() view returns (uint256)",
|
|
450
|
+
"function MAX_STAKE_SOURCES() view returns (uint256)",
|
|
451
|
+
"function GATEWAY_COLDKEY() view returns (bytes32)",
|
|
409
452
|
"function claimLiquid(address token,uint256 minTaoOut,address to)",
|
|
410
453
|
"function claimNative(address to)",
|
|
411
454
|
"function claimStaked(address token,bytes32 destColdkey,address to)",
|
|
412
455
|
"function claimToken(address token,address to)",
|
|
413
456
|
"function claimableNative(address account) view returns (uint256)",
|
|
414
|
-
"function claimableToken(address
|
|
457
|
+
"function claimableToken(address token,address account) view returns (uint256)",
|
|
415
458
|
"event BridgedOut(uint64 destChainSelector,address indexed token,address indexed sender,address indexed recipient,uint256 minted,bytes32 messageId)",
|
|
416
459
|
"event Claimable(address indexed token,address indexed user,uint256 native,uint256 wsn)",
|
|
460
|
+
"event NotDelivered(uint64 indexed sourceChainSelector,address indexed token,uint256 amount,uint8 reason)",
|
|
417
461
|
"event DeliveredLiquid(address indexed token,bytes32 indexed ss58,uint256 taoOut)",
|
|
418
462
|
"event DeliveredStaked(address indexed token,bytes32 indexed ss58,uint256 alphaRao)"
|
|
419
463
|
]);
|
|
@@ -571,6 +615,112 @@ var alphaAbi = (0, import_viem6.parseAbi)(ALPHA_GATEWAY_ABI);
|
|
|
571
615
|
var stakingAbi = (0, import_viem6.parseAbi)(STAKING_ABI);
|
|
572
616
|
var MIN_LIQUID_EVM_TO_SUBTENSOR_WEI = 10000000000000000n;
|
|
573
617
|
var MIN_LIQUID_BASE_TO_SUBTENSOR_WEI = MIN_LIQUID_EVM_TO_SUBTENSOR_WEI;
|
|
618
|
+
var MIN_LIQUID_SUBTENSOR_TO_EVM_WEI = 2000000n * EVM_WEI_PER_RAO;
|
|
619
|
+
var MAX_PARTNER_FEE_BPS = 1e4;
|
|
620
|
+
var MAX_STAKE_PULLS = 16;
|
|
621
|
+
var NO_PARTNER_FEE = {
|
|
622
|
+
recipient: "0x0000000000000000000000000000000000000000",
|
|
623
|
+
bps: 0
|
|
624
|
+
};
|
|
625
|
+
function resolvePartnerFee(fee, gateway) {
|
|
626
|
+
if (fee === void 0) return NO_PARTNER_FEE;
|
|
627
|
+
if (typeof fee.bps !== "number" || !Number.isInteger(fee.bps) || fee.bps < 0 || fee.bps > MAX_PARTNER_FEE_BPS) {
|
|
628
|
+
throw new ForeverMoneyError(
|
|
629
|
+
"INVALID_PARTNER_FEE",
|
|
630
|
+
`Partner fee bps must be an integer between 0 and ${MAX_PARTNER_FEE_BPS}.`,
|
|
631
|
+
{ bps: fee.bps }
|
|
632
|
+
);
|
|
633
|
+
}
|
|
634
|
+
if (fee.bps === 0) return NO_PARTNER_FEE;
|
|
635
|
+
const recipient = normalizeEvmAddress(fee.recipient);
|
|
636
|
+
if (recipient === NO_PARTNER_FEE.recipient || recipient.toLowerCase() === gateway.toLowerCase()) {
|
|
637
|
+
throw new ForeverMoneyError(
|
|
638
|
+
"INVALID_PARTNER_FEE",
|
|
639
|
+
"Partner fee recipient must not be the zero address or the gateway.",
|
|
640
|
+
{ recipient }
|
|
641
|
+
);
|
|
642
|
+
}
|
|
643
|
+
return { recipient, bps: fee.bps };
|
|
644
|
+
}
|
|
645
|
+
function partnerFeeCut(amount, bps) {
|
|
646
|
+
return amount * BigInt(bps) / 10000n;
|
|
647
|
+
}
|
|
648
|
+
function partnerFeeTaoTopUp(taoAmount, bps) {
|
|
649
|
+
const raw = partnerFeeCut(taoAmount, bps);
|
|
650
|
+
if (raw === 0n) return 0n;
|
|
651
|
+
return (raw + EVM_WEI_PER_RAO - 1n) / EVM_WEI_PER_RAO * EVM_WEI_PER_RAO;
|
|
652
|
+
}
|
|
653
|
+
function assertPartnerFeeAllowed(fee, maxBps) {
|
|
654
|
+
if (fee.bps > maxBps) {
|
|
655
|
+
throw new ForeverMoneyError(
|
|
656
|
+
"INVALID_PARTNER_FEE",
|
|
657
|
+
`Partner fee of ${fee.bps} bps exceeds the gateway maximum of ${maxBps} bps.`,
|
|
658
|
+
{ bps: fee.bps, maxBps }
|
|
659
|
+
);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
function bridgeAsset(evmChain, asset = "tao") {
|
|
663
|
+
if (asset !== "tao" && asset !== "sn80") {
|
|
664
|
+
throw new ForeverMoneyError(
|
|
665
|
+
"INVALID_TRANSACTION_PLAN",
|
|
666
|
+
'Asset must be "tao" or "sn80".'
|
|
667
|
+
);
|
|
668
|
+
}
|
|
669
|
+
if (asset === "sn80" && evmChain !== "base") {
|
|
670
|
+
throw new ForeverMoneyError(
|
|
671
|
+
"INVALID_TRANSACTION_PLAN",
|
|
672
|
+
"SN80 bridging is supported between Base and Subtensor."
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
const evm = getForeverMoneyEvmDeployment(evmChain);
|
|
676
|
+
return asset === "sn80" ? {
|
|
677
|
+
evmToken: foreverMoneyDeployment.base.contracts.wrappedSn80,
|
|
678
|
+
subtensorToken: foreverMoneyDeployment.subtensor.contracts.wrappedSn80,
|
|
679
|
+
label: "SN80",
|
|
680
|
+
wrappedLabel: "SN80"
|
|
681
|
+
} : {
|
|
682
|
+
evmToken: evm.contracts.wrappedTao,
|
|
683
|
+
subtensorToken: foreverMoneyDeployment.subtensor.contracts.wrappedTao,
|
|
684
|
+
label: "TAO",
|
|
685
|
+
wrappedLabel: "wrapped TAO"
|
|
686
|
+
};
|
|
687
|
+
}
|
|
688
|
+
function assertAssetMode(asset, mode) {
|
|
689
|
+
if (asset === "sn80" && mode !== "staked") {
|
|
690
|
+
throw new ForeverMoneyError(
|
|
691
|
+
"INVALID_TRANSACTION_PLAN",
|
|
692
|
+
"SN80 bridging requires staked subnet 80 input or delivery; liquid TAO conversion is not supported."
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
function sourceNetuid(input) {
|
|
697
|
+
if (input.source !== "staked") return void 0;
|
|
698
|
+
const netuid = input.netuid ?? (input.asset === "sn80" ? SN80_NETUID : void 0);
|
|
699
|
+
if (netuid === void 0) {
|
|
700
|
+
throw new ForeverMoneyError(
|
|
701
|
+
"INVALID_TRANSACTION_PLAN",
|
|
702
|
+
"A netuid is required when bridging staked TAO."
|
|
703
|
+
);
|
|
704
|
+
}
|
|
705
|
+
assertNonNegativeAmount(netuid, "netuid");
|
|
706
|
+
if (input.asset === "sn80" && netuid !== SN80_NETUID) {
|
|
707
|
+
throw new ForeverMoneyError(
|
|
708
|
+
"INVALID_TRANSACTION_PLAN",
|
|
709
|
+
"SN80 staking approvals must use netuid 80."
|
|
710
|
+
);
|
|
711
|
+
}
|
|
712
|
+
return netuid;
|
|
713
|
+
}
|
|
714
|
+
function stakePullsFor(input, amountRao) {
|
|
715
|
+
if (input.stakePulls === void 0) return void 0;
|
|
716
|
+
if (input.source !== "staked") {
|
|
717
|
+
throw new ForeverMoneyError(
|
|
718
|
+
"INVALID_TRANSACTION_PLAN",
|
|
719
|
+
"Stake pulls are only valid for a staked source."
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
return resolveStakePulls(input.stakePulls, amountRao);
|
|
723
|
+
}
|
|
574
724
|
function assertDelivery(value) {
|
|
575
725
|
if (value !== "liquid" && value !== "staked") {
|
|
576
726
|
throw new ForeverMoneyError(
|
|
@@ -600,6 +750,19 @@ function assertBaseToSubtensorAmount(amountWei, delivery) {
|
|
|
600
750
|
);
|
|
601
751
|
}
|
|
602
752
|
}
|
|
753
|
+
function assertSubtensorToEvmAmount(amountWei, source) {
|
|
754
|
+
assertWholeRao(amountWei);
|
|
755
|
+
if (source === "liquid" && amountWei < MIN_LIQUID_SUBTENSOR_TO_EVM_WEI) {
|
|
756
|
+
throw new ForeverMoneyError(
|
|
757
|
+
"AMOUNT_BELOW_MINIMUM",
|
|
758
|
+
"Bridging liquid TAO from Subtensor requires at least 0.002 TAO.",
|
|
759
|
+
{
|
|
760
|
+
amountWei: amountWei.toString(),
|
|
761
|
+
minimumAmountWei: MIN_LIQUID_SUBTENSOR_TO_EVM_WEI.toString()
|
|
762
|
+
}
|
|
763
|
+
);
|
|
764
|
+
}
|
|
765
|
+
}
|
|
603
766
|
function transactionStep(kind, label, chainId, from, to, data, value, gasLimit) {
|
|
604
767
|
return {
|
|
605
768
|
kind,
|
|
@@ -614,9 +777,159 @@ function transactionStep(kind, label, chainId, from, to, data, value, gasLimit)
|
|
|
614
777
|
}
|
|
615
778
|
};
|
|
616
779
|
}
|
|
780
|
+
function partnerFeeSummary(charged, unit, fee) {
|
|
781
|
+
if (fee.bps === 0) return "";
|
|
782
|
+
return ` Partner fee: ${charged} wei of ${unit} (${fee.bps} bps) on top, paid to ${fee.recipient}.`;
|
|
783
|
+
}
|
|
784
|
+
function encodeSpokeBridge(token, amountWei, exit, fee) {
|
|
785
|
+
return fee.bps === 0 ? (0, import_viem6.encodeFunctionData)({
|
|
786
|
+
abi: spokeAbi,
|
|
787
|
+
functionName: "bridgeToFinney",
|
|
788
|
+
args: [token, amountWei, exit]
|
|
789
|
+
}) : (0, import_viem6.encodeFunctionData)({
|
|
790
|
+
abi: spokeAbi,
|
|
791
|
+
functionName: "bridgeToFinneyWithFee",
|
|
792
|
+
args: [token, amountWei, exit, 0n, fee]
|
|
793
|
+
});
|
|
794
|
+
}
|
|
795
|
+
function resolveStakePulls(pulls, amountRao) {
|
|
796
|
+
if (pulls.length === 0 || pulls.length > MAX_STAKE_PULLS) {
|
|
797
|
+
throw new ForeverMoneyError(
|
|
798
|
+
"INVALID_TRANSACTION_PLAN",
|
|
799
|
+
`Between 1 and ${MAX_STAKE_PULLS} stake pulls are required.`,
|
|
800
|
+
{ count: pulls.length }
|
|
801
|
+
);
|
|
802
|
+
}
|
|
803
|
+
const seen = /* @__PURE__ */ new Set();
|
|
804
|
+
let total = 0n;
|
|
805
|
+
const resolved = pulls.map((pull) => {
|
|
806
|
+
const validator = normalizeBytes32(pull.hotkey, "Stake pull hotkey");
|
|
807
|
+
if (validator === `0x${"0".repeat(64)}`) {
|
|
808
|
+
throw new ForeverMoneyError(
|
|
809
|
+
"INVALID_TRANSACTION_PLAN",
|
|
810
|
+
"Stake pull hotkey must not be zero."
|
|
811
|
+
);
|
|
812
|
+
}
|
|
813
|
+
const key = validator.toLowerCase();
|
|
814
|
+
if (seen.has(key)) {
|
|
815
|
+
throw new ForeverMoneyError(
|
|
816
|
+
"INVALID_TRANSACTION_PLAN",
|
|
817
|
+
"Stake pulls must not repeat a hotkey.",
|
|
818
|
+
{ hotkey: validator }
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
seen.add(key);
|
|
822
|
+
if (typeof pull.amountRao !== "bigint" || pull.amountRao <= 0n) {
|
|
823
|
+
throw new ForeverMoneyError(
|
|
824
|
+
"INVALID_TRANSACTION_PLAN",
|
|
825
|
+
"Each stake pull must take a positive amount of RAO.",
|
|
826
|
+
{ hotkey: validator }
|
|
827
|
+
);
|
|
828
|
+
}
|
|
829
|
+
total += pull.amountRao;
|
|
830
|
+
return { validator, alphaRao: pull.amountRao };
|
|
831
|
+
});
|
|
832
|
+
if (total !== amountRao) {
|
|
833
|
+
throw new ForeverMoneyError(
|
|
834
|
+
"INVALID_TRANSACTION_PLAN",
|
|
835
|
+
"Stake pulls must sum to the bridged amount.",
|
|
836
|
+
{ pulledRao: total.toString(), amountRao: amountRao.toString() }
|
|
837
|
+
);
|
|
838
|
+
}
|
|
839
|
+
return resolved;
|
|
840
|
+
}
|
|
841
|
+
function encodeHubBridge(destSelector, token, recipient, taoAmount, stakedAlphaRao, minTokenOut, fee, pulls) {
|
|
842
|
+
if (pulls !== void 0) {
|
|
843
|
+
const args2 = [
|
|
844
|
+
destSelector,
|
|
845
|
+
token,
|
|
846
|
+
recipient,
|
|
847
|
+
taoAmount,
|
|
848
|
+
pulls,
|
|
849
|
+
minTokenOut
|
|
850
|
+
];
|
|
851
|
+
return fee.bps === 0 ? (0, import_viem6.encodeFunctionData)({
|
|
852
|
+
abi: alphaAbi,
|
|
853
|
+
functionName: "bridgeOutFromValidators",
|
|
854
|
+
args: args2
|
|
855
|
+
}) : (0, import_viem6.encodeFunctionData)({
|
|
856
|
+
abi: alphaAbi,
|
|
857
|
+
functionName: "bridgeOutFromValidatorsWithFee",
|
|
858
|
+
args: [...args2, fee]
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
const args = [
|
|
862
|
+
destSelector,
|
|
863
|
+
token,
|
|
864
|
+
recipient,
|
|
865
|
+
taoAmount,
|
|
866
|
+
stakedAlphaRao,
|
|
867
|
+
minTokenOut
|
|
868
|
+
];
|
|
869
|
+
return fee.bps === 0 ? (0, import_viem6.encodeFunctionData)({
|
|
870
|
+
abi: alphaAbi,
|
|
871
|
+
functionName: "bridgeOut",
|
|
872
|
+
args
|
|
873
|
+
}) : (0, import_viem6.encodeFunctionData)({
|
|
874
|
+
abi: alphaAbi,
|
|
875
|
+
functionName: "bridgeOutWithFee",
|
|
876
|
+
args: [...args, fee]
|
|
877
|
+
});
|
|
878
|
+
}
|
|
879
|
+
async function quoteSpokeWithFee(provider, gateway, token, amountWei, exit, fee) {
|
|
880
|
+
const maxBps = await provider.readContract({
|
|
881
|
+
address: gateway,
|
|
882
|
+
abi: spokeAbi,
|
|
883
|
+
functionName: "maxIntegratorFeeBps"
|
|
884
|
+
});
|
|
885
|
+
assertPartnerFeeAllowed(fee, maxBps);
|
|
886
|
+
const [networkFee] = await provider.readContract({
|
|
887
|
+
address: gateway,
|
|
888
|
+
abi: spokeAbi,
|
|
889
|
+
functionName: "quoteBridgeToFinneyWithFee",
|
|
890
|
+
args: [token, amountWei, exit, 0n, fee]
|
|
891
|
+
});
|
|
892
|
+
return networkFee;
|
|
893
|
+
}
|
|
894
|
+
async function quoteHubWithFee(provider, gateway, destSelector, token, recipient, mintedAmount, taoAmount, stakedAlphaRao, fee) {
|
|
895
|
+
const maxBps = await provider.readContract({
|
|
896
|
+
address: gateway,
|
|
897
|
+
abi: alphaAbi,
|
|
898
|
+
functionName: "maxIntegratorFeeBps"
|
|
899
|
+
});
|
|
900
|
+
assertPartnerFeeAllowed(fee, maxBps);
|
|
901
|
+
const [networkFee, taoTopUp, alphaTopUpRao, crossing] = await provider.readContract({
|
|
902
|
+
address: gateway,
|
|
903
|
+
abi: alphaAbi,
|
|
904
|
+
functionName: "quoteBridgeOutWithFee",
|
|
905
|
+
args: [
|
|
906
|
+
destSelector,
|
|
907
|
+
token,
|
|
908
|
+
recipient,
|
|
909
|
+
mintedAmount,
|
|
910
|
+
taoAmount,
|
|
911
|
+
stakedAlphaRao,
|
|
912
|
+
fee
|
|
913
|
+
]
|
|
914
|
+
});
|
|
915
|
+
if (crossing !== mintedAmount || taoTopUp !== partnerFeeTaoTopUp(taoAmount, fee.bps) || alphaTopUpRao !== partnerFeeCut(stakedAlphaRao, fee.bps)) {
|
|
916
|
+
throw new ForeverMoneyError(
|
|
917
|
+
"INVALID_PROVIDER_RESPONSE",
|
|
918
|
+
"The gateway partner fee quote does not match the SDK fee calculation.",
|
|
919
|
+
{
|
|
920
|
+
crossing: crossing.toString(),
|
|
921
|
+
taoTopUp: taoTopUp.toString(),
|
|
922
|
+
alphaTopUpRao: alphaTopUpRao.toString()
|
|
923
|
+
}
|
|
924
|
+
);
|
|
925
|
+
}
|
|
926
|
+
return { networkFee, taoTopUp, alphaTopUpRao };
|
|
927
|
+
}
|
|
617
928
|
function buildEvmToSubtensorPlan(input) {
|
|
618
929
|
const sender = normalizeEvmAddress(input.sender);
|
|
930
|
+
const asset = bridgeAsset(input.evmChain, input.asset);
|
|
619
931
|
assertDelivery(input.delivery);
|
|
932
|
+
assertAssetMode(input.asset, input.delivery);
|
|
620
933
|
assertBaseToSubtensorAmount(input.amountWei, input.delivery);
|
|
621
934
|
assertNonNegativeAmount(input.allowanceWei, "Token allowance");
|
|
622
935
|
assertNonNegativeAmount(input.exactNetworkFeeWei, "Network fee");
|
|
@@ -628,19 +941,24 @@ function buildEvmToSubtensorPlan(input) {
|
|
|
628
941
|
minTaoOut: input.amountWei
|
|
629
942
|
};
|
|
630
943
|
const evm = getForeverMoneyEvmDeployment(input.evmChain);
|
|
944
|
+
const partnerFee = resolvePartnerFee(
|
|
945
|
+
input.partnerFee,
|
|
946
|
+
evm.contracts.gateway
|
|
947
|
+
);
|
|
948
|
+
const cut = partnerFeeCut(input.amountWei, partnerFee.bps);
|
|
631
949
|
const steps = [];
|
|
632
|
-
if (input.allowanceWei < input.amountWei) {
|
|
950
|
+
if (input.allowanceWei < input.amountWei + cut) {
|
|
633
951
|
steps.push(
|
|
634
952
|
transactionStep(
|
|
635
953
|
"approval",
|
|
636
|
-
|
|
954
|
+
`Approve ${asset.wrappedLabel} for the ForeverMoney gateway`,
|
|
637
955
|
evm.chainId,
|
|
638
956
|
sender,
|
|
639
|
-
|
|
957
|
+
asset.evmToken,
|
|
640
958
|
(0, import_viem6.encodeFunctionData)({
|
|
641
959
|
abi: erc20Abi,
|
|
642
960
|
functionName: "approve",
|
|
643
|
-
args: [evm.contracts.gateway, input.amountWei]
|
|
961
|
+
args: [evm.contracts.gateway, input.amountWei + cut]
|
|
644
962
|
}),
|
|
645
963
|
0n
|
|
646
964
|
)
|
|
@@ -650,22 +968,23 @@ function buildEvmToSubtensorPlan(input) {
|
|
|
650
968
|
steps.push(
|
|
651
969
|
transactionStep(
|
|
652
970
|
"transaction",
|
|
653
|
-
`Bridge
|
|
971
|
+
`Bridge ${asset.wrappedLabel} from ${evm.name} to Subtensor (${input.delivery})`,
|
|
654
972
|
evm.chainId,
|
|
655
973
|
sender,
|
|
656
974
|
evm.contracts.gateway,
|
|
657
|
-
(
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
975
|
+
encodeSpokeBridge(
|
|
976
|
+
asset.evmToken,
|
|
977
|
+
input.amountWei,
|
|
978
|
+
exit,
|
|
979
|
+
partnerFee
|
|
980
|
+
),
|
|
662
981
|
value,
|
|
663
982
|
input.estimatedBridgeGas === void 0 ? void 0 : gasLimitWithBuffer(input.estimatedBridgeGas)
|
|
664
983
|
)
|
|
665
984
|
);
|
|
666
985
|
return createTransactionPlan({
|
|
667
986
|
action: evm.key === "base" ? "bridge.base-to-subtensor" : "bridge.robinhood-to-subtensor",
|
|
668
|
-
summary: `Bridge ${input.amountWei} wei of
|
|
987
|
+
summary: `Bridge ${input.amountWei} wei of ${asset.wrappedLabel} from ${evm.name} to ${destination}.${partnerFeeSummary(cut, asset.wrappedLabel, partnerFee)}`,
|
|
669
988
|
steps
|
|
670
989
|
});
|
|
671
990
|
}
|
|
@@ -675,21 +994,23 @@ function buildBaseToSubtensorPlan(input) {
|
|
|
675
994
|
function buildSubtensorToEvmPlan(input) {
|
|
676
995
|
const sender = normalizeEvmAddress(input.sender);
|
|
677
996
|
const recipient = normalizeEvmAddress(input.recipient);
|
|
997
|
+
const asset = bridgeAsset(input.evmChain, input.asset);
|
|
678
998
|
assertSource(input.source);
|
|
679
|
-
|
|
999
|
+
assertAssetMode(input.asset, input.source);
|
|
1000
|
+
const netuid = sourceNetuid(input);
|
|
1001
|
+
assertSubtensorToEvmAmount(input.amountWei, input.source);
|
|
680
1002
|
assertNonNegativeAmount(input.exactNetworkFeeWei, "Network fee");
|
|
681
1003
|
const { subtensor } = foreverMoneyDeployment;
|
|
682
1004
|
const evm = getForeverMoneyEvmDeployment(input.evmChain);
|
|
1005
|
+
const partnerFee = resolvePartnerFee(
|
|
1006
|
+
input.partnerFee,
|
|
1007
|
+
subtensor.contracts.gateway
|
|
1008
|
+
);
|
|
683
1009
|
const amountRao = input.amountWei / EVM_WEI_PER_RAO;
|
|
1010
|
+
const alphaTopUpRao = input.source === "staked" ? partnerFeeCut(amountRao, partnerFee.bps) : 0n;
|
|
1011
|
+
const taoTopUp = input.source === "liquid" ? partnerFeeTaoTopUp(input.amountWei, partnerFee.bps) : 0n;
|
|
684
1012
|
const steps = [];
|
|
685
1013
|
if (input.source === "staked") {
|
|
686
|
-
if (input.netuid === void 0) {
|
|
687
|
-
throw new ForeverMoneyError(
|
|
688
|
-
"INVALID_TRANSACTION_PLAN",
|
|
689
|
-
"A netuid is required when bridging staked TAO."
|
|
690
|
-
);
|
|
691
|
-
}
|
|
692
|
-
assertNonNegativeAmount(input.netuid, "netuid");
|
|
693
1014
|
if (input.stakingAllowanceRao === void 0) {
|
|
694
1015
|
throw new ForeverMoneyError(
|
|
695
1016
|
"INVALID_TRANSACTION_PLAN",
|
|
@@ -697,11 +1018,11 @@ function buildSubtensorToEvmPlan(input) {
|
|
|
697
1018
|
);
|
|
698
1019
|
}
|
|
699
1020
|
assertNonNegativeAmount(input.stakingAllowanceRao, "Staking allowance");
|
|
700
|
-
if (input.stakingAllowanceRao < amountRao) {
|
|
1021
|
+
if (input.stakingAllowanceRao < amountRao + alphaTopUpRao) {
|
|
701
1022
|
steps.push(
|
|
702
1023
|
transactionStep(
|
|
703
1024
|
"approval",
|
|
704
|
-
|
|
1025
|
+
`Approve staked ${asset.label} for the ForeverMoney gateway`,
|
|
705
1026
|
subtensor.chainId,
|
|
706
1027
|
sender,
|
|
707
1028
|
subtensor.contracts.stakingPrecompile,
|
|
@@ -710,8 +1031,8 @@ function buildSubtensorToEvmPlan(input) {
|
|
|
710
1031
|
functionName: "approve",
|
|
711
1032
|
args: [
|
|
712
1033
|
subtensor.contracts.gateway,
|
|
713
|
-
|
|
714
|
-
amountRao
|
|
1034
|
+
netuid,
|
|
1035
|
+
amountRao + alphaTopUpRao
|
|
715
1036
|
]
|
|
716
1037
|
}),
|
|
717
1038
|
0n
|
|
@@ -726,34 +1047,37 @@ function buildSubtensorToEvmPlan(input) {
|
|
|
726
1047
|
}
|
|
727
1048
|
const taoAmount = input.source === "liquid" ? input.amountWei : 0n;
|
|
728
1049
|
const stakedAlphaRao = input.source === "staked" ? amountRao : 0n;
|
|
729
|
-
const
|
|
1050
|
+
const pulls = stakePullsFor(input, amountRao);
|
|
1051
|
+
const value = taoAmount + taoTopUp + feeWithBuffer(input.exactNetworkFeeWei);
|
|
730
1052
|
assertNonNegativeAmount(value, "Transaction value");
|
|
731
1053
|
steps.push(
|
|
732
1054
|
transactionStep(
|
|
733
1055
|
"transaction",
|
|
734
|
-
`Bridge ${input.source}
|
|
1056
|
+
`Bridge ${input.source} ${asset.label} from Subtensor to ${evm.name}`,
|
|
735
1057
|
subtensor.chainId,
|
|
736
1058
|
sender,
|
|
737
1059
|
subtensor.contracts.gateway,
|
|
738
|
-
(
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
]
|
|
749
|
-
}),
|
|
1060
|
+
encodeHubBridge(
|
|
1061
|
+
evm.ccipSelector,
|
|
1062
|
+
asset.subtensorToken,
|
|
1063
|
+
recipient,
|
|
1064
|
+
taoAmount,
|
|
1065
|
+
stakedAlphaRao,
|
|
1066
|
+
input.amountWei,
|
|
1067
|
+
partnerFee,
|
|
1068
|
+
pulls
|
|
1069
|
+
),
|
|
750
1070
|
value,
|
|
751
1071
|
input.estimatedBridgeGas === void 0 ? void 0 : gasLimitWithBuffer(input.estimatedBridgeGas)
|
|
752
1072
|
)
|
|
753
1073
|
);
|
|
754
1074
|
return createTransactionPlan({
|
|
755
1075
|
action: evm.key === "base" ? "bridge.subtensor-to-base" : "bridge.subtensor-to-robinhood",
|
|
756
|
-
summary: `Bridge ${input.amountWei} wei of ${input.source}
|
|
1076
|
+
summary: `Bridge ${input.amountWei} wei of ${input.source} ${asset.label} from Subtensor to ${recipient} on ${evm.name}.${partnerFeeSummary(
|
|
1077
|
+
input.source === "liquid" ? taoTopUp : alphaTopUpRao * EVM_WEI_PER_RAO,
|
|
1078
|
+
input.source === "liquid" ? "TAO" : `staked ${asset.label}`,
|
|
1079
|
+
partnerFee
|
|
1080
|
+
)}`,
|
|
757
1081
|
steps
|
|
758
1082
|
});
|
|
759
1083
|
}
|
|
@@ -762,7 +1086,9 @@ function buildSubtensorToBasePlan(input) {
|
|
|
762
1086
|
}
|
|
763
1087
|
async function prepareEvmToSubtensor(provider, input) {
|
|
764
1088
|
const sender = normalizeEvmAddress(input.sender);
|
|
1089
|
+
const asset = bridgeAsset(input.evmChain, input.asset);
|
|
765
1090
|
assertDelivery(input.delivery);
|
|
1091
|
+
assertAssetMode(input.asset, input.delivery);
|
|
766
1092
|
assertBaseToSubtensorAmount(input.amountWei, input.delivery);
|
|
767
1093
|
const destination = normalizeSS58(input.destination);
|
|
768
1094
|
const evm = getForeverMoneyEvmDeployment(input.evmChain);
|
|
@@ -772,27 +1098,40 @@ async function prepareEvmToSubtensor(provider, input) {
|
|
|
772
1098
|
wantLiquid: input.delivery === "liquid",
|
|
773
1099
|
minTaoOut: input.amountWei
|
|
774
1100
|
};
|
|
1101
|
+
const partnerFee = resolvePartnerFee(
|
|
1102
|
+
input.partnerFee,
|
|
1103
|
+
evm.contracts.gateway
|
|
1104
|
+
);
|
|
1105
|
+
const cut = partnerFeeCut(input.amountWei, partnerFee.bps);
|
|
775
1106
|
const [allowanceWei, exactNetworkFeeWei] = await Promise.all([
|
|
776
1107
|
provider.readContract({
|
|
777
|
-
address:
|
|
1108
|
+
address: asset.evmToken,
|
|
778
1109
|
abi: erc20Abi,
|
|
779
1110
|
functionName: "allowance",
|
|
780
1111
|
args: [sender, evm.contracts.gateway]
|
|
781
1112
|
}),
|
|
782
|
-
provider.readContract({
|
|
1113
|
+
partnerFee.bps === 0 ? provider.readContract({
|
|
783
1114
|
address: evm.contracts.gateway,
|
|
784
1115
|
abi: spokeAbi,
|
|
785
1116
|
functionName: "quoteBridgeToFinney",
|
|
786
|
-
args: [
|
|
787
|
-
})
|
|
1117
|
+
args: [asset.evmToken, input.amountWei, exit]
|
|
1118
|
+
}) : quoteSpokeWithFee(
|
|
1119
|
+
provider,
|
|
1120
|
+
evm.contracts.gateway,
|
|
1121
|
+
asset.evmToken,
|
|
1122
|
+
input.amountWei,
|
|
1123
|
+
exit,
|
|
1124
|
+
partnerFee
|
|
1125
|
+
)
|
|
788
1126
|
]);
|
|
789
1127
|
let estimatedBridgeGas;
|
|
790
|
-
if (allowanceWei >= input.amountWei) {
|
|
791
|
-
const data = (
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1128
|
+
if (allowanceWei >= input.amountWei + cut) {
|
|
1129
|
+
const data = encodeSpokeBridge(
|
|
1130
|
+
asset.evmToken,
|
|
1131
|
+
input.amountWei,
|
|
1132
|
+
exit,
|
|
1133
|
+
partnerFee
|
|
1134
|
+
);
|
|
796
1135
|
estimatedBridgeGas = await provider.estimateGas({
|
|
797
1136
|
account: sender,
|
|
798
1137
|
to: evm.contracts.gateway,
|
|
@@ -809,6 +1148,7 @@ async function prepareEvmToSubtensor(provider, input) {
|
|
|
809
1148
|
return Object.freeze({
|
|
810
1149
|
exactNetworkFeeWei,
|
|
811
1150
|
transactionValueWei: feeWithBuffer(exactNetworkFeeWei),
|
|
1151
|
+
partnerFeeWei: cut,
|
|
812
1152
|
plan
|
|
813
1153
|
});
|
|
814
1154
|
}
|
|
@@ -818,58 +1158,68 @@ function prepareBaseToSubtensor(provider, input) {
|
|
|
818
1158
|
async function prepareSubtensorToEvm(provider, input) {
|
|
819
1159
|
const sender = normalizeEvmAddress(input.sender);
|
|
820
1160
|
const recipient = normalizeEvmAddress(input.recipient);
|
|
1161
|
+
const asset = bridgeAsset(input.evmChain, input.asset);
|
|
821
1162
|
assertSource(input.source);
|
|
822
|
-
|
|
1163
|
+
assertAssetMode(input.asset, input.source);
|
|
1164
|
+
const netuid = sourceNetuid(input);
|
|
1165
|
+
assertSubtensorToEvmAmount(input.amountWei, input.source);
|
|
823
1166
|
const { subtensor } = foreverMoneyDeployment;
|
|
824
1167
|
const evm = getForeverMoneyEvmDeployment(input.evmChain);
|
|
825
|
-
const
|
|
1168
|
+
const partnerFee = resolvePartnerFee(
|
|
1169
|
+
input.partnerFee,
|
|
1170
|
+
subtensor.contracts.gateway
|
|
1171
|
+
);
|
|
1172
|
+
const taoAmount = input.source === "liquid" ? input.amountWei : 0n;
|
|
1173
|
+
const stakedAlphaRao = input.source === "staked" ? input.amountWei / EVM_WEI_PER_RAO : 0n;
|
|
1174
|
+
const exactNetworkFeeWei = partnerFee.bps === 0 ? await provider.readContract({
|
|
826
1175
|
address: subtensor.contracts.gateway,
|
|
827
1176
|
abi: alphaAbi,
|
|
828
1177
|
functionName: "quoteBridgeOut",
|
|
829
1178
|
args: [
|
|
830
1179
|
evm.ccipSelector,
|
|
831
|
-
|
|
1180
|
+
asset.subtensorToken,
|
|
832
1181
|
recipient,
|
|
833
1182
|
input.amountWei
|
|
834
1183
|
]
|
|
835
|
-
})
|
|
1184
|
+
}) : (await quoteHubWithFee(
|
|
1185
|
+
provider,
|
|
1186
|
+
subtensor.contracts.gateway,
|
|
1187
|
+
evm.ccipSelector,
|
|
1188
|
+
asset.subtensorToken,
|
|
1189
|
+
recipient,
|
|
1190
|
+
input.amountWei,
|
|
1191
|
+
taoAmount,
|
|
1192
|
+
stakedAlphaRao,
|
|
1193
|
+
partnerFee
|
|
1194
|
+
)).networkFee;
|
|
836
1195
|
let stakingAllowanceRao;
|
|
837
1196
|
if (input.source === "staked") {
|
|
838
|
-
if (input.netuid === void 0) {
|
|
839
|
-
throw new ForeverMoneyError(
|
|
840
|
-
"INVALID_TRANSACTION_PLAN",
|
|
841
|
-
"A netuid is required when bridging staked TAO."
|
|
842
|
-
);
|
|
843
|
-
}
|
|
844
|
-
assertNonNegativeAmount(input.netuid, "netuid");
|
|
845
1197
|
stakingAllowanceRao = await provider.readContract({
|
|
846
1198
|
address: subtensor.contracts.stakingPrecompile,
|
|
847
1199
|
abi: stakingAbi,
|
|
848
1200
|
functionName: "allowance",
|
|
849
|
-
args: [sender, subtensor.contracts.gateway,
|
|
1201
|
+
args: [sender, subtensor.contracts.gateway, netuid]
|
|
850
1202
|
});
|
|
851
1203
|
}
|
|
852
|
-
const
|
|
853
|
-
const
|
|
854
|
-
const value = taoAmount + feeWithBuffer(exactNetworkFeeWei);
|
|
1204
|
+
const alphaTopUpRao = partnerFeeCut(stakedAlphaRao, partnerFee.bps);
|
|
1205
|
+
const taoTopUp = partnerFeeTaoTopUp(taoAmount, partnerFee.bps);
|
|
1206
|
+
const value = taoAmount + taoTopUp + feeWithBuffer(exactNetworkFeeWei);
|
|
855
1207
|
assertNonNegativeAmount(value, "Transaction value");
|
|
856
1208
|
let estimatedBridgeGas;
|
|
857
|
-
if (input.source === "liquid" || stakingAllowanceRao !== void 0 && stakingAllowanceRao >= stakedAlphaRao) {
|
|
1209
|
+
if (input.source === "liquid" || stakingAllowanceRao !== void 0 && stakingAllowanceRao >= stakedAlphaRao + alphaTopUpRao) {
|
|
858
1210
|
estimatedBridgeGas = await provider.estimateGas({
|
|
859
1211
|
account: sender,
|
|
860
1212
|
to: subtensor.contracts.gateway,
|
|
861
|
-
data: (
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
]
|
|
872
|
-
}),
|
|
1213
|
+
data: encodeHubBridge(
|
|
1214
|
+
evm.ccipSelector,
|
|
1215
|
+
asset.subtensorToken,
|
|
1216
|
+
recipient,
|
|
1217
|
+
taoAmount,
|
|
1218
|
+
stakedAlphaRao,
|
|
1219
|
+
input.amountWei,
|
|
1220
|
+
partnerFee,
|
|
1221
|
+
stakePullsFor(input, stakedAlphaRao)
|
|
1222
|
+
),
|
|
873
1223
|
value
|
|
874
1224
|
});
|
|
875
1225
|
}
|
|
@@ -882,6 +1232,7 @@ async function prepareSubtensorToEvm(provider, input) {
|
|
|
882
1232
|
return Object.freeze({
|
|
883
1233
|
exactNetworkFeeWei,
|
|
884
1234
|
transactionValueWei: value,
|
|
1235
|
+
partnerFeeWei: input.source === "liquid" ? taoTopUp : alphaTopUpRao * EVM_WEI_PER_RAO,
|
|
885
1236
|
plan
|
|
886
1237
|
});
|
|
887
1238
|
}
|
|
@@ -1469,13 +1820,20 @@ function bridgeMessageIdFromReceipt(direction, receipt) {
|
|
|
1469
1820
|
evmChainFromBridgeDirection(direction)
|
|
1470
1821
|
);
|
|
1471
1822
|
const evmToSubtensor = isEvmToSubtensorDirection(direction);
|
|
1472
|
-
const [
|
|
1473
|
-
|
|
1823
|
+
const [addresses, contractAbi, eventName] = evmToSubtensor ? [
|
|
1824
|
+
[...evm.contracts.legacyGateways, evm.contracts.gateway],
|
|
1825
|
+
spokeGatewayAbi,
|
|
1826
|
+
"BridgedToFinney"
|
|
1827
|
+
] : [
|
|
1828
|
+
[
|
|
1829
|
+
...foreverMoneyDeployment.subtensor.contracts.legacyGateways,
|
|
1830
|
+
foreverMoneyDeployment.subtensor.contracts.gateway
|
|
1831
|
+
],
|
|
1474
1832
|
alphaGatewayAbi,
|
|
1475
1833
|
"BridgedOut"
|
|
1476
1834
|
];
|
|
1477
1835
|
for (const log of receipt.logs) {
|
|
1478
|
-
if (!isLogFrom(log, address)) continue;
|
|
1836
|
+
if (!addresses.some((address) => isLogFrom(log, address))) continue;
|
|
1479
1837
|
try {
|
|
1480
1838
|
const parsed = (0, import_viem10.decodeEventLog)({
|
|
1481
1839
|
abi: contractAbi,
|
|
@@ -1629,16 +1987,23 @@ async function getCcipDeliveryStatus(provider, input) {
|
|
|
1629
1987
|
"The CCIP execution receipt is unavailable."
|
|
1630
1988
|
);
|
|
1631
1989
|
}
|
|
1990
|
+
const subtensorGateways = [
|
|
1991
|
+
...foreverMoneyDeployment.subtensor.contracts.legacyGateways,
|
|
1992
|
+
foreverMoneyDeployment.subtensor.contracts.gateway
|
|
1993
|
+
];
|
|
1632
1994
|
for (const log of receipt.logs) {
|
|
1633
|
-
if (
|
|
1995
|
+
if (!subtensorGateways.some(
|
|
1996
|
+
(gateway) => log.address.toLowerCase() === gateway.toLowerCase()
|
|
1997
|
+
)) {
|
|
1634
1998
|
continue;
|
|
1635
1999
|
}
|
|
1636
2000
|
try {
|
|
1637
|
-
|
|
2001
|
+
const { eventName } = (0, import_viem11.decodeEventLog)({
|
|
1638
2002
|
abi: alphaGatewayAbi2,
|
|
1639
2003
|
data: log.data,
|
|
1640
2004
|
topics: log.topics
|
|
1641
|
-
})
|
|
2005
|
+
});
|
|
2006
|
+
if (eventName === "Claimable" || eventName === "NotDelivered") {
|
|
1642
2007
|
return "recovery";
|
|
1643
2008
|
}
|
|
1644
2009
|
} catch {
|