Skip to content

Commit eb3f187

Browse files
authored
feat(contracts): Add fee vault (#223)
1 parent d5f0218 commit eb3f187

File tree

5 files changed

+187
-3
lines changed

5 files changed

+187
-3
lines changed

contracts/scripts/foundry/DeployL2BridgeContracts.s.sol

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,20 @@ import { L2ERC721Gateway } from "../../src/L2/gateways/L2ERC721Gateway.sol";
1313
import { L2GatewayRouter } from "../../src/L2/gateways/L2GatewayRouter.sol";
1414
import { L2ScrollMessenger } from "../../src/L2/L2ScrollMessenger.sol";
1515
import { L2StandardERC20Gateway } from "../../src/L2/gateways/L2StandardERC20Gateway.sol";
16+
import { L2TxFeeVault } from "../../src/L2/predeploys/L2TxFeeVault.sol";
1617
import { Whitelist } from "../../src/L2/predeploys/Whitelist.sol";
1718
import { ScrollStandardERC20 } from "../../src/libraries/token/ScrollStandardERC20.sol";
1819
import { ScrollStandardERC20Factory } from "../../src/libraries/token/ScrollStandardERC20Factory.sol";
1920

2021
contract DeployL2BridgeContracts is Script {
2122
uint256 L2_DEPLOYER_PRIVATE_KEY = vm.envUint("L2_DEPLOYER_PRIVATE_KEY");
23+
address L1_TX_FEE_RECIPIENT_ADDR = vm.envAddress("L1_TX_FEE_RECIPIENT_ADDR");
24+
25+
L2ScrollMessenger messenger;
2226
ProxyAdmin proxyAdmin;
2327

2428
address L2_SCROLL_MESSENGER_PREDEPLOY_ADDR = vm.envOr("L2_SCROLL_MESSENGER_PREDEPLOY_ADDR", address(0));
29+
address L2_TX_FEE_VAULT_PREDEPLOY_ADDR = vm.envOr("L2_TX_FEE_VAULT_PREDEPLOY_ADDR", address(0));
2530
address L2_PROXY_ADMIN_PREDEPLOY_ADDR = vm.envOr("L2_PROXY_ADMIN_PREDEPLOY_ADDR", address(0));
2631
address L2_STANDARD_ERC20_GATEWAY_PROXY_PREDEPLOY_ADDR = vm.envOr("L2_STANDARD_ERC20_GATEWAY_PROXY_PREDEPLOY_ADDR", address(0));
2732
address L2_GATEWAY_ROUTER_PROXY_PREDEPLOY_ADDR = vm.envOr("L2_GATEWAY_ROUTER_PROXY_PREDEPLOY_ADDR", address(0));
@@ -35,6 +40,7 @@ contract DeployL2BridgeContracts is Script {
3540
vm.startBroadcast(L2_DEPLOYER_PRIVATE_KEY);
3641

3742
deployL2ScrollMessenger();
43+
deployTxFeeVault();
3844
deployProxyAdmin();
3945
deployL2StandardERC20Gateway();
4046
deployL2GatewayRouter();
@@ -54,9 +60,20 @@ contract DeployL2BridgeContracts is Script {
5460
}
5561

5662
address owner = vm.addr(L2_DEPLOYER_PRIVATE_KEY);
57-
L2ScrollMessenger l2ScrollMessenger = new L2ScrollMessenger(owner);
63+
messenger = new L2ScrollMessenger(owner);
64+
65+
logAddress("L2_SCROLL_MESSENGER_ADDR", address(messenger));
66+
}
67+
68+
function deployTxFeeVault() internal {
69+
if (L2_TX_FEE_VAULT_PREDEPLOY_ADDR != address(0)) {
70+
logAddress("L2_TX_FEE_VAULT_ADDR", address(L2_TX_FEE_VAULT_PREDEPLOY_ADDR));
71+
return;
72+
}
73+
74+
L2TxFeeVault feeVault = new L2TxFeeVault(address(messenger), L1_TX_FEE_RECIPIENT_ADDR);
5875

59-
logAddress("L2_SCROLL_MESSENGER_ADDR", address(l2ScrollMessenger));
76+
logAddress("L2_TX_FEE_VAULT_ADDR", address(feeVault));
6077
}
6178

6279
function deployProxyAdmin() internal {

contracts/scripts/foundry/InitializeL2BridgeContracts.s.sol

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ contract InitializeL2BridgeContracts is Script {
2222
address L1_ERC1155_GATEWAY_PROXY_ADDR = vm.envAddress("L1_ERC1155_GATEWAY_PROXY_ADDR");
2323

2424
address L2_SCROLL_MESSENGER_ADDR = vm.envAddress("L2_SCROLL_MESSENGER_ADDR");
25+
address L2_TX_FEE_VAULT_ADDR = vm.envAddress("L2_TX_FEE_VAULT_ADDR");
2526
address L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR = vm.envAddress("L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR");
2627
address L2_GATEWAY_ROUTER_PROXY_ADDR = vm.envAddress("L2_GATEWAY_ROUTER_PROXY_ADDR");
2728
address L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR = vm.envAddress("L2_SCROLL_STANDARD_ERC20_FACTORY_ADDR");
@@ -74,12 +75,13 @@ contract InitializeL2BridgeContracts is Script {
7475

7576
// whitelist contracts which can call sendMessage
7677
{
77-
address[] memory gateways = new address[](5);
78+
address[] memory gateways = new address[](6);
7879
gateways[0] = L2_STANDARD_ERC20_GATEWAY_PROXY_ADDR;
7980
gateways[1] = L2_GATEWAY_ROUTER_PROXY_ADDR;
8081
gateways[2] = L2_CUSTOM_ERC20_GATEWAY_PROXY_ADDR;
8182
gateways[3] = L2_ERC1155_GATEWAY_PROXY_ADDR;
8283
gateways[4] = L2_ERC721_GATEWAY_PROXY_ADDR;
84+
gateways[5] = L2_TX_FEE_VAULT_ADDR;
8385
Whitelist(L2_WHITELIST_ADDR).updateWhitelistStatus(gateways, true);
8486
}
8587

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
import { FeeVault } from "../../libraries/FeeVault.sol";
6+
7+
/// @title L2TxFeeVault
8+
/// @notice The `L2TxFeeVault` contract collects all L2 transaction fees and allows withdrawing these fees to a predefined L1 address.
9+
/// The minimum withdrawal amount is 10 ether.
10+
contract L2TxFeeVault is FeeVault {
11+
/// @param _messenger The address of L2ScrollMessenger.
12+
/// @param _recipient The fee recipient address on L1.
13+
constructor(address _messenger, address _recipient) FeeVault(_messenger, _recipient, 10 ether) {}
14+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
// MIT License
4+
5+
// Copyright (c) 2022 Optimism
6+
// Copyright (c) 2022 Scroll
7+
8+
// Permission is hereby granted, free of charge, to any person obtaining a copy
9+
// of this software and associated documentation files (the "Software"), to deal
10+
// in the Software without restriction, including without limitation the rights
11+
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
// copies of the Software, and to permit persons to whom the Software is
13+
// furnished to do so, subject to the following conditions:
14+
15+
// The above copyright notice and this permission notice shall be included in all
16+
// copies or substantial portions of the Software.
17+
18+
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
// SOFTWARE.
25+
26+
pragma solidity ^0.8.0;
27+
28+
import { IL2ScrollMessenger } from "../L2/IL2ScrollMessenger.sol";
29+
30+
/**
31+
* @title FeeVault
32+
* @notice The FeeVault contract contains the basic logic for the various different vault contracts
33+
* used to hold fee revenue generated by the L2 system.
34+
*/
35+
abstract contract FeeVault {
36+
/**
37+
* @notice Emits each time that a withdrawal occurs.
38+
*
39+
* @param value Amount that was withdrawn (in wei).
40+
* @param to Address that the funds were sent to.
41+
* @param from Address that triggered the withdrawal.
42+
*/
43+
event Withdrawal(uint256 value, address to, address from);
44+
45+
/**
46+
* @notice Minimum balance before a withdrawal can be triggered.
47+
*/
48+
uint256 public MIN_WITHDRAWAL_AMOUNT;
49+
50+
/**
51+
* @notice Scroll L2 messenger address.
52+
*/
53+
address public MESSENGER;
54+
55+
/**
56+
* @notice Wallet that will receive the fees on L1.
57+
*/
58+
address public RECIPIENT;
59+
60+
/**
61+
* @notice Total amount of wei processed by the contract.
62+
*/
63+
uint256 public totalProcessed;
64+
65+
/**
66+
* @param _recipient Wallet that will receive the fees on L1.
67+
* @param _minWithdrawalAmount Minimum balance before a withdrawal can be triggered.
68+
*/
69+
constructor(
70+
address _messenger,
71+
address _recipient,
72+
uint256 _minWithdrawalAmount
73+
) {
74+
MIN_WITHDRAWAL_AMOUNT = _minWithdrawalAmount;
75+
MESSENGER = _messenger;
76+
RECIPIENT = _recipient;
77+
}
78+
79+
/**
80+
* @notice Allow the contract to receive ETH.
81+
*/
82+
receive() external payable {}
83+
84+
/**
85+
* @notice Triggers a withdrawal of funds to the L1 fee wallet.
86+
*/
87+
function withdraw() external {
88+
uint256 value = address(this).balance;
89+
90+
require(
91+
value >= MIN_WITHDRAWAL_AMOUNT,
92+
"FeeVault: withdrawal amount must be greater than minimum withdrawal amount"
93+
);
94+
95+
unchecked {
96+
totalProcessed += value;
97+
}
98+
99+
emit Withdrawal(value, RECIPIENT, msg.sender);
100+
101+
IL2ScrollMessenger(MESSENGER).sendMessage{ value: value }(
102+
RECIPIENT,
103+
0, // no fee provided
104+
bytes(""), // no message (simple eth transfer)
105+
0 // _gasLimit is not used for eth transfers
106+
);
107+
}
108+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// SPDX-License-Identifier: MIT
2+
3+
pragma solidity ^0.8.0;
4+
5+
import { DSTestPlus } from "solmate/test/utils/DSTestPlus.sol";
6+
7+
import { MockScrollMessenger } from "./mocks/MockScrollMessenger.sol";
8+
import { L2TxFeeVault } from "../L2/predeploys/L2TxFeeVault.sol";
9+
10+
contract L2TxFeeVaultTest is DSTestPlus {
11+
MockScrollMessenger private messenger;
12+
L2TxFeeVault private vault;
13+
14+
function setUp() public {
15+
messenger = new MockScrollMessenger();
16+
vault = new L2TxFeeVault(address(messenger), address(1));
17+
}
18+
19+
function testCantWithdrawBelowMinimum() public {
20+
hevm.deal(address(vault), 9 ether);
21+
hevm.expectRevert("FeeVault: withdrawal amount must be greater than minimum withdrawal amount");
22+
vault.withdraw();
23+
}
24+
25+
function testWithdrawOnce() public {
26+
hevm.deal(address(vault), 11 ether);
27+
vault.withdraw();
28+
assertEq(address(messenger).balance, 11 ether);
29+
assertEq(vault.totalProcessed(), 11 ether);
30+
}
31+
32+
function testWithdrawTwice() public {
33+
hevm.deal(address(vault), 11 ether);
34+
vault.withdraw();
35+
assertEq(address(messenger).balance, 11 ether);
36+
assertEq(vault.totalProcessed(), 11 ether);
37+
38+
hevm.deal(address(vault), 22 ether);
39+
vault.withdraw();
40+
assertEq(address(messenger).balance, 33 ether);
41+
assertEq(vault.totalProcessed(), 33 ether);
42+
}
43+
}

0 commit comments

Comments
 (0)