diff --git a/README.md b/README.md index 564c83a..477128d 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,14 @@ To enable bridging to native ether, bridging contracts need be able to mint/burn - `mint`: allows whitelisted addresses to mint specific amount of native ether to any account. - `burn`: allows whitelisted addresses to burn specific amount of native ether from any account. +### Fee Vault + +The mev-commit chain implements a fee mechanism where all base fees accumulate to a "fee vault" contract. The predetermined address for such a contract is hardcoded into mev-commit-geth. + +The fee vault contract is initiated with an immutable treasury address. The contract owner can invoke transfers of the accumulated fees from the sidechain contract, to the treasury address on L1, including bridging. + +Since the treasury address is immutable, the contract owner only has the power to initiate transfers. The separation between the owner account and treasury account is useful in that key management can be separate. Ie. a secure multisig governing the protocol treasury doesn't need to submit regular transfer transactions. + ## Tests The tests in this repository perform the following: @@ -186,6 +194,7 @@ ProviderRegistry deployed to: 0xBdd9CEd825167c0D616B0284BfACD034fF02D41D PreConfCommitmentStore deployed to: 0xFF435EC71C7cB6fEFbFfA8B1275c08c7e121E9Aa Oracle deployed to: 0xC68573B65444c21A4D9cE88B92DFEA222956B4E5 Whitelist deployed to: 0xC2009B1b9036db2931349F2242eDA081FEc43D91 +Fee Vault deployed to: TODO ``` #### Test Contracts diff --git a/contracts/FeeVault.sol b/contracts/FeeVault.sol new file mode 100644 index 0000000..1bb1703 --- /dev/null +++ b/contracts/FeeVault.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: BSL 1.1 +pragma solidity ^0.8.15; + +import "@openzeppelin/contracts/access/Ownable.sol"; + +// See hyperlane token router: https://github.com/primevprotocol/hyperlane-monorepo/blob/1c8cdc9e57389024274242d28e032a2de535c2c7/solidity/contracts/token/libs/TokenRouter.sol +interface ITokenRouter { + function transferRemote(uint32 _destination, bytes32 _recipient, uint256 _amountOrId) external payable returns (bytes32); +} + +// TODO: Make this it's own file, hyperlane should ref it too +interface IWhitelist { + function mint(address _mintTo, uint256 _amount) external; + function burn(address _burnFrom, uint256 _amount) external; +} + +contract FeeVault is Ownable { + address public immutable treasury; + uint256 public immutable destinationChainId; + ITokenRouter private tokenRouter; + + constructor(address _treasury, address _tokenRouterAddr, uint256 _destinationChainId) { + require(_treasury != address(0), "Treasury address cannot be zero"); + treasury = _treasury; + require(_tokenRouterAddr != address(0), "TokenRouter address cannot be zero"); + tokenRouter = ITokenRouter(_tokenRouterAddr); + require(_destinationChainId != 0, "Destination chain ID cannot be zero"); + destinationChainId = _destinationChainId; + + // Note the fee vault contract needs to burn funds accumulated from "operational + // accounts" funded on genesis, who submit chain initialization transactions. + // This pre-setup ether does not have corresponding liquidity on the L1 hyperlane contract. + // + // TODO: Address following concerns: + // - Confirm idea works + // - Confirm we can avoid accounting logic altogether? + // - Possibly burn account balances of all hardcoded operational accounts in this constructor? + // - What about burning signer balances? + // - Will most likely need ether buffer deposited into L1 contract equal to cumulative fees needed for sidechain setup + uint256 initialBalance = address(this).balance; + if (initialBalance > 0) { + whitelist.burn(address(this), initialBalance); + emit AmountBurned(initialBalance); + } + } + + function transferToL1() external onlyOwner { + uint256 balance = address(this).balance; + require(balance > 0, "No funds to transfer"); + + // Convert the treasury address to bytes32 for transferRemote call + // TODO: Confirm how this is done in hyperlane repo + bytes32 recipient = bytes32(uint256(uint160(treasury))); + + tokenRouter.transferRemote{value: balance}(destinationChainId, recipient, balance); + } + + // Allows contract to receive ether accumulated before deployment + receive() external payable {} +} diff --git a/test/FeeVaultTest.sol b/test/FeeVaultTest.sol new file mode 100644 index 0000000..3efedf1 --- /dev/null +++ b/test/FeeVaultTest.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +// TODO