Skip to content

Commit a2c7557

Browse files
authored
feat(Foundry): Constants, Deployments utils and Spoke Pool deploy scripts (#1069)
* Added address extraction script Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Renamed readme Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * feat(Foundry): Added constants file Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Consolidated chain id and native token functions Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * feat(Foundry): Part 3 - Added deployment utils contract (#1070) * feat(Foundry): Added deployment utils contract Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * feat(Foundry): Part 4 - Added hubpool, optimism, arbitrum and ethereum and spoke pool scripts (#1071) * feat(Foundry): Added hubpool, ethereum and arbitrum spoke pool scripts Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Added eth and arb adaptors scripts Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Renamed arb deploy script Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Added OP spokepool and adaptors Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Added deploy scripts for v4 upgrades Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Added Cher (Soneium) deploy script Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Deployed mode, redstone, lens and soneium spoke pools Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Updated lens deploy command Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Revert "Deployed mode, redstone, lens and soneium spoke pools" This reverts commit 8ccecb1. Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> * Fixed addresses extraction script Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> --------- Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> --------- Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com> --------- Signed-off-by: Faisal Usmani <faisal.of.usmani@gmail.com>
1 parent 64e113c commit a2c7557

29 files changed

+2315
-25
lines changed

broadcast/deployed-addresses.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"generated_at": "2025-08-22T17:41:10.581Z",
2+
"generated_at": "2025-08-25T21:46:10.497Z",
33
"chains": {
44
"1": {
55
"chain_name": "Mainnet",
@@ -15,9 +15,9 @@
1515
"block_number": 15976846
1616
},
1717
"Arbitrum_Adapter": {
18-
"address": "0x5473CBD30bEd1Bf97C0c9d7c59d268CD620dA426",
18+
"address": "0x5eC9844936875E27eBF22172f4d92E107D35B57C",
1919
"transaction_hash": "Unknown",
20-
"block_number": 19915048
20+
"block_number": 23086601
2121
},
2222
"Arbitrum_RescueAdapter": {
2323
"address": "0xC6fA0a4EBd802c01157d6E7fB1bbd2ae196ae375",

broadcast/deployed-addresses.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Deployed Contract Addresses
22

3-
Generated on: 2025-08-22T17:41:10.580Z
3+
Generated on: 2025-08-25T21:46:10.496Z
44

55
This file contains the latest deployed smart contract addresses from the broadcast folder.
66

@@ -22,9 +22,9 @@ This file contains the latest deployed smart contract addresses from the broadca
2222

2323
### Arbitrum_Adapter
2424

25-
- **Arbitrum_Adapter**: `0x5473CBD30bEd1Bf97C0c9d7c59d268CD620dA426`
25+
- **Arbitrum_Adapter**: `0x5eC9844936875E27eBF22172f4d92E107D35B57C`
2626
- Transaction Hash: `Unknown`
27-
- Block Number: `19915048`
27+
- Block Number: `23086601`
2828

2929
### Arbitrum_RescueAdapter
3030

script/001DeployHubPool.s.sol

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import { Script } from "forge-std/Script.sol";
5+
import { Test } from "forge-std/Test.sol";
6+
import { console } from "forge-std/console.sol";
7+
import { HubPool } from "../contracts/HubPool.sol";
8+
import { LpTokenFactory } from "../contracts/LpTokenFactory.sol";
9+
import { FinderInterface } from "@uma/core/contracts/data-verification-mechanism/interfaces/FinderInterface.sol";
10+
import { WETH9Interface } from "../contracts/external/interfaces/WETH9Interface.sol";
11+
import { Constants } from "./utils/Constants.sol";
12+
13+
// How to run:
14+
// 1. `source .env` where `.env` has MNEMONIC="x x x ... x" and ETHERSCAN_API_KEY="x" entries
15+
// 2. forge script script/001DeployHubPool.s.sol:DeployHubPool --rpc-url $NODE_URL_1 -vvvv
16+
// 3. Verify the above works in simulation mode.
17+
// 4. Deploy on mainnet by adding --broadcast --verify flags.
18+
// 5. forge script script/001DeployHubPool.s.sol:DeployHubPool --rpc-url $NODE_URL_1 --broadcast --verify -vvvv
19+
20+
contract DeployHubPool is Script, Test, Constants {
21+
function run() external {
22+
string memory deployerMnemonic = vm.envString("MNEMONIC");
23+
uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0);
24+
25+
// Get the current chain ID
26+
uint256 chainId = block.chainid;
27+
28+
// Get the appropriate addresses for this chain
29+
WETH9Interface weth = getWrappedNativeToken(chainId);
30+
FinderInterface finder = FinderInterface(getL1Addresses(chainId).finder);
31+
32+
vm.startBroadcast(deployerPrivateKey);
33+
34+
// Deploy LpTokenFactory first
35+
LpTokenFactory lpTokenFactory = new LpTokenFactory();
36+
37+
// Deploy HubPool with the LpTokenFactory address
38+
HubPool hubPool = new HubPool(lpTokenFactory, finder, weth, address(0));
39+
40+
// Log the deployed addresses
41+
console.log("Chain ID:", chainId);
42+
console.log("LpTokenFactory deployed to:", address(lpTokenFactory));
43+
console.log("HubPool deployed to:", address(hubPool));
44+
console.log("WETH address:", address(weth));
45+
console.log("Finder address:", address(finder));
46+
47+
vm.stopBroadcast();
48+
}
49+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import { Script } from "forge-std/Script.sol";
5+
import { Test } from "forge-std/Test.sol";
6+
import { console } from "forge-std/console.sol";
7+
import { Optimism_Adapter } from "../contracts/chain-adapters/Optimism_Adapter.sol";
8+
import { Constants } from "./utils/Constants.sol";
9+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
10+
import { ITokenMessenger } from "../contracts/external/interfaces/CCTPInterfaces.sol";
11+
import { IL1StandardBridge } from "@eth-optimism/contracts/L1/messaging/IL1StandardBridge.sol";
12+
13+
// How to run:
14+
// 1. `source .env` where `.env` has MNEMONIC="x x x ... x" and ETHERSCAN_API_KEY="x" entries
15+
// 2. forge script script/002DeployOptimismAdapter.s.sol:DeployOptimismAdapter --rpc-url $NODE_URL_1 -vvvv
16+
// 3. Verify the above works in simulation mode.
17+
// 4. Deploy on mainnet by adding --broadcast --verify flags.
18+
// 5. forge script script/002DeployOptimismAdapter.s.sol:DeployOptimismAdapter --rpc-url $NODE_URL_1 --broadcast --verify -vvvv
19+
20+
contract DeployOptimismAdapter is Script, Test, Constants {
21+
function run() external {
22+
string memory deployerMnemonic = vm.envString("MNEMONIC");
23+
uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0);
24+
25+
// Get the current chain ID
26+
uint256 chainId = block.chainid;
27+
28+
// Verify this is being deployed on Ethereum mainnet or Sepolia
29+
require(
30+
chainId == getChainId("MAINNET") || chainId == getChainId("SEPOLIA"),
31+
"Optimism_Adapter should only be deployed on Ethereum mainnet or Sepolia"
32+
);
33+
34+
// Get OP Stack addresses for this chain and Optimism
35+
uint256 optimismChainId = getChainId("OPTIMISM");
36+
Constants.OpStackAddresses memory opStack = getOpStackAddresses(chainId, optimismChainId);
37+
38+
vm.startBroadcast(deployerPrivateKey);
39+
40+
// Deploy Optimism_Adapter with constructor parameters
41+
Optimism_Adapter optimismAdapter = new Optimism_Adapter(
42+
getWrappedNativeToken(chainId), // L1 WETH
43+
opStack.L1CrossDomainMessenger, // L1 Cross Domain Messenger
44+
IL1StandardBridge(opStack.L1StandardBridge), // L1 Standard Bridge
45+
IERC20(getUSDCAddress(chainId)), // L1 USDC
46+
ITokenMessenger(getL1Addresses(chainId).cctpTokenMessenger) // CCTP Token Messenger
47+
);
48+
49+
// Log the deployed addresses
50+
console.log("Chain ID:", chainId);
51+
console.log("Optimism_Adapter deployed to:", address(optimismAdapter));
52+
console.log("L1 WETH:", address(getWrappedNativeToken(chainId)));
53+
console.log("L1 Cross Domain Messenger:", opStack.L1CrossDomainMessenger);
54+
console.log("L1 Standard Bridge:", opStack.L1StandardBridge);
55+
console.log("L1 USDC:", getUSDCAddress(chainId));
56+
console.log("CCTP Token Messenger:", getL1Addresses(chainId).cctpTokenMessenger);
57+
58+
vm.stopBroadcast();
59+
}
60+
}
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import { Script } from "forge-std/Script.sol";
5+
import { Test } from "forge-std/Test.sol";
6+
import { console } from "forge-std/console.sol";
7+
import { Optimism_SpokePool } from "../contracts/Optimism_SpokePool.sol";
8+
import { WETH9Interface } from "../contracts/external/interfaces/WETH9Interface.sol";
9+
import { DeploymentUtils } from "./utils/DeploymentUtils.sol";
10+
11+
// How to run:
12+
// 1. `source .env` where `.env` has MNEMONIC="x x x ... x" and HUBPOOL_ADDRESS="0x..." entries
13+
// 2. forge script script/003DeployOptimismSpokePool.s.sol:DeployOptimismSpokePool --rpc-url $NODE_URL_OPTIMISM -vvvv
14+
// 3. Verify the above works in simulation mode.
15+
// 4. Deploy with: forge script script/003DeployOptimismSpokePool.s.sol:DeployOptimismSpokePool --rpc-url $NODE_URL_OPTIMISM --broadcast --verify
16+
17+
contract DeployOptimismSpokePool is Script, Test, DeploymentUtils {
18+
function run() external {
19+
string memory deployerMnemonic = vm.envString("MNEMONIC");
20+
uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0);
21+
22+
// Get deployment information
23+
DeploymentInfo memory info = getSpokePoolDeploymentInfo(address(0)); // Will use HUBPOOL_ADDRESS from env
24+
25+
console.log("HubPool address:", info.hubPool);
26+
27+
// Get the appropriate addresses for this chain
28+
WETH9Interface weth = getWrappedNativeToken(info.spokeChainId);
29+
30+
// Get L2 addresses for Optimism
31+
address cctpTokenMessenger = getL2Address(info.spokeChainId, "cctpTokenMessenger");
32+
33+
vm.startBroadcast(deployerPrivateKey);
34+
35+
// Prepare constructor arguments for Optimism_SpokePool
36+
bytes memory constructorArgs = abi.encode(
37+
address(weth), // _wrappedNativeTokenAddress
38+
QUOTE_TIME_BUFFER(), // _depositQuoteTimeBuffer
39+
FILL_DEADLINE_BUFFER(), // _fillDeadlineBuffer
40+
getUSDCAddress(info.spokeChainId), // _l2Usdc
41+
cctpTokenMessenger // _cctpTokenMessenger
42+
);
43+
44+
// Initialize deposit counter to very high number of deposits to avoid duplicate deposit ID's
45+
// with deprecated spoke pool.
46+
bytes memory initArgs = abi.encodeWithSelector(
47+
Optimism_SpokePool.initialize.selector,
48+
1_000_000, // _initialDepositId
49+
info.hubPool, // _crossDomainAdmin
50+
info.hubPool // _withdrawalRecipient
51+
);
52+
53+
// Deploy the proxy
54+
DeploymentResult memory result = deployNewProxy(
55+
"Optimism_SpokePool",
56+
constructorArgs,
57+
initArgs,
58+
true // implementationOnly
59+
);
60+
61+
// Log the deployed addresses
62+
console.log("Chain ID:", info.spokeChainId);
63+
console.log("Hub Chain ID:", info.hubChainId);
64+
console.log("HubPool address:", info.hubPool);
65+
console.log("WETH address:", address(weth));
66+
console.log("CCTP Token Messenger:", cctpTokenMessenger);
67+
console.log("USDC address:", getUSDCAddress(info.spokeChainId));
68+
console.log("Optimism_SpokePool proxy deployed to:", result.proxy);
69+
console.log("Optimism_SpokePool implementation deployed to:", result.implementation);
70+
71+
console.log("QUOTE_TIME_BUFFER()", QUOTE_TIME_BUFFER());
72+
console.log("FILL_DEADLINE_BUFFER()", FILL_DEADLINE_BUFFER());
73+
74+
vm.stopBroadcast();
75+
}
76+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import { Script } from "forge-std/Script.sol";
5+
import { Test } from "forge-std/Test.sol";
6+
import { console } from "forge-std/console.sol";
7+
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
8+
import { Arbitrum_Adapter } from "../contracts/chain-adapters/Arbitrum_Adapter.sol";
9+
import { Constants } from "./utils/Constants.sol";
10+
11+
import { ITokenMessenger } from "../contracts/external/interfaces/CCTPInterfaces.sol";
12+
import { ArbitrumInboxLike as ArbitrumL1InboxLike, ArbitrumL1ERC20GatewayLike } from "../contracts/interfaces/ArbitrumBridge.sol";
13+
14+
// How to run:
15+
// 1. `source .env` where `.env` has MNEMONIC="x x x ... x" and ETHERSCAN_API_KEY="x" entries
16+
// 2. forge script script/004DeployArbitrumAdapter.s.sol:DeployArbitrumAdapter --rpc-url $NODE_URL_1 -vvvv
17+
// 3. Verify the above works in simulation mode.
18+
// 4. Deploy on mainnet by adding --broadcast --verify flags.
19+
// 5. forge script script/004DeployArbitrumAdapter.s.sol:DeployArbitrumAdapter --rpc-url $NODE_URL_1 --broadcast --verify -vvvv
20+
21+
contract DeployArbitrumAdapter is Script, Test, Constants {
22+
function run() external {
23+
string memory deployerMnemonic = vm.envString("MNEMONIC");
24+
uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0);
25+
26+
// Get the current chain ID
27+
uint256 chainId = block.chainid;
28+
29+
// Verify this is being deployed on Ethereum mainnet or Sepolia
30+
require(
31+
chainId == getChainId("MAINNET") || chainId == getChainId("SEPOLIA"),
32+
"Arbitrum_Adapter should only be deployed on Ethereum mainnet or Sepolia"
33+
);
34+
35+
// This address receives gas refunds on the L2 after messages are relayed. Currently
36+
// set to the Risk Labs relayer address. The deployer should change this if necessary.
37+
address l2RefundAddress = 0x07aE8551Be970cB1cCa11Dd7a11F47Ae82e70E67;
38+
39+
// Determine the spoke chain ID (Arbitrum mainnet or testnet)
40+
uint256 spokeChainId;
41+
if (chainId == getChainId("MAINNET")) {
42+
spokeChainId = getChainId("ARBITRUM");
43+
} else {
44+
spokeChainId = getChainId("ARBITRUM_SEPOLIA");
45+
}
46+
47+
// Get OFT destination endpoint ID and fee cap
48+
uint32 oftDstEid = uint32(getOftEid(spokeChainId));
49+
uint256 oftFeeCap = 1e18; // 1 eth transfer fee cap
50+
51+
// Get L1 addresses for this chain
52+
Constants.L1Addresses memory l1Addresses = getL1Addresses(chainId);
53+
54+
vm.startBroadcast(deployerPrivateKey);
55+
56+
// Deploy Arbitrum_Adapter with constructor parameters
57+
Arbitrum_Adapter arbitrumAdapter = new Arbitrum_Adapter(
58+
ArbitrumL1InboxLike(l1Addresses.l1ArbitrumInbox),
59+
ArbitrumL1ERC20GatewayLike(l1Addresses.l1ERC20GatewayRouter),
60+
l2RefundAddress,
61+
IERC20(getUSDCAddress(chainId)),
62+
ITokenMessenger(l1Addresses.cctpTokenMessenger),
63+
l1Addresses.adapterStore, // This might need to be deployed first or set to address(0)
64+
oftDstEid,
65+
oftFeeCap
66+
);
67+
68+
// Log the deployed addresses
69+
console.log("Chain ID:", chainId);
70+
console.log("Arbitrum_Adapter deployed to:", address(arbitrumAdapter));
71+
console.log("L1 Arbitrum Inbox:", l1Addresses.l1ArbitrumInbox);
72+
console.log("L1 ERC20 Gateway Router:", l1Addresses.l1ERC20GatewayRouter);
73+
console.log("L2 Refund Address:", l2RefundAddress);
74+
console.log("USDC Address:", getUSDCAddress(chainId));
75+
console.log("CCTP Token Messenger:", l1Addresses.cctpTokenMessenger);
76+
console.log("Adapter Store:", l1Addresses.adapterStore);
77+
console.log("OFT Destination EID:", oftDstEid);
78+
console.log("OFT Fee Cap:", oftFeeCap);
79+
80+
vm.stopBroadcast();
81+
}
82+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import { Script } from "forge-std/Script.sol";
5+
import { Test } from "forge-std/Test.sol";
6+
import { console } from "forge-std/console.sol";
7+
import { Arbitrum_SpokePool } from "../contracts/Arbitrum_SpokePool.sol";
8+
import { WETH9Interface } from "../contracts/external/interfaces/WETH9Interface.sol";
9+
import { DeploymentUtils } from "./utils/DeploymentUtils.sol";
10+
11+
// How to run:
12+
// 1. `source .env` where `.env` has MNEMONIC="x x x ... x"
13+
// 2. forge script script/008DeployArbitrumSpokePool.s.sol:DeployArbitrumSpokePool --rpc-url $NODE_URL_1 -vvvv
14+
// 3. Verify the above works in simulation mode.
15+
// 4. Deploy with: forge script script/008DeployArbitrumSpokePool.s.sol:DeployArbitrumSpokePool --rpc-url $NODE_URL_1 --broadcast --verify
16+
17+
contract DeployArbitrumSpokePool is Script, Test, DeploymentUtils {
18+
function run() external {
19+
string memory deployerMnemonic = vm.envString("MNEMONIC");
20+
uint256 deployerPrivateKey = vm.deriveKey(deployerMnemonic, 0);
21+
22+
// Get deployment information
23+
DeploymentInfo memory info = getSpokePoolDeploymentInfo(address(0));
24+
25+
console.log("HubPool address:", info.hubPool);
26+
27+
// Get the appropriate addresses for this chain
28+
WETH9Interface weth = getWrappedNativeToken(info.spokeChainId);
29+
30+
// Get L2 addresses for Arbitrum
31+
address l2GatewayRouter = getL2Address(info.spokeChainId, "l2GatewayRouter");
32+
address cctpTokenMessenger = getL2Address(info.spokeChainId, "cctpTokenMessenger");
33+
34+
vm.startBroadcast(deployerPrivateKey);
35+
36+
// Prepare constructor arguments for Arbitrum_SpokePool
37+
bytes memory constructorArgs = abi.encode(
38+
address(weth), // _weth
39+
QUOTE_TIME_BUFFER(), // _quoteTimeBuffer
40+
FILL_DEADLINE_BUFFER(), // _fillDeadlineBuffer
41+
getUSDCAddress(info.spokeChainId), // _usdc
42+
cctpTokenMessenger, // _cctpTokenMessenger
43+
getOftEid(info.hubChainId), // _oftDstEid
44+
1 ether // _oftFeeCap
45+
);
46+
47+
// Initialize deposit counter to very high number of deposits to avoid duplicate deposit ID's
48+
// with deprecated spoke pool.
49+
bytes memory initArgs = abi.encodeWithSelector(
50+
Arbitrum_SpokePool.initialize.selector,
51+
1_000_000, // _initialDepositId
52+
l2GatewayRouter, // _l2GatewayRouter
53+
info.hubPool, // _crossDomainAdmin
54+
info.hubPool // _hubPool
55+
);
56+
57+
// Deploy the proxy
58+
DeploymentResult memory result = deployNewProxy(
59+
"Arbitrum_SpokePool",
60+
constructorArgs,
61+
initArgs,
62+
true // implementationOnly
63+
);
64+
65+
// Log the deployed addresses
66+
console.log("Chain ID:", info.spokeChainId);
67+
console.log("Hub Chain ID:", info.hubChainId);
68+
console.log("HubPool address:", info.hubPool);
69+
console.log("WETH address:", address(weth));
70+
console.log("L2 Gateway Router:", l2GatewayRouter);
71+
console.log("CCTP Token Messenger:", cctpTokenMessenger);
72+
console.log("USDC address:", getUSDCAddress(info.spokeChainId));
73+
console.log("Arbitrum_SpokePool proxy deployed to:", result.proxy);
74+
console.log("Arbitrum_SpokePool implementation deployed to:", result.implementation);
75+
76+
console.log("QUOTE_TIME_BUFFER()", QUOTE_TIME_BUFFER());
77+
console.log("FILL_DEADLINE_BUFFER()", FILL_DEADLINE_BUFFER());
78+
console.log("OFT EID", getOftEid(info.hubChainId));
79+
80+
vm.stopBroadcast();
81+
}
82+
}

0 commit comments

Comments
 (0)