@across-protocol/contracts 3.0.17 → 3.0.18

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 (52) hide show
  1. package/dist/scripts/svm/addressToPublicKey.d.ts +1 -0
  2. package/dist/scripts/svm/addressToPublicKey.js +20 -0
  3. package/dist/scripts/svm/bridgeLiabilityToHubPool.d.ts +28 -0
  4. package/dist/scripts/svm/bridgeLiabilityToHubPool.js +231 -0
  5. package/dist/scripts/svm/closeRelayerPdas.js +6 -7
  6. package/dist/scripts/svm/enableRoute.js +7 -1
  7. package/dist/scripts/svm/executeRebalanceToHubPool.d.ts +32 -0
  8. package/dist/scripts/svm/executeRebalanceToHubPool.js +355 -0
  9. package/dist/scripts/svm/executeRebalanceToSpokePool.d.ts +1 -0
  10. package/dist/scripts/svm/executeRebalanceToSpokePool.js +253 -0
  11. package/dist/scripts/svm/proposeRebalanceToHubPool.d.ts +27 -0
  12. package/dist/scripts/svm/proposeRebalanceToHubPool.js +117 -0
  13. package/dist/scripts/svm/proposeRebalanceToSpokePool.d.ts +1 -0
  14. package/dist/scripts/svm/proposeRebalanceToSpokePool.js +101 -0
  15. package/dist/scripts/svm/publicKeyToAddress.d.ts +1 -0
  16. package/dist/scripts/svm/publicKeyToAddress.js +20 -0
  17. package/dist/scripts/svm/queryDeposits.js +1 -0
  18. package/dist/scripts/svm/queryFills.js +11 -12
  19. package/dist/scripts/svm/queryRoute.js +7 -1
  20. package/dist/scripts/svm/queryState.js +1 -0
  21. package/dist/scripts/svm/remoteHubPoolPauseDeposits.d.ts +1 -0
  22. package/dist/scripts/svm/remoteHubPoolPauseDeposits.js +205 -0
  23. package/dist/scripts/svm/simpleDeposit.js +7 -1
  24. package/dist/scripts/svm/simpleFill.js +5 -4
  25. package/dist/scripts/svm/utils/constants.d.ts +4 -0
  26. package/dist/scripts/svm/utils/constants.js +8 -1
  27. package/dist/scripts/svm/utils/helpers.d.ts +31 -0
  28. package/dist/scripts/svm/utils/helpers.js +50 -1
  29. package/dist/scripts/svm/utils/poolRebalanceTree.d.ts +22 -0
  30. package/dist/scripts/svm/utils/poolRebalanceTree.js +20 -0
  31. package/dist/src/SvmUtils.d.ts +24 -2
  32. package/dist/src/SvmUtils.js +107 -26
  33. package/dist/target/types/svm_spoke.d.ts +47 -42
  34. package/dist/tasks/enableL1TokenAcrossEcosystem.js +3 -33
  35. package/dist/tasks/types.d.ts +2 -0
  36. package/dist/tasks/types.js +2 -0
  37. package/dist/tasks/utils.d.ts +12 -0
  38. package/dist/tasks/utils.js +34 -0
  39. package/dist/test/svm/SvmSpoke.Bundle.js +15 -16
  40. package/dist/test/svm/SvmSpoke.Deposit.js +18 -26
  41. package/dist/test/svm/SvmSpoke.Fill.AcrossPlus.js +1 -1
  42. package/dist/test/svm/SvmSpoke.Fill.js +13 -14
  43. package/dist/test/svm/SvmSpoke.Ownership.js +10 -16
  44. package/dist/test/svm/SvmSpoke.RefundClaims.js +9 -8
  45. package/dist/test/svm/SvmSpoke.Routes.js +10 -6
  46. package/dist/test/svm/SvmSpoke.SlowFill.AcrossPlus.js +59 -2
  47. package/dist/test/svm/SvmSpoke.SlowFill.js +23 -18
  48. package/dist/test/svm/SvmSpoke.TokenBridge.js +3 -5
  49. package/dist/test/svm/SvmSpoke.common.js +2 -1
  50. package/dist/test/svm/utils.d.ts +4 -5
  51. package/dist/test/svm/utils.js +24 -18
  52. package/package.json +2 -2
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ /**
3
+ * Script: Propose Root Bundle for USDC Rebalance to Hub Pool
4
+ *
5
+ * Submits a root bundle proposal on the Hub Pool to rebalance USDC from the Solana Spoke Pool to the Ethereum Hub Pool.
6
+ * After submission and the liveness period, the rebalance can be executed with `executeRebalanceToHubPool.ts`.
7
+ *
8
+ * Required Environment Variables:
9
+ * - TESTNET: (Optional) Set to "true" to use Sepolia; defaults to mainnet.
10
+ * - MNEMONIC: Wallet mnemonic to sign the Ethereum transaction.
11
+ * - HUB_POOL_ADDRESS: Ethereum address of the Hub Pool.
12
+ * - NODE_URL_1: Ethereum RPC URL for mainnet (ignored if TESTNET=true).
13
+ * - NODE_URL_11155111: Ethereum RPC URL for Sepolia (ignored if TESTNET=false).
14
+ *
15
+ * Required Argument:
16
+ * - `--netSendAmount`: The unscaled amount of USDC to rebalance. (e.g., for USDC with 6 decimals, 1 = 0.000001 USDC).
17
+ *
18
+ * Example Usage:
19
+ * TESTNET=true \
20
+ * NODE_URL_11155111=$NODE_URL_11155111 \
21
+ * MNEMONIC=$MNEMONIC \
22
+ * HUB_POOL_ADDRESS=$HUB_POOL_ADDRESS \
23
+ * anchor run proposeRebalanceToHubPool -- --netSendAmount 7
24
+ *
25
+ * Note:
26
+ * Ensure the required environment variables are set before running this script.
27
+ */
28
+ var __importDefault = (this && this.__importDefault) || function (mod) {
29
+ return (mod && mod.__esModule) ? mod : { "default": mod };
30
+ };
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ const web3_js_1 = require("@solana/web3.js");
33
+ const common_1 = require("@uma/common");
34
+ const ethers_1 = require("ethers");
35
+ const yargs_1 = __importDefault(require("yargs"));
36
+ const helpers_1 = require("yargs/helpers");
37
+ const typechain_1 = require("../../typechain");
38
+ const constants_1 = require("../../utils/constants");
39
+ const constants_2 = require("./utils/constants");
40
+ const helpers_2 = require("./utils/helpers");
41
+ // Set up Ethereum provider and signer.
42
+ const nodeURL = process.env.TESTNET === "true" ? (0, common_1.getNodeUrl)("sepolia", true) : (0, common_1.getNodeUrl)("mainnet", true);
43
+ const ethersProvider = new ethers_1.ethers.providers.JsonRpcProvider(nodeURL);
44
+ const ethersSigner = ethers_1.ethers.Wallet.fromMnemonic((0, helpers_2.requireEnv)("MNEMONIC")).connect(ethersProvider);
45
+ // Get the HubPool contract instance.
46
+ const hubPoolAddress = ethers_1.ethers.utils.getAddress((0, helpers_2.requireEnv)("HUB_POOL_ADDRESS"));
47
+ const hubPool = typechain_1.HubPool__factory.connect(hubPoolAddress, ethersProvider);
48
+ // Parse arguments.
49
+ const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)).option("netSendAmount", {
50
+ type: "string",
51
+ demandOption: true,
52
+ describe: "Net send amount to Hub Pool from Solana Spoke Pool",
53
+ }).argv;
54
+ async function proposeRebalanceToHubPool() {
55
+ const resolvedArgv = await argv;
56
+ const netSendAmount = ethers_1.BigNumber.from(resolvedArgv.netSendAmount);
57
+ // Resolve chain IDs and USDC address.
58
+ const evmChainId = (await ethersProvider.getNetwork()).chainId;
59
+ if (evmChainId !== constants_1.CHAIN_IDs.MAINNET && evmChainId !== constants_1.CHAIN_IDs.SEPOLIA) {
60
+ throw new Error("Unsupported EVM chain ID");
61
+ }
62
+ // If evmChainId is mainnet, use mainnet solana cluster. Otherwise, use devnet.
63
+ const solanaCluster = evmChainId === constants_1.CHAIN_IDs.MAINNET ? "mainnet" : "devnet";
64
+ const solanaChainId = (0, helpers_2.getSolanaChainId)(solanaCluster);
65
+ const svmUsdc = evmChainId === constants_1.CHAIN_IDs.MAINNET ? constants_2.SOLANA_USDC_MAINNET : constants_2.SOLANA_USDC_DEVNET;
66
+ // Check there are no active proposals.
67
+ const currentRootBundleProposal = await hubPool.callStatic.rootBundleProposal();
68
+ if (currentRootBundleProposal.unclaimedPoolRebalanceLeafCount !== 0) {
69
+ throw new Error("Proposal has unclaimed leaves");
70
+ }
71
+ // Ensure bond token balance and approval is sufficient.
72
+ const bondTokenAddress = await hubPool.callStatic.bondToken();
73
+ const bondAmount = await hubPool.callStatic.bondAmount();
74
+ const bondToken = typechain_1.BondToken__factory.connect(bondTokenAddress, ethersProvider);
75
+ const bondBalance = await bondToken.callStatic.balanceOf(ethersSigner.address);
76
+ if (bondBalance.lt(bondAmount)) {
77
+ const ethDeposit = bondAmount.sub(bondBalance);
78
+ console.log(`Depositing ${ethers_1.ethers.utils.formatUnits(ethDeposit.toString())} ETH into bond token:`);
79
+ const tx = await bondToken.connect(ethersSigner).deposit({ value: ethDeposit });
80
+ console.log(`✅ submitted tx hash: ${tx.hash}`);
81
+ await tx.wait();
82
+ console.log("✅ tx confirmed");
83
+ }
84
+ const allowance = await bondToken.callStatic.allowance(ethersSigner.address, hubPool.address);
85
+ if (allowance.lt(bondAmount)) {
86
+ console.log(`Approving ${ethers_1.ethers.utils.formatUnits(bondAmount.toString())} bond tokens for HubPool:`);
87
+ const tx = await bondToken.connect(ethersSigner).approve(hubPool.address, bondAmount);
88
+ console.log(`✅ submitted tx hash: ${tx.hash}`);
89
+ await tx.wait();
90
+ console.log("✅ tx confirmed");
91
+ }
92
+ // Construct an empty pool rebalance tree as we need to propose at least one leaf.
93
+ const { poolRebalanceTree } = (0, helpers_2.constructEmptyPoolRebalanceTree)(solanaChainId, 0);
94
+ // Relayer refund root Merkle tree.
95
+ const { merkleTree } = (0, helpers_2.constructSimpleRebalanceTreeToHubPool)(netSendAmount, solanaChainId, new web3_js_1.PublicKey(svmUsdc));
96
+ console.log("Proposing rebalance pool bundle to spoke...");
97
+ console.table([
98
+ { Property: "isTestnet", Value: process.env.TESTNET === "true" },
99
+ { Property: "originChainId", Value: evmChainId.toString() },
100
+ { Property: "targetChainId", Value: solanaChainId.toString() },
101
+ { Property: "hubPoolAddress", Value: hubPool.address },
102
+ { Property: "netSendAmount (formatted)", Value: (0, helpers_2.formatUsdc)(netSendAmount) },
103
+ { Property: "poolRebalanceRoot", Value: poolRebalanceTree.getHexRoot() },
104
+ { Property: "relayerRefundRoot", Value: merkleTree.getHexRoot() },
105
+ ]);
106
+ console.log("Submitting proposal...");
107
+ const tx = await hubPool.connect(ethersSigner).proposeRootBundle([0], // bundleEvaluationBlockNumbers, not checked in this script.
108
+ 1, // poolRebalanceLeafCount, only one leaf in this script.
109
+ poolRebalanceTree.getHexRoot(), // poolRebalanceRoot.
110
+ merkleTree.getHexRoot(), // relayerRefundRoot.
111
+ ethers_1.ethers.constants.HashZero // slowRelayRoot.
112
+ );
113
+ await tx.wait();
114
+ console.log("✅ proposal submitted");
115
+ }
116
+ // Run the proposeRebalanceToHubPool function.
117
+ proposeRebalanceToHubPool();
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ // This script proposes root bundle on HubPool that would rebalance tokens to Solana Spoke Pool once executed.
3
+ // Required environment:
4
+ // - ETHERS_PROVIDER_URL: Ethereum RPC provider URL.
5
+ // - ETHERS_MNEMONIC: Mnemonic of the wallet that will sign the sending transaction on Ethereum
6
+ // - HUB_POOL_ADDRESS: Hub Pool address
7
+ var __importDefault = (this && this.__importDefault) || function (mod) {
8
+ return (mod && mod.__esModule) ? mod : { "default": mod };
9
+ };
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ // eslint-disable-next-line camelcase
12
+ const constants_1 = require("../../utils/constants");
13
+ const yargs_1 = __importDefault(require("yargs"));
14
+ const helpers_1 = require("yargs/helpers");
15
+ const ethers_1 = require("ethers");
16
+ // eslint-disable-next-line camelcase
17
+ const typechain_1 = require("../../typechain");
18
+ const poolRebalanceTree_1 = require("./utils/poolRebalanceTree");
19
+ // Set up Ethereum provider.
20
+ if (!process.env.ETHERS_PROVIDER_URL) {
21
+ throw new Error("Environment variable ETHERS_PROVIDER_URL is not set");
22
+ }
23
+ const ethersProvider = new ethers_1.ethers.providers.JsonRpcProvider(process.env.ETHERS_PROVIDER_URL);
24
+ if (!process.env.ETHERS_MNEMONIC) {
25
+ throw new Error("Environment variable ETHERS_MNEMONIC is not set");
26
+ }
27
+ const ethersSigner = ethers_1.ethers.Wallet.fromMnemonic(process.env.ETHERS_MNEMONIC).connect(ethersProvider);
28
+ // Get the HubPool contract instance.
29
+ if (!process.env.HUB_POOL_ADDRESS) {
30
+ throw new Error("Environment variable HUB_POOL_ADDRESS is not set");
31
+ }
32
+ const hubPoolAddress = ethers_1.ethers.utils.getAddress(process.env.HUB_POOL_ADDRESS);
33
+ const hubPool = typechain_1.HubPool__factory.connect(hubPoolAddress, ethersProvider);
34
+ // Parse arguments
35
+ const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)).option("netSendAmount", {
36
+ type: "string",
37
+ demandOption: true,
38
+ describe: "Net send amount to spoke",
39
+ }).argv;
40
+ async function proposeRebalanceToSpokePool() {
41
+ const resolvedArgv = await argv;
42
+ const netSendAmount = ethers_1.BigNumber.from(resolvedArgv.netSendAmount);
43
+ // Resolve chain IDs and USDC address.
44
+ const evmChainId = (await ethersProvider.getNetwork()).chainId;
45
+ if (evmChainId !== constants_1.CHAIN_IDs.MAINNET && evmChainId !== constants_1.CHAIN_IDs.SEPOLIA)
46
+ throw new Error("Unsupported EVM chain ID");
47
+ const solanaCluster = evmChainId === constants_1.CHAIN_IDs.MAINNET ? "mainnet" : "devnet";
48
+ const solanaChainId = ethers_1.BigNumber.from(BigInt(ethers_1.ethers.utils.keccak256(ethers_1.ethers.utils.toUtf8Bytes(`solana-${solanaCluster}`))) & BigInt("0xFFFFFFFFFFFFFFFF"));
49
+ const l1TokenAddress = constants_1.TOKEN_SYMBOLS_MAP.USDC.addresses[evmChainId];
50
+ // Construct simple merkle tree for the pool rebalance.
51
+ const { poolRebalanceTree } = (0, poolRebalanceTree_1.constructSimpleRebalanceTree)(l1TokenAddress, netSendAmount, solanaChainId);
52
+ console.log("Proposing rebalance pool bundle to spoke...");
53
+ console.table([
54
+ { Property: "originChainId", Value: evmChainId.toString() },
55
+ { Property: "targetChainId", Value: solanaChainId.toString() },
56
+ { Property: "hubPoolAddress", Value: hubPool.address },
57
+ { Property: "l1TokenAddress", Value: l1TokenAddress },
58
+ { Property: "netSendAmount", Value: netSendAmount.toString() },
59
+ { Property: "poolRebalanceRoot", Value: poolRebalanceTree.getHexRoot() },
60
+ ]);
61
+ // Check there are no active proposals.
62
+ const currentRootBundleProposal = await hubPool.callStatic.rootBundleProposal();
63
+ if (currentRootBundleProposal.unclaimedPoolRebalanceLeafCount !== 0)
64
+ throw new Error("Proposal has unclaimed leaves");
65
+ // Ensure bond token balance and approval is sufficient
66
+ const bondTokenAddress = await hubPool.callStatic.bondToken();
67
+ const bondAmount = await hubPool.callStatic.bondAmount();
68
+ const bondToken = typechain_1.BondToken__factory.connect(bondTokenAddress, ethersProvider);
69
+ const bondBalance = await bondToken.callStatic.balanceOf(ethersSigner.address);
70
+ if (bondBalance.lt(bondAmount)) {
71
+ const ethDeposit = bondAmount.sub(bondBalance);
72
+ console.log(`Depositing ${ethers_1.ethers.utils.formatUnits(ethDeposit.toString())} ETH into bond token:`);
73
+ // This will throw if the signer does not have enough ETH.
74
+ const tx = await bondToken.connect(ethersSigner).deposit({ value: bondAmount.sub(bondBalance) });
75
+ console.log(`✔️ submitted tx hash: ${tx.hash}`);
76
+ await tx.wait();
77
+ console.log(`✔️ tx confirmed`);
78
+ }
79
+ const allowance = await bondToken.callStatic.allowance(ethersSigner.address, hubPool.address);
80
+ if (allowance.lt(bondAmount)) {
81
+ console.log(`Approving ${ethers_1.ethers.utils.formatUnits(bondAmount.toString())} bond tokens for HubPool:`);
82
+ const tx = await bondToken.connect(ethersSigner).approve(hubPool.address, bondAmount);
83
+ console.log(`✔️ submitted tx hash: ${tx.hash}`);
84
+ await tx.wait();
85
+ console.log(`✔️ tx confirmed`);
86
+ }
87
+ // Propose the rebalance to the spoke pool.
88
+ console.log(`Proposing ${netSendAmount.toString()} rebalance to spoke pool:`);
89
+ const tx = await hubPool.connect(ethersSigner).proposeRootBundle([0], // bundleEvaluationBlockNumbers, not checked in this script.
90
+ 1, // poolRebalanceLeafCount.
91
+ poolRebalanceTree.getHexRoot(), // poolRebalanceRoot. Generated from the merkle tree constructed before.
92
+ ethers_1.ethers.constants.HashZero, // relayerRefundRoot, not relevant for this script.
93
+ ethers_1.ethers.constants.HashZero // slowRelayRoot, not relevant for this test.
94
+ );
95
+ console.log(`✔️ submitted tx hash: ${tx.hash}`);
96
+ await tx.wait();
97
+ console.log(`✔️ tx confirmed`);
98
+ console.log("Rebalance proposal submitted successfully, to execute, run executeRebalanceToSpokePool script with the same netSendAmount after liveness period.");
99
+ }
100
+ // Run the proposeRebalanceToSpokePool function
101
+ proposeRebalanceToSpokePool();
@@ -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 SvmUtils_1 = require("../../src/SvmUtils");
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("publicKey", {
10
+ type: "string",
11
+ demandOption: true,
12
+ describe: "Public key to convert",
13
+ }).argv;
14
+ async function logEvmAddress() {
15
+ const publicKey = (await argv).publicKey;
16
+ const evmAddress = (0, SvmUtils_1.publicKeyToEvmAddress)(publicKey);
17
+ console.log("Public Key:", publicKey);
18
+ console.log("Associated Ethereum Address:", evmAddress);
19
+ }
20
+ logEvmAddress();
@@ -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 argvPromise = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)).option("seed", {
44
45
  type: "string",
@@ -33,6 +33,7 @@ const web3_js_1 = require("@solana/web3.js");
33
33
  const yargs_1 = __importDefault(require("yargs"));
34
34
  const helpers_1 = require("yargs/helpers");
35
35
  const SvmUtils_1 = require("../../src/SvmUtils");
36
+ const utils_1 = require("../../test/svm/utils");
36
37
  // Set up the provider
37
38
  const provider = anchor_1.AnchorProvider.env();
38
39
  anchor.setProvider(provider);
@@ -66,25 +67,23 @@ async function queryFills() {
66
67
  fillEvents.forEach((event, index) => {
67
68
  console.log(`Fill Event ${index + 1}:`);
68
69
  console.table([
69
- { Property: "inputToken", Value: new web3_js_1.PublicKey(event.data.inputToken).toString() },
70
- { Property: "outputToken", Value: new web3_js_1.PublicKey(event.data.outputToken).toString() },
70
+ { Property: "inputToken", Value: (0, SvmUtils_1.strPublicKey)(event.data.inputToken) },
71
+ { Property: "outputToken", Value: (0, SvmUtils_1.strPublicKey)(event.data.outputToken) },
71
72
  { Property: "inputAmount", Value: event.data.inputAmount.toString() },
72
73
  { Property: "outputAmount", Value: event.data.outputAmount.toString() },
73
74
  { Property: "repaymentChainId", Value: event.data.repaymentChainId.toString() },
74
75
  { Property: "originChainId", Value: event.data.originChainId.toString() },
75
76
  { Property: "depositId", Value: event.data.depositId.toString() },
77
+ { Property: "depositIdNum", Value: (0, utils_1.u8Array32ToInt)(event.data.depositId).toString() },
76
78
  { Property: "fillDeadline", Value: event.data.fillDeadline.toString() },
77
79
  { Property: "exclusivityDeadline", Value: event.data.exclusivityDeadline.toString() },
78
- { Property: "exclusiveRelayer", Value: new web3_js_1.PublicKey(event.data.exclusiveRelayer).toString() },
79
- { Property: "relayer", Value: new web3_js_1.PublicKey(event.data.relayer).toString() },
80
- { Property: "depositor", Value: new web3_js_1.PublicKey(event.data.depositor).toString() },
81
- { Property: "recipient", Value: new web3_js_1.PublicKey(event.data.recipient).toString() },
82
- { Property: "message", Value: event.data.message.toString() },
83
- {
84
- Property: "updatedRecipient",
85
- Value: new web3_js_1.PublicKey(event.data.relayExecutionInfo.updatedRecipient).toString(),
86
- },
87
- { Property: "updatedMessage", Value: event.data.relayExecutionInfo.updatedMessage.toString() },
80
+ { Property: "exclusiveRelayer", Value: (0, SvmUtils_1.strPublicKey)(event.data.exclusiveRelayer) },
81
+ { Property: "relayer", Value: (0, SvmUtils_1.strPublicKey)(event.data.relayer) },
82
+ { Property: "depositor", Value: (0, SvmUtils_1.strPublicKey)(event.data.depositor) },
83
+ { Property: "recipient", Value: (0, SvmUtils_1.strPublicKey)(event.data.recipient) },
84
+ { Property: "messageHash", Value: event.data.messageHash.toString() },
85
+ { Property: "updatedRecipient", Value: (0, SvmUtils_1.strPublicKey)(event.data.relayExecutionInfo.updatedRecipient) },
86
+ { Property: "updatedMessageHash", Value: event.data.relayExecutionInfo.updatedMessageHash.toString() },
88
87
  { Property: "updatedOutputAmount", Value: event.data.relayExecutionInfo.updatedOutputAmount.toString() },
89
88
  { Property: "fillType", Value: event.data.relayExecutionInfo.fillType },
90
89
  ]);
@@ -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" })
@@ -52,7 +53,12 @@ async function queryRoute() {
52
53
  // Define the state account PDA
53
54
  const [statePda, _] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], programId);
54
55
  // Define the route account PDA
55
- const [routePda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("route"), Buffer.from(originToken), statePda.toBytes(), chainId.toArrayLike(Buffer, "le", 8)], programId);
56
+ const [routePda] = web3_js_1.PublicKey.findProgramAddressSync([
57
+ Buffer.from("route"),
58
+ Buffer.from(originToken),
59
+ seed.toArrayLike(Buffer, "le", 8),
60
+ chainId.toArrayLike(Buffer, "le", 8),
61
+ ], programId);
56
62
  // Compute the vault address
57
63
  const vault = (0, spl_token_1.getAssociatedTokenAddressSync)(new web3_js_1.PublicKey(originToken), statePda, true, spl_token_1.TOKEN_PROGRAM_ID, spl_token_1.ASSOCIATED_TOKEN_PROGRAM_ID);
58
64
  console.log("Querying route...");
@@ -38,6 +38,7 @@ anchor.setProvider(provider);
38
38
  const idl = require("../../target/idl/svm_spoke.json");
39
39
  const program = new anchor_1.Program(idl, provider);
40
40
  const programId = program.programId;
41
+ console.log("SVM-Spoke Program ID:", programId.toString());
41
42
  // Parse arguments
42
43
  const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv)).option("seed", {
43
44
  type: "string",
@@ -0,0 +1 @@
1
+ import "dotenv/config";
@@ -0,0 +1,205 @@
1
+ "use strict";
2
+ // This script bridges remote call to pause deposits on Solana Spoke Pool. Required environment:
3
+ // - ETHERS_PROVIDER_URL: Ethereum RPC provider URL.
4
+ // - ETHERS_MNEMONIC: Mnemonic of the wallet that will sign the sending transaction on Ethereum
5
+ // - HUB_POOL_ADDRESS: Hub Pool address
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || function (mod) {
23
+ if (mod && mod.__esModule) return mod;
24
+ var result = {};
25
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
26
+ __setModuleDefault(result, mod);
27
+ return result;
28
+ };
29
+ var __importDefault = (this && this.__importDefault) || function (mod) {
30
+ return (mod && mod.__esModule) ? mod : { "default": mod };
31
+ };
32
+ Object.defineProperty(exports, "__esModule", { value: true });
33
+ const anchor = __importStar(require("@coral-xyz/anchor"));
34
+ const anchor_1 = require("@coral-xyz/anchor");
35
+ const web3_js_1 = require("@solana/web3.js");
36
+ require("dotenv/config");
37
+ const ethers_1 = require("ethers");
38
+ const yargs_1 = __importDefault(require("yargs"));
39
+ const helpers_1 = require("yargs/helpers");
40
+ const cctpHelpers_1 = require("../../test/svm/cctpHelpers");
41
+ const typechain_1 = require("../../typechain");
42
+ const constants_1 = require("./utils/constants");
43
+ // Set up Solana provider.
44
+ const provider = anchor_1.AnchorProvider.env();
45
+ anchor.setProvider(provider);
46
+ // Parse arguments
47
+ const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
48
+ .option("chainId", { type: "string", demandOption: true, describe: "Chain ID" })
49
+ .option("pause", { type: "boolean", demandOption: true, describe: "Pause deposits" })
50
+ .option("resumeRemoteTx", { type: "string", demandOption: false, describe: "Resume receiving remote tx" }).argv;
51
+ async function remoteHubPoolPauseDeposit() {
52
+ const resolvedArgv = await argv;
53
+ const chainId = resolvedArgv.chainId;
54
+ const seed = new anchor_1.BN(0);
55
+ const resumeRemoteTx = resolvedArgv.resumeRemoteTx;
56
+ const pause = resolvedArgv.pause;
57
+ // Set up Ethereum provider.
58
+ if (!process.env.ETHERS_PROVIDER_URL) {
59
+ throw new Error("Environment variable ETHERS_PROVIDER_URL is not set");
60
+ }
61
+ const ethersProvider = new ethers_1.ethers.providers.JsonRpcProvider(process.env.ETHERS_PROVIDER_URL);
62
+ if (!process.env.ETHERS_MNEMONIC) {
63
+ throw new Error("Environment variable ETHERS_MNEMONIC is not set");
64
+ }
65
+ const ethersSigner = ethers_1.ethers.Wallet.fromMnemonic(process.env.ETHERS_MNEMONIC).connect(ethersProvider);
66
+ if (!process.env.HUB_POOL_ADDRESS) {
67
+ throw new Error("Environment variable HUB_POOL_ADDRESS is not set");
68
+ }
69
+ const hubPoolAddress = process.env.HUB_POOL_ADDRESS;
70
+ let cluster;
71
+ const rpcEndpoint = provider.connection.rpcEndpoint;
72
+ if (rpcEndpoint.includes("devnet"))
73
+ cluster = "devnet";
74
+ else if (rpcEndpoint.includes("mainnet"))
75
+ cluster = "mainnet";
76
+ else
77
+ throw new Error(`Unsupported cluster endpoint: ${rpcEndpoint}`);
78
+ const isDevnet = cluster == "devnet";
79
+ // CCTP domains.
80
+ const remoteDomain = 0; // Ethereum
81
+ // Get Solana programs and accounts.
82
+ const svmSpokeIdl = require("../../target/idl/svm_spoke.json");
83
+ const svmSpokeProgram = new anchor_1.Program(svmSpokeIdl, provider);
84
+ const [statePda, _] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], svmSpokeProgram.programId);
85
+ const messageTransmitterIdl = require("../../target/idl/message_transmitter.json");
86
+ const messageTransmitterProgram = new anchor_1.Program(messageTransmitterIdl, provider);
87
+ const [messageTransmitterState] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("message_transmitter")], messageTransmitterProgram.programId);
88
+ const [authorityPda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("message_transmitter_authority"), svmSpokeProgram.programId.toBuffer()], messageTransmitterProgram.programId);
89
+ const [selfAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("self_authority")], svmSpokeProgram.programId);
90
+ const [eventAuthority] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("__event_authority")], svmSpokeProgram.programId);
91
+ const irisApiUrl = isDevnet ? constants_1.CIRCLE_IRIS_API_URL_DEVNET : constants_1.CIRCLE_IRIS_API_URL_MAINNET;
92
+ const hubPool = typechain_1.HubPool__factory.connect(hubPoolAddress, ethersProvider);
93
+ const spokePoolIface = new ethers_1.ethers.utils.Interface(["function pauseDeposits(bool pause)"]);
94
+ console.log("Remotely configuring deposit route...");
95
+ console.table([
96
+ { Property: "seed", Value: seed.toString() },
97
+ { Property: "chainId", Value: chainId.toString() },
98
+ { Property: "pause", Value: pause },
99
+ { Property: "svmSpokeProgramProgramId", Value: svmSpokeProgram.programId.toString() },
100
+ { Property: "providerPublicKey", Value: provider.wallet.publicKey.toString() },
101
+ { Property: "statePda", Value: statePda.toString() },
102
+ { Property: "messageTransmitterProgramId", Value: messageTransmitterProgram.programId.toString() },
103
+ { Property: "messageTransmitterState", Value: messageTransmitterState.toString() },
104
+ { Property: "authorityPda", Value: authorityPda.toString() },
105
+ { Property: "selfAuthority", Value: selfAuthority.toString() },
106
+ { Property: "eventAuthority", Value: eventAuthority.toString() },
107
+ { Property: "remoteSender", Value: ethersSigner.address },
108
+ ]);
109
+ // Send pauseDeposits call from Ethereum, unless resuming a remote transaction.
110
+ let remoteTxHash;
111
+ if (!resumeRemoteTx) {
112
+ console.log("Sending pauseDeposits message from HubPool...");
113
+ const calldata = spokePoolIface.encodeFunctionData("pauseDeposits", [pause]);
114
+ const tx = await hubPool.connect(ethersSigner).relaySpokePoolAdminFunction(chainId, calldata);
115
+ await tx.wait();
116
+ remoteTxHash = tx.hash;
117
+ console.log("Message sent on remote chain, tx", remoteTxHash);
118
+ }
119
+ else
120
+ remoteTxHash = resumeRemoteTx;
121
+ // Fetch attestation from CCTP attestation service.
122
+ const attestationResponse = await (0, cctpHelpers_1.getMessages)(remoteTxHash, remoteDomain, irisApiUrl);
123
+ const { attestation, message } = attestationResponse.messages[0];
124
+ console.log("CCTP attestation response:", attestationResponse.messages[0]);
125
+ // Accounts in CCTP message_transmitter receive_message instruction.
126
+ const nonce = (0, cctpHelpers_1.decodeMessageHeader)(Buffer.from(message.replace("0x", ""), "hex")).nonce;
127
+ const usedNonces = (await messageTransmitterProgram.methods
128
+ .getNoncePda({
129
+ nonce: new anchor_1.BN(nonce.toString()),
130
+ sourceDomain: remoteDomain,
131
+ })
132
+ .accounts({
133
+ messageTransmitter: messageTransmitterState,
134
+ })
135
+ .view());
136
+ const receiveMessageAccounts = {
137
+ payer: provider.wallet.publicKey,
138
+ caller: provider.wallet.publicKey,
139
+ authorityPda,
140
+ messageTransmitter: messageTransmitterState,
141
+ usedNonces,
142
+ receiver: svmSpokeProgram.programId,
143
+ systemProgram: anchor_1.web3.SystemProgram.programId,
144
+ };
145
+ // accountMetas list to pass to remaining accounts when receiving message via CCTP.
146
+ const remainingAccounts = [];
147
+ // state in HandleReceiveMessage accounts (used for remote domain and sender authentication).
148
+ remainingAccounts.push({
149
+ isSigner: false,
150
+ isWritable: false,
151
+ pubkey: statePda,
152
+ });
153
+ // self_authority in HandleReceiveMessage accounts, also signer in self-invoked CPIs.
154
+ remainingAccounts.push({
155
+ isSigner: false,
156
+ isWritable: false,
157
+ pubkey: selfAuthority,
158
+ });
159
+ // program in HandleReceiveMessage accounts.
160
+ remainingAccounts.push({
161
+ isSigner: false,
162
+ isWritable: false,
163
+ pubkey: svmSpokeProgram.programId,
164
+ });
165
+ // state
166
+ remainingAccounts.push({
167
+ isSigner: false,
168
+ isWritable: true,
169
+ pubkey: statePda,
170
+ });
171
+ // event_authority in self-invoked CPIs (appended by Anchor with event_cpi macro).
172
+ remainingAccounts.push({
173
+ isSigner: false,
174
+ isWritable: true,
175
+ pubkey: eventAuthority,
176
+ });
177
+ // program
178
+ remainingAccounts.push({
179
+ isSigner: false,
180
+ isWritable: true,
181
+ pubkey: svmSpokeProgram.programId,
182
+ });
183
+ // Receive remote message on Solana.
184
+ console.log("Receiving message on Solana...");
185
+ const receiveMessageTx = await messageTransmitterProgram.methods
186
+ .receiveMessage({
187
+ message: Buffer.from(message.replace("0x", ""), "hex"),
188
+ attestation: Buffer.from(attestation.replace("0x", ""), "hex"),
189
+ })
190
+ .accounts(receiveMessageAccounts)
191
+ .remainingAccounts(remainingAccounts)
192
+ .rpc();
193
+ console.log("\nReceived remote message");
194
+ console.log("Your transaction signature", receiveMessageTx);
195
+ let stateAccount = await svmSpokeProgram.account.state.fetch(statePda);
196
+ console.log("Updated deposit route state to: pausedDeposits =", stateAccount.pausedDeposits);
197
+ }
198
+ remoteHubPoolPauseDeposit()
199
+ .then(() => {
200
+ process.exit(0);
201
+ })
202
+ .catch((err) => {
203
+ console.error(err);
204
+ process.exit(1);
205
+ });
@@ -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" })
@@ -65,7 +66,12 @@ async function depositV3() {
65
66
  // Define the state account PDA
66
67
  const [statePda, _] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], programId);
67
68
  // Define the route account PDA
68
- const [routePda] = web3_js_1.PublicKey.findProgramAddressSync([Buffer.from("route"), inputToken.toBytes(), statePda.toBytes(), destinationChainId.toArrayLike(Buffer, "le", 8)], programId);
69
+ const [routePda] = web3_js_1.PublicKey.findProgramAddressSync([
70
+ Buffer.from("route"),
71
+ inputToken.toBytes(),
72
+ seed.toArrayLike(Buffer, "le", 8),
73
+ destinationChainId.toArrayLike(Buffer, "le", 8),
74
+ ], programId);
69
75
  // Define the signer (replace with your actual signer)
70
76
  const signer = provider.wallet.payer;
71
77
  // Find ATA for the input token to be stored by state (vault). This was created when the route was enabled.
@@ -35,6 +35,7 @@ const spl_token_1 = require("@solana/spl-token");
35
35
  const yargs_1 = __importDefault(require("yargs"));
36
36
  const helpers_1 = require("yargs/helpers");
37
37
  const SvmUtils_1 = require("../../src/SvmUtils");
38
+ const utils_1 = require("../../test/svm/utils");
38
39
  // Set up the provider
39
40
  const provider = anchor_1.AnchorProvider.env();
40
41
  anchor.setProvider(provider);
@@ -52,7 +53,7 @@ const argv = (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
52
53
  .option("inputAmount", { type: "number", demandOption: true, describe: "Input amount" })
53
54
  .option("outputAmount", { type: "number", demandOption: true, describe: "Output amount" })
54
55
  .option("originChainId", { type: "string", demandOption: true, describe: "Origin chain ID" })
55
- .option("depositId", { type: "array", demandOption: true, describe: "Deposit ID" })
56
+ .option("depositId", { type: "string", demandOption: true, describe: "Deposit ID" })
56
57
  .option("fillDeadline", { type: "number", demandOption: false, describe: "Fill deadline" })
57
58
  .option("exclusivityDeadline", { type: "number", demandOption: false, describe: "Exclusivity deadline" }).argv;
58
59
  async function fillV3Relay() {
@@ -65,7 +66,7 @@ async function fillV3Relay() {
65
66
  const inputAmount = new anchor_1.BN(resolvedArgv.inputAmount);
66
67
  const outputAmount = new anchor_1.BN(resolvedArgv.outputAmount);
67
68
  const originChainId = new anchor_1.BN(resolvedArgv.originChainId);
68
- const depositId = resolvedArgv.depositId;
69
+ const depositId = (0, utils_1.intToU8Array32)(new anchor_1.BN(resolvedArgv.depositId));
69
70
  const fillDeadline = resolvedArgv.fillDeadline || Math.floor(Date.now() / 1000) + 60; // Current time + 1 minute
70
71
  const exclusivityDeadline = resolvedArgv.exclusivityDeadline || Math.floor(Date.now() / 1000) + 30; // Current time + 30 seconds
71
72
  const message = Buffer.from("");
@@ -79,7 +80,7 @@ async function fillV3Relay() {
79
80
  inputAmount,
80
81
  outputAmount,
81
82
  originChainId,
82
- depositId: depositId.map((id) => Number(id)),
83
+ depositId,
83
84
  fillDeadline,
84
85
  exclusivityDeadline,
85
86
  message,
@@ -122,7 +123,7 @@ async function fillV3Relay() {
122
123
  state: statePda,
123
124
  signer: signer.publicKey,
124
125
  instructionParams: program.programId,
125
- mintAccount: outputToken,
126
+ mint: outputToken,
126
127
  relayerTokenAccount: relayerTokenAccount,
127
128
  recipientTokenAccount: recipientTokenAccount,
128
129
  fillStatus: fillStatusPda,
@@ -1,4 +1,8 @@
1
+ import BN from "bn.js";
1
2
  export declare const CIRCLE_IRIS_API_URL_DEVNET = "https://iris-api-sandbox.circle.com";
2
3
  export declare const CIRCLE_IRIS_API_URL_MAINNET = "https://iris-api.circle.com";
3
4
  export declare const SOLANA_USDC_MAINNET = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
4
5
  export declare const SOLANA_USDC_DEVNET = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU";
6
+ export declare const SEPOLIA_CCTP_MESSAGE_TRANSMITTER_ADDRESS = "0x7865fAfC2db2093669d92c0F33AeEF291086BEFD";
7
+ export declare const MAINNET_CCTP_MESSAGE_TRANSMITTER_ADDRESS = "0x0a992d191deec32afe36203ad87d7d289a738f81";
8
+ export declare const SOLANA_SPOKE_STATE_SEED: BN;
@@ -1,7 +1,14 @@
1
1
  "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
2
5
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SOLANA_USDC_DEVNET = exports.SOLANA_USDC_MAINNET = exports.CIRCLE_IRIS_API_URL_MAINNET = exports.CIRCLE_IRIS_API_URL_DEVNET = void 0;
6
+ exports.SOLANA_SPOKE_STATE_SEED = exports.MAINNET_CCTP_MESSAGE_TRANSMITTER_ADDRESS = exports.SEPOLIA_CCTP_MESSAGE_TRANSMITTER_ADDRESS = exports.SOLANA_USDC_DEVNET = exports.SOLANA_USDC_MAINNET = exports.CIRCLE_IRIS_API_URL_MAINNET = exports.CIRCLE_IRIS_API_URL_DEVNET = void 0;
7
+ const bn_js_1 = __importDefault(require("bn.js"));
4
8
  exports.CIRCLE_IRIS_API_URL_DEVNET = "https://iris-api-sandbox.circle.com";
5
9
  exports.CIRCLE_IRIS_API_URL_MAINNET = "https://iris-api.circle.com";
6
10
  exports.SOLANA_USDC_MAINNET = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
7
11
  exports.SOLANA_USDC_DEVNET = "4zMMC9srt5Ri5X14GAgXhaHii3GnPAEERYPJgZJDncDU";
12
+ exports.SEPOLIA_CCTP_MESSAGE_TRANSMITTER_ADDRESS = "0x7865fAfC2db2093669d92c0F33AeEF291086BEFD";
13
+ exports.MAINNET_CCTP_MESSAGE_TRANSMITTER_ADDRESS = "0x0a992d191deec32afe36203ad87d7d289a738f81";
14
+ exports.SOLANA_SPOKE_STATE_SEED = new bn_js_1.default(0);