@across-protocol/contracts 3.0.17 → 3.0.19

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.
Files changed (95) hide show
  1. package/README.md +2 -0
  2. package/artifacts/build-info/9cb910e5bb5dd730cd01af84a0fb0466.json +1 -0
  3. package/artifacts/contracts/Ink_SpokePool.sol/Ink_SpokePool.dbg.json +4 -0
  4. package/artifacts/contracts/Ink_SpokePool.sol/Ink_SpokePool.json +2316 -0
  5. package/contracts/Ink_SpokePool.sol +72 -0
  6. package/dist/deploy/consts.js +8 -2
  7. package/dist/deployments/deployments.json +7 -1
  8. package/dist/hardhat.config.js +17 -0
  9. package/dist/scripts/svm/addressToPublicKey.d.ts +1 -0
  10. package/dist/scripts/svm/addressToPublicKey.js +20 -0
  11. package/dist/scripts/svm/bridgeLiabilityToHubPool.d.ts +25 -0
  12. package/dist/scripts/svm/bridgeLiabilityToHubPool.js +221 -0
  13. package/dist/scripts/svm/closeRelayerPdas.js +8 -9
  14. package/dist/scripts/svm/enableRoute.js +7 -1
  15. package/dist/scripts/svm/executeRebalanceToHubPool.d.ts +29 -0
  16. package/dist/scripts/svm/executeRebalanceToHubPool.js +345 -0
  17. package/dist/scripts/svm/executeRebalanceToSpokePool.d.ts +1 -0
  18. package/dist/scripts/svm/executeRebalanceToSpokePool.js +240 -0
  19. package/dist/scripts/svm/fakeFillWithRandomDistribution.js +6 -7
  20. package/dist/scripts/svm/initialize.js +2 -2
  21. package/dist/scripts/svm/proposeRebalanceToHubPool.d.ts +27 -0
  22. package/dist/scripts/svm/proposeRebalanceToHubPool.js +117 -0
  23. package/dist/scripts/svm/proposeRebalanceToSpokePool.d.ts +1 -0
  24. package/dist/scripts/svm/proposeRebalanceToSpokePool.js +97 -0
  25. package/dist/scripts/svm/publicKeyToAddress.d.ts +1 -0
  26. package/dist/scripts/svm/publicKeyToAddress.js +20 -0
  27. package/dist/scripts/svm/queryDeposits.js +3 -2
  28. package/dist/scripts/svm/queryFills.js +12 -14
  29. package/dist/scripts/svm/queryRoute.js +7 -1
  30. package/dist/scripts/svm/queryState.js +1 -0
  31. package/dist/scripts/svm/remoteHubPoolPauseDeposits.d.ts +1 -0
  32. package/dist/scripts/svm/remoteHubPoolPauseDeposits.js +191 -0
  33. package/dist/scripts/svm/remoteHubPoolSetDepositRoute.js +16 -29
  34. package/dist/scripts/svm/remotePauseDeposits.js +21 -27
  35. package/dist/scripts/svm/simpleDeposit.js +7 -1
  36. package/dist/scripts/svm/simpleFakeRelayerRepayment.js +3 -3
  37. package/dist/scripts/svm/simpleFill.js +6 -6
  38. package/dist/scripts/svm/utils/constants.d.ts +4 -0
  39. package/dist/scripts/svm/utils/constants.js +8 -1
  40. package/dist/scripts/svm/utils/helpers.d.ts +33 -0
  41. package/dist/scripts/svm/utils/helpers.js +60 -1
  42. package/dist/scripts/svm/utils/poolRebalanceTree.d.ts +22 -0
  43. package/dist/scripts/svm/utils/poolRebalanceTree.js +20 -0
  44. package/dist/src/svm/coders.d.ts +37 -0
  45. package/dist/src/svm/coders.js +250 -0
  46. package/dist/src/svm/conversionUtils.d.ts +22 -0
  47. package/dist/src/svm/conversionUtils.js +73 -0
  48. package/dist/src/svm/index.d.ts +6 -0
  49. package/dist/src/svm/index.js +22 -0
  50. package/dist/src/svm/instructionParamsUtils.d.ts +31 -0
  51. package/dist/src/svm/instructionParamsUtils.js +128 -0
  52. package/dist/src/svm/relayHashUtils.d.ts +30 -0
  53. package/dist/src/svm/relayHashUtils.js +209 -0
  54. package/dist/src/svm/solanaProgramUtils.d.ts +38 -0
  55. package/dist/src/svm/solanaProgramUtils.js +147 -0
  56. package/dist/src/svm/transactionUtils.d.ts +8 -0
  57. package/dist/src/svm/transactionUtils.js +55 -0
  58. package/dist/src/types/svm.d.ts +118 -0
  59. package/dist/src/types/svm.js +2 -0
  60. package/dist/target/types/svm_spoke.d.ts +47 -42
  61. package/dist/tasks/enableL1TokenAcrossEcosystem.js +3 -33
  62. package/dist/tasks/types.d.ts +2 -0
  63. package/dist/tasks/types.js +2 -0
  64. package/dist/tasks/utils.d.ts +12 -0
  65. package/dist/tasks/utils.js +34 -0
  66. package/dist/test/svm/MulticallHandler.js +2 -2
  67. package/dist/test/svm/SvmSpoke.Bundle.js +59 -60
  68. package/dist/test/svm/SvmSpoke.Deposit.js +23 -31
  69. package/dist/test/svm/SvmSpoke.Fill.AcrossPlus.js +16 -18
  70. package/dist/test/svm/SvmSpoke.Fill.js +38 -40
  71. package/dist/test/svm/SvmSpoke.HandleReceiveMessage.js +2 -2
  72. package/dist/test/svm/SvmSpoke.Ownership.js +11 -17
  73. package/dist/test/svm/SvmSpoke.RefundClaims.js +12 -11
  74. package/dist/test/svm/SvmSpoke.Routes.js +11 -7
  75. package/dist/test/svm/SvmSpoke.SlowFill.AcrossPlus.js +72 -16
  76. package/dist/test/svm/SvmSpoke.SlowFill.js +33 -28
  77. package/dist/test/svm/SvmSpoke.TokenBridge.js +15 -17
  78. package/dist/test/svm/SvmSpoke.common.d.ts +1 -49
  79. package/dist/test/svm/SvmSpoke.common.js +6 -5
  80. package/dist/test/svm/cctpHelpers.js +2 -2
  81. package/dist/test/svm/utils.d.ts +5 -67
  82. package/dist/test/svm/utils.js +10 -220
  83. package/dist/typechain/contracts/Ink_SpokePool.d.ts +1251 -0
  84. package/dist/typechain/contracts/Ink_SpokePool.js +2 -0
  85. package/dist/typechain/contracts/index.d.ts +1 -0
  86. package/dist/typechain/factories/contracts/Ink_SpokePool__factory.d.ts +1833 -0
  87. package/dist/typechain/factories/contracts/Ink_SpokePool__factory.js +2347 -0
  88. package/dist/typechain/factories/contracts/index.d.ts +1 -0
  89. package/dist/typechain/factories/contracts/index.js +3 -1
  90. package/dist/typechain/hardhat.d.ts +9 -0
  91. package/dist/typechain/index.d.ts +2 -0
  92. package/dist/typechain/index.js +5 -3
  93. package/package.json +2 -2
  94. package/dist/src/SvmUtils.d.ts +0 -50
  95. package/dist/src/SvmUtils.js +0 -415
@@ -0,0 +1,72 @@
1
+ // SPDX-License-Identifier: BUSL-1.1
2
+ pragma solidity ^0.8.0;
3
+ import "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
4
+
5
+ import "./Ovm_SpokePool.sol";
6
+ import "./external/interfaces/CCTPInterfaces.sol";
7
+ import { IOpUSDCBridgeAdapter } from "./external/interfaces/IOpUSDCBridgeAdapter.sol";
8
+
9
+ /**
10
+ * @notice Ink Spoke pool.
11
+ * @custom:security-contact bugs@across.to
12
+ */
13
+ contract Ink_SpokePool is Ovm_SpokePool {
14
+ using SafeERC20 for IERC20;
15
+
16
+ // Address of the custom L2 USDC bridge.
17
+ address private constant USDC_BRIDGE = address(0);
18
+
19
+ /// @custom:oz-upgrades-unsafe-allow constructor
20
+ constructor(
21
+ address _wrappedNativeTokenAddress,
22
+ uint32 _depositQuoteTimeBuffer,
23
+ uint32 _fillDeadlineBuffer,
24
+ IERC20 _l2Usdc,
25
+ ITokenMessenger _cctpTokenMessenger
26
+ )
27
+ Ovm_SpokePool(
28
+ _wrappedNativeTokenAddress,
29
+ _depositQuoteTimeBuffer,
30
+ _fillDeadlineBuffer,
31
+ _l2Usdc,
32
+ _cctpTokenMessenger
33
+ )
34
+ {} // solhint-disable-line no-empty-blocks
35
+
36
+ /**
37
+ * @notice Construct the OVM World Chain SpokePool.
38
+ * @param _initialDepositId Starting deposit ID. Set to 0 unless this is a re-deployment in order to mitigate
39
+ * relay hash collisions.
40
+ * @param _crossDomainAdmin Cross domain admin to set. Can be changed by admin.
41
+ * @param _withdrawalRecipient Address which receives token withdrawals. Can be changed by admin. For Spoke Pools on L2, this will
42
+ */
43
+ function initialize(
44
+ uint32 _initialDepositId,
45
+ address _crossDomainAdmin,
46
+ address _withdrawalRecipient
47
+ ) public initializer {
48
+ __OvmSpokePool_init(_initialDepositId, _crossDomainAdmin, _withdrawalRecipient, Lib_PredeployAddresses.OVM_ETH);
49
+ }
50
+
51
+ /**
52
+ * @notice Ink-specific logic to bridge tokens back to the hub pool contract on L1.
53
+ * @param amountToReturn Amount of the token to bridge back.
54
+ * @param l2TokenAddress Address of the l2 Token to bridge back. This token will either be bridged back to the token defined in the mapping `remoteL1Tokens`,
55
+ * or via the canonical mapping defined in the bridge contract retrieved from `tokenBridges`.
56
+ * @dev This implementation deviates slightly from `_bridgeTokensToHubPool` in the `Ovm_SpokePool` contract since World Chain has a USDC bridge which uses
57
+ * a custom interface. This is because the USDC token on World Chain is meant to be upgraded to a native, CCTP supported version in the future.
58
+ */
59
+ function _bridgeTokensToHubPool(uint256 amountToReturn, address l2TokenAddress) internal virtual override {
60
+ // Handle custom USDC bridge which doesn't conform to the standard bridge interface. In the future, CCTP may be used to bridge USDC to mainnet, in which
61
+ // case bridging logic is handled by the Ovm_SpokePool code. In the meantime, if CCTP is not enabled, then use the USDC bridge. Once CCTP is activated on
62
+ // WorldChain, this block of code will be unused.
63
+ if (l2TokenAddress == address(usdcToken) && !_isCCTPEnabled()) {
64
+ usdcToken.safeIncreaseAllowance(USDC_BRIDGE, amountToReturn);
65
+ IOpUSDCBridgeAdapter(USDC_BRIDGE).sendMessage(
66
+ withdrawalRecipient, // _to. Withdraw, over the bridge, to the l1 hub pool contract.
67
+ amountToReturn, // _amount.
68
+ l1Gas // _minGasLimit. Same value used in other OpStack bridges.
69
+ );
70
+ } else super._bridgeTokensToHubPool(amountToReturn, l2TokenAddress);
71
+ }
72
+ }
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.CIRCLE_DOMAIN_IDs = exports.POLYGON_CHAIN_IDS = exports.L2_ADDRESS_MAP = exports.OP_STACK_ADDRESS_MAP = exports.L1_ADDRESS_MAP = exports.AZERO_GAS_PRICE = exports.ARBITRUM_MAX_SUBMISSION_COST = exports.FILL_DEADLINE_BUFFER = exports.QUOTE_TIME_BUFFER = exports.AZERO = exports.WAZERO = exports.WMATIC = exports.WETH = exports.USDCe = exports.USDC = exports.ZERO_ADDRESS = void 0;
4
- var common_1 = require("@uma/common");
5
- Object.defineProperty(exports, "ZERO_ADDRESS", { enumerable: true, get: function () { return common_1.ZERO_ADDRESS; } });
4
+ const common_1 = require("@uma/common");
6
5
  const utils_1 = require("../utils");
6
+ var common_2 = require("@uma/common");
7
+ Object.defineProperty(exports, "ZERO_ADDRESS", { enumerable: true, get: function () { return common_2.ZERO_ADDRESS; } });
7
8
  exports.USDC = utils_1.TOKEN_SYMBOLS_MAP.USDC.addresses;
8
9
  exports.USDCe = utils_1.TOKEN_SYMBOLS_MAP["USDC.e"].addresses;
9
10
  exports.WETH = utils_1.TOKEN_SYMBOLS_MAP.WETH.addresses;
@@ -74,6 +75,11 @@ exports.OP_STACK_ADDRESS_MAP = {
74
75
  L1CrossDomainMessenger: "0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0",
75
76
  L1StandardBridge: "0x697402166Fbf2F22E970df8a6486Ef171dbfc524",
76
77
  },
78
+ [utils_1.CHAIN_IDs.INK]: {
79
+ L1CrossDomainMessenger: "0x69d3cf86b2bf1a9e99875b7e2d9b6a84426c171f",
80
+ L1StandardBridge: "0x88ff1e5b602916615391f55854588efcbb7663f0",
81
+ L1OpUSDCBridgeAdapter: common_1.ZERO_ADDRESS,
82
+ },
77
83
  [utils_1.CHAIN_IDs.LISK]: {
78
84
  L1CrossDomainMessenger: "0x31B72D76FB666844C41EdF08dF0254875Dbb7edB",
79
85
  L1StandardBridge: "0x2658723Bf70c7667De6B25F99fcce13A16D25d08",
@@ -28,7 +28,8 @@
28
28
  "Redstone_Adapter": { "address": "0x188F8C95B7cfB7993B53a4F643efa687916f73fA", "blockNumber": 20432774 },
29
29
  "Zora_Adapter": { "address": "0x024f2fc31cbdd8de17194b1892c834f98ef5169b", "blockNumber": 20512287 },
30
30
  "WorldChain_Adapter": { "address": "0xA8399e221a583A57F54Abb5bA22f31b5D6C09f32", "blockNumber": 20963234 },
31
- "AlephZero_Adapter": { "address": "0x6F4083304C2cA99B077ACE06a5DcF670615915Af", "blockNumber": 21131132 }
31
+ "AlephZero_Adapter": { "address": "0x6F4083304C2cA99B077ACE06a5DcF670615915Af", "blockNumber": 21131132 },
32
+ "Ink_Adapter": { "address": "0x7e90a40c7519b041a7df6498fbf5662e8cfc61d2", "blockNumber": 21438590 }
32
33
  },
33
34
  "10": {
34
35
  "SpokePool": { "address": "0x6f26Bf09B1C792e3228e5467807a900A503c0281", "blockNumber": 93903076 },
@@ -157,5 +158,10 @@
157
158
  "41455": {
158
159
  "SpokePool": { "address": "0x13fDac9F9b4777705db45291bbFF3c972c6d1d97", "blockNumber": 4240318 },
159
160
  "MulticallHandler": { "address": "0x924a9f036260DdD5808007E1AA95f08eD08aA569", "blockNumber": 4112529 }
161
+ },
162
+ "57073": {
163
+ "SpokePool": { "address": "0xeF684C38F94F48775959ECf2012D7E864ffb9dd4", "blockNumber": 1139240 },
164
+ "SpokePoolVerifier": { "address": "0xB4A8d45647445EA9FC3E1058096142390683dBC2", "blockNumber": 1152853 },
165
+ "MulticallHandler": { "address": "0x924a9f036260DdD5808007E1AA95f08eD08aA569", "blockNumber": 1145284 }
160
166
  }
161
167
  }
@@ -101,6 +101,7 @@ const config = {
101
101
  "contracts/Base_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS,
102
102
  "contracts/Optimism_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS,
103
103
  "contracts/WorldChain_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS,
104
+ "contracts/Ink_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS,
104
105
  },
105
106
  },
106
107
  zksolc: {
@@ -203,6 +204,13 @@ const config = {
203
204
  accounts: { mnemonic },
204
205
  companionNetworks: { l1: "sepolia" },
205
206
  },
207
+ ink: {
208
+ chainId: constants_1.CHAIN_IDs.INK,
209
+ url: "https://rpc-gel.inkonchain.com",
210
+ saveDeployments: true,
211
+ accounts: { mnemonic },
212
+ companionNetworks: { l1: "mainnet" },
213
+ },
206
214
  linea: {
207
215
  chainId: constants_1.CHAIN_IDs.LINEA,
208
216
  url: `https://linea-mainnet.infura.io/v3/${process.env.INFURA_API_KEY}`,
@@ -337,6 +345,7 @@ const config = {
337
345
  zora: "routescan",
338
346
  worldchain: "blockscout",
339
347
  alephzero: "blockscout",
348
+ ink: "blockscout",
340
349
  },
341
350
  customChains: [
342
351
  {
@@ -363,6 +372,14 @@ const config = {
363
372
  browserURL: "https://sepolia.basescan.org",
364
373
  },
365
374
  },
375
+ {
376
+ network: "ink",
377
+ chainId: constants_1.CHAIN_IDs.INK,
378
+ urls: {
379
+ apiURL: "https://explorer.inkonchain.com/api",
380
+ browserURL: "https://explorer.inkonchain.com",
381
+ },
382
+ },
366
383
  {
367
384
  network: "linea",
368
385
  chainId: constants_1.CHAIN_IDs.LINEA,
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const svm_1 = require("../../src/svm");
7
+ const yargs_1 = __importDefault(require("yargs"));
8
+ const helpers_1 = require("yargs/helpers");
9
+ const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)).option("address", {
10
+ type: "string",
11
+ demandOption: true,
12
+ describe: "Ethereum address to convert",
13
+ }).argv;
14
+ async function logPublicKey() {
15
+ const address = (await argv).address;
16
+ const publicKey = (0, svm_1.evmAddressToPublicKey)(address);
17
+ console.log("Ethereum Address:", address);
18
+ console.log("Associated Public Key:", publicKey.toString());
19
+ }
20
+ logPublicKey();
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Script: Bridge USDC Liability to Hub Pool
3
+ *
4
+ * This script bridges pending USDC liabilities from the Solana Spoke Pool to the Ethereum Hub Pool using the CCTP
5
+ * (Circle Cross-Chain Transfer Protocol). It manages CCTP message attestations, verifies transfer completion, and
6
+ * updates the Hub Pool’s USDC balance.
7
+ *
8
+ * Required Environment Variables:
9
+ * - MNEMONIC: Wallet mnemonic to sign the Ethereum transaction.
10
+ * - HUB_POOL_ADDRESS: Ethereum address of the Hub Pool.
11
+ * - NODE_URL_${CHAIN_ID}: Ethereum RPC URL (must point to the Mainnet or Sepolia depending on Solana cluster).
12
+ *
13
+ * Example Usage:
14
+ * NODE_URL_11155111=$NODE_URL_11155111 \
15
+ * MNEMONIC=$MNEMONIC \
16
+ * HUB_POOL_ADDRESS=$HUB_POOL_ADDRESS \
17
+ * anchor run bridgeLiabilityToHubPool \
18
+ * --provider.cluster "devnet" \
19
+ * --provider.wallet $SOLANA_PKEY_PATH
20
+ *
21
+ * Note:
22
+ * - Ensure all required environment variables are properly configured.
23
+ * - Pending USDC liabilities must exist in the Solana Spoke Pool for the script to execute.
24
+ */
25
+ export {};
@@ -0,0 +1,221 @@
1
+ "use strict";
2
+ /**
3
+ * Script: Bridge USDC Liability to Hub Pool
4
+ *
5
+ * This script bridges pending USDC liabilities from the Solana Spoke Pool to the Ethereum Hub Pool using the CCTP
6
+ * (Circle Cross-Chain Transfer Protocol). It manages CCTP message attestations, verifies transfer completion, and
7
+ * updates the Hub Pool’s USDC balance.
8
+ *
9
+ * Required Environment Variables:
10
+ * - MNEMONIC: Wallet mnemonic to sign the Ethereum transaction.
11
+ * - HUB_POOL_ADDRESS: Ethereum address of the Hub Pool.
12
+ * - NODE_URL_${CHAIN_ID}: Ethereum RPC URL (must point to the Mainnet or Sepolia depending on Solana cluster).
13
+ *
14
+ * Example Usage:
15
+ * NODE_URL_11155111=$NODE_URL_11155111 \
16
+ * MNEMONIC=$MNEMONIC \
17
+ * HUB_POOL_ADDRESS=$HUB_POOL_ADDRESS \
18
+ * anchor run bridgeLiabilityToHubPool \
19
+ * --provider.cluster "devnet" \
20
+ * --provider.wallet $SOLANA_PKEY_PATH
21
+ *
22
+ * Note:
23
+ * - Ensure all required environment variables are properly configured.
24
+ * - Pending USDC liabilities must exist in the Solana Spoke Pool for the script to execute.
25
+ */
26
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
27
+ if (k2 === undefined) k2 = k;
28
+ var desc = Object.getOwnPropertyDescriptor(m, k);
29
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
30
+ desc = { enumerable: true, get: function() { return m[k]; } };
31
+ }
32
+ Object.defineProperty(o, k2, desc);
33
+ }) : (function(o, m, k, k2) {
34
+ if (k2 === undefined) k2 = k;
35
+ o[k2] = m[k];
36
+ }));
37
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
38
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
39
+ }) : function(o, v) {
40
+ o["default"] = v;
41
+ });
42
+ var __importStar = (this && this.__importStar) || function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ Object.defineProperty(exports, "__esModule", { value: true });
50
+ const anchor = __importStar(require("@coral-xyz/anchor"));
51
+ const anchor_1 = require("@coral-xyz/anchor");
52
+ const spl_token_1 = require("@solana/spl-token");
53
+ const web3_js_1 = require("@solana/web3.js");
54
+ // eslint-disable-next-line camelcase
55
+ const constants_1 = require("./utils/constants");
56
+ const constants_2 = require("@across-protocol/constants");
57
+ const common_1 = require("@uma/common");
58
+ const ethers_1 = require("ethers");
59
+ const cctpHelpers_1 = require("../../test/svm/cctpHelpers");
60
+ const typechain_1 = require("../../typechain");
61
+ const helpers_1 = require("./utils/helpers");
62
+ // Set up Solana provider.
63
+ const provider = anchor_1.AnchorProvider.env();
64
+ anchor.setProvider(provider);
65
+ // Get Solana programs and IDLs.
66
+ const svmSpokeIdl = require("../../target/idl/svm_spoke.json");
67
+ const svmSpokeProgram = new anchor_1.Program(svmSpokeIdl, provider);
68
+ const messageTransmitterIdl = require("../../target/idl/message_transmitter.json");
69
+ const tokenMessengerMinterIdl = require("../../target/idl/token_messenger_minter.json");
70
+ // CCTP domains.
71
+ const ethereumDomain = 0; // Ethereum
72
+ const solanaDomain = 5; // Solana
73
+ // Set up Ethereum provider and signer.
74
+ const isDevnet = (0, helpers_1.isSolanaDevnet)(provider);
75
+ const nodeURL = isDevnet ? (0, common_1.getNodeUrl)("sepolia", true) : (0, common_1.getNodeUrl)("mainnet", true);
76
+ const ethersProvider = new ethers_1.ethers.providers.JsonRpcProvider(nodeURL);
77
+ const ethersSigner = ethers_1.ethers.Wallet.fromMnemonic((0, helpers_1.requireEnv)("MNEMONIC")).connect(ethersProvider);
78
+ // Get the HubPool contract instance.
79
+ const hubPoolAddress = ethers_1.ethers.utils.getAddress((0, helpers_1.requireEnv)("HUB_POOL_ADDRESS")); // Used to check USDC balance before and after.
80
+ const messageTransmitterAbi = [
81
+ {
82
+ inputs: [
83
+ {
84
+ internalType: "bytes",
85
+ name: "message",
86
+ type: "bytes",
87
+ },
88
+ {
89
+ internalType: "bytes",
90
+ name: "attestation",
91
+ type: "bytes",
92
+ },
93
+ ],
94
+ name: "receiveMessage",
95
+ outputs: [
96
+ {
97
+ internalType: "bool",
98
+ name: "success",
99
+ type: "bool",
100
+ },
101
+ ],
102
+ stateMutability: "nonpayable",
103
+ type: "function",
104
+ },
105
+ {
106
+ inputs: [
107
+ {
108
+ internalType: "bytes32",
109
+ name: "",
110
+ type: "bytes32",
111
+ },
112
+ ],
113
+ name: "usedNonces",
114
+ outputs: [
115
+ {
116
+ internalType: "uint256",
117
+ name: "",
118
+ type: "uint256",
119
+ },
120
+ ],
121
+ stateMutability: "view",
122
+ type: "function",
123
+ },
124
+ ];
125
+ async function bridgeLiabilityToHubPool() {
126
+ const seed = constants_1.SOLANA_SPOKE_STATE_SEED; // Seed is always 0 for the state account PDA in public networks.
127
+ // Resolve Solana USDC addresses.
128
+ const svmUsdc = isDevnet ? constants_1.SOLANA_USDC_DEVNET : constants_1.SOLANA_USDC_MAINNET;
129
+ const [statePda, _] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], svmSpokeProgram.programId);
130
+ const irisApiUrl = isDevnet ? constants_1.CIRCLE_IRIS_API_URL_DEVNET : constants_1.CIRCLE_IRIS_API_URL_MAINNET;
131
+ const cctpMessageTransmitter = isDevnet
132
+ ? constants_1.SEPOLIA_CCTP_MESSAGE_TRANSMITTER_ADDRESS
133
+ : constants_1.MAINNET_CCTP_MESSAGE_TRANSMITTER_ADDRESS;
134
+ const messageTransmitter = new ethers_1.ethers.Contract(cctpMessageTransmitter, messageTransmitterAbi, ethersSigner);
135
+ const evmChainId = (await ethersProvider.getNetwork()).chainId;
136
+ const usdcAddress = constants_2.TOKEN_SYMBOLS_MAP.USDC.addresses[evmChainId];
137
+ const usdc = typechain_1.BondToken__factory.connect(usdcAddress, ethersProvider);
138
+ const usdcBalanceBefore = await usdc.balanceOf(hubPoolAddress);
139
+ console.log("Receiving liability from Solana Spoke Pool to Ethereum Hub Pool...");
140
+ console.table([
141
+ { Property: "isTestnet", Value: isDevnet },
142
+ { Property: "hubPoolAddress", Value: hubPoolAddress },
143
+ { Property: "svmSpokeProgramProgramId", Value: svmSpokeProgram.programId.toString() },
144
+ { Property: "providerPublicKey", Value: provider.wallet.publicKey.toString() },
145
+ { Property: "usdcBalanceBefore", Value: usdcBalanceBefore.toString() },
146
+ ]);
147
+ const [transferLiability] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("transfer_liability"), new web3_js_1.PublicKey(svmUsdc).toBuffer()], svmSpokeProgram.programId);
148
+ const liability = await svmSpokeProgram.account.transferLiability.fetch(transferLiability);
149
+ console.log(`Pending transfer liability: ${(0, helpers_1.formatUsdc)(ethers_1.BigNumber.from(liability.pendingToHubPool.toString()))} USDC.`);
150
+ if (liability.pendingToHubPool.eq(new anchor_1.BN(0))) {
151
+ console.log("No pending transfer liability to bridge. Exiting...");
152
+ return;
153
+ }
154
+ console.log("Bridging liability to hub pool...");
155
+ const txHash = await bridgeTokensToHubPool(liability.pendingToHubPool, provider.wallet, statePda, new web3_js_1.PublicKey(svmUsdc));
156
+ const attestationResponse = await (0, cctpHelpers_1.getMessages)(txHash, solanaDomain, irisApiUrl);
157
+ const { attestation, message, eventNonce } = attestationResponse.messages[0];
158
+ console.log("CCTP attestation response:", attestationResponse.messages[0]);
159
+ const nonceHash = ethers_1.ethers.utils.solidityKeccak256(["uint32", "uint64"], [solanaDomain, eventNonce]);
160
+ const usedNonces = await messageTransmitter.usedNonces(nonceHash);
161
+ if (usedNonces.eq(1)) {
162
+ console.log(`Skipping already received message. Exiting...`);
163
+ return;
164
+ }
165
+ console.log("Receiving message from CCTP...");
166
+ const receiveTx = await messageTransmitter.receiveMessage(message, attestation);
167
+ console.log(`Tx hash: ${receiveTx.hash}`);
168
+ await receiveTx.wait();
169
+ console.log(`Received message`);
170
+ const usdcBalanceAfter = await usdc.balanceOf(hubPoolAddress);
171
+ console.log(`Hub Pool USDC balance after: ${(0, helpers_1.formatUsdc)(usdcBalanceAfter)}. Received ${(0, helpers_1.formatUsdc)(usdcBalanceAfter.sub(usdcBalanceBefore))} USDC.`);
172
+ console.log("✅ Bridge liability to hub pool completed successfully.");
173
+ }
174
+ async function bridgeTokensToHubPool(amount, signer, statePda, inputToken) {
175
+ const messageTransmitterProgram = new anchor_1.Program(messageTransmitterIdl, provider);
176
+ const vault = (0, spl_token_1.getAssociatedTokenAddressSync)(inputToken, statePda, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
177
+ // Derive the transferLiability PDA
178
+ const [transferLiability] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("transfer_liability"), inputToken.toBuffer()], svmSpokeProgram.programId);
179
+ const tokenMessengerMinterProgram = new anchor_1.Program(tokenMessengerMinterIdl, provider);
180
+ const [tokenMessengerMinterSenderAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("sender_authority")], tokenMessengerMinterProgram.programId);
181
+ const [messageTransmitter] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("message_transmitter")], messageTransmitterProgram.programId);
182
+ const [tokenMessenger] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token_messenger")], tokenMessengerMinterProgram.programId);
183
+ const [remoteTokenMessenger] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("remote_token_messenger"), Buffer.from(anchor.utils.bytes.utf8.encode(ethereumDomain.toString()))], tokenMessengerMinterProgram.programId);
184
+ const [tokenMinter] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("token_minter")], tokenMessengerMinterProgram.programId);
185
+ const [localToken] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("local_token"), inputToken.toBuffer()], tokenMessengerMinterProgram.programId);
186
+ const [cctpEventAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], tokenMessengerMinterProgram.programId);
187
+ const messageSentEventData = anchor.web3.Keypair.generate(); // This will hold the message sent event data.
188
+ const bridgeTokensToHubPoolAccounts = {
189
+ payer: signer.publicKey,
190
+ mint: inputToken,
191
+ state: statePda,
192
+ transferLiability,
193
+ vault,
194
+ tokenMessengerMinterSenderAuthority,
195
+ messageTransmitter,
196
+ tokenMessenger,
197
+ remoteTokenMessenger,
198
+ tokenMinter,
199
+ localToken,
200
+ messageSentEventData: messageSentEventData.publicKey,
201
+ messageTransmitterProgram: messageTransmitterProgram.programId,
202
+ tokenMessengerMinterProgram: tokenMessengerMinterProgram.programId,
203
+ tokenProgram: spl_token_1.TOKEN_PROGRAM_ID,
204
+ systemProgram: web3_js_1.SystemProgram.programId,
205
+ cctpEventAuthority: cctpEventAuthority,
206
+ program: svmSpokeProgram.programId,
207
+ };
208
+ const initialVaultBalance = (await provider.connection.getTokenAccountBalance(vault)).value.amount;
209
+ const tx = await svmSpokeProgram.methods
210
+ .bridgeTokensToHubPool(new anchor_1.BN(amount))
211
+ .accounts(bridgeTokensToHubPoolAccounts)
212
+ .signers([messageSentEventData])
213
+ .rpc();
214
+ const finalVaultBalance = (await provider.connection.getTokenAccountBalance(vault)).value.amount;
215
+ console.log(`SVM Spoke Pool Initial Vault balance: ${(0, helpers_1.formatUsdc)(ethers_1.BigNumber.from(initialVaultBalance))} USDC.`);
216
+ console.log(`SVM Spoke Pool Final Vault balance: ${(0, helpers_1.formatUsdc)(ethers_1.BigNumber.from(finalVaultBalance))} USDC.`);
217
+ console.log(`Sent ${(0, helpers_1.formatUsdc)(ethers_1.BigNumber.from(initialVaultBalance).sub(ethers_1.BigNumber.from(finalVaultBalance)))} USDC through CCTP.`);
218
+ return tx;
219
+ }
220
+ // Run the bridgeLiabilityToHubPool function
221
+ bridgeLiabilityToHubPool();
@@ -33,8 +33,7 @@ const anchor_1 = require("@coral-xyz/anchor");
33
33
  const web3_js_1 = require("@solana/web3.js");
34
34
  const yargs_1 = __importDefault(require("yargs"));
35
35
  const helpers_1 = require("yargs/helpers");
36
- const SvmUtils_1 = require("../../src/SvmUtils");
37
- const SvmUtils_2 = require("../../src/SvmUtils");
36
+ const svm_1 = require("../../src/svm");
38
37
  // Set up the provider
39
38
  const provider = anchor_1.AnchorProvider.env();
40
39
  anchor.setProvider(provider);
@@ -55,7 +54,7 @@ async function closeExpiredRelays() {
55
54
  { Property: "programId", Value: programId.toString() },
56
55
  ]);
57
56
  try {
58
- const events = await (0, SvmUtils_1.readProgramEvents)(provider.connection, program);
57
+ const events = await (0, svm_1.readProgramEvents)(provider.connection, program);
59
58
  const fillEvents = events.filter((event) => event.name === "filledV3Relay" && new web3_js_1.PublicKey(event.data.relayer).equals(relayer));
60
59
  console.log(`Number of fill events found: ${fillEvents.length}`);
61
60
  if (fillEvents.length === 0) {
@@ -78,7 +77,7 @@ async function closeExpiredRelays() {
78
77
  }
79
78
  async function closeFillPda(eventData, seed) {
80
79
  // Accept seed as a parameter
81
- const relayData = {
80
+ const relayEventData = {
82
81
  depositor: new web3_js_1.PublicKey(eventData.depositor),
83
82
  recipient: new web3_js_1.PublicKey(eventData.recipient),
84
83
  exclusiveRelayer: new web3_js_1.PublicKey(eventData.exclusiveRelayer),
@@ -90,13 +89,13 @@ async function closeFillPda(eventData, seed) {
90
89
  depositId: eventData.depositId,
91
90
  fillDeadline: eventData.fillDeadline,
92
91
  exclusivityDeadline: eventData.exclusivityDeadline,
93
- message: Buffer.from(eventData.message),
92
+ messageHash: eventData.messageHash,
94
93
  };
95
94
  const [statePda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], programId);
96
95
  // Fetch the state to get the chainId
97
96
  const state = await program.account.state.fetch(statePda);
98
97
  const chainId = new anchor_1.BN(state.chainId);
99
- const relayHashUint8Array = (0, SvmUtils_2.calculateRelayHashUint8Array)(relayData, chainId);
98
+ const relayHashUint8Array = (0, svm_1.calculateRelayEventHashUint8Array)(relayEventData, chainId);
100
99
  const [fillStatusPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("fills"), relayHashUint8Array], programId);
101
100
  try {
102
101
  // Check if the fillStatusPda account exists
@@ -106,8 +105,8 @@ async function closeFillPda(eventData, seed) {
106
105
  return;
107
106
  }
108
107
  // Display additional information in a table
109
- console.log("Found a relay to close. Relay Data:");
110
- console.table(Object.entries(relayData).map(([key, value]) => ({
108
+ console.log("Found a relay to close. Relay event data:");
109
+ console.table(Object.entries(relayEventData).map(([key, value]) => ({
111
110
  key,
112
111
  value: value.toString(),
113
112
  })));
@@ -116,7 +115,7 @@ async function closeFillPda(eventData, seed) {
116
115
  { Property: "Fill Status PDA", Value: fillStatusPda.toString() },
117
116
  { Property: "Relay Hash", Value: Buffer.from(relayHashUint8Array).toString("hex") },
118
117
  ]);
119
- const tx = await program.methods.closeFillPda(Array.from(relayHashUint8Array), relayData)
118
+ const tx = await program.methods.closeFillPda()
120
119
  .accounts({
121
120
  state: statePda,
122
121
  signer: provider.wallet.publicKey,
@@ -39,6 +39,7 @@ anchor.setProvider(provider);
39
39
  const idl = require("../../target/idl/svm_spoke.json");
40
40
  const program = new anchor_1.Program(idl, provider);
41
41
  const programId = program.programId;
42
+ console.log("SVM-Spoke Program ID:", programId.toString());
42
43
  // Parse arguments
43
44
  const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
44
45
  .option("seed", { type: "string", demandOption: true, describe: "Seed for the state account PDA" })
@@ -54,7 +55,12 @@ async function enableRoute() {
54
55
  // Define the state account PDA
55
56
  const [statePda, _] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], programId);
56
57
  // Define the route account PDA
57
- const [routePda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("route"), originToken.toBytes(), statePda.toBytes(), chainId.toArrayLike(Buffer, "le", 8)], programId);
58
+ const [routePda] = web3_js_1.PublicKey.findProgramAddressSync([
59
+ Buffer.from("route"),
60
+ originToken.toBytes(),
61
+ seed.toArrayLike(Buffer, "le", 8),
62
+ chainId.toArrayLike(Buffer, "le", 8),
63
+ ], programId);
58
64
  // Define the signer (replace with your actual signer)
59
65
  const signer = provider.wallet.publicKey;
60
66
  console.log("Enabling route...");
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Script: Execute USDC Rebalance to Hub Pool
3
+ *
4
+ * This script executes a previously proposed root bundle on the Hub Pool to rebalance USDC from the Solana Spoke Pool
5
+ * to the Ethereum Hub Pool. It handles CCTP attestations, relayer refund leaf execution, and prepares pending
6
+ * liabilities for bridging back to the Hub Pool.
7
+ *
8
+ * Required Environment Variables:
9
+ * - MNEMONIC: Wallet mnemonic to sign the Ethereum transaction.
10
+ * - HUB_POOL_ADDRESS: Ethereum address of the Hub Pool.
11
+ * - NODE_URL_${CHAIN_ID}: Ethereum RPC URL (must point to the Mainnet or Sepolia depending on Solana cluster).
12
+ *
13
+ * Required Arguments:
14
+ * - `--netSendAmount`: The unscaled amount of USDC to rebalance (e.g., for USDC with 6 decimals, 1 = 0.000001 USDC).
15
+ * - `--resumeRemoteTx`: (Optional) Hash of a previously submitted remote transaction to resume.
16
+ *
17
+ * Example Usage:
18
+ * NODE_URL_11155111=$NODE_URL_11155111 \
19
+ * MNEMONIC=$MNEMONIC \
20
+ * HUB_POOL_ADDRESS=$HUB_POOL_ADDRESS \
21
+ * anchor run executeRebalanceToHubPool \
22
+ * --provider.cluster "devnet" \
23
+ * --provider.wallet $SOLANA_PKEY_PATH \
24
+ * -- --netSendAmount 7
25
+ *
26
+ * Note:
27
+ * - Ensure all required environment variables are properly configured.
28
+ */
29
+ export {};