Skip to main content

multichain-deployment

Use when deploying the same protocol to multiple chains at the same address. Covers CREATE2 for deterministic addresses, deployment registries, chain-specific configuration, multi-chain testing, and canonical deployment patterns.

소스 정보

저장소
ccashwell/evm-cortex
최근 소스 활동
2026년 4월 10일 16:31
감지된 SKILL.md 언어
영어
스타
131
포크
18

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
multichain-deployment
description
Use when deploying the same protocol to multiple chains at the same address. Covers CREATE2 for deterministic addresses, deployment registries, chain-specific configuration, multi-chain testing, and canonical deployment patterns.
# Multi-Chain Deployment Patterns ## Strategy Overview For protocol credibility and UX, deploy contracts at the **same address** on every chain. This requires deterministic deployment via CREATE2 and a consistent deployment flow. ## Deterministic Deployment Factory Use a pre-deployed CREATE2 factory that exists at the same address on all EVM chains. **Arachnid's Deterministic Deployment Proxy** (available on nearly all chains): ``` 0x4e59b44847b379578588920cA78FbF26c0B4956C ``` ```solidity // Deploy via the keyless CREATE2 factory function deployDeterministic(bytes memory creationCode, bytes32 salt) external returns (address deployed) { address factory = 0x4e59b44847b379578588920cA78FbF26c0B4956C; bytes memory payload = abi.encodePacked(salt, creationCode); (bool ok, bytes memory result) = factory.call(payload); require(ok, "Deploy failed"); deployed = address(uint160(uint256(keccak256(abi.encodePacked( bytes1(0xff), factory, salt, keccak256(creationCode) ))))); } ``` ## Multi-Chain Forge Script ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {Script, console2} from "forge-std/Script.sol"; import {MyProtocol} from "../src/MyProtocol.sol"; contract MultiChainDeploy is Script { address constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C; bytes32 constant SALT = keccak256("myprotocol-v1.0.0"); struct ChainConfig { string rpc; uint256 chainId; address admin; address weth; } function configs() internal view returns (ChainConfig[] memory) { ChainConfig[] memory c = new ChainConfig[](4); address admin = vm.envAddress("ADMIN_ADDRESS"); c[0] = ChainConfig("mainnet", 1, admin, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); c[1] = ChainConfig("base", 8453, admin, 0x4200000000000000000000000000000000000006); c[2] = ChainConfig("optimism", 10, admin, 0x4200000000000000000000000000000000000006); c[3] = ChainConfig("arbitrum", 42161, admin, 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1); return c; } function predictAddress() public view returns (address) { bytes memory initCode = type(MyProtocol).creationCode; return address(uint160(uint256(keccak256(abi.encodePacked( bytes1(0xff), CREATE2_FACTORY, SALT, keccak256(initCode) ))))); } function run() external { uint256 deployerKey = vm.envUint("DEPLOYER_PRIVATE_KEY"); ChainConfig[] memory chains = configs(); address predicted = predictAddress(); console2.log("Predicted address:", predicted); for (uint256 i = 0; i < chains.length; i++) { console2.log("\n--- Deploying to", chains[i].rpc, "---"); vm.createSelectFork(chains[i].rpc); if (predicted.code.length > 0) { console2.log(" Already deployed, skipping"); continue; } vm.startBroadcast(deployerKey); bytes memory initCode = type(MyProtocol).creationCode; bytes memory payload = abi.encodePacked(SALT, initCode); (bool ok,) = CREATE2_FACTORY.call(payload); require(ok, "Deploy failed"); require(predicted.code.length > 0, "Code not at predicted address"); console2.log(" Deployed at:", predicted); // Chain-specific initialization MyProtocol(predicted).initialize(chains[i].admin, chains[i].weth); vm.stopBroadcast(); } } } ``` ```bash forge script script/MultiChainDeploy.s.sol --broadcast --multi --verify ``` ## Deployment Registry Track deployments across chains: ```json { "protocol": "MyProtocol", "version": "1.0.0", "salt": "0x...", "deployments": { "1": { "address": "0xSameOnAllChains", "txHash": "0x...", "blockNumber": 18000000, "deployer": "0x...", "timestamp": "2025-06-01T00:00:00Z" }, "8453": { "address": "0xSameOnAllChains", "txHash": "0x...", "blockNumber": 5000000, "deployer": "0x...", "timestamp": "2025-06-01T00:05:00Z" } } } ``` Generate automatically in your deploy script: ```solidity function _writeDeployment(uint256 chainId, address deployed, bytes32 txHash) internal { string memory key = vm.toString(chainId); string memory json = vm.serializeAddress(key, "address", deployed); json = vm.serializeBytes32(key, "txHash", txHash); json = vm.serializeUint(key, "blockNumber", block.number); vm.writeJson(json, "./deployments.json", string.concat(".", key)); } ``` ## Chain-Specific Configuration When contracts need different config per chain but the same address: ```solidity contract MyProtocol { address public admin; address public weth; bool private _initialized; function initialize(address admin_, address weth_) external { require(!_initialized, "Already initialized"); _initialized = true; admin = admin_; weth = weth_; } } ``` For CREATE3 (address independent of constructor args): ```solidity import {CREATE3} from "solady/utils/CREATE3.sol"; contract MultiChainFactory { function deploy(bytes32 salt, bytes memory creationCode) external returns (address) { return CREATE3.deploy(salt, creationCode, 0); } function predict(bytes32 salt) external view returns (address) { return CREATE3.getDeployed(salt); } } ``` ## Multi-Chain Testing ```solidity contract MultiChainTest is Test { uint256 mainnetFork; uint256 baseFork; uint256 opFork; function setUp() public { mainnetFork = vm.createFork("mainnet"); baseFork = vm.createFork("base"); opFork = vm.createFork("optimism"); } function test_deploysSameAddress() public { bytes32 salt = keccak256("test-v1"); bytes memory initCode = type(MyProtocol).creationCode; vm.selectFork(mainnetFork); address addrMainnet = _deployViaCreate2(salt, initCode); vm.selectFork(baseFork); address addrBase = _deployViaCreate2(salt, initCode); vm.selectFork(opFork); address addrOp = _deployViaCreate2(salt, initCode); assertEq(addrMainnet, addrBase, "Mainnet != Base"); assertEq(addrBase, addrOp, "Base != Optimism"); } } ``` ## Verification Across Chains ```bash #!/bin/bash # verify-all.sh CHAINS=("mainnet" "base" "optimism" "arbitrum") CONTRACT_ADDR="0xSameOnAllChains" CONTRACT_PATH="src/MyProtocol.sol:MyProtocol" for chain in "${CHAINS[@]}"; do echo "Verifying on $chain..." forge verify-contract "$CONTRACT_ADDR" "$CONTRACT_PATH" \ --chain "$chain" --watch done ``` ## Multi-Chain Deployment Checklist - [ ] Same CREATE2 factory exists on all target chains - [ ] Salt is unique per protocol version - [ ] Init code is identical (same compiler, settings, no chain-specific constructor args) - [ ] Predicted address verified before deployment - [ ] Idempotent script (skips chains already deployed) - [ ] Chain-specific config applied via `initialize` (not constructor) - [ ] Deployment registry updated after each chain - [ ] Contracts verified on each chain's block explorer - [ ] Cross-chain functionality tested (bridges, messaging) - [ ] Admin/governance addresses correct per chain
GitHub에서 보기