diff --git a/Anchor.toml b/Anchor.toml index d146e7a76..4e998fc9c 100644 --- a/Anchor.toml +++ b/Anchor.toml @@ -43,6 +43,8 @@ bridgeLiabilityToHubPool = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/br remoteHubPoolPauseDeposits = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/remoteHubPoolPauseDeposits.ts" generateExternalTypes = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/generateExternalTypes.ts" fakeFillWithRandomDistribution = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/fakeFillWithRandomDistribution.ts" +addressToPublicKey = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/addressToPublicKey.ts" +publicKeyToAddress = "NODE_NO_WARNINGS=1 yarn run ts-node ./scripts/svm/publicKeyToAddress.ts" [test.validator] url = "https://api.mainnet-beta.solana.com" diff --git a/scripts/svm/addressToPublicKey.ts b/scripts/svm/addressToPublicKey.ts new file mode 100644 index 000000000..558d88a3e --- /dev/null +++ b/scripts/svm/addressToPublicKey.ts @@ -0,0 +1,18 @@ +import { evmAddressToPublicKey } from "../../src/SvmUtils"; +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; +const argv = yargs(hideBin(process.argv)).option("address", { + type: "string", + demandOption: true, + describe: "Ethereum address to convert", +}).argv; + +async function logPublicKey(): Promise { + const address = (await argv).address; + + const publicKey = evmAddressToPublicKey(address); + + console.log("Ethereum Address:", address); + console.log("Associated Public Key:", publicKey.toString()); +} +logPublicKey(); diff --git a/scripts/svm/enableRoute.ts b/scripts/svm/enableRoute.ts index e681cbd32..061df09ec 100644 --- a/scripts/svm/enableRoute.ts +++ b/scripts/svm/enableRoute.ts @@ -14,6 +14,7 @@ anchor.setProvider(provider); const idl = require("../../target/idl/svm_spoke.json"); const program = new Program(idl, provider); const programId = program.programId; +console.log("SVM-Spoke Program ID:", programId.toString()); // Parse arguments const argv = yargs(hideBin(process.argv)) @@ -37,7 +38,12 @@ async function enableRoute(): Promise { // Define the route account PDA const [routePda] = PublicKey.findProgramAddressSync( - [Buffer.from("route"), originToken.toBytes(), statePda.toBytes(), chainId.toArrayLike(Buffer, "le", 8)], + [ + Buffer.from("route"), + originToken.toBytes(), + seed.toArrayLike(Buffer, "le", 8), + chainId.toArrayLike(Buffer, "le", 8), + ], programId ); diff --git a/scripts/svm/publicKeyToAddress.ts b/scripts/svm/publicKeyToAddress.ts new file mode 100644 index 000000000..d3fa8bd70 --- /dev/null +++ b/scripts/svm/publicKeyToAddress.ts @@ -0,0 +1,19 @@ +import { publicKeyToEvmAddress } from "../../src/SvmUtils"; +import yargs from "yargs"; +import { hideBin } from "yargs/helpers"; + +const argv = yargs(hideBin(process.argv)).option("publicKey", { + type: "string", + demandOption: true, + describe: "Public key to convert", +}).argv; + +async function logEvmAddress(): Promise { + const publicKey = (await argv).publicKey; + const evmAddress = publicKeyToEvmAddress(publicKey); + + console.log("Public Key:", publicKey); + console.log("Associated Ethereum Address:", evmAddress); +} + +logEvmAddress(); diff --git a/scripts/svm/queryDeposits.ts b/scripts/svm/queryDeposits.ts index c60bcf1bb..b6c943797 100644 --- a/scripts/svm/queryDeposits.ts +++ b/scripts/svm/queryDeposits.ts @@ -14,6 +14,7 @@ anchor.setProvider(provider); const idl = require("../../target/idl/svm_spoke.json"); const program = new Program(idl, provider); const programId = program.programId; +console.log("SVM-Spoke Program ID:", programId.toString()); // Parse arguments const argvPromise = yargs(hideBin(process.argv)).option("seed", { diff --git a/scripts/svm/queryFills.ts b/scripts/svm/queryFills.ts index 2501f9a9d..830ef1af6 100644 --- a/scripts/svm/queryFills.ts +++ b/scripts/svm/queryFills.ts @@ -6,7 +6,8 @@ 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/SvmUtils"; +import { readProgramEvents, strPublicKey } from "../../src/SvmUtils"; +import { u8Array32ToInt } from "../../test/svm/utils"; // Set up the provider const provider = AnchorProvider.env(); @@ -46,30 +47,27 @@ async function queryFills(): Promise { console.log("No fill events found for the given seed."); return; } - console.log("Fill events fetched successfully:"); fillEvents.forEach((event, index) => { console.log(`Fill Event ${index + 1}:`); console.table([ - { Property: "inputToken", Value: new PublicKey(event.data.inputToken).toString() }, - { Property: "outputToken", Value: new PublicKey(event.data.outputToken).toString() }, + { Property: "inputToken", Value: strPublicKey(event.data.inputToken) }, + { Property: "outputToken", Value: strPublicKey(event.data.outputToken) }, { Property: "inputAmount", Value: event.data.inputAmount.toString() }, { Property: "outputAmount", Value: event.data.outputAmount.toString() }, { Property: "repaymentChainId", Value: event.data.repaymentChainId.toString() }, { Property: "originChainId", Value: event.data.originChainId.toString() }, { Property: "depositId", Value: event.data.depositId.toString() }, + { Property: "depositIdNum", Value: u8Array32ToInt(event.data.depositId).toString() }, { Property: "fillDeadline", Value: event.data.fillDeadline.toString() }, { Property: "exclusivityDeadline", Value: event.data.exclusivityDeadline.toString() }, - { Property: "exclusiveRelayer", Value: new PublicKey(event.data.exclusiveRelayer).toString() }, - { Property: "relayer", Value: new PublicKey(event.data.relayer).toString() }, - { Property: "depositor", Value: new PublicKey(event.data.depositor).toString() }, - { Property: "recipient", Value: new PublicKey(event.data.recipient).toString() }, - { Property: "message", Value: event.data.message.toString() }, - { - Property: "updatedRecipient", - Value: new PublicKey(event.data.relayExecutionInfo.updatedRecipient).toString(), - }, - { Property: "updatedMessage", Value: event.data.relayExecutionInfo.updatedMessage.toString() }, + { Property: "exclusiveRelayer", Value: strPublicKey(event.data.exclusiveRelayer) }, + { Property: "relayer", Value: strPublicKey(event.data.relayer) }, + { Property: "depositor", Value: strPublicKey(event.data.depositor) }, + { Property: "recipient", Value: strPublicKey(event.data.recipient) }, + { Property: "messageHash", Value: event.data.messageHash.toString() }, + { Property: "updatedRecipient", Value: strPublicKey(event.data.relayExecutionInfo.updatedRecipient) }, + { Property: "updatedMessageHash", Value: event.data.relayExecutionInfo.updatedMessageHash.toString() }, { Property: "updatedOutputAmount", Value: event.data.relayExecutionInfo.updatedOutputAmount.toString() }, { Property: "fillType", Value: event.data.relayExecutionInfo.fillType }, ]); diff --git a/scripts/svm/queryRoute.ts b/scripts/svm/queryRoute.ts index e8ceb7e64..a67edb59b 100644 --- a/scripts/svm/queryRoute.ts +++ b/scripts/svm/queryRoute.ts @@ -19,6 +19,7 @@ anchor.setProvider(provider); const idl = require("../../target/idl/svm_spoke.json"); const program = new Program(idl, provider); const programId = program.programId; +console.log("SVM-Spoke Program ID:", programId.toString()); // Parse arguments const argv = yargs(hideBin(process.argv)) @@ -40,7 +41,12 @@ async function queryRoute(): Promise { // Define the route account PDA const [routePda] = PublicKey.findProgramAddressSync( - [Buffer.from("route"), Buffer.from(originToken), statePda.toBytes(), chainId.toArrayLike(Buffer, "le", 8)], + [ + Buffer.from("route"), + Buffer.from(originToken), + seed.toArrayLike(Buffer, "le", 8), + chainId.toArrayLike(Buffer, "le", 8), + ], programId ); diff --git a/scripts/svm/queryState.ts b/scripts/svm/queryState.ts index b396ced8a..0875b2cf0 100644 --- a/scripts/svm/queryState.ts +++ b/scripts/svm/queryState.ts @@ -13,6 +13,7 @@ anchor.setProvider(provider); const idl = require("../../target/idl/svm_spoke.json"); const program = new Program(idl, provider); const programId = program.programId; +console.log("SVM-Spoke Program ID:", programId.toString()); // Parse arguments const argv = yargs(hideBin(process.argv)).option("seed", { diff --git a/scripts/svm/simpleDeposit.ts b/scripts/svm/simpleDeposit.ts index 517629acc..b8a264b4a 100644 --- a/scripts/svm/simpleDeposit.ts +++ b/scripts/svm/simpleDeposit.ts @@ -20,6 +20,7 @@ anchor.setProvider(provider); const idl = require("../../target/idl/svm_spoke.json"); const program = new Program(idl, provider); const programId = program.programId; +console.log("SVM-Spoke Program ID:", programId.toString()); // Parse arguments const argv = yargs(hideBin(process.argv)) @@ -54,7 +55,12 @@ async function depositV3(): Promise { // Define the route account PDA const [routePda] = PublicKey.findProgramAddressSync( - [Buffer.from("route"), inputToken.toBytes(), statePda.toBytes(), destinationChainId.toArrayLike(Buffer, "le", 8)], + [ + Buffer.from("route"), + inputToken.toBytes(), + seed.toArrayLike(Buffer, "le", 8), + destinationChainId.toArrayLike(Buffer, "le", 8), + ], programId ); diff --git a/scripts/svm/simpleFill.ts b/scripts/svm/simpleFill.ts index a298629d6..b2ca036c5 100644 --- a/scripts/svm/simpleFill.ts +++ b/scripts/svm/simpleFill.ts @@ -16,6 +16,7 @@ import { SvmSpoke } from "../../target/types/svm_spoke"; import yargs from "yargs"; import { hideBin } from "yargs/helpers"; import { calculateRelayHashUint8Array } from "../../src/SvmUtils"; +import { intToU8Array32 } from "../../test/svm/utils"; // Set up the provider const provider = AnchorProvider.env(); @@ -35,7 +36,7 @@ const argv = yargs(hideBin(process.argv)) .option("inputAmount", { type: "number", demandOption: true, describe: "Input amount" }) .option("outputAmount", { type: "number", demandOption: true, describe: "Output amount" }) .option("originChainId", { type: "string", demandOption: true, describe: "Origin chain ID" }) - .option("depositId", { type: "array", demandOption: true, describe: "Deposit ID" }) + .option("depositId", { type: "string", demandOption: true, describe: "Deposit ID" }) .option("fillDeadline", { type: "number", demandOption: false, describe: "Fill deadline" }) .option("exclusivityDeadline", { type: "number", demandOption: false, describe: "Exclusivity deadline" }).argv; @@ -49,7 +50,7 @@ async function fillV3Relay(): Promise { const inputAmount = new BN(resolvedArgv.inputAmount); const outputAmount = new BN(resolvedArgv.outputAmount); const originChainId = new BN(resolvedArgv.originChainId); - const depositId = resolvedArgv.depositId; + const depositId = intToU8Array32(new BN(resolvedArgv.depositId)); const fillDeadline = resolvedArgv.fillDeadline || Math.floor(Date.now() / 1000) + 60; // Current time + 1 minute const exclusivityDeadline = resolvedArgv.exclusivityDeadline || Math.floor(Date.now() / 1000) + 30; // Current time + 30 seconds const message = Buffer.from(""); @@ -64,7 +65,7 @@ async function fillV3Relay(): Promise { inputAmount, outputAmount, originChainId, - depositId: depositId.map((id) => Number(id)), + depositId, fillDeadline, exclusivityDeadline, message, @@ -154,7 +155,7 @@ async function fillV3Relay(): Promise { state: statePda, signer: signer.publicKey, instructionParams: program.programId, - mintAccount: outputToken, + mint: outputToken, relayerTokenAccount: relayerTokenAccount, recipientTokenAccount: recipientTokenAccount, fillStatus: fillStatusPda, diff --git a/src/SvmUtils.ts b/src/SvmUtils.ts index 70047345c..76583d97c 100644 --- a/src/SvmUtils.ts +++ b/src/SvmUtils.ts @@ -202,6 +202,17 @@ export const evmAddressToPublicKey = (address: string): PublicKey => { return new PublicKey(ethers.utils.arrayify(bytes32Address)); }; +export const publicKeyToEvmAddress = (publicKey: PublicKey | string): string => { + // Convert the input to a PublicKey if it's a string + const pubKeyBuffer = typeof publicKey === "string" ? new PublicKey(publicKey).toBuffer() : publicKey.toBuffer(); + + // Extract the last 20 bytes to get the Ethereum address + const addressBuffer = pubKeyBuffer.slice(-20); + + // Convert the buffer to a hex string and prepend '0x' + return `0x${addressBuffer.toString("hex")}`; +}; + // TODO: we are inconsistant with where we are placing some utils. we have some stuff here, some stuff that we might // want to re-use within the test directory. more over, when moving things into the canonical across repo, we should // re-use the test utils there. @@ -583,3 +594,7 @@ export function hashNonEmptyMessage(message: Buffer) { // else return zeroed bytes32 return new Uint8Array(32); } + +export function strPublicKey(publicKey: PublicKey): string { + return new PublicKey(publicKey).toString(); +} diff --git a/test/svm/utils.ts b/test/svm/utils.ts index c33d0c39f..e19c78098 100644 --- a/test/svm/utils.ts +++ b/test/svm/utils.ts @@ -431,26 +431,41 @@ export async function loadExecuteV3SlowRelayLeafParams( return loadInstructions; } -export function intToU8Array32(num: number): number[] { - if (!Number.isInteger(num) || num < 0) { - throw new Error("Input must be a non-negative integer"); +export function intToU8Array32(num: number | BN): number[] { + let bigIntValue: bigint; + + if (typeof num === "number") { + if (!Number.isInteger(num) || num < 0) { + throw new Error("Input must be a non-negative integer"); + } + bigIntValue = BigInt(num); + } else if (BN.isBN(num)) { + if (num.isNeg()) { + throw new Error("Input must be a non-negative BN"); + } + bigIntValue = BigInt(num.toString()); + } else { + throw new Error("Input must be a non-negative integer or BN"); } const u8Array = new Array(32).fill(0); let i = 0; - while (num > 0 && i < 32) { - u8Array[i++] = num & 0xff; // Get least significant byte - num >>= 8; // Shift right by 8 bits + while (bigIntValue > 0 && i < 32) { + u8Array[i++] = Number(bigIntValue & 0xffn); // Get least significant byte + bigIntValue >>= 8n; // Shift right by 8 bits } return u8Array; } -export function u8Array32ToInt(u8Array: Uint8Array): bigint { - if (!(u8Array instanceof Uint8Array) || u8Array.length !== 32) { - throw new Error("Input must be a Uint8Array of length 32"); +export function u8Array32ToInt(u8Array: Uint8Array | number[]): bigint { + const isValidArray = (arr: any): arr is number[] => Array.isArray(arr) && arr.every(Number.isInteger); + + if ((u8Array instanceof Uint8Array || isValidArray(u8Array)) && u8Array.length === 32) { + return Array.from(u8Array).reduce((num, byte, i) => num | (BigInt(byte) << BigInt(i * 8)), 0n); } - return u8Array.reduce((num, byte, i) => num | (BigInt(byte) << BigInt(i * 8)), 0n); + + throw new Error("Input must be a Uint8Array or an array of 32 numbers."); } // Encodes empty list of multicall handler instructions to be used as a test message field for fills.