Skip to content

WIP: First attempt at foundry deployment migration #984

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 5 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ artifacts
cache-zk
artifacts-zk

# Foundry files
broadcast/
script/deployments/tested/

# Upgradeability files
.openzeppelin

Expand All @@ -34,3 +38,5 @@ test-ledger
src/svm/assets
src/svm/clients/*
!src/svm/clients/index.ts

**/.claude/settings.local.json
152 changes: 152 additions & 0 deletions FOUNDRY_DEPLOYMENT.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
# Foundry Deployment Guide

This document provides guidance on deploying Across protocol contracts using Foundry instead of Hardhat.

## Overview

The Across protocol has migrated from Hardhat to Foundry for deployment scripts. Foundry provides several advantages:

- Faster execution
- Better debugging
- Native Solidity scripting
- Improved gas reporting and optimization

All deployment scripts are located in the `script/deployments` directory and follow a consistent pattern.

## Prerequisites

1. Install Foundry:

```bash
curl -L https://foundry.paradigm.xyz | bash
foundryup
```

2. Create an `.env` file based on `.env.example`:

```bash
cp .env.example .env
```

3. Edit the `.env` file with your specific configuration:
- Add your secure mnemonic or private key
- Add RPC URLs for chains you will deploy to
- Add Etherscan API keys for contract verification
- Configure any other required parameters

## Deployment Scripts

### Script Structure

All deployment scripts follow a similar structure:

1. **Imports**: Required dependencies including the contract to be deployed.
2. **Contract Definition**: The script is defined as a Forge script that inherits from ChainUtils.
3. **Constants**: Any constants needed for the deployment.
4. **Run Function**: The main function that performs the deployment.
5. **Helper Functions**: Any additional utility functions needed.

### Deployment Process

The basic deployment flow is:

1. **Configuration**: Load environment variables and set up configuration.
2. **Address Resolution**: Resolve chain-specific addresses for tokens and contracts.
3. **Deployment**: Deploy the contract with appropriate parameters.
4. **Proxy Setup**: For upgradeable contracts, deploy and initialize a proxy.
5. **Verification**: Automatically verify the contract on Etherscan if API key is provided.

### Deterministic Deployments

Some contracts require deterministic deployment to ensure they have the same address across different chains. We use the CREATE2 opcode for this purpose, replicating the functionality from hardhat-deploy:

1. **DeterministicDeployer**: Used to deploy contracts deterministically with CREATE2.
2. **CREATE2 Factory**: We use the same factory address (0x4e59b44847b379578588920ca78fbf26c0b4956c) as hardhat-deploy.
3. **Salt**: Each contract type has a specific salt value (e.g., "0x1234" for PolygonTokenBridger and SpokePoolVerifier, "0x12345678" for MulticallHandler).

Deterministic deployment is used for contracts that need the same address on multiple chains, such as:

- **PolygonTokenBridger**: Deployed with the same address on Ethereum and Polygon.
- **SpokePoolVerifier**: Deployed with the same address across all chains.
- **MulticallHandler**: Deployed with the same address on all EVM-compatible chains.

### Example Deployment

To deploy the HubPool contract to Ethereum mainnet:

```bash
# Load environment variables
source .env

# Deploy HubPool on mainnet
forge script script/deployments/DeployHubPool.s.sol --rpc-url $MAINNET_RPC_URL --broadcast --verify -vvvv
```

For a spoke pool on an L2 chain (e.g., Optimism):

```bash
# Deploy Optimism SpokePool on Optimism
forge script script/deployments/DeployOptimismSpokePool.s.sol --rpc-url $OPTIMISM_RPC_URL --broadcast --verify -vvvv
```

## Chain-Specific Deployments

### Multi-Chain Pattern

The Across protocol operates across multiple chains, requiring coordinated deployments:

1. **Hub Pool**: Deployed on Ethereum mainnet (Layer 1).
2. **Chain Adapters**: Deployed on Ethereum mainnet, one for each supported L2.
3. **Spoke Pools**: Deployed on each L2 chain and connected to the Hub Pool.

### Address Management

Chain-specific addresses are managed in `script/utils/ChainUtils.sol`, which provides:

- Token addresses (WETH, USDC, etc.) for each chain
- Protocol contract addresses for each chain
- Helper functions for address resolution

## Testing Deployment Scripts

Before executing on actual networks, test your deployments:

```bash
# Test a specific deployment script on a forked network
./script/deployments/test-core-deployments.sh

# Test all deployment scripts
./script/deployments/test-deployments.sh
```

## Troubleshooting

### Common Issues

1. **RPC Connection Errors**: Ensure RPC URLs are correct and the service is available.
2. **Gas Estimation Failures**: The deployment might require more gas than estimated. Try increasing the gas limit.
3. **Address Checksum Errors**: Ensure addresses use the correct Ethereum checksum format.
4. **Initialization Errors**: For upgradeable contracts, ensure initialization parameters are correct.

### Chain-Specific Issues

1. **Blast Chain**: For testing Blast deployments, set `TESTING_MODE=true` to skip initialization.
2. **ZkSync Chain**: ZkSync deployments require special handling due to the custom ZkSync VM.

## Migration from Hardhat

If you're familiar with the previous Hardhat deployment scripts:

1. The `deploy/` directory contains the original Hardhat scripts.
2. The `script/deployments/` directory contains the new Foundry scripts.
3. Each Hardhat script has a corresponding Foundry script with the same functionality.

### Key Differences

- Foundry scripts are written in Solidity instead of TypeScript
- Configuration uses environment variables instead of Hardhat config
- Contract verification is handled by Forge's `--verify` flag

## Conclusion

The Foundry deployment scripts provide a more efficient and native way to deploy the Across protocol contracts. By following this guide, you should be able to successfully deploy the contracts to any supported chain.
9 changes: 6 additions & 3 deletions deployments/deployments.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@
"UniswapV3_SwapAndBridge": { "address": "0x6f4A733c7889f038D77D4f540182Dda17423CcbF", "blockNumber": 120044742 },
"AcrossMerkleDistributor": { "address": "0xc8b31410340d57417bE62672f6B53dfB9de30aC2", "blockNumber": 114652330 },
"SpokePoolVerifier": { "address": "0x3Fb9cED51E968594C87963a371Ed90c39519f65A", "blockNumber": 135440379 },
"MulticallHandler": { "address": "0x924a9f036260DdD5808007E1AA95f08eD08aA569", "blockNumber": 122513129 }
"MulticallHandler": { "address": "0x924a9f036260DdD5808007E1AA95f08eD08aA569", "blockNumber": 122513129 },
"AcrossOriginSettler": { "address": "0x5cC9dde9Fdc4fE3A910006709bFa7A39155ef93f", "blockNumber": 135580184 }
},
"11155420": {
"SpokePool": { "address": "0x4e8E101924eDE233C13e2D8622DC8aED2872d505", "blockNumber": 7762656 },
Expand Down Expand Up @@ -107,7 +108,8 @@
"SpokePoolVerifier": { "address": "0x3Fb9cED51E968594C87963a371Ed90c39519f65A", "blockNumber": 29844942 },
"1inch_SwapAndBridge": { "address": "0x7CFaBF2eA327009B39f40078011B0Fb714b65926", "blockNumber": 14450808 },
"UniswapV3_SwapAndBridge": { "address": "0xbcfbCE9D92A516e3e7b0762AE218B4194adE34b4", "blockNumber": 14450714 },
"MulticallHandler": { "address": "0x924a9f036260DdD5808007E1AA95f08eD08aA569", "blockNumber": 16917922 }
"MulticallHandler": { "address": "0x924a9f036260DdD5808007E1AA95f08eD08aA569", "blockNumber": 16917922 },
"AcrossOriginSettler": { "address": "0x514496264fa0B4Ee0522bC7Db644F78B02AEb1ae", "blockNumber": 29984972 }
},
"34443": {
"SpokePool": { "address": "0x3baD7AD0728f9917d1Bf08af5782dCbD516cDd96", "blockNumber": 8043187 },
Expand All @@ -119,7 +121,8 @@
"SpokePoolVerifier": { "address": "0x3Fb9cED51E968594C87963a371Ed90c39519f65A", "blockNumber": 333624754 },
"1inch_SwapAndBridge": { "address": "0xC456398D5eE3B93828252e48beDEDbc39e03368E", "blockNumber": 211175795 },
"UniswapV3_SwapAndBridge": { "address": "0xF633b72A4C2Fb73b77A379bf72864A825aD35b6D", "blockNumber": 211175481 },
"MulticallHandler": { "address": "0x924a9f036260DdD5808007E1AA95f08eD08aA569", "blockNumber": 230779625 }
"MulticallHandler": { "address": "0x924a9f036260DdD5808007E1AA95f08eD08aA569", "blockNumber": 230779625 },
"AcrossOriginSettler": { "address": "0xDE5cFBDE966bF8a187a332EC9c5081A2c8a537c5", "blockNumber": 334742335 }
},
"59144": {
"SpokePool": { "address": "0x7E63A5f1a8F0B4d0934B2f2327DAED3F6bb2ee75", "blockNumber": 2721169 },
Expand Down
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ via_ir = true
optimizer_runs = 800
solc_version = "0.8.23"
revert_strings = "strip"

solc = "0.8.23"
evm_version = "shanghai"
fs_permissions = [{ access = "read-write", path = "./"}]

[rpc_endpoints]
ethereum = "${NODE_URL_1}"
Expand Down
38 changes: 38 additions & 0 deletions script/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Foundry Scripts

This directory contains Foundry scripts for various operations on the Across Protocol contracts.

## Migration Status

The migration from Hardhat to Foundry is in progress. We've successfully migrated all deployment scripts for the following components:

- Hub Pool and core protocol contracts
- Chain adapters for all supported chains (Arbitrum, Optimism, Base, Blast, etc.)
- Spoke pools for all supported chains
- Supporting utility contracts (Multicall, DAI Retriever, etc.)

## Directories

- **deployments/**: Contains deployment scripts that have been migrated from Hardhat to Foundry. See [deployments/README.md](./deployments/README.md) for details on how to use these scripts.
- **utils/**: Contains utility libraries like ChainUtils.sol for chain-specific constants
- Other scripts in this directory are for various operations like proxy deployments, contract interactions, etc.

## Usage

To run any script, use the following command:

```bash
forge script script/<script-path>.s.sol --rpc-url <RPC_URL> -vvvv
```

For scripts that modify state (deploy contracts, make transactions), add the `--broadcast` flag:

```bash
forge script script/<script-path>.s.sol --rpc-url <RPC_URL> --broadcast -vvvv
```

For contract verification, add the `--verify` flag:

```bash
forge script script/<script-path>.s.sol --rpc-url <RPC_URL> --broadcast --verify -vvvv
```
37 changes: 37 additions & 0 deletions script/deployments/DeployAcrossMerkleDistributor.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;

import { Script } from "forge-std/Script.sol";
import { console } from "forge-std/console.sol";
import { ChainUtils } from "../../script/utils/ChainUtils.sol";

/**
* @title DeployAcrossMerkleDistributor
* @notice Template for deploying utility contracts like Multicall, ERC1155, etc.
* @dev Replace AcrossMerkleDistributor with the specific name (e.g., Multicall3, ERC1155).
*/
contract DeployAcrossMerkleDistributor is Script, ChainUtils {
function run() external {
string memory deployerMnemonic = vm.envString("MNEMONIC");
uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0);
address deployer = vm.addr(deployerPrivateKey);
uint256 chainId = block.chainid;

// Get any addresses needed for this utility contract
// address hubPoolAddress = vm.envAddress("HUB_POOL_ADDRESS");

console.log("Deploying AcrossMerkleDistributor on chain %s", chainId);
console.log("Deployer: %s", deployer);
// console.log("Hub Pool: %s", hubPoolAddress);

vm.startBroadcast(deployerPrivateKey);

// Example utility contract deployment
// AcrossMerkleDistributor utility = new AcrossMerkleDistributor(
// // Constructor parameters if any
// );
// console.log("AcrossMerkleDistributor deployed at: %s", address(utility));

vm.stopBroadcast();
}
}
Loading
Loading