-
Notifications
You must be signed in to change notification settings - Fork 7
modify contract for deploying native minter later #79
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
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
ab96c20
modify contract for deploying native minter later
tac0turtle 1fa6f24
Merge branch 'main' into marko/modify_contract
tac0turtle 868986d
create2
tac0turtle 7fca23f
update readme
tac0turtle adcc953
adjust readme
tac0turtle 5411649
fmt
tac0turtle File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,66 +1,140 @@ | ||
| ## Foundry | ||
| # EV-Reth Contracts | ||
|
|
||
| **Foundry is a blazing fast, portable and modular toolkit for Ethereum application development written in Rust.** | ||
| Smart contracts for EV-Reth, including the FeeVault for bridging collected fees to Celestia. | ||
|
|
||
| Foundry consists of: | ||
| ## FeeVault | ||
|
|
||
| - **Forge**: Ethereum testing framework (like Truffle, Hardhat and DappTools). | ||
| - **Cast**: Swiss army knife for interacting with EVM smart contracts, sending transactions and getting chain data. | ||
| - **Anvil**: Local Ethereum node, akin to Ganache, Hardhat Network. | ||
| - **Chisel**: Fast, utilitarian, and verbose solidity REPL. | ||
| The FeeVault contract collects base fees and bridges them to Celestia via Hyperlane. It supports: | ||
|
|
||
| ## Documentation | ||
| - Configurable fee splitting between bridge and another recipient | ||
| - Minimum amount thresholds before bridging | ||
| - Call fee for incentivizing bridge calls | ||
| - Owner-controlled configuration | ||
|
|
||
| https://book.getfoundry.sh/ | ||
| ## Prerequisites | ||
|
|
||
| ## Usage | ||
| - [Foundry](https://book.getfoundry.sh/getting-started/installation) | ||
|
|
||
| ### Build | ||
| ## Build | ||
|
|
||
| ```shell | ||
| $ forge build | ||
| forge build | ||
| ``` | ||
|
|
||
| ### Test | ||
| ## Test | ||
|
|
||
| ```shell | ||
| $ forge test | ||
| forge test | ||
| ``` | ||
|
|
||
| ### Format | ||
| ## Deploying FeeVault | ||
|
|
||
| ```shell | ||
| $ forge fmt | ||
| ``` | ||
| The FeeVault uses CREATE2 for deterministic addresses across chains. | ||
|
|
||
| ### Environment Variables | ||
|
|
||
| | Variable | Deploy | Operational | Description | | ||
| |----------|--------|-------------|-------------| | ||
| | `OWNER` | Required | - | Owner address (can configure the vault) | | ||
| | `SALT` | Optional | - | CREATE2 salt (default: `0x0`). Use any bytes32 value | | ||
| | `DESTINATION_DOMAIN` | Optional | Required | Hyperlane destination chain ID | | ||
| | `RECIPIENT_ADDRESS` | Optional | Required | Recipient on destination chain (bytes32, left-padded) | | ||
| | `MINIMUM_AMOUNT` | Optional | Optional | Minimum wei to bridge | | ||
| | `CALL_FEE` | Optional | Optional | Fee in wei for calling `sendToCelestia()` | | ||
| | `BRIDGE_SHARE_BPS` | Optional | Optional | Basis points to bridge (default: 10000 = 100%) | | ||
| | `OTHER_RECIPIENT` | Optional | Required* | Address to receive non-bridged portion | | ||
|
|
||
| *`OTHER_RECIPIENT` is required only if `BRIDGE_SHARE_BPS` < 10000 | ||
|
|
||
| ### Gas Snapshots | ||
| **Note:** `HYP_NATIVE_MINTER` must be set via `setHypNativeMinter()` after deployment for the vault to be operational. | ||
|
|
||
| ### Choosing a Salt | ||
|
|
||
| Any bytes32 value works as a salt. Common approaches: | ||
|
|
||
| ```shell | ||
| $ forge snapshot | ||
| # Simple approach - just use a version number | ||
| export SALT=0x0000000000000000000000000000000000000000000000000000000000000001 | ||
|
|
||
| # Or hash a meaningful string | ||
| export SALT=$(cast keccak "FeeVault-v1") | ||
| ``` | ||
|
|
||
| ### Anvil | ||
| ### Compute Address Before Deploying | ||
|
|
||
| To see what address will be deployed to without actually deploying: | ||
|
|
||
| ```shell | ||
| $ anvil | ||
| export OWNER=0xYourOwnerAddress | ||
| export SALT=0x0000000000000000000000000000000000000000000000000000000000000001 | ||
| export DEPLOYER=0xYourDeployerAddress # The address that will run the script | ||
|
|
||
| forge script script/DeployFeeVault.s.sol:ComputeFeeVaultAddress | ||
| ``` | ||
|
|
||
| ### Deploy | ||
|
|
||
| ```shell | ||
| $ forge script script/Counter.s.sol:CounterScript --rpc-url <your_rpc_url> --private-key <your_private_key> | ||
| # Required | ||
| export OWNER=0xYourOwnerAddress | ||
| export SALT=0x0000000000000000000000000000000000000000000000000000000000000001 | ||
|
|
||
| # Optional - configure at deploy time (can also be set later) | ||
| export DESTINATION_DOMAIN=1234 | ||
| export RECIPIENT_ADDRESS=0x000000000000000000000000... # bytes32, left-padded cosmos address | ||
| export MINIMUM_AMOUNT=1000000000000000000 # 1 ETH in wei | ||
| export CALL_FEE=100000000000000 # 0.0001 ETH | ||
| export BRIDGE_SHARE_BPS=8000 # 80% to bridge | ||
| export OTHER_RECIPIENT=0xOtherAddress | ||
|
|
||
| # Dry run (no broadcast) | ||
| forge script script/DeployFeeVault.s.sol:DeployFeeVault \ | ||
| --rpc-url <RPC_URL> | ||
|
|
||
| # Deploy for real | ||
| forge script script/DeployFeeVault.s.sol:DeployFeeVault \ | ||
| --rpc-url <RPC_URL> \ | ||
| --private-key <PRIVATE_KEY> \ | ||
| --broadcast | ||
| ``` | ||
|
|
||
| ### Cast | ||
| ### Post-Deployment: Set HypNativeMinter | ||
|
|
||
| After deploying the HypNativeMinter contract, link it to the FeeVault: | ||
|
|
||
| ```shell | ||
| $ cast <subcommand> | ||
| cast send <FEEVAULT_ADDRESS> "setHypNativeMinter(address)" <HYP_NATIVE_MINTER_ADDRESS> \ | ||
| --rpc-url <RPC_URL> \ | ||
| --private-key <PRIVATE_KEY> | ||
| ``` | ||
|
|
||
| ### Help | ||
| ### Converting Cosmos Addresses to bytes32 | ||
|
|
||
| The `recipientAddress` must be a bytes32. To convert a bech32 Cosmos address: | ||
|
|
||
| 1. Decode the bech32 to get the 20-byte address | ||
| 2. Left-pad with zeros to 32 bytes | ||
|
|
||
| Example using cast: | ||
|
|
||
| ```shell | ||
| $ forge --help | ||
| $ anvil --help | ||
| $ cast --help | ||
| # Left-pad a 20-byte address to 32 bytes | ||
| cast pad --left --len 32 1234567890abcdef1234567890abcdef12345678 | ||
| # Output: 0x0000000000000000000000001234567890abcdef1234567890abcdef12345678 | ||
| ``` | ||
|
|
||
| Note: When calling `transferRemote()` via cast, you may need to omit the `0x` prefix depending on your invocation method. | ||
|
|
||
| ## Admin Functions | ||
|
|
||
| All functions are owner-only: | ||
|
|
||
| | Function | Description | | ||
| |----------|-------------| | ||
| | `setHypNativeMinter(address)` | Set the Hyperlane minter contract | | ||
| | `setRecipient(uint32, bytes32)` | Set destination domain and recipient | | ||
| | `setMinimumAmount(uint256)` | Set minimum amount to bridge | | ||
| | `setCallFee(uint256)` | Set fee for calling sendToCelestia | | ||
| | `setBridgeShare(uint256)` | Set bridge percentage (basis points) | | ||
| | `setOtherRecipient(address)` | Set recipient for non-bridged funds | | ||
| | `transferOwnership(address)` | Transfer contract ownership | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,91 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.24; | ||
|
|
||
| import {Script, console} from "forge-std/Script.sol"; | ||
| import {FeeVault} from "../src/FeeVault.sol"; | ||
|
|
||
| contract DeployFeeVault is Script { | ||
| function run() external { | ||
| // ========== CONFIGURATION ========== | ||
| address owner = vm.envAddress("OWNER"); | ||
| bytes32 salt = vm.envOr("SALT", bytes32(0)); | ||
|
|
||
| // Optional: Post-deployment configuration | ||
| uint32 destinationDomain = uint32(vm.envOr("DESTINATION_DOMAIN", uint256(0))); | ||
| bytes32 recipientAddress = vm.envOr("RECIPIENT_ADDRESS", bytes32(0)); | ||
| uint256 minimumAmount = vm.envOr("MINIMUM_AMOUNT", uint256(0)); | ||
| uint256 callFee = vm.envOr("CALL_FEE", uint256(0)); | ||
| uint256 bridgeShareBps = vm.envOr("BRIDGE_SHARE_BPS", uint256(10000)); | ||
| address otherRecipient = vm.envOr("OTHER_RECIPIENT", address(0)); | ||
| // =================================== | ||
|
|
||
| // Compute address before deployment | ||
| address predicted = computeAddress(salt, owner); | ||
| console.log("Predicted FeeVault address:", predicted); | ||
|
|
||
| vm.startBroadcast(); | ||
|
|
||
| // Deploy FeeVault with CREATE2 | ||
| FeeVault feeVault = new FeeVault{salt: salt}(owner); | ||
| console.log("FeeVault deployed at:", address(feeVault)); | ||
| require(address(feeVault) == predicted, "Address mismatch"); | ||
|
|
||
| // Configure if values provided | ||
| if (destinationDomain != 0 && recipientAddress != bytes32(0)) { | ||
| feeVault.setRecipient(destinationDomain, recipientAddress); | ||
| console.log("Recipient set - domain:", destinationDomain); | ||
| } | ||
|
|
||
| if (minimumAmount > 0) { | ||
| feeVault.setMinimumAmount(minimumAmount); | ||
| console.log("Minimum amount set:", minimumAmount); | ||
| } | ||
|
|
||
| if (callFee > 0) { | ||
| feeVault.setCallFee(callFee); | ||
| console.log("Call fee set:", callFee); | ||
| } | ||
|
|
||
| if (bridgeShareBps != 10000) { | ||
| feeVault.setBridgeShare(bridgeShareBps); | ||
| console.log("Bridge share set:", bridgeShareBps, "bps"); | ||
| } | ||
|
|
||
| if (otherRecipient != address(0)) { | ||
| feeVault.setOtherRecipient(otherRecipient); | ||
| console.log("Other recipient set:", otherRecipient); | ||
| } | ||
|
|
||
| vm.stopBroadcast(); | ||
|
|
||
| console.log(""); | ||
| console.log("NOTE: Call setHypNativeMinter() after deploying HypNativeMinter"); | ||
| } | ||
|
|
||
| /// @notice Compute the CREATE2 address for FeeVault deployment | ||
| function computeAddress(bytes32 salt, address owner) public view returns (address) { | ||
| bytes32 bytecodeHash = keccak256(abi.encodePacked(type(FeeVault).creationCode, abi.encode(owner))); | ||
| return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, bytecodeHash))))); | ||
| } | ||
| } | ||
|
|
||
| /// @notice Standalone script to compute FeeVault address without deploying | ||
| contract ComputeFeeVaultAddress is Script { | ||
| function run() external view { | ||
| address owner = vm.envAddress("OWNER"); | ||
| bytes32 salt = vm.envOr("SALT", bytes32(0)); | ||
| address deployer = vm.envAddress("DEPLOYER"); | ||
|
|
||
| bytes32 bytecodeHash = keccak256(abi.encodePacked(type(FeeVault).creationCode, abi.encode(owner))); | ||
|
|
||
| address predicted = | ||
| address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), deployer, salt, bytecodeHash))))); | ||
|
|
||
| console.log("========== FeeVault Address Computation =========="); | ||
| console.log("Owner:", owner); | ||
| console.log("Salt:", vm.toString(salt)); | ||
| console.log("Deployer:", deployer); | ||
| console.log("Predicted address:", predicted); | ||
| console.log("=================================================="); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
hypNativeMinteraddress can be updated multiple times by the owner. This was previously animmutablevariable, which provided stronger security guarantees. If the intention is to only set this address once after deployment, consider adding a check to prevent it from being updated again. This would reduce the trust placed in the owner and prevent potential malicious updates in the future.