diff --git a/.github/workflows/solana.yml b/.github/workflows/solana.yml index 19a6a71d7..5a17bebda 100644 --- a/.github/workflows/solana.yml +++ b/.github/workflows/solana.yml @@ -47,10 +47,10 @@ jobs: run: cargo fmt --check --all --manifest-path Cargo.toml - name: Run `cargo check` - run: cargo check --workspace --tests --manifest-path Cargo.toml + run: cargo check --workspace --tests --manifest-path Cargo.toml --features mainnet - name: Run `cargo clippy` - run: cargo clippy --workspace --tests --manifest-path Cargo.toml -- -Dclippy::cast_possible_truncation + run: cargo clippy --workspace --tests --manifest-path Cargo.toml --features mainnet -- -Dclippy::cast_possible_truncation - name: Cache solana tools id: cache-solana diff --git a/cli/src/index.ts b/cli/src/index.ts index 49396c620..9896bc164 100755 --- a/cli/src/index.ts +++ b/cli/src/index.ts @@ -363,7 +363,7 @@ const options = { type: "number", }, payer: { - describe: "Path to the payer json file (Solana)", + describe: "Path to the payer json file (SVM)", type: "string", }, skipChain: { @@ -537,15 +537,15 @@ yargs(hideBin(process.argv)) // type: "string", // }) .option("program-key", { - describe: "Path to program key json (Solana)", + describe: "Path to program key json (SVM)", type: "string", }) .option("payer", { - describe: "Path to payer key json (Solana)", + describe: "Path to payer key json (SVM)", type: "string", }) .option("binary", { - describe: "Path to program binary (.so file -- Solana)", + describe: "Path to program binary (.so file -- SVM)", type: "string", }) .option("token", { @@ -559,7 +559,7 @@ yargs(hideBin(process.argv)) choices: ["locking", "burning"], }) .option("solana-priority-fee", { - describe: "Priority fee for Solana deployment (in microlamports)", + describe: "Priority fee for SVM deployment (in microlamports)", type: "number", default: 50000, }) @@ -757,15 +757,15 @@ yargs(hideBin(process.argv)) .option("path", options.deploymentPath) .option("yes", options.yes) .option("payer", { - describe: "Path to payer key json (Solana)", + describe: "Path to payer key json (SVM)", type: "string", }) .option("program-key", { - describe: "Path to program key json (Solana)", + describe: "Path to program key json (SVM)", type: "string", }) .option("binary", { - describe: "Path to program binary (.so file -- Solana)", + describe: "Path to program binary (.so file -- SVM)", type: "string", }) .option("gas-estimate-multiplier", options.gasEstimateMultiplier) @@ -1113,7 +1113,7 @@ yargs(hideBin(process.argv)) ) .example( "$0 push --payer ", - "Path to the payer json file (Solana), instead of setting SOLANA_PRIVATE_KEY env variable" + "Path to the payer json file (SVM), instead of setting SOLANA_PRIVATE_KEY env variable" ), async (argv) => { const deployments: Config = loadConfig(argv["path"]); @@ -1904,7 +1904,7 @@ yargs(hideBin(process.argv)) } } ) - .command("solana", "Solana commands", (yargs) => { + .command(["solana", "svm"], "svm commands", (yargs) => { yargs .command( "key-base58 ", @@ -1999,11 +1999,11 @@ yargs(hideBin(process.argv)) .option("yes", options.yes) .option("payer", { ...options.payer, demandOption: true }) .example( - "$0 solana create-spl-multisig Sol1234... --token Sol3456... --manager Sol5678... --payer ", + "$0 svm create-spl-multisig Sol1234... --token Sol3456... --manager Sol5678... --payer ", "Create multisig with Sol1234... having independent mint privilege alongside NTT token-authority for undeployed program" ) .example( - "$0 solana create-spl-multisig Sol1234... Sol3456... Sol5678... --payer ", + "$0 svm create-spl-multisig Sol1234... Sol3456... Sol5678... --payer ", "Create multisig with Sol1234..., Sol3456..., and Sol5678... having mint privileges alongside NTT token-authority for deployed program" ), async (argv) => { @@ -2134,6 +2134,107 @@ yargs(hideBin(process.argv)) } } ) + .command( + "build ", + "build the SVM program binary without deploying", + (yargs) => + yargs + .positional("chain", options.chain) + .option("program-key", { + describe: "Path to program key json", + type: "string", + }) + .option("binary", { + describe: "Path to existing program binary (.so file) - if provided, only validates the binary", + type: "string", + }) + .option("ver", options.version) + .option("latest", options.latest) + .option("local", options.local) + .option("path", options.deploymentPath) + .example( + "$0 svm build Solana --latest", + "Build the SVM program binary using the latest version" + ) + .example( + "$0 svm build Solana --ver 1.0.0", + "Build using a specific version" + ) + .example( + "$0 svm build Solana --local --program-key my-program-keypair.json", + "Build from local source with a specific program keypair" + ) + .example( + "$0 svm build Solana --latest --binary target/deploy/example_native_token_transfers.so", + "Validate an existing binary against the latest version" + ), + async (argv) => { + const path = argv["path"]; + const deployments: Config = loadConfig(path); + const chain: Chain = argv["chain"]; + const network = deployments.network as Network; + + // Check that the platform is Solana + const platform = chainToPlatform(chain); + if (platform !== "Solana") { + console.error( + `build command is only supported for Solana chains. Got platform: ${platform}` + ); + process.exit(1); + } + + validateChain(network, chain); + + // Resolve version (--latest, --ver, or --local) + const version = resolveVersion( + argv["latest"], + argv["ver"], + argv["local"], + platform + ); + + // Create worktree if version is specified, otherwise use current directory + const worktree = version ? createWorkTree(platform, version) : "."; + + // Get wormhole core bridge address for verification + const wh = new Wormhole( + network, + [solana.Platform, evm.Platform, sui.Platform], + overrides + ); + const ch = wh.getChain(chain); + const wormhole = ch.config.contracts.coreBridge; + if (!wormhole) { + console.error("Core bridge not found"); + process.exit(1); + } + + const programKeyPath = argv["program-key"]; + const binaryPath = argv["binary"]; + + console.log(`Building SVM program for ${chain} on ${network}...`); + if (version) { + console.log(chalk.blue(`Using version: ${version}`)); + console.log(chalk.blue(`Worktree: ${worktree}`)); + } else { + console.log(chalk.blue(`Using local source`)); + } + + const buildResult = await buildSvm( + worktree, + network, + chain, + wormhole, + version, + programKeyPath, + binaryPath + ); + + console.log(`Program ID: ${buildResult.programId}`); + console.log(`Binary: ${buildResult.binary}`); + console.log(`Keypair: ${buildResult.programKeypairPath}`); + } + ) .demandCommand(); }) .command("manual", "Manual NTT operations", (yargs) => { @@ -2729,7 +2830,7 @@ async function upgradeSolana( throw new Error("Cannot upgrade Solana to local version"); // TODO: this is not hard to enabled } const mint = (await ntt.getConfig()).mint; - await deploySolana( + await deploySvm( pwd, version, await ntt.getMode(), @@ -2974,7 +3075,7 @@ async function deploy( process.exit(1); } const solanaCtx = ch as ChainContext; - return (await deploySolana( + return (await deploySvm( worktree, version, mode, @@ -3145,34 +3246,138 @@ ${simulateArg} \ return { chain: ch.chain, address: universalManager }; } -async function deploySolana( +/** + * Check if the Solana program supports the bridge-address-from-env feature + * @param pwd - Project root directory + * @returns true if the feature exists in Cargo.toml + */ +function hasBridgeAddressFromEnvFeature(pwd: string): boolean { + try { + const cargoTomlPath = `${pwd}/solana/programs/example-native-token-transfers/Cargo.toml`; + if (!fs.existsSync(cargoTomlPath)) { + return false; + } + const cargoToml = fs.readFileSync(cargoTomlPath, 'utf8'); + // Check if bridge-address-from-env feature is defined + return cargoToml.includes('bridge-address-from-env'); + } catch (error) { + return false; + } +} + +/** + * Build the Solana NTT program using anchor build. + * Uses bridge-address-from-env feature if available, otherwise uses network-specific features. + * For legacy builds on non-Solana chains, patches the binary after building. + * @param pwd - Project root directory + * @param network - Network to build for + * @param chain - Target chain (used to determine if patching is needed) + * @param wormhole - Wormhole core bridge address + * @returns Exit code from anchor build + */ +async function runAnchorBuild( pwd: string, - version: string | null, - mode: Ntt.Mode, - ch: ChainContext, - token: string, - payer: string, - initialize: boolean, - managerKeyPath?: string, - binaryPath?: string, - priorityFee?: number -): Promise> { - ensureNttRoot(pwd); + network: Network, + chain: Chain, + wormhole: string, +): Promise { + checkAnchorVersion(pwd); + + const useBridgeFromEnv = hasBridgeAddressFromEnvFeature(pwd); + + let buildArgs: string[]; + let buildEnv: Record; + + if (useBridgeFromEnv) { + // New method: use bridge-address-from-env feature with BRIDGE_ADDRESS env var + console.log(`Building with bridge-address-from-env feature (BRIDGE_ADDRESS=${wormhole})...`); + buildArgs = [ + "anchor", + "build", + "-p", + "example_native_token_transfers", + "--", + "--no-default-features", + "--features", + "bridge-address-from-env" + ]; + buildEnv = { + ...process.env, + BRIDGE_ADDRESS: wormhole + }; + } else { + // Old method: use network-specific feature (mainnet, solana-devnet, tilt-devnet) + const networkFeature = cargoNetworkFeature(network); + console.log(`Building with ${networkFeature} feature (legacy method)...`); + buildArgs = [ + "anchor", + "build", + "-p", + "example_native_token_transfers", + "--", + "--no-default-features", + "--features", + networkFeature + ]; + buildEnv = process.env; + } - checkSolanaVersion(pwd); + const proc = Bun.spawn(buildArgs, { + cwd: `${pwd}/solana`, + env: buildEnv + }); - // TODO: if the binary is provided, we should not check addresses in the source tree. (so we should move around the control flow a bit) - // TODO: factor out some of this into separate functions to help readability of this function (maybe even move to a different file) + await proc.exited; + const exitCode = proc.exitCode ?? 1; - const wormhole = ch.config.contracts.coreBridge; - if (!wormhole) { - console.error("Core bridge not found"); - process.exit(1); + if (exitCode !== 0) { + return exitCode; + } + + // For legacy builds on non-Solana chains, patch the binary + if (!useBridgeFromEnv && chain !== "Solana") { + const binary = `${pwd}/solana/target/deploy/example_native_token_transfers.so`; + + // Get Solana mainnet address for patching + const wh = new Wormhole(network, [solana.Platform], overrides); + const sol = wh.getChain("Solana"); + const solanaAddress = sol.config.contracts.coreBridge; + if (!solanaAddress) { + console.error("Core bridge address not found in Solana config"); + return 1; + } + + console.log(`Patching binary for ${chain}...`); + await patchSolanaBinary(binary, wormhole, solanaAddress); } - // grep example_native_token_transfers = ".*" - // in solana/Anchor.toml - // TODO: what if they rename the program? + return exitCode; +} + +/** + * Build the Solana NTT program binary + * @param pwd - Project root directory + * @param network - Network to build for (affects cargo features) + * @param chain - Target chain (for patching non-Solana chains) + * @param wormhole - Wormhole core bridge address for verification + * @param version - Version string for verification (optional) + * @param programKeyPath - Optional path to program keypair (if not provided, will look for {programId}.json) + * @param binaryPath - Optional path to pre-built binary (if provided, building is skipped) + * @returns Object containing binary path, program ID, and program keypair path + */ +async function buildSvm( + pwd: string, + network: Network, + chain: Chain, + wormhole: string, + version: string | null, + programKeyPath?: string, + binaryPath?: string +): Promise<{ binary: string, programId: string, programKeypairPath: string }> { + ensureNttRoot(pwd); + checkSolanaVersion(pwd); + + // If binary is provided, still need to get program ID const existingProgramId = fs .readFileSync(`${pwd}/solana/Anchor.toml`) .toString() @@ -3184,17 +3389,17 @@ async function deploySolana( process.exit(1); } - let programKeypairPath; - let programKeypair; + let programKeypairPath: string; + let programKeypair: Keypair; - if (managerKeyPath) { - if (!fs.existsSync(managerKeyPath)) { - console.error(`Program keypair not found: ${managerKeyPath}`); + if (programKeyPath) { + if (!fs.existsSync(programKeyPath)) { + console.error(`Program keypair not found: ${programKeyPath}`); process.exit(1); } - programKeypairPath = managerKeyPath; + programKeypairPath = programKeyPath; programKeypair = Keypair.fromSecretKey( - new Uint8Array(JSON.parse(fs.readFileSync(managerKeyPath).toString())) + new Uint8Array(JSON.parse(fs.readFileSync(programKeyPath).toString())) ); } else { const programKeyJson = `${existingProgramId}.json`; @@ -3250,6 +3455,62 @@ async function deploySolana( fs.writeFileSync(libRsPath, newLibRs); } + let binary: string; + + if (binaryPath) { + console.log(`Using provided binary: ${binaryPath}`); + binary = binaryPath; + } else { + // build the program + console.log(`Building SVM program for ${network}...`); + const exitCode = await runAnchorBuild(pwd, network, chain, wormhole); + if (exitCode !== 0) { + process.exit(exitCode); + } + + binary = `${pwd}/solana/target/deploy/example_native_token_transfers.so`; + console.log(`Build complete: ${binary}`); + } + + // Verify the binary contains expected addresses and version + console.log(`Verifying binary...`); + await checkSvmBinary( + binary, + wormhole, + providedProgramId, + version ?? undefined + ); + console.log(`✓ Binary verification passed`); + + return { + binary, + programId: providedProgramId, + programKeypairPath, + }; +} + +async function deploySvm( + pwd: string, + version: string | null, + mode: Ntt.Mode, + ch: ChainContext, + token: string, + payer: string, + initialize: boolean, + managerKeyPath?: string, + binaryPath?: string, + priorityFee?: number +): Promise> { + const wormhole = ch.config.contracts.coreBridge; + if (!wormhole) { + console.error("Core bridge not found"); + process.exit(1); + } + + // Build the Solana program (or use provided binary) + const buildResult = await buildSvm(pwd, ch.network, ch.chain, wormhole, version, managerKeyPath, binaryPath); + const { binary, programId: providedProgramId, programKeypairPath } = buildResult; + // First we check that the provided mint's mint authority is the program's token authority PDA when in burning mode. // This is checked in the program initialiser anyway, but we can save some // time by checking it here and failing early (not to mention better @@ -3331,61 +3592,10 @@ async function deploySolana( } } - let binary: string; - + // Deploy the binary (patching was already done during build for legacy builds on non-Solana chains) const skipDeploy = false; if (!skipDeploy) { - if (binaryPath) { - binary = binaryPath; - } else { - // build the program - // TODO: build with docker - checkAnchorVersion(pwd); - const proc = Bun.spawn( - [ - "anchor", - "build", - "-p", - "example_native_token_transfers", - "--", - "--no-default-features", - "--features", - cargoNetworkFeature(ch.network), - ], - { - cwd: `${pwd}/solana`, - } - ); - - // const _out = await new Response(proc.stdout).text(); - - await proc.exited; - if (proc.exitCode !== 0) { - process.exit(proc.exitCode ?? 1); - } - - binary = `${pwd}/solana/target/deploy/example_native_token_transfers.so`; - } - - const wh = new Wormhole(ch.network, [solana.Platform], overrides); - const sol = wh.getChain("Solana"); - const solanaAddress = sol.config.contracts.coreBridge; - if (!solanaAddress) { - console.error("Core bridge address not found in Solana config"); - process.exit(1); - } - - if (ch.chain !== "Solana") { - await patchSolanaBinary(binary, wormhole, solanaAddress); - } - await checkSolanaBinary( - binary, - wormhole, - providedProgramId, - version ?? undefined - ); - // if buffer.json doesn't exist, create it if (!fs.existsSync(`buffer.json`)) { execSync(`solana-keygen new -o buffer.json --no-bip39-passphrase`); @@ -4794,7 +5004,7 @@ async function patchSolanaBinary( } } -async function checkSolanaBinary( +async function checkSvmBinary( binary: string, wormhole: string, providedProgramId: string, diff --git a/solana/Cargo.lock b/solana/Cargo.lock index 6030b4278..3db7ade19 100644 --- a/solana/Cargo.lock +++ b/solana/Cargo.lock @@ -699,7 +699,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4114279215a005bc675e386011e594e1d9b800918cea18fcadadcce864a2046b" dependencies = [ "borsh-derive 0.10.3", - "hashbrown 0.13.2", + "hashbrown 0.12.3", ] [[package]] @@ -2527,7 +2527,7 @@ dependencies = [ "wormhole-io", "wormhole-post-message-shim-interface", "wormhole-sdk", - "wormhole-svm-definitions 0.1.0 (git+https://github.com/wormholelabs-xyz/wormhole?branch=svm-shims-fr-env-addr)", + "wormhole-svm-definitions 1.0.0 (git+https://github.com/wormhole-foundation/wormhole?rev=325cca4b628f17536f54b079eeb82b41247bfbef)", "wormhole-verify-vaa-shim-interface", ] @@ -2693,7 +2693,7 @@ version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "681030a937600a36906c185595136d26abfebb4aa9c65701cefcaf8578bb982b" dependencies = [ - "proc-macro-crate 3.1.0", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 2.0.48", @@ -6423,13 +6423,14 @@ dependencies = [ [[package]] name = "wormhole-anchor-sdk" -version = "0.29.0-alpha.1" -source = "git+https://github.com/wormholelabs-xyz/wormhole-scaffolding?branch=solana/anchor-0.29.0#2b4598732a417dc8bd658dedbdd234839b662321" +version = "1.0.0" +source = "git+https://github.com/wormhole-foundation/wormhole-scaffolding.git?rev=d38ef8944edfb479ab7eb9399e065711b1dbec13#d38ef8944edfb479ab7eb9399e065711b1dbec13" dependencies = [ "anchor-lang", "anchor-spl", "cfg-if", "wormhole-io", + "wormhole-svm-definitions 1.0.0 (git+https://github.com/wormhole-foundation/wormhole?rev=325cca4b628f17536f54b079eeb82b41247bfbef)", ] [[package]] @@ -6437,11 +6438,14 @@ name = "wormhole-governance" version = "3.0.0" dependencies = [ "anchor-lang", + "cfg-if", + "const-crypto", "hex", "solana-program", "wormhole-anchor-sdk", "wormhole-io", "wormhole-sdk", + "wormhole-svm-definitions 1.0.0 (git+https://github.com/wormhole-foundation/wormhole?rev=325cca4b628f17536f54b079eeb82b41247bfbef)", ] [[package]] @@ -6453,7 +6457,7 @@ checksum = "b021a14ea7bcef9517ed9f81d4466c4a663dd90e726c5724707a976fa83ad8f3" [[package]] name = "wormhole-post-message-shim-interface" version = "0.0.0" -source = "git+https://github.com/wormhole-foundation/wormhole?branch=svm/anchor-v0.29.0-shim-interface#63481c6486f8747529f7df692bab8b7fdf85146b" +source = "git+https://github.com/wormhole-foundation/wormhole?rev=783ae2da1#783ae2da14d33043f07d8ff9bf4ce80a9c081251" dependencies = [ "anchor-lang", ] @@ -6490,8 +6494,8 @@ dependencies = [ [[package]] name = "wormhole-svm-definitions" -version = "0.1.0" -source = "git+https://github.com/wormholelabs-xyz/wormhole?branch=svm-shims-fr-env-addr#085a45eefb279b42f17d5b880fce7b96f092290f" +version = "1.0.0" +source = "git+https://github.com/wormhole-foundation/wormhole?rev=325cca4b628f17536f54b079eeb82b41247bfbef#325cca4b628f17536f54b079eeb82b41247bfbef" dependencies = [ "cfg-if", "const-crypto", @@ -6501,10 +6505,11 @@ dependencies = [ [[package]] name = "wormhole-svm-definitions" -version = "0.1.0" -source = "git+https://github.com/wormhole-foundation/wormhole?branch=svm/anchor-v0.29.0-shim-interface#63481c6486f8747529f7df692bab8b7fdf85146b" +version = "1.0.0" +source = "git+https://github.com/wormhole-foundation/wormhole?rev=783ae2da1#783ae2da14d33043f07d8ff9bf4ce80a9c081251" dependencies = [ "cfg-if", + "const-crypto", "sha2-const-stable", "solana-program", ] @@ -6512,10 +6517,10 @@ dependencies = [ [[package]] name = "wormhole-verify-vaa-shim-interface" version = "0.0.0" -source = "git+https://github.com/wormhole-foundation/wormhole?branch=svm/anchor-v0.29.0-shim-interface#63481c6486f8747529f7df692bab8b7fdf85146b" +source = "git+https://github.com/wormhole-foundation/wormhole?rev=783ae2da1#783ae2da14d33043f07d8ff9bf4ce80a9c081251" dependencies = [ "anchor-lang", - "wormhole-svm-definitions 0.1.0 (git+https://github.com/wormhole-foundation/wormhole?branch=svm/anchor-v0.29.0-shim-interface)", + "wormhole-svm-definitions 1.0.0 (git+https://github.com/wormhole-foundation/wormhole?rev=783ae2da1)", ] [[package]] diff --git a/solana/Cargo.toml b/solana/Cargo.toml index ae4e05d0d..c9a761fef 100644 --- a/solana/Cargo.toml +++ b/solana/Cargo.toml @@ -54,7 +54,9 @@ solana-address-lookup-table-program = "=1.18.26" spl-token = "4.0.0" spl-token-2022 = "3.0.2" -wormhole-anchor-sdk = { git = "https://github.com/wormholelabs-xyz/wormhole-scaffolding", branch = "solana/anchor-0.29.0", default-features = false } +wormhole-anchor-sdk = { git = "https://github.com/wormhole-foundation/wormhole-scaffolding.git", rev = "d38ef8944edfb479ab7eb9399e065711b1dbec13", default-features = false } # TODO: depends on https://github.com/wormhole-foundation/wormhole-scaffolding/pull/105, need to update this once merged +# NOTE: this is the `main` commit at the time of writing. +wormhole-svm-definitions = { git = "https://github.com/wormhole-foundation/wormhole", rev = "325cca4b628f17536f54b079eeb82b41247bfbef" } wormhole-sdk = { git = "https://github.com/wormhole-foundation/wormhole", rev = "eee4641" } serde_wormhole = { git = "https://github.com/wormhole-foundation/wormhole", rev = "eee4641" } diff --git a/solana/Makefile b/solana/Makefile index 6d0e9c53b..910c6adbb 100644 --- a/solana/Makefile +++ b/solana/Makefile @@ -21,13 +21,13 @@ cargo-build: # After building, remove the generics from the idl file. This is necessary as of anchor 0.29.0, # because the javascript library does not support generics yet, and just panics anchor-build: - anchor build --arch sbf + anchor build --arch sbf -- --features mainnet for jsonfile in target/idl/*.json; do \ echo "Removing generics from" $$jsonfile; \ ./scripts/patch-idl $$jsonfile; \ done -artifacts-mainnet: +artifacts-mainnet: $(MAKE) _artifacts TARGET_DIR=$@ NETWORK=mainnet artifacts-solana-devnet: diff --git a/solana/programs/example-native-token-transfers/Cargo.toml b/solana/programs/example-native-token-transfers/Cargo.toml index c42bf3b6d..fc08b9138 100644 --- a/solana/programs/example-native-token-transfers/Cargo.toml +++ b/solana/programs/example-native-token-transfers/Cargo.toml @@ -9,7 +9,7 @@ crate-type = ["cdylib", "lib"] name = "example_native_token_transfers" [features] -default = ["mainnet"] +default = [] no-entrypoint = [] no-idl = [] no-log-ix-name = [] @@ -17,12 +17,15 @@ cpi = ["no-entrypoint"] idl-build = [ "anchor-lang/idl-build", "anchor-spl/idl-build", + "wormhole-anchor-sdk/idl-build", + "wormhole-anchor-sdk/no-custom-discriminator", + "wormhole-anchor-sdk/mainnet", ] # cargo-test-sbf will pass this along test-sbf = [] # networks mainnet = [ "wormhole-anchor-sdk/mainnet" ] -bridge-address-from-env = [ "wormhole-anchor-sdk/bridge-address-from-env" ] +bridge-address-from-env = [ "wormhole-anchor-sdk/from-env" ] solana-devnet = [ "wormhole-anchor-sdk/solana-devnet" ] tilt-devnet = [ "wormhole-anchor-sdk/tilt-devnet" ] tilt-devnet2 = [ "tilt-devnet" ] diff --git a/solana/programs/ntt-quoter/Cargo.toml b/solana/programs/ntt-quoter/Cargo.toml index e804d68be..cbe452911 100644 --- a/solana/programs/ntt-quoter/Cargo.toml +++ b/solana/programs/ntt-quoter/Cargo.toml @@ -8,16 +8,16 @@ crate-type = ["cdylib", "lib"] name = "ntt_quoter" [features] -default = ["mainnet", "no-idl"] +default = ["no-idl"] no-entrypoint = [] no-idl = [] no-log-ix-name = [] cpi = ["no-entrypoint"] -mainnet = [] -solana-devnet = [] -tilt-devnet = [] -tilt-devnet2 = ["tilt-devnet"] +mainnet = ["example-native-token-transfers/mainnet"] +solana-devnet = ["example-native-token-transfers/solana-devnet"] +tilt-devnet = ["example-native-token-transfers/tilt-devnet"] +tilt-devnet2 = ["example-native-token-transfers/tilt-devnet2"] [lints] workspace = true diff --git a/solana/programs/ntt-transceiver/Cargo.toml b/solana/programs/ntt-transceiver/Cargo.toml index fd9462f1c..a75649d16 100644 --- a/solana/programs/ntt-transceiver/Cargo.toml +++ b/solana/programs/ntt-transceiver/Cargo.toml @@ -15,15 +15,16 @@ idl-build = [ "anchor-lang/idl-build", "anchor-spl/idl-build", "example-native-token-transfers/idl-build", - "wormhole-anchor-sdk/mainnet" # TODO: is this kosher? builds idl without passing on our feature flags + "wormhole-anchor-sdk/mainnet", + "wormhole-transceiver" ] no-log-ix-name = [] cpi = ["no-entrypoint", "example-native-token-transfers/cpi"] -default = ["mainnet"] +default = [] wormhole-transceiver = ["example-native-token-transfers/cpi"] transceiver-type-from-env = [] -bridge-address-from-env = [ "wormhole-anchor-sdk/bridge-address-from-env", "wormhole-svm-definitions/from-env", "example-native-token-transfers/bridge-address-from-env" ] +bridge-address-from-env = [ "wormhole-anchor-sdk/from-env", "wormhole-svm-definitions/from-env", "example-native-token-transfers/bridge-address-from-env" ] mainnet = [ "wormhole-transceiver", "wormhole-anchor-sdk/mainnet", "example-native-token-transfers/mainnet" ] solana-devnet = [ "wormhole-transceiver", "wormhole-anchor-sdk/solana-devnet", "wormhole-svm-definitions/testnet", "example-native-token-transfers/solana-devnet" ] tilt-devnet = [ "wormhole-transceiver", "wormhole-anchor-sdk/tilt-devnet", "wormhole-svm-definitions/localnet", "example-native-token-transfers/tilt-devnet" ] @@ -37,9 +38,9 @@ ntt-messages = { path = "../../modules/ntt-messages", features = ["anchor", "has anchor-lang.workspace = true anchor-spl.workspace = true solana-program.workspace = true -wormhole-post-message-shim-interface = { git = "https://github.com/wormhole-foundation/wormhole", branch = "svm/anchor-v0.29.0-shim-interface", features = ["no-entrypoint", "cpi"] } -wormhole-verify-vaa-shim-interface = { git = "https://github.com/wormhole-foundation/wormhole", branch = "svm/anchor-v0.29.0-shim-interface", features = ["no-entrypoint", "cpi"] } -wormhole-svm-definitions = { git = "https://github.com/wormholelabs-xyz/wormhole", branch = "svm-shims-fr-env-addr" } +wormhole-post-message-shim-interface = { git = "https://github.com/wormhole-foundation/wormhole", rev = "783ae2da1", features = ["no-entrypoint", "cpi"] } # TODO: update once https://github.com/wormhole-foundation/wormhole/pull/4559 is merged +wormhole-verify-vaa-shim-interface = { git = "https://github.com/wormhole-foundation/wormhole", rev = "783ae2da1", features = ["no-entrypoint", "cpi"] } # TODO: update once https://github.com/wormhole-foundation/wormhole/pull/4559 is merged +wormhole-svm-definitions.workspace = true cfg-if = "1.0.0" wormhole-anchor-sdk.workspace = true diff --git a/solana/programs/wormhole-governance/Cargo.toml b/solana/programs/wormhole-governance/Cargo.toml index 15e39d0d6..9fb0b9786 100644 --- a/solana/programs/wormhole-governance/Cargo.toml +++ b/solana/programs/wormhole-governance/Cargo.toml @@ -9,16 +9,19 @@ crate-type = ["cdylib", "lib"] name = "wormhole_governance" [features] -default = ["mainnet"] +default = [] no-entrypoint = [] no-idl = [] no-log-ix-name = [] cpi = ["no-entrypoint"] idl-build = [ "anchor-lang/idl-build", + "wormhole-anchor-sdk/idl-build", + "wormhole-anchor-sdk/no-custom-discriminator", + "wormhole-anchor-sdk/mainnet", ] -bridge-address-from-env = [ "wormhole-anchor-sdk/bridge-address-from-env" ] +bridge-address-from-env = [ "wormhole-anchor-sdk/from-env", "wormhole-svm-definitions/chain-id" ] mainnet = [ "wormhole-anchor-sdk/mainnet" ] solana-devnet = [ "wormhole-anchor-sdk/solana-devnet" ] tilt-devnet = [ "wormhole-anchor-sdk/tilt-devnet" ] @@ -34,6 +37,11 @@ solana-program.workspace = true wormhole-anchor-sdk.workspace = true wormhole-io.workspace = true wormhole-sdk.workspace = true +wormhole-svm-definitions.workspace = true + +cfg-if.workspace = true + +const-crypto = "0.3.0" [dev-dependencies] hex.workspace = true diff --git a/solana/programs/wormhole-governance/README.md b/solana/programs/wormhole-governance/README.md index 85a48e9cc..0ed16c94e 100644 --- a/solana/programs/wormhole-governance/README.md +++ b/solana/programs/wormhole-governance/README.md @@ -12,3 +12,24 @@ The program interacts with the Wormhole program, and as such, needs to be aware ```sh BRIDGE_ADDRESS=worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth cargo build-sbf --no-default-features --features bridge-address-from-env ``` + +## Verifiable build + +From the `solana` directory: + +1. Ensure that the cargo target directory is clean. Assuming that your target cargo directory is `target`: +``` +rm -r target/ +``` + +2. Use the following command: +```sh +solana-verify build \ + --library-name wormhole_governance -- \ + --no-default-features \ + --features bridge-address-from-env \ + --config 'env.BRIDGE_ADDRESS=""' \ + --config 'env.GOVERNANCE_PROGRAM_ID=""' +``` + +3. The build should be successful and you should see the program in `target/deploy/wormhole_governance.so`. diff --git a/solana/programs/wormhole-governance/src/instructions/governance.rs b/solana/programs/wormhole-governance/src/instructions/governance.rs index 8a1d772d0..36a5bb622 100644 --- a/solana/programs/wormhole-governance/src/instructions/governance.rs +++ b/solana/programs/wormhole-governance/src/instructions/governance.rs @@ -213,7 +213,7 @@ impl Readable for GovernanceMessage { )); } let chain: u16 = Readable::read(reader)?; - if Chain::from(chain) != Chain::Solana { + if chain != wormhole_svm_definitions::CHAIN_ID { return Err(io::Error::new( io::ErrorKind::InvalidData, "Invalid GovernanceMessage chain", diff --git a/solana/programs/wormhole-governance/src/lib.rs b/solana/programs/wormhole-governance/src/lib.rs index 5c85fce9e..5e303ac97 100644 --- a/solana/programs/wormhole-governance/src/lib.rs +++ b/solana/programs/wormhole-governance/src/lib.rs @@ -1,10 +1,20 @@ use anchor_lang::prelude::*; -declare_id!("wgvEiKVzX9yyEoh41jZAdC6JqGUTS4CFXbFGBV5TKdZ"); - pub mod error; pub mod instructions; +cfg_if::cfg_if! { + if #[cfg(feature = "bridge-address-from-env")] { + use wormhole_svm_definitions::env_pubkey; + pub const WORMHOLE_GOVERNANCE_ID_ARRAY: [u8; 32] = env_pubkey!("GOVERNANCE_PROGRAM_ID"); + } else { + use const_crypto::bs58; + pub const WORMHOLE_GOVERNANCE_ID_ARRAY: [u8; 32] = bs58::decode_pubkey("NGoD1yTeq5KaURrZo7MnCTFzTA4g62ygakJCnzMLCfm"); + } +} + +declare_id!(solana_program::pubkey::Pubkey::new_from_array(WORMHOLE_GOVERNANCE_ID_ARRAY)); + use instructions::*; #[program]