diff --git a/.gitignore b/.gitignore index 1fc9a54dd..99c25217f 100644 --- a/.gitignore +++ b/.gitignore @@ -32,3 +32,4 @@ target **/*.rs.bk test-ledger idls +src/svm/assets diff --git a/deployments/README.md b/deployments/README.md index c0601b886..9dc40252d 100644 --- a/deployments/README.md +++ b/deployments/README.md @@ -133,7 +133,7 @@ This is because this `deployments.json` file is used by bots in [`@across-protoc ## Ink mainnet (57073) -| Contract Name | Address | -| ------------- | -------------------------------------------------------------------------------------------------------------------------------- | -| Ink_SpokePool | [0xeF684C38F94F48775959ECf2012D7E864ffb9dd4](https://explorer.inkonchain.com/address/0xeF684C38F94F48775959ECf2012D7E864ffb9dd4) | -| MulticallHandler | [0x924a9f036260DdD5808007E1AA95f08eD08aA569](https://explorer.inkonchain.com/address/0x924a9f036260DdD5808007E1AA95f08eD08aA569) | +| Contract Name | Address | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------- | +| Ink_SpokePool | [0xeF684C38F94F48775959ECf2012D7E864ffb9dd4](https://explorer.inkonchain.com/address/0xeF684C38F94F48775959ECf2012D7E864ffb9dd4) | +| MulticallHandler | [0x924a9f036260DdD5808007E1AA95f08eD08aA569](https://explorer.inkonchain.com/address/0x924a9f036260DdD5808007E1AA95f08eD08aA569) | diff --git a/deployments/deployments.json b/deployments/deployments.json index 5f4d50bf4..092b53757 100644 --- a/deployments/deployments.json +++ b/deployments/deployments.json @@ -163,5 +163,10 @@ "SpokePool": { "address": "0xeF684C38F94F48775959ECf2012D7E864ffb9dd4", "blockNumber": 1139240 }, "SpokePoolVerifier": { "address": "0xB4A8d45647445EA9FC3E1058096142390683dBC2", "blockNumber": 1152853 }, "MulticallHandler": { "address": "0x924a9f036260DdD5808007E1AA95f08eD08aA569", "blockNumber": 1145284 } + }, + "1810017368444177321": { + "SvmSpoke": { "address": "YVMQN27RnCNt23NRxzJPumXRd8iovEfKtzkqyMc5vDt", "blockNumber": 0 }, + "MessageTransmitter": { "address": "CCTPmbSD7gX1bxKPAmg77w8oFzNFpaQiQUWD43TKaecd", "blockNumber": 0 }, + "TokenMessengerMinter": { "address": "CCTPiPYPc6AsJuwueEnWgSgucamXDZwBd53dQ11YiKX3", "blockNumber": 0 } } } diff --git a/deployments/ink/SpokePoolVerifier.json b/deployments/ink/SpokePoolVerifier.json index 7d25b4799..5344f10b1 100644 --- a/deployments/ink/SpokePoolVerifier.json +++ b/deployments/ink/SpokePoolVerifier.json @@ -112,4 +112,4 @@ "storage": [], "types": null } -} \ No newline at end of file +} diff --git a/deployments/ink/solcInputs/53ab13385da43753d9a657e7780a0560.json b/deployments/ink/solcInputs/53ab13385da43753d9a657e7780a0560.json index b640ddc74..b9b6c4373 100644 --- a/deployments/ink/solcInputs/53ab13385da43753d9a657e7780a0560.json +++ b/deployments/ink/solcInputs/53ab13385da43753d9a657e7780a0560.json @@ -33,13 +33,11 @@ "storageLayout", "evm.gasEstimates" ], - "": [ - "ast" - ] + "": ["ast"] } }, "metadata": { "useLiteralContent": true } } -} \ No newline at end of file +} diff --git a/index.ts b/index.ts index 018dc8eb0..9b499d4b8 100644 --- a/index.ts +++ b/index.ts @@ -1,3 +1,4 @@ export * from "./typechain"; export * from "./src/DeploymentUtils"; export * from "./utils"; +export * from "./src/svm"; diff --git a/package.json b/package.json index ffb1b1d17..47b32cb03 100644 --- a/package.json +++ b/package.json @@ -26,16 +26,17 @@ "lint-fix": "yarn prettier --write **/*.js **/*.ts ./programs/**/*.rs ./contracts**/*.sol && cargo +nightly fmt --all && cargo clippy", "clean-fast": "for dir in node_modules cache cache-zk artifacts artifacts-zk dist typechain; do mv \"${dir}\" \"_${dir}\"; rm -rf \"_${dir}\" &; done", "clean": "rm -rf node_modules cache cache-zk artifacts artifacts-zk dist typechain", + "generate-svm-assets": "sh ./scripts/generate-svm-assets.sh", "build-evm": "hardhat compile", "build-svm": "echo 'Generating IDLs...' && anchor build > /dev/null 2>&1 || true && anchor run generateExternalTypes && anchor build", "build-ts": "tsc && rsync -a --include '*/' --include '*.d.ts' --exclude '*' ./typechain ./dist/", - "build": "yarn build-evm && yarn build-svm && yarn build-ts", + "build": "yarn build-evm && yarn build-svm && yarn generate-svm-assets && yarn build-ts", "test-evm": "IS_TEST=true hardhat test", "test-svm": "anchor test -- --features test", "test": "yarn test-evm && yarn test-svm", "test:report-gas": "IS_TEST=true REPORT_GAS=true hardhat test", - "generate-contract-types": "rm -rf typechain && TYPECHAIN=ethers yarn hardhat typechain", - "prepublish": "yarn build && hardhat export --export-all ./cache/massExport.json && ts-node ./scripts/processHardhatExport.ts && prettier --write ./deployments/deployments.json && yarn generate-contract-types", + "generate-evm-assets": "rm -rf typechain && TYPECHAIN=ethers yarn hardhat typechain", + "prepublish": "yarn build && hardhat export --export-all ./cache/massExport.json && ts-node ./scripts/processHardhatExport.ts && prettier --write ./deployments/deployments.json && yarn generate-evm-assets", "pre-commit-hook": "sh scripts/preCommitHook.sh" }, "dependencies": { diff --git a/scripts/generate-svm-assets.sh b/scripts/generate-svm-assets.sh new file mode 100644 index 000000000..f5c6ea505 --- /dev/null +++ b/scripts/generate-svm-assets.sh @@ -0,0 +1,89 @@ +#!/bin/bash + +# Set paths for source and destination +TARGET_IDL="./target/idl" +TARGET_TYPES="./target/types" +SVM_ASSETS="./src/svm/assets" +SVM_IDL="$SVM_ASSETS/idl" +SVM_TYPES="$SVM_ASSETS" +IDL_OUTPUT_FILE="$SVM_IDL/index.ts" +TYPES_OUTPUT_FILE="$SVM_TYPES/index.ts" + +# Ensure the destination directories exist +mkdir -p "$SVM_IDL" +mkdir -p "$SVM_TYPES" + +# --- Copy Files --- +echo "Copying IDL files..." +cp -r "$TARGET_IDL/"* "$SVM_IDL/" + +echo "Copying Types files..." +cp -r "$TARGET_TYPES/"* "$SVM_TYPES/" + +# --- Generate IDL index.ts --- +echo "Generating IDL index.ts..." +> "$IDL_OUTPUT_FILE" + +# Add autogenerated file note +{ + echo "// This file has been autogenerated. Do not edit manually." + echo "// Generated by a script." + echo +} >> "$IDL_OUTPUT_FILE" + +IMPORTS="" +EXPORTS="" + +for file in "$SVM_IDL"/*.json; do + filename=$(basename -- "$file") + name="${filename%.json}" + camelCaseName=$(echo "$name" | awk -F'_' '{ + for (i=1; i<=NF; i++) { + printf toupper(substr($i,1,1)) tolower(substr($i,2)); + } + }') + IMPORTS="${IMPORTS}const ${camelCaseName}Idl = require(\"./${filename}\");\n" + EXPORTS="${EXPORTS} ${camelCaseName}Idl,\n" +done + +# Write the imports to the file +printf "$IMPORTS" >> "$IDL_OUTPUT_FILE" + +# Write the exports block +{ + echo "export {" + printf "$EXPORTS" | sed '$ s/,$//' + echo "};" +} >> "$IDL_OUTPUT_FILE" + +echo "IDL index.ts generated successfully at $IDL_OUTPUT_FILE" + +# --- Generate svm-types index.ts --- +echo "Generating svm-types index.ts..." +> "$TYPES_OUTPUT_FILE" + +# Add autogenerated file note +{ + echo "// This file has been autogenerated. Do not edit manually." + echo "// Generated by a script." + echo +} >> "$TYPES_OUTPUT_FILE" + +# Export * from ./idl +echo "export * from \"./idl\";" >> "$TYPES_OUTPUT_FILE" + +# Export * from each .ts file in ./svm-types, removing underscores and capitalizing names +for file in "$SVM_TYPES"/*.ts; do + [ "$(basename -- "$file")" = "index.ts" ] && continue + filename=$(basename -- "$file") + name="${filename%.ts}" + camelCaseName=$(echo "$name" | awk -F'_' '{ + for (i=1; i<=NF; i++) { + printf toupper(substr($i,1,1)) tolower(substr($i,2)); + } + }') + newName="${camelCaseName}Anchor" + echo "export {${camelCaseName} as ${newName}} from \"./${name}\";" >> "$TYPES_OUTPUT_FILE" +done + +echo "svm-types index.ts generated successfully at $TYPES_OUTPUT_FILE" \ No newline at end of file diff --git a/scripts/svm/bridgeLiabilityToHubPool.ts b/scripts/svm/bridgeLiabilityToHubPool.ts index c48db5794..9794e4999 100644 --- a/scripts/svm/bridgeLiabilityToHubPool.ts +++ b/scripts/svm/bridgeLiabilityToHubPool.ts @@ -24,13 +24,10 @@ */ import * as anchor from "@coral-xyz/anchor"; -import { AnchorProvider, BN, Program } from "@coral-xyz/anchor"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { PublicKey, SystemProgram } from "@solana/web3.js"; // eslint-disable-next-line camelcase -import { MessageTransmitter } from "../../target/types/message_transmitter"; -import { SvmSpoke } from "../../target/types/svm_spoke"; -// eslint-disable-next-line camelcase import { CIRCLE_IRIS_API_URL_DEVNET, CIRCLE_IRIS_API_URL_MAINNET, @@ -46,7 +43,7 @@ import { import { TOKEN_SYMBOLS_MAP } from "@across-protocol/constants"; import { getNodeUrl } from "@uma/common"; import { BigNumber, ethers } from "ethers"; -import { TokenMessengerMinter } from "../../target/types/token_messenger_minter"; +import { getMessageTransmitterProgram, getSpokePoolProgram, getTokenMessengerMinterProgram } from "../../src/svm"; import { BondToken__factory } from "../../typechain"; import { formatUsdc, requireEnv } from "./utils/helpers"; @@ -55,10 +52,7 @@ const provider = AnchorProvider.env(); anchor.setProvider(provider); // Get Solana programs and IDLs. -const svmSpokeIdl = require("../../target/idl/svm_spoke.json"); -const svmSpokeProgram = new Program(svmSpokeIdl, provider); -const messageTransmitterIdl = require("../../target/idl/message_transmitter.json"); -const tokenMessengerMinterIdl = require("../../target/idl/token_messenger_minter.json"); +const svmSpokeProgram = getSpokePoolProgram(provider); // CCTP domains. const ethereumDomain = 0; // Ethereum @@ -200,7 +194,7 @@ async function bridgeLiabilityToHubPool(): Promise { } async function bridgeTokensToHubPool(amount: BN, signer: anchor.Wallet, statePda: PublicKey, inputToken: PublicKey) { - const messageTransmitterProgram = new Program(messageTransmitterIdl, provider); + const messageTransmitterProgram = getMessageTransmitterProgram(provider); const vault = getAssociatedTokenAddressSync( inputToken, @@ -215,7 +209,7 @@ async function bridgeTokensToHubPool(amount: BN, signer: anchor.Wallet, statePda [Buffer.from("transfer_liability"), inputToken.toBuffer()], svmSpokeProgram.programId ); - const tokenMessengerMinterProgram = new Program(tokenMessengerMinterIdl, provider); + const tokenMessengerMinterProgram = getTokenMessengerMinterProgram(provider); const [tokenMessengerMinterSenderAuthority] = PublicKey.findProgramAddressSync( [Buffer.from("sender_authority")], diff --git a/scripts/svm/closeRelayerPdas.ts b/scripts/svm/closeRelayerPdas.ts index 4e15c6f51..c9c5d8dbe 100644 --- a/scripts/svm/closeRelayerPdas.ts +++ b/scripts/svm/closeRelayerPdas.ts @@ -1,18 +1,16 @@ // This script closes all Relayer PDAs associated with tracking fill Status. Relayers should do this periodically to // reclaim the lamports within these tracking accounts. Fill Status PDAs can be closed on the deposit has expired. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { PublicKey, SystemProgram } from "@solana/web3.js"; -import { SvmSpoke } from "../../target/types/svm_spoke"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import { calculateRelayEventHashUint8Array, readProgramEvents } from "../../src/svm"; +import { calculateRelayEventHashUint8Array, getSpokePoolProgram, readProgramEvents } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; // Parse arguments diff --git a/scripts/svm/enableRoute.ts b/scripts/svm/enableRoute.ts index 061df09ec..3849a8a01 100644 --- a/scripts/svm/enableRoute.ts +++ b/scripts/svm/enableRoute.ts @@ -1,18 +1,17 @@ // This script is used by a chain admin to enable or disable a route for a token on the Solana Spoke Pool. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; -import { PublicKey, SystemProgram } from "@solana/web3.js"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token"; -import { SvmSpoke } from "../../target/types/svm_spoke"; +import { PublicKey, SystemProgram } from "@solana/web3.js"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; +import { getSpokePoolProgram } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; console.log("SVM-Spoke Program ID:", programId.toString()); diff --git a/scripts/svm/executeRebalanceToHubPool.ts b/scripts/svm/executeRebalanceToHubPool.ts index f4258ce8a..9b3274a36 100644 --- a/scripts/svm/executeRebalanceToHubPool.ts +++ b/scripts/svm/executeRebalanceToHubPool.ts @@ -43,8 +43,6 @@ import { import { BigNumber, ethers } from "ethers"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import { MessageTransmitter } from "../../target/types/message_transmitter"; -import { SvmSpoke } from "../../target/types/svm_spoke"; import { CHAIN_IDs } from "../../utils/constants"; // eslint-disable-next-line camelcase import { HubPool__factory } from "../../typechain"; @@ -61,12 +59,15 @@ import { CIRCLE_IRIS_API_URL_MAINNET, decodeMessageHeader, getMessages, + getMessageTransmitterProgram, getSolanaChainId, + getSpokePoolProgram, isSolanaDevnet, loadExecuteRelayerRefundLeafParams, SOLANA_SPOKE_STATE_SEED, SOLANA_USDC_DEVNET, SOLANA_USDC_MAINNET, + SvmSpokeAnchor, } from "../../src/svm"; import { RelayerRefundLeafSolana, RelayerRefundLeafType } from "../../src/types/svm"; @@ -75,10 +76,9 @@ const provider = AnchorProvider.env(); anchor.setProvider(provider); // Get Solana programs. -const svmSpokeIdl = require("../../target/idl/svm_spoke.json"); -const svmSpokeProgram = new Program(svmSpokeIdl, provider); -const messageTransmitterIdl = require("../../target/idl/message_transmitter.json"); -const messageTransmitterProgram = new Program(messageTransmitterIdl, provider); + +const svmSpokeProgram = getSpokePoolProgram(provider); +const messageTransmitterProgram = getMessageTransmitterProgram(provider); const [messageTransmitterState] = PublicKey.findProgramAddressSync( [Buffer.from("message_transmitter")], @@ -340,7 +340,7 @@ async function executeRootBalanceOnHubPool(solanaChainId: BigNumber) { async function executeRelayerRefundLeaf( signer: anchor.Wallet, - program: Program, + program: Program, statePda: PublicKey, rootBundle: PublicKey, relayerRefundLeaf: RelayerRefundLeafSolana, diff --git a/scripts/svm/executeRebalanceToSpokePool.ts b/scripts/svm/executeRebalanceToSpokePool.ts index e6b85acef..8ba2645ec 100644 --- a/scripts/svm/executeRebalanceToSpokePool.ts +++ b/scripts/svm/executeRebalanceToSpokePool.ts @@ -4,13 +4,13 @@ // - HUB_POOL_ADDRESS: Hub Pool address import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; +import { getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { AccountMeta, PublicKey, SystemProgram } from "@solana/web3.js"; -import { TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync } from "@solana/spl-token"; import { getNodeUrl } from "@uma/common"; // eslint-disable-next-line camelcase -import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "../../utils/constants"; -import { SvmSpoke } from "../../target/types/svm_spoke"; +import { CHAIN_IDs, TOKEN_SYMBOLS_MAP } from "@across-protocol/constants"; +import { BigNumber, ethers } from "ethers"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { @@ -19,30 +19,26 @@ import { decodeMessageHeader, evmAddressToPublicKey, getMessages, + getMessageTransmitterProgram, getSolanaChainId, + getSpokePoolProgram, + getTokenMessengerMinterProgram, isSolanaDevnet, SOLANA_USDC_DEVNET, SOLANA_USDC_MAINNET, } from "../../src/svm"; -import { MessageTransmitter } from "../../target/types/message_transmitter"; -import { TokenMessengerMinter } from "../../target/types/token_messenger_minter"; -import { ethers, BigNumber } from "ethers"; -// eslint-disable-next-line camelcase import { HubPool__factory } from "../../typechain"; -import { constructSimpleRebalanceTree } from "./utils/poolRebalanceTree"; import { requireEnv } from "./utils/helpers"; +import { constructSimpleRebalanceTree } from "./utils/poolRebalanceTree"; // Set up Solana provider. const provider = AnchorProvider.env(); anchor.setProvider(provider); // Get Solana programs. -const svmSpokeIdl = require("../../target/idl/svm_spoke.json"); -const svmSpokeProgram = new Program(svmSpokeIdl, provider); -const messageTransmitterIdl = require("../../target/idl/message_transmitter.json"); -const messageTransmitterProgram = new Program(messageTransmitterIdl, provider); -const tokenMessengerMinterIdl = require("../../target/idl/token_messenger_minter.json"); -const tokenMessengerMinterProgram = new Program(tokenMessengerMinterIdl, provider); +const svmSpokeProgram = getSpokePoolProgram(provider); +const messageTransmitterProgram = getMessageTransmitterProgram(provider); +const tokenMessengerMinterProgram = getTokenMessengerMinterProgram(provider); // Set up Ethereum provider and signer. const isDevnet = isSolanaDevnet(provider); diff --git a/scripts/svm/fakeFillWithRandomDistribution.ts b/scripts/svm/fakeFillWithRandomDistribution.ts index 5159795ba..a50bf259e 100644 --- a/scripts/svm/fakeFillWithRandomDistribution.ts +++ b/scripts/svm/fakeFillWithRandomDistribution.ts @@ -1,24 +1,24 @@ // This script implements a fill where relayed tokens are distributed to random recipients via the message handler. // Note that this should be run only on devnet as this is fake fill and all filled tokens are sent to random recipients. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; -import { AccountMeta, Keypair, PublicKey, SystemProgram, TransactionInstruction } from "@solana/web3.js"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, + createApproveCheckedInstruction, createTransferCheckedInstruction, getAssociatedTokenAddressSync, - getOrCreateAssociatedTokenAccount, getMint, - createApproveCheckedInstruction, + getOrCreateAssociatedTokenAccount, } from "@solana/spl-token"; -import { SvmSpoke } from "../../target/types/svm_spoke"; +import { AccountMeta, Keypair, PublicKey, SystemProgram, TransactionInstruction } from "@solana/web3.js"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { AcrossPlusMessageCoder, MulticallHandlerCoder, calculateRelayHashUint8Array, + getSpokePoolProgram, loadFillV3RelayParams, sendTransactionWithLookupTable, } from "../../src/svm"; @@ -29,8 +29,7 @@ const provider = AnchorProvider.env(); anchor.setProvider(provider); const signer = (anchor.AnchorProvider.env().wallet as anchor.Wallet).payer; -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; // Parse arguments diff --git a/scripts/svm/initialize.ts b/scripts/svm/initialize.ts index 2300913a6..a6ecff148 100644 --- a/scripts/svm/initialize.ts +++ b/scripts/svm/initialize.ts @@ -1,18 +1,16 @@ // This script initializes a SVM spoke pool with initialization parameters. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { PublicKey, SystemProgram } from "@solana/web3.js"; -import { SvmSpoke } from "../../target/types/svm_spoke"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import { evmAddressToPublicKey } from "../../src/svm"; +import { evmAddressToPublicKey, getSpokePoolProgram } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; // Parse arguments diff --git a/scripts/svm/queryDeposits.ts b/scripts/svm/queryDeposits.ts index e75cd273c..1a1a623c6 100644 --- a/scripts/svm/queryDeposits.ts +++ b/scripts/svm/queryDeposits.ts @@ -1,18 +1,16 @@ // This script fetches all deposits for a given spoke pool. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { PublicKey } from "@solana/web3.js"; -import { SvmSpoke } from "../../target/types/svm_spoke"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import { readProgramEvents } from "../../src/svm"; +import { getSpokePoolProgram, readProgramEvents } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; console.log("SVM-Spoke Program ID:", programId.toString()); diff --git a/scripts/svm/queryFills.ts b/scripts/svm/queryFills.ts index b28525c60..ee9795a57 100644 --- a/scripts/svm/queryFills.ts +++ b/scripts/svm/queryFills.ts @@ -1,18 +1,16 @@ // This script fetches all fills for a given spoke pool. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { PublicKey } from "@solana/web3.js"; -import { SvmSpoke } from "../../target/types/svm_spoke"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import { readProgramEvents, strPublicKey, u8Array32ToInt } from "../../src/svm"; +import { getSpokePoolProgram, readProgramEvents, strPublicKey, u8Array32ToInt } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; // Parse arguments diff --git a/scripts/svm/queryRoute.ts b/scripts/svm/queryRoute.ts index a67edb59b..6a9791c52 100644 --- a/scripts/svm/queryRoute.ts +++ b/scripts/svm/queryRoute.ts @@ -1,23 +1,22 @@ // This script fetches route information for a given spoke pool, originToken and chainId. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; -import { PublicKey } from "@solana/web3.js"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, - getAssociatedTokenAddressSync, getAccount, + getAssociatedTokenAddressSync, } from "@solana/spl-token"; -import { SvmSpoke } from "../../target/types/svm_spoke"; +import { PublicKey } from "@solana/web3.js"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; +import { getSpokePoolProgram } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; console.log("SVM-Spoke Program ID:", programId.toString()); diff --git a/scripts/svm/queryState.ts b/scripts/svm/queryState.ts index 0875b2cf0..99a04958e 100644 --- a/scripts/svm/queryState.ts +++ b/scripts/svm/queryState.ts @@ -1,17 +1,16 @@ // This script queries the state of a given spoke pool. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { PublicKey } from "@solana/web3.js"; -import { SvmSpoke } from "../../target/types/svm_spoke"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; +import { getSpokePoolProgram } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; console.log("SVM-Spoke Program ID:", programId.toString()); diff --git a/scripts/svm/remoteHubPoolPauseDeposits.ts b/scripts/svm/remoteHubPoolPauseDeposits.ts index d74f2768b..b7448700c 100644 --- a/scripts/svm/remoteHubPoolPauseDeposits.ts +++ b/scripts/svm/remoteHubPoolPauseDeposits.ts @@ -4,7 +4,7 @@ // - HUB_POOL_ADDRESS: Hub Pool address import * as anchor from "@coral-xyz/anchor"; -import { AnchorProvider, BN, Program, web3 } from "@coral-xyz/anchor"; +import { AnchorProvider, BN, web3 } from "@coral-xyz/anchor"; import { AccountMeta, PublicKey } from "@solana/web3.js"; import { getNodeUrl } from "@uma/common"; import "dotenv/config"; @@ -16,10 +16,10 @@ import { CIRCLE_IRIS_API_URL_MAINNET, decodeMessageHeader, getMessages, + getMessageTransmitterProgram, + getSpokePoolProgram, isSolanaDevnet, } from "../../src/svm"; -import { MessageTransmitter } from "../../target/types/message_transmitter"; -import { SvmSpoke } from "../../target/types/svm_spoke"; import { HubPool__factory } from "../../typechain"; import { requireEnv } from "./utils/helpers"; @@ -53,15 +53,13 @@ async function remoteHubPoolPauseDeposit(): Promise { const remoteDomain = 0; // Ethereum // Get Solana programs and accounts. - const svmSpokeIdl = require("../../target/idl/svm_spoke.json"); - const svmSpokeProgram = new Program(svmSpokeIdl, provider); + const svmSpokeProgram = getSpokePoolProgram(provider); const [statePda, _] = PublicKey.findProgramAddressSync( [Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], svmSpokeProgram.programId ); - const messageTransmitterIdl = require("../../target/idl/message_transmitter.json"); - const messageTransmitterProgram = new Program(messageTransmitterIdl, provider); + const messageTransmitterProgram = getMessageTransmitterProgram(provider); const [messageTransmitterState] = PublicKey.findProgramAddressSync( [Buffer.from("message_transmitter")], messageTransmitterProgram.programId diff --git a/scripts/svm/remoteHubPoolSetDepositRoute.ts b/scripts/svm/remoteHubPoolSetDepositRoute.ts index 6b3b55570..e9ca08a9f 100644 --- a/scripts/svm/remoteHubPoolSetDepositRoute.ts +++ b/scripts/svm/remoteHubPoolSetDepositRoute.ts @@ -4,7 +4,7 @@ // - HUB_POOL_ADDRESS: Hub Pool address import * as anchor from "@coral-xyz/anchor"; -import { AnchorProvider, BN, Program, web3 } from "@coral-xyz/anchor"; +import { AnchorProvider, BN, web3 } from "@coral-xyz/anchor"; import { ASSOCIATED_TOKEN_PROGRAM_ID, getAssociatedTokenAddressSync, TOKEN_PROGRAM_ID } from "@solana/spl-token"; import { AccountMeta, PublicKey, SystemProgram } from "@solana/web3.js"; import { getNodeUrl } from "@uma/common"; @@ -19,12 +19,12 @@ import { fromBase58ToBytes32, fromBytes32ToAddress, getMessages, + getMessageTransmitterProgram, + getSpokePoolProgram, isSolanaDevnet, SOLANA_USDC_DEVNET, SOLANA_USDC_MAINNET, } from "../../src/svm"; -import { MessageTransmitter } from "../../target/types/message_transmitter"; -import { SvmSpoke } from "../../target/types/svm_spoke"; import { HubPool__factory } from "../../typechain"; import { CHAIN_IDs } from "../../utils/constants"; import { requireEnv } from "./utils/helpers"; @@ -65,8 +65,7 @@ async function remoteHubPoolSetDepositRoute(): Promise { const remoteDomain = 0; // Ethereum // Get Solana programs and accounts. - const svmSpokeIdl = require("../../target/idl/svm_spoke.json"); - const svmSpokeProgram = new Program(svmSpokeIdl, provider); + const svmSpokeProgram = getSpokePoolProgram(provider); const [statePda, _] = PublicKey.findProgramAddressSync( [Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], svmSpokeProgram.programId @@ -89,8 +88,7 @@ async function remoteHubPoolSetDepositRoute(): Promise { ASSOCIATED_TOKEN_PROGRAM_ID ); - const messageTransmitterIdl = require("../../target/idl/message_transmitter.json"); - const messageTransmitterProgram = new Program(messageTransmitterIdl, provider); + const messageTransmitterProgram = getMessageTransmitterProgram(provider); const [messageTransmitterState] = PublicKey.findProgramAddressSync( [Buffer.from("message_transmitter")], messageTransmitterProgram.programId diff --git a/scripts/svm/remotePauseDeposits.ts b/scripts/svm/remotePauseDeposits.ts index da6cafce0..5ca75aec4 100644 --- a/scripts/svm/remotePauseDeposits.ts +++ b/scripts/svm/remotePauseDeposits.ts @@ -3,7 +3,7 @@ // - MNEMONIC: Mnemonic of the wallet that will sign the sending transaction on Ethereum import * as anchor from "@coral-xyz/anchor"; -import { AnchorProvider, BN, Program, web3 } from "@coral-xyz/anchor"; +import { AnchorProvider, BN, web3 } from "@coral-xyz/anchor"; import { AccountMeta, PublicKey } from "@solana/web3.js"; import { getNodeUrl } from "@uma/common"; import "dotenv/config"; @@ -15,12 +15,12 @@ import { CIRCLE_IRIS_API_URL_MAINNET, decodeMessageHeader, getMessages, + getMessageTransmitterProgram, + getSpokePoolProgram, isSolanaDevnet, MAINNET_CCTP_MESSAGE_TRANSMITTER_ADDRESS, SEPOLIA_CCTP_MESSAGE_TRANSMITTER_ADDRESS, } from "../../src/svm"; -import { MessageTransmitter } from "../../target/types/message_transmitter"; -import { SvmSpoke } from "../../target/types/svm_spoke"; import { CHAIN_IDs } from "../../utils/constants"; import { requireEnv } from "./utils/helpers"; @@ -60,14 +60,12 @@ async function remotePauseDeposits(): Promise { const localDomain = 5; // Solana // Get Solana programs and accounts. - const svmSpokeIdl = require("../../target/idl/svm_spoke.json"); - const svmSpokeProgram = new Program(svmSpokeIdl, provider); + const svmSpokeProgram = getSpokePoolProgram(provider); const [statePda, _] = PublicKey.findProgramAddressSync( [Buffer.from("state"), seed.toArrayLike(Buffer, "le", 8)], svmSpokeProgram.programId ); - const messageTransmitterIdl = require("../../target/idl/message_transmitter.json"); - const messageTransmitterProgram = new Program(messageTransmitterIdl, provider); + const messageTransmitterProgram = getMessageTransmitterProgram(provider); const [messageTransmitterState] = PublicKey.findProgramAddressSync( [Buffer.from("message_transmitter")], messageTransmitterProgram.programId diff --git a/scripts/svm/simpleDeposit.ts b/scripts/svm/simpleDeposit.ts index b8a264b4a..00c261707 100644 --- a/scripts/svm/simpleDeposit.ts +++ b/scripts/svm/simpleDeposit.ts @@ -1,8 +1,7 @@ // This script is used to initiate a Solana deposit. useful in testing. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; -import { PublicKey, Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, @@ -10,15 +9,15 @@ import { getAssociatedTokenAddressSync, getMint, } from "@solana/spl-token"; -import { SvmSpoke } from "../../target/types/svm_spoke"; +import { PublicKey, Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; +import { getSpokePoolProgram } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; console.log("SVM-Spoke Program ID:", programId.toString()); diff --git a/scripts/svm/simpleFakeRelayerRepayment.ts b/scripts/svm/simpleFakeRelayerRepayment.ts index fc08516fc..351a1470a 100644 --- a/scripts/svm/simpleFakeRelayerRepayment.ts +++ b/scripts/svm/simpleFakeRelayerRepayment.ts @@ -1,38 +1,36 @@ // This script executes a fake relayer repayment with a generated leaf. Useful for testing. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; -import { - PublicKey, - SystemProgram, - Keypair, - ComputeBudgetProgram, - AddressLookupTableProgram, - VersionedTransaction, - TransactionMessage, - sendAndConfirmTransaction, - Transaction, -} from "@solana/web3.js"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, - getAssociatedTokenAddressSync, + createApproveCheckedInstruction, createAssociatedTokenAccount, + getAssociatedTokenAddressSync, getMint, - createApproveCheckedInstruction, } from "@solana/spl-token"; -import { SvmSpoke } from "../../target/types/svm_spoke"; +import { + AddressLookupTableProgram, + ComputeBudgetProgram, + Keypair, + PublicKey, + SystemProgram, + Transaction, + TransactionMessage, + VersionedTransaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { MerkleTree } from "@uma/common/dist/MerkleTree"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import { MerkleTree } from "@uma/common/dist/MerkleTree"; +import { getSpokePoolProgram, loadExecuteRelayerRefundLeafParams, relayerRefundHashFn } from "../../src/svm"; import { RelayerRefundLeafSolana, RelayerRefundLeafType } from "../../src/types/svm"; -import { loadExecuteRelayerRefundLeafParams, relayerRefundHashFn } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; // Parse arguments diff --git a/scripts/svm/simpleFill.ts b/scripts/svm/simpleFill.ts index 1f2ee8647..9a159e7b1 100644 --- a/scripts/svm/simpleFill.ts +++ b/scripts/svm/simpleFill.ts @@ -2,8 +2,7 @@ // this script can easily create invalid fills. import * as anchor from "@coral-xyz/anchor"; -import { BN, Program, AnchorProvider } from "@coral-xyz/anchor"; -import { PublicKey, SystemProgram, Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; +import { AnchorProvider, BN } from "@coral-xyz/anchor"; import { ASSOCIATED_TOKEN_PROGRAM_ID, TOKEN_PROGRAM_ID, @@ -12,16 +11,15 @@ import { getMint, getOrCreateAssociatedTokenAccount, } from "@solana/spl-token"; -import { SvmSpoke } from "../../target/types/svm_spoke"; +import { PublicKey, SystemProgram, Transaction, sendAndConfirmTransaction } from "@solana/web3.js"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; -import { calculateRelayHashUint8Array, intToU8Array32 } from "../../src/svm"; +import { calculateRelayHashUint8Array, getSpokePoolProgram, intToU8Array32 } from "../../src/svm"; // Set up the provider const provider = AnchorProvider.env(); anchor.setProvider(provider); -const idl = require("../../target/idl/svm_spoke.json"); -const program = new Program(idl, provider); +const program = getSpokePoolProgram(provider); const programId = program.programId; // Parse arguments diff --git a/src/DeploymentUtils.ts b/src/DeploymentUtils.ts index 58844ac45..b822aa933 100644 --- a/src/DeploymentUtils.ts +++ b/src/DeploymentUtils.ts @@ -6,7 +6,11 @@ interface DeploymentExport { const deployments: DeploymentExport = deployments_ as any; // Returns the deployed address of any contract on any network. -export function getDeployedAddress(contractName: string, networkId: number, throwOnError = true): string | undefined { +export function getDeployedAddress( + contractName: string, + networkId: number | string, + throwOnError = true +): string | undefined { const address = deployments[networkId.toString()]?.[contractName]?.address; if (!address && throwOnError) { throw new Error(`Contract ${contractName} not found on ${networkId} in deployments.json`); diff --git a/src/svm/index.ts b/src/svm/index.ts index 614baeef3..8276197e4 100644 --- a/src/svm/index.ts +++ b/src/svm/index.ts @@ -4,6 +4,8 @@ export * from "./conversionUtils"; export * from "./transactionUtils"; export * from "./solanaProgramUtils"; export * from "./coders"; +export * from "./programConnectors"; +export * from "./assets"; export * from "./constants"; export * from "./helpers"; export * from "./cctpHelpers"; diff --git a/src/svm/programConnectors.ts b/src/svm/programConnectors.ts new file mode 100644 index 000000000..ddcfeefae --- /dev/null +++ b/src/svm/programConnectors.ts @@ -0,0 +1,61 @@ +import { AnchorProvider, Idl, Program } from "@coral-xyz/anchor"; +import { getDeployedAddress } from "../DeploymentUtils"; +import { SupportedNetworks } from "../types/svm"; +import { + MessageTransmitterAnchor, + MessageTransmitterIdl, + MulticallHandlerAnchor, + MulticallHandlerIdl, + SvmSpokeAnchor, + SvmSpokeIdl, + TokenMessengerMinterAnchor, + TokenMessengerMinterIdl, +} from "./assets"; +import { getSolanaChainId, isSolanaDevnet } from "./helpers"; + +type ProgramOptions = { network?: SupportedNetworks; programId?: string }; + +export function getConnectedProgram

(idl: P, provider: AnchorProvider, programId: string) { + idl.address = programId; + return new Program

(idl, provider); +} + +// Resolves the program ID from options or defaults to the deployed address. Prioritizes programId, falls back to +// network, and if network is not defined, determines the network from the provider's RPC URL. Throws an error if +// the program ID cannot be resolved. +function resolveProgramId(programName: string, provider: AnchorProvider, options?: ProgramOptions): string { + const { network, programId } = options ?? {}; + + if (programId) { + return programId; // Prioritize explicitly provided programId + } + + const resolvedNetwork = network ?? (isSolanaDevnet(provider) ? "devnet" : "mainnet"); + const deployedAddress = getDeployedAddress(programName, getSolanaChainId(resolvedNetwork).toString()); + + if (!deployedAddress) { + throw new Error(`${programName} Program ID not found for ${resolvedNetwork}`); + } + + return deployedAddress; +} + +export function getSpokePoolProgram(provider: AnchorProvider, options?: ProgramOptions) { + const id = resolveProgramId("SvmSpoke", provider, options); + return getConnectedProgram(SvmSpokeIdl, provider, id); +} + +export function getMessageTransmitterProgram(provider: AnchorProvider, options?: ProgramOptions) { + const id = resolveProgramId("MessageTransmitter", provider, options); + return getConnectedProgram(MessageTransmitterIdl, provider, id); +} + +export function getTokenMessengerMinterProgram(provider: AnchorProvider, options?: ProgramOptions) { + const id = resolveProgramId("TokenMessengerMinter", provider, options); + return getConnectedProgram(TokenMessengerMinterIdl, provider, id); +} + +export function getMulticallHandlerProgram(provider: AnchorProvider, options?: ProgramOptions) { + const id = resolveProgramId("MulticallHandler", provider, options); + return getConnectedProgram(MulticallHandlerIdl, provider, id); +} diff --git a/src/types/svm.ts b/src/types/svm.ts index 63b26d27a..e181eec29 100644 --- a/src/types/svm.ts +++ b/src/types/svm.ts @@ -131,3 +131,8 @@ export interface EventType { blockTime: number; signature: string; } + +/** + * Supported Networks + */ +export type SupportedNetworks = "mainnet" | "devnet";