From 784b473fb3c8549953c92030ee9092ca0ed9cc77 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:03:00 +0100 Subject: [PATCH 01/24] solana: add governance SDK module for VAA execution --- solana/ts/sdk/governance.ts | 137 ++++++++++++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 solana/ts/sdk/governance.ts diff --git a/solana/ts/sdk/governance.ts b/solana/ts/sdk/governance.ts new file mode 100644 index 000000000..4a5facc12 --- /dev/null +++ b/solana/ts/sdk/governance.ts @@ -0,0 +1,137 @@ +import { Program } from "@coral-xyz/anchor"; +import { + PublicKey, + type Connection, + SystemProgram, + AccountMeta, + TransactionInstruction, +} from "@solana/web3.js"; +import { ParsedVaa } from "@certusone/wormhole-sdk"; +import { derivePostedVaaKey } from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole"; + +import { encoding } from "@wormhole-foundation/sdk-base"; +import { type WormholeGovernance as RawWormholeGovernance } from "../../target/types/wormhole_governance"; +import IDL from "../../target/idl/wormhole_governance.json"; + +type Seed = Uint8Array | string; +function derivePda( + seeds: Seed | readonly Seed[], + programId: PublicKey +) { + const toBytes = (s: string | Uint8Array) => typeof s === "string" ? encoding.bytes.encode(s) : s; + return PublicKey.findProgramAddressSync( + Array.isArray(seeds) ? seeds.map(toBytes) : [toBytes(seeds as Seed)], + programId, + )[0]; +} + +// This is a workaround for the fact that the anchor idl doesn't support generics +// yet. This type is used to remove the generics from the idl types. +type OmitGenerics = { + [P in keyof T]: T[P] extends Record<"generics", any> + ? never + : T[P] extends object + ? OmitGenerics + : T[P]; +}; + +export type WormholeGovernance = OmitGenerics; + +export const GOV_PROGRAM_IDS = [ + "NTTManager111111111111111111111111111111111", + "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm", + "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm", +] as const; + +export type GovProgramId = (typeof GOV_PROGRAM_IDS)[number]; + +export class NTTGovernance { + readonly program: Program; + readonly wormholeId: PublicKey; + + constructor(connection: Connection, args: { programId: GovProgramId }) { + // TODO: initialise a new Program here with a passed in Connection + this.program = new Program(IDL as any, new PublicKey(args.programId), { + connection, + }); + } + + governanceAccountAddress() { + return derivePda("governance", this.program.programId); + } + + async createGovernanceVaaInstruction(args: { + payer: PublicKey; + wormholeId: PublicKey; + vaa: ParsedVaa; + }) { + const vaaKey = derivePostedVaaKey(args.wormholeId, args.vaa.hash); + const emitterChain = Buffer.alloc(2); + emitterChain.writeUInt16BE(args.vaa.emitterChain); + const sequence = Buffer.alloc(8); + sequence.writeBigUInt64BE(BigInt(args.vaa.sequence)); + const [governanceProgramId, ixData] = verifyGovernanceHeader(args.vaa.payload); + const ix = deserializeInstruction(ixData); + + const governanceIx = await this.program.methods.governance() + .accountsStrict({ + payer: args.payer, + governance: derivePda('governance', this.program.programId), + vaa: vaaKey, + program: ix.programId, + replay: derivePda(['replay', emitterChain, args.vaa.emitterAddress, sequence], this.program.programId), + systemProgram: SystemProgram.programId, + }).instruction(); + + // add extra instructions + governanceIx.keys = governanceIx.keys.concat(ix.keys.map(k => { return { ...k, isSigner: false }; })); + return governanceIx; + } +} + + +function deserializeInstruction(data: Buffer): TransactionInstruction { + let offset = 0; + const programId = new PublicKey(data.subarray(offset, offset + 32)); + offset += 32; + const accountsLen = data.readUInt16BE(offset); + offset += 2; + const keys: Array = []; + for (let i = 0; i < accountsLen; i++) { + const pubkey = new PublicKey(data.subarray(offset, offset + 32)); + offset += 32; + const isSigner = data.readUInt8(offset) === 1; + offset += 1; + const isWritable = data.readUInt8(offset) === 1; + offset += 1; + keys.push({ pubkey, isSigner, isWritable }); + } + const dataLen = data.readUInt16BE(offset); + offset += 2; + const instructionData = data.subarray(offset, offset + dataLen); + return new TransactionInstruction({ keys, programId, data: instructionData }); +} + +function verifyGovernanceHeader(data: Buffer): [PublicKey, Buffer] { + let offset = 0; + const module = data.subarray(offset, offset + 32); + offset += 32; + const action = data.readUInt8(offset); + offset += 1; + const chainId = data.readUInt16BE(offset); + offset += 2; + const governanceProgramId = new PublicKey(data.subarray(offset, offset + 32)); + offset += 32; + if ( + !module.equals(Buffer.from("GeneralPurposeGovernance".padStart(32, "\0"))) + ) { + throw new Error("Invalid module"); + } + if (action !== 2) { + throw new Error("Invalid action"); + } + if (chainId !== 1) { + throw new Error("Invalid chainId"); + } + return [governanceProgramId, data.subarray(offset)]; +} From d458340a34d6bd0d1c3aa93b90bbc1555d503e6b Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:04:38 +0100 Subject: [PATCH 02/24] solana: add governance scripts infrastructure (env, helpers, config) --- solana/package.json | 2 + solana/ts/scripts/.env.sample | 14 ++++ .../scripts/config/devnet/governance-vaa.json | 3 + solana/ts/scripts/config/devnet/ntt.json | 4 + solana/ts/scripts/config/devnet/programs.json | 2 +- .../mainnet/governance-vaas-june-05.json | 10 +++ solana/ts/scripts/config/mainnet/ntt.json | 4 + .../ts/scripts/config/mainnet/programs.json | 2 +- solana/ts/scripts/env.ts | 82 +++++++++++++++++++ solana/ts/scripts/env_short.ts | 52 ++++++++++++ solana/ts/scripts/helpers.ts | 48 +++++++++++ 11 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 solana/ts/scripts/.env.sample create mode 100644 solana/ts/scripts/config/devnet/governance-vaa.json create mode 100644 solana/ts/scripts/config/devnet/ntt.json create mode 100644 solana/ts/scripts/config/mainnet/governance-vaas-june-05.json create mode 100644 solana/ts/scripts/config/mainnet/ntt.json create mode 100644 solana/ts/scripts/env.ts create mode 100644 solana/ts/scripts/env_short.ts create mode 100644 solana/ts/scripts/helpers.ts diff --git a/solana/package.json b/solana/package.json index d0879cd72..8d84ec244 100644 --- a/solana/package.json +++ b/solana/package.json @@ -44,6 +44,8 @@ }, "devDependencies": { "@types/bn.js": "^5.1.5", + "@xlabs-xyz/ledger-signer-solana": "^0.0.1", + "dotenv": "^16.4.5", "nock": "^13.3.3", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", diff --git a/solana/ts/scripts/.env.sample b/solana/ts/scripts/.env.sample new file mode 100644 index 000000000..5b7b83e95 --- /dev/null +++ b/solana/ts/scripts/.env.sample @@ -0,0 +1,14 @@ +# Required: "devnet" or "mainnet" +ENV=devnet + +# Solana RPC URL +SOLANA_RPC_URL=https://api.devnet.solana.com + +# Commitment level (optional, defaults to "confirmed") +SOLANA_COMMITMENT=confirmed + +# Ledger derivation path (required for env.ts / Ledger scripts) +LEDGER_DERIVATION_PATH= + +# Guardian key (required for governance message creation) +GUARDIAN_KEY= diff --git a/solana/ts/scripts/config/devnet/governance-vaa.json b/solana/ts/scripts/config/devnet/governance-vaa.json new file mode 100644 index 000000000..5edbd8187 --- /dev/null +++ b/solana/ts/scripts/config/devnet/governance-vaa.json @@ -0,0 +1,3 @@ +{ + "vaa": "01000000000100403baa70cb8c22af68f1f17ce4790024ef4073a7d380f936e418da36a3ee9e8134515679b2feaff806584edf5a8b1976d11a77b1c1b9323d3596c425652957870100000000a3c9167e00010000000000000000000000000000000000000000000000000000000000000004c815dfbc88fdf79420000000000000000047656e6572616c507572706f7365476f7665726e616e636502000105730eefefc3605ca196155a3ca5594706690d45d01848b8bedfb7d99980f7ec057f97be1c39478e57974f6cc9dbfbeebb0e5ce340c2efd52b8295e889a9ede400050885ee38373250ab21bafa2ea2b6c037b511243608b750fcd6c24bdf31710b1f00018a98013dc14753bff4aa9ba82a9bb6f95835fdc6f97561e4d9eaee76cae462ee00006f776e65720000000000000000000000000000000000000000000000000000000100963bdfa10970171c86e222a176f6f831222031172f2c2de3e511ed95ace40018000102a8f6914e88a1b0e210153ef763ae2b00c2b93d16c124d2c0537a100480000000000008eca6efde0e2d8ffe" +} \ No newline at end of file diff --git a/solana/ts/scripts/config/devnet/ntt.json b/solana/ts/scripts/config/devnet/ntt.json new file mode 100644 index 000000000..e565c8048 --- /dev/null +++ b/solana/ts/scripts/config/devnet/ntt.json @@ -0,0 +1,4 @@ +{ + "outboundLimit": "10000000000000", + "mode": "locking" +} \ No newline at end of file diff --git a/solana/ts/scripts/config/devnet/programs.json b/solana/ts/scripts/config/devnet/programs.json index 79501af18..d04513669 100644 --- a/solana/ts/scripts/config/devnet/programs.json +++ b/solana/ts/scripts/config/devnet/programs.json @@ -4,4 +4,4 @@ "wormholeProgramId": "3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5", "quoterProgramId": "Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ", "governanceProgramId": "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm" -} +} \ No newline at end of file diff --git a/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json b/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json new file mode 100644 index 000000000..a4d2cc4ae --- /dev/null +++ b/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json @@ -0,0 +1,10 @@ +[ + { + "id": "1/0000000000000000000000000000000000000000000000000000000000000004/17267778771311161006", + "vaa": "AQAAAAQNAI65VaLhYFpsKDt5UzezT5jFBhRxbGHhBWSuDPIVNyH9HQOlTHSyBDpXhOAR8hy0Wjj1AlPG67AnuGRJNSbIhvYAA03z3U0upcHC0JOnMnmPNSPE+lCKuYF/vHU/FtQ+rg8UIOggAn9/meZLhhlCh7sJN/xIe5Mhnm19psjKz9FfGnIBBUgKKGChLIlRUgb2ZWLGS4OjByi+j6YSExb+5R3UTgJDLdU5K5F54m//HLP4hagpMb314+UWJv7EJfdYeS8Qx94ABvPI4Ae+H3wwH5I/Y65DRYQDtpMGDH6EogkYNN/DyiShB9Pm5gGN0JM7DgNmCHODlV6O7UB3zI2+W9WsnZnCOdkABz4HOBelUsaeBXvLeEgIuDYrE6/4sUWMqlOjB1FY4V9PM+ZG6a5Ts4K9zRcMpT3t1NM4Wjf+CffvBK/C+Wu4MSwACsXQ5JvMpXTe7QAAKaTmtfrH0EYKCwtNkbqJULyLMbC3AQXh677QeaSqZ8/snJqHADClVkfTsuF8u4vm46MEQAcBCxq4Dbif1rNBXVxFeF0JrvFD7dWCPF207pAQzTv43ZRNP+vt+h0tfB1m1xDWMjms4EsEoTB/gGmhZI/pRRW32wUBDHS/dvPBezSpPVaOZx3jhuOBj6XNkAI7aXGyYR5PyrYvNjxOlf3YaMeud9bt4MUjuZY1PdKYqTwNJULRO6g3eBUBDQbuIAf3N/WcmmIIGHXTVMxMPEPI9YHlyxTwIRqzA1NBZrVYnRcc7NSmdLr3bJ4QXwxNi2/fwvdsQSC1YVTmAskBDp3ylnFFrXZGOw+NGUzRl+GbVxMG/PPJe4vCaijmENuwRQkQl6e9/gVboWEk0doI5xV+cNQpzzyMQvtcoMuB6TMBEA49JvrmJ9Tys/OO/l0O8kuPTnBiEQ0MNnOWVJ8JwVTZHNYMxx5xjqEGT+aGZRZ5AtGyaX4/wW1a/A+0UsRGxUQAEf6/+jJtuPGxUHvlJzgUAAMs+/LMsew8VRG/tpU9uH00DZitLKDXogfEyPI0laGix8Pgk81YiF+HkuUZCtKi6WgBEvj+rOA48qWczx1m9wdxFFqtawbG4We+qx0U1ot4ExvhEVJoP8OMPArQnJd78okWn2ekZK+yWuQ8e307OoBl2pkAAAAAAAaA4fgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE76N5TcwIbq4gAAAAAAAAAABHZW5lcmFsUHVycG9zZUdvdmVybmFuY2UCAAEFcw7v78NgXKGWFVo8pVlHBmkNRdAYSLi+37fZmYD37AV/l74cOUeOV5dPbMnb++67DlzjQMLv1SuCleiJqe3kAAMIhe44NzJQqyG6+i6itsA3tREkNgi3UPzWwkvfMXELHwAAb3duZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABADlm1IlN79/aRsLMXswc43tUQKJlINi1e69RyzvOkpJhAAEAENoIAcyn6QqeAADBb/KGIwA=" + }, + { + "id": "1/0000000000000000000000000000000000000000000000000000000000000004/5665574265595141132", + "vaa": "AQAAAAQNALq8JasAs0jexvcmn9DUk729hB/Hn+Nb6fiJlgGpkJUvbfCNUF6c/miNTGBdTWiRvZpUugBfOCha1/rZ0qYvCRsBA+ov5I985O9AQgBYllJGuiLjzxQRF7Zu7LL9vbKyuqICMvkHXNpJYe+XWjjrA0/9dzbEeqbCq2aEibFd4UeHIqUBBfeObPyG+EFKwVhjp14aQ3j1RBSBE2LVr2asP3Gh2E2xVgAIqOis+q0pABMF3ES1oC1gVPTDq3hwZYIncP9bQkIBBsvKjIUf6dzQc1nsla2nzO/7hy+3K+zrnI73RVz6ntyCKFxXLJIFqTxbmwSOEn+X+CpR/1/dZ+///MVcHALo8iUAB6mEOYDzEKbOpwqzHBTKetvLtuytUbDjXLfT2JmIGpDPRBnSOjrdWVeV/xEVs/KT6TmSNATemBerVpUgNRWjln0ACFABS7L/OytWxOvEd415GYO1S8Ome7O9OtcdkBqW7qVCf0kKc4y0WCsKKizS/Zl1JB8MtQTRNeQ4M40wg2HCcK4ACTREFSREEj2w1pXIBoj8FStDn75bSWlgVoumYyvmB7JJVuOwB7ao4yreGRFzKrOTyxjkbHMIVKe1MoI/nekjlnkACs1KektGrLvVTZjB/jKT3Ncr0epxAgX7mUoupH8MZZiHHoT/jcUQX5wKajAaAAQIBS0fQDXQc6ndlHw+/1kNpK0ADdiRpv0GABSe4fPPjJUnchYFlXmkh9mR1XBPetoZcvqiCZEdJgtPNqkg9qpK7t9kna247nsdwzTXmvy7ROsSPfsBDmOH9g4AWxmuViggcYPHN9b4KvM+4YOwUU54hWYGOxGMboS+QzVx+l8m975J8V98xsFklAy+ZdPEwg8oR90rorIBEJgM4lkC+jg77/atGLm9KjGZaefyav89MqcBDjBCBVZjQ5Wn6xhljUlemTXwhnsakaZUyjJe2ovCO0dtPylwCxEAEbth2Ejuso+rAhZGkdvTjVVlzC3EqstMCU/X+Re4FQrIGTjNJfpjn1ZeQ/v0Mwuu8TC9gwJJssju0h+A4/t/k/IAEujBi2W5AGxiDXA22u9q3fQdJ3L7W3pXtGRPtuVHh6FUD0pm5n4jaPuWbcnH2F+EwZU/bSacUAZyiJkWxHaGYqwBAAAAAPB6e/4AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAETqApxu1knAwgAAAAAAAAAABHZW5lcmFsUHVycG9zZUdvdmVybmFuY2UCAAEFcw7v78NgXKGWFVo8pVlHBmkNRdAYSLi+37fZmYD37AV/l74cOUeOV5dPbMnb++67DlzjQMLv1SuCleiJqe3kAAMIhe44NzJQqyG6+i6itsA3tREkNgi3UPzWwkvfMXELHwAAb3duZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABADlm1IlN79/aRsLMXswc43tUQKJlINi1e69RyzvOkpJhAAEAENoIAcyn6QqeAEB6EPNaAAA=" + } +] \ No newline at end of file diff --git a/solana/ts/scripts/config/mainnet/ntt.json b/solana/ts/scripts/config/mainnet/ntt.json new file mode 100644 index 000000000..e565c8048 --- /dev/null +++ b/solana/ts/scripts/config/mainnet/ntt.json @@ -0,0 +1,4 @@ +{ + "outboundLimit": "10000000000000", + "mode": "locking" +} \ No newline at end of file diff --git a/solana/ts/scripts/config/mainnet/programs.json b/solana/ts/scripts/config/mainnet/programs.json index 45bea3c55..1a92028b1 100644 --- a/solana/ts/scripts/config/mainnet/programs.json +++ b/solana/ts/scripts/config/mainnet/programs.json @@ -4,4 +4,4 @@ "wormholeProgramId": "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth", "quoterProgramId": "Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ", "governanceProgramId": "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm" -} +} \ No newline at end of file diff --git a/solana/ts/scripts/env.ts b/solana/ts/scripts/env.ts new file mode 100644 index 000000000..c4c114d97 --- /dev/null +++ b/solana/ts/scripts/env.ts @@ -0,0 +1,82 @@ +import fs from "fs"; +import { Connection, Commitment } from "@solana/web3.js"; +import { SolanaLedgerSigner } from "@xlabs-xyz/ledger-signer-solana"; + +if (!process.env.LEDGER_DERIVATION_PATH) { + throw new Error("LEDGER_DERIVATION_PATH is not set"); +} + +if (!process.env.ENV) { + throw new Error("ENV not set"); +} + +const env = process.env.ENV; + +const derivationPath = process.env.LEDGER_DERIVATION_PATH! as string; + +let signer; +export async function getSigner(): Promise { + if (!signer) { + signer = await SolanaLedgerSigner.create(derivationPath); + } + + return signer; +} + +export function getEnv(key: string): string { + if (!process.env[key]) { + throw new Error(`${key} not found on environment`); + } + + return process.env[key]!; +} + +export const rpcUrl = + process.env.SOLANA_RPC_URL || "https://api.devnet.solana.com"; + +export const connectionCommitmentLevel = (process.env.SOLANA_COMMITMENT || + "confirmed") as Commitment; + +export const connection = new Connection(rpcUrl, connectionCommitmentLevel); + +export type NttConfig = { + outboundLimit: string; + mode: "locking" | "burning"; +} + +export type Programs = { + mintProgramId: string; + nttProgramId: string; + wormholeProgramId: string; + quoterProgramId: string; + governanceProgramId: string; +} + +export type GovernanceVaa = { + vaa: string; +} + +export function getNttConfiguration(): NttConfig { + return loadScriptConfig("ntt"); +} + +export function getProgramAddresses(): Programs { + return loadScriptConfig("programs"); +} + +export function getGovernanceVaa(): GovernanceVaa { + return loadScriptConfig("governance-vaa"); +} + +export function loadScriptConfig(filename: string): any { + const configFile = fs.readFileSync( + `./ts/scripts/config/${env}/${filename}.json` + ); + const config = JSON.parse(configFile.toString()); + if (!config) { + throw Error("Failed to pull config file!"); + } + return config; +} + +export const guardianKey = getEnv("GUARDIAN_KEY"); diff --git a/solana/ts/scripts/env_short.ts b/solana/ts/scripts/env_short.ts new file mode 100644 index 000000000..43732aaf5 --- /dev/null +++ b/solana/ts/scripts/env_short.ts @@ -0,0 +1,52 @@ +import { Commitment, Connection } from "@solana/web3.js"; +import fs from "fs"; + +const env = "mainnet"; // Or devnet + +export function getEnv(key: string): string { + if (!process.env[key]) { + throw new Error(`${key} not found on environment`); + } + + return process.env[key]!; +} + +export const rpcUrl = + process.env.SOLANA_RPC_URL || "INSERT_RPC_URL"; + +export const connectionCommitmentLevel = (process.env.SOLANA_COMMITMENT || + "confirmed") as Commitment; + +export const connection = new Connection(rpcUrl, connectionCommitmentLevel); + +export type Programs = { + mintProgramId: string; + nttProgramId: string; + wormholeProgramId: string; + quoterProgramId: string; + governanceProgramId: string; +} + +export type GovernanceVaa = { + vaa: string; +} + +export function getProgramAddresses(): Programs { + return loadScriptConfig("programs"); +} + +export function getGovernanceVaa(): GovernanceVaa { + return loadScriptConfig("governance-vaa"); +} + +export function loadScriptConfig(filename: string): any { + const configFile = fs.readFileSync( + `./ts/scripts/config/${env}/${filename}.json` + ); + const config = JSON.parse(configFile.toString()); + if (!config) { + throw Error("Failed to pull config file!"); + } + return config; +} + diff --git a/solana/ts/scripts/helpers.ts b/solana/ts/scripts/helpers.ts new file mode 100644 index 000000000..25a768198 --- /dev/null +++ b/solana/ts/scripts/helpers.ts @@ -0,0 +1,48 @@ +import { + Transaction, + TransactionInstruction, + Keypair, + PublicKey, + ComputeBudgetProgram, +} from "@solana/web3.js"; + +import { SolanaLedgerSigner } from "@xlabs-xyz/ledger-signer-solana"; +import { connection, getSigner } from "./env"; + +export async function ledgerSignAndSend(instructions: TransactionInstruction[], signers: Keypair[]) { + const deployerSigner = await getSigner(); + const deployerPk = new PublicKey(await deployerSigner.getAddress()); + + const tx = new Transaction(); + + const setComputePriceIx = ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: 1_000_000, + }); + + tx.add(setComputePriceIx); + + tx.add(...instructions); + + const recentBlockHash = await connection.getLatestBlockhash(); + + tx.recentBlockhash = recentBlockHash.blockhash; + tx.feePayer = deployerPk; + + signers.forEach((signer) => tx.partialSign(signer)); + + await addLedgerSignature(tx, deployerSigner, deployerPk); + + const txSignature = await connection.sendRawTransaction(tx.serialize(), { + skipPreflight: true, + }); + + return { + signature: txSignature, + ...recentBlockHash, + } +} + +export async function addLedgerSignature(tx: Transaction, signer: SolanaLedgerSigner, signerPk: PublicKey) { + const signedByPayer = await signer.signTransaction(tx.compileMessage().serialize()); + tx.addSignature(signerPk, signedByPayer); +} \ No newline at end of file From 2ce452e7acb49f1d106191958c1ebe6b5a479fa3 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:17:26 +0100 Subject: [PATCH 03/24] solana: add executeGovernanceVaa scripts (ledger single, ledger batch, keypair) --- solana/ts/scripts/executeGovernanceVaa.ts | 64 ++++++++++++++ .../ts/scripts/executeGovernanceVaa_short.ts | 85 +++++++++++++++++++ solana/ts/scripts/executeGovernanceVaas.ts | 78 +++++++++++++++++ 3 files changed, 227 insertions(+) create mode 100644 solana/ts/scripts/executeGovernanceVaa.ts create mode 100644 solana/ts/scripts/executeGovernanceVaa_short.ts create mode 100644 solana/ts/scripts/executeGovernanceVaas.ts diff --git a/solana/ts/scripts/executeGovernanceVaa.ts b/solana/ts/scripts/executeGovernanceVaa.ts new file mode 100644 index 000000000..f68225b6a --- /dev/null +++ b/solana/ts/scripts/executeGovernanceVaa.ts @@ -0,0 +1,64 @@ +import { + PublicKey, + Transaction, +} from "@solana/web3.js"; +import { parseVaa } from "@certusone/wormhole-sdk"; +import { + connection, + getSigner, + getProgramAddresses, + getGovernanceVaa, +} from "./env"; +import { addLedgerSignature, ledgerSignAndSend } from "./helpers"; +import { postVaaSolana } from "@certusone/wormhole-sdk"; +import { NTTGovernance } from "../sdk/governance"; + +(async () => { + const { vaa } = getGovernanceVaa(); + + const { nttProgramId, wormholeProgramId, governanceProgramId } = + getProgramAddresses(); + + const governance = new NTTGovernance(connection, { + programId: governanceProgramId as any, + }); + + const signer = await getSigner(); + const signerPk = new PublicKey(await signer.getAddress()); + + const vaaBuff = Buffer.from(vaa, "hex"); + async function sign(tx: Transaction) { + await addLedgerSignature(tx, signer, signerPk); + return tx; + } + + console.log("Posting VAA to Solana..."); + + await postVaaSolana( + connection, + sign, + new PublicKey(wormholeProgramId), + signerPk, + vaaBuff, + ); + + console.log("VAA posted to Solana."); + + const parsedVaa = parseVaa(vaaBuff); + + const governanceIx = await governance.createGovernanceVaaInstruction({ + payer: signerPk, + vaa: parsedVaa, + wormholeId: new PublicKey(wormholeProgramId), + }); + + console.log(`Account ${signerPk.toBase58()} is claiming ownership of NTT Program ${nttProgramId}.`); + + const tx = await ledgerSignAndSend([governanceIx], []); + + console.log("Waiting for confirmation... Signature: ", tx.signature); + + await connection.confirmTransaction(tx); + + console.log("success."); +})(); diff --git a/solana/ts/scripts/executeGovernanceVaa_short.ts b/solana/ts/scripts/executeGovernanceVaa_short.ts new file mode 100644 index 000000000..a271c1edb --- /dev/null +++ b/solana/ts/scripts/executeGovernanceVaa_short.ts @@ -0,0 +1,85 @@ +import { + ComputeBudgetProgram, + Keypair, + PublicKey, + Transaction, + sendAndConfirmTransaction, +} from "@solana/web3.js"; +import { parseVaa } from "@certusone/wormhole-sdk"; +import { + connection, + getProgramAddresses, + getGovernanceVaa, +} from "./env_short"; +import { postVaaSolana } from "@certusone/wormhole-sdk"; +import { NTTGovernance } from "../sdk/governance"; +import fs from "fs"; + +(async () => { + const { vaa } = getGovernanceVaa(); + + const { nttProgramId, wormholeProgramId, governanceProgramId } = + getProgramAddresses(); + + const governance = new NTTGovernance(connection, { + programId: governanceProgramId as any, + }); + + const payerSecretKey = Uint8Array.from( + JSON.parse( + fs.readFileSync(`YOUR_SIGNER_FILE`, { + encoding: "utf-8", + }) + ) + ); + const signer = Keypair.fromSecretKey(payerSecretKey); + // const signer: Keypair = "INSERT_KEYPAIR"; // Derive from file/secret key etc. + const signerPk = signer.publicKey; + + const vaaBuff = Buffer.from(vaa, "base64"); // replace with "hex" if VAA is in hex + async function sign(tx: Transaction) { + tx.partialSign(signer); // partial sign using signer instead of adding ledger signature + return tx; + } + + console.log("Posting VAA to Solana..."); + + await postVaaSolana( + connection, + sign, + new PublicKey(wormholeProgramId).toBase58(), + signerPk.toBase58(), + vaaBuff + ); + + console.log("VAA posted to Solana."); + + const parsedVaa = parseVaa(vaaBuff); + + const governanceIx = await governance.createGovernanceVaaInstruction({ + payer: signerPk, + vaa: parsedVaa, + wormholeId: new PublicKey(wormholeProgramId), + }); + + console.log( + `Governance PDA is claiming ownership of NTT Program ${nttProgramId}.` + ); + + const tx = new Transaction().add( + ComputeBudgetProgram.setComputeUnitPrice({ + microLamports: 1_000_000, + }), + governanceIx + ); + const recentBlockHash = await connection.getLatestBlockhash(); + + tx.recentBlockhash = recentBlockHash.blockhash; + tx.feePayer = signerPk; + const txSig = await sendAndConfirmTransaction(connection, tx, [signer], { + skipPreflight: true, + }); + + console.log("success:", txSig); +})(); + diff --git a/solana/ts/scripts/executeGovernanceVaas.ts b/solana/ts/scripts/executeGovernanceVaas.ts new file mode 100644 index 000000000..09faca368 --- /dev/null +++ b/solana/ts/scripts/executeGovernanceVaas.ts @@ -0,0 +1,78 @@ +import { + PublicKey, + Transaction, + TransactionInstruction, +} from "@solana/web3.js"; +import { parseVaa } from "@certusone/wormhole-sdk"; +import { + connection, + getSigner, + getProgramAddresses, + loadScriptConfig, +} from "./env"; +import { addLedgerSignature, ledgerSignAndSend } from "./helpers"; +import { postVaaSolana } from "@certusone/wormhole-sdk"; +import { NTTGovernance } from "../sdk/governance"; + +const governanceVaasFileName = process.env.GOVERNANCE_VAAS_FILE_PATH; +if (!governanceVaasFileName) { + throw new Error("GOVERNANCE_VAAS_FILE_NAME is required."); +} + +(async () => { + + const vaas: { id: string, vaa: string }[] = loadScriptConfig(governanceVaasFileName); + const { wormholeProgramId, governanceProgramId } = + getProgramAddresses(); + + const governance = new NTTGovernance(connection, { + programId: governanceProgramId as any, + }); + + const signer = await getSigner(); + const signerPk = new PublicKey(await signer.getAddress()); + + async function sign(tx: Transaction) { + await addLedgerSignature(tx, signer, signerPk); + return tx; + } + + const instructions: TransactionInstruction[] = []; + for (const vaa of vaas) { + const vaaBuff = Buffer.from(vaa.vaa, "base64"); + + console.log(`Posting VAA ${vaa.id} to Solana...`); + console.log(`Vaa: ${vaa.vaa}`); + + await postVaaSolana( + connection, + sign, + new PublicKey(wormholeProgramId), + signerPk, + vaaBuff, + ); + + console.log("VAA post succeeded"); + + const parsedVaa = parseVaa(vaaBuff); + + const governanceIx = await governance.createGovernanceVaaInstruction({ + payer: signerPk, + vaa: parsedVaa, + wormholeId: new PublicKey(wormholeProgramId), + }); + + console.log("Parsed VAA Payload", parsedVaa.payload.toString("hex")); + instructions.push(governanceIx) + } + + console.log(`Executing governance instructions.`, instructions); + + const tx = await ledgerSignAndSend(instructions, []); + + console.log("Waiting for confirmation... Signature: ", tx.signature); + + await connection.confirmTransaction(tx); + + console.log("success."); +})(); From b2856fceea54beda0e916bdee8e01aadb3b8fa9c Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:17:42 +0100 Subject: [PATCH 04/24] solana: add governance ownership transfer and diagnostic scripts --- solana/ts/scripts/claimOwnership.ts | 29 ++++++++++++++ solana/ts/scripts/readManagerConfiguration.ts | 21 ++++++++++ solana/ts/scripts/transferOwnershipToGov.ts | 38 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 solana/ts/scripts/claimOwnership.ts create mode 100644 solana/ts/scripts/readManagerConfiguration.ts create mode 100644 solana/ts/scripts/transferOwnershipToGov.ts diff --git a/solana/ts/scripts/claimOwnership.ts b/solana/ts/scripts/claimOwnership.ts new file mode 100644 index 000000000..e967165b0 --- /dev/null +++ b/solana/ts/scripts/claimOwnership.ts @@ -0,0 +1,29 @@ +import { PublicKey } from "@solana/web3.js"; + +import { connection, getSigner, getProgramAddresses } from "./env"; +import { NTT } from "../sdk"; +import { ledgerSignAndSend } from "./helpers"; + +(async () => { + const { nttProgramId, wormholeProgramId } = getProgramAddresses(); + + const signer = await getSigner(); + const signerPk = new PublicKey(await signer.getAddress()); + + const ntt = new NTT(connection, { + nttId: nttProgramId as any, + wormholeId: wormholeProgramId as any, + }); + + const claimOwnershipIx = await ntt.createClaimOwnershipInstruction({ + owner: signerPk, + }); + + console.log(`Account ${signerPk.toBase58()} is claiming ownership of NTT Program ${nttProgramId}.`); + + const tx = await ledgerSignAndSend([claimOwnershipIx], []); + + await connection.confirmTransaction(tx); + console.log("Success."); +})(); + diff --git a/solana/ts/scripts/readManagerConfiguration.ts b/solana/ts/scripts/readManagerConfiguration.ts new file mode 100644 index 000000000..697bf3974 --- /dev/null +++ b/solana/ts/scripts/readManagerConfiguration.ts @@ -0,0 +1,21 @@ +import { connection, getProgramAddresses } from "./env"; +import { NTT } from "../sdk"; + +(async () => { + const programs = getProgramAddresses(); + const ntt = new NTT(connection, { + nttId: programs.nttProgramId as any, + wormholeId: programs.wormholeProgramId as any, + }); + + // console.log("outbox rate limit account:", await ntt.outboxRateLimitAccountAddress()); + console.log("mintAccountAddress", (await ntt.mintAccountAddress()).toString()); + const accountInfo = await connection.getParsedAccountInfo(await ntt.mintAccountAddress()) + if (accountInfo.value === null) { + console.log("Account not found."); + return; + } + console.log("pared account info", accountInfo.value.data); + console.log("Success."); +})(); + diff --git a/solana/ts/scripts/transferOwnershipToGov.ts b/solana/ts/scripts/transferOwnershipToGov.ts new file mode 100644 index 000000000..acccb21d5 --- /dev/null +++ b/solana/ts/scripts/transferOwnershipToGov.ts @@ -0,0 +1,38 @@ +import { PublicKey } from "@solana/web3.js"; + +import { connection, getSigner, getProgramAddresses } from "./env"; +import { NTT } from "../sdk"; +import { ledgerSignAndSend } from "./helpers"; +import { NTTGovernance } from "../sdk/governance"; + +(async () => { + const { nttProgramId, wormholeProgramId, governanceProgramId } = getProgramAddresses(); + + const ntt = new NTT(connection, { + nttId: nttProgramId as any, + wormholeId: wormholeProgramId as any, + }); + + const governance = new NTTGovernance(connection, { + programId: governanceProgramId as any, + }); + + const signer = await getSigner(); + const signerPk = new PublicKey(await signer.getAddress()); + + const governancePda = governance.governanceAccountAddress(); + + const transferOwnershipIx = await ntt.createTransferOwnershipInstruction({ + owner: signerPk, + newOwner: governancePda, + }); + + console.log(`Transferring NTT Program ${nttProgramId} ownership to ${governancePda.toBase58()} derived from(${governanceProgramId}).`); + + const tx = await ledgerSignAndSend([transferOwnershipIx], []); + + console.log("Waiting for confirmation... Signature: ", tx.signature); + await connection.confirmTransaction(tx); + console.log("Success."); +})(); + From 4fd8134f0698e06520ac65c9feada97730247704 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:17:59 +0100 Subject: [PATCH 05/24] evm: add ts-scripts package scaffolding and config --- evm/ts-scripts/.env.sample | 3 + evm/ts-scripts/.gitignore | 4 + evm/ts-scripts/config/mainnet/chains.json | 2 +- evm/ts-scripts/config/mainnet/contracts.json | 156 +++++++++++++++++- evm/ts-scripts/config/testnet/contracts.json | 146 +++++++++++++++- .../config/testnet/governance-vaas.json | 18 ++ evm/ts-scripts/package.json | 22 +++ 7 files changed, 347 insertions(+), 4 deletions(-) create mode 100644 evm/ts-scripts/.env.sample create mode 100644 evm/ts-scripts/.gitignore create mode 100644 evm/ts-scripts/config/testnet/governance-vaas.json create mode 100644 evm/ts-scripts/package.json diff --git a/evm/ts-scripts/.env.sample b/evm/ts-scripts/.env.sample new file mode 100644 index 000000000..b200e26c1 --- /dev/null +++ b/evm/ts-scripts/.env.sample @@ -0,0 +1,3 @@ +WALLET_KEY= +LEDGER_BIP32_PATH= +GUARDIAN_KEY= \ No newline at end of file diff --git a/evm/ts-scripts/.gitignore b/evm/ts-scripts/.gitignore new file mode 100644 index 000000000..f6da57941 --- /dev/null +++ b/evm/ts-scripts/.gitignore @@ -0,0 +1,4 @@ +output +contract-bindings +.env.testnet +.env.mainnet \ No newline at end of file diff --git a/evm/ts-scripts/config/mainnet/chains.json b/evm/ts-scripts/config/mainnet/chains.json index bd5e08d02..1f9f58d40 100644 --- a/evm/ts-scripts/config/mainnet/chains.json +++ b/evm/ts-scripts/config/mainnet/chains.json @@ -53,7 +53,7 @@ "description": "MegaETH", "evmNetworkId": 4326, "chainId": 64, - "rpc": "https://mainnet.megaeth.com/rpc" + "rpc": "http://localhost:50444" } ] } diff --git a/evm/ts-scripts/config/mainnet/contracts.json b/evm/ts-scripts/config/mainnet/contracts.json index 55be86608..e3d2fb9c2 100644 --- a/evm/ts-scripts/config/mainnet/contracts.json +++ b/evm/ts-scripts/config/mainnet/contracts.json @@ -1,4 +1,156 @@ { + "WormholeCoreContracts": [ + { + "chainId": 2, + "address": "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B" + }, + { + "chainId": 4, + "address": "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B" + }, + { + "chainId": 6, + "address": "0x54a8e5f9c4CbA08F9943965859F6c34eAF03E26c" + }, + { + "chainId": 23, + "address": "0xa5f208e072434bC67592E4C49C1B991BA79BCA46" + }, + { + "chainId": 24, + "address": "0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722" + }, + { + "chainId": 30, + "address": "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6" + } + ], + "WormholeRelayers": [ + { + "chainId": 2, + "address": "0x27428DD2d3DD32A4D7f7C497eAaa23130d894911" + }, + { + "chainId": 23, + "address": "0x27428DD2d3DD32A4D7f7C497eAaa23130d894911" + }, + { + "chainId": 24, + "address": "0x27428DD2d3DD32A4D7f7C497eAaa23130d894911" + }, + { + "chainId": 30, + "address": "0x706F82e9bb5b0813501714Ab5974216704980e31" + } + ], + "SpecializedRelayers": [ + { + "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", + "chainId": 2 + }, + { + "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", + "chainId": 23 + }, + { + "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", + "chainId": 24 + }, + { + "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", + "chainId": 30 + } + ], + "NttManagerImplementations": [ + { + "address": "0x6883bdb0694751DE1545a1057c25b0F4fCC0443d", + "chainId": 2 + }, + { + "address": "0x23Fea5514DFC9821479fBE18BA1D7e1A61f6FfCf", + "chainId": 23 + }, + { + "address": "0x36CF4c88FA548c6Ad9fcDc696e1c27Bb3306163F", + "chainId": 24 + }, + { + "address": "0xc3a1248e9bdC1EEF81d16b2AD1594764cBd9307a", + "chainId": 30 + } + ], + "NttManagerProxies": [ + { + "address": "0xc072B1AEf336eDde59A049699Ef4e8Fa9D594A48", + "chainId": 2 + }, + { + "address": "0x5333d0AcA64a450Add6FeF76D6D1375F726CB484", + "chainId": 23 + }, + { + "address": "0x1a4F1a790f23Ffb9772966cB6F36dCd658033e13", + "chainId": 24 + }, + { + "address": "0x5333d0AcA64a450Add6FeF76D6D1375F726CB484", + "chainId": 30 + } + ], + "TransceiverStructsLibs": [ + { + "chainId": 2, + "address": "0x169D91C797edF56100F1B765268145660503a423" + }, + { + "chainId": 23, + "address": "0x3b5CE72cf76dc9866aCc20DB51E4cAD3Eb719b0E" + }, + { + "chainId": 24, + "address": "0x574B7864119C9223A9870Ea614dC91A8EE09E512" + }, + { + "chainId": 30, + "address": "0x3b5CE72cf76dc9866aCc20DB51E4cAD3Eb719b0E" + } + ], + "NttTransceiverImplementations": [ + { + "address": "0x9bD8b7b527CA4e6738cBDaBdF51C22466756073d", + "chainId": 2 + }, + { + "address": "0x83F97c06dD151B0f8A8aB2629dEf30096443114E", + "chainId": 23 + }, + { + "address": "0xc072B1AEf336eDde59A049699Ef4e8Fa9D594A48", + "chainId": 24 + }, + { + "address": "0x83F97c06dD151B0f8A8aB2629dEf30096443114E", + "chainId": 30 + } + ], + "NttTransceiverProxies": [ + { + "address": "0xDb55492d7190D1baE8ACbE03911C4E3E7426870c", + "chainId": 2 + }, + { + "address": "0xD1a8AB69e00266e8B791a15BC47514153A5045a6", + "chainId": 23 + }, + { + "address": "0x9bD8b7b527CA4e6738cBDaBdF51C22466756073d", + "chainId": 24 + }, + { + "address": "0xD1a8AB69e00266e8B791a15BC47514153A5045a6", + "chainId": 30 + } + ], "GeneralPurposeGovernances": [ { "address": "0x23Fea5514DFC9821479fBE18BA1D7e1A61f6FfCf", @@ -7,7 +159,7 @@ { "address": "0x8E4dc685e990379b8D53EcA47841E09B8d30043e", "chainId": 4 - }, + } { "address": "0x169D91C797edF56100F1B765268145660503a423", "chainId": 6 @@ -37,4 +189,4 @@ "chainId": 64 } ] -} +} \ No newline at end of file diff --git a/evm/ts-scripts/config/testnet/contracts.json b/evm/ts-scripts/config/testnet/contracts.json index fa348bd91..50320974d 100644 --- a/evm/ts-scripts/config/testnet/contracts.json +++ b/evm/ts-scripts/config/testnet/contracts.json @@ -1,4 +1,148 @@ { + "WormholeCoreContracts": [ + { + "chainId": 10002, + "address": "0x4a8bc80Ed5a4067f1CCf107057b8270E0cC11A78" + }, + { + "chainId": 10003, + "address": "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35" + }, + { + "chainId": 10004, + "address": "0x79A1027a6A159502049F10906D333EC57E95F083" + }, + { + "chainId": 10005, + "address": "0x31377888146f3253211EFEf5c676D41ECe7D58Fe" + } + ], + "WormholeRelayers": [ + { + "address": "0x7B1bD7a6b4E61c2a123AC6BC2cbfC614437D0470", + "chainId": 10002 + }, + { + "address": "0x7B1bD7a6b4E61c2a123AC6BC2cbfC614437D0470", + "chainId": 10003 + }, + { + "address": "0x93BAD53DDfB6132b0aC8E37f6029163E63372cEE", + "chainId": 10004 + }, + { + "address": "0x93BAD53DDfB6132b0aC8E37f6029163E63372cEE", + "chainId": 10005 + } + ], + "SpecializedRelayers": [ + { + "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", + "chainId": 10002 + }, + { + "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", + "chainId": 10003 + }, + { + "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", + "chainId": 10004 + }, + { + "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", + "chainId": 10005 + } + ], + "NttManagerImplementations": [ + { + "address": "0x194ef936BB95926597dB614537B4Bd9eAA6dE22F", + "chainId": 10002 + }, + { + "address": "0x5F6D34eF4B60Ae3110Dc001f4925E2931Fc8C123", + "chainId": 10003 + }, + { + "address": "0xB2BC8F10d8C77Bf59b27597a9b91358430268168", + "chainId": 10005 + }, + { + "address": "0xeCF0496DE01e9Aa4ADB50ae56dB550f52003bdB7", + "chainId": 10004 + } + ], + "NttManagerProxies": [ + { + "address": "0x06413c42e913327Bc9a08B7C1E362BAE7C0b9598", + "chainId": 10002 + }, + { + "address": "0xCeC6FB4F352bf3DC2b95E1c41831E4D2DBF9a35D", + "chainId": 10003 + }, + { + "address": "0x27F9Fdd3eaD5aA9A5D827Ca860Be28442A1e7582", + "chainId": 10005 + }, + { + "address": "0x8b9E328bE1b1Bc7501B413d04EBF7479B110775c", + "chainId": 10004 + } + ], + "TransceiverStructsLibs": [ + { + "chainId": 10002, + "address": "0x248277DF3E5d712FB795dFEa2622114670c04Da8" + }, + { + "chainId": 10003, + "address": "0x29f9C3143d51AeA75F9F13F4d47d60C5053096FF" + }, + { + "chainId": 10005, + "address": "0x4998eE4F02DcAD16b2A51Cf7b85710354ef692AC" + }, + { + "chainId": 10004, + "address": "0x9646530F12bD41113F834e61bEd99970f0716Abf" + } + ], + "NttTransceiverImplementations": [ + { + "address": "0xc6DbF3344D40d35ce93E73A19c2AfA647F89C15f", + "chainId": 10002 + }, + { + "address": "0xe9580a0867Dfdcb35E23ffb40d13291417b07161", + "chainId": 10003 + }, + { + "address": "0x2438d2934759954329aa4cA499d7fAE684d1429A", + "chainId": 10005 + }, + { + "address": "0x194ef936BB95926597dB614537B4Bd9eAA6dE22F", + "chainId": 10004 + } + ], + "NttTransceiverProxies": [ + { + "address": "0x649fF7B32C2DE771043ea105c4aAb2D724497238", + "chainId": 10002 + }, + { + "address": "0xfA42603152E4f133F5F3DA610CDa91dF5821d8bc", + "chainId": 10003 + }, + { + "address": "0xeCF0496DE01e9Aa4ADB50ae56dB550f52003bdB7", + "chainId": 10005 + }, + { + "address": "0x149987472333cD48ac6D28293A338a1EEa6Be7EE", + "chainId": 10004 + } + ], "GeneralPurposeGovernances": [ { "address": "0x574B7864119C9223A9870Ea614dC91A8EE09E512", @@ -21,4 +165,4 @@ "chainId": 10005 } ] -} +} \ No newline at end of file diff --git a/evm/ts-scripts/config/testnet/governance-vaas.json b/evm/ts-scripts/config/testnet/governance-vaas.json new file mode 100644 index 000000000..98e926610 --- /dev/null +++ b/evm/ts-scripts/config/testnet/governance-vaas.json @@ -0,0 +1,18 @@ +[ + { + "chainId": 10002, + "vaa": "AQAAAAABAO9GFw0p7M1k1+dApnFYahEtKd6We4dPSZa+FirgR0BNWAcESgcQvNguSV66noi7/0dY+/Vg4NvyjC3WSPsiTBcAAAAAALXylaUAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAExxsVgsLk1ikgAAAAAAAAAABHZW5lcmFsUHVycG9zZUdvdmVybmFuY2UBJxKVF/AWTB0ImtcuZp5XuQiHkJZtvXOBQe/2WWJfLq1P7s382UFVxn8YAATO/BQp" + }, + { + "chainId": 10003, + "vaa": "AQAAAAABABRt1qnWaWa2nRm22+NDCbmX1SIt6uck4CFZf5OFhrHVP8HhCMWOfobvSmnrAp0aBCyg1Wq/smyZybczTthXnmMAAAAAAL931RgAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE0TeyxhuLDpwgAAAAAAAAAABHZW5lcmFsUHVycG9zZUdvdmVybmFuY2UBJxOBtlpI3KzLoErKPAVcQRKwcVuQwDldPHQjLRKRbsqJUro1K00ngYA1AATO/BQp" + }, + { + "chainId": 10004, + "vaa": "AQAAAAABAH345FjR31wRQxliMQKFNmGmerZZ3VCzfJxnW4LYABHPCGlnicrUHmXSXfx6hMvdr560prlSoJqO8ard2a+dhJwAAAAAAEB58FMAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAE19jKl8J3pksgAAAAAAAAAABHZW5lcmFsUHVycG9zZUdvdmVybmFuY2UBJxRyClkSi5btpuwpQMeJlAbk3FbQ3B0w54t8f7vO+Hrm6XtTibLkcMpKAATO/BQp" + }, + { + "chainId": 10005, + "vaa": "AQAAAAABAGHthpHgkBKYghkiZ9wlsjq/yzfOrqtNQI84ZmrQcQX+NFQG2o1XhL2Gm+/iYZjFpmAX3Z9vVs+iMjstsbNwpZsBAAAAAK7WazAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMpcRWPILd/UgAAAAAAAAAABHZW5lcmFsUHVycG9zZUdvdmVybmFuY2UBJxXOHeHqSwQNMkoHcZBDpiNMlP0LXd3+q8zyBjzWb1OhIY4jxoG6bnliAATO/BQp" + } +] \ No newline at end of file diff --git a/evm/ts-scripts/package.json b/evm/ts-scripts/package.json new file mode 100644 index 000000000..3cb069377 --- /dev/null +++ b/evm/ts-scripts/package.json @@ -0,0 +1,22 @@ +{ + "name": "ts-scripts", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "build": "npx typechain --target=ethers-v5 --out-dir=./contract-bindings ../out/*/*.json" + }, + "author": "", + "license": "ISC", + "devDependencies": { + "@certusone/wormhole-sdk": "^0.10.11", + "@typechain/ethers-v5": "^11.1.2", + "@types/node": "^20.11.22", + "@xlabs-xyz/ledger-signer": "^0.0.3", + "dotenv": "^16.4.5", + "ethers": "^5.7.2", + "typechain": "^8.3.2", + "tsx": "^4.7.2", + "typescript": "^5.4.5" + } +} From f3a51096a99454dbd738275aff7c44ed9032fda2 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:18:03 +0100 Subject: [PATCH 06/24] evm: add ts-scripts environment and config loading --- evm/ts-scripts/src/env.ts | 233 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 233 insertions(+) create mode 100644 evm/ts-scripts/src/env.ts diff --git a/evm/ts-scripts/src/env.ts b/evm/ts-scripts/src/env.ts new file mode 100644 index 000000000..5f9ed7edf --- /dev/null +++ b/evm/ts-scripts/src/env.ts @@ -0,0 +1,233 @@ +import { ChainId } from "@certusone/wormhole-sdk"; +import { ethers } from "ethers"; +import fs from "fs"; + +export type ChainInfo = { + evmNetworkId: number; + chainId: ChainId; + rpc: string; + wormholeAddress: string; +}; + +export type Deployment = { + chainId: ChainId; + address: string; +}; + +export type ContractsJson = { + WormholeCoreContracts: Deployment[]; + WormholeRelayers: Deployment[]; + SpecializedRelayers: Deployment[]; + + NttManagerProxies: Deployment[]; + NttManagerImplementations: Deployment[]; + + NttTransceiverProxies: Deployment[]; + NttTransceiverImplementations: Deployment[]; + + TransceiverStructsLibs: Deployment[]; + TrimmedAmountLibs: Deployment[]; + + GeneralPurposeGovernances: Deployment[]; +}; + +const DEFAULT_ENV = "testnet"; + +export let env = ""; +let lastRunOverride: boolean | undefined; + +export function init(overrides: { lastRunOverride?: boolean } = {}): string { + env = get_env_var("ENV"); + if (!env) { + console.log( + "No environment was specified, using default environment files" + ); + env = DEFAULT_ENV; + } + lastRunOverride = overrides?.lastRunOverride; + + require("dotenv").config({ + path: `./ts-scripts/.env${env != DEFAULT_ENV ? "." + env : ""}`, + }); + return env; +} + +function get_env_var(env: string): string { + const v = process.env[env]; + return v || ""; +} + +export function loadScriptConfig(filename: string): any { + const configFile = fs.readFileSync( + `./ts-scripts/config/${env}/${filename}.json` + ); + const config = JSON.parse(configFile.toString()); + if (!config) { + throw Error("Failed to pull config file!"); + } + return config; +} + +type ChainConfig = { + chainId: ChainId; +} + +export async function getChainConfig(filename: string, chainId: ChainId): Promise { + const scriptConfig: T[] = await loadScriptConfig(filename); + + const chainConfig = scriptConfig.find((x) => x.chainId == chainId); + + if (!chainConfig) { + throw Error(`Failed to find chain config for chain ${chainId}`); + } + + return chainConfig; +} + + +export function loadOperatingChains(): ChainInfo[] { + const allChains = loadChains(); + let operatingChains: number[] | null = null; + + const chainFile = fs.readFileSync(`./ts-scripts/config/${env}/chains.json`); + const chains = JSON.parse(chainFile.toString()); + if (chains.operatingChains) { + operatingChains = chains.operatingChains; + } + if (!operatingChains) { + return allChains; + } + + const output: ChainInfo[] = []; + operatingChains.forEach((x: number) => { + const item = allChains.find((y) => { + return x == y.chainId; + }); + if (item) { + output.push(item); + } + }); + + return output; +} + +export function loadChains(): ChainInfo[] { + const chainFile = fs.readFileSync(`./ts-scripts/config/${env}/chains.json`); + const chains = JSON.parse(chainFile.toString()); + if (!chains.chains) { + throw Error("Failed to pull chain config file!"); + } + return chains.chains; +} + +export function getChain(chain: ChainId): ChainInfo { + const chains = loadChains(); + const output = chains.find((x) => x.chainId == chain); + if (!output) { + throw Error("bad chain ID"); + } + + return output; +} + +export function loadPrivateKey(): string { + const privateKey = get_env_var("WALLET_KEY"); + if (!privateKey) { + throw Error("Failed to find private key for this process!"); + } + return privateKey; +} + +export function loadGuardianSetIndex(): number { + const chainFile = fs.readFileSync(`./ts-scripts/config/${env}/chains.json`); + const chains = JSON.parse(chainFile.toString()); + if (chains.guardianSetIndex == undefined) { + throw Error("Failed to pull guardian set index from the chains file!"); + } + return chains.guardianSetIndex; +} + +export function writeOutputFiles(output: any, processName: string) { + fs.mkdirSync(`./ts-scripts/output/${env}/${processName}`, { + recursive: true, + }); + fs.writeFileSync( + `./ts-scripts/output/${env}/${processName}/lastrun.json`, + JSON.stringify(output), + { flag: "w" } + ); + fs.writeFileSync( + `./ts-scripts/output/${env}/${processName}/${Date.now()}.json`, + JSON.stringify(output), + { flag: "w" } + ); +} + +export async function getSigner(chain: ChainInfo): Promise { + const provider = getProvider(chain); + const privateKey = loadPrivateKey(); + + if (privateKey === "ledger") { + if (process.env.LEDGER_BIP32_PATH === undefined) { + throw new Error(`Missing BIP32 derivation path. +With ledger devices the path needs to be specified in env var 'LEDGER_BIP32_PATH'.`); + } + const { LedgerSigner } = await import("@xlabs-xyz/ledger-signer"); + return LedgerSigner.create(provider, process.env.LEDGER_BIP32_PATH); + } + + const signer = new ethers.Wallet(privateKey, provider); + return signer; +} + +export function getProvider( + chain: ChainInfo +): ethers.providers.StaticJsonRpcProvider { + const providerRpc = loadChains().find((x: any) => x.chainId == chain.chainId)?.rpc || ""; + + if (!providerRpc) { + throw new Error("Failed to find a provider RPC for chain " + chain.chainId); + } + + let provider = new ethers.providers.StaticJsonRpcProvider( + providerRpc, + ); + + return provider; +} + +let contracts: ContractsJson; +export function loadContracts() { + if (contracts) { + return contracts; + } + + const contractsFile = fs.readFileSync( + `./ts-scripts/config/${env}/contracts.json` + ); + + if (!contractsFile) { + throw Error("Failed to find contracts file for this process!"); + } + + // NOTE: We assume that the contracts.json file is correctly formed... + contracts = JSON.parse(contractsFile.toString()) as ContractsJson; + + return loadContracts(); +} +type ContractTypes = keyof ContractsJson; + +export async function getAllContracts(contractName: ContractTypes) { + return (await loadContracts())[contractName]; +} + +export async function getContractAddress(contractName: ContractTypes, chainId: ChainId): Promise { + const contract = (await getAllContracts(contractName))?.find((c) => c.chainId === chainId)?.address; + + if (!contract) { + throw new Error(`No ${contractName} contract found for chain ${chainId}`); + } + + return contract; +} + From 074bd5bff19378890677c31e8c57d614261dd33b Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:18:39 +0100 Subject: [PATCH 07/24] evm: add governance scripts (execute VAA, create message) --- evm/ts-scripts/src/createGovernanceMessage.ts | 173 ++++++++++++++++++ evm/ts-scripts/src/executeGovernanceVaa.ts | 84 +++++++++ 2 files changed, 257 insertions(+) create mode 100644 evm/ts-scripts/src/createGovernanceMessage.ts create mode 100644 evm/ts-scripts/src/executeGovernanceVaa.ts diff --git a/evm/ts-scripts/src/createGovernanceMessage.ts b/evm/ts-scripts/src/createGovernanceMessage.ts new file mode 100644 index 000000000..60f464e68 --- /dev/null +++ b/evm/ts-scripts/src/createGovernanceMessage.ts @@ -0,0 +1,173 @@ +import { ethers } from "ethers"; +const elliptic = require("elliptic"); +import { inspect } from "util"; +import { ChainId } from "@certusone/wormhole-sdk"; +import { + loadOperatingChains, + init, + ChainInfo, + loadScriptConfig, + getContractAddress, + loadGuardianSetIndex, +} from "./env"; + +type ManagerConfig = { + token: string; + chainId: ChainId; + threshold: number; + outboundLimit: string; + inboundLimit: { chainId: ChainId; limit: string }[]; +}; + +function loadGuardianKey(): string { + const key = process.env.GUARDIAN_KEY; + if (!key) throw new Error("GUARDIAN_KEY environment variable is required"); + return key; +} + +const governanceChainId = 1; +const governanceContract = + "0x0000000000000000000000000000000000000000000000000000000000000004"; + +const governanceModule = + "0x000000000000000047656E6572616C507572706F7365476F7665726E616E6365"; + +const processName = "deployGovernances"; + +init(); +const chains = loadOperatingChains(); + +async function run() { + console.log(`Start ${processName}!`); + const managersConfiguration: ManagerConfig[] = loadScriptConfig("managers"); + + const results = await Promise.all( + chains.map(async (chain) => { + const govAddress = await getContractAddress( + "GeneralPurposeGovernances", + chain.chainId + ); + const managerConfig = managersConfiguration.find( + (config) => config.chainId === chain.chainId + ); + + if (!managerConfig) { + return { + chainId: chain.chainId, + error: "Manager configuration not found for chain", + }; + } + + const vaa = await createAcceptDefaultAdminTransferVaa( + chain, + govAddress, + managerConfig.token + ); + + console.log(`Vaa for chain: ${chain.chainId}: ${vaa}`); + return { chainId: chain.chainId, vaa }; + }) + ); + + for (const result of results) { + if ("error" in result) { + console.error( + `Error deploying for chain ${result.chainId}: ${inspect(result.error)}` + ); + continue; + } + + console.log(`Deployed succeded for chain ${result.chainId}`); + } +} + +run().then(() => console.log("Done!")); + +async function createAcceptDefaultAdminTransferVaa( + chain: ChainInfo, + generalPurposeGovernanceAddress: string, + tokenAddress: string +) { + const abi = ["function acceptDefaultAdminTransfer()"]; + const contract = new ethers.Contract( + "0x0000000000000000000000000000000000000000", + abi + ); + + const call = await contract.populateTransaction.acceptDefaultAdminTransfer(); + const callData = call.data; + + const payload = ethers.utils.solidityPack( + ["bytes32", "uint8", "uin16", "address", "address", "uint16", "bytes"], + [ + governanceModule, + 1, + chain.chainId, + generalPurposeGovernanceAddress, + tokenAddress, + callData, + ] + ); + + return encodeAndSignGovernancePayload(payload); +} + +export function encodeAndSignGovernancePayload(payload: string): string { + const timestamp = Math.floor(+new Date() / 1000); + const nonce = 1; + const sequence = 1; + const consistencyLevel = 1; + + const encodedVAABody = ethers.utils.solidityPack( + ["uint32", "uint32", "uint16", "bytes32", "uint64", "uint8", "bytes"], + [ + timestamp, + nonce, + governanceChainId, + governanceContract, + sequence, + consistencyLevel, + payload, + ] + ); + + const hash = doubleKeccak256(encodedVAABody); + + // sign the hash + const ec = new elliptic.ec("secp256k1"); + const key = ec.keyFromPrivate(loadGuardianKey()); + const signature = key.sign(hash.substring(2), { canonical: true }); + + // pack the signatures + const packSig = [ + ethers.utils.solidityPack(["uint8"], [0]).substring(2), + zeroPadBytes(signature.r.toString(16), 32), + zeroPadBytes(signature.s.toString(16), 32), + ethers.utils + .solidityPack(["uint8"], [signature.recoveryParam]) + .substring(2), + ]; + + const vm = [ + ethers.utils.solidityPack(["uint8"], [1]).substring(2), + ethers.utils + .solidityPack(["uint32"], [loadGuardianSetIndex()]) + .substring(2), // guardianSetIndex + ethers.utils.solidityPack(["uint8"], [1]).substring(2), // number of signers + [packSig], + encodedVAABody.substring(2), + ].join(""); + + return "0x" + vm; +} + +export function zeroPadBytes(value: string, length: number): string { + while (value.length < 2 * length) { + value = "0" + value; + } + return value; +} + +function doubleKeccak256(body: ethers.BytesLike) { + return ethers.utils.keccak256(ethers.utils.keccak256(body)); +} diff --git a/evm/ts-scripts/src/executeGovernanceVaa.ts b/evm/ts-scripts/src/executeGovernanceVaa.ts new file mode 100644 index 000000000..00a63c2e0 --- /dev/null +++ b/evm/ts-scripts/src/executeGovernanceVaa.ts @@ -0,0 +1,84 @@ +import { inspect } from "util"; +import { ChainId } from "@certusone/wormhole-sdk"; + +import { + loadOperatingChains, + init, + ChainInfo, + getChainConfig, + getSigner, + getContractAddress, +} from "./env"; +import { Governance__factory } from "../contract-bindings"; + +const processName = "executeGovernanceVaas"; + +type GovernanceConfig = { + chainId: ChainId; + vaa: string; +}; + +init(); +const chains = loadOperatingChains(); +async function run() { + console.log(`Start ${processName}!`); + + const results = await Promise.all( + chains.map(async (chain) => { + let result; + try { + result = await executeGovernance(chain); + } catch (error: unknown) { + return { chainId: chain.chainId, peerUpdateTxs: [] as string[], error }; + } + + return result; + }) + ); + + for (const result of results) { + if (!result) { + continue; + } + + if ("error" in result) { + console.error( + `${processName} failed for chain ${result.chainId}: ${inspect(result.error)}` + ); + continue; + } + + console.log(`${processName} succeeded for chain ${result.chainId}`); + } +} + +async function executeGovernance( + chain: ChainInfo, +) { + const log = (...args: any[]) => console.log(`[${chain.chainId}]`, ...args); + const { vaa } = await getChainConfig("governance-vaas", chain.chainId); + + const governanceContract = await getGovernanceContract(chain); + + const vaaHex = Buffer.from(vaa, "base64").toString("hex"); + log("Executing governance with VAA: ", vaaHex); + + const tx = await governanceContract.performGovernance( + `0x${vaaHex}`, + {} // overrides + ); + + log(`Submitted governance transaction: ${tx.hash}`); + + await tx.wait(); + + log("success."); +} + +run().then(() => console.log("Done!")); + +async function getGovernanceContract(chain: ChainInfo) { + const signer = await getSigner(chain); + const governanceAddress = await getContractAddress("GeneralPurposeGovernances", chain.chainId); + return Governance__factory.connect(governanceAddress, signer); +} \ No newline at end of file From 21930b68dc242ea0470182295fa6b2f8e9782371 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:19:17 +0100 Subject: [PATCH 08/24] evm: add transceiver config reading script --- evm/ts-scripts/src/readTransceiverConfig.ts | 78 +++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 evm/ts-scripts/src/readTransceiverConfig.ts diff --git a/evm/ts-scripts/src/readTransceiverConfig.ts b/evm/ts-scripts/src/readTransceiverConfig.ts new file mode 100644 index 000000000..2a5f909af --- /dev/null +++ b/evm/ts-scripts/src/readTransceiverConfig.ts @@ -0,0 +1,78 @@ +import { inspect } from "util"; +import { ChainId } from "@certusone/wormhole-sdk"; +import { + WormholeTransceiver__factory, +} from "../contract-bindings"; +import { + loadOperatingChains, + init, + ChainInfo, + getSigner, + getContractAddress, + loadScriptConfig, +} from "./env"; + +const processName = "readTransceiverConfig"; + +type Peer = { + chainId: ChainId; +}; + +init(); +const chains = loadOperatingChains(); +async function run() { + // Warning: we assume that the script configuration file is correctly formed + console.log(`Start ${processName}!`); + const peers = await loadScriptConfig("peers") as Peer[]; + + const results = await Promise.all( + chains.map(async chain => { + try { + await readContractConfig(chain, peers); + } catch (error) { + return { chainId: chain.chainId, error }; + } + + return { chainId: chain.chainId }; + }) + ) + + for (const result of results) { + if (("error" in result)) { + console.error( + `Error configuring contract for chain ${result.chainId}: ${inspect( + result.error + )}` + ); + continue; + } + + console.log(`Configuration succeded for chain ${result.chainId}`); + } +} +async function readContractConfig(chain: ChainInfo, peers: Peer[]) { + const log = (...args: any[]) => console.log(`[${chain.chainId}]`, ...args); + + const transceiverContract = await getTransceiverContract(chain); + + log("Reading contract configuration..."); + log("Token address: ", await transceiverContract.getNttManagerToken()); + log("Peers information:"); + for (const peer of peers) { + log("Peer:", peer.chainId); + log(" Wormhole relaying enabled:", await transceiverContract.isWormholeRelayingEnabled(peer.chainId)); + log(" Wormhole evm chain:", await transceiverContract.isWormholeEvmChain(peer.chainId)); + } + + return { chainId: chain.chainId }; +} + +async function getTransceiverContract(chain: ChainInfo) { + const signer = await getSigner(chain); + const transceiverAddress = await getContractAddress("NttTransceiverProxies", chain.chainId); + return WormholeTransceiver__factory.connect(transceiverAddress, signer); +} + + + +run().then(() => console.log("Done!")); From 3f4eac9d863c6b0a3417470878479eee3a98e9a3 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:19:47 +0100 Subject: [PATCH 09/24] docs: add deployment documentation and governance shell script --- deployments.md | 156 ++++++++++++++++++ evm/.gitignore | 1 + .../shell/verify-governance-contract.sh | 60 +++++++ 3 files changed, 217 insertions(+) create mode 100644 deployments.md create mode 100644 evm/.gitignore create mode 100755 evm/ts-scripts/shell/verify-governance-contract.sh diff --git a/deployments.md b/deployments.md new file mode 100644 index 000000000..dfe56c074 --- /dev/null +++ b/deployments.md @@ -0,0 +1,156 @@ +## NTT Deployment: +This document serves as an example of a possible order of execution of commands to perform a full deployment of NTT on Solana + one or more EVMs. + +### Requirements: +NTT needs the following contracts to be able to operate. This doc assumes that these contracts are already deployed by the time you execute this deployment. + +Deployed by NTT deployment owner: +- an ntt token + +Deployed by wormhole: +- wormhole core layer +- wormhole relayer (you can use wormhole's public one or deploy your own) + +### High level description of the process: +To have a multi-chain NTT setup working a few things need to happen. +- Deploy managers on EVM +- Register managers on NTT Token +- Deploy Transceivers on EVM +- Deploy NTT Program on Solana +- Set Mint authority to NTT program +- Initialize NTT program on Solana +- Cross register managers (let each manager know about the other managers) +- Cross register transceivers + +## EVM Deployment: + +### Environments & Configuration: +These scripts are implemented in way that helps keep track of the deployed addresses by reading contract addresses from json files that can be commited to the repository. Multiple environments are supported through the use of the `ENV` environment variable. +To create or use a new env all that needs to be done is to create a new directory on `evm/ts-scripts/config/` with the name of the env you want to use and then set the environment variable ENV to the same value (ie on linux: `export ENV="my-env"`) +Inside the configuration directory, there needs to be a few files that will contain all the configuration you need to set up a full NTT deployment: +- `chains.json` contains the configuration used by the deployment runner such as what rpcs will be used or what chains will it run scripts against +- `contracts.json` this file contains the addresses of all contracts related to this deployment and will be used any time a script needs to know about the address of other of the contracts. Addresses of contracts will be written to this file as the deployment progress (ie after deploying managers we'll add the manager addresses here) +- `managers.json` contains the configuration that will be applied to managers +- `transceivers.json` contains the configuration that will be applied to transceivers +- `peers.json` contains the configuration that will be used for contract cross-registrations + +> When a script deploys new contracts, it outputs the resulting addresseses on the directory `ts-scripts/output//.json (aside from logging them). Whenever we need to configure the addresses of a contract that we have just deployed we'll fetch the new addresses from those files. + +### Deployment Steps: +- create/update `evm/config//contracts.json` as needed +- create/update `evm/config//managers.json` as needed +- create/update `evm/config//transceivers.json` as needed +- Set environment variables: +``` +export FOUNDRY_PROFILE=prod +export WALLET_KEY="ledger" +export LEDGER_BIP32_PATH="" +export ENV=testnet +``` +- Build: +```shell +make build-evm-prod +cd evm/ts-scripts +npm install +cd .. +npm --prefix ./ts-scripts run build # builds typescript bindings to evm/ts-scripts/output +``` +- Deploy Managers: +```shell +bash -c ./ts-scripts/shell/deploy-managers.sh +# +# Use the addresses to update the `contracts.json` file +# +``` +- Now that you have deployed your manager, you can go to your token deployment and configure the token to be managed by the managers you have deployed (aka, call `setMinter` on the token to the manager address). +- Deploy Transceivers: +```shell +bash -c ./ts-scripts/shell/deploy-transceivers.sh +# +# Use the addresses to update the `contracts.json` file +# +``` +- Configure Managers: +```shell +bash -c ./ts-scripts/shell/configure-managers.sh +``` + +## Solana Deployment: +- set environment: +```shell +# Runner Configurations: +export SOLANA_RPC_URL="" +export WORMHOLE_PROGRAM_ID="" # wormhole core layer program id +export LEDGER_DERIVATION_PATH="" # derivation path + +# Deployment configuration: +export MINT_ADDRESS="" # the program id of your spl token +``` +- Create and set the pub key of the program: +```shell +solana-keygen grind --ignore-case --starts-with ntt:1 +``` +- use the pub key created to replace the `declare_id!` macro value on `lib.rs` +- export the pub-key of the program: +``` +export NTT_PROGRAM_ID= +``` +- Build: +``` +cd solana +anchor build --arch sbf -- --features "mainnet" # if you are deploying to tesnet, you should use "solana-devnet" instead of mainnet +``` +- Deploy: +``` +solana program -k usb://ledger?key= deploy --url $SOLANA_RPC_URL --program-id "$NTT_PROGRAM_ID.json" target/deploy/example_native_token_transfers.so +``` +- Initialize the program: +``` shell +make build +npx tsx ./ts/scripts/initializeNtt.ts +# +# The script will print the manager emitter address PDA. You can always derive +# this pda again, but might be easier if you take note of it at this point. +# You'll need it later to do cross-registrations. +# +``` + +## Cross Registrations: + +### EVM cross Registrations: +- Create or update evm/config//peers.json (this file contains the data of all peers that the different evm deployments should know about. Note that the evm cross-registration script can find the addresses of its evm peers, but it can't find the solana peer address, so for solana you'll need to add two extra properties `managerAddress` and `transceiverAddress`. They correspond to the solana ntt deployment program-id and the manager-emitter-address PDA (the one printed during solana program initialization) +- Run update peers script: +```shell +bash -c ./ts-scripts/shell/update-peer-addresses.sh +``` + +### Solana Cross Registrations: +- update `evmNttDeployments` on solana/ts/scripts/env.ts with the addresses of your EVM managers and transceivers. +- Run cross registrations script: +```shell +npx tsx ./ts/scripts/updatePeers.ts +``` + +## Contract Bytecode Verification: + +### EVM verification: +On evm there are scripts that verify the deployed contracts. To run them you'll need a file with the etherscan scanners keys per each of the supported chains. +Such file should have this structure: +```json +[ + { + "chainId": 6, + "etherscan": "YOUR-ETTHERSCAN-API_KEY" + } +] +``` +Run verification scripts: +``` +export SCANNER_TOKENS_FILE_PATH="the/path/to/file/above" +bash -c ./evm/ts-scripts/shell/verify-managers.sh +bash -c ./evm/ts-scripts/shell/verify-transceivers.sh +``` +Finally, go to the etherscan scanner of the different chains and link the verified proxy to the verified implementation. + +### Solana Verification: +TODO \ No newline at end of file diff --git a/evm/.gitignore b/evm/.gitignore new file mode 100644 index 000000000..c4f48aa8a --- /dev/null +++ b/evm/.gitignore @@ -0,0 +1 @@ +forge-build \ No newline at end of file diff --git a/evm/ts-scripts/shell/verify-governance-contract.sh b/evm/ts-scripts/shell/verify-governance-contract.sh new file mode 100755 index 000000000..1365693c3 --- /dev/null +++ b/evm/ts-scripts/shell/verify-governance-contract.sh @@ -0,0 +1,60 @@ +if test -z "$ENV"; then + echo "ENV is not set" + exit 1 +fi + +scanner_tokens_file="$SCANNER_TOKENS_FILE_PATH" +if test -z $scanner_tokens_file; then + echo "SCANNER_TOKENS_FILE_PATH is not set" + exit 1 +fi +echo "Using scanner tokens file at $scanner_tokens_file" + +chains_file_path="ts-scripts/config/$ENV/chains.json" +if ! test -f "$chains_file_path"; then + echo "File does not exist at $chains_file_path" + exit 1 +fi + +contracts_file_path="ts-scripts/config/$ENV/contracts.json" +if ! test -f "$contracts_file_path"; then + echo "Contracts file configuration does not exist at $contracts_file_path" + exit 1 +fi + +operating_chains=$(jq -r '.operatingChains' $chains_file_path); + +if [ "$operating_chains" = "null" ]; then + operating_chains=$(jq -r '.chains[] | .chainId' $chains_file_path) +else + operating_chains=$(jq -r '.operatingChains[]' $chains_file_path) +fi + +export FOUNDRY_PROFILE=prod + +for chain in $operating_chains; do + echo "Operating on chain $chain:" + + export governance_address=$(jq --raw-output ".GeneralPurposeGovernances[] | select(.chainId == $chain) | .address" $contracts_file_path) + export core_wormhole_address=$(jq --raw-output ".WormholeCoreContracts[] | select(.chainId == $chain) | .address" $contracts_file_path) + export etherscan_api_key=$(jq --raw-output ".[] | select(.chainId == $chain) | .etherscan" $scanner_tokens_file) + export evm_chain_id=$(jq ".chains[] | select(.chainId == $chain) | .evmNetworkId" $chains_file_path) + + # echo "governance_address: $governance_address" + # echo "etherscan_api_key: $etherscan_api_key" + # echo "evm_chain_id: $evm_chain_id" + + if [ "$governance_address" = "" ] || + [ "$governance_address" = "" ] || + [ "$etherscan_api_key" = "null" ] || + [ "$evm_chain_id" = "null" ]; then + echo "One of the addresses is not set. Skipping..."; + continue + fi + + forge verify-contract --chain "$evm_chain_id" \ + --etherscan-api-key "$etherscan_api_key" \ + "$governance_address" \ + src/wormhole/Governance.sol:Governance --watch \ + --constructor-args $(cast abi-encode "constructor(address)" "$core_wormhole_address") +done From ca2d12a25e50806a490ef57018ea2afc43350a3b Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:41:22 +0100 Subject: [PATCH 10/24] fix: restore mainnet RPC URL, fix trailing newlines, update lockfile --- bun.lock | 77 ++++++++++++++++++- evm/ts-scripts/config/mainnet/chains.json | 2 +- solana/ts/scripts/config/devnet/programs.json | 2 +- .../ts/scripts/config/mainnet/programs.json | 2 +- 4 files changed, 79 insertions(+), 4 deletions(-) diff --git a/bun.lock b/bun.lock index 9b54d7628..d290f318b 100644 --- a/bun.lock +++ b/bun.lock @@ -1,6 +1,5 @@ { "lockfileVersion": 1, - "configVersion": 0, "workspaces": { "": { "name": "ntt", @@ -114,6 +113,8 @@ }, "devDependencies": { "@types/bn.js": "^5.1.5", + "@xlabs-xyz/ledger-signer-solana": "^0.0.1", + "dotenv": "^16.4.5", "nock": "^13.3.3", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", @@ -488,6 +489,20 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@ledgerhq/devices": ["@ledgerhq/devices@8.13.0", "", { "dependencies": { "@ledgerhq/errors": "^6.32.0", "@ledgerhq/logs": "^6.16.0", "rxjs": "7.8.2", "semver": "7.7.3" } }, "sha512-hgGn1kpe/rT0EJ0Qs7rG+1TXA4g6HN2t3dB4DndRTqVqC9aSSbME+ajA0QWLZisxOD3zkwvO4Q0mJ2zARAKyag=="], + + "@ledgerhq/errors": ["@ledgerhq/errors@6.32.0", "", {}, "sha512-BjjvhLM6UXYUbhllqAduo9PSneLt9FXZ3TBEUFQ3MMSZOCHt0gAgDySLwul99R8fdYWkXBza4DYQjUNckpN2lg=="], + + "@ledgerhq/hw-app-solana": ["@ledgerhq/hw-app-solana@7.1.4", "", { "dependencies": { "@ledgerhq/errors": "^6.16.2", "@ledgerhq/hw-transport": "^6.30.4", "bip32-path": "^0.4.2" } }, "sha512-xOrCPKzF6RnqD9eRyjltJkWQDS39/Wev3GxvD4sKYuhERdZ+W8Gg0/nxKK468qGNPwhiDaRj7inPKGSMNCADqQ=="], + + "@ledgerhq/hw-transport": ["@ledgerhq/hw-transport@6.30.4", "", { "dependencies": { "@ledgerhq/devices": "^8.2.1", "@ledgerhq/errors": "^6.16.2", "@ledgerhq/logs": "^6.12.0", "events": "^3.3.0" } }, "sha512-VBcVd7UG8HDrjWMoZI5rqBDz+PBxLHTIPZOGY/fdMoEUwaBbss0Z3MxuJanMyerlfaLqnBSVuL0blz7rOyagkw=="], + + "@ledgerhq/hw-transport-node-hid": ["@ledgerhq/hw-transport-node-hid@6.28.4", "", { "dependencies": { "@ledgerhq/devices": "^8.2.1", "@ledgerhq/errors": "^6.16.2", "@ledgerhq/hw-transport": "^6.30.4", "@ledgerhq/hw-transport-node-hid-noevents": "^6.29.4", "@ledgerhq/logs": "^6.12.0", "lodash": "^4.17.21", "node-hid": "^2.1.2", "usb": "2.9.0" } }, "sha512-f6yQvZe4cwg6ibRn1YE9wWTdvpyT7FBIxxYHAd4RKm0l5/WFZS2G8CwdNJQwlOr89iBgGvEswWYh8EhUxM5QYA=="], + + "@ledgerhq/hw-transport-node-hid-noevents": ["@ledgerhq/hw-transport-node-hid-noevents@6.34.0", "", { "dependencies": { "@ledgerhq/devices": "8.13.0", "@ledgerhq/errors": "^6.32.0", "@ledgerhq/hw-transport": "6.34.1", "@ledgerhq/logs": "^6.16.0", "node-hid": "2.1.2" } }, "sha512-aW8iGvpcxw02aaxRJ2IiAGhEvt82ouL08culAfaIGjO+xqvvN6x1D4K1CZAHG50i8feuaq+YF+asd4l8H1ufYA=="], + + "@ledgerhq/logs": ["@ledgerhq/logs@6.16.0", "", {}, "sha512-v/PLfb1dq1En35kkpbfRWp8jLYgbPUXxGhmd4pmvPSIe0nRGkNTomsZASmWQAv6pRonVGqHIBVlte7j1MBbOww=="], + "@msgpack/msgpack": ["@msgpack/msgpack@3.1.3", "", {}, "sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA=="], "@mysten/bcs": ["@mysten/bcs@1.7.0", "", { "dependencies": { "@mysten/utils": "0.1.1", "@scure/base": "^1.2.6" } }, "sha512-8zE2Jzj2ai55RlVXx2pEMbbq+X3vB+uPGBvZr0F79IdTwuwcu4QdFG3PT/zHsytsvATkn+z0f2YDWhM5916u2A=="], @@ -634,6 +649,8 @@ "@types/uuid": ["@types/uuid@8.3.4", "", {}, "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="], + "@types/w3c-web-usb": ["@types/w3c-web-usb@1.0.13", "", {}, "sha512-N2nSl3Xsx8mRHZBvMSdNGtzMyeleTvtlEw+ujujgXalPqOjIA6UtrqcB6OzyUjkTbDm3J7P1RNK1lgoO7jxtsw=="], + "@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], "@types/yargs": ["@types/yargs@17.0.33", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA=="], @@ -774,6 +791,8 @@ "@wry/trie": ["@wry/trie@0.5.0", "", { "dependencies": { "tslib": "^2.3.0" } }, "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA=="], + "@xlabs-xyz/ledger-signer-solana": ["@xlabs-xyz/ledger-signer-solana@0.0.1", "", { "dependencies": { "@ledgerhq/hw-app-solana": "7.1.4", "@ledgerhq/hw-transport": "6.30.4", "@ledgerhq/hw-transport-node-hid": "6.28.4" } }, "sha512-2h9BNc0UYzqbV+S0GDxvh3nV6OSpUOVcbBwQGP0ckl1TAKbwgSzJ+1VYt4xVFJWHLntvfoOpH+DV4LszGWOSNA=="], + "@xrplf/isomorphic": ["@xrplf/isomorphic@1.0.1", "", { "dependencies": { "@noble/hashes": "^1.0.0", "eventemitter3": "5.0.1", "ws": "^8.13.0" } }, "sha512-0bIpgx8PDjYdrLFeC3csF305QQ1L7sxaWnL5y71mCvhenZzJgku9QsA+9QCXBC1eNYtxWO/xR91zrXJy2T/ixg=="], "@xrplf/secret-numbers": ["@xrplf/secret-numbers@2.0.0", "", { "dependencies": { "@xrplf/isomorphic": "^1.0.1", "ripple-keypairs": "^2.0.0" } }, "sha512-z3AOibRTE9E8MbjgzxqMpG1RNaBhQ1jnfhNCa1cGf2reZUJzPMYs4TggQTc7j8+0WyV3cr7y/U8Oz99SXIkN5Q=="], @@ -842,8 +861,12 @@ "bip32": ["bip32@2.0.6", "", { "dependencies": { "@types/node": "10.12.18", "bs58check": "^2.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "tiny-secp256k1": "^1.1.3", "typeforce": "^1.11.5", "wif": "^2.0.6" } }, "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA=="], + "bip32-path": ["bip32-path@0.4.2", "", {}, "sha512-ZBMCELjJfcNMkz5bDuJ1WrYvjlhEF5k6mQ8vUr4N7MbVRsXei7ZOg8VhhwMfNiW68NWmLkgkc6WvTickrLGprQ=="], + "bip39": ["bip39@3.1.0", "", { "dependencies": { "@noble/hashes": "^1.2.0" } }, "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A=="], + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + "bn.js": ["bn.js@5.2.1", "", {}, "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="], "borsh": ["borsh@0.7.0", "", { "dependencies": { "bn.js": "^5.2.0", "bs58": "^4.0.0", "text-encoding-utf-8": "^1.0.2" } }, "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA=="], @@ -900,6 +923,8 @@ "char-regex": ["char-regex@1.0.2", "", {}, "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="], + "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "cipher-base": ["cipher-base@1.0.7", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.2" } }, "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA=="], @@ -970,6 +995,8 @@ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "detect-newline": ["detect-newline@3.1.0", "", {}, "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA=="], "diff": ["diff@4.0.2", "", {}, "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="], @@ -1020,10 +1047,14 @@ "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], + "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], "exit-x": ["exit-x@0.2.2", "", {}, "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ=="], + "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + "expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], "eyes": ["eyes@0.1.8", "", {}, "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ=="], @@ -1054,6 +1085,8 @@ "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], + "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], @@ -1078,6 +1111,8 @@ "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], + "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], + "glob": ["glob@7.1.7", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], @@ -1140,6 +1175,8 @@ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], + "interpret": ["interpret@1.4.0", "", {}, "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA=="], "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], @@ -1336,10 +1373,14 @@ "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], + "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nan": ["nan@2.24.0", "", {}, "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg=="], + "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], + "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], @@ -1350,12 +1391,16 @@ "nock": ["nock@13.5.6", "", { "dependencies": { "debug": "^4.1.0", "json-stringify-safe": "^5.0.1", "propagate": "^2.0.0" } }, "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ=="], + "node-abi": ["node-abi@3.89.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA=="], + "node-addon-api": ["node-addon-api@2.0.2", "", {}, "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="], "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], + "node-hid": ["node-hid@2.2.0", "", { "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^3.0.2", "prebuild-install": "^7.1.1" }, "bin": { "hid-showdevices": "src/show-devices.js" } }, "sha512-vj48zh9j555DZzUhMc8tk/qw6xPFrDyPBH1ST1Z/hWaA/juBJw7IuSxPeOgpzNFNU36mGYj+THioRMt1xOdm/g=="], + "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], @@ -1414,6 +1459,8 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], + "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], + "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], "pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], @@ -1434,6 +1481,8 @@ "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="], + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], + "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], @@ -1494,6 +1543,10 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], + + "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], + "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "snake-case": ["snake-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg=="], @@ -1544,6 +1597,10 @@ "table-layout": ["table-layout@1.0.2", "", { "dependencies": { "array-back": "^4.0.1", "deep-extend": "~0.6.0", "typical": "^5.2.0", "wordwrapjs": "^4.0.0" } }, "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A=="], + "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], + + "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + "test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="], "text-encoding-utf-8": ["text-encoding-utf-8@1.0.2", "", {}, "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="], @@ -1580,6 +1637,8 @@ "tsx": ["tsx@4.20.5", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw=="], + "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], + "tweetnacl": ["tweetnacl@1.0.3", "", {}, "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="], "type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="], @@ -1606,6 +1665,8 @@ "update-browserslist-db": ["update-browserslist-db@1.2.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA=="], + "usb": ["usb@2.9.0", "", { "dependencies": { "@types/w3c-web-usb": "^1.0.6", "node-addon-api": "^6.0.0", "node-gyp-build": "^4.5.0" } }, "sha512-G0I/fPgfHUzWH8xo2KkDxTTFruUWfppgSFJ+bQxz/kVY2x15EQ/XDB7dqD1G432G4gBG4jYQuF3U7j/orSs5nw=="], + "utf-8-validate": ["utf-8-validate@5.0.10", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], @@ -1810,6 +1871,10 @@ "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], + "@ledgerhq/hw-transport-node-hid-noevents/@ledgerhq/hw-transport": ["@ledgerhq/hw-transport@6.34.1", "", { "dependencies": { "@ledgerhq/devices": "8.13.0", "@ledgerhq/errors": "^6.32.0", "@ledgerhq/logs": "^6.16.0", "events": "^3.3.0" } }, "sha512-Bg9Qk2vtm0m0cZn9prZV2Hbvh3b42KBh4uomO00derh+eiwsdg5AXBBptAJiREkew1RVtETRdWxrKchUJfeWvA=="], + + "@ledgerhq/hw-transport-node-hid-noevents/node-hid": ["node-hid@2.1.2", "", { "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^3.0.2", "prebuild-install": "^7.1.1" }, "bin": { "hid-showdevices": "src/show-devices.js" } }, "sha512-qhCyQqrPpP93F/6Wc/xUR7L8mAJW0Z6R7HMQV8jCHHksAxNDe/4z4Un/H9CpLOT+5K39OPyt9tIQlavxWES3lg=="], + "@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], "@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], @@ -1902,6 +1967,8 @@ "bip32/@types/node": ["@types/node@10.12.18", "", {}, "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ=="], + "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], + "borsh/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], "bun-types/@types/node": ["@types/node@22.7.5", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ=="], @@ -2066,6 +2133,8 @@ "no-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + "node-hid/node-addon-api": ["node-addon-api@3.2.1", "", {}, "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="], + "optimism/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "ox/@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], @@ -2082,6 +2151,8 @@ "protobufjs/@types/node": ["@types/node@22.7.5", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ=="], + "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], + "rpc-websockets/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], "rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], @@ -2122,6 +2193,8 @@ "typechain/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], + "usb/node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="], + "viem/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], "viem/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], @@ -2288,6 +2361,8 @@ "@jest/types/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "@ledgerhq/hw-transport-node-hid-noevents/node-hid/node-addon-api": ["node-addon-api@3.2.1", "", {}, "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="], + "@solana/codecs-core/@solana/errors/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], diff --git a/evm/ts-scripts/config/mainnet/chains.json b/evm/ts-scripts/config/mainnet/chains.json index 1f9f58d40..bd5e08d02 100644 --- a/evm/ts-scripts/config/mainnet/chains.json +++ b/evm/ts-scripts/config/mainnet/chains.json @@ -53,7 +53,7 @@ "description": "MegaETH", "evmNetworkId": 4326, "chainId": 64, - "rpc": "http://localhost:50444" + "rpc": "https://mainnet.megaeth.com/rpc" } ] } diff --git a/solana/ts/scripts/config/devnet/programs.json b/solana/ts/scripts/config/devnet/programs.json index d04513669..79501af18 100644 --- a/solana/ts/scripts/config/devnet/programs.json +++ b/solana/ts/scripts/config/devnet/programs.json @@ -4,4 +4,4 @@ "wormholeProgramId": "3u8hJUVTA4jH1wYAyUur7FFZVQ8H635K3tSHHF4ssjQ5", "quoterProgramId": "Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ", "governanceProgramId": "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm" -} \ No newline at end of file +} diff --git a/solana/ts/scripts/config/mainnet/programs.json b/solana/ts/scripts/config/mainnet/programs.json index 1a92028b1..45bea3c55 100644 --- a/solana/ts/scripts/config/mainnet/programs.json +++ b/solana/ts/scripts/config/mainnet/programs.json @@ -4,4 +4,4 @@ "wormholeProgramId": "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth", "quoterProgramId": "Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ", "governanceProgramId": "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm" -} \ No newline at end of file +} From 9e273fc397747c878aa121cad5347fb5efb1e1ef Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:07:36 +0100 Subject: [PATCH 11/24] fix: trim ContractsJson type to only governance-relevant contracts --- evm/ts-scripts/src/env.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/evm/ts-scripts/src/env.ts b/evm/ts-scripts/src/env.ts index 5f9ed7edf..cfb8e34d4 100644 --- a/evm/ts-scripts/src/env.ts +++ b/evm/ts-scripts/src/env.ts @@ -15,19 +15,7 @@ export type Deployment = { }; export type ContractsJson = { - WormholeCoreContracts: Deployment[]; - WormholeRelayers: Deployment[]; - SpecializedRelayers: Deployment[]; - - NttManagerProxies: Deployment[]; - NttManagerImplementations: Deployment[]; - NttTransceiverProxies: Deployment[]; - NttTransceiverImplementations: Deployment[]; - - TransceiverStructsLibs: Deployment[]; - TrimmedAmountLibs: Deployment[]; - GeneralPurposeGovernances: Deployment[]; }; From e877da11f0bf4d26ef04502e52d851140bd336ac Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 16:41:21 +0100 Subject: [PATCH 12/24] fix: trim contracts.json to only governance-relevant contract addresses --- evm/ts-scripts/config/mainnet/contracts.json | 138 +------------------ evm/ts-scripts/config/testnet/contracts.json | 128 +---------------- 2 files changed, 3 insertions(+), 263 deletions(-) diff --git a/evm/ts-scripts/config/mainnet/contracts.json b/evm/ts-scripts/config/mainnet/contracts.json index e3d2fb9c2..78149e71f 100644 --- a/evm/ts-scripts/config/mainnet/contracts.json +++ b/evm/ts-scripts/config/mainnet/contracts.json @@ -1,138 +1,4 @@ { - "WormholeCoreContracts": [ - { - "chainId": 2, - "address": "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B" - }, - { - "chainId": 4, - "address": "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B" - }, - { - "chainId": 6, - "address": "0x54a8e5f9c4CbA08F9943965859F6c34eAF03E26c" - }, - { - "chainId": 23, - "address": "0xa5f208e072434bC67592E4C49C1B991BA79BCA46" - }, - { - "chainId": 24, - "address": "0xEe91C335eab126dF5fDB3797EA9d6aD93aeC9722" - }, - { - "chainId": 30, - "address": "0xbebdb6C8ddC678FfA9f8748f85C815C556Dd8ac6" - } - ], - "WormholeRelayers": [ - { - "chainId": 2, - "address": "0x27428DD2d3DD32A4D7f7C497eAaa23130d894911" - }, - { - "chainId": 23, - "address": "0x27428DD2d3DD32A4D7f7C497eAaa23130d894911" - }, - { - "chainId": 24, - "address": "0x27428DD2d3DD32A4D7f7C497eAaa23130d894911" - }, - { - "chainId": 30, - "address": "0x706F82e9bb5b0813501714Ab5974216704980e31" - } - ], - "SpecializedRelayers": [ - { - "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", - "chainId": 2 - }, - { - "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", - "chainId": 23 - }, - { - "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", - "chainId": 24 - }, - { - "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", - "chainId": 30 - } - ], - "NttManagerImplementations": [ - { - "address": "0x6883bdb0694751DE1545a1057c25b0F4fCC0443d", - "chainId": 2 - }, - { - "address": "0x23Fea5514DFC9821479fBE18BA1D7e1A61f6FfCf", - "chainId": 23 - }, - { - "address": "0x36CF4c88FA548c6Ad9fcDc696e1c27Bb3306163F", - "chainId": 24 - }, - { - "address": "0xc3a1248e9bdC1EEF81d16b2AD1594764cBd9307a", - "chainId": 30 - } - ], - "NttManagerProxies": [ - { - "address": "0xc072B1AEf336eDde59A049699Ef4e8Fa9D594A48", - "chainId": 2 - }, - { - "address": "0x5333d0AcA64a450Add6FeF76D6D1375F726CB484", - "chainId": 23 - }, - { - "address": "0x1a4F1a790f23Ffb9772966cB6F36dCd658033e13", - "chainId": 24 - }, - { - "address": "0x5333d0AcA64a450Add6FeF76D6D1375F726CB484", - "chainId": 30 - } - ], - "TransceiverStructsLibs": [ - { - "chainId": 2, - "address": "0x169D91C797edF56100F1B765268145660503a423" - }, - { - "chainId": 23, - "address": "0x3b5CE72cf76dc9866aCc20DB51E4cAD3Eb719b0E" - }, - { - "chainId": 24, - "address": "0x574B7864119C9223A9870Ea614dC91A8EE09E512" - }, - { - "chainId": 30, - "address": "0x3b5CE72cf76dc9866aCc20DB51E4cAD3Eb719b0E" - } - ], - "NttTransceiverImplementations": [ - { - "address": "0x9bD8b7b527CA4e6738cBDaBdF51C22466756073d", - "chainId": 2 - }, - { - "address": "0x83F97c06dD151B0f8A8aB2629dEf30096443114E", - "chainId": 23 - }, - { - "address": "0xc072B1AEf336eDde59A049699Ef4e8Fa9D594A48", - "chainId": 24 - }, - { - "address": "0x83F97c06dD151B0f8A8aB2629dEf30096443114E", - "chainId": 30 - } - ], "NttTransceiverProxies": [ { "address": "0xDb55492d7190D1baE8ACbE03911C4E3E7426870c", @@ -159,7 +25,7 @@ { "address": "0x8E4dc685e990379b8D53EcA47841E09B8d30043e", "chainId": 4 - } + }, { "address": "0x169D91C797edF56100F1B765268145660503a423", "chainId": 6 @@ -189,4 +55,4 @@ "chainId": 64 } ] -} \ No newline at end of file +} diff --git a/evm/ts-scripts/config/testnet/contracts.json b/evm/ts-scripts/config/testnet/contracts.json index 50320974d..4b4f59b7f 100644 --- a/evm/ts-scripts/config/testnet/contracts.json +++ b/evm/ts-scripts/config/testnet/contracts.json @@ -1,130 +1,4 @@ { - "WormholeCoreContracts": [ - { - "chainId": 10002, - "address": "0x4a8bc80Ed5a4067f1CCf107057b8270E0cC11A78" - }, - { - "chainId": 10003, - "address": "0x6b9C8671cdDC8dEab9c719bB87cBd3e782bA6a35" - }, - { - "chainId": 10004, - "address": "0x79A1027a6A159502049F10906D333EC57E95F083" - }, - { - "chainId": 10005, - "address": "0x31377888146f3253211EFEf5c676D41ECe7D58Fe" - } - ], - "WormholeRelayers": [ - { - "address": "0x7B1bD7a6b4E61c2a123AC6BC2cbfC614437D0470", - "chainId": 10002 - }, - { - "address": "0x7B1bD7a6b4E61c2a123AC6BC2cbfC614437D0470", - "chainId": 10003 - }, - { - "address": "0x93BAD53DDfB6132b0aC8E37f6029163E63372cEE", - "chainId": 10004 - }, - { - "address": "0x93BAD53DDfB6132b0aC8E37f6029163E63372cEE", - "chainId": 10005 - } - ], - "SpecializedRelayers": [ - { - "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", - "chainId": 10002 - }, - { - "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", - "chainId": 10003 - }, - { - "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", - "chainId": 10004 - }, - { - "address": "0x63BE47835c7D66c4aA5B2C688Dc6ed9771c94C74", - "chainId": 10005 - } - ], - "NttManagerImplementations": [ - { - "address": "0x194ef936BB95926597dB614537B4Bd9eAA6dE22F", - "chainId": 10002 - }, - { - "address": "0x5F6D34eF4B60Ae3110Dc001f4925E2931Fc8C123", - "chainId": 10003 - }, - { - "address": "0xB2BC8F10d8C77Bf59b27597a9b91358430268168", - "chainId": 10005 - }, - { - "address": "0xeCF0496DE01e9Aa4ADB50ae56dB550f52003bdB7", - "chainId": 10004 - } - ], - "NttManagerProxies": [ - { - "address": "0x06413c42e913327Bc9a08B7C1E362BAE7C0b9598", - "chainId": 10002 - }, - { - "address": "0xCeC6FB4F352bf3DC2b95E1c41831E4D2DBF9a35D", - "chainId": 10003 - }, - { - "address": "0x27F9Fdd3eaD5aA9A5D827Ca860Be28442A1e7582", - "chainId": 10005 - }, - { - "address": "0x8b9E328bE1b1Bc7501B413d04EBF7479B110775c", - "chainId": 10004 - } - ], - "TransceiverStructsLibs": [ - { - "chainId": 10002, - "address": "0x248277DF3E5d712FB795dFEa2622114670c04Da8" - }, - { - "chainId": 10003, - "address": "0x29f9C3143d51AeA75F9F13F4d47d60C5053096FF" - }, - { - "chainId": 10005, - "address": "0x4998eE4F02DcAD16b2A51Cf7b85710354ef692AC" - }, - { - "chainId": 10004, - "address": "0x9646530F12bD41113F834e61bEd99970f0716Abf" - } - ], - "NttTransceiverImplementations": [ - { - "address": "0xc6DbF3344D40d35ce93E73A19c2AfA647F89C15f", - "chainId": 10002 - }, - { - "address": "0xe9580a0867Dfdcb35E23ffb40d13291417b07161", - "chainId": 10003 - }, - { - "address": "0x2438d2934759954329aa4cA499d7fAE684d1429A", - "chainId": 10005 - }, - { - "address": "0x194ef936BB95926597dB614537B4Bd9eAA6dE22F", - "chainId": 10004 - } - ], "NttTransceiverProxies": [ { "address": "0x649fF7B32C2DE771043ea105c4aAb2D724497238", @@ -165,4 +39,4 @@ "chainId": 10005 } ] -} \ No newline at end of file +} From 2997b168839674e1803a5f68a53692fa53f6f463 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:07:39 +0100 Subject: [PATCH 13/24] fix: move governance.ts out of SDK, address CI and review feedback - Move governance.ts from solana/ts/sdk/ to solana/ts/scripts/ so it doesn't break the SDK build (it depends on @certusone/wormhole-sdk and local IDL artifacts not available in CI) - Update import paths in all scripts that reference it - Fix IDL import paths (../../target -> ../../../target) - Add .env to evm/ts-scripts/.gitignore to prevent secret leakage - Make guardianKey lazy (getGuardianKey function) so scripts that don't need it aren't forced to set the env var - Add "private": true to evm/ts-scripts/package.json - Fix prettier violation in evm env.ts provider construction --- evm/ts-scripts/.gitignore | 1 + evm/ts-scripts/package.json | 1 + evm/ts-scripts/src/env.ts | 6 +----- solana/ts/scripts/env.ts | 4 +++- solana/ts/scripts/executeGovernanceVaa.ts | 2 +- solana/ts/scripts/executeGovernanceVaa_short.ts | 2 +- solana/ts/scripts/executeGovernanceVaas.ts | 2 +- solana/ts/{sdk => scripts}/governance.ts | 4 ++-- solana/ts/scripts/transferOwnershipToGov.ts | 2 +- 9 files changed, 12 insertions(+), 12 deletions(-) rename solana/ts/{sdk => scripts}/governance.ts (97%) diff --git a/evm/ts-scripts/.gitignore b/evm/ts-scripts/.gitignore index f6da57941..22ac52776 100644 --- a/evm/ts-scripts/.gitignore +++ b/evm/ts-scripts/.gitignore @@ -1,4 +1,5 @@ output contract-bindings +.env .env.testnet .env.mainnet \ No newline at end of file diff --git a/evm/ts-scripts/package.json b/evm/ts-scripts/package.json index 3cb069377..ab7a6d398 100644 --- a/evm/ts-scripts/package.json +++ b/evm/ts-scripts/package.json @@ -1,6 +1,7 @@ { "name": "ts-scripts", "version": "1.0.0", + "private": true, "description": "", "main": "index.js", "scripts": { diff --git a/evm/ts-scripts/src/env.ts b/evm/ts-scripts/src/env.ts index cfb8e34d4..bdd812726 100644 --- a/evm/ts-scripts/src/env.ts +++ b/evm/ts-scripts/src/env.ts @@ -177,11 +177,7 @@ export function getProvider( throw new Error("Failed to find a provider RPC for chain " + chain.chainId); } - let provider = new ethers.providers.StaticJsonRpcProvider( - providerRpc, - ); - - return provider; + return new ethers.providers.StaticJsonRpcProvider(providerRpc); } let contracts: ContractsJson; diff --git a/solana/ts/scripts/env.ts b/solana/ts/scripts/env.ts index c4c114d97..8937b409c 100644 --- a/solana/ts/scripts/env.ts +++ b/solana/ts/scripts/env.ts @@ -79,4 +79,6 @@ export function loadScriptConfig(filename: string): any { return config; } -export const guardianKey = getEnv("GUARDIAN_KEY"); +export function getGuardianKey(): string { + return getEnv("GUARDIAN_KEY"); +} diff --git a/solana/ts/scripts/executeGovernanceVaa.ts b/solana/ts/scripts/executeGovernanceVaa.ts index f68225b6a..90ca0223d 100644 --- a/solana/ts/scripts/executeGovernanceVaa.ts +++ b/solana/ts/scripts/executeGovernanceVaa.ts @@ -11,7 +11,7 @@ import { } from "./env"; import { addLedgerSignature, ledgerSignAndSend } from "./helpers"; import { postVaaSolana } from "@certusone/wormhole-sdk"; -import { NTTGovernance } from "../sdk/governance"; +import { NTTGovernance } from "./governance"; (async () => { const { vaa } = getGovernanceVaa(); diff --git a/solana/ts/scripts/executeGovernanceVaa_short.ts b/solana/ts/scripts/executeGovernanceVaa_short.ts index a271c1edb..95529f9d4 100644 --- a/solana/ts/scripts/executeGovernanceVaa_short.ts +++ b/solana/ts/scripts/executeGovernanceVaa_short.ts @@ -12,7 +12,7 @@ import { getGovernanceVaa, } from "./env_short"; import { postVaaSolana } from "@certusone/wormhole-sdk"; -import { NTTGovernance } from "../sdk/governance"; +import { NTTGovernance } from "./governance"; import fs from "fs"; (async () => { diff --git a/solana/ts/scripts/executeGovernanceVaas.ts b/solana/ts/scripts/executeGovernanceVaas.ts index 09faca368..2bd37c567 100644 --- a/solana/ts/scripts/executeGovernanceVaas.ts +++ b/solana/ts/scripts/executeGovernanceVaas.ts @@ -12,7 +12,7 @@ import { } from "./env"; import { addLedgerSignature, ledgerSignAndSend } from "./helpers"; import { postVaaSolana } from "@certusone/wormhole-sdk"; -import { NTTGovernance } from "../sdk/governance"; +import { NTTGovernance } from "./governance"; const governanceVaasFileName = process.env.GOVERNANCE_VAAS_FILE_PATH; if (!governanceVaasFileName) { diff --git a/solana/ts/sdk/governance.ts b/solana/ts/scripts/governance.ts similarity index 97% rename from solana/ts/sdk/governance.ts rename to solana/ts/scripts/governance.ts index 4a5facc12..7223883a6 100644 --- a/solana/ts/sdk/governance.ts +++ b/solana/ts/scripts/governance.ts @@ -10,8 +10,8 @@ import { ParsedVaa } from "@certusone/wormhole-sdk"; import { derivePostedVaaKey } from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole"; import { encoding } from "@wormhole-foundation/sdk-base"; -import { type WormholeGovernance as RawWormholeGovernance } from "../../target/types/wormhole_governance"; -import IDL from "../../target/idl/wormhole_governance.json"; +import { type WormholeGovernance as RawWormholeGovernance } from "../../../target/types/wormhole_governance"; +import IDL from "../../../target/idl/wormhole_governance.json"; type Seed = Uint8Array | string; function derivePda( diff --git a/solana/ts/scripts/transferOwnershipToGov.ts b/solana/ts/scripts/transferOwnershipToGov.ts index acccb21d5..38dc61bac 100644 --- a/solana/ts/scripts/transferOwnershipToGov.ts +++ b/solana/ts/scripts/transferOwnershipToGov.ts @@ -3,7 +3,7 @@ import { PublicKey } from "@solana/web3.js"; import { connection, getSigner, getProgramAddresses } from "./env"; import { NTT } from "../sdk"; import { ledgerSignAndSend } from "./helpers"; -import { NTTGovernance } from "../sdk/governance"; +import { NTTGovernance } from "./governance"; (async () => { const { nttProgramId, wormholeProgramId, governanceProgramId } = getProgramAddresses(); From cbfeff5fda2e8322ebbacff32dd7b60f24e64ef1 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:17:44 +0100 Subject: [PATCH 14/24] style: run prettier on all migrated files --- deployments.md | 52 ++++++++++++++++--- .../config/testnet/governance-vaas.json | 2 +- evm/ts-scripts/src/env.ts | 21 +++++--- evm/ts-scripts/src/executeGovernanceVaa.ts | 20 ++++--- evm/ts-scripts/src/readTransceiverConfig.ts | 33 +++++++----- solana/ts/scripts/claimOwnership.ts | 5 +- .../scripts/config/devnet/governance-vaa.json | 3 +- solana/ts/scripts/config/devnet/ntt.json | 3 +- solana/ts/scripts/config/devnet/programs.json | 1 + .../mainnet/governance-vaas-june-05.json | 3 +- solana/ts/scripts/config/mainnet/ntt.json | 3 +- .../ts/scripts/config/mainnet/programs.json | 1 + solana/ts/scripts/env.ts | 6 +-- solana/ts/scripts/env_short.ts | 8 ++- solana/ts/scripts/executeGovernanceVaa.ts | 11 ++-- .../ts/scripts/executeGovernanceVaa_short.ts | 11 ++-- solana/ts/scripts/executeGovernanceVaas.ts | 14 ++--- solana/ts/scripts/governance.ts | 40 ++++++++------ solana/ts/scripts/helpers.ts | 21 +++++--- solana/ts/scripts/readManagerConfiguration.ts | 10 ++-- solana/ts/scripts/transferOwnershipToGov.ts | 10 ++-- 21 files changed, 178 insertions(+), 100 deletions(-) diff --git a/deployments.md b/deployments.md index dfe56c074..b4179ebd1 100644 --- a/deployments.md +++ b/deployments.md @@ -1,18 +1,24 @@ ## NTT Deployment: + This document serves as an example of a possible order of execution of commands to perform a full deployment of NTT on Solana + one or more EVMs. ### Requirements: + NTT needs the following contracts to be able to operate. This doc assumes that these contracts are already deployed by the time you execute this deployment. Deployed by NTT deployment owner: + - an ntt token Deployed by wormhole: + - wormhole core layer - wormhole relayer (you can use wormhole's public one or deploy your own) ### High level description of the process: + To have a multi-chain NTT setup working a few things need to happen. + - Deploy managers on EVM - Register managers on NTT Token - Deploy Transceivers on EVM @@ -25,9 +31,11 @@ To have a multi-chain NTT setup working a few things need to happen. ## EVM Deployment: ### Environments & Configuration: + These scripts are implemented in way that helps keep track of the deployed addresses by reading contract addresses from json files that can be commited to the repository. Multiple environments are supported through the use of the `ENV` environment variable. To create or use a new env all that needs to be done is to create a new directory on `evm/ts-scripts/config/` with the name of the env you want to use and then set the environment variable ENV to the same value (ie on linux: `export ENV="my-env"`) Inside the configuration directory, there needs to be a few files that will contain all the configuration you need to set up a full NTT deployment: + - `chains.json` contains the configuration used by the deployment runner such as what rpcs will be used or what chains will it run scripts against - `contracts.json` this file contains the addresses of all contracts related to this deployment and will be used any time a script needs to know about the address of other of the contracts. Addresses of contracts will be written to this file as the deployment progress (ie after deploying managers we'll add the manager addresses here) - `managers.json` contains the configuration that will be applied to managers @@ -37,75 +45,97 @@ Inside the configuration directory, there needs to be a few files that will cont > When a script deploys new contracts, it outputs the resulting addresseses on the directory `ts-scripts/output//.json (aside from logging them). Whenever we need to configure the addresses of a contract that we have just deployed we'll fetch the new addresses from those files. ### Deployment Steps: + - create/update `evm/config//contracts.json` as needed - create/update `evm/config//managers.json` as needed - create/update `evm/config//transceivers.json` as needed - Set environment variables: + ``` export FOUNDRY_PROFILE=prod export WALLET_KEY="ledger" export LEDGER_BIP32_PATH="" export ENV=testnet ``` + - Build: + ```shell make build-evm-prod cd evm/ts-scripts npm install -cd .. +cd .. npm --prefix ./ts-scripts run build # builds typescript bindings to evm/ts-scripts/output ``` + - Deploy Managers: + ```shell bash -c ./ts-scripts/shell/deploy-managers.sh # # Use the addresses to update the `contracts.json` file # ``` + - Now that you have deployed your manager, you can go to your token deployment and configure the token to be managed by the managers you have deployed (aka, call `setMinter` on the token to the manager address). - Deploy Transceivers: + ```shell bash -c ./ts-scripts/shell/deploy-transceivers.sh # # Use the addresses to update the `contracts.json` file -# +# ``` + - Configure Managers: + ```shell bash -c ./ts-scripts/shell/configure-managers.sh ``` ## Solana Deployment: + - set environment: + ```shell # Runner Configurations: -export SOLANA_RPC_URL="" +export SOLANA_RPC_URL="" export WORMHOLE_PROGRAM_ID="" # wormhole core layer program id export LEDGER_DERIVATION_PATH="" # derivation path # Deployment configuration: export MINT_ADDRESS="" # the program id of your spl token ``` + - Create and set the pub key of the program: + ```shell solana-keygen grind --ignore-case --starts-with ntt:1 ``` + - use the pub key created to replace the `declare_id!` macro value on `lib.rs` - export the pub-key of the program: + ``` export NTT_PROGRAM_ID= ``` + - Build: + ``` cd solana anchor build --arch sbf -- --features "mainnet" # if you are deploying to tesnet, you should use "solana-devnet" instead of mainnet ``` + - Deploy: + ``` solana program -k usb://ledger?key= deploy --url $SOLANA_RPC_URL --program-id "$NTT_PROGRAM_ID.json" target/deploy/example_native_token_transfers.so ``` + - Initialize the program: -``` shell + +```shell make build npx tsx ./ts/scripts/initializeNtt.ts # @@ -118,24 +148,30 @@ npx tsx ./ts/scripts/initializeNtt.ts ## Cross Registrations: ### EVM cross Registrations: + - Create or update evm/config//peers.json (this file contains the data of all peers that the different evm deployments should know about. Note that the evm cross-registration script can find the addresses of its evm peers, but it can't find the solana peer address, so for solana you'll need to add two extra properties `managerAddress` and `transceiverAddress`. They correspond to the solana ntt deployment program-id and the manager-emitter-address PDA (the one printed during solana program initialization) - Run update peers script: + ```shell bash -c ./ts-scripts/shell/update-peer-addresses.sh ``` ### Solana Cross Registrations: + - update `evmNttDeployments` on solana/ts/scripts/env.ts with the addresses of your EVM managers and transceivers. - Run cross registrations script: + ```shell npx tsx ./ts/scripts/updatePeers.ts -``` +``` ## Contract Bytecode Verification: ### EVM verification: + On evm there are scripts that verify the deployed contracts. To run them you'll need a file with the etherscan scanners keys per each of the supported chains. Such file should have this structure: + ```json [ { @@ -144,13 +180,17 @@ Such file should have this structure: } ] ``` + Run verification scripts: + ``` export SCANNER_TOKENS_FILE_PATH="the/path/to/file/above" bash -c ./evm/ts-scripts/shell/verify-managers.sh bash -c ./evm/ts-scripts/shell/verify-transceivers.sh ``` + Finally, go to the etherscan scanner of the different chains and link the verified proxy to the verified implementation. ### Solana Verification: -TODO \ No newline at end of file + +TODO diff --git a/evm/ts-scripts/config/testnet/governance-vaas.json b/evm/ts-scripts/config/testnet/governance-vaas.json index 98e926610..446c3734a 100644 --- a/evm/ts-scripts/config/testnet/governance-vaas.json +++ b/evm/ts-scripts/config/testnet/governance-vaas.json @@ -15,4 +15,4 @@ "chainId": 10005, "vaa": "AQAAAAABAGHthpHgkBKYghkiZ9wlsjq/yzfOrqtNQI84ZmrQcQX+NFQG2o1XhL2Gm+/iYZjFpmAX3Z9vVs+iMjstsbNwpZsBAAAAAK7WazAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEMpcRWPILd/UgAAAAAAAAAABHZW5lcmFsUHVycG9zZUdvdmVybmFuY2UBJxXOHeHqSwQNMkoHcZBDpiNMlP0LXd3+q8zyBjzWb1OhIY4jxoG6bnliAATO/BQp" } -] \ No newline at end of file +] diff --git a/evm/ts-scripts/src/env.ts b/evm/ts-scripts/src/env.ts index bdd812726..d76a5910c 100644 --- a/evm/ts-scripts/src/env.ts +++ b/evm/ts-scripts/src/env.ts @@ -58,9 +58,12 @@ export function loadScriptConfig(filename: string): any { type ChainConfig = { chainId: ChainId; -} +}; -export async function getChainConfig(filename: string, chainId: ChainId): Promise { +export async function getChainConfig( + filename: string, + chainId: ChainId +): Promise { const scriptConfig: T[] = await loadScriptConfig(filename); const chainConfig = scriptConfig.find((x) => x.chainId == chainId); @@ -72,7 +75,6 @@ export async function getChainConfig(filename: string, ch return chainConfig; } - export function loadOperatingChains(): ChainInfo[] { const allChains = loadChains(); let operatingChains: number[] | null = null; @@ -171,7 +173,8 @@ With ledger devices the path needs to be specified in env var 'LEDGER_BIP32_PATH export function getProvider( chain: ChainInfo ): ethers.providers.StaticJsonRpcProvider { - const providerRpc = loadChains().find((x: any) => x.chainId == chain.chainId)?.rpc || ""; + const providerRpc = + loadChains().find((x: any) => x.chainId == chain.chainId)?.rpc || ""; if (!providerRpc) { throw new Error("Failed to find a provider RPC for chain " + chain.chainId); @@ -205,8 +208,13 @@ export async function getAllContracts(contractName: ContractTypes) { return (await loadContracts())[contractName]; } -export async function getContractAddress(contractName: ContractTypes, chainId: ChainId): Promise { - const contract = (await getAllContracts(contractName))?.find((c) => c.chainId === chainId)?.address; +export async function getContractAddress( + contractName: ContractTypes, + chainId: ChainId +): Promise { + const contract = (await getAllContracts(contractName))?.find( + (c) => c.chainId === chainId + )?.address; if (!contract) { throw new Error(`No ${contractName} contract found for chain ${chainId}`); @@ -214,4 +222,3 @@ export async function getContractAddress(contractName: ContractTypes, chainId: C return contract; } - diff --git a/evm/ts-scripts/src/executeGovernanceVaa.ts b/evm/ts-scripts/src/executeGovernanceVaa.ts index 00a63c2e0..72bce9794 100644 --- a/evm/ts-scripts/src/executeGovernanceVaa.ts +++ b/evm/ts-scripts/src/executeGovernanceVaa.ts @@ -40,7 +40,7 @@ async function run() { if (!result) { continue; } - + if ("error" in result) { console.error( `${processName} failed for chain ${result.chainId}: ${inspect(result.error)}` @@ -52,12 +52,13 @@ async function run() { } } -async function executeGovernance( - chain: ChainInfo, -) { +async function executeGovernance(chain: ChainInfo) { const log = (...args: any[]) => console.log(`[${chain.chainId}]`, ...args); - const { vaa } = await getChainConfig("governance-vaas", chain.chainId); - + const { vaa } = await getChainConfig( + "governance-vaas", + chain.chainId + ); + const governanceContract = await getGovernanceContract(chain); const vaaHex = Buffer.from(vaa, "base64").toString("hex"); @@ -79,6 +80,9 @@ run().then(() => console.log("Done!")); async function getGovernanceContract(chain: ChainInfo) { const signer = await getSigner(chain); - const governanceAddress = await getContractAddress("GeneralPurposeGovernances", chain.chainId); + const governanceAddress = await getContractAddress( + "GeneralPurposeGovernances", + chain.chainId + ); return Governance__factory.connect(governanceAddress, signer); -} \ No newline at end of file +} diff --git a/evm/ts-scripts/src/readTransceiverConfig.ts b/evm/ts-scripts/src/readTransceiverConfig.ts index 2a5f909af..3164c893d 100644 --- a/evm/ts-scripts/src/readTransceiverConfig.ts +++ b/evm/ts-scripts/src/readTransceiverConfig.ts @@ -1,8 +1,6 @@ import { inspect } from "util"; import { ChainId } from "@certusone/wormhole-sdk"; -import { - WormholeTransceiver__factory, -} from "../contract-bindings"; +import { WormholeTransceiver__factory } from "../contract-bindings"; import { loadOperatingChains, init, @@ -23,22 +21,22 @@ const chains = loadOperatingChains(); async function run() { // Warning: we assume that the script configuration file is correctly formed console.log(`Start ${processName}!`); - const peers = await loadScriptConfig("peers") as Peer[]; + const peers = (await loadScriptConfig("peers")) as Peer[]; const results = await Promise.all( - chains.map(async chain => { + chains.map(async (chain) => { try { await readContractConfig(chain, peers); } catch (error) { return { chainId: chain.chainId, error }; } - + return { chainId: chain.chainId }; }) - ) + ); for (const result of results) { - if (("error" in result)) { + if ("error" in result) { console.error( `Error configuring contract for chain ${result.chainId}: ${inspect( result.error @@ -60,19 +58,26 @@ async function readContractConfig(chain: ChainInfo, peers: Peer[]) { log("Peers information:"); for (const peer of peers) { log("Peer:", peer.chainId); - log(" Wormhole relaying enabled:", await transceiverContract.isWormholeRelayingEnabled(peer.chainId)); - log(" Wormhole evm chain:", await transceiverContract.isWormholeEvmChain(peer.chainId)); + log( + " Wormhole relaying enabled:", + await transceiverContract.isWormholeRelayingEnabled(peer.chainId) + ); + log( + " Wormhole evm chain:", + await transceiverContract.isWormholeEvmChain(peer.chainId) + ); } - + return { chainId: chain.chainId }; } async function getTransceiverContract(chain: ChainInfo) { const signer = await getSigner(chain); - const transceiverAddress = await getContractAddress("NttTransceiverProxies", chain.chainId); + const transceiverAddress = await getContractAddress( + "NttTransceiverProxies", + chain.chainId + ); return WormholeTransceiver__factory.connect(transceiverAddress, signer); } - - run().then(() => console.log("Done!")); diff --git a/solana/ts/scripts/claimOwnership.ts b/solana/ts/scripts/claimOwnership.ts index e967165b0..eb67c0cc3 100644 --- a/solana/ts/scripts/claimOwnership.ts +++ b/solana/ts/scripts/claimOwnership.ts @@ -19,11 +19,12 @@ import { ledgerSignAndSend } from "./helpers"; owner: signerPk, }); - console.log(`Account ${signerPk.toBase58()} is claiming ownership of NTT Program ${nttProgramId}.`); + console.log( + `Account ${signerPk.toBase58()} is claiming ownership of NTT Program ${nttProgramId}.` + ); const tx = await ledgerSignAndSend([claimOwnershipIx], []); await connection.confirmTransaction(tx); console.log("Success."); })(); - diff --git a/solana/ts/scripts/config/devnet/governance-vaa.json b/solana/ts/scripts/config/devnet/governance-vaa.json index 5edbd8187..7e2a4dbdc 100644 --- a/solana/ts/scripts/config/devnet/governance-vaa.json +++ b/solana/ts/scripts/config/devnet/governance-vaa.json @@ -1,3 +1,4 @@ { "vaa": "01000000000100403baa70cb8c22af68f1f17ce4790024ef4073a7d380f936e418da36a3ee9e8134515679b2feaff806584edf5a8b1976d11a77b1c1b9323d3596c425652957870100000000a3c9167e00010000000000000000000000000000000000000000000000000000000000000004c815dfbc88fdf79420000000000000000047656e6572616c507572706f7365476f7665726e616e636502000105730eefefc3605ca196155a3ca5594706690d45d01848b8bedfb7d99980f7ec057f97be1c39478e57974f6cc9dbfbeebb0e5ce340c2efd52b8295e889a9ede400050885ee38373250ab21bafa2ea2b6c037b511243608b750fcd6c24bdf31710b1f00018a98013dc14753bff4aa9ba82a9bb6f95835fdc6f97561e4d9eaee76cae462ee00006f776e65720000000000000000000000000000000000000000000000000000000100963bdfa10970171c86e222a176f6f831222031172f2c2de3e511ed95ace40018000102a8f6914e88a1b0e210153ef763ae2b00c2b93d16c124d2c0537a100480000000000008eca6efde0e2d8ffe" -} \ No newline at end of file +} + diff --git a/solana/ts/scripts/config/devnet/ntt.json b/solana/ts/scripts/config/devnet/ntt.json index e565c8048..9b2015b15 100644 --- a/solana/ts/scripts/config/devnet/ntt.json +++ b/solana/ts/scripts/config/devnet/ntt.json @@ -1,4 +1,5 @@ { "outboundLimit": "10000000000000", "mode": "locking" -} \ No newline at end of file +} + diff --git a/solana/ts/scripts/config/devnet/programs.json b/solana/ts/scripts/config/devnet/programs.json index 79501af18..c83dc24c1 100644 --- a/solana/ts/scripts/config/devnet/programs.json +++ b/solana/ts/scripts/config/devnet/programs.json @@ -5,3 +5,4 @@ "quoterProgramId": "Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ", "governanceProgramId": "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm" } + diff --git a/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json b/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json index a4d2cc4ae..9bb7a8e5a 100644 --- a/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json +++ b/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json @@ -7,4 +7,5 @@ "id": "1/0000000000000000000000000000000000000000000000000000000000000004/5665574265595141132", "vaa": "AQAAAAQNALq8JasAs0jexvcmn9DUk729hB/Hn+Nb6fiJlgGpkJUvbfCNUF6c/miNTGBdTWiRvZpUugBfOCha1/rZ0qYvCRsBA+ov5I985O9AQgBYllJGuiLjzxQRF7Zu7LL9vbKyuqICMvkHXNpJYe+XWjjrA0/9dzbEeqbCq2aEibFd4UeHIqUBBfeObPyG+EFKwVhjp14aQ3j1RBSBE2LVr2asP3Gh2E2xVgAIqOis+q0pABMF3ES1oC1gVPTDq3hwZYIncP9bQkIBBsvKjIUf6dzQc1nsla2nzO/7hy+3K+zrnI73RVz6ntyCKFxXLJIFqTxbmwSOEn+X+CpR/1/dZ+///MVcHALo8iUAB6mEOYDzEKbOpwqzHBTKetvLtuytUbDjXLfT2JmIGpDPRBnSOjrdWVeV/xEVs/KT6TmSNATemBerVpUgNRWjln0ACFABS7L/OytWxOvEd415GYO1S8Ome7O9OtcdkBqW7qVCf0kKc4y0WCsKKizS/Zl1JB8MtQTRNeQ4M40wg2HCcK4ACTREFSREEj2w1pXIBoj8FStDn75bSWlgVoumYyvmB7JJVuOwB7ao4yreGRFzKrOTyxjkbHMIVKe1MoI/nekjlnkACs1KektGrLvVTZjB/jKT3Ncr0epxAgX7mUoupH8MZZiHHoT/jcUQX5wKajAaAAQIBS0fQDXQc6ndlHw+/1kNpK0ADdiRpv0GABSe4fPPjJUnchYFlXmkh9mR1XBPetoZcvqiCZEdJgtPNqkg9qpK7t9kna247nsdwzTXmvy7ROsSPfsBDmOH9g4AWxmuViggcYPHN9b4KvM+4YOwUU54hWYGOxGMboS+QzVx+l8m975J8V98xsFklAy+ZdPEwg8oR90rorIBEJgM4lkC+jg77/atGLm9KjGZaefyav89MqcBDjBCBVZjQ5Wn6xhljUlemTXwhnsakaZUyjJe2ovCO0dtPylwCxEAEbth2Ejuso+rAhZGkdvTjVVlzC3EqstMCU/X+Re4FQrIGTjNJfpjn1ZeQ/v0Mwuu8TC9gwJJssju0h+A4/t/k/IAEujBi2W5AGxiDXA22u9q3fQdJ3L7W3pXtGRPtuVHh6FUD0pm5n4jaPuWbcnH2F+EwZU/bSacUAZyiJkWxHaGYqwBAAAAAPB6e/4AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAETqApxu1knAwgAAAAAAAAAABHZW5lcmFsUHVycG9zZUdvdmVybmFuY2UCAAEFcw7v78NgXKGWFVo8pVlHBmkNRdAYSLi+37fZmYD37AV/l74cOUeOV5dPbMnb++67DlzjQMLv1SuCleiJqe3kAAMIhe44NzJQqyG6+i6itsA3tREkNgi3UPzWwkvfMXELHwAAb3duZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABADlm1IlN79/aRsLMXswc43tUQKJlINi1e69RyzvOkpJhAAEAENoIAcyn6QqeAEB6EPNaAAA=" } -] \ No newline at end of file +] + diff --git a/solana/ts/scripts/config/mainnet/ntt.json b/solana/ts/scripts/config/mainnet/ntt.json index e565c8048..9b2015b15 100644 --- a/solana/ts/scripts/config/mainnet/ntt.json +++ b/solana/ts/scripts/config/mainnet/ntt.json @@ -1,4 +1,5 @@ { "outboundLimit": "10000000000000", "mode": "locking" -} \ No newline at end of file +} + diff --git a/solana/ts/scripts/config/mainnet/programs.json b/solana/ts/scripts/config/mainnet/programs.json index 45bea3c55..8944b65f2 100644 --- a/solana/ts/scripts/config/mainnet/programs.json +++ b/solana/ts/scripts/config/mainnet/programs.json @@ -5,3 +5,4 @@ "quoterProgramId": "Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ", "governanceProgramId": "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm" } + diff --git a/solana/ts/scripts/env.ts b/solana/ts/scripts/env.ts index 8937b409c..fc976d825 100644 --- a/solana/ts/scripts/env.ts +++ b/solana/ts/scripts/env.ts @@ -42,7 +42,7 @@ export const connection = new Connection(rpcUrl, connectionCommitmentLevel); export type NttConfig = { outboundLimit: string; mode: "locking" | "burning"; -} +}; export type Programs = { mintProgramId: string; @@ -50,11 +50,11 @@ export type Programs = { wormholeProgramId: string; quoterProgramId: string; governanceProgramId: string; -} +}; export type GovernanceVaa = { vaa: string; -} +}; export function getNttConfiguration(): NttConfig { return loadScriptConfig("ntt"); diff --git a/solana/ts/scripts/env_short.ts b/solana/ts/scripts/env_short.ts index 43732aaf5..567f5892b 100644 --- a/solana/ts/scripts/env_short.ts +++ b/solana/ts/scripts/env_short.ts @@ -11,8 +11,7 @@ export function getEnv(key: string): string { return process.env[key]!; } -export const rpcUrl = - process.env.SOLANA_RPC_URL || "INSERT_RPC_URL"; +export const rpcUrl = process.env.SOLANA_RPC_URL || "INSERT_RPC_URL"; export const connectionCommitmentLevel = (process.env.SOLANA_COMMITMENT || "confirmed") as Commitment; @@ -25,11 +24,11 @@ export type Programs = { wormholeProgramId: string; quoterProgramId: string; governanceProgramId: string; -} +}; export type GovernanceVaa = { vaa: string; -} +}; export function getProgramAddresses(): Programs { return loadScriptConfig("programs"); @@ -49,4 +48,3 @@ export function loadScriptConfig(filename: string): any { } return config; } - diff --git a/solana/ts/scripts/executeGovernanceVaa.ts b/solana/ts/scripts/executeGovernanceVaa.ts index 90ca0223d..374cbbdf9 100644 --- a/solana/ts/scripts/executeGovernanceVaa.ts +++ b/solana/ts/scripts/executeGovernanceVaa.ts @@ -1,7 +1,4 @@ -import { - PublicKey, - Transaction, -} from "@solana/web3.js"; +import { PublicKey, Transaction } from "@solana/web3.js"; import { parseVaa } from "@certusone/wormhole-sdk"; import { connection, @@ -39,7 +36,7 @@ import { NTTGovernance } from "./governance"; sign, new PublicKey(wormholeProgramId), signerPk, - vaaBuff, + vaaBuff ); console.log("VAA posted to Solana."); @@ -52,7 +49,9 @@ import { NTTGovernance } from "./governance"; wormholeId: new PublicKey(wormholeProgramId), }); - console.log(`Account ${signerPk.toBase58()} is claiming ownership of NTT Program ${nttProgramId}.`); + console.log( + `Account ${signerPk.toBase58()} is claiming ownership of NTT Program ${nttProgramId}.` + ); const tx = await ledgerSignAndSend([governanceIx], []); diff --git a/solana/ts/scripts/executeGovernanceVaa_short.ts b/solana/ts/scripts/executeGovernanceVaa_short.ts index 95529f9d4..9ee2204bd 100644 --- a/solana/ts/scripts/executeGovernanceVaa_short.ts +++ b/solana/ts/scripts/executeGovernanceVaa_short.ts @@ -6,22 +6,18 @@ import { sendAndConfirmTransaction, } from "@solana/web3.js"; import { parseVaa } from "@certusone/wormhole-sdk"; -import { - connection, - getProgramAddresses, - getGovernanceVaa, -} from "./env_short"; +import { connection, getProgramAddresses, getGovernanceVaa } from "./env_short"; import { postVaaSolana } from "@certusone/wormhole-sdk"; import { NTTGovernance } from "./governance"; import fs from "fs"; (async () => { - const { vaa } = getGovernanceVaa(); + const { vaa } = getGovernanceVaa(); const { nttProgramId, wormholeProgramId, governanceProgramId } = getProgramAddresses(); - const governance = new NTTGovernance(connection, { + const governance = new NTTGovernance(connection, { programId: governanceProgramId as any, }); @@ -82,4 +78,3 @@ import fs from "fs"; console.log("success:", txSig); })(); - diff --git a/solana/ts/scripts/executeGovernanceVaas.ts b/solana/ts/scripts/executeGovernanceVaas.ts index 2bd37c567..5601ef015 100644 --- a/solana/ts/scripts/executeGovernanceVaas.ts +++ b/solana/ts/scripts/executeGovernanceVaas.ts @@ -20,10 +20,10 @@ if (!governanceVaasFileName) { } (async () => { - - const vaas: { id: string, vaa: string }[] = loadScriptConfig(governanceVaasFileName); - const { wormholeProgramId, governanceProgramId } = - getProgramAddresses(); + const vaas: { id: string; vaa: string }[] = loadScriptConfig( + governanceVaasFileName + ); + const { wormholeProgramId, governanceProgramId } = getProgramAddresses(); const governance = new NTTGovernance(connection, { programId: governanceProgramId as any, @@ -43,13 +43,13 @@ if (!governanceVaasFileName) { console.log(`Posting VAA ${vaa.id} to Solana...`); console.log(`Vaa: ${vaa.vaa}`); - + await postVaaSolana( connection, sign, new PublicKey(wormholeProgramId), signerPk, - vaaBuff, + vaaBuff ); console.log("VAA post succeeded"); @@ -63,7 +63,7 @@ if (!governanceVaasFileName) { }); console.log("Parsed VAA Payload", parsedVaa.payload.toString("hex")); - instructions.push(governanceIx) + instructions.push(governanceIx); } console.log(`Executing governance instructions.`, instructions); diff --git a/solana/ts/scripts/governance.ts b/solana/ts/scripts/governance.ts index 7223883a6..6a9366b5e 100644 --- a/solana/ts/scripts/governance.ts +++ b/solana/ts/scripts/governance.ts @@ -1,5 +1,5 @@ import { Program } from "@coral-xyz/anchor"; -import { +import { PublicKey, type Connection, SystemProgram, @@ -14,14 +14,12 @@ import { type WormholeGovernance as RawWormholeGovernance } from "../../../targe import IDL from "../../../target/idl/wormhole_governance.json"; type Seed = Uint8Array | string; -function derivePda( - seeds: Seed | readonly Seed[], - programId: PublicKey -) { - const toBytes = (s: string | Uint8Array) => typeof s === "string" ? encoding.bytes.encode(s) : s; +function derivePda(seeds: Seed | readonly Seed[], programId: PublicKey) { + const toBytes = (s: string | Uint8Array) => + typeof s === "string" ? encoding.bytes.encode(s) : s; return PublicKey.findProgramAddressSync( Array.isArray(seeds) ? seeds.map(toBytes) : [toBytes(seeds as Seed)], - programId, + programId )[0]; } @@ -70,26 +68,36 @@ export class NTTGovernance { emitterChain.writeUInt16BE(args.vaa.emitterChain); const sequence = Buffer.alloc(8); sequence.writeBigUInt64BE(BigInt(args.vaa.sequence)); - const [governanceProgramId, ixData] = verifyGovernanceHeader(args.vaa.payload); + const [governanceProgramId, ixData] = verifyGovernanceHeader( + args.vaa.payload + ); const ix = deserializeInstruction(ixData); - - const governanceIx = await this.program.methods.governance() + + const governanceIx = await this.program.methods + .governance() .accountsStrict({ payer: args.payer, - governance: derivePda('governance', this.program.programId), + governance: derivePda("governance", this.program.programId), vaa: vaaKey, program: ix.programId, - replay: derivePda(['replay', emitterChain, args.vaa.emitterAddress, sequence], this.program.programId), + replay: derivePda( + ["replay", emitterChain, args.vaa.emitterAddress, sequence], + this.program.programId + ), systemProgram: SystemProgram.programId, - }).instruction(); - + }) + .instruction(); + // add extra instructions - governanceIx.keys = governanceIx.keys.concat(ix.keys.map(k => { return { ...k, isSigner: false }; })); + governanceIx.keys = governanceIx.keys.concat( + ix.keys.map((k) => { + return { ...k, isSigner: false }; + }) + ); return governanceIx; } } - function deserializeInstruction(data: Buffer): TransactionInstruction { let offset = 0; const programId = new PublicKey(data.subarray(offset, offset + 32)); diff --git a/solana/ts/scripts/helpers.ts b/solana/ts/scripts/helpers.ts index 25a768198..6b7c5a246 100644 --- a/solana/ts/scripts/helpers.ts +++ b/solana/ts/scripts/helpers.ts @@ -9,7 +9,10 @@ import { import { SolanaLedgerSigner } from "@xlabs-xyz/ledger-signer-solana"; import { connection, getSigner } from "./env"; -export async function ledgerSignAndSend(instructions: TransactionInstruction[], signers: Keypair[]) { +export async function ledgerSignAndSend( + instructions: TransactionInstruction[], + signers: Keypair[] +) { const deployerSigner = await getSigner(); const deployerPk = new PublicKey(await deployerSigner.getAddress()); @@ -27,7 +30,7 @@ export async function ledgerSignAndSend(instructions: TransactionInstruction[], tx.recentBlockhash = recentBlockHash.blockhash; tx.feePayer = deployerPk; - + signers.forEach((signer) => tx.partialSign(signer)); await addLedgerSignature(tx, deployerSigner, deployerPk); @@ -39,10 +42,16 @@ export async function ledgerSignAndSend(instructions: TransactionInstruction[], return { signature: txSignature, ...recentBlockHash, - } + }; } -export async function addLedgerSignature(tx: Transaction, signer: SolanaLedgerSigner, signerPk: PublicKey) { - const signedByPayer = await signer.signTransaction(tx.compileMessage().serialize()); +export async function addLedgerSignature( + tx: Transaction, + signer: SolanaLedgerSigner, + signerPk: PublicKey +) { + const signedByPayer = await signer.signTransaction( + tx.compileMessage().serialize() + ); tx.addSignature(signerPk, signedByPayer); -} \ No newline at end of file +} diff --git a/solana/ts/scripts/readManagerConfiguration.ts b/solana/ts/scripts/readManagerConfiguration.ts index 697bf3974..f4e9d7790 100644 --- a/solana/ts/scripts/readManagerConfiguration.ts +++ b/solana/ts/scripts/readManagerConfiguration.ts @@ -9,8 +9,13 @@ import { NTT } from "../sdk"; }); // console.log("outbox rate limit account:", await ntt.outboxRateLimitAccountAddress()); - console.log("mintAccountAddress", (await ntt.mintAccountAddress()).toString()); - const accountInfo = await connection.getParsedAccountInfo(await ntt.mintAccountAddress()) + console.log( + "mintAccountAddress", + (await ntt.mintAccountAddress()).toString() + ); + const accountInfo = await connection.getParsedAccountInfo( + await ntt.mintAccountAddress() + ); if (accountInfo.value === null) { console.log("Account not found."); return; @@ -18,4 +23,3 @@ import { NTT } from "../sdk"; console.log("pared account info", accountInfo.value.data); console.log("Success."); })(); - diff --git a/solana/ts/scripts/transferOwnershipToGov.ts b/solana/ts/scripts/transferOwnershipToGov.ts index 38dc61bac..e294c5287 100644 --- a/solana/ts/scripts/transferOwnershipToGov.ts +++ b/solana/ts/scripts/transferOwnershipToGov.ts @@ -6,7 +6,8 @@ import { ledgerSignAndSend } from "./helpers"; import { NTTGovernance } from "./governance"; (async () => { - const { nttProgramId, wormholeProgramId, governanceProgramId } = getProgramAddresses(); + const { nttProgramId, wormholeProgramId, governanceProgramId } = + getProgramAddresses(); const ntt = new NTT(connection, { nttId: nttProgramId as any, @@ -19,7 +20,7 @@ import { NTTGovernance } from "./governance"; const signer = await getSigner(); const signerPk = new PublicKey(await signer.getAddress()); - + const governancePda = governance.governanceAccountAddress(); const transferOwnershipIx = await ntt.createTransferOwnershipInstruction({ @@ -27,7 +28,9 @@ import { NTTGovernance } from "./governance"; newOwner: governancePda, }); - console.log(`Transferring NTT Program ${nttProgramId} ownership to ${governancePda.toBase58()} derived from(${governanceProgramId}).`); + console.log( + `Transferring NTT Program ${nttProgramId} ownership to ${governancePda.toBase58()} derived from(${governanceProgramId}).` + ); const tx = await ledgerSignAndSend([transferOwnershipIx], []); @@ -35,4 +38,3 @@ import { NTTGovernance } from "./governance"; await connection.confirmTransaction(tx); console.log("Success."); })(); - From 69c2af9ae4059ed0ff2aa9b95ba88ff4a5dbd1f5 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:44:12 +0100 Subject: [PATCH 15/24] style: fix remaining prettier issues (match repo prettier 3.6.2) --- solana/ts/scripts/config/devnet/governance-vaa.json | 1 - solana/ts/scripts/config/devnet/ntt.json | 1 - solana/ts/scripts/config/devnet/programs.json | 1 - solana/ts/scripts/config/mainnet/governance-vaas-june-05.json | 1 - solana/ts/scripts/config/mainnet/ntt.json | 1 - solana/ts/scripts/config/mainnet/programs.json | 1 - solana/ts/scripts/governance.ts | 4 ++-- 7 files changed, 2 insertions(+), 8 deletions(-) diff --git a/solana/ts/scripts/config/devnet/governance-vaa.json b/solana/ts/scripts/config/devnet/governance-vaa.json index 7e2a4dbdc..30f39e319 100644 --- a/solana/ts/scripts/config/devnet/governance-vaa.json +++ b/solana/ts/scripts/config/devnet/governance-vaa.json @@ -1,4 +1,3 @@ { "vaa": "01000000000100403baa70cb8c22af68f1f17ce4790024ef4073a7d380f936e418da36a3ee9e8134515679b2feaff806584edf5a8b1976d11a77b1c1b9323d3596c425652957870100000000a3c9167e00010000000000000000000000000000000000000000000000000000000000000004c815dfbc88fdf79420000000000000000047656e6572616c507572706f7365476f7665726e616e636502000105730eefefc3605ca196155a3ca5594706690d45d01848b8bedfb7d99980f7ec057f97be1c39478e57974f6cc9dbfbeebb0e5ce340c2efd52b8295e889a9ede400050885ee38373250ab21bafa2ea2b6c037b511243608b750fcd6c24bdf31710b1f00018a98013dc14753bff4aa9ba82a9bb6f95835fdc6f97561e4d9eaee76cae462ee00006f776e65720000000000000000000000000000000000000000000000000000000100963bdfa10970171c86e222a176f6f831222031172f2c2de3e511ed95ace40018000102a8f6914e88a1b0e210153ef763ae2b00c2b93d16c124d2c0537a100480000000000008eca6efde0e2d8ffe" } - diff --git a/solana/ts/scripts/config/devnet/ntt.json b/solana/ts/scripts/config/devnet/ntt.json index 9b2015b15..e9ca4d0dd 100644 --- a/solana/ts/scripts/config/devnet/ntt.json +++ b/solana/ts/scripts/config/devnet/ntt.json @@ -2,4 +2,3 @@ "outboundLimit": "10000000000000", "mode": "locking" } - diff --git a/solana/ts/scripts/config/devnet/programs.json b/solana/ts/scripts/config/devnet/programs.json index c83dc24c1..79501af18 100644 --- a/solana/ts/scripts/config/devnet/programs.json +++ b/solana/ts/scripts/config/devnet/programs.json @@ -5,4 +5,3 @@ "quoterProgramId": "Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ", "governanceProgramId": "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm" } - diff --git a/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json b/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json index 9bb7a8e5a..c5283b92a 100644 --- a/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json +++ b/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json @@ -8,4 +8,3 @@ "vaa": "AQAAAAQNALq8JasAs0jexvcmn9DUk729hB/Hn+Nb6fiJlgGpkJUvbfCNUF6c/miNTGBdTWiRvZpUugBfOCha1/rZ0qYvCRsBA+ov5I985O9AQgBYllJGuiLjzxQRF7Zu7LL9vbKyuqICMvkHXNpJYe+XWjjrA0/9dzbEeqbCq2aEibFd4UeHIqUBBfeObPyG+EFKwVhjp14aQ3j1RBSBE2LVr2asP3Gh2E2xVgAIqOis+q0pABMF3ES1oC1gVPTDq3hwZYIncP9bQkIBBsvKjIUf6dzQc1nsla2nzO/7hy+3K+zrnI73RVz6ntyCKFxXLJIFqTxbmwSOEn+X+CpR/1/dZ+///MVcHALo8iUAB6mEOYDzEKbOpwqzHBTKetvLtuytUbDjXLfT2JmIGpDPRBnSOjrdWVeV/xEVs/KT6TmSNATemBerVpUgNRWjln0ACFABS7L/OytWxOvEd415GYO1S8Ome7O9OtcdkBqW7qVCf0kKc4y0WCsKKizS/Zl1JB8MtQTRNeQ4M40wg2HCcK4ACTREFSREEj2w1pXIBoj8FStDn75bSWlgVoumYyvmB7JJVuOwB7ao4yreGRFzKrOTyxjkbHMIVKe1MoI/nekjlnkACs1KektGrLvVTZjB/jKT3Ncr0epxAgX7mUoupH8MZZiHHoT/jcUQX5wKajAaAAQIBS0fQDXQc6ndlHw+/1kNpK0ADdiRpv0GABSe4fPPjJUnchYFlXmkh9mR1XBPetoZcvqiCZEdJgtPNqkg9qpK7t9kna247nsdwzTXmvy7ROsSPfsBDmOH9g4AWxmuViggcYPHN9b4KvM+4YOwUU54hWYGOxGMboS+QzVx+l8m975J8V98xsFklAy+ZdPEwg8oR90rorIBEJgM4lkC+jg77/atGLm9KjGZaefyav89MqcBDjBCBVZjQ5Wn6xhljUlemTXwhnsakaZUyjJe2ovCO0dtPylwCxEAEbth2Ejuso+rAhZGkdvTjVVlzC3EqstMCU/X+Re4FQrIGTjNJfpjn1ZeQ/v0Mwuu8TC9gwJJssju0h+A4/t/k/IAEujBi2W5AGxiDXA22u9q3fQdJ3L7W3pXtGRPtuVHh6FUD0pm5n4jaPuWbcnH2F+EwZU/bSacUAZyiJkWxHaGYqwBAAAAAPB6e/4AAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAETqApxu1knAwgAAAAAAAAAABHZW5lcmFsUHVycG9zZUdvdmVybmFuY2UCAAEFcw7v78NgXKGWFVo8pVlHBmkNRdAYSLi+37fZmYD37AV/l74cOUeOV5dPbMnb++67DlzjQMLv1SuCleiJqe3kAAMIhe44NzJQqyG6+i6itsA3tREkNgi3UPzWwkvfMXELHwAAb3duZXIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABADlm1IlN79/aRsLMXswc43tUQKJlINi1e69RyzvOkpJhAAEAENoIAcyn6QqeAEB6EPNaAAA=" } ] - diff --git a/solana/ts/scripts/config/mainnet/ntt.json b/solana/ts/scripts/config/mainnet/ntt.json index 9b2015b15..e9ca4d0dd 100644 --- a/solana/ts/scripts/config/mainnet/ntt.json +++ b/solana/ts/scripts/config/mainnet/ntt.json @@ -2,4 +2,3 @@ "outboundLimit": "10000000000000", "mode": "locking" } - diff --git a/solana/ts/scripts/config/mainnet/programs.json b/solana/ts/scripts/config/mainnet/programs.json index 8944b65f2..45bea3c55 100644 --- a/solana/ts/scripts/config/mainnet/programs.json +++ b/solana/ts/scripts/config/mainnet/programs.json @@ -5,4 +5,3 @@ "quoterProgramId": "Nqd6XqA8LbsCuG8MLWWuP865NV6jR1MbXeKxD4HLKDJ", "governanceProgramId": "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm" } - diff --git a/solana/ts/scripts/governance.ts b/solana/ts/scripts/governance.ts index 6a9366b5e..1e289455c 100644 --- a/solana/ts/scripts/governance.ts +++ b/solana/ts/scripts/governance.ts @@ -29,8 +29,8 @@ type OmitGenerics = { [P in keyof T]: T[P] extends Record<"generics", any> ? never : T[P] extends object - ? OmitGenerics - : T[P]; + ? OmitGenerics + : T[P]; }; export type WormholeGovernance = OmitGenerics; From 22bfeaa8a9076ee3fa0260e44909af5400fc5aeb Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 18:50:34 +0100 Subject: [PATCH 16/24] fix: remove readTransceiverConfig and NttTransceiverProxies (not needed for governance) --- evm/ts-scripts/config/mainnet/contracts.json | 18 ----- evm/ts-scripts/config/testnet/contracts.json | 18 ----- evm/ts-scripts/src/env.ts | 1 - evm/ts-scripts/src/readTransceiverConfig.ts | 83 -------------------- 4 files changed, 120 deletions(-) delete mode 100644 evm/ts-scripts/src/readTransceiverConfig.ts diff --git a/evm/ts-scripts/config/mainnet/contracts.json b/evm/ts-scripts/config/mainnet/contracts.json index 78149e71f..55be86608 100644 --- a/evm/ts-scripts/config/mainnet/contracts.json +++ b/evm/ts-scripts/config/mainnet/contracts.json @@ -1,22 +1,4 @@ { - "NttTransceiverProxies": [ - { - "address": "0xDb55492d7190D1baE8ACbE03911C4E3E7426870c", - "chainId": 2 - }, - { - "address": "0xD1a8AB69e00266e8B791a15BC47514153A5045a6", - "chainId": 23 - }, - { - "address": "0x9bD8b7b527CA4e6738cBDaBdF51C22466756073d", - "chainId": 24 - }, - { - "address": "0xD1a8AB69e00266e8B791a15BC47514153A5045a6", - "chainId": 30 - } - ], "GeneralPurposeGovernances": [ { "address": "0x23Fea5514DFC9821479fBE18BA1D7e1A61f6FfCf", diff --git a/evm/ts-scripts/config/testnet/contracts.json b/evm/ts-scripts/config/testnet/contracts.json index 4b4f59b7f..fa348bd91 100644 --- a/evm/ts-scripts/config/testnet/contracts.json +++ b/evm/ts-scripts/config/testnet/contracts.json @@ -1,22 +1,4 @@ { - "NttTransceiverProxies": [ - { - "address": "0x649fF7B32C2DE771043ea105c4aAb2D724497238", - "chainId": 10002 - }, - { - "address": "0xfA42603152E4f133F5F3DA610CDa91dF5821d8bc", - "chainId": 10003 - }, - { - "address": "0xeCF0496DE01e9Aa4ADB50ae56dB550f52003bdB7", - "chainId": 10005 - }, - { - "address": "0x149987472333cD48ac6D28293A338a1EEa6Be7EE", - "chainId": 10004 - } - ], "GeneralPurposeGovernances": [ { "address": "0x574B7864119C9223A9870Ea614dC91A8EE09E512", diff --git a/evm/ts-scripts/src/env.ts b/evm/ts-scripts/src/env.ts index d76a5910c..f5774340d 100644 --- a/evm/ts-scripts/src/env.ts +++ b/evm/ts-scripts/src/env.ts @@ -15,7 +15,6 @@ export type Deployment = { }; export type ContractsJson = { - NttTransceiverProxies: Deployment[]; GeneralPurposeGovernances: Deployment[]; }; diff --git a/evm/ts-scripts/src/readTransceiverConfig.ts b/evm/ts-scripts/src/readTransceiverConfig.ts deleted file mode 100644 index 3164c893d..000000000 --- a/evm/ts-scripts/src/readTransceiverConfig.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { inspect } from "util"; -import { ChainId } from "@certusone/wormhole-sdk"; -import { WormholeTransceiver__factory } from "../contract-bindings"; -import { - loadOperatingChains, - init, - ChainInfo, - getSigner, - getContractAddress, - loadScriptConfig, -} from "./env"; - -const processName = "readTransceiverConfig"; - -type Peer = { - chainId: ChainId; -}; - -init(); -const chains = loadOperatingChains(); -async function run() { - // Warning: we assume that the script configuration file is correctly formed - console.log(`Start ${processName}!`); - const peers = (await loadScriptConfig("peers")) as Peer[]; - - const results = await Promise.all( - chains.map(async (chain) => { - try { - await readContractConfig(chain, peers); - } catch (error) { - return { chainId: chain.chainId, error }; - } - - return { chainId: chain.chainId }; - }) - ); - - for (const result of results) { - if ("error" in result) { - console.error( - `Error configuring contract for chain ${result.chainId}: ${inspect( - result.error - )}` - ); - continue; - } - - console.log(`Configuration succeded for chain ${result.chainId}`); - } -} -async function readContractConfig(chain: ChainInfo, peers: Peer[]) { - const log = (...args: any[]) => console.log(`[${chain.chainId}]`, ...args); - - const transceiverContract = await getTransceiverContract(chain); - - log("Reading contract configuration..."); - log("Token address: ", await transceiverContract.getNttManagerToken()); - log("Peers information:"); - for (const peer of peers) { - log("Peer:", peer.chainId); - log( - " Wormhole relaying enabled:", - await transceiverContract.isWormholeRelayingEnabled(peer.chainId) - ); - log( - " Wormhole evm chain:", - await transceiverContract.isWormholeEvmChain(peer.chainId) - ); - } - - return { chainId: chain.chainId }; -} - -async function getTransceiverContract(chain: ChainInfo) { - const signer = await getSigner(chain); - const transceiverAddress = await getContractAddress( - "NttTransceiverProxies", - chain.chainId - ); - return WormholeTransceiver__factory.connect(transceiverAddress, signer); -} - -run().then(() => console.log("Done!")); From fe0aa64bdbce227d472a846f9edda3730e4a625a Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:04:54 +0100 Subject: [PATCH 17/24] fix: remove unused files (deployments.md, verify shell script, ntt configs, evm gitignore, NttConfig type) --- deployments.md | 196 ------------------ evm/.gitignore | 1 - .../shell/verify-governance-contract.sh | 60 ------ solana/ts/scripts/config/devnet/ntt.json | 4 - solana/ts/scripts/config/mainnet/ntt.json | 4 - solana/ts/scripts/env.ts | 9 - 6 files changed, 274 deletions(-) delete mode 100644 deployments.md delete mode 100644 evm/.gitignore delete mode 100755 evm/ts-scripts/shell/verify-governance-contract.sh delete mode 100644 solana/ts/scripts/config/devnet/ntt.json delete mode 100644 solana/ts/scripts/config/mainnet/ntt.json diff --git a/deployments.md b/deployments.md deleted file mode 100644 index b4179ebd1..000000000 --- a/deployments.md +++ /dev/null @@ -1,196 +0,0 @@ -## NTT Deployment: - -This document serves as an example of a possible order of execution of commands to perform a full deployment of NTT on Solana + one or more EVMs. - -### Requirements: - -NTT needs the following contracts to be able to operate. This doc assumes that these contracts are already deployed by the time you execute this deployment. - -Deployed by NTT deployment owner: - -- an ntt token - -Deployed by wormhole: - -- wormhole core layer -- wormhole relayer (you can use wormhole's public one or deploy your own) - -### High level description of the process: - -To have a multi-chain NTT setup working a few things need to happen. - -- Deploy managers on EVM -- Register managers on NTT Token -- Deploy Transceivers on EVM -- Deploy NTT Program on Solana -- Set Mint authority to NTT program -- Initialize NTT program on Solana -- Cross register managers (let each manager know about the other managers) -- Cross register transceivers - -## EVM Deployment: - -### Environments & Configuration: - -These scripts are implemented in way that helps keep track of the deployed addresses by reading contract addresses from json files that can be commited to the repository. Multiple environments are supported through the use of the `ENV` environment variable. -To create or use a new env all that needs to be done is to create a new directory on `evm/ts-scripts/config/` with the name of the env you want to use and then set the environment variable ENV to the same value (ie on linux: `export ENV="my-env"`) -Inside the configuration directory, there needs to be a few files that will contain all the configuration you need to set up a full NTT deployment: - -- `chains.json` contains the configuration used by the deployment runner such as what rpcs will be used or what chains will it run scripts against -- `contracts.json` this file contains the addresses of all contracts related to this deployment and will be used any time a script needs to know about the address of other of the contracts. Addresses of contracts will be written to this file as the deployment progress (ie after deploying managers we'll add the manager addresses here) -- `managers.json` contains the configuration that will be applied to managers -- `transceivers.json` contains the configuration that will be applied to transceivers -- `peers.json` contains the configuration that will be used for contract cross-registrations - -> When a script deploys new contracts, it outputs the resulting addresseses on the directory `ts-scripts/output//.json (aside from logging them). Whenever we need to configure the addresses of a contract that we have just deployed we'll fetch the new addresses from those files. - -### Deployment Steps: - -- create/update `evm/config//contracts.json` as needed -- create/update `evm/config//managers.json` as needed -- create/update `evm/config//transceivers.json` as needed -- Set environment variables: - -``` -export FOUNDRY_PROFILE=prod -export WALLET_KEY="ledger" -export LEDGER_BIP32_PATH="" -export ENV=testnet -``` - -- Build: - -```shell -make build-evm-prod -cd evm/ts-scripts -npm install -cd .. -npm --prefix ./ts-scripts run build # builds typescript bindings to evm/ts-scripts/output -``` - -- Deploy Managers: - -```shell -bash -c ./ts-scripts/shell/deploy-managers.sh -# -# Use the addresses to update the `contracts.json` file -# -``` - -- Now that you have deployed your manager, you can go to your token deployment and configure the token to be managed by the managers you have deployed (aka, call `setMinter` on the token to the manager address). -- Deploy Transceivers: - -```shell -bash -c ./ts-scripts/shell/deploy-transceivers.sh -# -# Use the addresses to update the `contracts.json` file -# -``` - -- Configure Managers: - -```shell -bash -c ./ts-scripts/shell/configure-managers.sh -``` - -## Solana Deployment: - -- set environment: - -```shell -# Runner Configurations: -export SOLANA_RPC_URL="" -export WORMHOLE_PROGRAM_ID="" # wormhole core layer program id -export LEDGER_DERIVATION_PATH="" # derivation path - -# Deployment configuration: -export MINT_ADDRESS="" # the program id of your spl token -``` - -- Create and set the pub key of the program: - -```shell -solana-keygen grind --ignore-case --starts-with ntt:1 -``` - -- use the pub key created to replace the `declare_id!` macro value on `lib.rs` -- export the pub-key of the program: - -``` -export NTT_PROGRAM_ID= -``` - -- Build: - -``` -cd solana -anchor build --arch sbf -- --features "mainnet" # if you are deploying to tesnet, you should use "solana-devnet" instead of mainnet -``` - -- Deploy: - -``` -solana program -k usb://ledger?key= deploy --url $SOLANA_RPC_URL --program-id "$NTT_PROGRAM_ID.json" target/deploy/example_native_token_transfers.so -``` - -- Initialize the program: - -```shell -make build -npx tsx ./ts/scripts/initializeNtt.ts -# -# The script will print the manager emitter address PDA. You can always derive -# this pda again, but might be easier if you take note of it at this point. -# You'll need it later to do cross-registrations. -# -``` - -## Cross Registrations: - -### EVM cross Registrations: - -- Create or update evm/config//peers.json (this file contains the data of all peers that the different evm deployments should know about. Note that the evm cross-registration script can find the addresses of its evm peers, but it can't find the solana peer address, so for solana you'll need to add two extra properties `managerAddress` and `transceiverAddress`. They correspond to the solana ntt deployment program-id and the manager-emitter-address PDA (the one printed during solana program initialization) -- Run update peers script: - -```shell -bash -c ./ts-scripts/shell/update-peer-addresses.sh -``` - -### Solana Cross Registrations: - -- update `evmNttDeployments` on solana/ts/scripts/env.ts with the addresses of your EVM managers and transceivers. -- Run cross registrations script: - -```shell -npx tsx ./ts/scripts/updatePeers.ts -``` - -## Contract Bytecode Verification: - -### EVM verification: - -On evm there are scripts that verify the deployed contracts. To run them you'll need a file with the etherscan scanners keys per each of the supported chains. -Such file should have this structure: - -```json -[ - { - "chainId": 6, - "etherscan": "YOUR-ETTHERSCAN-API_KEY" - } -] -``` - -Run verification scripts: - -``` -export SCANNER_TOKENS_FILE_PATH="the/path/to/file/above" -bash -c ./evm/ts-scripts/shell/verify-managers.sh -bash -c ./evm/ts-scripts/shell/verify-transceivers.sh -``` - -Finally, go to the etherscan scanner of the different chains and link the verified proxy to the verified implementation. - -### Solana Verification: - -TODO diff --git a/evm/.gitignore b/evm/.gitignore deleted file mode 100644 index c4f48aa8a..000000000 --- a/evm/.gitignore +++ /dev/null @@ -1 +0,0 @@ -forge-build \ No newline at end of file diff --git a/evm/ts-scripts/shell/verify-governance-contract.sh b/evm/ts-scripts/shell/verify-governance-contract.sh deleted file mode 100755 index 1365693c3..000000000 --- a/evm/ts-scripts/shell/verify-governance-contract.sh +++ /dev/null @@ -1,60 +0,0 @@ -if test -z "$ENV"; then - echo "ENV is not set" - exit 1 -fi - -scanner_tokens_file="$SCANNER_TOKENS_FILE_PATH" -if test -z $scanner_tokens_file; then - echo "SCANNER_TOKENS_FILE_PATH is not set" - exit 1 -fi -echo "Using scanner tokens file at $scanner_tokens_file" - -chains_file_path="ts-scripts/config/$ENV/chains.json" -if ! test -f "$chains_file_path"; then - echo "File does not exist at $chains_file_path" - exit 1 -fi - -contracts_file_path="ts-scripts/config/$ENV/contracts.json" -if ! test -f "$contracts_file_path"; then - echo "Contracts file configuration does not exist at $contracts_file_path" - exit 1 -fi - -operating_chains=$(jq -r '.operatingChains' $chains_file_path); - -if [ "$operating_chains" = "null" ]; then - operating_chains=$(jq -r '.chains[] | .chainId' $chains_file_path) -else - operating_chains=$(jq -r '.operatingChains[]' $chains_file_path) -fi - -export FOUNDRY_PROFILE=prod - -for chain in $operating_chains; do - echo "Operating on chain $chain:" - - export governance_address=$(jq --raw-output ".GeneralPurposeGovernances[] | select(.chainId == $chain) | .address" $contracts_file_path) - export core_wormhole_address=$(jq --raw-output ".WormholeCoreContracts[] | select(.chainId == $chain) | .address" $contracts_file_path) - export etherscan_api_key=$(jq --raw-output ".[] | select(.chainId == $chain) | .etherscan" $scanner_tokens_file) - export evm_chain_id=$(jq ".chains[] | select(.chainId == $chain) | .evmNetworkId" $chains_file_path) - - # echo "governance_address: $governance_address" - # echo "etherscan_api_key: $etherscan_api_key" - # echo "evm_chain_id: $evm_chain_id" - - if [ "$governance_address" = "" ] || - [ "$governance_address" = "" ] || - [ "$etherscan_api_key" = "null" ] || - [ "$evm_chain_id" = "null" ]; then - echo "One of the addresses is not set. Skipping..."; - continue - fi - - forge verify-contract --chain "$evm_chain_id" \ - --etherscan-api-key "$etherscan_api_key" \ - "$governance_address" \ - src/wormhole/Governance.sol:Governance --watch \ - --constructor-args $(cast abi-encode "constructor(address)" "$core_wormhole_address") -done diff --git a/solana/ts/scripts/config/devnet/ntt.json b/solana/ts/scripts/config/devnet/ntt.json deleted file mode 100644 index e9ca4d0dd..000000000 --- a/solana/ts/scripts/config/devnet/ntt.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "outboundLimit": "10000000000000", - "mode": "locking" -} diff --git a/solana/ts/scripts/config/mainnet/ntt.json b/solana/ts/scripts/config/mainnet/ntt.json deleted file mode 100644 index e9ca4d0dd..000000000 --- a/solana/ts/scripts/config/mainnet/ntt.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "outboundLimit": "10000000000000", - "mode": "locking" -} diff --git a/solana/ts/scripts/env.ts b/solana/ts/scripts/env.ts index fc976d825..60ed1363b 100644 --- a/solana/ts/scripts/env.ts +++ b/solana/ts/scripts/env.ts @@ -39,11 +39,6 @@ export const connectionCommitmentLevel = (process.env.SOLANA_COMMITMENT || export const connection = new Connection(rpcUrl, connectionCommitmentLevel); -export type NttConfig = { - outboundLimit: string; - mode: "locking" | "burning"; -}; - export type Programs = { mintProgramId: string; nttProgramId: string; @@ -56,10 +51,6 @@ export type GovernanceVaa = { vaa: string; }; -export function getNttConfiguration(): NttConfig { - return loadScriptConfig("ntt"); -} - export function getProgramAddresses(): Programs { return loadScriptConfig("programs"); } From 797559b1cf408b67e43e8cb5e3a26fc796e395a8 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:11:04 +0100 Subject: [PATCH 18/24] fix: fix typos in error message and log output --- solana/ts/scripts/executeGovernanceVaas.ts | 2 +- solana/ts/scripts/readManagerConfiguration.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/solana/ts/scripts/executeGovernanceVaas.ts b/solana/ts/scripts/executeGovernanceVaas.ts index 5601ef015..87bd2920e 100644 --- a/solana/ts/scripts/executeGovernanceVaas.ts +++ b/solana/ts/scripts/executeGovernanceVaas.ts @@ -16,7 +16,7 @@ import { NTTGovernance } from "./governance"; const governanceVaasFileName = process.env.GOVERNANCE_VAAS_FILE_PATH; if (!governanceVaasFileName) { - throw new Error("GOVERNANCE_VAAS_FILE_NAME is required."); + throw new Error("GOVERNANCE_VAAS_FILE_PATH is required."); } (async () => { diff --git a/solana/ts/scripts/readManagerConfiguration.ts b/solana/ts/scripts/readManagerConfiguration.ts index f4e9d7790..c3ada47ab 100644 --- a/solana/ts/scripts/readManagerConfiguration.ts +++ b/solana/ts/scripts/readManagerConfiguration.ts @@ -20,6 +20,6 @@ import { NTT } from "../sdk"; console.log("Account not found."); return; } - console.log("pared account info", accountInfo.value.data); + console.log("parsed account info", accountInfo.value.data); console.log("Success."); })(); From 2c9b54ddfcc2c454c482c8ea544327c692a706f1 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 19:19:05 +0100 Subject: [PATCH 19/24] fix: remove scripts that depend on old NTT SDK class (needs adaptation to SolanaNtt) --- solana/ts/scripts/claimOwnership.ts | 30 -------------- solana/ts/scripts/readManagerConfiguration.ts | 25 ------------ solana/ts/scripts/transferOwnershipToGov.ts | 40 ------------------- 3 files changed, 95 deletions(-) delete mode 100644 solana/ts/scripts/claimOwnership.ts delete mode 100644 solana/ts/scripts/readManagerConfiguration.ts delete mode 100644 solana/ts/scripts/transferOwnershipToGov.ts diff --git a/solana/ts/scripts/claimOwnership.ts b/solana/ts/scripts/claimOwnership.ts deleted file mode 100644 index eb67c0cc3..000000000 --- a/solana/ts/scripts/claimOwnership.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { PublicKey } from "@solana/web3.js"; - -import { connection, getSigner, getProgramAddresses } from "./env"; -import { NTT } from "../sdk"; -import { ledgerSignAndSend } from "./helpers"; - -(async () => { - const { nttProgramId, wormholeProgramId } = getProgramAddresses(); - - const signer = await getSigner(); - const signerPk = new PublicKey(await signer.getAddress()); - - const ntt = new NTT(connection, { - nttId: nttProgramId as any, - wormholeId: wormholeProgramId as any, - }); - - const claimOwnershipIx = await ntt.createClaimOwnershipInstruction({ - owner: signerPk, - }); - - console.log( - `Account ${signerPk.toBase58()} is claiming ownership of NTT Program ${nttProgramId}.` - ); - - const tx = await ledgerSignAndSend([claimOwnershipIx], []); - - await connection.confirmTransaction(tx); - console.log("Success."); -})(); diff --git a/solana/ts/scripts/readManagerConfiguration.ts b/solana/ts/scripts/readManagerConfiguration.ts deleted file mode 100644 index c3ada47ab..000000000 --- a/solana/ts/scripts/readManagerConfiguration.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { connection, getProgramAddresses } from "./env"; -import { NTT } from "../sdk"; - -(async () => { - const programs = getProgramAddresses(); - const ntt = new NTT(connection, { - nttId: programs.nttProgramId as any, - wormholeId: programs.wormholeProgramId as any, - }); - - // console.log("outbox rate limit account:", await ntt.outboxRateLimitAccountAddress()); - console.log( - "mintAccountAddress", - (await ntt.mintAccountAddress()).toString() - ); - const accountInfo = await connection.getParsedAccountInfo( - await ntt.mintAccountAddress() - ); - if (accountInfo.value === null) { - console.log("Account not found."); - return; - } - console.log("parsed account info", accountInfo.value.data); - console.log("Success."); -})(); diff --git a/solana/ts/scripts/transferOwnershipToGov.ts b/solana/ts/scripts/transferOwnershipToGov.ts deleted file mode 100644 index e294c5287..000000000 --- a/solana/ts/scripts/transferOwnershipToGov.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { PublicKey } from "@solana/web3.js"; - -import { connection, getSigner, getProgramAddresses } from "./env"; -import { NTT } from "../sdk"; -import { ledgerSignAndSend } from "./helpers"; -import { NTTGovernance } from "./governance"; - -(async () => { - const { nttProgramId, wormholeProgramId, governanceProgramId } = - getProgramAddresses(); - - const ntt = new NTT(connection, { - nttId: nttProgramId as any, - wormholeId: wormholeProgramId as any, - }); - - const governance = new NTTGovernance(connection, { - programId: governanceProgramId as any, - }); - - const signer = await getSigner(); - const signerPk = new PublicKey(await signer.getAddress()); - - const governancePda = governance.governanceAccountAddress(); - - const transferOwnershipIx = await ntt.createTransferOwnershipInstruction({ - owner: signerPk, - newOwner: governancePda, - }); - - console.log( - `Transferring NTT Program ${nttProgramId} ownership to ${governancePda.toBase58()} derived from(${governanceProgramId}).` - ); - - const tx = await ledgerSignAndSend([transferOwnershipIx], []); - - console.log("Waiting for confirmation... Signature: ", tx.signature); - await connection.confirmTransaction(tx); - console.log("Success."); -})(); From 5a1fccad177e631b3f71b7ce3d895fb6a4270316 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 20:03:44 +0100 Subject: [PATCH 20/24] fix: restore migrated governance script inputs --- evm/ts-scripts/config/mainnet/managers.json | 62 +++++++++++++++++++++ evm/ts-scripts/config/testnet/managers.json | 62 +++++++++++++++++++++ solana/ts/scripts/governance.ts | 4 +- 3 files changed, 126 insertions(+), 2 deletions(-) create mode 100644 evm/ts-scripts/config/mainnet/managers.json create mode 100644 evm/ts-scripts/config/testnet/managers.json diff --git a/evm/ts-scripts/config/mainnet/managers.json b/evm/ts-scripts/config/mainnet/managers.json new file mode 100644 index 000000000..8685e2a28 --- /dev/null +++ b/evm/ts-scripts/config/mainnet/managers.json @@ -0,0 +1,62 @@ +[ + { + "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", + "mode": 1, + "chainId": 2, + "rateLimitDuration": 86400, + "skipRateLimit": false, + "threshold": 1, + "outboundLimit": 4000000000000000000000000, + "inboundLimit": [ + { "chainId": 1, "limit": "5500000000000000000000000" }, + { "chainId": 23, "limit": "1500000000000000000000000" }, + { "chainId": 24, "limit": "40000000000000000000000" }, + { "chainId": 30, "limit": "120000000000000000000000" } + ] + }, + { + "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", + "mode": 1, + "chainId": 23, + "rateLimitDuration": 86400, + "skipRateLimit": false, + "threshold": 1, + "outboundLimit": 2300000000000000000000000, + "inboundLimit": [ + { "chainId": 1, "limit": "1500000000000000000000000" }, + { "chainId": 2, "limit": "1000000000000000000000000" }, + { "chainId": 24, "limit": "335000000000000000000000" }, + { "chainId": 30, "limit": "100000000000000000000000" } + ] + }, + { + "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", + "mode": 1, + "chainId": 24, + "rateLimitDuration": 86400, + "skipRateLimit": false, + "threshold": 1, + "outboundLimit": 544000000000000000000000, + "inboundLimit": [ + { "chainId": 1, "limit": "35000000000000000000000" }, + { "chainId": 2, "limit": "200000000000000000000000" }, + { "chainId": 23, "limit": "60000000000000000000000" }, + { "chainId": 30, "limit": "140000000000000000000000" } + ] + }, + { + "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", + "mode": 1, + "chainId": 30, + "rateLimitDuration": 86400, + "skipRateLimit": false, + "threshold": 1, + "outboundLimit": 1700000000000000000000000, + "inboundLimit": [ + { "chainId": 1, "limit": "135000000000000000000000" }, + { "chainId": 2, "limit": "440000000000000000000000" }, + { "chainId": 23, "limit": "140000000000000000000000" }, + { "chainId": 24, "limit": "240000000000000000000000" } + ] + } +] diff --git a/evm/ts-scripts/config/testnet/managers.json b/evm/ts-scripts/config/testnet/managers.json new file mode 100644 index 000000000..4bcb224dd --- /dev/null +++ b/evm/ts-scripts/config/testnet/managers.json @@ -0,0 +1,62 @@ +[ + { + "token": "0x738141EFf659625F2eAD4feECDfCD94155C67f18", + "mode": 1, + "chainId": 10002, + "rateLimitDuration": 3600, + "skipRateLimit": false, + "threshold": 1, + "outboundLimit": 4000000000000000000000000, + "inboundLimit": [ + { "chainId": 1, "limit": "1000000000000000000000" }, + { "chainId": 10003, "limit": "120000000000000000000000" }, + { "chainId": 10004, "limit": "40000000000000000000000000" }, + { "chainId": 10005, "limit": "1500000000000000000000000" } + ] + }, + { + "token": "0x395D3C74232D12916ecA8952BA352b4d27818035", + "mode": 1, + "chainId": 10003, + "rateLimitDuration": 3600, + "skipRateLimit": false, + "threshold": 1, + "outboundLimit": 100000000000000000000, + "inboundLimit": [ + { "chainId": 1, "limit": "1500000000000000000000000" }, + { "chainId": 10002, "limit": "1000000000000000000000000" }, + { "chainId": 10004, "limit": "335000000000000000000000" }, + { "chainId": 10005, "limit": "100000000000000000000000" } + ] + }, + { + "token": "0x1d30E78B7C7fbbcef87ae6e97B5389b2e470CA4a", + "mode": 1, + "chainId": 10004, + "rateLimitDuration": 3600, + "skipRateLimit": false, + "threshold": 1, + "outboundLimit": 1700000000000000000000000, + "inboundLimit": [ + { "chainId": 1, "limit": "135000000000000000000000" }, + { "chainId": 10002, "limit": "440000000000000000000000" }, + { "chainId": 10003, "limit": "140000000000000000000000" }, + { "chainId": 10005, "limit": "240000000000000000000000" } + ] + }, + { + "token": "0xdDFeABcCf2063CD66f53a1218e23c681Ba6e7962", + "mode": 1, + "chainId": 10005, + "rateLimitDuration": 3600, + "skipRateLimit": false, + "threshold": 1, + "outboundLimit": 544000000000000000000000, + "inboundLimit": [ + { "chainId": 1, "limit": "35000000000000000000000" }, + { "chainId": 10002, "limit": "200000000000000000000000" }, + { "chainId": 10003, "limit": "140000000000000000000000" }, + { "chainId": 10004, "limit": "60000000000000000000000" } + ] + } +] diff --git a/solana/ts/scripts/governance.ts b/solana/ts/scripts/governance.ts index 1e289455c..9db813b9c 100644 --- a/solana/ts/scripts/governance.ts +++ b/solana/ts/scripts/governance.ts @@ -10,8 +10,8 @@ import { ParsedVaa } from "@certusone/wormhole-sdk"; import { derivePostedVaaKey } from "@certusone/wormhole-sdk/lib/cjs/solana/wormhole"; import { encoding } from "@wormhole-foundation/sdk-base"; -import { type WormholeGovernance as RawWormholeGovernance } from "../../../target/types/wormhole_governance"; -import IDL from "../../../target/idl/wormhole_governance.json"; +import { type WormholeGovernance as RawWormholeGovernance } from "../../target/types/wormhole_governance"; +import IDL from "../../target/idl/wormhole_governance.json"; type Seed = Uint8Array | string; function derivePda(seeds: Seed | readonly Seed[], programId: PublicKey) { From d8083357a1dc527928576e56767a96ffa7f40f07 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 21:06:50 +0100 Subject: [PATCH 21/24] refactor: move Solana scripts to separate private package Moves governance scripts from solana/ts/scripts/ into solana/ts-scripts/ with its own package.json (private: true) to avoid polluting the published @wormhole-foundation/sdk-solana-ntt SDK with script-only dependencies like @xlabs-xyz/ledger-signer-solana and dotenv. Mirrors the existing evm/ts-scripts/ pattern. --- solana/package.json | 2 -- solana/{ts/scripts => ts-scripts}/.env.sample | 0 solana/ts-scripts/.gitignore | 3 +++ .../config/devnet/governance-vaa.json | 0 .../config/devnet/programs.json | 0 .../mainnet/governance-vaas-june-05.json | 0 .../config/mainnet/programs.json | 0 solana/ts-scripts/package.json | 19 +++++++++++++++++++ solana/{ts/scripts => ts-scripts/src}/env.ts | 2 +- .../scripts => ts-scripts/src}/env_short.ts | 2 +- .../src}/executeGovernanceVaa.ts | 0 .../src}/executeGovernanceVaa_short.ts | 0 .../src}/executeGovernanceVaas.ts | 0 .../scripts => ts-scripts/src}/governance.ts | 0 .../{ts/scripts => ts-scripts/src}/helpers.ts | 0 .../src}/setUpTestnet.ts | 2 +- solana/ts-scripts/tsconfig.json | 13 +++++++++++++ 17 files changed, 38 insertions(+), 5 deletions(-) rename solana/{ts/scripts => ts-scripts}/.env.sample (100%) create mode 100644 solana/ts-scripts/.gitignore rename solana/{ts/scripts => ts-scripts}/config/devnet/governance-vaa.json (100%) rename solana/{ts/scripts => ts-scripts}/config/devnet/programs.json (100%) rename solana/{ts/scripts => ts-scripts}/config/mainnet/governance-vaas-june-05.json (100%) rename solana/{ts/scripts => ts-scripts}/config/mainnet/programs.json (100%) create mode 100644 solana/ts-scripts/package.json rename solana/{ts/scripts => ts-scripts/src}/env.ts (97%) rename solana/{ts/scripts => ts-scripts/src}/env_short.ts (95%) rename solana/{ts/scripts => ts-scripts/src}/executeGovernanceVaa.ts (100%) rename solana/{ts/scripts => ts-scripts/src}/executeGovernanceVaa_short.ts (100%) rename solana/{ts/scripts => ts-scripts/src}/executeGovernanceVaas.ts (100%) rename solana/{ts/scripts => ts-scripts/src}/governance.ts (100%) rename solana/{ts/scripts => ts-scripts/src}/helpers.ts (100%) rename solana/{ts/scripts => ts-scripts/src}/setUpTestnet.ts (96%) create mode 100644 solana/ts-scripts/tsconfig.json diff --git a/solana/package.json b/solana/package.json index 8d84ec244..d0879cd72 100644 --- a/solana/package.json +++ b/solana/package.json @@ -44,8 +44,6 @@ }, "devDependencies": { "@types/bn.js": "^5.1.5", - "@xlabs-xyz/ledger-signer-solana": "^0.0.1", - "dotenv": "^16.4.5", "nock": "^13.3.3", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", diff --git a/solana/ts/scripts/.env.sample b/solana/ts-scripts/.env.sample similarity index 100% rename from solana/ts/scripts/.env.sample rename to solana/ts-scripts/.env.sample diff --git a/solana/ts-scripts/.gitignore b/solana/ts-scripts/.gitignore new file mode 100644 index 000000000..ecff56526 --- /dev/null +++ b/solana/ts-scripts/.gitignore @@ -0,0 +1,3 @@ +.env +node_modules +dist diff --git a/solana/ts/scripts/config/devnet/governance-vaa.json b/solana/ts-scripts/config/devnet/governance-vaa.json similarity index 100% rename from solana/ts/scripts/config/devnet/governance-vaa.json rename to solana/ts-scripts/config/devnet/governance-vaa.json diff --git a/solana/ts/scripts/config/devnet/programs.json b/solana/ts-scripts/config/devnet/programs.json similarity index 100% rename from solana/ts/scripts/config/devnet/programs.json rename to solana/ts-scripts/config/devnet/programs.json diff --git a/solana/ts/scripts/config/mainnet/governance-vaas-june-05.json b/solana/ts-scripts/config/mainnet/governance-vaas-june-05.json similarity index 100% rename from solana/ts/scripts/config/mainnet/governance-vaas-june-05.json rename to solana/ts-scripts/config/mainnet/governance-vaas-june-05.json diff --git a/solana/ts/scripts/config/mainnet/programs.json b/solana/ts-scripts/config/mainnet/programs.json similarity index 100% rename from solana/ts/scripts/config/mainnet/programs.json rename to solana/ts-scripts/config/mainnet/programs.json diff --git a/solana/ts-scripts/package.json b/solana/ts-scripts/package.json new file mode 100644 index 000000000..77ee0d890 --- /dev/null +++ b/solana/ts-scripts/package.json @@ -0,0 +1,19 @@ +{ + "name": "solana-ts-scripts", + "version": "1.0.0", + "private": true, + "description": "Governance scripts for Solana NTT", + "type": "module", + "scripts": {}, + "devDependencies": { + "@certusone/wormhole-sdk": "^0.10.11", + "@coral-xyz/anchor": "0.29.0", + "@solana/web3.js": "^1.95.8", + "@types/node": "^20.11.22", + "@wormhole-foundation/sdk-base": "^4.12.2", + "@xlabs-xyz/ledger-signer-solana": "^0.0.1", + "dotenv": "^16.4.5", + "tsx": "^4.7.2", + "typescript": "^5.4.5" + } +} diff --git a/solana/ts/scripts/env.ts b/solana/ts-scripts/src/env.ts similarity index 97% rename from solana/ts/scripts/env.ts rename to solana/ts-scripts/src/env.ts index 60ed1363b..53a8c419a 100644 --- a/solana/ts/scripts/env.ts +++ b/solana/ts-scripts/src/env.ts @@ -61,7 +61,7 @@ export function getGovernanceVaa(): GovernanceVaa { export function loadScriptConfig(filename: string): any { const configFile = fs.readFileSync( - `./ts/scripts/config/${env}/${filename}.json` + `./config/${env}/${filename}.json` ); const config = JSON.parse(configFile.toString()); if (!config) { diff --git a/solana/ts/scripts/env_short.ts b/solana/ts-scripts/src/env_short.ts similarity index 95% rename from solana/ts/scripts/env_short.ts rename to solana/ts-scripts/src/env_short.ts index 567f5892b..f41a5978e 100644 --- a/solana/ts/scripts/env_short.ts +++ b/solana/ts-scripts/src/env_short.ts @@ -40,7 +40,7 @@ export function getGovernanceVaa(): GovernanceVaa { export function loadScriptConfig(filename: string): any { const configFile = fs.readFileSync( - `./ts/scripts/config/${env}/${filename}.json` + `./config/${env}/${filename}.json` ); const config = JSON.parse(configFile.toString()); if (!config) { diff --git a/solana/ts/scripts/executeGovernanceVaa.ts b/solana/ts-scripts/src/executeGovernanceVaa.ts similarity index 100% rename from solana/ts/scripts/executeGovernanceVaa.ts rename to solana/ts-scripts/src/executeGovernanceVaa.ts diff --git a/solana/ts/scripts/executeGovernanceVaa_short.ts b/solana/ts-scripts/src/executeGovernanceVaa_short.ts similarity index 100% rename from solana/ts/scripts/executeGovernanceVaa_short.ts rename to solana/ts-scripts/src/executeGovernanceVaa_short.ts diff --git a/solana/ts/scripts/executeGovernanceVaas.ts b/solana/ts-scripts/src/executeGovernanceVaas.ts similarity index 100% rename from solana/ts/scripts/executeGovernanceVaas.ts rename to solana/ts-scripts/src/executeGovernanceVaas.ts diff --git a/solana/ts/scripts/governance.ts b/solana/ts-scripts/src/governance.ts similarity index 100% rename from solana/ts/scripts/governance.ts rename to solana/ts-scripts/src/governance.ts diff --git a/solana/ts/scripts/helpers.ts b/solana/ts-scripts/src/helpers.ts similarity index 100% rename from solana/ts/scripts/helpers.ts rename to solana/ts-scripts/src/helpers.ts diff --git a/solana/ts/scripts/setUpTestnet.ts b/solana/ts-scripts/src/setUpTestnet.ts similarity index 96% rename from solana/ts/scripts/setUpTestnet.ts rename to solana/ts-scripts/src/setUpTestnet.ts index 45a89da89..ae1c49c54 100644 --- a/solana/ts/scripts/setUpTestnet.ts +++ b/solana/ts-scripts/src/setUpTestnet.ts @@ -1,6 +1,6 @@ import { Connection, Keypair, PublicKey } from "@solana/web3.js"; import "dotenv/config"; -import { NTT } from "../lib"; +import { NTT } from "../../ts/lib"; import { BN } from "@coral-xyz/anchor"; main(); diff --git a/solana/ts-scripts/tsconfig.json b/solana/ts-scripts/tsconfig.json new file mode 100644 index 000000000..ae9df7397 --- /dev/null +++ b/solana/ts-scripts/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "esModuleInterop": true, + "resolveJsonModule": true, + "strict": true, + "skipLibCheck": true, + "outDir": "./dist" + }, + "include": ["src/**/*.ts"] +} From a0498302c7f6de6b473e86920f24754c1a53f0d9 Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Fri, 27 Mar 2026 21:12:52 +0100 Subject: [PATCH 22/24] fix: update lockfile, trim managers.json to used fields, fix prettier - Regenerate bun.lock after removing script deps from solana/package.json - Trim managers.json configs to only token + chainId (the only fields used by createGovernanceMessage.ts) - Fix prettier formatting on moved env files --- bun.lock | 76 ------------------- evm/ts-scripts/config/mainnet/managers.json | 64 +--------------- evm/ts-scripts/config/testnet/managers.json | 64 +--------------- evm/ts-scripts/src/createGovernanceMessage.ts | 3 - solana/ts-scripts/src/env.ts | 4 +- solana/ts-scripts/src/env_short.ts | 4 +- 6 files changed, 10 insertions(+), 205 deletions(-) diff --git a/bun.lock b/bun.lock index d290f318b..5a43f6f4c 100644 --- a/bun.lock +++ b/bun.lock @@ -113,8 +113,6 @@ }, "devDependencies": { "@types/bn.js": "^5.1.5", - "@xlabs-xyz/ledger-signer-solana": "^0.0.1", - "dotenv": "^16.4.5", "nock": "^13.3.3", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", @@ -489,20 +487,6 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], - "@ledgerhq/devices": ["@ledgerhq/devices@8.13.0", "", { "dependencies": { "@ledgerhq/errors": "^6.32.0", "@ledgerhq/logs": "^6.16.0", "rxjs": "7.8.2", "semver": "7.7.3" } }, "sha512-hgGn1kpe/rT0EJ0Qs7rG+1TXA4g6HN2t3dB4DndRTqVqC9aSSbME+ajA0QWLZisxOD3zkwvO4Q0mJ2zARAKyag=="], - - "@ledgerhq/errors": ["@ledgerhq/errors@6.32.0", "", {}, "sha512-BjjvhLM6UXYUbhllqAduo9PSneLt9FXZ3TBEUFQ3MMSZOCHt0gAgDySLwul99R8fdYWkXBza4DYQjUNckpN2lg=="], - - "@ledgerhq/hw-app-solana": ["@ledgerhq/hw-app-solana@7.1.4", "", { "dependencies": { "@ledgerhq/errors": "^6.16.2", "@ledgerhq/hw-transport": "^6.30.4", "bip32-path": "^0.4.2" } }, "sha512-xOrCPKzF6RnqD9eRyjltJkWQDS39/Wev3GxvD4sKYuhERdZ+W8Gg0/nxKK468qGNPwhiDaRj7inPKGSMNCADqQ=="], - - "@ledgerhq/hw-transport": ["@ledgerhq/hw-transport@6.30.4", "", { "dependencies": { "@ledgerhq/devices": "^8.2.1", "@ledgerhq/errors": "^6.16.2", "@ledgerhq/logs": "^6.12.0", "events": "^3.3.0" } }, "sha512-VBcVd7UG8HDrjWMoZI5rqBDz+PBxLHTIPZOGY/fdMoEUwaBbss0Z3MxuJanMyerlfaLqnBSVuL0blz7rOyagkw=="], - - "@ledgerhq/hw-transport-node-hid": ["@ledgerhq/hw-transport-node-hid@6.28.4", "", { "dependencies": { "@ledgerhq/devices": "^8.2.1", "@ledgerhq/errors": "^6.16.2", "@ledgerhq/hw-transport": "^6.30.4", "@ledgerhq/hw-transport-node-hid-noevents": "^6.29.4", "@ledgerhq/logs": "^6.12.0", "lodash": "^4.17.21", "node-hid": "^2.1.2", "usb": "2.9.0" } }, "sha512-f6yQvZe4cwg6ibRn1YE9wWTdvpyT7FBIxxYHAd4RKm0l5/WFZS2G8CwdNJQwlOr89iBgGvEswWYh8EhUxM5QYA=="], - - "@ledgerhq/hw-transport-node-hid-noevents": ["@ledgerhq/hw-transport-node-hid-noevents@6.34.0", "", { "dependencies": { "@ledgerhq/devices": "8.13.0", "@ledgerhq/errors": "^6.32.0", "@ledgerhq/hw-transport": "6.34.1", "@ledgerhq/logs": "^6.16.0", "node-hid": "2.1.2" } }, "sha512-aW8iGvpcxw02aaxRJ2IiAGhEvt82ouL08culAfaIGjO+xqvvN6x1D4K1CZAHG50i8feuaq+YF+asd4l8H1ufYA=="], - - "@ledgerhq/logs": ["@ledgerhq/logs@6.16.0", "", {}, "sha512-v/PLfb1dq1En35kkpbfRWp8jLYgbPUXxGhmd4pmvPSIe0nRGkNTomsZASmWQAv6pRonVGqHIBVlte7j1MBbOww=="], - "@msgpack/msgpack": ["@msgpack/msgpack@3.1.3", "", {}, "sha512-47XIizs9XZXvuJgoaJUIE2lFoID8ugvc0jzSHP+Ptfk8nTbnR8g788wv48N03Kx0UkAv559HWRQ3yzOgzlRNUA=="], "@mysten/bcs": ["@mysten/bcs@1.7.0", "", { "dependencies": { "@mysten/utils": "0.1.1", "@scure/base": "^1.2.6" } }, "sha512-8zE2Jzj2ai55RlVXx2pEMbbq+X3vB+uPGBvZr0F79IdTwuwcu4QdFG3PT/zHsytsvATkn+z0f2YDWhM5916u2A=="], @@ -649,8 +633,6 @@ "@types/uuid": ["@types/uuid@8.3.4", "", {}, "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw=="], - "@types/w3c-web-usb": ["@types/w3c-web-usb@1.0.13", "", {}, "sha512-N2nSl3Xsx8mRHZBvMSdNGtzMyeleTvtlEw+ujujgXalPqOjIA6UtrqcB6OzyUjkTbDm3J7P1RNK1lgoO7jxtsw=="], - "@types/ws": ["@types/ws@7.4.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww=="], "@types/yargs": ["@types/yargs@17.0.33", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA=="], @@ -791,8 +773,6 @@ "@wry/trie": ["@wry/trie@0.5.0", "", { "dependencies": { "tslib": "^2.3.0" } }, "sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA=="], - "@xlabs-xyz/ledger-signer-solana": ["@xlabs-xyz/ledger-signer-solana@0.0.1", "", { "dependencies": { "@ledgerhq/hw-app-solana": "7.1.4", "@ledgerhq/hw-transport": "6.30.4", "@ledgerhq/hw-transport-node-hid": "6.28.4" } }, "sha512-2h9BNc0UYzqbV+S0GDxvh3nV6OSpUOVcbBwQGP0ckl1TAKbwgSzJ+1VYt4xVFJWHLntvfoOpH+DV4LszGWOSNA=="], - "@xrplf/isomorphic": ["@xrplf/isomorphic@1.0.1", "", { "dependencies": { "@noble/hashes": "^1.0.0", "eventemitter3": "5.0.1", "ws": "^8.13.0" } }, "sha512-0bIpgx8PDjYdrLFeC3csF305QQ1L7sxaWnL5y71mCvhenZzJgku9QsA+9QCXBC1eNYtxWO/xR91zrXJy2T/ixg=="], "@xrplf/secret-numbers": ["@xrplf/secret-numbers@2.0.0", "", { "dependencies": { "@xrplf/isomorphic": "^1.0.1", "ripple-keypairs": "^2.0.0" } }, "sha512-z3AOibRTE9E8MbjgzxqMpG1RNaBhQ1jnfhNCa1cGf2reZUJzPMYs4TggQTc7j8+0WyV3cr7y/U8Oz99SXIkN5Q=="], @@ -861,12 +841,8 @@ "bip32": ["bip32@2.0.6", "", { "dependencies": { "@types/node": "10.12.18", "bs58check": "^2.1.1", "create-hash": "^1.2.0", "create-hmac": "^1.1.7", "tiny-secp256k1": "^1.1.3", "typeforce": "^1.11.5", "wif": "^2.0.6" } }, "sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA=="], - "bip32-path": ["bip32-path@0.4.2", "", {}, "sha512-ZBMCELjJfcNMkz5bDuJ1WrYvjlhEF5k6mQ8vUr4N7MbVRsXei7ZOg8VhhwMfNiW68NWmLkgkc6WvTickrLGprQ=="], - "bip39": ["bip39@3.1.0", "", { "dependencies": { "@noble/hashes": "^1.2.0" } }, "sha512-c9kiwdk45Do5GL0vJMe7tS95VjCii65mYAH7DfWl3uW8AVzXKQVUm64i3hzVybBDMp9r7j9iNxR85+ul8MdN/A=="], - "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], - "bn.js": ["bn.js@5.2.1", "", {}, "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ=="], "borsh": ["borsh@0.7.0", "", { "dependencies": { "bn.js": "^5.2.0", "bs58": "^4.0.0", "text-encoding-utf-8": "^1.0.2" } }, "sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA=="], @@ -923,8 +899,6 @@ "char-regex": ["char-regex@1.0.2", "", {}, "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw=="], - "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], - "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "cipher-base": ["cipher-base@1.0.7", "", { "dependencies": { "inherits": "^2.0.4", "safe-buffer": "^5.2.1", "to-buffer": "^1.2.2" } }, "sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA=="], @@ -995,8 +969,6 @@ "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], - "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], - "detect-newline": ["detect-newline@3.1.0", "", {}, "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA=="], "diff": ["diff@4.0.2", "", {}, "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A=="], @@ -1047,14 +1019,10 @@ "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], - "events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="], - "execa": ["execa@5.1.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" } }, "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg=="], "exit-x": ["exit-x@0.2.2", "", {}, "sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ=="], - "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], - "expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], "eyes": ["eyes@0.1.8", "", {}, "sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ=="], @@ -1085,8 +1053,6 @@ "form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="], - "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], - "fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], @@ -1111,8 +1077,6 @@ "get-tsconfig": ["get-tsconfig@4.13.0", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ=="], - "github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="], - "glob": ["glob@7.1.7", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], @@ -1175,8 +1139,6 @@ "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], - "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - "interpret": ["interpret@1.4.0", "", {}, "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA=="], "is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="], @@ -1373,14 +1335,10 @@ "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], - "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], - "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nan": ["nan@2.24.0", "", {}, "sha512-Vpf9qnVW1RaDkoNKFUvfxqAbtI8ncb8OJlqZ9wwpXzWPEsvsB1nvdUi6oYrHIkQ1Y/tMDnr1h4nczS0VB9Xykg=="], - "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], - "napi-postinstall": ["napi-postinstall@0.3.4", "", { "bin": { "napi-postinstall": "lib/cli.js" } }, "sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], @@ -1391,16 +1349,12 @@ "nock": ["nock@13.5.6", "", { "dependencies": { "debug": "^4.1.0", "json-stringify-safe": "^5.0.1", "propagate": "^2.0.0" } }, "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ=="], - "node-abi": ["node-abi@3.89.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-6u9UwL0HlAl21+agMN3YAMXcKByMqwGx+pq+P76vii5f7hTPtKDp08/H9py6DY+cfDw7kQNTGEj/rly3IgbNQA=="], - "node-addon-api": ["node-addon-api@2.0.2", "", {}, "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="], "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], "node-gyp-build": ["node-gyp-build@4.8.4", "", { "bin": { "node-gyp-build": "bin.js", "node-gyp-build-optional": "optional.js", "node-gyp-build-test": "build-test.js" } }, "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ=="], - "node-hid": ["node-hid@2.2.0", "", { "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^3.0.2", "prebuild-install": "^7.1.1" }, "bin": { "hid-showdevices": "src/show-devices.js" } }, "sha512-vj48zh9j555DZzUhMc8tk/qw6xPFrDyPBH1ST1Z/hWaA/juBJw7IuSxPeOgpzNFNU36mGYj+THioRMt1xOdm/g=="], - "node-int64": ["node-int64@0.4.0", "", {}, "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw=="], "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], @@ -1459,8 +1413,6 @@ "possible-typed-array-names": ["possible-typed-array-names@1.1.0", "", {}, "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg=="], - "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], - "prettier": ["prettier@3.6.2", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ=="], "pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="], @@ -1481,8 +1433,6 @@ "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="], - "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], - "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], @@ -1543,10 +1493,6 @@ "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], - "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], - - "simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="], - "slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="], "snake-case": ["snake-case@3.0.4", "", { "dependencies": { "dot-case": "^3.0.4", "tslib": "^2.0.3" } }, "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg=="], @@ -1597,10 +1543,6 @@ "table-layout": ["table-layout@1.0.2", "", { "dependencies": { "array-back": "^4.0.1", "deep-extend": "~0.6.0", "typical": "^5.2.0", "wordwrapjs": "^4.0.0" } }, "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A=="], - "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], - - "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], - "test-exclude": ["test-exclude@6.0.0", "", { "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" } }, "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w=="], "text-encoding-utf-8": ["text-encoding-utf-8@1.0.2", "", {}, "sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg=="], @@ -1637,8 +1579,6 @@ "tsx": ["tsx@4.20.5", "", { "dependencies": { "esbuild": "~0.25.0", "get-tsconfig": "^4.7.5" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "bin": { "tsx": "dist/cli.mjs" } }, "sha512-+wKjMNU9w/EaQayHXb7WA7ZaHY6hN8WgfvHNQ3t1PnU91/7O8TcTnIhCDYTZwnt8JsO9IBqZ30Ln1r7pPF52Aw=="], - "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], - "tweetnacl": ["tweetnacl@1.0.3", "", {}, "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw=="], "type-detect": ["type-detect@4.0.8", "", {}, "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g=="], @@ -1665,8 +1605,6 @@ "update-browserslist-db": ["update-browserslist-db@1.2.2", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-E85pfNzMQ9jpKkA7+TJAi4TJN+tBCuWh5rUcS/sv6cFi+1q9LYDwDI5dpUL0u/73EElyQ8d3TEaeW4sPedBqYA=="], - "usb": ["usb@2.9.0", "", { "dependencies": { "@types/w3c-web-usb": "^1.0.6", "node-addon-api": "^6.0.0", "node-gyp-build": "^4.5.0" } }, "sha512-G0I/fPgfHUzWH8xo2KkDxTTFruUWfppgSFJ+bQxz/kVY2x15EQ/XDB7dqD1G432G4gBG4jYQuF3U7j/orSs5nw=="], - "utf-8-validate": ["utf-8-validate@5.0.10", "", { "dependencies": { "node-gyp-build": "^4.3.0" } }, "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], @@ -1871,10 +1809,6 @@ "@jest/types/chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], - "@ledgerhq/hw-transport-node-hid-noevents/@ledgerhq/hw-transport": ["@ledgerhq/hw-transport@6.34.1", "", { "dependencies": { "@ledgerhq/devices": "8.13.0", "@ledgerhq/errors": "^6.32.0", "@ledgerhq/logs": "^6.16.0", "events": "^3.3.0" } }, "sha512-Bg9Qk2vtm0m0cZn9prZV2Hbvh3b42KBh4uomO00derh+eiwsdg5AXBBptAJiREkew1RVtETRdWxrKchUJfeWvA=="], - - "@ledgerhq/hw-transport-node-hid-noevents/node-hid": ["node-hid@2.1.2", "", { "dependencies": { "bindings": "^1.5.0", "node-addon-api": "^3.0.2", "prebuild-install": "^7.1.1" }, "bin": { "hid-showdevices": "src/show-devices.js" } }, "sha512-qhCyQqrPpP93F/6Wc/xUR7L8mAJW0Z6R7HMQV8jCHHksAxNDe/4z4Un/H9CpLOT+5K39OPyt9tIQlavxWES3lg=="], - "@solana/codecs/@solana/codecs-numbers": ["@solana/codecs-numbers@2.0.0-rc.1", "", { "dependencies": { "@solana/codecs-core": "2.0.0-rc.1", "@solana/errors": "2.0.0-rc.1" }, "peerDependencies": { "typescript": ">=5" } }, "sha512-J5i5mOkvukXn8E3Z7sGIPxsThRCgSdgTWJDQeZvucQ9PT6Y3HiVXJ0pcWiOWAoQ3RX8e/f4I3IC+wE6pZiJzDQ=="], "@solana/codecs-core/@solana/errors": ["@solana/errors@2.0.0-rc.1", "", { "dependencies": { "chalk": "^5.3.0", "commander": "^12.1.0" }, "peerDependencies": { "typescript": ">=5" }, "bin": { "errors": "bin/cli.mjs" } }, "sha512-ejNvQ2oJ7+bcFAYWj225lyRkHnixuAeb7RQCixm+5mH4n1IA4Qya/9Bmfy5RAAHQzxK43clu3kZmL5eF9VGtYQ=="], @@ -1967,8 +1901,6 @@ "bip32/@types/node": ["@types/node@10.12.18", "", {}, "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ=="], - "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], - "borsh/bn.js": ["bn.js@5.2.2", "", {}, "sha512-v2YAxEmKaBLahNwE1mjp4WON6huMNeuDvagFZW+ASCuA/ku0bXR9hSMw0XpiqMoA3+rmnyck/tPRSFQkoC9Cuw=="], "bun-types/@types/node": ["@types/node@22.7.5", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ=="], @@ -2133,8 +2065,6 @@ "no-case/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], - "node-hid/node-addon-api": ["node-addon-api@3.2.1", "", {}, "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="], - "optimism/tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "ox/@adraffy/ens-normalize": ["@adraffy/ens-normalize@1.11.1", "", {}, "sha512-nhCBV3quEgesuf7c7KYfperqSS14T8bYuvJ8PcLJp6znkZpFc0AuW4qBtr8eKVyPPe/8RSr7sglCWPU5eaxwKQ=="], @@ -2151,8 +2081,6 @@ "protobufjs/@types/node": ["@types/node@22.7.5", "", { "dependencies": { "undici-types": "~6.19.2" } }, "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ=="], - "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], - "rpc-websockets/@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="], "rpc-websockets/eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], @@ -2193,8 +2121,6 @@ "typechain/prettier": ["prettier@2.8.8", "", { "bin": { "prettier": "bin-prettier.js" } }, "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q=="], - "usb/node-addon-api": ["node-addon-api@6.1.0", "", {}, "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA=="], - "viem/@noble/curves": ["@noble/curves@1.9.1", "", { "dependencies": { "@noble/hashes": "1.8.0" } }, "sha512-k11yZxZg+t+gWvBbIswW0yoJlu8cHOC7dhunwOzoWH/mXGBiYyR4YY6hAEK/3EUs4UpB8la1RfdRpeGsFHkWsA=="], "viem/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], @@ -2361,8 +2287,6 @@ "@jest/types/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], - "@ledgerhq/hw-transport-node-hid-noevents/node-hid/node-addon-api": ["node-addon-api@3.2.1", "", {}, "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A=="], - "@solana/codecs-core/@solana/errors/chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], "@solana/codecs-core/@solana/errors/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], diff --git a/evm/ts-scripts/config/mainnet/managers.json b/evm/ts-scripts/config/mainnet/managers.json index 8685e2a28..35f8ebcb4 100644 --- a/evm/ts-scripts/config/mainnet/managers.json +++ b/evm/ts-scripts/config/mainnet/managers.json @@ -1,62 +1,6 @@ [ - { - "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", - "mode": 1, - "chainId": 2, - "rateLimitDuration": 86400, - "skipRateLimit": false, - "threshold": 1, - "outboundLimit": 4000000000000000000000000, - "inboundLimit": [ - { "chainId": 1, "limit": "5500000000000000000000000" }, - { "chainId": 23, "limit": "1500000000000000000000000" }, - { "chainId": 24, "limit": "40000000000000000000000" }, - { "chainId": 30, "limit": "120000000000000000000000" } - ] - }, - { - "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", - "mode": 1, - "chainId": 23, - "rateLimitDuration": 86400, - "skipRateLimit": false, - "threshold": 1, - "outboundLimit": 2300000000000000000000000, - "inboundLimit": [ - { "chainId": 1, "limit": "1500000000000000000000000" }, - { "chainId": 2, "limit": "1000000000000000000000000" }, - { "chainId": 24, "limit": "335000000000000000000000" }, - { "chainId": 30, "limit": "100000000000000000000000" } - ] - }, - { - "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", - "mode": 1, - "chainId": 24, - "rateLimitDuration": 86400, - "skipRateLimit": false, - "threshold": 1, - "outboundLimit": 544000000000000000000000, - "inboundLimit": [ - { "chainId": 1, "limit": "35000000000000000000000" }, - { "chainId": 2, "limit": "200000000000000000000000" }, - { "chainId": 23, "limit": "60000000000000000000000" }, - { "chainId": 30, "limit": "140000000000000000000000" } - ] - }, - { - "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", - "mode": 1, - "chainId": 30, - "rateLimitDuration": 86400, - "skipRateLimit": false, - "threshold": 1, - "outboundLimit": 1700000000000000000000000, - "inboundLimit": [ - { "chainId": 1, "limit": "135000000000000000000000" }, - { "chainId": 2, "limit": "440000000000000000000000" }, - { "chainId": 23, "limit": "140000000000000000000000" }, - { "chainId": 24, "limit": "240000000000000000000000" } - ] - } + { "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", "chainId": 2 }, + { "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", "chainId": 23 }, + { "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", "chainId": 24 }, + { "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", "chainId": 30 } ] diff --git a/evm/ts-scripts/config/testnet/managers.json b/evm/ts-scripts/config/testnet/managers.json index 4bcb224dd..893a5274f 100644 --- a/evm/ts-scripts/config/testnet/managers.json +++ b/evm/ts-scripts/config/testnet/managers.json @@ -1,62 +1,6 @@ [ - { - "token": "0x738141EFf659625F2eAD4feECDfCD94155C67f18", - "mode": 1, - "chainId": 10002, - "rateLimitDuration": 3600, - "skipRateLimit": false, - "threshold": 1, - "outboundLimit": 4000000000000000000000000, - "inboundLimit": [ - { "chainId": 1, "limit": "1000000000000000000000" }, - { "chainId": 10003, "limit": "120000000000000000000000" }, - { "chainId": 10004, "limit": "40000000000000000000000000" }, - { "chainId": 10005, "limit": "1500000000000000000000000" } - ] - }, - { - "token": "0x395D3C74232D12916ecA8952BA352b4d27818035", - "mode": 1, - "chainId": 10003, - "rateLimitDuration": 3600, - "skipRateLimit": false, - "threshold": 1, - "outboundLimit": 100000000000000000000, - "inboundLimit": [ - { "chainId": 1, "limit": "1500000000000000000000000" }, - { "chainId": 10002, "limit": "1000000000000000000000000" }, - { "chainId": 10004, "limit": "335000000000000000000000" }, - { "chainId": 10005, "limit": "100000000000000000000000" } - ] - }, - { - "token": "0x1d30E78B7C7fbbcef87ae6e97B5389b2e470CA4a", - "mode": 1, - "chainId": 10004, - "rateLimitDuration": 3600, - "skipRateLimit": false, - "threshold": 1, - "outboundLimit": 1700000000000000000000000, - "inboundLimit": [ - { "chainId": 1, "limit": "135000000000000000000000" }, - { "chainId": 10002, "limit": "440000000000000000000000" }, - { "chainId": 10003, "limit": "140000000000000000000000" }, - { "chainId": 10005, "limit": "240000000000000000000000" } - ] - }, - { - "token": "0xdDFeABcCf2063CD66f53a1218e23c681Ba6e7962", - "mode": 1, - "chainId": 10005, - "rateLimitDuration": 3600, - "skipRateLimit": false, - "threshold": 1, - "outboundLimit": 544000000000000000000000, - "inboundLimit": [ - { "chainId": 1, "limit": "35000000000000000000000" }, - { "chainId": 10002, "limit": "200000000000000000000000" }, - { "chainId": 10003, "limit": "140000000000000000000000" }, - { "chainId": 10004, "limit": "60000000000000000000000" } - ] - } + { "token": "0x738141EFf659625F2eAD4feECDfCD94155C67f18", "chainId": 10002 }, + { "token": "0x395D3C74232D12916ecA8952BA352b4d27818035", "chainId": 10003 }, + { "token": "0x1d30E78B7C7fbbcef87ae6e97B5389b2e470CA4a", "chainId": 10004 }, + { "token": "0xdDFeABcCf2063CD66f53a1218e23c681Ba6e7962", "chainId": 10005 } ] diff --git a/evm/ts-scripts/src/createGovernanceMessage.ts b/evm/ts-scripts/src/createGovernanceMessage.ts index 60f464e68..5d908fbed 100644 --- a/evm/ts-scripts/src/createGovernanceMessage.ts +++ b/evm/ts-scripts/src/createGovernanceMessage.ts @@ -14,9 +14,6 @@ import { type ManagerConfig = { token: string; chainId: ChainId; - threshold: number; - outboundLimit: string; - inboundLimit: { chainId: ChainId; limit: string }[]; }; function loadGuardianKey(): string { diff --git a/solana/ts-scripts/src/env.ts b/solana/ts-scripts/src/env.ts index 53a8c419a..e8238031a 100644 --- a/solana/ts-scripts/src/env.ts +++ b/solana/ts-scripts/src/env.ts @@ -60,9 +60,7 @@ export function getGovernanceVaa(): GovernanceVaa { } export function loadScriptConfig(filename: string): any { - const configFile = fs.readFileSync( - `./config/${env}/${filename}.json` - ); + const configFile = fs.readFileSync(`./config/${env}/${filename}.json`); const config = JSON.parse(configFile.toString()); if (!config) { throw Error("Failed to pull config file!"); diff --git a/solana/ts-scripts/src/env_short.ts b/solana/ts-scripts/src/env_short.ts index f41a5978e..2e8f62fbb 100644 --- a/solana/ts-scripts/src/env_short.ts +++ b/solana/ts-scripts/src/env_short.ts @@ -39,9 +39,7 @@ export function getGovernanceVaa(): GovernanceVaa { } export function loadScriptConfig(filename: string): any { - const configFile = fs.readFileSync( - `./config/${env}/${filename}.json` - ); + const configFile = fs.readFileSync(`./config/${env}/${filename}.json`); const config = JSON.parse(configFile.toString()); if (!config) { throw Error("Failed to pull config file!"); From 99f67873b6d4ce823d7d6a0779da5d29a2ca320e Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Mon, 30 Mar 2026 21:53:29 +0200 Subject: [PATCH 23/24] fix: remove createGovernanceMessage script and its unused dependencies Remove the EVM createGovernanceMessage script which is not needed for governance VAA execution. Also removes managers.json configs and loadGuardianSetIndex from env.ts as they were only used by this script. --- evm/ts-scripts/config/mainnet/managers.json | 6 - evm/ts-scripts/config/testnet/managers.json | 6 - evm/ts-scripts/src/createGovernanceMessage.ts | 170 ------------------ evm/ts-scripts/src/env.ts | 9 - 4 files changed, 191 deletions(-) delete mode 100644 evm/ts-scripts/config/mainnet/managers.json delete mode 100644 evm/ts-scripts/config/testnet/managers.json delete mode 100644 evm/ts-scripts/src/createGovernanceMessage.ts diff --git a/evm/ts-scripts/config/mainnet/managers.json b/evm/ts-scripts/config/mainnet/managers.json deleted file mode 100644 index 35f8ebcb4..000000000 --- a/evm/ts-scripts/config/mainnet/managers.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", "chainId": 2 }, - { "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", "chainId": 23 }, - { "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", "chainId": 24 }, - { "token": "0xB0fFa8000886e57F86dd5264b9582b2Ad87b2b91", "chainId": 30 } -] diff --git a/evm/ts-scripts/config/testnet/managers.json b/evm/ts-scripts/config/testnet/managers.json deleted file mode 100644 index 893a5274f..000000000 --- a/evm/ts-scripts/config/testnet/managers.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { "token": "0x738141EFf659625F2eAD4feECDfCD94155C67f18", "chainId": 10002 }, - { "token": "0x395D3C74232D12916ecA8952BA352b4d27818035", "chainId": 10003 }, - { "token": "0x1d30E78B7C7fbbcef87ae6e97B5389b2e470CA4a", "chainId": 10004 }, - { "token": "0xdDFeABcCf2063CD66f53a1218e23c681Ba6e7962", "chainId": 10005 } -] diff --git a/evm/ts-scripts/src/createGovernanceMessage.ts b/evm/ts-scripts/src/createGovernanceMessage.ts deleted file mode 100644 index 5d908fbed..000000000 --- a/evm/ts-scripts/src/createGovernanceMessage.ts +++ /dev/null @@ -1,170 +0,0 @@ -import { ethers } from "ethers"; -const elliptic = require("elliptic"); -import { inspect } from "util"; -import { ChainId } from "@certusone/wormhole-sdk"; -import { - loadOperatingChains, - init, - ChainInfo, - loadScriptConfig, - getContractAddress, - loadGuardianSetIndex, -} from "./env"; - -type ManagerConfig = { - token: string; - chainId: ChainId; -}; - -function loadGuardianKey(): string { - const key = process.env.GUARDIAN_KEY; - if (!key) throw new Error("GUARDIAN_KEY environment variable is required"); - return key; -} - -const governanceChainId = 1; -const governanceContract = - "0x0000000000000000000000000000000000000000000000000000000000000004"; - -const governanceModule = - "0x000000000000000047656E6572616C507572706F7365476F7665726E616E6365"; - -const processName = "deployGovernances"; - -init(); -const chains = loadOperatingChains(); - -async function run() { - console.log(`Start ${processName}!`); - const managersConfiguration: ManagerConfig[] = loadScriptConfig("managers"); - - const results = await Promise.all( - chains.map(async (chain) => { - const govAddress = await getContractAddress( - "GeneralPurposeGovernances", - chain.chainId - ); - const managerConfig = managersConfiguration.find( - (config) => config.chainId === chain.chainId - ); - - if (!managerConfig) { - return { - chainId: chain.chainId, - error: "Manager configuration not found for chain", - }; - } - - const vaa = await createAcceptDefaultAdminTransferVaa( - chain, - govAddress, - managerConfig.token - ); - - console.log(`Vaa for chain: ${chain.chainId}: ${vaa}`); - return { chainId: chain.chainId, vaa }; - }) - ); - - for (const result of results) { - if ("error" in result) { - console.error( - `Error deploying for chain ${result.chainId}: ${inspect(result.error)}` - ); - continue; - } - - console.log(`Deployed succeded for chain ${result.chainId}`); - } -} - -run().then(() => console.log("Done!")); - -async function createAcceptDefaultAdminTransferVaa( - chain: ChainInfo, - generalPurposeGovernanceAddress: string, - tokenAddress: string -) { - const abi = ["function acceptDefaultAdminTransfer()"]; - const contract = new ethers.Contract( - "0x0000000000000000000000000000000000000000", - abi - ); - - const call = await contract.populateTransaction.acceptDefaultAdminTransfer(); - const callData = call.data; - - const payload = ethers.utils.solidityPack( - ["bytes32", "uint8", "uin16", "address", "address", "uint16", "bytes"], - [ - governanceModule, - 1, - chain.chainId, - generalPurposeGovernanceAddress, - tokenAddress, - callData, - ] - ); - - return encodeAndSignGovernancePayload(payload); -} - -export function encodeAndSignGovernancePayload(payload: string): string { - const timestamp = Math.floor(+new Date() / 1000); - const nonce = 1; - const sequence = 1; - const consistencyLevel = 1; - - const encodedVAABody = ethers.utils.solidityPack( - ["uint32", "uint32", "uint16", "bytes32", "uint64", "uint8", "bytes"], - [ - timestamp, - nonce, - governanceChainId, - governanceContract, - sequence, - consistencyLevel, - payload, - ] - ); - - const hash = doubleKeccak256(encodedVAABody); - - // sign the hash - const ec = new elliptic.ec("secp256k1"); - const key = ec.keyFromPrivate(loadGuardianKey()); - const signature = key.sign(hash.substring(2), { canonical: true }); - - // pack the signatures - const packSig = [ - ethers.utils.solidityPack(["uint8"], [0]).substring(2), - zeroPadBytes(signature.r.toString(16), 32), - zeroPadBytes(signature.s.toString(16), 32), - ethers.utils - .solidityPack(["uint8"], [signature.recoveryParam]) - .substring(2), - ]; - - const vm = [ - ethers.utils.solidityPack(["uint8"], [1]).substring(2), - ethers.utils - .solidityPack(["uint32"], [loadGuardianSetIndex()]) - .substring(2), // guardianSetIndex - ethers.utils.solidityPack(["uint8"], [1]).substring(2), // number of signers - [packSig], - encodedVAABody.substring(2), - ].join(""); - - return "0x" + vm; -} - -export function zeroPadBytes(value: string, length: number): string { - while (value.length < 2 * length) { - value = "0" + value; - } - return value; -} - -function doubleKeccak256(body: ethers.BytesLike) { - return ethers.utils.keccak256(ethers.utils.keccak256(body)); -} diff --git a/evm/ts-scripts/src/env.ts b/evm/ts-scripts/src/env.ts index f5774340d..21a873046 100644 --- a/evm/ts-scripts/src/env.ts +++ b/evm/ts-scripts/src/env.ts @@ -127,15 +127,6 @@ export function loadPrivateKey(): string { return privateKey; } -export function loadGuardianSetIndex(): number { - const chainFile = fs.readFileSync(`./ts-scripts/config/${env}/chains.json`); - const chains = JSON.parse(chainFile.toString()); - if (chains.guardianSetIndex == undefined) { - throw Error("Failed to pull guardian set index from the chains file!"); - } - return chains.guardianSetIndex; -} - export function writeOutputFiles(output: any, processName: string) { fs.mkdirSync(`./ts-scripts/output/${env}/${processName}`, { recursive: true, From 702cb47485e4a5e1b781fc046f1ae00e29b134ff Mon Sep 17 00:00:00 2001 From: evgeniko <97796468+evgeniko@users.noreply.github.com> Date: Mon, 30 Mar 2026 22:48:13 +0200 Subject: [PATCH 24/24] fix: bump ethers to ^5.8.0 and pin elliptic to 6.6.1 for security fixes --- evm/ts-scripts/package.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/evm/ts-scripts/package.json b/evm/ts-scripts/package.json index ab7a6d398..172ec2232 100644 --- a/evm/ts-scripts/package.json +++ b/evm/ts-scripts/package.json @@ -9,13 +9,16 @@ }, "author": "", "license": "ISC", + "overrides": { + "elliptic": "6.6.1" + }, "devDependencies": { "@certusone/wormhole-sdk": "^0.10.11", "@typechain/ethers-v5": "^11.1.2", "@types/node": "^20.11.22", "@xlabs-xyz/ledger-signer": "^0.0.3", "dotenv": "^16.4.5", - "ethers": "^5.7.2", + "ethers": "^5.8.0", "typechain": "^8.3.2", "tsx": "^4.7.2", "typescript": "^5.4.5"