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-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 37368514..2b027eb8 100644 --- a/solana/devnet/2025-10-20-deploy-mcm/.env +++ b/solana/devnet/2025-10-20-deploy-mcm/.env @@ -3,8 +3,9 @@ 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 +MCM_AUDITED_COMMIT=0ee732e80586c2e9df5e9b0c3b5e9a19ee66b3a1 +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..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 @@ -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-22-mcm-upgrade/.env b/solana/devnet/2025-10-22-mcm-upgrade/.env new file mode 100644 index 00000000..04bb9508 --- /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=0ee732e80586c2e9df5e9b0c3b5e9a19ee66b3a1 +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..bfc33c03 --- /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/devnet/2025-10-22-mcm-upgrade +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..314a82ce --- /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: setup-deps +setup-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..bf25cde4 --- /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/devnet/2025-10-22-mcm-upgrade +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