diff --git a/packages/contracts-bedrock/README.md b/packages/contracts-bedrock/README.md index ed7503c5e9f86..71eb7335e45ca 100644 --- a/packages/contracts-bedrock/README.md +++ b/packages/contracts-bedrock/README.md @@ -244,3 +244,8 @@ Test contracts should be named one of the following according to their use: - `TargetContract_Init` for contracts that perform basic setup to be reused in other test contracts. - `TargetContract_Function_Test` for contracts containing happy path tests for a given function. - `TargetContract_Function_TestFail` for contracts containing sad path tests for a given function. + +## Withdrawaing From Fee Vaults + +See the file `scripts/FeeVaultWithdrawal.s.sol` to withdraw from the L2 fee vaults. It includes +instructions on how to run it. `foundry` is required. diff --git a/packages/contracts-bedrock/scripts/FeeVaultWithdrawal.s.sol b/packages/contracts-bedrock/scripts/FeeVaultWithdrawal.s.sol new file mode 100644 index 0000000000000..4a71d710053c8 --- /dev/null +++ b/packages/contracts-bedrock/scripts/FeeVaultWithdrawal.s.sol @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.15; + +import { console } from "forge-std/console.sol"; +import { Script } from "forge-std/Script.sol"; +import { Predeploys } from "../contracts/libraries/Predeploys.sol"; +import { FeeVault } from "../contracts/universal/FeeVault.sol"; +import { IMulticall3 } from "forge-std/interfaces/IMulticall3.sol"; + +/** + * @title FeeVaultWithdrawal + * @notice A script to make it very simple to withdraw from the fee vaults. + * The usage is as follows: + * $ forge script scripts/FeeVaultWithdrawal.s.sol \ + * --rpc-url $ETH_RPC_URL --broadcast \ + * --private-key $PRIVATE_KEY + */ +contract FeeVaultWithdrawal is Script { + IMulticall3 private constant multicall = IMulticall3(MULTICALL3_ADDRESS); + IMulticall3.Call3[] internal calls; + + /** + * @notice The entrypoint function. Determines which FeeVaults can be withdrawn from and then + * will send the transaction via Multicall3 to withdraw all FeeVaults. + */ + function run() external { + require(address(multicall).code.length > 0); + + address[] memory vaults = new address[](3); + vaults[0] = Predeploys.SEQUENCER_FEE_WALLET; + vaults[1] = Predeploys.BASE_FEE_VAULT; + vaults[2] = Predeploys.L1_FEE_VAULT; + + for (uint256 i; i < vaults.length; i++) { + address vault = vaults[i]; + bool shouldCall = canWithdrawal(vault); + if (shouldCall) { + calls.push(IMulticall3.Call3({ + target: vault, + allowFailure: false, + callData: abi.encodeWithSelector(FeeVault.withdraw.selector) + })); + + address recipient = FeeVault(payable(vault)).RECIPIENT(); + uint256 balance = vault.balance; + log(balance, recipient, vault); + } else { + string memory logline = string.concat( + vm.toString(vault), + " does not have a large enough balance to withdraw." + ); + console.log(logline); + } + } + + if (calls.length > 0) { + vm.broadcast(); + multicall.aggregate3(calls); + console.log("Success."); + } + } + + /** + * @notice Checks whether or not a FeeVault can be withdrawn. The balance of the account must + * be larger than the `MIN_WITHDRAWAL_AMOUNT`. + */ + function canWithdrawal(address _vault) internal view returns (bool) { + uint256 minWithdrawalAmount = FeeVault(payable(_vault)).MIN_WITHDRAWAL_AMOUNT(); + uint256 balance = _vault.balance; + return balance >= minWithdrawalAmount; + } + + /** + * @notice Logs the information relevant to the user. + */ + function log(uint256 _balance, address _recipient, address _vault) internal view { + string memory logline = string.concat( + "Withdrawing ", + vm.toString(_balance), + " to ", + vm.toString(_recipient), + " from ", + vm.toString(_vault) + ); + console.log(logline); + } +}