From acc51c68c351cd316f00a888c20989f22e790dad Mon Sep 17 00:00:00 2001 From: Baptiste Oueriagli Date: Wed, 22 Oct 2025 11:56:29 +0200 Subject: [PATCH 1/5] chore(solana): split patches, fix make command and generate mcm upgrade proposal --- solana/Makefile | 2 +- solana/devnet/2025-10-20-deploy-mcm/.env | 3 +- solana/devnet/2025-10-20-deploy-mcm/Makefile | 3 +- .../{patch/cb.patch => patches/eip712.patch} | 18 - .../patches/invoke_signed.patch | 18 + solana/devnet/2025-10-21-deploy-bridge/.env | 52 +++ .../2025-10-21-deploy-bridge/.gitignore | 2 + .../devnet/2025-10-21-deploy-bridge/Makefile | 138 ++++++ solana/devnet/2025-10-22-mcm-upgrade/.env | 68 +++ .../devnet/2025-10-22-mcm-upgrade/.gitignore | 1 + .../2025-10-22-mcm-upgrade/FACILITATORS.md | 156 +++++++ solana/devnet/2025-10-22-mcm-upgrade/Makefile | 79 ++++ .../devnet/2025-10-22-mcm-upgrade/README.md | 61 +++ .../set-buffer-authority-artifacts.json | 69 +++ .../patches/eip712.patch | 404 ++++++++++++++++++ .../2025-10-22-mcm-upgrade/patches/id.patch | 12 + .../patches/invoke_signed.patch | 18 + .../patches/simplify_eip712.patch | 74 ++++ .../2025-10-22-mcm-upgrade/proposal.json | 54 +++ .../template-mcm-program-upgrade/Makefile | 6 +- 20 files changed, 1214 insertions(+), 24 deletions(-) rename solana/devnet/2025-10-20-deploy-mcm/{patch/cb.patch => patches/eip712.patch} (94%) create mode 100644 solana/devnet/2025-10-20-deploy-mcm/patches/invoke_signed.patch create mode 100644 solana/devnet/2025-10-21-deploy-bridge/.env create mode 100644 solana/devnet/2025-10-21-deploy-bridge/.gitignore create mode 100644 solana/devnet/2025-10-21-deploy-bridge/Makefile create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/.env create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/.gitignore create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/FACILITATORS.md create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/Makefile create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/README.md create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/artifacts/set-buffer-authority-artifacts.json create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/patches/eip712.patch create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/patches/id.patch create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/patches/invoke_signed.patch create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/patches/simplify_eip712.patch create mode 100644 solana/devnet/2025-10-22-mcm-upgrade/proposal.json diff --git a/solana/Makefile b/solana/Makefile index 881fcb95..f3607daf 100644 --- a/solana/Makefile +++ b/solana/Makefile @@ -117,4 +117,4 @@ sol-write-buffer: sol-set-buffer-authority: solana program set-buffer-authority $(BUFFER) \ --new-buffer-authority $(NEW_BUFFER_AUTHORITY) \ - --url $(SOL_RPC_URL)" + --url $(SOL_RPC_URL) diff --git a/solana/devnet/2025-10-20-deploy-mcm/.env b/solana/devnet/2025-10-20-deploy-mcm/.env index 37368514..63d33830 100644 --- a/solana/devnet/2025-10-20-deploy-mcm/.env +++ b/solana/devnet/2025-10-20-deploy-mcm/.env @@ -4,7 +4,8 @@ ANCHOR_VERSION=0.29.0 # Variables for cloning the MCM repo MCM_REPO=https://github.com/smartcontractkit/chainlink-ccip.git MCM_AUDITED_COMMIT=0ee732e80586 -MCM_PROGRAM_PATCH=patch/cb.patch +INVOKE_SIGNED_PATCH=patches/invoke_signed.patch +EIP712_PATCH=patches/eip712.patch # Variables for deploying and sending transactions CLUSTER=devnet diff --git a/solana/devnet/2025-10-20-deploy-mcm/Makefile b/solana/devnet/2025-10-20-deploy-mcm/Makefile index 58ad4960..3ba2a793 100644 --- a/solana/devnet/2025-10-20-deploy-mcm/Makefile +++ b/solana/devnet/2025-10-20-deploy-mcm/Makefile @@ -34,7 +34,8 @@ step1-clone-and-patch: git clone --filter=blob:none $(MCM_REPO) chainlink-ccip cd chainlink-ccip && \ git checkout $(MCM_AUDITED_COMMIT) && \ - git apply ../$(MCM_PROGRAM_PATCH) + git apply ../$(INVOKE_SIGNED_PATCH) && \ + git apply ../$(EIP712_PATCH) # Step 2: Build and deploy the MCM program .PHONY: step2-build-and-deploy diff --git a/solana/devnet/2025-10-20-deploy-mcm/patch/cb.patch b/solana/devnet/2025-10-20-deploy-mcm/patches/eip712.patch similarity index 94% rename from solana/devnet/2025-10-20-deploy-mcm/patch/cb.patch rename to solana/devnet/2025-10-20-deploy-mcm/patches/eip712.patch index 96309990..97cc39d1 100644 --- a/solana/devnet/2025-10-20-deploy-mcm/patch/cb.patch +++ b/solana/devnet/2025-10-20-deploy-mcm/patches/eip712.patch @@ -349,24 +349,6 @@ index 7b030cda..eaf3b666 100644 mod test_hash_leaf { use super::*; -diff --git a/chains/solana/contracts/programs/mcm/src/instructions/execute.rs b/chains/solana/contracts/programs/mcm/src/instructions/execute.rs -index 155b1a71..b550e7fe 100644 ---- a/chains/solana/contracts/programs/mcm/src/instructions/execute.rs -+++ b/chains/solana/contracts/programs/mcm/src/instructions/execute.rs -@@ -93,6 +93,13 @@ pub fn execute<'info>( - - invoke_signed(&instruction, acc_infos, signer)?; - -+ // If the CPI modified any typed accounts present in this outer context -+ // (e.g., calling `accept_ownership` which updates `multisig_config`), -+ // reload them to avoid Anchor writing back the stale outer copy on exit. -+ ctx.accounts.multisig_config.reload()?; -+ ctx.accounts.root_metadata.reload()?; -+ ctx.accounts.expiring_root_and_op_count.reload()?; -+ - emit!(OpExecuted { - nonce, - to: instruction.program_id, diff --git a/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs b/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs index 5d5167d4..8cbce01c 100644 --- a/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs diff --git a/solana/devnet/2025-10-20-deploy-mcm/patches/invoke_signed.patch b/solana/devnet/2025-10-20-deploy-mcm/patches/invoke_signed.patch new file mode 100644 index 00000000..bd49aa6d --- /dev/null +++ b/solana/devnet/2025-10-20-deploy-mcm/patches/invoke_signed.patch @@ -0,0 +1,18 @@ +diff --git a/chains/solana/contracts/programs/mcm/src/instructions/execute.rs b/chains/solana/contracts/programs/mcm/src/instructions/execute.rs +index 155b1a71..b550e7fe 100644 +--- a/chains/solana/contracts/programs/mcm/src/instructions/execute.rs ++++ b/chains/solana/contracts/programs/mcm/src/instructions/execute.rs +@@ -93,6 +93,13 @@ pub fn execute<'info>( + + invoke_signed(&instruction, acc_infos, signer)?; + ++ // If the CPI modified any typed accounts present in this outer context ++ // (e.g., calling `accept_ownership` which updates `multisig_config`), ++ // reload them to avoid Anchor writing back the stale outer copy on exit. ++ ctx.accounts.multisig_config.reload()?; ++ ctx.accounts.root_metadata.reload()?; ++ ctx.accounts.expiring_root_and_op_count.reload()?; ++ + emit!(OpExecuted { + nonce, + to: instruction.program_id, diff --git a/solana/devnet/2025-10-21-deploy-bridge/.env b/solana/devnet/2025-10-21-deploy-bridge/.env new file mode 100644 index 00000000..5a85eaba --- /dev/null +++ b/solana/devnet/2025-10-21-deploy-bridge/.env @@ -0,0 +1,52 @@ +RUST_TOOLCHAIN=1.87.0 +SOLANA_VERSION=2.1.21 +ANCHOR_VERSION=0.31.0 + +# Variables for cloning Bridge repo +BRIDGE_REPO=https://github.com/base/bridge.git +BRIDGE_AUDITED_COMMIT=e9b5fe27280a + +# Variables for deploying and sending transactions +DEPLOY_ENV=testnet-alpha +DEPLOYER_KP=~/.config/solana/id.json + +# Signature of the deployWithMaxDataLen txs (used to generate the deployment artifacts) +BRIDGE_DEPLOY_SIGNATURE= +BASE_RELAYER_DEPLOY_SIGNATURE= + +# Variables for initializing the Bridge Solana program +BRIDGE_PROGRAM_ID= +GUARDIAN= +SOL_REMOTE_TOKEN= +BRIDGE_EIP1559_TARGET=5_000_000 +BRIDGE_EIP1559_DENOMINATOR=2 +BRIDGE_EIP1559_WINDOW_DURATION_SECONDS=1 +BRIDGE_EIP1559_MINIMUM_BASE_FEE=1 +BRIDGE_GAS_PER_CALL=125_000 +BRIDGE_GAS_COST_SCALER=1_000_000 +BRIDGE_GAS_COST_SCALER_DP=1_000_000 +BRIDGE_GAS_FEE_RECEIVER=2aWsUTRfJu2hPNUYHaYJjm6C3SVPqXjtoxu2VgBXsd72 # Deployer address +BRIDGE_PROTOCOL_BLOCK_INTERVAL_REQUIREMENT=300 +BRIDGE_BUFFER_MAX_CALL_BUFFER_SIZE=8192 # 8KB +BRIDGE_BASE_ORACLE_THRESHOLD=2 +BRIDGE_BASE_ORACLE_SIGNER_COUNT=2 +BRIDGE_BASE_ORACLE_SIGNERS=0x20BFBCCC8aBaD55c8aA383a75838348A646eDbA0,0xfc85de3f52047b993b2dda967b606a8b9caa2c29 +BRIDGE_PARTNER_ORACLE_REQUIRED_THRESHOLD=0 + +# Variables for initializing the Base Relayer Solana program +BASE_RELAYER_PROGRAM_ID= +GUARDIAN= +BASE_RELAYER_EIP1559_TARGET=5_000_000 +BASE_RELAYER_EIP1559_DENOMINATOR=2 +BASE_RELAYER_EIP1559_WINDOW_DURATION_SECONDS=1 +BASE_RELAYER_EIP1559_MINIMUM_BASE_FEE=1 +BASE_RELAYER_MIN_GAS_LIMIT_PER_MESSAGE=100_000 +BASE_RELAYER_MAX_GAS_LIMIT_PER_MESSAGE=5_000_000 +BASE_RELAYER_GAS_COST_SCALER=1_000_000 +BASE_RELAYER_GAS_COST_SCALER_DP=1_000_000 +BASE_RELAYER_GAS_FEE_RECEIVER=2aWsUTRfJu2hPNUYHaYJjm6C3SVPqXjtoxu2VgBXsd72 # Deployer address + +# Variables for printing the SOL vault PDA +AUTHORITY=$(DEPLOYER_KP) +SOL_RECIPIENT= # SOL vault PDA +SOL_AMOUNT=0.1 \ No newline at end of file diff --git a/solana/devnet/2025-10-21-deploy-bridge/.gitignore b/solana/devnet/2025-10-21-deploy-bridge/.gitignore new file mode 100644 index 00000000..d4dc7f7b --- /dev/null +++ b/solana/devnet/2025-10-21-deploy-bridge/.gitignore @@ -0,0 +1,2 @@ +bridge/ +*keypair.json diff --git a/solana/devnet/2025-10-21-deploy-bridge/Makefile b/solana/devnet/2025-10-21-deploy-bridge/Makefile new file mode 100644 index 00000000..93e23389 --- /dev/null +++ b/solana/devnet/2025-10-21-deploy-bridge/Makefile @@ -0,0 +1,138 @@ +include ../.env +include .env + +include ../../Makefile + +.PHONY: install-rust +install-rust: + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + rustup toolchain install $(RUST_TOOLCHAIN) && \ + rustup default $(RUST_TOOLCHAIN) + +.PHONY: install-solana-cli +install-solana-cli: + curl -sSfL https://release.anza.xyz/v$(SOLANA_VERSION)/install | sh + +.PHONY: install-anchor +install-anchor: + cargo install --git https://github.com/coral-xyz/anchor --tag v$(ANCHOR_VERSION) anchor-cli --force + +.PHONY: install-bun +install-bun: + curl -fsSL https://bun.sh/install | bash + +.PHONY: deps +deps: install-rust install-solana-cli install-anchor install-bun + +## +# Deployment Workflow +## + +# Step 1: Clone and build bridge programs +.PHONY: step1-clone-bridge +step1-clone-bridge: + @echo "==> Step 1: Cloning and building bridge programs..." + rm -rf bridge + git clone --filter=blob:none $(BRIDGE_REPO) bridge + cd bridge && \ + git checkout $(BRIDGE_AUDITED_COMMIT) + cd bridge/clients/ts && \ + bun install && \ + bun run build + cd bridge/scripts && \ + bun install + +# Step 2: Build programs +.PHONY: step2-build-programs +step2-build-programs: + @echo "==> Step 2: Building bridge programs..." + cd bridge/solana && \ + anchor keys sync && \ + cp target/deploy/bridge-keypair.json ../../bridge-keypair.json && \ + cp target/deploy/base_relayer-keypair.json ../../base_relayer-keypair.json + cd bridge/scripts && \ + bun cli sol build \ + --deploy-env $(DEPLOY_ENV) \ + --bridge-program-kp ../../bridge-keypair.json \ + --base-relayer-program-kp ../../base_relayer-keypair.json + +# Step 3: Deploy programs +.PHONY: step3-deploy-programs +step3-deploy-programs: + @echo "==> Step 3: Deploying programs..." + make sol-confirm-cmd \ + cmd="cd bridge/scripts && \ + bun cli sol deploy \ + --deploy-env $(DEPLOY_ENV) \ + --program bridge \ + --program-kp ../../bridge-keypair.json \ + --deployer-kp $(DEPLOYER_KP)" \ + output=artifacts/bridge-deploy-artifacts.json + make sol-confirm-cmd \ + cmd="cd bridge/scripts && \ + bun cli sol deploy \ + --deploy-env $(DEPLOY_ENV) \ + --program base-relayer \ + --program-kp ../../base_relayer-keypair.json \ + --deployer-kp $(DEPLOYER_KP)" \ + output=artifacts/base-relayer-deploy-artifacts.json + +# Step 4: Initialize programs +.PHONY: step4-init-programs +step4-init-programs: + @echo "==> Step 4: Initializing programs..." + make sol-confirm-cmd \ + cmd="cd bridge/scripts && \ + bun cli sol bridge initialize \ + --program-id $(BRIDGE_PROGRAM_ID) \ + --rpc-url $(SOL_RPC_URL) \ + --payer-kp $(DEPLOYER_KP) \ + --guardian $(GUARDIAN) \ + --remote-sol-address $(SOL_REMOTE_TOKEN) \ + --eip1559-target $(BRIDGE_EIP1559_TARGET) \ + --eip1559-denominator $(BRIDGE_EIP1559_DENOMINATOR) \ + --eip1559-window-duration-seconds $(BRIDGE_EIP1559_WINDOW_DURATION_SECONDS) \ + --eip1559-minimum-base-fee $(BRIDGE_EIP1559_MINIMUM_BASE_FEE) \ + --gas-per-call $(BRIDGE_GAS_PER_CALL) \ + --gas-cost-scaler $(BRIDGE_GAS_COST_SCALER) \ + --gas-cost-scaler-dp $(BRIDGE_GAS_COST_SCALER_DP) \ + --gas-fee-receiver $(BRIDGE_GAS_FEE_RECEIVER) \ + --protocol-block-interval-requirement $(BRIDGE_PROTOCOL_BLOCK_INTERVAL_REQUIREMENT) \ + --buffer-max-call-buffer-size $(BRIDGE_BUFFER_MAX_CALL_BUFFER_SIZE) \ + --base-oracle-threshold $(BRIDGE_BASE_ORACLE_THRESHOLD) \ + --base-oracle-signer-count $(BRIDGE_BASE_ORACLE_SIGNER_COUNT) \ + --base-oracle-signers $(BRIDGE_BASE_ORACLE_SIGNERS) \ + --partner-oracle-required-threshold $(BRIDGE_PARTNER_ORACLE_REQUIRED_THRESHOLD)" + output=artifacts/init-bridge.json + make sol-confirm-cmd \ + cmd="cd bridge/scripts && \ + bun cli sol base-relayer initialize \ + --program-id $(BASE_RELAYER_PROGRAM_ID) \ + --rpc-url $(SOL_RPC_URL) \ + --payer-kp $(DEPLOYER_KP) \ + --guardian $(GUARDIAN) \ + --eip1559-target $(BASE_RELAYER_EIP1559_TARGET) \ + --eip1559-denominator $(BASE_RELAYER_EIP1559_DENOMINATOR) \ + --eip1559-window-duration-seconds $(BASE_RELAYER_EIP1559_WINDOW_DURATION_SECONDS) \ + --eip1559-minimum-base-fee $(BASE_RELAYER_EIP1559_MINIMUM_BASE_FEE) \ + --min-gas-limit-per-message $(BASE_RELAYER_MIN_GAS_LIMIT_PER_MESSAGE) \ + --max-gas-limit-per-message $(BASE_RELAYER_MAX_GAS_LIMIT_PER_MESSAGE) \ + --gas-cost-scaler $(BASE_RELAYER_GAS_COST_SCALER) \ + --gas-cost-scaler-dp $(BASE_RELAYER_GAS_COST_SCALER_DP) \ + --gas-fee-receiver $(BASE_RELAYER_GAS_FEE_RECEIVER)" \ + output=artifacts/init-base-relayer.json + +# Step 5: Print SOL vault +.PHONY: step5-print-sol-vault +step5-print-sol-vault: + @echo "==> Step 5: Printing SOL vault..." + cd bridge/scripts && \ + bun cli sol bridge sol-vault \ + --bridge-program $(BRIDGE_PROGRAM_ID) \ + --remote-token $(SOL_REMOTE_TOKEN) + +# Step 6: Print SOL vault and fund it +.PHONY: step6-fund-sol-vault +step6-fund-sol-vault: + @echo "==> Step 6: Funding SOL vault..." + make sol-transfer output=artifacts/fund-sol-vault.json diff --git a/solana/devnet/2025-10-22-mcm-upgrade/.env b/solana/devnet/2025-10-22-mcm-upgrade/.env new file mode 100644 index 00000000..71474061 --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/.env @@ -0,0 +1,68 @@ +SOLANA_VERSION=1.17.25 +ANCHOR_VERSION=0.29.0 + +# Variables for cloning the MCM repo +MCM_REPO=https://github.com/smartcontractkit/chainlink-ccip.git +MCM_AUDITED_COMMIT=0ee732e80586 +INVOKE_SIGNED_PATCH=patches/invoke_signed.patch +EIP712_PATCH=patches/eip712.patch +SIMPLIFY_EIP712_PATCH=patches/simplify_eip712.patch +ID_PATCH=patches/id.patch + +## +# Common MCM Proposal Variables +## + +# MCM program ID (base58 encoded) +MCM_PROGRAM_ID=Ea5qsVs2kwQXi7LMAh3Qg5YKpuPQbW7oqvYaPXUwBwoX + +# MCM multisig ID (32 bytes hex with 0x prefix) +MCM_MULTISIG_ID=0x0000000000000000000000000000000000000000000000000000000000000000 + +# MCM authority address (derived PDA, base58 encoded) +MCM_AUTHORITY=2N9nKVFPHsBVazniiYNxECB2nPLcwjk3dHAJH9tkr2xQ + +# Timestamp until which the proposal is valid and can be executed +MCM_VALID_UNTIL=1761205094 # Thu Oct 23 2025 07:38:14 GMT+0000 + +# Set to true if you want to override the previous root when registering the proposal +# If set to true, it allows to override the (proposal) root even if there are outstanding operations left to execute in the previous proposal +MCM_OVERRIDE_PREVIOUS_ROOT=false + +# Total number of signatures required to execute the proposal +MCM_SIGNATURES_COUNT=3 + +# MCM proposal signatures +MCM_SIGNATURES= + +# Output file for the MCM proposal +MCM_PROPOSAL_OUTPUT=proposal.json + +# Authority account that will sign and submit MCM transactions +AUTHORITY=~/.config/solana/id.json + +## +# MCM Proposal Variables Specific to Program Upgrade +## + +# Program address to upgrade +PROGRAM=$(MCM_PROGRAM_ID) + +# Buffer address containing the new program data (obtained from 'make write-buffer') +BUFFER=C4XSxK5zGw2ANbWQSURn6Cx4HaFiCxpmD6Nbb4T6szNu + +# Spill account address to receive refunded lamports +SPILL=2aWsUTRfJu2hPNUYHaYJjm6C3SVPqXjtoxu2VgBXsd72 # Payer address + +## +# Variables for managing the temporary buffer +## + +# Path to the compiled program binary (.so file) +PROGRAM_BINARY=./chainlink-ccip/chains/solana/contracts/target/deploy/mcm.so + +# New buffer authority (MCM authority) +NEW_BUFFER_AUTHORITY=$(MCM_AUTHORITY) + +# Signature of the set-buffer-authority transaction +SET_BUFFER_AUTHORITY_SIGNATURE=5MH2pXrEmvoUepzVNfqWzJ5mVXyJn4T9b81yqFaK8T8QzW8quRAXoaATWqtaaaVXJ7hzmywaHFDwcAHj7EQ5EFEs \ No newline at end of file diff --git a/solana/devnet/2025-10-22-mcm-upgrade/.gitignore b/solana/devnet/2025-10-22-mcm-upgrade/.gitignore new file mode 100644 index 00000000..6164bc8d --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/.gitignore @@ -0,0 +1 @@ +chainlink-ccip/ diff --git a/solana/devnet/2025-10-22-mcm-upgrade/FACILITATORS.md b/solana/devnet/2025-10-22-mcm-upgrade/FACILITATORS.md new file mode 100644 index 00000000..278a34fb --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/FACILITATORS.md @@ -0,0 +1,156 @@ +# Facilitator Instructions: MCM Program Upgrade + +## Overview + +As a Facilitator, you are responsible for: +1. Preparing the program buffer +2. Creating the MCM proposal +3. Committing and pushing the proposal to the repo +4. Coordinating with Signers +5. Collecting signatures +6. Executing the proposal on-chain + +## Prerequisites + +```bash +cd contract-deployments +git pull +cd solana// +make deps +``` + +Ensure you have: +- Solana CLI installed and configured +- `mcmctl` installed (via `make deps`) +- `eip712sign` installed (via `make deps`) +- A funded Solana wallet configured +- The compiled program binary (`.so` file) ready + +## Phase 1: Prepare the Program Buffer + +### 1.1. Obtain the compiled program + +Get the compiled program binary (`.so` file) that contains the new version of the program. + +### 1.2. Update .env for buffer upload + +Set the following in `.env`: + +```bash +PROGRAM= +PROGRAM_BINARY= +``` + +### 1.3. Write buffer + +```bash +make write-buffer +``` + +This will output a buffer address. Copy it. + +### 1.4. Update .env and transfer buffer authority + +Set the following in `.env`: + +```bash +BUFFER= +MCM_AUTHORITY= +SPILL= +``` + +Then transfer buffer authority to MCM: + +```bash +make transfer-buffer +``` + +The buffer is now controlled by the MCM authority. + +## Phase 2: Create and Commit MCM Proposal + +### 2.1. Update .env for proposal generation + +Set the following in `.env`: + +```bash +MCM_PROGRAM_ID= +MCM_MULTISIG_ID= +MCM_VALID_UNTIL= +MCM_OVERRIDE_PREVIOUS_ROOT=false # or true if needed +MCM_PROPOSAL_OUTPUT=proposal.json +``` + +### 2.2. Generate proposal + +```bash +make mcm-proposal +``` + +This creates the proposal file (default `proposal.json` or whatever is set in `MCM_PROPOSAL_OUTPUT`). + +### 2.3. Review proposal + +Open and review the generated proposal file to verify: +- Program address matches `PROGRAM` +- Buffer address matches `BUFFER` +- Spill address is correct +- Valid until timestamp is appropriate +- All instructions are correct + +### 2.4. Commit and push + +```bash +git add . +git commit -m "Add MCM program upgrade proposal for " +git push +``` + +## Phase 3: Coordinate with Signers and Collect Signatures + +Coordinate with Signers to collect their signatures. Each Signer will run `make mcm-sign` and provide their signature. + +Concatenate all signatures in the format: `0xSIG1,0xSIG2,0xSIG3` + +Once you have all required signatures, update `.env`: + +```bash +MCM_SIGNATURES_COUNT= +MCM_SIGNATURES=0xSIG1,0xSIG2,0xSIG3 +``` + +## Phase 4: Execute Proposal + +```bash +make mcm-all +``` + +This command executes all the necessary steps: +- Initialize signatures account +- Append signatures +- Finalize signatures +- Set root +- Execute proposal + +## Phase 5: Verification + +### 5.1. Verify program on Solana Explorer + +Visit the Solana Explorer for your network: +- Mainnet: https://explorer.solana.com/ +- Devnet: https://explorer.solana.com/?cluster=devnet + +Search for the program address (`$PROGRAM`) and verify: +- The "Last Deployed Slot" is recent +- The upgrade authority is still `MCM_AUTHORITY` +- The execution transaction is visible in history + +### 5.2. Update README + +Update the Status line in README.md to: + +```markdown +Status: [EXECUTED](https://explorer.solana.com/tx/?cluster=) +``` + +Replace `` with the execution transaction signature and `` with the appropriate cluster (devnet, mainnet-beta, etc.). diff --git a/solana/devnet/2025-10-22-mcm-upgrade/Makefile b/solana/devnet/2025-10-22-mcm-upgrade/Makefile new file mode 100644 index 00000000..531d4a43 --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/Makefile @@ -0,0 +1,79 @@ +include ../.env +include .env + +include ../../Makefile + +## +# Project Setup +## + +.PHONY: install-rust +install-rust: + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y + +.PHONY: install-solana-cli +install-solana-cli: + curl -sSfL https://release.anza.xyz/v$(SOLANA_VERSION)/install | sh + +.PHONY: install-anchor +install-anchor: + cargo install --git https://github.com/coral-xyz/anchor --tag v$(ANCHOR_VERSION) anchor-cli --force + +.PHONY: deps +deps: install-rust install-solana-cli install-anchor + +## +# MCM Program Upgrade Workflow +## + +# Step 1: Clone and patch the MCM program +.PHONY: step1-clone-patch-and-build +step1-clone-patch-and-build: + @echo "==> Step 1: Cloning and patching the MCM program..." + rm -rf chainlink-ccip + git clone --filter=blob:none $(MCM_REPO) chainlink-ccip + cd chainlink-ccip && \ + git checkout $(MCM_AUDITED_COMMIT) && \ + git apply ../$(INVOKE_SIGNED_PATCH) && \ + git apply ../$(EIP712_PATCH) && \ + git apply ../$(SIMPLIFY_EIP712_PATCH) && \ + git apply ../$(ID_PATCH) + cd chainlink-ccip/chains/solana/contracts && \ + anchor build -p mcm + +# Step 2: Write program buffer +.PHONY: step2-write-buffer +step2-write-buffer: + @echo "==> Step 2: Writing program buffer..." + make sol-write-buffer + +# Step 3: Transfer buffer authority to MCM +.PHONY: step3-transfer-buffer +step3-transfer-buffer: + @echo "==> Step 2: Transferring buffer authority to MCM..." + make sol-set-buffer-authority + +# Step 3.5: Generate set-buffer-authority artifacts (use solana explorer to get the signature of the set-buffer-authority tx) +.PHONY: step3.5-generate-set-buffer-authority-artifacts +step3.5-generate-set-buffer-authority-artifacts: + @echo "==> Step 3.5: Generating MCM set-buffer-authority artifacts..." + make sol-confirm SIG=$(SET_BUFFER_AUTHORITY_SIGNATURE) output=artifacts/set-buffer-authority-artifacts.json + +# Step 4: Create upgrade proposal +.PHONY: step4-create-proposal +step4-create-proposal: + @echo "==> Step 4: Creating MCM upgrade proposal..." + make mcm-proposal-loader-v3-upgrade + +# Step 5: Sign proposal +.PHONY: sign +sign: + @echo "==> Step 5: Signing proposal..." + make mcm-sign + +# Step 6: Execute proposal (signatures + set-root + execute) +.PHONY: step6-execute-proposal +step5-execute-proposal: + @echo "==> Step 6: Executing MCM proposal..." + make mcm-signatures-all + make mcm-proposal-all diff --git a/solana/devnet/2025-10-22-mcm-upgrade/README.md b/solana/devnet/2025-10-22-mcm-upgrade/README.md new file mode 100644 index 00000000..5faa61d3 --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/README.md @@ -0,0 +1,61 @@ +# MCM Program Upgrade via Multi-Chain Multisig + +Status: PENDING + +## Description + +This task upgrades a Solana program using the Multi-Chain Multisig (MCM) governance system. The upgrade is performed by uploading a new program buffer and creating an MCM proposal that, once signed and executed, will upgrade the program to the new version. + +## Procedure for Signers + +### 1. Update repo + +```bash +cd contract-deployments +git pull +cd solana// +make deps +``` + +### 2. Setup Ledger + +Your Ledger needs to be connected and unlocked. The **Ethereum application** needs to be opened on Ledger with the message "Application is ready". + +### 3. Review the proposal + +The Facilitator will provide you with the proposal details. Review: +- Program being upgraded: `PROGRAM` +- Buffer address: `BUFFER` +- MCM Program ID: `MCM_PROGRAM_ID` +- Valid until timestamp: `MCM_VALID_UNTIL` + +These values are in the `.env` file and the generated `proposal.json`. + +### 4. Sign the proposal + +```bash +make mcm-sign +``` + +This command will: +1. Display the proposal hash +2. Prompt you to sign on your Ledger +3. Output your signature + +**Verify on your Ledger**: Check that the data you're signing matches the proposal hash displayed in the terminal. + +After signing, you will see output like: + +``` +Signature: 1234567890abcdef... +``` + +### 5. Send signature to Facilitator + +Copy the **entire signature** and send it to the Facilitator via your secure communication channel. + +**That's it!** The Facilitator will collect all signatures and execute the proposal. + +## For Facilitators + +See [FACILITATORS.md](./FACILITATORS.md) for complete instructions on preparing, executing, and verifying this task. diff --git a/solana/devnet/2025-10-22-mcm-upgrade/artifacts/set-buffer-authority-artifacts.json b/solana/devnet/2025-10-22-mcm-upgrade/artifacts/set-buffer-authority-artifacts.json new file mode 100644 index 00000000..721e4dfe --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/artifacts/set-buffer-authority-artifacts.json @@ -0,0 +1,69 @@ +{ + "confirmationStatus": "finalized", + "transaction": { + "signatures": [ + "5MH2pXrEmvoUepzVNfqWzJ5mVXyJn4T9b81yqFaK8T8QzW8quRAXoaATWqtaaaVXJ7hzmywaHFDwcAHj7EQ5EFEs" + ], + "message": { + "header": { + "numRequiredSignatures": 1, + "numReadonlySignedAccounts": 0, + "numReadonlyUnsignedAccounts": 2 + }, + "accountKeys": [ + "2aWsUTRfJu2hPNUYHaYJjm6C3SVPqXjtoxu2VgBXsd72", + "C4XSxK5zGw2ANbWQSURn6Cx4HaFiCxpmD6Nbb4T6szNu", + "BPFLoaderUpgradeab1e11111111111111111111111", + "2N9nKVFPHsBVazniiYNxECB2nPLcwjk3dHAJH9tkr2xQ" + ], + "recentBlockhash": "4qMgkLQCjsdT7VePSKroogZwptPehuzyLkeC5SrMGBY2", + "instructions": [ + { + "programIdIndex": 2, + "accounts": [ + 1, + 0, + 3 + ], + "data": "6vx8P", + "stackHeight": null + } + ] + } + }, + "meta": { + "err": null, + "status": { + "Ok": null + }, + "fee": 5000, + "preBalances": [ + 201171174280, + 3335489520, + 1, + 0 + ], + "postBalances": [ + 201171169280, + 3335489520, + 1, + 0 + ], + "innerInstructions": [], + "logMessages": [ + "Program BPFLoaderUpgradeab1e11111111111111111111111 invoke [1]", + "New authority Some(2N9nKVFPHsBVazniiYNxECB2nPLcwjk3dHAJH9tkr2xQ)", + "Program BPFLoaderUpgradeab1e11111111111111111111111 success" + ], + "preTokenBalances": [], + "postTokenBalances": [], + "rewards": [], + "loadedAddresses": { + "writable": [], + "readonly": [] + }, + "computeUnitsConsumed": 2370 + }, + "blockTime": 1761126754, + "slot": 416301211 +} diff --git a/solana/devnet/2025-10-22-mcm-upgrade/patches/eip712.patch b/solana/devnet/2025-10-22-mcm-upgrade/patches/eip712.patch new file mode 100644 index 00000000..97cc39d1 --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/patches/eip712.patch @@ -0,0 +1,404 @@ +diff --git a/chains/solana/contracts/programs/mcm/src/eip712.rs b/chains/solana/contracts/programs/mcm/src/eip712.rs +new file mode 100644 +index 00000000..87b20002 +--- /dev/null ++++ b/chains/solana/contracts/programs/mcm/src/eip712.rs +@@ -0,0 +1,268 @@ ++//! # EIP-712 Typed Structured Data Hashing ++//! ++//! This module implements EIP-712 typed structured data hashing for Ethereum-compatible ++//! signature verification. ++//! ++//! ## Domain Separator ++//! ++//! The domain separator uniquely identifies this contract instance and prevents ++//! signature replay attacks across different chains or contracts: ++//! ++//! - **name**: "ManyChainMultiSig" - The contract name ++//! - **version**: "1" - The contract version ++//! - **chainId**: Solana chain identifier (e.g., hash of "solana:localnet") ++//! - **verifyingContract**: `address(0)` - Not applicable for Solana ++//! - **salt**: The Solana Program ID (32 bytes) - Uniquely identifies this program instance ++//! ++//! ## Message Structure ++//! ++//! Users sign a `RootValidation` message containing: ++//! - **root**: The 32-byte Merkle root of operations ++//! - **validUntil**: Timestamp (uint32) until which the root is valid ++ ++use anchor_lang::prelude::*; ++use anchor_lang::solana_program::keccak::{hashv, Hash, HASH_BYTES}; ++ ++/// EIP-712 domain name for the ManyChainMultiSig contract ++pub const EIP712_DOMAIN_NAME: &str = "ManyChainMultiSig"; ++ ++/// EIP-712 domain version ++pub const EIP712_DOMAIN_VERSION: &str = "1"; ++ ++/// Type hash for EIP712Domain ++/// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)") ++pub const EIP712_DOMAIN_TYPE_HASH: &[u8; HASH_BYTES] = &[ ++ 0xd8, 0x7c, 0xd6, 0xef, 0x79, 0xd4, 0xe2, 0xb9, 0x5e, 0x15, 0xce, 0x8a, 0xbf, 0x73, 0x2d, 0xb5, ++ 0x1e, 0xc7, 0x71, 0xf1, 0xca, 0x2e, 0xdc, 0xcf, 0x22, 0xa4, 0x6c, 0x72, 0x9a, 0xc5, 0x64, 0x72, ++]; ++ ++/// Type hash for RootValidation message ++/// keccak256("RootValidation(bytes32 root,uint32 validUntil)") ++pub const ROOT_VALIDATION_TYPE_HASH: &[u8; HASH_BYTES] = &[ ++ 0x07, 0x02, 0x19, 0xd6, 0x56, 0xf4, 0x72, 0x71, 0xfe, 0x6e, 0x2e, 0x8c, 0xff, 0x49, 0x55, 0xfe, ++ 0xcb, 0xca, 0xb4, 0xc9, 0x42, 0x22, 0x83, 0x09, 0xe9, 0x0a, 0xae, 0x20, 0xbd, 0x1f, 0xa8, 0x95, ++]; ++ ++/// Hash of the domain name "ManyChainMultiSig" ++/// keccak256("ManyChainMultiSig") ++pub const EIP712_DOMAIN_NAME_HASH: &[u8; HASH_BYTES] = &[ ++ 0x62, 0x7b, 0x9a, 0x5c, 0xae, 0x29, 0x68, 0x84, 0x2a, 0x54, 0x1a, 0x6b, 0xa8, 0x61, 0xa7, 0x15, ++ 0x0a, 0xb5, 0xec, 0x3c, 0x42, 0x02, 0xa6, 0x24, 0xbc, 0xa4, 0x6b, 0xff, 0x04, 0x11, 0xce, 0x9a, ++]; ++ ++/// Hash of the domain version "1" ++/// keccak256("1") ++pub const EIP712_DOMAIN_VERSION_HASH: &[u8; HASH_BYTES] = &[ ++ 0xc8, 0x9e, 0xfd, 0xaa, 0x54, 0xc0, 0xf2, 0x0c, 0x7a, 0xdf, 0x61, 0x28, 0x82, 0xdf, 0x09, 0x50, ++ 0xf5, 0xa9, 0x51, 0x63, 0x7e, 0x03, 0x07, 0xcd, 0xcb, 0x4c, 0x67, 0x2f, 0x29, 0x8b, 0x8b, 0xc6, ++]; ++ ++/// Computes the EIP-712 message hash for root validation. ++/// ++/// This is the final hash that gets signed by the user. It follows the EIP-712 ++/// format: `keccak256("\x19\x01" || domainSeparator || structHash)` ++/// ++/// # Parameters ++/// ++/// - `root`: The 32-byte Merkle root ++/// - `valid_until`: Timestamp until which the root is valid ++/// - `chain_id`: The chain identifier ++/// - `program_id`: The Solana Program ID ++/// ++/// # Returns ++/// ++/// - The 32-byte message hash ready for ECDSA verification ++pub fn compute_message_hash( ++ root: &[u8; HASH_BYTES], ++ valid_until: u32, ++ chain_id: u64, ++ program_id: &Pubkey, ++) -> Hash { ++ let domain_separator = compute_domain_hash(chain_id, program_id); ++ let struct_hash = compute_struct_hash(root, valid_until); ++ ++ hashv(&[ ++ b"\x19\x01", ++ &domain_separator.to_bytes(), ++ &struct_hash.to_bytes(), ++ ]) ++} ++ ++/// Computes the EIP-712 domain separator hash. ++/// ++/// The domain separator ensures that signatures are unique to this specific ++/// contract instance and chain, preventing replay attacks. ++/// ++/// # Parameters ++/// ++/// - `chain_id`: The chain identifier (e.g., keccak256("solana:localnet")) ++/// - `program_id`: The Solana Program ID (32 bytes), used as the salt ++/// ++/// # Returns ++/// ++/// - The 32-byte domain separator hash ++pub fn compute_domain_hash(chain_id: u64, program_id: &Pubkey) -> Hash { ++ // Chain ID as bytes32 (big-endian, left-padded) ++ let chain_id_bytes = left_pad_to_32(&chain_id.to_be_bytes()); ++ ++ // Verifying contract = address(0) = 20 zero bytes, left-padded to 32 bytes ++ let verifying_contract = [0u8; 32]; ++ ++ // Salt = Program ID (32 bytes) ++ let salt = program_id.to_bytes(); ++ ++ hashv(&[ ++ EIP712_DOMAIN_TYPE_HASH, ++ EIP712_DOMAIN_NAME_HASH, ++ EIP712_DOMAIN_VERSION_HASH, ++ &chain_id_bytes, ++ &verifying_contract, ++ &salt, ++ ]) ++} ++ ++/// Computes the EIP-712 struct hash for a RootValidation message. ++/// ++/// # Parameters ++/// ++/// - `root`: The 32-byte Merkle root ++/// - `valid_until`: Timestamp until which the root is valid ++/// ++/// # Returns ++/// ++/// - The 32-byte struct hash ++pub fn compute_struct_hash(root: &[u8; HASH_BYTES], valid_until: u32) -> Hash { ++ // valid_until as bytes32 (big-endian, left-padded) ++ let valid_until_bytes = left_pad_to_32(&valid_until.to_be_bytes()); ++ ++ hashv(&[ROOT_VALIDATION_TYPE_HASH, root, &valid_until_bytes]) ++} ++ ++/// Left-pads a byte array to 32 bytes with zeros. ++/// ++/// # Parameters ++/// ++/// - `input`: The input byte array ++/// ++/// # Returns ++/// ++/// - A 32-byte array with the input left-padded with zeros ++fn left_pad_to_32(input: &[u8]) -> [u8; 32] { ++ let mut padded = [0u8; 32]; ++ let start = 32 - input.len(); ++ padded[start..].copy_from_slice(input); ++ padded ++} ++ ++#[cfg(test)] ++mod tests { ++ use super::*; ++ use anchor_lang::solana_program::keccak::hash; ++ ++ // Last 8 bytes of keccak256("solana:localnet") as big-endian ++ const CHAIN_ID: u64 = 5190648258797659666; ++ ++ fn decode32(s: &str) -> [u8; HASH_BYTES] { ++ hex::decode(s).unwrap().try_into().unwrap() ++ } ++ ++ #[test] ++ fn verify_domain_type_hash() { ++ let expected = hash(b"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"); ++ assert_eq!(&expected.to_bytes(), EIP712_DOMAIN_TYPE_HASH); ++ } ++ ++ #[test] ++ fn verify_domain_name_hash() { ++ let expected = hash(EIP712_DOMAIN_NAME.as_bytes()); ++ assert_eq!(&expected.to_bytes(), EIP712_DOMAIN_NAME_HASH); ++ } ++ ++ #[test] ++ fn verify_domain_version_hash() { ++ let expected = hash(EIP712_DOMAIN_VERSION.as_bytes()); ++ assert_eq!(&expected.to_bytes(), EIP712_DOMAIN_VERSION_HASH); ++ } ++ ++ #[test] ++ fn verify_root_validation_type_hash() { ++ let expected = hash(b"RootValidation(bytes32 root,uint32 validUntil)"); ++ assert_eq!(&expected.to_bytes(), ROOT_VALIDATION_TYPE_HASH); ++ } ++ ++ #[test] ++ fn test_left_pad_to_32() { ++ let input = [1, 2, 3, 4]; ++ let result = left_pad_to_32(&input); ++ let expected: [u8; 32] = [ ++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, ++ 2, 3, 4, ++ ]; ++ assert_eq!(result, expected); ++ } ++ ++ #[test] ++ fn test_compute_domain_hash() { ++ let program_id = Pubkey::new_from_array(decode32( ++ "b870e12dd379891561d2e9fa8f26431834eb736f2f24fc2a2a4dff1fd5dca4df", ++ )); ++ ++ let domain_hash = compute_domain_hash(CHAIN_ID, &program_id); ++ ++ // The domain hash should be deterministic ++ // We'll verify it matches the expected structure by recomputing ++ let name_hash = hash(EIP712_DOMAIN_NAME.as_bytes()); ++ let version_hash = hash(EIP712_DOMAIN_VERSION.as_bytes()); ++ let chain_id_bytes = left_pad_to_32(&CHAIN_ID.to_be_bytes()); ++ let verifying_contract = [0u8; 32]; ++ let salt = program_id.to_bytes(); ++ ++ let expected = hashv(&[ ++ EIP712_DOMAIN_TYPE_HASH, ++ &name_hash.to_bytes(), ++ &version_hash.to_bytes(), ++ &chain_id_bytes, ++ &verifying_contract, ++ &salt, ++ ]); ++ ++ assert_eq!(domain_hash.to_bytes(), expected.to_bytes()); ++ } ++ ++ #[test] ++ fn test_compute_struct_hash() { ++ let root = decode32("d5ef592d1ad183db43b4980d7ab7ee43a6f6a284988c3e3a23d38c07beb520c7"); ++ let valid_until: u32 = 1748317727; ++ ++ let struct_hash = compute_struct_hash(&root, valid_until); ++ ++ // Verify the structure ++ let valid_until_bytes = left_pad_to_32(&valid_until.to_be_bytes()); ++ let expected = hashv(&[ROOT_VALIDATION_TYPE_HASH, &root, &valid_until_bytes]); ++ ++ assert_eq!(struct_hash.to_bytes(), expected.to_bytes()); ++ } ++ ++ #[test] ++ fn test_compute_message_hash() { ++ let root = decode32("d5ef592d1ad183db43b4980d7ab7ee43a6f6a284988c3e3a23d38c07beb520c7"); ++ let valid_until: u32 = 1748317727; ++ let program_id = Pubkey::new_from_array(decode32( ++ "b870e12dd379891561d2e9fa8f26431834eb736f2f24fc2a2a4dff1fd5dca4df", ++ )); ++ ++ let message_hash = compute_message_hash(&root, valid_until, CHAIN_ID, &program_id); ++ ++ // Verify it follows EIP-712 format: \x19\x01 || domainSeparator || structHash ++ let domain_separator = compute_domain_hash(CHAIN_ID, &program_id); ++ let struct_hash = compute_struct_hash(&root, valid_until); ++ ++ let expected = hashv(&[ ++ b"\x19\x01", ++ &domain_separator.to_bytes(), ++ &struct_hash.to_bytes(), ++ ]); ++ ++ assert_eq!(message_hash.to_bytes(), expected.to_bytes()); ++ } ++} +diff --git a/chains/solana/contracts/programs/mcm/src/eth_utils.rs b/chains/solana/contracts/programs/mcm/src/eth_utils.rs +index 7b030cda..eaf3b666 100644 +--- a/chains/solana/contracts/programs/mcm/src/eth_utils.rs ++++ b/chains/solana/contracts/programs/mcm/src/eth_utils.rs +@@ -14,7 +14,7 @@ + //! + //! These separators ensure that hashes for different purposes cannot be reused or confused. + use anchor_lang::prelude::*; +-use anchor_lang::solana_program::keccak::{hash, hashv, Hash, HASH_BYTES}; // use keccak256 for EVM compatibility ++use anchor_lang::solana_program::keccak::{hash, hashv, HASH_BYTES}; // use keccak256 for EVM compatibility + use anchor_lang::solana_program::secp256k1_recover::{ + secp256k1_recover, Secp256k1Pubkey, Secp256k1RecoverError, + }; +@@ -54,7 +54,7 @@ pub const EVM_ADDRESS_BYTES: usize = 20; + /// + /// # Parameters + /// +-/// - `eth_signed_msg_hash`: 32-byte hash of the Ethereum signed message ++/// - `eth_signed_msg_hash`: 32-byte EIP-712 message hash + /// - `sig`: The ECDSA signature containing v, r, s components + /// + /// # Returns +@@ -81,30 +81,6 @@ pub fn ecdsa_recover_evm_addr( + Ok(evm_addr) + } + +-/// Computes the Ethereum-compatible message hash for root validation. +-/// +-/// Creates a hash that matches Ethereum's personal sign message format: +-/// "\x19Ethereum Signed Message:\n32" + keccak256(root || valid_until) +-/// +-/// # Parameters +-/// +-/// - `root`: The 32-byte Merkle root +-/// - `valid_until`: Timestamp until which the root is valid +-/// +-/// # Returns +-/// +-/// - The 32-byte message hash ready for ECDSA verification +-pub fn compute_eth_message_hash(root: &[u8; HASH_BYTES], valid_until: u32) -> Hash { +- // Use big-endian encoding for EVM compatibility +- let valid_until_bytes = left_pad_vec(&valid_until.to_be_bytes()); +- let hashed_encoded_params = hashv(&[root, &valid_until_bytes]); +- +- hashv(&[ +- b"\x19Ethereum Signed Message:\n32", +- &hashed_encoded_params.to_bytes(), +- ]) +-} +- + /// Calculates a Merkle root from a leaf node and a proof path. + /// + /// This function iteratively combines a leaf hash with the provided proof elements +@@ -392,23 +368,6 @@ mod tests { + } + } + +- mod test_compute_eth_message_hash { +- use super::*; +- +- #[test] +- fn basic() { +- let root = +- &decode32("d5ef592d1ad183db43b4980d7ab7ee43a6f6a284988c3e3a23d38c07beb520c7"); +- let valid_until: u32 = 1748317727; +- +- let result = compute_eth_message_hash(root, valid_until); +- +- assert_eq!( +- result.to_bytes(), +- decode32("032705bd71839baef725154f00f87ddcc1d95c4b5189c9fb5983f26ad6c95102") +- ); +- } +- } + + mod test_hash_leaf { + use super::*; +diff --git a/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs b/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs +index 5d5167d4..8cbce01c 100644 +--- a/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs ++++ b/chains/solana/contracts/programs/mcm/src/instructions/set_root.rs +@@ -2,6 +2,7 @@ use anchor_lang::prelude::*; + + use crate::config::MultisigConfig; + use crate::constant::*; ++use crate::eip712; + use crate::error::*; + use crate::eth_utils::*; + use crate::event::*; +@@ -20,12 +21,13 @@ pub fn set_root( + McmError::SignedHashAlreadySeen + ); + +- // verify ECDSA signatures on (root, validUntil) and ensure that the root group is successful ++ // verify EIP-712 ECDSA signatures on (root, validUntil) and ensure that the root group is successful + verify_ecdsa_signatures( + &ctx.accounts.root_signatures.signatures, + &ctx.accounts.multisig_config, + &root, + valid_until, ++ ctx.program_id, + )?; + + require!( +@@ -114,8 +116,10 @@ fn verify_ecdsa_signatures( + multisig_config: &MultisigConfig, + root: &[u8; 32], + valid_until: u32, ++ program_id: &Pubkey, + ) -> Result<()> { +- let signed_hash = compute_eth_message_hash(root, valid_until); ++ let signed_hash = ++ eip712::compute_message_hash(root, valid_until, multisig_config.chain_id, program_id); + let mut previous_addr: [u8; EVM_ADDRESS_BYTES] = [0; EVM_ADDRESS_BYTES]; + let mut group_vote_counts: [u8; NUM_GROUPS] = [0; NUM_GROUPS]; + +diff --git a/chains/solana/contracts/programs/mcm/src/lib.rs b/chains/solana/contracts/programs/mcm/src/lib.rs +index 88f855e8..a2e1d0b3 100644 +--- a/chains/solana/contracts/programs/mcm/src/lib.rs ++++ b/chains/solana/contracts/programs/mcm/src/lib.rs +@@ -19,6 +19,9 @@ pub use state::*; + mod eth_utils; + use eth_utils::*; + ++mod eip712; ++pub use eip712::*; ++ + mod instructions; + use instructions::*; + diff --git a/solana/devnet/2025-10-22-mcm-upgrade/patches/id.patch b/solana/devnet/2025-10-22-mcm-upgrade/patches/id.patch new file mode 100644 index 00000000..35c4e6d3 --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/patches/id.patch @@ -0,0 +1,12 @@ +diff --git a/chains/solana/contracts/programs/mcm/src/lib.rs b/chains/solana/contracts/programs/mcm/src/lib.rs +index a2e1d0b3..f2420a45 100644 +--- a/chains/solana/contracts/programs/mcm/src/lib.rs ++++ b/chains/solana/contracts/programs/mcm/src/lib.rs +@@ -1,6 +1,6 @@ + use anchor_lang::prelude::*; + +-declare_id!("5vNJx78mz7KVMjhuipyr9jKBKcMrKYGdjGkgE4LUmjKk"); ++declare_id!("Ea5qsVs2kwQXi7LMAh3Qg5YKpuPQbW7oqvYaPXUwBwoX"); + + use program::Mcm; + diff --git a/solana/devnet/2025-10-22-mcm-upgrade/patches/invoke_signed.patch b/solana/devnet/2025-10-22-mcm-upgrade/patches/invoke_signed.patch new file mode 100644 index 00000000..bd49aa6d --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/patches/invoke_signed.patch @@ -0,0 +1,18 @@ +diff --git a/chains/solana/contracts/programs/mcm/src/instructions/execute.rs b/chains/solana/contracts/programs/mcm/src/instructions/execute.rs +index 155b1a71..b550e7fe 100644 +--- a/chains/solana/contracts/programs/mcm/src/instructions/execute.rs ++++ b/chains/solana/contracts/programs/mcm/src/instructions/execute.rs +@@ -93,6 +93,13 @@ pub fn execute<'info>( + + invoke_signed(&instruction, acc_infos, signer)?; + ++ // If the CPI modified any typed accounts present in this outer context ++ // (e.g., calling `accept_ownership` which updates `multisig_config`), ++ // reload them to avoid Anchor writing back the stale outer copy on exit. ++ ctx.accounts.multisig_config.reload()?; ++ ctx.accounts.root_metadata.reload()?; ++ ctx.accounts.expiring_root_and_op_count.reload()?; ++ + emit!(OpExecuted { + nonce, + to: instruction.program_id, diff --git a/solana/devnet/2025-10-22-mcm-upgrade/patches/simplify_eip712.patch b/solana/devnet/2025-10-22-mcm-upgrade/patches/simplify_eip712.patch new file mode 100644 index 00000000..1734457a --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/patches/simplify_eip712.patch @@ -0,0 +1,74 @@ +diff --git a/chains/solana/contracts/programs/mcm/src/eip712.rs b/chains/solana/contracts/programs/mcm/src/eip712.rs +index 87b20002..3a19c422 100644 +--- a/chains/solana/contracts/programs/mcm/src/eip712.rs ++++ b/chains/solana/contracts/programs/mcm/src/eip712.rs +@@ -11,7 +11,6 @@ + //! - **name**: "ManyChainMultiSig" - The contract name + //! - **version**: "1" - The contract version + //! - **chainId**: Solana chain identifier (e.g., hash of "solana:localnet") +-//! - **verifyingContract**: `address(0)` - Not applicable for Solana + //! - **salt**: The Solana Program ID (32 bytes) - Uniquely identifies this program instance + //! + //! ## Message Structure +@@ -30,10 +29,10 @@ pub const EIP712_DOMAIN_NAME: &str = "ManyChainMultiSig"; + pub const EIP712_DOMAIN_VERSION: &str = "1"; + + /// Type hash for EIP712Domain +-/// keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)") ++/// keccak256("EIP712Domain(string name,string version,uint256 chainId,bytes32 salt)") + pub const EIP712_DOMAIN_TYPE_HASH: &[u8; HASH_BYTES] = &[ +- 0xd8, 0x7c, 0xd6, 0xef, 0x79, 0xd4, 0xe2, 0xb9, 0x5e, 0x15, 0xce, 0x8a, 0xbf, 0x73, 0x2d, 0xb5, +- 0x1e, 0xc7, 0x71, 0xf1, 0xca, 0x2e, 0xdc, 0xcf, 0x22, 0xa4, 0x6c, 0x72, 0x9a, 0xc5, 0x64, 0x72, ++ 0xa6, 0x04, 0xff, 0xf5, 0xa2, 0x7d, 0x59, 0x51, 0xf3, 0x34, 0xcc, 0xda, 0x7a, 0xbf, 0xf3, 0x28, ++ 0x6a, 0x8a, 0xf2, 0x9c, 0xae, 0xeb, 0x19, 0x6a, 0x6f, 0x2b, 0x40, 0xa1, 0xdc, 0xe7, 0x61, 0x2b, + ]; + + /// Type hash for RootValidation message +@@ -105,9 +104,6 @@ pub fn compute_domain_hash(chain_id: u64, program_id: &Pubkey) -> Hash { + // Chain ID as bytes32 (big-endian, left-padded) + let chain_id_bytes = left_pad_to_32(&chain_id.to_be_bytes()); + +- // Verifying contract = address(0) = 20 zero bytes, left-padded to 32 bytes +- let verifying_contract = [0u8; 32]; +- + // Salt = Program ID (32 bytes) + let salt = program_id.to_bytes(); + +@@ -116,7 +112,6 @@ pub fn compute_domain_hash(chain_id: u64, program_id: &Pubkey) -> Hash { + EIP712_DOMAIN_NAME_HASH, + EIP712_DOMAIN_VERSION_HASH, + &chain_id_bytes, +- &verifying_contract, + &salt, + ]) + } +@@ -168,7 +163,7 @@ mod tests { + + #[test] + fn verify_domain_type_hash() { +- let expected = hash(b"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"); ++ let expected = hash(b"EIP712Domain(string name,string version,uint256 chainId,bytes32 salt)"); + assert_eq!(&expected.to_bytes(), EIP712_DOMAIN_TYPE_HASH); + } + +@@ -211,18 +206,14 @@ mod tests { + + // The domain hash should be deterministic + // We'll verify it matches the expected structure by recomputing +- let name_hash = hash(EIP712_DOMAIN_NAME.as_bytes()); +- let version_hash = hash(EIP712_DOMAIN_VERSION.as_bytes()); + let chain_id_bytes = left_pad_to_32(&CHAIN_ID.to_be_bytes()); +- let verifying_contract = [0u8; 32]; + let salt = program_id.to_bytes(); + + let expected = hashv(&[ + EIP712_DOMAIN_TYPE_HASH, +- &name_hash.to_bytes(), +- &version_hash.to_bytes(), ++ EIP712_DOMAIN_NAME_HASH, ++ EIP712_DOMAIN_VERSION_HASH, + &chain_id_bytes, +- &verifying_contract, + &salt, + ]); + diff --git a/solana/devnet/2025-10-22-mcm-upgrade/proposal.json b/solana/devnet/2025-10-22-mcm-upgrade/proposal.json new file mode 100644 index 00000000..d9eb603a --- /dev/null +++ b/solana/devnet/2025-10-22-mcm-upgrade/proposal.json @@ -0,0 +1,54 @@ +{ + "multisigId": "0x0000000000000000000000000000000000000000000000000000000000000000", + "validUntil": 1761205094, + "instructions": [ + { + "programId": "BPFLoaderUpgradeab1e11111111111111111111111", + "data": "AwAAAA==", + "accounts": [ + { + "pubkey": "Ca93fFb5tQR14XzNpvBi6WNTuPHWZLNwoZ3hu6EErgDF", + "isSigner": false, + "isWritable": true + }, + { + "pubkey": "Ea5qsVs2kwQXi7LMAh3Qg5YKpuPQbW7oqvYaPXUwBwoX", + "isSigner": false, + "isWritable": true + }, + { + "pubkey": "C4XSxK5zGw2ANbWQSURn6Cx4HaFiCxpmD6Nbb4T6szNu", + "isSigner": false, + "isWritable": true + }, + { + "pubkey": "2aWsUTRfJu2hPNUYHaYJjm6C3SVPqXjtoxu2VgBXsd72", + "isSigner": false, + "isWritable": true + }, + { + "pubkey": "SysvarRent111111111111111111111111111111111", + "isSigner": false, + "isWritable": false + }, + { + "pubkey": "SysvarC1ock11111111111111111111111111111111", + "isSigner": false, + "isWritable": false + }, + { + "pubkey": "2N9nKVFPHsBVazniiYNxECB2nPLcwjk3dHAJH9tkr2xQ", + "isSigner": false, + "isWritable": false + } + ] + } + ], + "rootMetadata": { + "chainId": 0, + "multisig": "7BrnaHaHtFDshmyrZFi28r9vzxTqcmTjSZcf7KdLHRhL", + "preOpCount": 0, + "postOpCount": 1, + "overridePreviousRoot": false + } +} \ No newline at end of file diff --git a/solana/setup-templates/template-mcm-program-upgrade/Makefile b/solana/setup-templates/template-mcm-program-upgrade/Makefile index 2f2be947..3598df42 100644 --- a/solana/setup-templates/template-mcm-program-upgrade/Makefile +++ b/solana/setup-templates/template-mcm-program-upgrade/Makefile @@ -20,9 +20,9 @@ step2-transfer-buffer: make sol-set-buffer-authority # Step 2.5: Generate set-buffer-authority artifacts (use solana explorer to get the signature of the set-buffer-authority tx) -.PHONY: step7.5-generate-set-buffer-authority-artifacts -step7.5-generate-set-buffer-authority-artifacts: - @echo "==> Step 7.5: Generating MCM set-buffer-authority artifacts..." +.PHONY: step2.5-generate-set-buffer-authority-artifacts +step2.5-generate-set-buffer-authority-artifacts: + @echo "==> Step 2.5: Generating MCM set-buffer-authority artifacts..." make sol-confirm SIG=$(SET_BUFFER_AUTHORITY_SIGNATURE) output=artifacts/set-buffer-authority-artifacts.json # Step 3: Create upgrade proposal From c8bebf37b24ddf1bb3bf3de8aeb75683b83feed7 Mon Sep 17 00:00:00 2001 From: Baptiste Oueriagli Date: Wed, 22 Oct 2025 16:13:59 +0200 Subject: [PATCH 2/5] chore(solana): remove unrelated deploy bridge task --- solana/devnet/2025-10-21-deploy-bridge/.env | 52 ------- .../2025-10-21-deploy-bridge/.gitignore | 2 - .../devnet/2025-10-21-deploy-bridge/Makefile | 138 ------------------ 3 files changed, 192 deletions(-) delete mode 100644 solana/devnet/2025-10-21-deploy-bridge/.env delete mode 100644 solana/devnet/2025-10-21-deploy-bridge/.gitignore delete mode 100644 solana/devnet/2025-10-21-deploy-bridge/Makefile diff --git a/solana/devnet/2025-10-21-deploy-bridge/.env b/solana/devnet/2025-10-21-deploy-bridge/.env deleted file mode 100644 index 5a85eaba..00000000 --- a/solana/devnet/2025-10-21-deploy-bridge/.env +++ /dev/null @@ -1,52 +0,0 @@ -RUST_TOOLCHAIN=1.87.0 -SOLANA_VERSION=2.1.21 -ANCHOR_VERSION=0.31.0 - -# Variables for cloning Bridge repo -BRIDGE_REPO=https://github.com/base/bridge.git -BRIDGE_AUDITED_COMMIT=e9b5fe27280a - -# Variables for deploying and sending transactions -DEPLOY_ENV=testnet-alpha -DEPLOYER_KP=~/.config/solana/id.json - -# Signature of the deployWithMaxDataLen txs (used to generate the deployment artifacts) -BRIDGE_DEPLOY_SIGNATURE= -BASE_RELAYER_DEPLOY_SIGNATURE= - -# Variables for initializing the Bridge Solana program -BRIDGE_PROGRAM_ID= -GUARDIAN= -SOL_REMOTE_TOKEN= -BRIDGE_EIP1559_TARGET=5_000_000 -BRIDGE_EIP1559_DENOMINATOR=2 -BRIDGE_EIP1559_WINDOW_DURATION_SECONDS=1 -BRIDGE_EIP1559_MINIMUM_BASE_FEE=1 -BRIDGE_GAS_PER_CALL=125_000 -BRIDGE_GAS_COST_SCALER=1_000_000 -BRIDGE_GAS_COST_SCALER_DP=1_000_000 -BRIDGE_GAS_FEE_RECEIVER=2aWsUTRfJu2hPNUYHaYJjm6C3SVPqXjtoxu2VgBXsd72 # Deployer address -BRIDGE_PROTOCOL_BLOCK_INTERVAL_REQUIREMENT=300 -BRIDGE_BUFFER_MAX_CALL_BUFFER_SIZE=8192 # 8KB -BRIDGE_BASE_ORACLE_THRESHOLD=2 -BRIDGE_BASE_ORACLE_SIGNER_COUNT=2 -BRIDGE_BASE_ORACLE_SIGNERS=0x20BFBCCC8aBaD55c8aA383a75838348A646eDbA0,0xfc85de3f52047b993b2dda967b606a8b9caa2c29 -BRIDGE_PARTNER_ORACLE_REQUIRED_THRESHOLD=0 - -# Variables for initializing the Base Relayer Solana program -BASE_RELAYER_PROGRAM_ID= -GUARDIAN= -BASE_RELAYER_EIP1559_TARGET=5_000_000 -BASE_RELAYER_EIP1559_DENOMINATOR=2 -BASE_RELAYER_EIP1559_WINDOW_DURATION_SECONDS=1 -BASE_RELAYER_EIP1559_MINIMUM_BASE_FEE=1 -BASE_RELAYER_MIN_GAS_LIMIT_PER_MESSAGE=100_000 -BASE_RELAYER_MAX_GAS_LIMIT_PER_MESSAGE=5_000_000 -BASE_RELAYER_GAS_COST_SCALER=1_000_000 -BASE_RELAYER_GAS_COST_SCALER_DP=1_000_000 -BASE_RELAYER_GAS_FEE_RECEIVER=2aWsUTRfJu2hPNUYHaYJjm6C3SVPqXjtoxu2VgBXsd72 # Deployer address - -# Variables for printing the SOL vault PDA -AUTHORITY=$(DEPLOYER_KP) -SOL_RECIPIENT= # SOL vault PDA -SOL_AMOUNT=0.1 \ No newline at end of file diff --git a/solana/devnet/2025-10-21-deploy-bridge/.gitignore b/solana/devnet/2025-10-21-deploy-bridge/.gitignore deleted file mode 100644 index d4dc7f7b..00000000 --- a/solana/devnet/2025-10-21-deploy-bridge/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -bridge/ -*keypair.json diff --git a/solana/devnet/2025-10-21-deploy-bridge/Makefile b/solana/devnet/2025-10-21-deploy-bridge/Makefile deleted file mode 100644 index 93e23389..00000000 --- a/solana/devnet/2025-10-21-deploy-bridge/Makefile +++ /dev/null @@ -1,138 +0,0 @@ -include ../.env -include .env - -include ../../Makefile - -.PHONY: install-rust -install-rust: - curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y - rustup toolchain install $(RUST_TOOLCHAIN) && \ - rustup default $(RUST_TOOLCHAIN) - -.PHONY: install-solana-cli -install-solana-cli: - curl -sSfL https://release.anza.xyz/v$(SOLANA_VERSION)/install | sh - -.PHONY: install-anchor -install-anchor: - cargo install --git https://github.com/coral-xyz/anchor --tag v$(ANCHOR_VERSION) anchor-cli --force - -.PHONY: install-bun -install-bun: - curl -fsSL https://bun.sh/install | bash - -.PHONY: deps -deps: install-rust install-solana-cli install-anchor install-bun - -## -# Deployment Workflow -## - -# Step 1: Clone and build bridge programs -.PHONY: step1-clone-bridge -step1-clone-bridge: - @echo "==> Step 1: Cloning and building bridge programs..." - rm -rf bridge - git clone --filter=blob:none $(BRIDGE_REPO) bridge - cd bridge && \ - git checkout $(BRIDGE_AUDITED_COMMIT) - cd bridge/clients/ts && \ - bun install && \ - bun run build - cd bridge/scripts && \ - bun install - -# Step 2: Build programs -.PHONY: step2-build-programs -step2-build-programs: - @echo "==> Step 2: Building bridge programs..." - cd bridge/solana && \ - anchor keys sync && \ - cp target/deploy/bridge-keypair.json ../../bridge-keypair.json && \ - cp target/deploy/base_relayer-keypair.json ../../base_relayer-keypair.json - cd bridge/scripts && \ - bun cli sol build \ - --deploy-env $(DEPLOY_ENV) \ - --bridge-program-kp ../../bridge-keypair.json \ - --base-relayer-program-kp ../../base_relayer-keypair.json - -# Step 3: Deploy programs -.PHONY: step3-deploy-programs -step3-deploy-programs: - @echo "==> Step 3: Deploying programs..." - make sol-confirm-cmd \ - cmd="cd bridge/scripts && \ - bun cli sol deploy \ - --deploy-env $(DEPLOY_ENV) \ - --program bridge \ - --program-kp ../../bridge-keypair.json \ - --deployer-kp $(DEPLOYER_KP)" \ - output=artifacts/bridge-deploy-artifacts.json - make sol-confirm-cmd \ - cmd="cd bridge/scripts && \ - bun cli sol deploy \ - --deploy-env $(DEPLOY_ENV) \ - --program base-relayer \ - --program-kp ../../base_relayer-keypair.json \ - --deployer-kp $(DEPLOYER_KP)" \ - output=artifacts/base-relayer-deploy-artifacts.json - -# Step 4: Initialize programs -.PHONY: step4-init-programs -step4-init-programs: - @echo "==> Step 4: Initializing programs..." - make sol-confirm-cmd \ - cmd="cd bridge/scripts && \ - bun cli sol bridge initialize \ - --program-id $(BRIDGE_PROGRAM_ID) \ - --rpc-url $(SOL_RPC_URL) \ - --payer-kp $(DEPLOYER_KP) \ - --guardian $(GUARDIAN) \ - --remote-sol-address $(SOL_REMOTE_TOKEN) \ - --eip1559-target $(BRIDGE_EIP1559_TARGET) \ - --eip1559-denominator $(BRIDGE_EIP1559_DENOMINATOR) \ - --eip1559-window-duration-seconds $(BRIDGE_EIP1559_WINDOW_DURATION_SECONDS) \ - --eip1559-minimum-base-fee $(BRIDGE_EIP1559_MINIMUM_BASE_FEE) \ - --gas-per-call $(BRIDGE_GAS_PER_CALL) \ - --gas-cost-scaler $(BRIDGE_GAS_COST_SCALER) \ - --gas-cost-scaler-dp $(BRIDGE_GAS_COST_SCALER_DP) \ - --gas-fee-receiver $(BRIDGE_GAS_FEE_RECEIVER) \ - --protocol-block-interval-requirement $(BRIDGE_PROTOCOL_BLOCK_INTERVAL_REQUIREMENT) \ - --buffer-max-call-buffer-size $(BRIDGE_BUFFER_MAX_CALL_BUFFER_SIZE) \ - --base-oracle-threshold $(BRIDGE_BASE_ORACLE_THRESHOLD) \ - --base-oracle-signer-count $(BRIDGE_BASE_ORACLE_SIGNER_COUNT) \ - --base-oracle-signers $(BRIDGE_BASE_ORACLE_SIGNERS) \ - --partner-oracle-required-threshold $(BRIDGE_PARTNER_ORACLE_REQUIRED_THRESHOLD)" - output=artifacts/init-bridge.json - make sol-confirm-cmd \ - cmd="cd bridge/scripts && \ - bun cli sol base-relayer initialize \ - --program-id $(BASE_RELAYER_PROGRAM_ID) \ - --rpc-url $(SOL_RPC_URL) \ - --payer-kp $(DEPLOYER_KP) \ - --guardian $(GUARDIAN) \ - --eip1559-target $(BASE_RELAYER_EIP1559_TARGET) \ - --eip1559-denominator $(BASE_RELAYER_EIP1559_DENOMINATOR) \ - --eip1559-window-duration-seconds $(BASE_RELAYER_EIP1559_WINDOW_DURATION_SECONDS) \ - --eip1559-minimum-base-fee $(BASE_RELAYER_EIP1559_MINIMUM_BASE_FEE) \ - --min-gas-limit-per-message $(BASE_RELAYER_MIN_GAS_LIMIT_PER_MESSAGE) \ - --max-gas-limit-per-message $(BASE_RELAYER_MAX_GAS_LIMIT_PER_MESSAGE) \ - --gas-cost-scaler $(BASE_RELAYER_GAS_COST_SCALER) \ - --gas-cost-scaler-dp $(BASE_RELAYER_GAS_COST_SCALER_DP) \ - --gas-fee-receiver $(BASE_RELAYER_GAS_FEE_RECEIVER)" \ - output=artifacts/init-base-relayer.json - -# Step 5: Print SOL vault -.PHONY: step5-print-sol-vault -step5-print-sol-vault: - @echo "==> Step 5: Printing SOL vault..." - cd bridge/scripts && \ - bun cli sol bridge sol-vault \ - --bridge-program $(BRIDGE_PROGRAM_ID) \ - --remote-token $(SOL_REMOTE_TOKEN) - -# Step 6: Print SOL vault and fund it -.PHONY: step6-fund-sol-vault -step6-fund-sol-vault: - @echo "==> Step 6: Funding SOL vault..." - make sol-transfer output=artifacts/fund-sol-vault.json From 15ec722e0ca0da11a3b94784de97b63e3a809c75 Mon Sep 17 00:00:00 2001 From: Baptiste Oueriagli Date: Wed, 22 Oct 2025 16:31:36 +0200 Subject: [PATCH 3/5] fix(solana): fix path in README --- solana/devnet/2025-10-22-mcm-upgrade/FACILITATORS.md | 2 +- solana/devnet/2025-10-22-mcm-upgrade/README.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/solana/devnet/2025-10-22-mcm-upgrade/FACILITATORS.md b/solana/devnet/2025-10-22-mcm-upgrade/FACILITATORS.md index 278a34fb..bfc33c03 100644 --- a/solana/devnet/2025-10-22-mcm-upgrade/FACILITATORS.md +++ b/solana/devnet/2025-10-22-mcm-upgrade/FACILITATORS.md @@ -15,7 +15,7 @@ As a Facilitator, you are responsible for: ```bash cd contract-deployments git pull -cd solana// +cd solana/devnet/2025-10-22-mcm-upgrade make deps ``` diff --git a/solana/devnet/2025-10-22-mcm-upgrade/README.md b/solana/devnet/2025-10-22-mcm-upgrade/README.md index 5faa61d3..bf25cde4 100644 --- a/solana/devnet/2025-10-22-mcm-upgrade/README.md +++ b/solana/devnet/2025-10-22-mcm-upgrade/README.md @@ -13,7 +13,7 @@ This task upgrades a Solana program using the Multi-Chain Multisig (MCM) governa ```bash cd contract-deployments git pull -cd solana// +cd solana/devnet/2025-10-22-mcm-upgrade make deps ``` From 45988b8173d985c02ee5c5b79d8bfe95af090183 Mon Sep 17 00:00:00 2001 From: Baptiste Oueriagli Date: Wed, 22 Oct 2025 16:39:12 +0200 Subject: [PATCH 4/5] chore(solana): standardize commits in .env files --- solana/devnet-alpha/2025-10-20-deploy-bridge/.env | 2 +- solana/devnet-alpha/2025-10-20-deploy-bridge/Makefile | 2 +- solana/devnet/2025-10-20-deploy-mcm/.env | 2 +- solana/devnet/2025-10-22-mcm-upgrade/.env | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/solana/devnet-alpha/2025-10-20-deploy-bridge/.env b/solana/devnet-alpha/2025-10-20-deploy-bridge/.env index 5a85eaba..e525dc2c 100644 --- a/solana/devnet-alpha/2025-10-20-deploy-bridge/.env +++ b/solana/devnet-alpha/2025-10-20-deploy-bridge/.env @@ -4,7 +4,7 @@ ANCHOR_VERSION=0.31.0 # Variables for cloning Bridge repo BRIDGE_REPO=https://github.com/base/bridge.git -BRIDGE_AUDITED_COMMIT=e9b5fe27280a +BRIDGE_COMMIT=e9b5fe27280ad2345d823d42668810432b0651aa # Variables for deploying and sending transactions DEPLOY_ENV=testnet-alpha diff --git a/solana/devnet-alpha/2025-10-20-deploy-bridge/Makefile b/solana/devnet-alpha/2025-10-20-deploy-bridge/Makefile index 93e23389..2d9849ed 100644 --- a/solana/devnet-alpha/2025-10-20-deploy-bridge/Makefile +++ b/solana/devnet-alpha/2025-10-20-deploy-bridge/Makefile @@ -35,7 +35,7 @@ step1-clone-bridge: rm -rf bridge git clone --filter=blob:none $(BRIDGE_REPO) bridge cd bridge && \ - git checkout $(BRIDGE_AUDITED_COMMIT) + git checkout $(BRIDGE_COMMIT) cd bridge/clients/ts && \ bun install && \ bun run build diff --git a/solana/devnet/2025-10-20-deploy-mcm/.env b/solana/devnet/2025-10-20-deploy-mcm/.env index 63d33830..2b027eb8 100644 --- a/solana/devnet/2025-10-20-deploy-mcm/.env +++ b/solana/devnet/2025-10-20-deploy-mcm/.env @@ -3,7 +3,7 @@ ANCHOR_VERSION=0.29.0 # Variables for cloning the MCM repo MCM_REPO=https://github.com/smartcontractkit/chainlink-ccip.git -MCM_AUDITED_COMMIT=0ee732e80586 +MCM_AUDITED_COMMIT=0ee732e80586c2e9df5e9b0c3b5e9a19ee66b3a1 INVOKE_SIGNED_PATCH=patches/invoke_signed.patch EIP712_PATCH=patches/eip712.patch diff --git a/solana/devnet/2025-10-22-mcm-upgrade/.env b/solana/devnet/2025-10-22-mcm-upgrade/.env index 71474061..04bb9508 100644 --- a/solana/devnet/2025-10-22-mcm-upgrade/.env +++ b/solana/devnet/2025-10-22-mcm-upgrade/.env @@ -3,7 +3,7 @@ ANCHOR_VERSION=0.29.0 # Variables for cloning the MCM repo MCM_REPO=https://github.com/smartcontractkit/chainlink-ccip.git -MCM_AUDITED_COMMIT=0ee732e80586 +MCM_AUDITED_COMMIT=0ee732e80586c2e9df5e9b0c3b5e9a19ee66b3a1 INVOKE_SIGNED_PATCH=patches/invoke_signed.patch EIP712_PATCH=patches/eip712.patch SIMPLIFY_EIP712_PATCH=patches/simplify_eip712.patch From a90152e7ba7773c0db996630fe2e65eb2a25b48c Mon Sep 17 00:00:00 2001 From: Baptiste Oueriagli Date: Wed, 22 Oct 2025 16:46:46 +0200 Subject: [PATCH 5/5] chore(solana): rename deps to setup-deps --- solana/devnet/2025-10-20-deploy-mcm/Makefile | 4 ++-- solana/devnet/2025-10-22-mcm-upgrade/Makefile | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/solana/devnet/2025-10-20-deploy-mcm/Makefile b/solana/devnet/2025-10-20-deploy-mcm/Makefile index 3ba2a793..fd10552e 100644 --- a/solana/devnet/2025-10-20-deploy-mcm/Makefile +++ b/solana/devnet/2025-10-20-deploy-mcm/Makefile @@ -19,8 +19,8 @@ install-solana-cli: install-anchor: cargo install --git https://github.com/coral-xyz/anchor --tag v$(ANCHOR_VERSION) anchor-cli --force -.PHONY: deps -deps: install-rust install-solana-cli install-anchor +.PHONY: setup-deps +setup-deps: install-rust install-solana-cli install-anchor ## # Deployment Workflow diff --git a/solana/devnet/2025-10-22-mcm-upgrade/Makefile b/solana/devnet/2025-10-22-mcm-upgrade/Makefile index 531d4a43..314a82ce 100644 --- a/solana/devnet/2025-10-22-mcm-upgrade/Makefile +++ b/solana/devnet/2025-10-22-mcm-upgrade/Makefile @@ -19,8 +19,8 @@ install-solana-cli: install-anchor: cargo install --git https://github.com/coral-xyz/anchor --tag v$(ANCHOR_VERSION) anchor-cli --force -.PHONY: deps -deps: install-rust install-solana-cli install-anchor +.PHONY: setup-deps +setup-deps: install-rust install-solana-cli install-anchor ## # MCM Program Upgrade Workflow