|
1 | | -## Foundry |
| 1 | +# EV-Reth Contracts |
2 | 2 |
|
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. |
4 | 4 |
|
5 | | -Foundry consists of: |
| 5 | +## FeeVault |
6 | 6 |
|
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: |
11 | 8 |
|
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 |
13 | 13 |
|
14 | | -https://book.getfoundry.sh/ |
| 14 | +## Prerequisites |
15 | 15 |
|
16 | | -## Usage |
| 16 | +- [Foundry](https://book.getfoundry.sh/getting-started/installation) |
17 | 17 |
|
18 | | -### Build |
| 18 | +## Build |
19 | 19 |
|
20 | 20 | ```shell |
21 | | -$ forge build |
| 21 | +forge build |
22 | 22 | ``` |
23 | 23 |
|
24 | | -### Test |
| 24 | +## Test |
25 | 25 |
|
26 | 26 | ```shell |
27 | | -$ forge test |
| 27 | +forge test |
28 | 28 | ``` |
29 | 29 |
|
30 | | -### Format |
| 30 | +## Deploying FeeVault |
31 | 31 |
|
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 | |
35 | 46 |
|
36 | | -### Gas Snapshots |
| 47 | +### Choosing a Salt |
| 48 | + |
| 49 | +Any bytes32 value works as a salt. Common approaches: |
37 | 50 |
|
38 | 51 | ```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") |
40 | 57 | ``` |
41 | 58 |
|
42 | | -### Anvil |
| 59 | +### Compute Address Before Deploying |
| 60 | + |
| 61 | +To see what address will be deployed to without actually deploying: |
43 | 62 |
|
44 | 63 | ```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 |
46 | 69 | ``` |
47 | 70 |
|
48 | 71 | ### Deploy |
49 | 72 |
|
50 | 73 | ```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 |
52 | 95 | ``` |
53 | 96 |
|
54 | | -### Cast |
| 97 | +### Post-Deployment: Set HypNativeMinter |
| 98 | + |
| 99 | +After deploying the HypNativeMinter contract, link it to the FeeVault: |
55 | 100 |
|
56 | 101 | ```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> |
58 | 105 | ``` |
59 | 106 |
|
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: |
61 | 115 |
|
62 | 116 | ```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 |
66 | 119 | ``` |
| 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 | |
0 commit comments