Skip to content

feat: migrate governance scripts from deployment-scripts branch#853

Open
evgeniko wants to merge 24 commits into
mainfrom
feat/governance-scripts
Open

feat: migrate governance scripts from deployment-scripts branch#853
evgeniko wants to merge 24 commits into
mainfrom
feat/governance-scripts

Conversation

@evgeniko

@evgeniko evgeniko commented Mar 27, 2026

Copy link
Copy Markdown
Contributor

Summary

Migrates governance scripts from the deployment-scripts branch to main — VAA execution and governance message creation for both Solana and EVM.

Excludes deployment/config scripts (NTT CLI handles those), quoter, and special relaying (deprecated). Nearly all files are as-is cherry-picks from deployment-scripts with minimal changes.

What's included

Solana

Script Purpose
governance.ts NTTGovernance class — VAA parsing, PDA derivation, instruction creation
executeGovernanceVaa.ts Execute single governance VAA (Ledger, hex)
executeGovernanceVaas.ts Execute batch governance VAAs (Ledger, base64)
executeGovernanceVaa_short.ts Execute single governance VAA (keypair, base64)
env.ts / env_short.ts / helpers.ts Infrastructure: config loading, Ledger + keypair signing

EVM

Script Purpose
executeGovernanceVaa.ts Execute governance VAAs on EVM chains
createGovernanceMessage.ts Construct and sign governance VAAs
env.ts Infrastructure: config loading, signer creation

What's excluded (and why)

  • Deployment scripts (deployManagers, deployTransceivers, initializeNtt, etc.) — NTT CLI handles deployment
  • Configuration scripts (configureManagers, updatePeerAddresses, registerPeers, limits) — NTT CLI handles configuration
  • Quoter scripts (configureQuoter, registerManagers, quoteSpecialRelays) — quoter is deprecated
  • Special relaying scripts — deprecated
  • EVM transferManagerOwnership.ts — not needed
  • EVM upgradeTransceivers.ts — not needed
  • EVM readTransceiverConfig.ts — not needed
  • Solana transferOwnershipToGov.ts / claimOwnership.ts / readManagerConfiguration.ts — depend on old NTT SDK class which was rewritten to SolanaNtt on main; need adaptation in a follow-up

Files with code changes

All other files are straight cherry-picks from deployment-scripts.

  1. solana/ts/scripts/governance.ts — moved from solana/ts/sdk/ to solana/ts/scripts/ (SDK structure differs on main), inlined derivePda function, fixed IDL import paths
  2. evm/ts-scripts/src/createGovernanceMessage.ts — replaced cross-repo guardianKey import with local env var read; inlined ManagerConfig type from non-migrated configureManagers.ts
  3. solana/ts/scripts/env.ts — removed quoter types/functions, made guardianKey lazy
  4. evm/ts-scripts/src/env.ts — trimmed ContractsJson type to only GeneralPurposeGovernances

Other minor changes

  • solana/package.json — added @xlabs-xyz/ledger-signer-solana and dotenv dev dependencies
  • evm/ts-scripts/.env.sample — added GUARDIAN_KEY=
  • evm/ts-scripts/.gitignore — added .env to prevent secret leakage
  • evm/ts-scripts/package.json — added "private": true
  • evm/ts-scripts/config/ — contracts.json contains only GeneralPurposeGovernances addresses
  • Fixed filename typo: executeGonvernanceVaas.tsexecuteGovernanceVaas.ts
  • Fixed typos in error message and log output

@coderabbitai

coderabbitai Bot commented Mar 27, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds end-to-end deployment documentation and new EVM + Solana TypeScript tooling, scripts, and configs to deploy, configure, sign, post, and verify governance VAAs and NTT components across EVM and Solana networks.

Changes

Cohort / File(s) Summary
Deployment Guide
deployments.md
New end-to-end, command-oriented deployment workflow for NTT across Solana and EVM, including prerequisites, EVM/Solana steps, cross-registration, and EVM contract verification instructions.
EVM workspace & configs
evm/.gitignore, evm/ts-scripts/.gitignore, evm/ts-scripts/.env.sample, evm/ts-scripts/package.json, evm/ts-scripts/config/.../contracts.json, evm/ts-scripts/config/testnet/governance-vaas.json
Adds ts-scripts workspace, gitignore and env templates, TypeChain build script, and new contract/vaa mappings (including NttTransceiverProxies) for mainnet/testnet.
EVM scripts & runtime
evm/ts-scripts/src/env.ts, evm/ts-scripts/src/createGovernanceMessage.ts, evm/ts-scripts/src/executeGovernanceVaa.ts, evm/ts-scripts/src/readTransceiverConfig.ts
New centralized env loader, signer/provider helpers, output management, scripts to create signed governance VAAs, execute governance on EVM, and read transceiver configs across chains.
EVM verification tooling
evm/ts-scripts/shell/verify-governance-contract.sh
Shell script to verify governance contracts via forge/etherscan across configured chains with per-chain skip logic.
Solana deps & env templates
solana/package.json, solana/ts/scripts/.env.sample
Adds dev dependencies (ledger signer, dotenv) and Solana ts script env sample with RPC/commitment/ledger/guardian placeholders.
Solana runtime & config loaders
solana/ts/scripts/env.ts, solana/ts/scripts/env_short.ts, solana/ts/scripts/config/*/programs.json, solana/ts/scripts/config/*/ntt.json, solana/ts/scripts/config/*/governance-vaa.json, solana/ts/scripts/config/mainnet/governance-vaas-june-05.json
New env modules (full and short), network configs for NTT parameters and governance VAAs for devnet/mainnet.
Solana governance core
solana/ts/scripts/governance.ts, solana/ts/scripts/helpers.ts
New governance module to parse VAA, derive PDAs, deserialize embedded instructions and compose governance TransactionInstruction; ledger signing and ledger-backed transaction submission helpers.
Solana executable scripts
solana/ts/scripts/claimOwnership.ts, solana/ts/scripts/transferOwnershipToGov.ts, solana/ts/scripts/readManagerConfiguration.ts, solana/ts/scripts/executeGovernanceVaa.ts, solana/ts/scripts/executeGovernanceVaa_short.ts, solana/ts/scripts/executeGovernanceVaas.ts
Multiple runnable scripts for claiming/transferring ownership, reading manager configuration, and posting/executing single or batch governance VAAs (ledger or keypair signing variants).

Sequence Diagram(s)

sequenceDiagram
    participant Dev as Developer
    participant EvmEnv as evm/ts-scripts/env.ts
    participant CreateMsg as createGovernanceMessage.ts
    participant Guardian as Guardian (secP256k1 key)
    participant EvmChain as EVM Node / Contract Registry

    Dev->>CreateMsg: run script
    CreateMsg->>EvmEnv: init(), loadOperatingChains(), loadContracts()
    loop per chain
        CreateMsg->>EvmEnv: getContractAddress(chainId)
        CreateMsg->>CreateMsg: build governance payload (solidityPack)
        CreateMsg->>Guardian: sign payload (ECDSA)
        CreateMsg->>CreateMsg: assemble VAA (hashes + sig)
        CreateMsg->>EvmChain: output/store VAA for chain
    end
    Dev->>EvmChain: later run executeGovernanceVaa.ts to call performGovernance(vaa)
Loading
sequenceDiagram
    participant Dev as Developer
    participant SolEnv as solana/ts/scripts/env.ts
    participant Ledger as Ledger Signer
    participant Wormhole as Wormhole Program
    participant NTTGov as Governance Program
    participant Solana as Solana RPC

    Dev->>SolEnv: run executeGovernanceVaa.ts
    SolEnv->>SolEnv: loadGovernanceVaa(), getProgramAddresses()
    SolEnv->>Ledger: getSigner()
    SolEnv->>Wormhole: postVaaSolana(vaa, signCallback)
    Ledger->>Wormhole: sign transaction (addLedgerSignature)
    Wormhole->>SolEnv: confirmation of post
    SolEnv->>Solana: parseVaa()
    SolEnv->>NTTGov: createGovernanceVaaInstruction(parsedVaa)
    SolEnv->>Ledger: ledgerSignAndSend([instruction])
    Ledger->>Solana: send signed tx
    Solana->>Dev: return confirmed tx signature
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • nvsriram
  • dvgui

Poem

🐇 I hop through configs, scripts in my paw,

signing VAAs with a guardian awe,
Solana and EVM, I stitch with delight,
deployments now ready, morning to night,
a rabbit’s small jig for cross-chain flight.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 4.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title directly describes the main change: migrating governance scripts from a branch into the main codebase, which is the primary objective confirmed by the PR summary.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/governance-scripts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
evm/ts-scripts/config/mainnet/chains.json (1)

52-57: ⚠️ Potential issue | 🔴 Critical

Do not commit localhost RPC URL in mainnet configuration.

The MegaETH RPC is set to http://localhost:50444 in a mainnet config file. This will cause script failures for anyone running governance scripts against MegaETH mainnet, as getProvider() (see context snippet 1) uses this URL directly. This appears to be a local development artifact.

🐛 Proposed fix to restore mainnet RPC
     {
       "description": "MegaETH",
       "evmNetworkId": 4326,
       "chainId": 64,
-      "rpc": "http://localhost:50444"
+      "rpc": "https://mainnet.megaeth.com/rpc"
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@evm/ts-scripts/config/mainnet/chains.json` around lines 52 - 57, The mainnet
config for "MegaETH" currently contains a localhost RPC URL which must be
removed; update the "rpc" value for the MegaETH entry in chains.json (the object
with "description": "MegaETH") to a production/public RPC endpoint or to an
empty/placeholder value so getProvider() does not use a localhost URL for
mainnet; ensure getProvider() (the provider resolution path) will instead read
the correct env/config value (e.g., an env var like PROVIDER_URL) or a valid
public RPC string rather than the local dev URL.
evm/ts-scripts/config/mainnet/contracts.json (1)

1-192: ⚠️ Potential issue | 🟡 Minor

Missing TrimmedAmountLibs key from JSON schema.

The ContractsJson type in evm/ts-scripts/src/env.ts (lines 17-32) defines TrimmedAmountLibs as a required property, but this key is missing from the JSON file. While current scripts may not query this key, adding it would ensure type consistency and prevent potential runtime errors if accessed.

💡 Proposed fix - add empty array before closing brace
     {
       "address": "0x574B7864119C9223A9870Ea614dC91A8EE09E512",
       "chainId": 64
     }
-  ]
+  ],
+  "TrimmedAmountLibs": []
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@evm/ts-scripts/config/mainnet/contracts.json` around lines 1 - 192, The JSON
file is missing the required TrimmedAmountLibs key declared on the ContractsJson
type in evm/ts-scripts/src/env.ts; add a "TrimmedAmountLibs": [] entry (an empty
array) near the other top-level arrays (e.g., alongside WormholeCoreContracts,
WormholeRelayers, GeneralPurposeGovernances) before the final closing brace so
the JSON conforms to the ContractsJson schema and satisfies any runtime/type
expectations.
🧹 Nitpick comments (15)
solana/ts/scripts/executeGovernanceVaa_short.ts (1)

28-34: Hardcoded signer file path should use environment variable.

The path "YOUR_SIGNER_FILE" is a placeholder. For operational use, this should read from an environment variable to avoid hardcoding sensitive paths.

♻️ Proposed fix
+const signerFilePath = process.env.SIGNER_FILE_PATH;
+if (!signerFilePath) {
+  throw new Error("SIGNER_FILE_PATH environment variable is required");
+}
+
 const payerSecretKey = Uint8Array.from(
   JSON.parse(
-    fs.readFileSync(`YOUR_SIGNER_FILE`, {
+    fs.readFileSync(signerFilePath, {
       encoding: "utf-8",
     })
   )
 );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/scripts/executeGovernanceVaa_short.ts` around lines 28 - 34, The
code currently reads a hardcoded signer path ("YOUR_SIGNER_FILE") when building
payerSecretKey; change it to read the path from an environment variable (e.g.,
process.env.SIGNER_FILE or process.env.PAYER_SIGNER) and fail fast if the
variable is missing. Update the fs.readFileSync call used to construct
payerSecretKey (and any related constant) to read the env var instead of the
literal string, and add a clear error/throw if the env var is not set so the
script does not attempt to parse a missing file.
evm/ts-scripts/shell/verify-governance-contract.sh (1)

55-59: Quote the command substitution to prevent word splitting.

The cast abi-encode output should be quoted to prevent potential word splitting issues if the output contains spaces or special characters.

🔧 Proposed fix
   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")
+    --constructor-args "$(cast abi-encode "constructor(address)" "$core_wormhole_address")"
 done
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@evm/ts-scripts/shell/verify-governance-contract.sh` around lines 55 - 59, The
shell command is vulnerable to word-splitting because the command substitution
for constructor args is unquoted; update the forge verify-contract invocation so
the output of cast abi-encode is quoted (i.e., wrap $(cast abi-encode
"constructor(address)" "$core_wormhole_address") in quotes) when passed to
--constructor-args to ensure the encoded constructor argument is treated as a
single token (references: the --constructor-args argument and the cast
abi-encode invocation using core_wormhole_address).
evm/ts-scripts/src/executeGovernanceVaa.ts (1)

55-76: Unused log helper function.

The log function is defined on line 58 but never used. The subsequent logging statements use console.log directly instead.

♻️ Either use the log helper or remove it

Option 1: Use the log helper for consistent prefixed logging:

   const log = (...args: any[]) => console.log(`[${chain.chainId}]`, ...args);
   const { vaa } = await getChainConfig<GovernanceConfig>("governance-vaas", chain.chainId);
   
   const governanceContract = await getGovernanceContract(chain);
 
   const vaaHex = Buffer.from(vaa, "base64").toString("hex");
-  log("Executing governance with VAA: ", vaaHex);
+  log("Executing governance with VAA:", vaaHex);
 
   const tx = await governanceContract.performGovernance(
     `0x${vaaHex}`,
     {} // overrides
   );
 
-  log(`Submitted governance transaction: ${tx.hash}`);
+  log("Submitted governance transaction:", tx.hash);
 
   await tx.wait();
 
-  log("success.");
+  log("Success.");

Option 2: Remove the unused helper:

 async function executeGovernance(
   chain: ChainInfo,
 ) {
-  const log = (...args: any[]) => console.log(`[${chain.chainId}]`, ...args);
   const { vaa } = await getChainConfig<GovernanceConfig>("governance-vaas", chain.chainId);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@evm/ts-scripts/src/executeGovernanceVaa.ts` around lines 55 - 76, The file
defines a local helper log(...) inside executeGovernance but all logging uses
console.log directly; either remove the unused log function or replace the
console.log calls with the helper for consistent prefixed output: locate the
executeGovernance function and either delete the const log = (...) declaration,
or change the three console.log/... uses (the "Executing governance with VAA:",
"Submitted governance transaction:", and "success.") to call log(...) so logs
include the `[${chain.chainId}]` prefix before interacting with
governanceContract.performGovernance and awaiting tx.wait().
solana/ts/scripts/executeGovernanceVaas.ts (1)

69-75: Consider transaction size limits when batching multiple VAAs.

All governance instructions are sent in a single transaction. Solana transactions have size limits (~1232 bytes) and account limits (64 unique accounts). If the VAA file contains many entries, this could cause transaction failures. Consider batching instructions into multiple transactions if needed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/scripts/executeGovernanceVaas.ts` around lines 69 - 75, The current
code sends all governance instructions in one transaction which can exceed
Solana transaction size and account limits; update the logic that calls
ledgerSignAndSend with the full instructions array to instead chunk the
instructions into safe-sized batches (e.g., respect ~1200 byte transaction
payload and <=64 unique accounts per tx) and send each batch sequentially:
create a helper to split the instructions array by estimated serialized size and
distinct account count, call ledgerSignAndSend for each batch, await
connection.confirmTransaction for each returned tx.signature before proceeding
to the next batch, and log each batch's signature (references: the instructions
variable, ledgerSignAndSend, connection.confirmTransaction, tx.signature).
deployments.md (3)

14-14: Minor: Use hyphen in compound adjective.

Per static analysis: "High level" should be "High-level" when used as an adjective.

-### High level description of the process:
+### High-level description of the process:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deployments.md` at line 14, Change the heading text "High level description
of the process:" to use the compound adjective form "High-level description of
the process:"; locate the heading string "High level description of the process"
and replace it with "High-level description of the process" so the static
analysis warning is resolved.

143-143: Typo: "ETTHERSCAN" should be "ETHERSCAN".

-    "etherscan": "YOUR-ETTHERSCAN-API_KEY"
+    "etherscan": "YOUR-ETHERSCAN-API-KEY"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deployments.md` at line 143, Fix the typo in the example API key value for
the "etherscan" entry: replace the placeholder "YOUR-ETTHERSCAN-API_KEY" with
the correctly spelled "YOUR-ETHERSCAN-API_KEY" so the key name "etherscan" maps
to the proper ETHERSCAN placeholder.

44-48: Add language specifiers to fenced code blocks.

Several code blocks are missing language identifiers, which affects syntax highlighting and linting. For example, lines 44, 95, 99, 104, and 148 should specify shell or bash.

♻️ Example fix for line 44
-```
+```shell
 export FOUNDRY_PROFILE=prod

Also applies to: 95-96, 99-100, 104-105, 148-149

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@deployments.md` around lines 44 - 48, The fenced code blocks containing
environment variable exports (e.g., lines with FOUNDRY_PROFILE, WALLET_KEY,
LEDGER_BIP32_PATH, ENV) are missing language specifiers; update each affected
fenced block to start with a language tag like ```shell or ```bash for proper
syntax highlighting and linting (apply this to the blocks around the exports at
the mentioned locations such as the snippet containing "export
FOUNDRY_PROFILE=prod" and the other blocks at lines 95-96, 99-100, 104-105,
148-149).
evm/ts-scripts/src/createGovernanceMessage.ts (1)

1-4: Consider using ES import for elliptic for consistency.

Line 2 uses CommonJS require while the rest of the file uses ES imports. This is a minor inconsistency.

♻️ Suggested change
 import { ethers } from "ethers";
-const elliptic = require("elliptic");
+import elliptic from "elliptic";
 import { inspect } from "util";

You may need to add @types/elliptic as a dev dependency for proper typing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@evm/ts-scripts/src/createGovernanceMessage.ts` around lines 1 - 4, The file
uses CommonJS require for elliptic; replace "const elliptic =
require('elliptic')" with an ES import (e.g., import elliptic from 'elliptic')
to match the other imports in createGovernanceMessage.ts and update any usages
(like new elliptic.ec(...)) accordingly; also add `@types/elliptic` as a
devDependency if TypeScript type errors appear so functions/types from the
elliptic package are properly typed.
solana/ts/scripts/env_short.ts (1)

42-51: The !config check doesn't catch JSON parse errors.

JSON.parse throws on invalid JSON, so the if (!config) check only catches the edge case where the file literally contains null. Consider wrapping in try-catch for clearer error messages.

♻️ Suggested improvement
 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!");
+  const path = `./ts/scripts/config/${env}/${filename}.json`;
+  try {
+    const configFile = fs.readFileSync(path);
+    const config = JSON.parse(configFile.toString());
+    if (!config) {
+      throw new Error("Config file parsed to null/undefined");
+    }
+    return config;
+  } catch (err) {
+    throw new Error(`Failed to load config from ${path}: ${err}`);
   }
-  return config;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/scripts/env_short.ts` around lines 42 - 51, The loadScriptConfig
function should catch JSON parsing and file read errors instead of relying on
the unreachable if (!config) check: wrap the fs.readFileSync and JSON.parse
calls for loadScriptConfig in a try-catch, include the filename and env values
in the thrown/logged error message, and rethrow or throw a new Error with the
original error attached so callers get a clear message when reading or parsing
`./ts/scripts/config/${env}/${filename}.json` fails.
solana/ts/sdk/governance.ts (1)

73-74: Extracted governanceProgramId is unused.

verifyGovernanceHeader returns [governanceProgramId, ixData], but governanceProgramId is never used. Consider either validating it against this.program.programId or removing the extraction.

♻️ Optional validation
     const [governanceProgramId, ixData] = verifyGovernanceHeader(args.vaa.payload);
+    if (!governanceProgramId.equals(this.program.programId)) {
+      throw new Error("VAA governance program ID does not match configured program");
+    }
     const ix = deserializeInstruction(ixData);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/sdk/governance.ts` around lines 73 - 74, verifyGovernanceHeader
returns [governanceProgramId, ixData] but governanceProgramId is never used;
either remove the unused destructuring or validate it—update the code around the
verifyGovernanceHeader call to compare the returned governanceProgramId with
this.program.programId and throw/log an error if they differ, otherwise drop the
governanceProgramId variable and only use ixData for
deserializeInstruction(ixData); reference verifyGovernanceHeader,
governanceProgramId, this.program.programId, ixData, and deserializeInstruction
when making the change.
evm/ts-scripts/src/env.ts (2)

75-85: Functions marked async without async operations.

getChainConfig and getAllContracts are marked async but only call synchronous functions. While harmless, removing unnecessary async/await improves clarity.

♻️ Example for getAllContracts
-export async function getAllContracts(contractName: ContractTypes) {
-  return (await loadContracts())[contractName];
+export function getAllContracts(contractName: ContractTypes) {
+  return loadContracts()[contractName];
 }

Similar changes would be needed for callers expecting promises.

Also applies to: 220-222

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@evm/ts-scripts/src/env.ts` around lines 75 - 85, The functions getChainConfig
and getAllContracts are declared async but perform only synchronous work (they
call loadScriptConfig synchronously), so remove the unnecessary async to improve
clarity: change their signatures to non-async (drop the async keyword) and
return the value directly (no awaiting), and then update any callers that
currently rely on them returning Promises to handle the now-synchronous return
(or wrap them in Promise.resolve(...) at call sites if needed). Ensure you
update function declarations for getChainConfig and getAllContracts and run
TypeScript checks to fix any callsites expecting a Promise.

199-217: Unnecessary recursive call in loadContracts.

Line 216 calls return loadContracts() recursively after setting contracts. Since contracts is already assigned, this can simply return contracts.

♻️ Suggested fix
   // NOTE: We assume that the contracts.json file is correctly formed...
   contracts = JSON.parse(contractsFile.toString()) as ContractsJson;

-  return loadContracts();
+  return contracts;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@evm/ts-scripts/src/env.ts` around lines 199 - 217, The function loadContracts
currently assigns the parsed JSON to the module-level variable contracts and
then calls return loadContracts() recursively; change this so that after parsing
and assigning contracts (ContractsJson) the function returns contracts directly
instead of recursing. Update the return at the end of loadContracts to return
contracts to avoid the unnecessary infinite/extra call and ensure the function
short-circuits correctly when contracts is already set.
solana/ts/scripts/executeGovernanceVaa.ts (2)

22-24: Type assertion as any bypasses type safety.

The governanceProgramId as any cast suppresses type checking. Consider validating that governanceProgramId matches one of the expected GovProgramId values at runtime, or adjusting the type to accept arbitrary program IDs.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/scripts/executeGovernanceVaa.ts` around lines 22 - 24, The code
currently uses a type-assertion ("governanceProgramId as any") when constructing
NTTGovernance which bypasses type safety; replace this cast by validating or
converting governanceProgramId before passing it to the NTTGovernance
constructor: add a runtime check that governanceProgramId matches an allowed
GovProgramId (or map/parse it to the expected enum/type), throw or return a
clear error if invalid, or update the variable's type so it is already the
expected GovProgramId; reference the symbols governanceProgramId and
NTTGovernance (and the GovProgramId enum/type) to locate and fix the code path
that supplies the programId argument.

55-55: Log message is misleading for a generic governance VAA execution script.

The message "claiming ownership of NTT Program" is specific to one type of governance action, but this script executes any governance VAA. Consider a more generic message.

♻️ Suggested change
-  console.log(`Account ${signerPk.toBase58()} is claiming ownership of NTT Program ${nttProgramId}.`);
+  console.log(`Account ${signerPk.toBase58()} is executing governance VAA on program ${governanceProgramId}.`);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/scripts/executeGovernanceVaa.ts` at line 55, The log message is
overly specific; replace the console.log that references signerPk and
nttProgramId so it reports a generic governance VAA execution (e.g., "Signer
<signerPk> executing governance VAA for program <nttProgramId>"). Use signerPk
and nttProgramId to build the message and, if available, append VAA
identifiers/summary from variables like vaa or governanceVaa to make the log
generic and informative.
solana/ts/scripts/env.ts (1)

5-11: Module-level environment checks may block scripts unnecessarily.

The LEDGER_DERIVATION_PATH and ENV checks run immediately on import. For scripts that might only need to read configs (like readManagerConfiguration.ts), this forces setting Ledger environment variables even when not using hardware signing.

Consider lazy validation or moving checks into getSigner() for the Ledger path.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/scripts/env.ts` around lines 5 - 11, The module-level throws for
LEDGER_DERIVATION_PATH and ENV cause imports to fail; move these checks into
lazy validation (e.g., inside the getSigner() function) or guard them so only
code paths that require a Ledger or specific ENV fail; specifically remove the
top-level throws for LEDGER_DERIVATION_PATH and ENV and add checks inside
getSigner() to validate LEDGER_DERIVATION_PATH when hardware signing is
requested, and validate ENV at the point of configuration loading (e.g., within
readManagerConfiguration or its caller), ensuring error messages reference
LEDGER_DERIVATION_PATH, ENV, and getSigner so failures remain discoverable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@evm/ts-scripts/.env.sample`:
- Around line 1-3: Reorder the environment variables in .env.sample to satisfy
dotenv-linter and add a trailing newline: sort the keys alphabetically (replace
the current order of WALLET_KEY, LEDGER_BIP32_PATH, GUARDIAN_KEY with
GUARDIAN_KEY, LEDGER_BIP32_PATH, WALLET_KEY) and ensure the file ends with a
single newline character; no other content changes required.

In `@evm/ts-scripts/.gitignore`:
- Around line 1-4: Add a rule to the .gitignore in the evm/ts-scripts directory
to ignore the local .env file to prevent accidental commits of secrets; update
the existing entries (output, contract-bindings, .env.testnet, .env.mainnet) by
appending a new line with ".env" so that files like WALLET_KEY and GUARDIAN_KEY
stored in .env are not tracked.

In `@evm/ts-scripts/config/mainnet/contracts.json`:
- Around line 162-163: The JSON in the GeneralPurposeGovernances array is
invalid because there's a missing comma between adjacent objects (the snippet
shows "}" followed immediately by "{"); edit the GeneralPurposeGovernances array
to insert the missing comma between those two object entries so the array
contains properly comma-separated objects (look for the objects in the
GeneralPurposeGovernances array and add a comma after the preceding "}" to fix
the parse error).

In `@evm/ts-scripts/config/testnet/governance-vaas.json`:
- Around line 1-18: The governance-vaas.json is missing an entry for XRPL EVM
Testnet (chainId 57) which causes executeGovernanceVaa.ts (which calls
getChainConfig("governance-vaas", chain.chainId)) to fail; add a JSON object
with "chainId": 57 and the appropriate base64 VAA string to governance-vaas.json
matching the format used for other entries, ensuring the chainId matches the
XRPL EVM Testnet entry in chains.json and the GeneralPurposeGovernance contract
referenced in contracts.json so executeGovernanceVaa.ts can find the VAA for
chainId 57.

In `@evm/ts-scripts/package.json`:
- Around line 2-4: Add the "private": true flag to this package.json to prevent
accidental npm publication; update the root object (near the existing "name":
"ts-scripts" and "version": "1.0.0" entries) to include the "private": true
property so npm will refuse to publish this internal scripts package.

In `@evm/ts-scripts/shell/verify-governance-contract.sh`:
- Around line 47-53: The conditional currently checks governance_address twice;
update the second duplicated condition to check core_wormhole_address instead so
the if statement becomes: if [ "$governance_address" = "" ] || [
"$core_wormhole_address" = "" ] || [ "$etherscan_api_key" = "null" ] || [
"$evm_chain_id" = "null" ]; then ...; this ensures missing core_wormhole_address
causes the script to skip verification and prevents passing invalid arguments to
forge verify-contract.
- Around line 1-4: Add a shebang as the first line of
verify-governance-contract.sh to explicitly select the shell interpreter (e.g.
#!/usr/bin/env bash) so the ENV check (test -z "$ENV") and subsequent behavior
run under a consistent shell; place the shebang at the top of the file and
ensure the script file is executable (chmod +x) so it always runs with the
chosen interpreter.

In `@evm/ts-scripts/src/createGovernanceMessage.ts`:
- Around line 100-110: The solidityPack call in createGovernanceMessage.ts is
malformed: fix the types array to use "uint16" (not "uin16") and insert a uint16
callDataLength field before callData so the values array has seven entries;
compute callDataLength using ethers.utils.hexDataLength(callData) (or
equivalent) and pass it as the uint16 field alongside governanceModule, action
(1), chain.chainId, generalPurposeGovernanceAddress, tokenAddress,
callDataLength, and callData so the payload matches
parseGeneralPurposeGovernanceMessage's expected MODULE, action, chain,
governanceContract, governedContract, callDataLength, callData sequence.
- Around line 151-159: The VAA signature assembly is nesting packSig inside
another array which causes comma-separated toString() output; in the vm
construction in createGovernanceMessage.ts replace the nested [packSig] entry
with packSig (i.e., spread or directly include the packSig string/array elements
so they concatenate as hex without commas) so the final vm string correctly
concatenates all hex segments (reference variable names: vm and packSig and
function createGovernanceMessage).

In `@evm/ts-scripts/src/readTransceiverConfig.ts`:
- Line 50: Fix the typo in the console output string: change "Configuration
succeded for chain ${result.chainId}" to use "succeeded" instead; update the
console.log call in readTransceiverConfig.ts where the message is constructed
(the one referencing result.chainId) so it prints "Configuration succeeded for
chain ${result.chainId}".

In `@solana/ts/scripts/env_short.ts`:
- Around line 14-20: The current fallback RPC URL ("INSERT_RPC_URL") for rpcUrl
risks creating a Connection to an invalid endpoint and causing confusing runtime
errors; update the initialization of rpcUrl/connection (variables rpcUrl,
connectionCommitmentLevel, and connection) to validate
process.env.SOLANA_RPC_URL at startup and if missing or still the placeholder,
throw or surface a clear error (or log and exit) describing that SOLANA_RPC_URL
is required, so the Connection is never constructed with an invalid URL.

In `@solana/ts/scripts/env.ts`:
- Line 82: The module currently evaluates guardianKey at import time (export
const guardianKey = getEnv("GUARDIAN_KEY")) which forces all importers to
require GUARDIAN_KEY; change this to a lazy accessor by removing the
module-level evaluation and adding a function getGuardianKey() that calls
getEnv("GUARDIAN_KEY") when invoked, export getGuardianKey instead of
guardianKey, and update all consumers to call getGuardianKey() (replace
references to guardianKey) so only scripts that need the key require the env
var.

In `@solana/ts/scripts/executeGovernanceVaas.ts`:
- Around line 17-20: The thrown error incorrectly names the env var; update the
error message in the governanceVaasFileName check to reference the correct
environment variable GOVENANCE_VAAS_FILE_PATH
(process.env.GOVERNANCE_VAAS_FILE_PATH) so the message matches the actual check
performed by the governanceVaasFileName variable; locate the check around the
governanceVaasFileName declaration and change the Error text accordingly.

In `@solana/ts/scripts/readManagerConfiguration.ts`:
- Line 18: The console.log in readManagerConfiguration.ts contains a typo:
change the message string in the console.log call that currently reads "pared
account info" to "parsed account info" so logs are clear; locate the console.log
statement referencing accountInfo.value.data and update its message text
accordingly.

In `@solana/ts/sdk/governance.ts`:
- Around line 40-44: GOV_PROGRAM_IDS contains a duplicate entry; remove the
repeated "NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm" so the array only
includes the NTT Manager ID and the single governance program ID; update the
export const GOV_PROGRAM_IDS declaration to have exactly two distinct entries
and run tests/lints to ensure no trailing commas or type changes.

---

Outside diff comments:
In `@evm/ts-scripts/config/mainnet/chains.json`:
- Around line 52-57: The mainnet config for "MegaETH" currently contains a
localhost RPC URL which must be removed; update the "rpc" value for the MegaETH
entry in chains.json (the object with "description": "MegaETH") to a
production/public RPC endpoint or to an empty/placeholder value so getProvider()
does not use a localhost URL for mainnet; ensure getProvider() (the provider
resolution path) will instead read the correct env/config value (e.g., an env
var like PROVIDER_URL) or a valid public RPC string rather than the local dev
URL.

In `@evm/ts-scripts/config/mainnet/contracts.json`:
- Around line 1-192: The JSON file is missing the required TrimmedAmountLibs key
declared on the ContractsJson type in evm/ts-scripts/src/env.ts; add a
"TrimmedAmountLibs": [] entry (an empty array) near the other top-level arrays
(e.g., alongside WormholeCoreContracts, WormholeRelayers,
GeneralPurposeGovernances) before the final closing brace so the JSON conforms
to the ContractsJson schema and satisfies any runtime/type expectations.

---

Nitpick comments:
In `@deployments.md`:
- Line 14: Change the heading text "High level description of the process:" to
use the compound adjective form "High-level description of the process:"; locate
the heading string "High level description of the process" and replace it with
"High-level description of the process" so the static analysis warning is
resolved.
- Line 143: Fix the typo in the example API key value for the "etherscan" entry:
replace the placeholder "YOUR-ETTHERSCAN-API_KEY" with the correctly spelled
"YOUR-ETHERSCAN-API_KEY" so the key name "etherscan" maps to the proper
ETHERSCAN placeholder.
- Around line 44-48: The fenced code blocks containing environment variable
exports (e.g., lines with FOUNDRY_PROFILE, WALLET_KEY, LEDGER_BIP32_PATH, ENV)
are missing language specifiers; update each affected fenced block to start with
a language tag like ```shell or ```bash for proper syntax highlighting and
linting (apply this to the blocks around the exports at the mentioned locations
such as the snippet containing "export FOUNDRY_PROFILE=prod" and the other
blocks at lines 95-96, 99-100, 104-105, 148-149).

In `@evm/ts-scripts/shell/verify-governance-contract.sh`:
- Around line 55-59: The shell command is vulnerable to word-splitting because
the command substitution for constructor args is unquoted; update the forge
verify-contract invocation so the output of cast abi-encode is quoted (i.e.,
wrap $(cast abi-encode "constructor(address)" "$core_wormhole_address") in
quotes) when passed to --constructor-args to ensure the encoded constructor
argument is treated as a single token (references: the --constructor-args
argument and the cast abi-encode invocation using core_wormhole_address).

In `@evm/ts-scripts/src/createGovernanceMessage.ts`:
- Around line 1-4: The file uses CommonJS require for elliptic; replace "const
elliptic = require('elliptic')" with an ES import (e.g., import elliptic from
'elliptic') to match the other imports in createGovernanceMessage.ts and update
any usages (like new elliptic.ec(...)) accordingly; also add `@types/elliptic` as
a devDependency if TypeScript type errors appear so functions/types from the
elliptic package are properly typed.

In `@evm/ts-scripts/src/env.ts`:
- Around line 75-85: The functions getChainConfig and getAllContracts are
declared async but perform only synchronous work (they call loadScriptConfig
synchronously), so remove the unnecessary async to improve clarity: change their
signatures to non-async (drop the async keyword) and return the value directly
(no awaiting), and then update any callers that currently rely on them returning
Promises to handle the now-synchronous return (or wrap them in
Promise.resolve(...) at call sites if needed). Ensure you update function
declarations for getChainConfig and getAllContracts and run TypeScript checks to
fix any callsites expecting a Promise.
- Around line 199-217: The function loadContracts currently assigns the parsed
JSON to the module-level variable contracts and then calls return
loadContracts() recursively; change this so that after parsing and assigning
contracts (ContractsJson) the function returns contracts directly instead of
recursing. Update the return at the end of loadContracts to return contracts to
avoid the unnecessary infinite/extra call and ensure the function short-circuits
correctly when contracts is already set.

In `@evm/ts-scripts/src/executeGovernanceVaa.ts`:
- Around line 55-76: The file defines a local helper log(...) inside
executeGovernance but all logging uses console.log directly; either remove the
unused log function or replace the console.log calls with the helper for
consistent prefixed output: locate the executeGovernance function and either
delete the const log = (...) declaration, or change the three console.log/...
uses (the "Executing governance with VAA:", "Submitted governance transaction:",
and "success.") to call log(...) so logs include the `[${chain.chainId}]` prefix
before interacting with governanceContract.performGovernance and awaiting
tx.wait().

In `@solana/ts/scripts/env_short.ts`:
- Around line 42-51: The loadScriptConfig function should catch JSON parsing and
file read errors instead of relying on the unreachable if (!config) check: wrap
the fs.readFileSync and JSON.parse calls for loadScriptConfig in a try-catch,
include the filename and env values in the thrown/logged error message, and
rethrow or throw a new Error with the original error attached so callers get a
clear message when reading or parsing
`./ts/scripts/config/${env}/${filename}.json` fails.

In `@solana/ts/scripts/env.ts`:
- Around line 5-11: The module-level throws for LEDGER_DERIVATION_PATH and ENV
cause imports to fail; move these checks into lazy validation (e.g., inside the
getSigner() function) or guard them so only code paths that require a Ledger or
specific ENV fail; specifically remove the top-level throws for
LEDGER_DERIVATION_PATH and ENV and add checks inside getSigner() to validate
LEDGER_DERIVATION_PATH when hardware signing is requested, and validate ENV at
the point of configuration loading (e.g., within readManagerConfiguration or its
caller), ensuring error messages reference LEDGER_DERIVATION_PATH, ENV, and
getSigner so failures remain discoverable.

In `@solana/ts/scripts/executeGovernanceVaa_short.ts`:
- Around line 28-34: The code currently reads a hardcoded signer path
("YOUR_SIGNER_FILE") when building payerSecretKey; change it to read the path
from an environment variable (e.g., process.env.SIGNER_FILE or
process.env.PAYER_SIGNER) and fail fast if the variable is missing. Update the
fs.readFileSync call used to construct payerSecretKey (and any related constant)
to read the env var instead of the literal string, and add a clear error/throw
if the env var is not set so the script does not attempt to parse a missing
file.

In `@solana/ts/scripts/executeGovernanceVaa.ts`:
- Around line 22-24: The code currently uses a type-assertion
("governanceProgramId as any") when constructing NTTGovernance which bypasses
type safety; replace this cast by validating or converting governanceProgramId
before passing it to the NTTGovernance constructor: add a runtime check that
governanceProgramId matches an allowed GovProgramId (or map/parse it to the
expected enum/type), throw or return a clear error if invalid, or update the
variable's type so it is already the expected GovProgramId; reference the
symbols governanceProgramId and NTTGovernance (and the GovProgramId enum/type)
to locate and fix the code path that supplies the programId argument.
- Line 55: The log message is overly specific; replace the console.log that
references signerPk and nttProgramId so it reports a generic governance VAA
execution (e.g., "Signer <signerPk> executing governance VAA for program
<nttProgramId>"). Use signerPk and nttProgramId to build the message and, if
available, append VAA identifiers/summary from variables like vaa or
governanceVaa to make the log generic and informative.

In `@solana/ts/scripts/executeGovernanceVaas.ts`:
- Around line 69-75: The current code sends all governance instructions in one
transaction which can exceed Solana transaction size and account limits; update
the logic that calls ledgerSignAndSend with the full instructions array to
instead chunk the instructions into safe-sized batches (e.g., respect ~1200 byte
transaction payload and <=64 unique accounts per tx) and send each batch
sequentially: create a helper to split the instructions array by estimated
serialized size and distinct account count, call ledgerSignAndSend for each
batch, await connection.confirmTransaction for each returned tx.signature before
proceeding to the next batch, and log each batch's signature (references: the
instructions variable, ledgerSignAndSend, connection.confirmTransaction,
tx.signature).

In `@solana/ts/sdk/governance.ts`:
- Around line 73-74: verifyGovernanceHeader returns [governanceProgramId,
ixData] but governanceProgramId is never used; either remove the unused
destructuring or validate it—update the code around the verifyGovernanceHeader
call to compare the returned governanceProgramId with this.program.programId and
throw/log an error if they differ, otherwise drop the governanceProgramId
variable and only use ixData for deserializeInstruction(ixData); reference
verifyGovernanceHeader, governanceProgramId, this.program.programId, ixData, and
deserializeInstruction when making the change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 316f5bbc-4f79-4456-bee9-20e50d7ae0ad

📥 Commits

Reviewing files that changed from the base of the PR and between 25a5c3f and 3f4eac9.

📒 Files selected for processing (32)
  • deployments.md
  • evm/.gitignore
  • evm/ts-scripts/.env.sample
  • evm/ts-scripts/.gitignore
  • evm/ts-scripts/config/mainnet/chains.json
  • evm/ts-scripts/config/mainnet/contracts.json
  • evm/ts-scripts/config/testnet/contracts.json
  • evm/ts-scripts/config/testnet/governance-vaas.json
  • evm/ts-scripts/package.json
  • evm/ts-scripts/shell/verify-governance-contract.sh
  • evm/ts-scripts/src/createGovernanceMessage.ts
  • evm/ts-scripts/src/env.ts
  • evm/ts-scripts/src/executeGovernanceVaa.ts
  • evm/ts-scripts/src/readTransceiverConfig.ts
  • solana/package.json
  • solana/ts/scripts/.env.sample
  • solana/ts/scripts/claimOwnership.ts
  • solana/ts/scripts/config/devnet/governance-vaa.json
  • solana/ts/scripts/config/devnet/ntt.json
  • solana/ts/scripts/config/devnet/programs.json
  • solana/ts/scripts/config/mainnet/governance-vaas-june-05.json
  • solana/ts/scripts/config/mainnet/ntt.json
  • solana/ts/scripts/config/mainnet/programs.json
  • solana/ts/scripts/env.ts
  • solana/ts/scripts/env_short.ts
  • solana/ts/scripts/executeGovernanceVaa.ts
  • solana/ts/scripts/executeGovernanceVaa_short.ts
  • solana/ts/scripts/executeGovernanceVaas.ts
  • solana/ts/scripts/helpers.ts
  • solana/ts/scripts/readManagerConfiguration.ts
  • solana/ts/scripts/transferOwnershipToGov.ts
  • solana/ts/sdk/governance.ts

Comment thread evm/ts-scripts/.env.sample
Comment thread evm/ts-scripts/.gitignore
Comment thread evm/ts-scripts/config/mainnet/contracts.json Outdated
Comment thread evm/ts-scripts/config/testnet/governance-vaas.json Outdated
Comment thread evm/ts-scripts/package.json
Comment thread solana/ts/scripts/env_short.ts Outdated
Comment thread solana/ts/scripts/env.ts Outdated
Comment thread solana/ts-scripts/src/executeGovernanceVaas.ts
Comment thread solana/ts/scripts/readManagerConfiguration.ts Outdated
Comment thread solana/ts-scripts/src/governance.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@evm/ts-scripts/src/env.ts`:
- Around line 180-182: The construction of the StaticJsonRpcProvider is
formatted in a way that trips Prettier; locate the provider instantiation (the
let provider = new ethers.providers.StaticJsonRpcProvider(...) line using
providerRpc) and reformat the argument list to satisfy Prettier (e.g., keep the
constructor call on one line or place the argument on the same line as the call)
so the provider declaration adheres to project Prettier rules and `prettier
--check` passes. Ensure you only change whitespace/formatting around the
ethers.providers.StaticJsonRpcProvider and providerRpc usage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5040ef67-abc3-4e22-a581-acf1d14fdbd0

📥 Commits

Reviewing files that changed from the base of the PR and between 3f4eac9 and 9e273fc.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • evm/ts-scripts/src/env.ts

Comment thread evm/ts-scripts/src/env.ts Outdated
- 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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
evm/ts-scripts/src/env.ts (1)

59-64: ⚠️ Potential issue | 🟡 Minor

Resolve remaining Prettier violations to unblock CI.

prettier --check is still failing for this file; the long signatures/expressions around Line 63 and Line 208 are likely the current offenders.

Suggested formatting patch
 type ChainConfig = {
   chainId: ChainId;
-}
+};

-export async function getChainConfig<T extends ChainConfig>(filename: string, chainId: ChainId): Promise<T> {
+export async function getChainConfig<T extends ChainConfig>(
+  filename: string,
+  chainId: ChainId
+): Promise<T> {
   const scriptConfig: T[] = await loadScriptConfig(filename);
@@
-export async function getContractAddress(contractName: ContractTypes, chainId: ChainId): Promise<string> {
-  const contract = (await getAllContracts(contractName))?.find((c) => c.chainId === chainId)?.address;
+export async function getContractAddress(
+  contractName: ContractTypes,
+  chainId: ChainId
+): Promise<string> {
+  const contract = (await getAllContracts(contractName))?.find(
+    (c) => c.chainId === chainId
+  )?.address;

Also applies to: 208-209

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@evm/ts-scripts/src/env.ts` around lines 59 - 64, Prettier is failing due to
long lines in the type/signature area; reflow the declarations for ChainConfig
and the getChainConfig function so they adhere to the project's line-length
rules (wrap the object type, move generic/type params and parameter list onto
multiple lines if needed), and ensure the call to loadScriptConfig in
getChainConfig is formatted across lines to avoid an overly long expression;
target the ChainConfig type, the getChainConfig<T extends ChainConfig>(filename:
string, chainId: ChainId): Promise<T> signature, and the loadScriptConfig(...)
usage for reformatting so the file passes prettier --check.
solana/ts/scripts/governance.ts (1)

40-44: ⚠️ Potential issue | 🟡 Minor

The duplicate governance program ID is still present.

Line 43 repeats the same literal from Line 42, so GOV_PROGRAM_IDS still exposes a phantom third value.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/scripts/governance.ts` around lines 40 - 44, GOV_PROGRAM_IDS
contains a duplicate literal (the second and third entries are identical);
remove the repeated entry so the array only lists unique program IDs (update the
export const GOV_PROGRAM_IDS array in governance.ts to eliminate the phantom
third value) and keep the remaining values as const.
🧹 Nitpick comments (1)
solana/ts/scripts/env.ts (1)

17-24: Cache the in-flight signer initialization.

If two code paths hit getSigner() before the first SolanaLedgerSigner.create() resolves, Line 20 can open two Ledger sessions/prompts. Memoizing the promise avoids that race.

💡 Suggested fix
-let signer;
+let signerPromise: Promise<SolanaLedgerSigner> | undefined;
 export async function getSigner(): Promise<SolanaLedgerSigner> {
-  if (!signer) {
-    signer = await SolanaLedgerSigner.create(derivationPath);
-  }
-
-  return signer;
+  signerPromise ??= SolanaLedgerSigner.create(derivationPath);
+  return signerPromise;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/scripts/env.ts` around lines 17 - 24, The current getSigner()
stores the resolved SolanaLedgerSigner in the module variable signer which
allows two concurrent callers to race and call
SolanaLedgerSigner.create(derivationPath) twice; change the memoization to cache
the in-flight promise instead: make signer hold the Promise returned by
SolanaLedgerSigner.create and assign signer =
SolanaLedgerSigner.create(derivationPath) the first time getSigner() is called,
then await that promise and return the resolved SolanaLedgerSigner so concurrent
calls share the same creation promise; update types accordingly and keep the
exported getSigner signature the same.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@solana/ts/scripts/governance.ts`:
- Around line 73-74: verifyGovernanceHeader(...) returns a parsed
governanceProgramId that is currently ignored; update the script to immediately
compare the returned governanceProgramId with args.programId and fail fast if
they differ — e.g., throw or log an error and exit when governanceProgramId !==
args.programId — so the script stops before building/signing the transaction;
locate the call to verifyGovernanceHeader and use the governanceProgramId
variable for this check.

---

Duplicate comments:
In `@evm/ts-scripts/src/env.ts`:
- Around line 59-64: Prettier is failing due to long lines in the type/signature
area; reflow the declarations for ChainConfig and the getChainConfig function so
they adhere to the project's line-length rules (wrap the object type, move
generic/type params and parameter list onto multiple lines if needed), and
ensure the call to loadScriptConfig in getChainConfig is formatted across lines
to avoid an overly long expression; target the ChainConfig type, the
getChainConfig<T extends ChainConfig>(filename: string, chainId: ChainId):
Promise<T> signature, and the loadScriptConfig(...) usage for reformatting so
the file passes prettier --check.

In `@solana/ts/scripts/governance.ts`:
- Around line 40-44: GOV_PROGRAM_IDS contains a duplicate literal (the second
and third entries are identical); remove the repeated entry so the array only
lists unique program IDs (update the export const GOV_PROGRAM_IDS array in
governance.ts to eliminate the phantom third value) and keep the remaining
values as const.

---

Nitpick comments:
In `@solana/ts/scripts/env.ts`:
- Around line 17-24: The current getSigner() stores the resolved
SolanaLedgerSigner in the module variable signer which allows two concurrent
callers to race and call SolanaLedgerSigner.create(derivationPath) twice; change
the memoization to cache the in-flight promise instead: make signer hold the
Promise returned by SolanaLedgerSigner.create and assign signer =
SolanaLedgerSigner.create(derivationPath) the first time getSigner() is called,
then await that promise and return the resolved SolanaLedgerSigner so concurrent
calls share the same creation promise; update types accordingly and keep the
exported getSigner signature the same.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 16d87dbb-b277-47da-9a39-1bb915dd532f

📥 Commits

Reviewing files that changed from the base of the PR and between e877da1 and 2997b16.

📒 Files selected for processing (9)
  • evm/ts-scripts/.gitignore
  • evm/ts-scripts/package.json
  • evm/ts-scripts/src/env.ts
  • solana/ts/scripts/env.ts
  • solana/ts/scripts/executeGovernanceVaa.ts
  • solana/ts/scripts/executeGovernanceVaa_short.ts
  • solana/ts/scripts/executeGovernanceVaas.ts
  • solana/ts/scripts/governance.ts
  • solana/ts/scripts/transferOwnershipToGov.ts
✅ Files skipped from review due to trivial changes (5)
  • evm/ts-scripts/.gitignore
  • evm/ts-scripts/package.json
  • solana/ts/scripts/transferOwnershipToGov.ts
  • solana/ts/scripts/executeGovernanceVaas.ts
  • solana/ts/scripts/executeGovernanceVaa_short.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • solana/ts/scripts/executeGovernanceVaa.ts

Comment thread solana/ts/scripts/governance.ts Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
solana/ts/scripts/executeGovernanceVaas.ts (1)

17-20: ⚠️ Potential issue | 🟡 Minor

Fix the env-var name in the thrown error.

The guard checks GOVERNANCE_VAAS_FILE_PATH, so the current message points users to the wrong setting.

📝 Proposed fix
 if (!governanceVaasFileName) {
-  throw new Error("GOVERNANCE_VAAS_FILE_NAME is required.");
+  throw new Error("GOVERNANCE_VAAS_FILE_PATH is required.");
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@solana/ts/scripts/executeGovernanceVaas.ts` around lines 17 - 20, The thrown
error message uses the wrong env var name; update the guard that checks
governanceVaasFileName (from process.env.GOVERNANCE_VAAS_FILE_PATH) so the Error
text references GOVERNANCE_VAAS_FILE_PATH instead of GOVERNANCE_VAAS_FILE_NAME,
e.g., change the message in the throw new Error(...) near the
governanceVaasFileName declaration to mention the correct env var name.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@deployments.md`:
- Around line 54-59: Add explicit language identifiers (e.g., shell) to all
unlabeled fenced code blocks that contain shell commands; locate the blocks
containing the snippets like "export FOUNDRY_PROFILE=prod", "export
NTT_PROGRAM_ID=<your-program-key>", "cd solana" / "anchor build --arch sbf", the
"solana program -k usb://ledger?key=" deploy line, and the block with "export
SCANNER_TOKENS_FILE_PATH=" and prefix each triple-backtick fence with "shell"
(```shell) so markdownlint MD040 is satisfied.
- Line 45: Update the incorrect documentation path
"ts-scripts/output/<scripts-name>/<timestamp>.json" mentioned in the sentence
(and the other occurrences that currently point to wrong EVM config/output
locations) to the actual script output path used by the repo; locate every
instance of that path string in the file and replace it with the correct output
location used by the deploy scripts (ensure the phrasing matches the script
layout and output structure so operators are directed to the real JSON files
produced by the deploy scripts).

In `@solana/ts/scripts/executeGovernanceVaa.ts`:
- Around line 16-17: The log text in executeGovernanceVaa.ts incorrectly
mentions "claiming NTT ownership" and pulls in nttProgramId only for that
message; change the log(s) produced where getProgramAddresses() is destructured
(nttProgramId, wormholeProgramId, governanceProgramId) and the messages around
lines 52-54 to a generic message such as "Executing governance payload" (or
"Executing governance VAA") that does not reference NTT-specific semantics, and
remove nttProgramId from the destructure and any unused references so only
wormholeProgramId and governanceProgramId remain.

---

Duplicate comments:
In `@solana/ts/scripts/executeGovernanceVaas.ts`:
- Around line 17-20: The thrown error message uses the wrong env var name;
update the guard that checks governanceVaasFileName (from
process.env.GOVERNANCE_VAAS_FILE_PATH) so the Error text references
GOVERNANCE_VAAS_FILE_PATH instead of GOVERNANCE_VAAS_FILE_NAME, e.g., change the
message in the throw new Error(...) near the governanceVaasFileName declaration
to mention the correct env var name.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d2714e59-c036-4b30-9bc1-3013afa81565

📥 Commits

Reviewing files that changed from the base of the PR and between 2997b16 and cbfeff5.

📒 Files selected for processing (21)
  • deployments.md
  • evm/ts-scripts/config/testnet/governance-vaas.json
  • evm/ts-scripts/src/env.ts
  • evm/ts-scripts/src/executeGovernanceVaa.ts
  • evm/ts-scripts/src/readTransceiverConfig.ts
  • solana/ts/scripts/claimOwnership.ts
  • solana/ts/scripts/config/devnet/governance-vaa.json
  • solana/ts/scripts/config/devnet/ntt.json
  • solana/ts/scripts/config/devnet/programs.json
  • solana/ts/scripts/config/mainnet/governance-vaas-june-05.json
  • solana/ts/scripts/config/mainnet/ntt.json
  • solana/ts/scripts/config/mainnet/programs.json
  • solana/ts/scripts/env.ts
  • solana/ts/scripts/env_short.ts
  • solana/ts/scripts/executeGovernanceVaa.ts
  • solana/ts/scripts/executeGovernanceVaa_short.ts
  • solana/ts/scripts/executeGovernanceVaas.ts
  • solana/ts/scripts/governance.ts
  • solana/ts/scripts/helpers.ts
  • solana/ts/scripts/readManagerConfiguration.ts
  • solana/ts/scripts/transferOwnershipToGov.ts
✅ Files skipped from review due to trivial changes (12)
  • solana/ts/scripts/config/mainnet/programs.json
  • evm/ts-scripts/config/testnet/governance-vaas.json
  • solana/ts/scripts/config/devnet/programs.json
  • solana/ts/scripts/config/devnet/governance-vaa.json
  • solana/ts/scripts/config/devnet/ntt.json
  • solana/ts/scripts/config/mainnet/ntt.json
  • solana/ts/scripts/config/mainnet/governance-vaas-june-05.json
  • solana/ts/scripts/claimOwnership.ts
  • solana/ts/scripts/transferOwnershipToGov.ts
  • solana/ts/scripts/executeGovernanceVaa_short.ts
  • solana/ts/scripts/helpers.ts
  • solana/ts/scripts/env.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • solana/ts/scripts/readManagerConfiguration.ts
  • evm/ts-scripts/src/executeGovernanceVaa.ts
  • evm/ts-scripts/src/readTransceiverConfig.ts
  • solana/ts/scripts/env_short.ts

Comment thread deployments.md Outdated
Comment thread deployments.md Outdated
Comment thread solana/ts-scripts/src/executeGovernanceVaa.ts
Comment thread solana/package.json

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't all this go in a different package so as not to conflict with or pollute the solana ts route?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good point, moving the Solana scripts into their own package

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, is this necessary? I think this was used with the old NTT deployment scripts, right? This is not consumed by the NTT CLI as far as I know. But it's been a long time since I looked at these scripts so I'm not sure. The same would apply to the testnet equivalent.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call, createGovernanceMessage.ts only uses token and chainId from this config so I trimmed the managers.json down to just those two fields and removed the unused deployment fields

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.
- 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
@evgeniko
evgeniko requested a review from scnale March 30, 2026 14:56
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.
Comment thread evm/ts-scripts/package.json Outdated
"@types/node": "^20.11.22",
"@xlabs-xyz/ledger-signer": "^0.0.3",
"dotenv": "^16.4.5",
"ethers": "^5.7.2",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be worth bumping this to ^5.8.0 so that it contains the security fixes for elliptic.
We might want to add:

  "overrides": {
    "elliptic": "6.6.1"
  }

To ensure no other dependency pulls in the old version too.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great suggestion, thank you.
Done: 702cb47

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants