Skip to content

Commit 868986d

Browse files
committed
create2
1 parent 1fa6f24 commit 868986d

2 files changed

Lines changed: 146 additions & 31 deletions

File tree

contracts/README.md

Lines changed: 96 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,133 @@
1-
## Foundry
1+
# EV-Reth Contracts
22

3-
**Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.**
3+
Smart contracts for EV-Reth, including the FeeVault for bridging collected fees to Celestia.
44

5-
Foundry consists of:
5+
## FeeVault
66

7-
- **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools).
8-
- **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data.
9-
- **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network.
10-
- **Chisel**: Fast, utilitarian, and verbose solidity REPL.
7+
The FeeVault contract collects base fees and bridges them to Celestia via Hyperlane. It supports:
118

12-
## Documentation
9+
- Configurable fee splitting between bridge and another recipient
10+
- Minimum amount thresholds before bridging
11+
- Call fee for incentivizing bridge calls
12+
- Owner-controlled configuration
1313

14-
https://book.getfoundry.sh/
14+
## Prerequisites
1515

16-
## Usage
16+
- [Foundry](https://book.getfoundry.sh/getting-started/installation)
1717

18-
### Build
18+
## Build
1919

2020
```shell
21-
$ forge build
21+
forge build
2222
```
2323

24-
### Test
24+
## Test
2525

2626
```shell
27-
$ forge test
27+
forge test
2828
```
2929

30-
### Format
30+
## Deploying FeeVault
3131

32-
```shell
33-
$ forge fmt
34-
```
32+
The FeeVault uses CREATE2 for deterministic addresses across chains.
33+
34+
### Environment Variables
35+
36+
| Variable | Required | Description |
37+
|----------|----------|-------------|
38+
| `OWNER` | Yes | Owner address (can configure the vault) |
39+
| `SALT` | No | CREATE2 salt (default: `0x0`). Use any bytes32 value |
40+
| `DESTINATION_DOMAIN` | No | Hyperlane destination chain ID |
41+
| `RECIPIENT_ADDRESS` | No | Recipient on destination chain (bytes32, left-padded) |
42+
| `MINIMUM_AMOUNT` | No | Minimum wei to bridge |
43+
| `CALL_FEE` | No | Fee in wei for calling `sendToCelestia()` |
44+
| `BRIDGE_SHARE_BPS` | No | Basis points to bridge (default: 10000 = 100%) |
45+
| `OTHER_RECIPIENT` | No | Address to receive non-bridged portion |
3546

36-
### Gas Snapshots
47+
### Choosing a Salt
48+
49+
Any bytes32 value works as a salt. Common approaches:
3750

3851
```shell
39-
$ forge snapshot
52+
# Simple approach - just use a version number
53+
export SALT=0x0000000000000000000000000000000000000000000000000000000000000001
54+
55+
# Or hash a meaningful string
56+
export SALT=$(cast keccak "FeeVault-v1")
4057
```
4158

42-
### Anvil
59+
### Compute Address Before Deploying
60+
61+
To see what address will be deployed to without actually deploying:
4362

4463
```shell
45-
$ anvil
64+
export OWNER=0xYourOwnerAddress
65+
export SALT=0x0000000000000000000000000000000000000000000000000000000000000001
66+
export DEPLOYER=0xYourDeployerAddress # The address that will run the script
67+
68+
forge script script/DeployFeeVault.s.sol:ComputeFeeVaultAddress
4669
```
4770

4871
### Deploy
4972

5073
```shell
51-
$ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key>
74+
# Required
75+
export OWNER=0xYourOwnerAddress
76+
export SALT=0x0000000000000000000000000000000000000000000000000000000000000001
77+
78+
# Optional - configure at deploy time (can also be set later)
79+
export DESTINATION_DOMAIN=1234
80+
export RECIPIENT_ADDRESS=0x000000000000000000000000... # bytes32, left-padded cosmos address
81+
export MINIMUM_AMOUNT=1000000000000000000 # 1 ETH in wei
82+
export CALL_FEE=100000000000000 # 0.0001 ETH
83+
export BRIDGE_SHARE_BPS=8000 # 80% to bridge
84+
export OTHER_RECIPIENT=0xOtherAddress
85+
86+
# Dry run (no broadcast)
87+
forge script script/DeployFeeVault.s.sol:DeployFeeVault \
88+
--rpc-url <RPC_URL>
89+
90+
# Deploy for real
91+
forge script script/DeployFeeVault.s.sol:DeployFeeVault \
92+
--rpc-url <RPC_URL> \
93+
--private-key <PRIVATE_KEY> \
94+
--broadcast
5295
```
5396

54-
### Cast
97+
### Post-Deployment: Set HypNativeMinter
98+
99+
After deploying the HypNativeMinter contract, link it to the FeeVault:
55100

56101
```shell
57-
$ cast <subcommand>
102+
cast send <FEEVAULT_ADDRESS> "setHypNativeMinter(address)" <HYP_NATIVE_MINTER_ADDRESS> \
103+
--rpc-url <RPC_URL> \
104+
--private-key <PRIVATE_KEY>
58105
```
59106

60-
### Help
107+
### Converting Cosmos Addresses to bytes32
108+
109+
The `recipientAddress` must be a bytes32. To convert a bech32 Cosmos address:
110+
111+
1. Decode the bech32 to get the 20-byte address
112+
2. Left-pad with zeros to 32 bytes
113+
114+
Example using cast:
61115

62116
```shell
63-
$ forge --help
64-
$ anvil --help
65-
$ cast --help
117+
# If you have the raw 20-byte hex address
118+
cast to-bytes32 0x1234567890abcdef1234567890abcdef12345678
66119
```
120+
121+
## Admin Functions
122+
123+
All functions are owner-only:
124+
125+
| Function | Description |
126+
|----------|-------------|
127+
| `setHypNativeMinter(address)` | Set the Hyperlane minter contract |
128+
| `setRecipient(uint32, bytes32)` | Set destination domain and recipient |
129+
| `setMinimumAmount(uint256)` | Set minimum amount to bridge |
130+
| `setCallFee(uint256)` | Set fee for calling sendToCelestia |
131+
| `setBridgeShare(uint256)` | Set bridge percentage (basis points) |
132+
| `setOtherRecipient(address)` | Set recipient for non-bridged funds |
133+
| `transferOwnership(address)` | Transfer contract ownership |

contracts/script/DeployFeeVault.s.sol

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ contract DeployFeeVault is Script {
88
function run() external {
99
// ========== CONFIGURATION ==========
1010
address owner = vm.envAddress("OWNER");
11+
bytes32 salt = vm.envOr("SALT", bytes32(0));
1112

1213
// Optional: Post-deployment configuration
1314
uint32 destinationDomain = uint32(vm.envOr("DESTINATION_DOMAIN", uint256(0)));
@@ -18,11 +19,16 @@ contract DeployFeeVault is Script {
1819
address otherRecipient = vm.envOr("OTHER_RECIPIENT", address(0));
1920
// ===================================
2021

22+
// Compute address before deployment
23+
address predicted = computeAddress(salt, owner);
24+
console.log("Predicted FeeVault address:", predicted);
25+
2126
vm.startBroadcast();
2227

23-
// Deploy FeeVault
24-
FeeVault feeVault = new FeeVault(owner);
28+
// Deploy FeeVault with CREATE2
29+
FeeVault feeVault = new FeeVault{salt: salt}(owner);
2530
console.log("FeeVault deployed at:", address(feeVault));
31+
require(address(feeVault) == predicted, "Address mismatch");
2632

2733
// Configure if values provided
2834
if (destinationDomain != 0 && recipientAddress != bytes32(0)) {
@@ -55,4 +61,46 @@ contract DeployFeeVault is Script {
5561
console.log("");
5662
console.log("NOTE: Call setHypNativeMinter() after deploying HypNativeMinter");
5763
}
64+
65+
/// @notice Compute the CREATE2 address for FeeVault deployment
66+
function computeAddress(bytes32 salt, address owner) public view returns (address) {
67+
bytes32 bytecodeHash = keccak256(
68+
abi.encodePacked(type(FeeVault).creationCode, abi.encode(owner))
69+
);
70+
return address(
71+
uint160(
72+
uint256(
73+
keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, bytecodeHash))
74+
)
75+
)
76+
);
77+
}
78+
}
79+
80+
/// @notice Standalone script to compute FeeVault address without deploying
81+
contract ComputeFeeVaultAddress is Script {
82+
function run() external view {
83+
address owner = vm.envAddress("OWNER");
84+
bytes32 salt = vm.envOr("SALT", bytes32(0));
85+
address deployer = vm.envAddress("DEPLOYER");
86+
87+
bytes32 bytecodeHash = keccak256(
88+
abi.encodePacked(type(FeeVault).creationCode, abi.encode(owner))
89+
);
90+
91+
address predicted = address(
92+
uint160(
93+
uint256(
94+
keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash))
95+
)
96+
)
97+
);
98+
99+
console.log("========== FeeVault Address Computation ==========");
100+
console.log("Owner:", owner);
101+
console.log("Salt:", vm.toString(salt));
102+
console.log("Deployer:", deployer);
103+
console.log("Predicted address:", predicted);
104+
console.log("==================================================");
105+
}
58106
}

0 commit comments

Comments
 (0)