Commit c04ca0c4 authored by Mark Tyneway's avatar Mark Tyneway Committed by GitHub

contracts-bedrock: fix custom gas token withdrawals to L1 (#10685)

* contracts-bedrock: fix custom gas token withdrawals to L1

The previous implementation of the `FeeVault` used the `StandardBridge`
to withdraw `ether`. Under custom gas token, this meant that withdrawals
to L1 didn't work and withdrawals to L2 were the only supported path.
This updates the `L2ToL1Messenger` to withdraw to L1 via the `L2ToL1Messenger`.
This fixes the ability to withdraw to L1 while custom gas token is
enabled while also saving some gas on both L1 and L2 by removing
extra call frames. The reason why this was originally implemented using
the `StandardBridge` was to reduce additional changes during the
development of bedrock so that more effort could be spent thinking
about the new protocol design, as the legacy implementation used
the `StandardBridge` for doing the withdrawal.

Having the `StandardBridge` used for `ether` deposits and withdrawals
is unncessarily expensive and a bit of a leaky abstraction.

Gas savings are scalability improvements, both on L1 and on L2.

Also uses a new variant of `SafeCall` that passes through all gas
in its `transfer`. The implementation ensures to assert on the return
value of the call to hold the invariant true that the `totalProcessed`
will always be consistent. If the call reverts, then its possible for
the `ether` to not be transferred and `totalProcessed` to be incremented.

Also bump the semver of the contracts to a beta version. They will
be bumped out of beta at a release of the contracts.

The tests are also improved for both the `SafeCall` library as well
as the `FeeVault`.

* op-e2e: withdrawal tests

* e2e: update test with comments

* feevault: fix tech debt

Updates the `FeeVault` to use modern style guide.
Its not great to leak `immutable` style into the ABI
so add extra getters and mark the legacy getters as
deprecated. Doing this now will help to migrate to the
new getters in 1 year from now.

Also add in the storage layout spacer that was previously
noticed in an audit, see https://github.com/ethereum-optimism/optimism/pull/9477.

* semver-lock: update + compiler warning

* snapshots: regenerate

* lint: fix

* tests: fixup

* gas-snapshot: update

* test: fix

* contracts-bedrock: reuse existing implementation of send in SafeCall
Co-authored-by: default avatarMatt Solomon <matt@mattsolomon.dev>

* contracts-bedrock: update gap in FeeVault for inheritance to start at 50 gap

* contracts-bedrock: add missing assertions in tests for SequencerFeeVault

* contracts-bedrock: remove redundant if clause in SafeCall tests

* contracts-bedrock: fix gas relation in SafeCall tests

* contracts-bedrock: update send method tests for SafeCall

* op-e2e: add missing check in custom gas token tests

* contracts-bedrock: fix types in SafeCall tests

* contracts-bedrock: update snapshots

* contracts-bedrock: update semver-lock

* op-e2e: fix custom_gas_token test

* op-e2e: fix custom gas token test

* contracts-bedrock: fix tests for custom gas token

* op-e2e: fix tests for custom gas token

* op-e2e: fix build

---------
Co-authored-by: default avatarDiego <105765223+0xfuturistic@users.noreply.github.com>
Co-authored-by: default avatarMatt Solomon <matt@mattsolomon.dev>
parent 2a01915b
...@@ -200,6 +200,144 @@ func TestCustomGasToken(t *testing.T) { ...@@ -200,6 +200,144 @@ func TestCustomGasToken(t *testing.T) {
require.Equal(t, withdrawAmount, diff) require.Equal(t, withdrawAmount, diff)
} }
// checkFeeWithdrawal ensures that the FeeVault can be withdrawn from
checkFeeWithdrawal := func(t *testing.T, enabled bool) {
feeVault, err := bindings.NewSequencerFeeVault(predeploys.SequencerFeeVaultAddr, l2Client)
require.NoError(t, err)
// Alice will be sending transactions
aliceOpts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.Alice, cfg.L2ChainIDBig())
require.NoError(t, err)
// Get the recipient of the funds
recipient, err := feeVault.RECIPIENT(&bind.CallOpts{})
require.NoError(t, err)
// This test depends on the withdrawal network being L1 which is represented
// by 0 in the enum.
withdrawalNetwork, err := feeVault.WITHDRAWALNETWORK(&bind.CallOpts{})
require.NoError(t, err)
require.Equal(t, withdrawalNetwork, uint8(0))
// Get the balance of the recipient on L1
var recipientBalanceBefore *big.Int
if enabled {
recipientBalanceBefore, err = weth9.BalanceOf(&bind.CallOpts{}, recipient)
} else {
recipientBalanceBefore, err = l1Client.BalanceAt(context.Background(), recipient, nil)
}
require.NoError(t, err)
// Get the min withdrawal amount for the FeeVault
amount, err := feeVault.MINWITHDRAWALAMOUNT(&bind.CallOpts{})
require.NoError(t, err)
l1opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.Alice, cfg.L1ChainIDBig())
require.NoError(t, err)
optimismPortal, err := bindings.NewOptimismPortal(cfg.L1Deployments.OptimismPortalProxy, l1Client)
require.NoError(t, err)
depositAmount := new(big.Int).Mul(amount, big.NewInt(14))
l1opts.Value = depositAmount
var receipt *types.Receipt
// Alice deposits funds
if enabled {
// approve + transferFrom flow
// Cannot use `transfer` because of the tracking of balance in the OptimismPortal
dep, err := weth9.Deposit(l1opts)
waitForTx(t, dep, err, l1Client)
l1opts.Value = nil
tx, err := weth9.Approve(l1opts, cfg.L1Deployments.OptimismPortalProxy, depositAmount)
waitForTx(t, tx, err, l1Client)
require.NoError(t, err)
deposit, err := optimismPortal.DepositERC20Transaction(l1opts, cfg.Secrets.Addresses().Alice, depositAmount, depositAmount, 500_000, false, []byte{})
waitForTx(t, deposit, err, l1Client)
receipt, err = wait.ForReceiptOK(context.Background(), l1Client, deposit.Hash())
require.NoError(t, err)
} else {
// send ether to the portal directly, alice already has funds on L2
tx, err := optimismPortal.DepositTransaction(l1opts, cfg.Secrets.Addresses().Alice, depositAmount, 500_000, false, []byte{})
waitForTx(t, tx, err, l1Client)
receipt, err = wait.ForReceiptOK(context.Background(), l1Client, tx.Hash())
require.NoError(t, err)
}
// Compute the deposit transaction hash + poll for it
depositEvent, err := receipts.FindLog(receipt.Logs, optimismPortal.ParseTransactionDeposited)
require.NoError(t, err, "Should emit deposit event")
depositTx, err := derive.UnmarshalDepositLogEvent(&depositEvent.Raw)
require.NoError(t, err)
_, err = wait.ForReceiptOK(context.Background(), l2Client, types.NewTx(depositTx).Hash())
require.NoError(t, err)
// Get Alice's balance on L2
aliceBalance, err := l2Client.BalanceAt(context.Background(), cfg.Secrets.Addresses().Alice, nil)
require.NoError(t, err)
require.GreaterOrEqual(t, aliceBalance.Uint64(), amount.Uint64())
// Send funds to the FeeVault so its balance is above the min withdrawal amount
aliceOpts.Value = amount
feeVaultTx, err := feeVault.Receive(aliceOpts)
waitForTx(t, feeVaultTx, err, l2Client)
// Ensure that the balance of the vault is large enough to withdraw
vaultBalance, err := l2Client.BalanceAt(context.Background(), predeploys.SequencerFeeVaultAddr, nil)
require.NoError(t, err)
require.GreaterOrEqual(t, vaultBalance.Uint64(), amount.Uint64())
// Ensure there is code at the vault address
code, err := l2Client.CodeAt(context.Background(), predeploys.SequencerFeeVaultAddr, nil)
require.NoError(t, err)
require.NotEmpty(t, code)
// Poke the fee vault to withdraw
l2Opts, err := bind.NewKeyedTransactorWithChainID(cfg.Secrets.Bob, cfg.L2ChainIDBig())
require.NoError(t, err)
withdrawalTx, err := feeVault.Withdraw(l2Opts)
waitForTx(t, withdrawalTx, err, l2Client)
// Get the receipt and the amount withdrawn
receipt, err = l2Client.TransactionReceipt(context.Background(), withdrawalTx.Hash())
require.NoError(t, err)
inclusionHeight := receipt.BlockNumber.Uint64()
it, err := feeVault.FilterWithdrawal(&bind.FilterOpts{
Start: inclusionHeight,
End: &inclusionHeight,
})
require.NoError(t, err)
require.True(t, it.Next())
withdrawnAmount := it.Event.Value
// Finalize the withdrawal
proveReceipt, finalizeReceipt, resolveClaimReceipt, resolveReceipt := ProveAndFinalizeWithdrawal(t, cfg, sys, "verifier", cfg.Secrets.Alice, receipt)
require.Equal(t, types.ReceiptStatusSuccessful, proveReceipt.Status)
require.Equal(t, types.ReceiptStatusSuccessful, finalizeReceipt.Status)
if e2eutils.UseFaultProofs() {
require.Equal(t, types.ReceiptStatusSuccessful, resolveClaimReceipt.Status)
require.Equal(t, types.ReceiptStatusSuccessful, resolveReceipt.Status)
}
// Assert that the recipient's balance did increase
var recipientBalanceAfter *big.Int
if enabled {
recipientBalanceAfter, err = weth9.BalanceOf(&bind.CallOpts{}, recipient)
} else {
recipientBalanceAfter, err = l1Client.BalanceAt(context.Background(), recipient, nil)
}
require.NoError(t, err)
require.Equal(t, recipientBalanceAfter, new(big.Int).Add(recipientBalanceBefore, withdrawnAmount))
}
checkL1TokenNameAndSymbol := func(t *testing.T, enabled bool) { checkL1TokenNameAndSymbol := func(t *testing.T, enabled bool) {
systemConfig, err := bindings.NewSystemConfig(cfg.L1Deployments.SystemConfigProxy, l1Client) systemConfig, err := bindings.NewSystemConfig(cfg.L1Deployments.SystemConfigProxy, l1Client)
require.NoError(t, err) require.NoError(t, err)
...@@ -278,6 +416,7 @@ func TestCustomGasToken(t *testing.T) { ...@@ -278,6 +416,7 @@ func TestCustomGasToken(t *testing.T) {
checkL1TokenNameAndSymbol(t, enabled) checkL1TokenNameAndSymbol(t, enabled)
checkL2TokenNameAndSymbol(t, enabled) checkL2TokenNameAndSymbol(t, enabled)
checkWETHTokenNameAndSymbol(t, enabled) checkWETHTokenNameAndSymbol(t, enabled)
checkFeeWithdrawal(t, enabled)
// Activate custom gas token feature (devnet does not have this activated at genesis) // Activate custom gas token feature (devnet does not have this activated at genesis)
setCustomGasToken(t, cfg, sys, weth9Address) setCustomGasToken(t, cfg, sys, weth9Address)
...@@ -289,6 +428,7 @@ func TestCustomGasToken(t *testing.T) { ...@@ -289,6 +428,7 @@ func TestCustomGasToken(t *testing.T) {
checkL1TokenNameAndSymbol(t, enabled) checkL1TokenNameAndSymbol(t, enabled)
checkL2TokenNameAndSymbol(t, enabled) checkL2TokenNameAndSymbol(t, enabled)
checkWETHTokenNameAndSymbol(t, enabled) checkWETHTokenNameAndSymbol(t, enabled)
checkFeeWithdrawal(t, enabled)
} }
// callViaSafe will use the Safe smart account at safeAddress to send a transaction to target using the provided data. The transaction signature is constructed from // callViaSafe will use the Safe smart account at safeAddress to send a transaction to target using the provided data. The transaction signature is constructed from
......
...@@ -50,7 +50,7 @@ func ForReceipt(ctx context.Context, client *ethclient.Client, hash common.Hash, ...@@ -50,7 +50,7 @@ func ForReceipt(ctx context.Context, client *ethclient.Client, hash common.Hash,
if errors.Is(err, ethereum.NotFound) || (err != nil && strings.Contains(err.Error(), "transaction indexing is in progress")) { if errors.Is(err, ethereum.NotFound) || (err != nil && strings.Contains(err.Error(), "transaction indexing is in progress")) {
select { select {
case <-ctx.Done(): case <-ctx.Done():
return nil, ctx.Err() return nil, fmt.Errorf("timed out waiting for tx %s: %w: %w", hash, err, ctx.Err())
case <-ticker.C: case <-ticker.C:
continue continue
} }
...@@ -59,7 +59,7 @@ func ForReceipt(ctx context.Context, client *ethclient.Client, hash common.Hash, ...@@ -59,7 +59,7 @@ func ForReceipt(ctx context.Context, client *ethclient.Client, hash common.Hash,
continue continue
} }
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to get receipt: %w", err) return nil, fmt.Errorf("failed to get receipt for tx %s: %w", hash, err)
} }
if receipt.Status != status { if receipt.Status != status {
printDebugTrace(ctx, client, hash) printDebugTrace(ctx, client, hash)
......
...@@ -4,7 +4,7 @@ GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 5619 ...@@ -4,7 +4,7 @@ GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_0() (gas: 5619
GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4074035) GasBenchMark_L1StandardBridge_Deposit:test_depositERC20_benchmark_1() (gas: 4074035)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 466947) GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_0() (gas: 466947)
GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512629) GasBenchMark_L1StandardBridge_Deposit:test_depositETH_benchmark_1() (gas: 3512629)
GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 72629) GasBenchMark_L1StandardBridge_Finalize:test_finalizeETHWithdrawal_benchmark() (gas: 72624)
GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92973) GasBenchMark_L2OutputOracle:test_proposeL2Output_benchmark() (gas: 92973)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68433) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark() (gas: 68433)
GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68903) GasBenchMark_OptimismPortal:test_depositTransaction_benchmark_1() (gas: 68903)
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
"sourceCodeHash": "0x2ed7a2d6d66839fb3d207952f44b001bce349334adc40ce66d0503ce64e48548" "sourceCodeHash": "0x2ed7a2d6d66839fb3d207952f44b001bce349334adc40ce66d0503ce64e48548"
}, },
"src/L1/DataAvailabilityChallenge.sol": { "src/L1/DataAvailabilityChallenge.sol": {
"initCodeHash": "0x39d938938b13abb2455959960c2ee96d48149fd16a7fb69f1f6699460d5019be", "initCodeHash": "0x9e9ac7238791b2eaade946e58b98f6f1f43f60b8b393664d85a553857a0ab5c7",
"sourceCodeHash": "0xf6c72a2cca24cfa7c9274d720e93b05d665a2751cca3d747105e6c511ccffc73" "sourceCodeHash": "0xf6c72a2cca24cfa7c9274d720e93b05d665a2751cca3d747105e6c511ccffc73"
}, },
"src/L1/DelayedVetoable.sol": { "src/L1/DelayedVetoable.sol": {
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
"sourceCodeHash": "0xc59b8574531162e016d7342aeb6e79d05574e90dbea6c0e5ede35b65010ad894" "sourceCodeHash": "0xc59b8574531162e016d7342aeb6e79d05574e90dbea6c0e5ede35b65010ad894"
}, },
"src/L1/L1CrossDomainMessenger.sol": { "src/L1/L1CrossDomainMessenger.sol": {
"initCodeHash": "0xff2c621c5ae8f1190779b13d8da4ee9fcd64f202d6e5ee309f0028bca17a8b8f", "initCodeHash": "0x1f97347da46930d5b3cd40bd2699a6b78812e5f597793c8d59a90b0ef6800da8",
"sourceCodeHash": "0x56a2d3abed97eb7b292db758aac1e36fc08a12bfa44f7969824e26866a1417fa" "sourceCodeHash": "0x56a2d3abed97eb7b292db758aac1e36fc08a12bfa44f7969824e26866a1417fa"
}, },
"src/L1/L1ERC721Bridge.sol": { "src/L1/L1ERC721Bridge.sol": {
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
"sourceCodeHash": "0xd57acdbd001941e75cf4326ba7c1bdad809912f10b1e44ffaebe073917cdd296" "sourceCodeHash": "0xd57acdbd001941e75cf4326ba7c1bdad809912f10b1e44ffaebe073917cdd296"
}, },
"src/L1/L1StandardBridge.sol": { "src/L1/L1StandardBridge.sol": {
"initCodeHash": "0xf46dc2d8e171bd5aebbafe31386e9d27323a124b1b99f378cb8f67ab22225a7b", "initCodeHash": "0x508977d2517b49649bef57899e2c2519c271953708b2c84325377dae64a92baf",
"sourceCodeHash": "0x0d07e526c1891abb81c7e94f73642caa8ee386ab036b3b2a67f1b21ca85089c5" "sourceCodeHash": "0x0d07e526c1891abb81c7e94f73642caa8ee386ab036b3b2a67f1b21ca85089c5"
}, },
"src/L1/L2OutputOracle.sol": { "src/L1/L2OutputOracle.sol": {
...@@ -60,8 +60,8 @@ ...@@ -60,8 +60,8 @@
"sourceCodeHash": "0x40d708140ee6345e146e358c169a191dbbc991782560a2dcbf90ba45a82f7117" "sourceCodeHash": "0x40d708140ee6345e146e358c169a191dbbc991782560a2dcbf90ba45a82f7117"
}, },
"src/L2/BaseFeeVault.sol": { "src/L2/BaseFeeVault.sol": {
"initCodeHash": "0x2744d34573be83206d1b75d049d18a7bb37f9058e68c0803e5008c46b0dc2474", "initCodeHash": "0x623bf6892f0bdb536f2916bc9eb45e52012ad2c80893ff87d750757966b9be68",
"sourceCodeHash": "0xd787bd2a192ba5025fa0a8de2363af66a8de20de226e411bdb576adb64636cd0" "sourceCodeHash": "0x3a725791a0f5ed84dc46dcdae26f6170a759b2fe3dc360d704356d088b76cfd6"
}, },
"src/L2/CrossL2Inbox.sol": { "src/L2/CrossL2Inbox.sol": {
"initCodeHash": "0x46e15ac5de81ea415061d049730da25acf31040d6d5d70fe3a9bf4cac100c282", "initCodeHash": "0x46e15ac5de81ea415061d049730da25acf31040d6d5d70fe3a9bf4cac100c282",
...@@ -80,11 +80,11 @@ ...@@ -80,11 +80,11 @@
"sourceCodeHash": "0xd8ec2f814690d1ffd55e5b8496bca5a179d6d1772d61f71cdf8296c9058dc2c6" "sourceCodeHash": "0xd8ec2f814690d1ffd55e5b8496bca5a179d6d1772d61f71cdf8296c9058dc2c6"
}, },
"src/L2/L1FeeVault.sol": { "src/L2/L1FeeVault.sol": {
"initCodeHash": "0x2744d34573be83206d1b75d049d18a7bb37f9058e68c0803e5008c46b0dc2474", "initCodeHash": "0x623bf6892f0bdb536f2916bc9eb45e52012ad2c80893ff87d750757966b9be68",
"sourceCodeHash": "0x3a94f273937d8908fb37dd2c495a6a0b9c3941fe68ccea51723f84eb343ba225" "sourceCodeHash": "0x29c64c43e3deedfa63572d36ce511febb99e500352a003242aed339716d3e3ce"
}, },
"src/L2/L2CrossDomainMessenger.sol": { "src/L2/L2CrossDomainMessenger.sol": {
"initCodeHash": "0x5d1d1c0c3cc2171832438cbafb6e55c142eab43a7d7ea09ee818a7e3c50d9314", "initCodeHash": "0xad95e4e98adf35ea2baeb15348d751fc76e1bb0d001474d4eaefba47f7aeaf5f",
"sourceCodeHash": "0x440d299b7429c44f6faa4005b33428f9edc1283027d9c78a63eb3d76452bfa47" "sourceCodeHash": "0x440d299b7429c44f6faa4005b33428f9edc1283027d9c78a63eb3d76452bfa47"
}, },
"src/L2/L2ERC721Bridge.sol": { "src/L2/L2ERC721Bridge.sol": {
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
"sourceCodeHash": "0xa7646a588275046f92525ef121e5a0fe149e7752ea51fe62f7e0686a21153542" "sourceCodeHash": "0xa7646a588275046f92525ef121e5a0fe149e7752ea51fe62f7e0686a21153542"
}, },
"src/L2/L2StandardBridge.sol": { "src/L2/L2StandardBridge.sol": {
"initCodeHash": "0xdfe717975f7c76e80201f715a0d68f3fef02beca6a9e0f378a115e175c6a5944", "initCodeHash": "0xb6af582e9edaae531d48559b7cd0ca5813a112361ea19b8cb46292726ad88d40",
"sourceCodeHash": "0xb4a9f333f04008f610eb55fa6ff7e610bab340d53156cb50ec65a575c9576b0e" "sourceCodeHash": "0xb4a9f333f04008f610eb55fa6ff7e610bab340d53156cb50ec65a575c9576b0e"
}, },
"src/L2/L2ToL1MessagePasser.sol": { "src/L2/L2ToL1MessagePasser.sol": {
...@@ -104,8 +104,8 @@ ...@@ -104,8 +104,8 @@
"sourceCodeHash": "0x249218d69909750f5245a42d247a789f1837c24863bded94dc577fcbec914175" "sourceCodeHash": "0x249218d69909750f5245a42d247a789f1837c24863bded94dc577fcbec914175"
}, },
"src/L2/SequencerFeeVault.sol": { "src/L2/SequencerFeeVault.sol": {
"initCodeHash": "0xd62e193d89b1661d34031227a45ce1eade9c2a89b0bd7f362f511d03cceef294", "initCodeHash": "0xb94145f571e92ee615c6fe903b6568e8aac5fe760b6b65148ffc45d2fb0f5433",
"sourceCodeHash": "0xa304b4b556162323d69662b4dd9a1d073d55ec661494465489bb67f1e465e7b3" "sourceCodeHash": "0x8f2a54104e5e7105ba03ba37e3ef9b6684a447245f0e0b787ba4cca12957b97c"
}, },
"src/L2/WETH.sol": { "src/L2/WETH.sol": {
"initCodeHash": "0xde72ae96910e95249623c2d695749847e4c4adeaf96a7a35033afd77318a528a", "initCodeHash": "0xde72ae96910e95249623c2d695749847e4c4adeaf96a7a35033afd77318a528a",
......
...@@ -63,6 +63,32 @@ ...@@ -63,6 +63,32 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "minWithdrawalAmount",
"outputs": [
{
"internalType": "uint256",
"name": "amount_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "recipient",
"outputs": [
{
"internalType": "address",
"name": "recipient_",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "totalProcessed", "name": "totalProcessed",
...@@ -96,6 +122,19 @@ ...@@ -96,6 +122,19 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "withdrawalNetwork",
"outputs": [
{
"internalType": "enum FeeVault.WithdrawalNetwork",
"name": "network_",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
......
...@@ -63,6 +63,32 @@ ...@@ -63,6 +63,32 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "minWithdrawalAmount",
"outputs": [
{
"internalType": "uint256",
"name": "amount_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "recipient",
"outputs": [
{
"internalType": "address",
"name": "recipient_",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "totalProcessed", "name": "totalProcessed",
...@@ -96,6 +122,19 @@ ...@@ -96,6 +122,19 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "withdrawalNetwork",
"outputs": [
{
"internalType": "enum FeeVault.WithdrawalNetwork",
"name": "network_",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
......
...@@ -76,6 +76,32 @@ ...@@ -76,6 +76,32 @@
"stateMutability": "view", "stateMutability": "view",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "minWithdrawalAmount",
"outputs": [
{
"internalType": "uint256",
"name": "amount_",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "recipient",
"outputs": [
{
"internalType": "address",
"name": "recipient_",
"type": "address"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"inputs": [], "inputs": [],
"name": "totalProcessed", "name": "totalProcessed",
...@@ -109,6 +135,19 @@ ...@@ -109,6 +135,19 @@
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
}, },
{
"inputs": [],
"name": "withdrawalNetwork",
"outputs": [
{
"internalType": "enum FeeVault.WithdrawalNetwork",
"name": "network_",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{ {
"anonymous": false, "anonymous": false,
"inputs": [ "inputs": [
......
...@@ -5,5 +5,12 @@ ...@@ -5,5 +5,12 @@
"offset": 0, "offset": 0,
"slot": "0", "slot": "0",
"type": "uint256" "type": "uint256"
},
{
"bytes": "1536",
"label": "__gap",
"offset": 0,
"slot": "1",
"type": "uint256[48]"
} }
] ]
\ No newline at end of file
...@@ -5,5 +5,12 @@ ...@@ -5,5 +5,12 @@
"offset": 0, "offset": 0,
"slot": "0", "slot": "0",
"type": "uint256" "type": "uint256"
},
{
"bytes": "1536",
"label": "__gap",
"offset": 0,
"slot": "1",
"type": "uint256[48]"
} }
] ]
\ No newline at end of file
...@@ -5,5 +5,12 @@ ...@@ -5,5 +5,12 @@
"offset": 0, "offset": 0,
"slot": "0", "slot": "0",
"type": "uint256" "type": "uint256"
},
{
"bytes": "1536",
"label": "__gap",
"offset": 0,
"slot": "1",
"type": "uint256[48]"
} }
] ]
\ No newline at end of file
...@@ -10,8 +10,8 @@ import { FeeVault } from "src/universal/FeeVault.sol"; ...@@ -10,8 +10,8 @@ import { FeeVault } from "src/universal/FeeVault.sol";
/// @notice The BaseFeeVault accumulates the base fee that is paid by transactions. /// @notice The BaseFeeVault accumulates the base fee that is paid by transactions.
contract BaseFeeVault is FeeVault, ISemver { contract BaseFeeVault is FeeVault, ISemver {
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.4.1 /// @custom:semver 1.5.0-beta.1
string public constant version = "1.4.1"; string public constant version = "1.5.0-beta.1";
/// @notice Constructs the BaseFeeVault contract. /// @notice Constructs the BaseFeeVault contract.
/// @param _recipient Wallet that will receive the fees. /// @param _recipient Wallet that will receive the fees.
......
...@@ -10,8 +10,8 @@ import { FeeVault } from "src/universal/FeeVault.sol"; ...@@ -10,8 +10,8 @@ import { FeeVault } from "src/universal/FeeVault.sol";
/// @notice The L1FeeVault accumulates the L1 portion of the transaction fees. /// @notice The L1FeeVault accumulates the L1 portion of the transaction fees.
contract L1FeeVault is FeeVault, ISemver { contract L1FeeVault is FeeVault, ISemver {
/// @notice Semantic version. /// @notice Semantic version.
/// @custom:semver 1.4.1 /// @custom:semver 1.5.0-beta.1
string public constant version = "1.4.1"; string public constant version = "1.5.0-beta.1";
/// @notice Constructs the L1FeeVault contract. /// @notice Constructs the L1FeeVault contract.
/// @param _recipient Wallet that will receive the fees. /// @param _recipient Wallet that will receive the fees.
......
...@@ -10,8 +10,8 @@ import { FeeVault } from "src/universal/FeeVault.sol"; ...@@ -10,8 +10,8 @@ import { FeeVault } from "src/universal/FeeVault.sol";
/// @notice The SequencerFeeVault is the contract that holds any fees paid to the Sequencer during /// @notice The SequencerFeeVault is the contract that holds any fees paid to the Sequencer during
/// transaction processing and block production. /// transaction processing and block production.
contract SequencerFeeVault is FeeVault, ISemver { contract SequencerFeeVault is FeeVault, ISemver {
/// @custom:semver 1.4.1 /// @custom:semver 1.5.0-beta.1
string public constant version = "1.4.1"; string public constant version = "1.5.0-beta.1";
/// @notice Constructs the SequencerFeeVault contract. /// @notice Constructs the SequencerFeeVault contract.
/// @param _recipient Wallet that will receive the fees. /// @param _recipient Wallet that will receive the fees.
......
...@@ -9,10 +9,9 @@ library SafeCall { ...@@ -9,10 +9,9 @@ library SafeCall {
/// @param _target Address to call /// @param _target Address to call
/// @param _gas Amount of gas to pass to the call /// @param _gas Amount of gas to pass to the call
/// @param _value Amount of value to pass to the call /// @param _value Amount of value to pass to the call
function send(address _target, uint256 _gas, uint256 _value) internal returns (bool) { function send(address _target, uint256 _gas, uint256 _value) internal returns (bool success_) {
bool _success;
assembly { assembly {
_success := success_ :=
call( call(
_gas, // gas _gas, // gas
_target, // recipient _target, // recipient
...@@ -23,7 +22,13 @@ library SafeCall { ...@@ -23,7 +22,13 @@ library SafeCall {
0 // outlen 0 // outlen
) )
} }
return _success; }
/// @notice Perform a low level call with all gas without copying any returndata
/// @param _target Address to call
/// @param _value Amount of value to pass to the call
function send(address _target, uint256 _value) internal returns (bool success_) {
success_ = send(_target, gasleft(), _value);
} }
/// @notice Perform a low level call without copying any returndata /// @notice Perform a low level call without copying any returndata
...@@ -31,10 +36,17 @@ library SafeCall { ...@@ -31,10 +36,17 @@ library SafeCall {
/// @param _gas Amount of gas to pass to the call /// @param _gas Amount of gas to pass to the call
/// @param _value Amount of value to pass to the call /// @param _value Amount of value to pass to the call
/// @param _calldata Calldata to pass to the call /// @param _calldata Calldata to pass to the call
function call(address _target, uint256 _gas, uint256 _value, bytes memory _calldata) internal returns (bool) { function call(
bool _success; address _target,
uint256 _gas,
uint256 _value,
bytes memory _calldata
)
internal
returns (bool success_)
{
assembly { assembly {
_success := success_ :=
call( call(
_gas, // gas _gas, // gas
_target, // recipient _target, // recipient
...@@ -45,7 +57,6 @@ library SafeCall { ...@@ -45,7 +57,6 @@ library SafeCall {
0 // outlen 0 // outlen
) )
} }
return _success;
} }
/// @notice Helper function to determine if there is sufficient gas remaining within the context /// @notice Helper function to determine if there is sufficient gas remaining within the context
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity 0.8.15;
import { L2StandardBridge } from "src/L2/L2StandardBridge.sol"; import { L2ToL1MessagePasser } from "src/L2/L2ToL1MessagePasser.sol";
import { SafeCall } from "src/libraries/SafeCall.sol";
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
/// @title FeeVault /// @title FeeVault
...@@ -17,20 +18,32 @@ abstract contract FeeVault { ...@@ -17,20 +18,32 @@ abstract contract FeeVault {
} }
/// @notice Minimum balance before a withdrawal can be triggered. /// @notice Minimum balance before a withdrawal can be triggered.
/// Use the `minWithdrawalAmount()` getter as this is deprecated
/// and is subject to be removed in the future.
/// @custom:legacy
uint256 public immutable MIN_WITHDRAWAL_AMOUNT; uint256 public immutable MIN_WITHDRAWAL_AMOUNT;
/// @notice Wallet that will receive the fees. /// @notice Account that will receive the fees. Can be located on L1 or L2.
/// Use the `recipient()` getter as this is deprecated
/// and is subject to be removed in the future.
/// @custom:legacy
address public immutable RECIPIENT; address public immutable RECIPIENT;
/// @notice Network which the RECIPIENT will receive fees on. /// @notice Network which the recipient will receive fees on.
/// Use the `withdrawalNetwork()` getter as this is deprecated
/// and is subject to be removed in the future.
/// @custom:legacy
WithdrawalNetwork public immutable WITHDRAWAL_NETWORK; WithdrawalNetwork public immutable WITHDRAWAL_NETWORK;
/// @notice The minimum gas limit for the FeeVault withdrawal transaction. /// @notice The minimum gas limit for the FeeVault withdrawal transaction.
uint32 internal constant WITHDRAWAL_MIN_GAS = 35_000; uint32 internal constant WITHDRAWAL_MIN_GAS = 400_000;
/// @notice Total amount of wei processed by the contract. /// @notice Total amount of wei processed by the contract.
uint256 public totalProcessed; uint256 public totalProcessed;
/// @notice Reserve extra slots in the storage layout for future upgrades.
uint256[48] private __gap;
/// @notice Emitted each time a withdrawal occurs. This event will be deprecated /// @notice Emitted each time a withdrawal occurs. This event will be deprecated
/// in favor of the Withdrawal event containing the WithdrawalNetwork parameter. /// in favor of the Withdrawal event containing the WithdrawalNetwork parameter.
/// @param value Amount that was withdrawn (in wei). /// @param value Amount that was withdrawn (in wei).
...@@ -57,6 +70,21 @@ abstract contract FeeVault { ...@@ -57,6 +70,21 @@ abstract contract FeeVault {
/// @notice Allow the contract to receive ETH. /// @notice Allow the contract to receive ETH.
receive() external payable { } receive() external payable { }
/// @notice Minimum balance before a withdrawal can be triggered.
function minWithdrawalAmount() public view returns (uint256 amount_) {
amount_ = MIN_WITHDRAWAL_AMOUNT;
}
/// @notice Account that will receive the fees. Can be located on L1 or L2.
function recipient() public view returns (address recipient_) {
recipient_ = RECIPIENT;
}
/// @notice Network which the recipient will receive fees on.
function withdrawalNetwork() public view returns (WithdrawalNetwork network_) {
network_ = WITHDRAWAL_NETWORK;
}
/// @notice Triggers a withdrawal of funds to the fee wallet on L1 or L2. /// @notice Triggers a withdrawal of funds to the fee wallet on L1 or L2.
function withdraw() external { function withdraw() external {
require( require(
...@@ -71,12 +99,14 @@ abstract contract FeeVault { ...@@ -71,12 +99,14 @@ abstract contract FeeVault {
emit Withdrawal(value, RECIPIENT, msg.sender, WITHDRAWAL_NETWORK); emit Withdrawal(value, RECIPIENT, msg.sender, WITHDRAWAL_NETWORK);
if (WITHDRAWAL_NETWORK == WithdrawalNetwork.L2) { if (WITHDRAWAL_NETWORK == WithdrawalNetwork.L2) {
(bool success,) = RECIPIENT.call{ value: value }(hex""); bool success = SafeCall.send(RECIPIENT, value);
require(success, "FeeVault: failed to send ETH to L2 fee recipient"); require(success, "FeeVault: failed to send ETH to L2 fee recipient");
} else { } else {
L2StandardBridge(payable(Predeploys.L2_STANDARD_BRIDGE)).bridgeETHTo{ value: value }( L2ToL1MessagePasser(payable(Predeploys.L2_TO_L1_MESSAGE_PASSER)).initiateWithdrawal{ value: value }({
RECIPIENT, WITHDRAWAL_MIN_GAS, bytes("") _target: RECIPIENT,
); _gasLimit: WITHDRAWAL_MIN_GAS,
_data: hex""
});
} }
} }
} }
...@@ -6,6 +6,9 @@ import { CommonTest } from "test/setup/CommonTest.sol"; ...@@ -6,6 +6,9 @@ import { CommonTest } from "test/setup/CommonTest.sol";
import { Reverter } from "test/mocks/Callers.sol"; import { Reverter } from "test/mocks/Callers.sol";
import { StandardBridge } from "src/universal/StandardBridge.sol"; import { StandardBridge } from "src/universal/StandardBridge.sol";
import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol"; import { EIP1967Helper } from "test/mocks/EIP1967Helper.sol";
import { L2ToL1MessagePasser } from "src/L2/L2ToL1MessagePasser.sol";
import { Hashing } from "src/libraries/Hashing.sol";
import { Types } from "src/libraries/Types.sol";
// Libraries // Libraries
import { Predeploys } from "src/libraries/Predeploys.sol"; import { Predeploys } from "src/libraries/Predeploys.sol";
...@@ -25,14 +28,15 @@ contract SequencerFeeVault_Test is CommonTest { ...@@ -25,14 +28,15 @@ contract SequencerFeeVault_Test is CommonTest {
recipient = deploy.cfg().sequencerFeeVaultRecipient(); recipient = deploy.cfg().sequencerFeeVaultRecipient();
} }
/// @dev Tests that the minimum withdrawal amount is correct.
function test_minWithdrawalAmount_succeeds() external view {
assertEq(sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount());
}
/// @dev Tests that the l1 fee wallet is correct. /// @dev Tests that the l1 fee wallet is correct.
function test_constructor_succeeds() external view { function test_constructor_succeeds() external view {
assertEq(sequencerFeeVault.l1FeeWallet(), recipient); assertEq(sequencerFeeVault.l1FeeWallet(), recipient);
assertEq(sequencerFeeVault.RECIPIENT(), recipient);
assertEq(sequencerFeeVault.recipient(), recipient);
assertEq(sequencerFeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount());
assertEq(sequencerFeeVault.minWithdrawalAmount(), deploy.cfg().sequencerFeeVaultMinimumWithdrawalAmount());
assertEq(uint8(sequencerFeeVault.WITHDRAWAL_NETWORK()), uint8(FeeVault.WithdrawalNetwork.L1));
assertEq(uint8(sequencerFeeVault.withdrawalNetwork()), uint8(FeeVault.WithdrawalNetwork.L1));
} }
/// @dev Tests that the fee vault is able to receive ETH. /// @dev Tests that the fee vault is able to receive ETH.
...@@ -64,21 +68,31 @@ contract SequencerFeeVault_Test is CommonTest { ...@@ -64,21 +68,31 @@ contract SequencerFeeVault_Test is CommonTest {
assertEq(sequencerFeeVault.totalProcessed(), 0); assertEq(sequencerFeeVault.totalProcessed(), 0);
vm.expectEmit(address(Predeploys.SEQUENCER_FEE_WALLET)); vm.expectEmit(address(Predeploys.SEQUENCER_FEE_WALLET));
emit Withdrawal(address(sequencerFeeVault).balance, sequencerFeeVault.RECIPIENT(), address(this)); emit Withdrawal(address(sequencerFeeVault).balance, recipient, address(this));
vm.expectEmit(address(Predeploys.SEQUENCER_FEE_WALLET)); vm.expectEmit(address(Predeploys.SEQUENCER_FEE_WALLET));
emit Withdrawal( emit Withdrawal(address(sequencerFeeVault).balance, recipient, address(this), FeeVault.WithdrawalNetwork.L1);
address(sequencerFeeVault).balance,
sequencerFeeVault.RECIPIENT(),
address(this),
FeeVault.WithdrawalNetwork.L1
);
// The entire vault's balance is withdrawn // The entire vault's balance is withdrawn
vm.expectCall( vm.expectCall(Predeploys.L2_TO_L1_MESSAGE_PASSER, address(sequencerFeeVault).balance, hex"");
Predeploys.L2_STANDARD_BRIDGE,
address(sequencerFeeVault).balance, // The message is passed to the correct recipient
abi.encodeWithSelector( vm.expectEmit(Predeploys.L2_TO_L1_MESSAGE_PASSER);
StandardBridge.bridgeETHTo.selector, sequencerFeeVault.l1FeeWallet(), 35_000, bytes("") emit MessagePassed(
l2ToL1MessagePasser.messageNonce(),
address(sequencerFeeVault),
recipient,
amount,
400_000,
hex"",
Hashing.hashWithdrawal(
Types.WithdrawalTransaction({
nonce: l2ToL1MessagePasser.messageNonce(),
sender: address(sequencerFeeVault),
target: recipient,
value: amount,
gasLimit: 400_000,
data: hex""
})
) )
); );
......
...@@ -13,11 +13,11 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -13,11 +13,11 @@ contract DeploymentSummary is DeploymentSummaryCode {
Vm private constant vm = Vm(VM_ADDRESS); Vm private constant vm = Vm(VM_ADDRESS);
address internal constant addressManagerAddress = 0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3; address internal constant addressManagerAddress = 0xBb2180ebd78ce97360503434eD37fcf4a1Df61c3;
address internal constant l1CrossDomainMessengerAddress = 0xf3E6CBcbF1AE12Fc13Bc8B14FA8A67CbE147fD99; address internal constant l1CrossDomainMessengerAddress = 0x094e6508ba9d9bf1ce421fff3dE06aE56e67901b;
address internal constant l1CrossDomainMessengerProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D; address internal constant l1CrossDomainMessengerProxyAddress = 0x20A42a5a785622c6Ba2576B2D6e924aA82BFA11D;
address internal constant l1ERC721BridgeAddress = 0x44637A4292E0CD2B17A55d5F6B2F05AFcAcD0586; address internal constant l1ERC721BridgeAddress = 0x44637A4292E0CD2B17A55d5F6B2F05AFcAcD0586;
address internal constant l1ERC721BridgeProxyAddress = 0xDeF3bca8c80064589E6787477FFa7Dd616B5574F; address internal constant l1ERC721BridgeProxyAddress = 0xDeF3bca8c80064589E6787477FFa7Dd616B5574F;
address internal constant l1StandardBridgeAddress = 0x04c50B398Cd4182428E79f7186b7C919cF17e86F; address internal constant l1StandardBridgeAddress = 0xb7900B27Be8f0E0fF65d1C3A4671e1220437dd2b;
address internal constant l1StandardBridgeProxyAddress = 0x0c8b5822b6e02CDa722174F19A1439A7495a3fA6; address internal constant l1StandardBridgeProxyAddress = 0x0c8b5822b6e02CDa722174F19A1439A7495a3fA6;
address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60; address internal constant l2OutputOracleAddress = 0x19652082F846171168Daf378C4fD3ee85a0D4A60;
address internal constant l2OutputOracleProxyAddress = 0x8B71b41D4dBEb2b6821d44692d3fACAAf77480Bb; address internal constant l2OutputOracleProxyAddress = 0x8B71b41D4dBEb2b6821d44692d3fACAAf77480Bb;
...@@ -347,7 +347,7 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -347,7 +347,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000005"; value = hex"0000000000000000000000000000000000000000000000000000000000000005";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"00000000000000000000000004c50b398cd4182428e79f7186b7c919cf17e86f"; value = hex"000000000000000000000000b7900b27be8f0e0ff65d1c3a4671e1220437dd2b";
vm.store(l1StandardBridgeProxyAddress, slot, value); vm.store(l1StandardBridgeProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
...@@ -410,7 +410,7 @@ contract DeploymentSummary is DeploymentSummaryCode { ...@@ -410,7 +410,7 @@ contract DeploymentSummary is DeploymentSummaryCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000009"; value = hex"0000000000000000000000000000000000000000000000000000000000000009";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"515216935740e67dfdda5cf8e248ea32b3277787818ab59153061ac875c9385e"; slot = hex"515216935740e67dfdda5cf8e248ea32b3277787818ab59153061ac875c9385e";
value = hex"000000000000000000000000f3e6cbcbf1ae12fc13bc8b14fa8a67cbe147fd99"; value = hex"000000000000000000000000094e6508ba9d9bf1ce421fff3de06ae56e67901b";
vm.store(addressManagerAddress, slot, value); vm.store(addressManagerAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000010000000000000000000000000000000000000000"; value = hex"0000000000000000000000010000000000000000000000000000000000000000";
......
This source diff could not be displayed because it is too large. You can view the blob instead.
...@@ -19,11 +19,11 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -19,11 +19,11 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
address internal constant delayedWETHProxyAddress = 0x0c8b5822b6e02CDa722174F19A1439A7495a3fA6; address internal constant delayedWETHProxyAddress = 0x0c8b5822b6e02CDa722174F19A1439A7495a3fA6;
address internal constant disputeGameFactoryAddress = 0x20B168142354Cee65a32f6D8cf3033E592299765; address internal constant disputeGameFactoryAddress = 0x20B168142354Cee65a32f6D8cf3033E592299765;
address internal constant disputeGameFactoryProxyAddress = 0x8B71b41D4dBEb2b6821d44692d3fACAAf77480Bb; address internal constant disputeGameFactoryProxyAddress = 0x8B71b41D4dBEb2b6821d44692d3fACAAf77480Bb;
address internal constant l1CrossDomainMessengerAddress = 0xf3E6CBcbF1AE12Fc13Bc8B14FA8A67CbE147fD99; address internal constant l1CrossDomainMessengerAddress = 0x094e6508ba9d9bf1ce421fff3dE06aE56e67901b;
address internal constant l1CrossDomainMessengerProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB; address internal constant l1CrossDomainMessengerProxyAddress = 0xc7B87b2b892EA5C3CfF47168881FE168C00377FB;
address internal constant l1ERC721BridgeAddress = 0x44637A4292E0CD2B17A55d5F6B2F05AFcAcD0586; address internal constant l1ERC721BridgeAddress = 0x44637A4292E0CD2B17A55d5F6B2F05AFcAcD0586;
address internal constant l1ERC721BridgeProxyAddress = 0xD31598c909d9C935a9e35bA70d9a3DD47d4D5865; address internal constant l1ERC721BridgeProxyAddress = 0xD31598c909d9C935a9e35bA70d9a3DD47d4D5865;
address internal constant l1StandardBridgeAddress = 0x04c50B398Cd4182428E79f7186b7C919cF17e86F; address internal constant l1StandardBridgeAddress = 0xb7900B27Be8f0E0fF65d1C3A4671e1220437dd2b;
address internal constant l1StandardBridgeProxyAddress = 0xDeF3bca8c80064589E6787477FFa7Dd616B5574F; address internal constant l1StandardBridgeProxyAddress = 0xDeF3bca8c80064589E6787477FFa7Dd616B5574F;
address internal constant mipsAddress = 0xF698388BFCDbd3f9f2F13ebC3E01471B3cc7cE83; address internal constant mipsAddress = 0xF698388BFCDbd3f9f2F13ebC3E01471B3cc7cE83;
address internal constant optimismPortal2Address = 0xfcbb237388CaF5b08175C9927a37aB6450acd535; address internal constant optimismPortal2Address = 0xfcbb237388CaF5b08175C9927a37aB6450acd535;
...@@ -442,7 +442,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -442,7 +442,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000005"; value = hex"0000000000000000000000000000000000000000000000000000000000000005";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc";
value = hex"00000000000000000000000004c50b398cd4182428e79f7186b7c919cf17e86f"; value = hex"000000000000000000000000b7900b27be8f0e0ff65d1c3a4671e1220437dd2b";
vm.store(l1StandardBridgeProxyAddress, slot, value); vm.store(l1StandardBridgeProxyAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000000000000000000000000000000000000000000001"; value = hex"0000000000000000000000000000000000000000000000000000000000000001";
...@@ -505,7 +505,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode { ...@@ -505,7 +505,7 @@ contract DeploymentSummaryFaultProofs is DeploymentSummaryFaultProofsCode {
value = hex"0000000000000000000000000000000000000000000000000000000000000009"; value = hex"0000000000000000000000000000000000000000000000000000000000000009";
vm.store(systemOwnerSafeAddress, slot, value); vm.store(systemOwnerSafeAddress, slot, value);
slot = hex"515216935740e67dfdda5cf8e248ea32b3277787818ab59153061ac875c9385e"; slot = hex"515216935740e67dfdda5cf8e248ea32b3277787818ab59153061ac875c9385e";
value = hex"000000000000000000000000f3e6cbcbf1ae12fc13bc8b14fa8a67cbe147fd99"; value = hex"000000000000000000000000094e6508ba9d9bf1ce421fff3de06ae56e67901b";
vm.store(addressManagerAddress, slot, value); vm.store(addressManagerAddress, slot, value);
slot = hex"0000000000000000000000000000000000000000000000000000000000000000"; slot = hex"0000000000000000000000000000000000000000000000000000000000000000";
value = hex"0000000000000000000000010000000000000000000000000000000000000000"; value = hex"0000000000000000000000010000000000000000000000000000000000000000";
......
...@@ -3,59 +3,63 @@ pragma solidity 0.8.15; ...@@ -3,59 +3,63 @@ pragma solidity 0.8.15;
// Testing utilities // Testing utilities
import { Test } from "forge-std/Test.sol"; import { Test } from "forge-std/Test.sol";
import { StdCheatsSafe } from "forge-std/StdCheats.sol";
// Target contract // Target contract
import { SafeCall } from "src/libraries/SafeCall.sol"; import { SafeCall } from "src/libraries/SafeCall.sol";
contract SafeCall_Test is Test { contract SafeCall_Test is Test {
/// @dev Tests that the `send` function succeeds. /// @notice Helper function to deduplicate code. Makes all assumptions required for these tests.
function testFuzz_send_succeeds(address from, address to, uint256 gas, uint64 value) external { function assumeNot(address _addr) internal {
vm.assume(from.balance == 0); vm.assume(_addr.balance == 0);
vm.assume(to.balance == 0); vm.assume(_addr != address(this));
// no precompiles (mainnet) assumeAddressIsNot(_addr, StdCheatsSafe.AddressType.ForgeAddress, StdCheatsSafe.AddressType.Precompile);
assumeNotPrecompile(to); }
// don't call the vm
vm.assume(to != address(vm));
vm.assume(from != address(vm));
// don't call the console
vm.assume(to != address(0x000000000000000000636F6e736F6c652e6c6f67));
// don't call the create2 deployer
vm.assume(to != address(0x4e59b44847b379578588920cA78FbF26c0B4956C));
vm.assume(to != address(this));
assertEq(from.balance, 0, "from balance is 0"); /// @notice Internal helper function for `send` tests
vm.deal(from, value); function sendTest(address _from, address _to, uint64 _gas, uint256 _value) internal {
assertEq(from.balance, value, "from balance not dealt"); assumeNot(_from);
assumeNot(_to);
uint256[2] memory balancesBefore = [from.balance, to.balance]; assertEq(_from.balance, 0, "from balance is 0");
vm.deal(_from, _value);
assertEq(_from.balance, _value, "from balance not dealt");
vm.expectCall(to, value, bytes("")); uint256[2] memory balancesBefore = [_from.balance, _to.balance];
vm.prank(from);
bool success = SafeCall.send(to, gas, value); vm.expectCall(_to, _value, bytes(""));
vm.prank(_from);
bool success;
if (_gas == 0) {
success = SafeCall.send({ _target: _to, _value: _value });
} else {
success = SafeCall.send({ _target: _to, _gas: _gas, _value: _value });
}
assertTrue(success, "send not successful"); assertTrue(success, "send not successful");
if (from == to) { if (_from == _to) {
assertEq(from.balance, balancesBefore[0], "Self-send did not change balance"); assertEq(_from.balance, balancesBefore[0], "Self-send did not change balance");
} else { } else {
assertEq(from.balance, balancesBefore[0] - value, "from balance not drained"); assertEq(_from.balance, balancesBefore[0] - _value, "from balance not drained");
assertEq(to.balance, balancesBefore[1] + value, "to balance received"); assertEq(_to.balance, balancesBefore[1] + _value, "to balance received");
} }
} }
/// @dev Tests that the `send` function succeeds.
function testFuzz_send_succeeds(address _from, address _to, uint256 _value) external {
sendTest({ _from: _from, _to: _to, _gas: 0, _value: _value });
}
/// @dev Tests that the `send` function with value succeeds.
function testFuzz_sendWithGas_succeeds(address _from, address _to, uint64 _gas, uint256 _value) external {
vm.assume(_gas != 0);
sendTest({ _from: _from, _to: _to, _gas: _gas, _value: _value });
}
/// @dev Tests that `call` succeeds. /// @dev Tests that `call` succeeds.
function testFuzz_call_succeeds(address from, address to, uint256 gas, uint64 value, bytes memory data) external { function testFuzz_call_succeeds(address from, address to, uint256 gas, uint64 value, bytes memory data) external {
vm.assume(from.balance == 0); assumeNot(from);
vm.assume(to.balance == 0); assumeNot(to);
// no precompiles (mainnet)
assumeNotPrecompile(to);
// don't call the vm
vm.assume(to != address(vm));
vm.assume(from != address(vm));
// don't call the console
vm.assume(to != address(0x000000000000000000636F6e736F6c652e6c6f67));
// don't call the create2 deployer
vm.assume(to != address(0x4e59b44847b379578588920cA78FbF26c0B4956C));
vm.assume(to != address(this));
assertEq(from.balance, 0, "from balance is 0"); assertEq(from.balance, 0, "from balance is 0");
vm.deal(from, value); vm.deal(from, value);
...@@ -86,18 +90,8 @@ contract SafeCall_Test is Test { ...@@ -86,18 +90,8 @@ contract SafeCall_Test is Test {
) )
external external
{ {
vm.assume(from.balance == 0); assumeNot(from);
vm.assume(to.balance == 0); assumeNot(to);
// no precompiles (mainnet)
assumeNotPrecompile(to);
// don't call the vm
vm.assume(to != address(vm));
vm.assume(from != address(vm));
// don't call the console
vm.assume(to != address(0x000000000000000000636F6e736F6c652e6c6f67));
// don't call the create2 deployer
vm.assume(to != address(0x4e59b44847b379578588920cA78FbF26c0B4956C));
vm.assume(to != address(this));
assertEq(from.balance, 0, "from balance is 0"); assertEq(from.balance, 0, "from balance is 0");
vm.deal(from, value); vm.deal(from, value);
......
...@@ -12,14 +12,20 @@ contract FeeVault_Test is Bridge_Initializer { ...@@ -12,14 +12,20 @@ contract FeeVault_Test is Bridge_Initializer {
/// @dev Tests that the constructor sets the correct values. /// @dev Tests that the constructor sets the correct values.
function test_constructor_l1FeeVault_succeeds() external view { function test_constructor_l1FeeVault_succeeds() external view {
assertEq(l1FeeVault.RECIPIENT(), deploy.cfg().l1FeeVaultRecipient()); assertEq(l1FeeVault.RECIPIENT(), deploy.cfg().l1FeeVaultRecipient());
assertEq(l1FeeVault.recipient(), deploy.cfg().l1FeeVaultRecipient());
assertEq(l1FeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().l1FeeVaultMinimumWithdrawalAmount()); assertEq(l1FeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().l1FeeVaultMinimumWithdrawalAmount());
assertEq(l1FeeVault.minWithdrawalAmount(), deploy.cfg().l1FeeVaultMinimumWithdrawalAmount());
assertEq(uint8(l1FeeVault.WITHDRAWAL_NETWORK()), uint8(FeeVault.WithdrawalNetwork.L1)); assertEq(uint8(l1FeeVault.WITHDRAWAL_NETWORK()), uint8(FeeVault.WithdrawalNetwork.L1));
assertEq(uint8(l1FeeVault.withdrawalNetwork()), uint8(FeeVault.WithdrawalNetwork.L1));
} }
/// @dev Tests that the constructor sets the correct values. /// @dev Tests that the constructor sets the correct values.
function test_constructor_baseFeeVault_succeeds() external view { function test_constructor_baseFeeVault_succeeds() external view {
assertEq(baseFeeVault.RECIPIENT(), deploy.cfg().baseFeeVaultRecipient()); assertEq(baseFeeVault.RECIPIENT(), deploy.cfg().baseFeeVaultRecipient());
assertEq(baseFeeVault.recipient(), deploy.cfg().baseFeeVaultRecipient());
assertEq(baseFeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().baseFeeVaultMinimumWithdrawalAmount()); assertEq(baseFeeVault.MIN_WITHDRAWAL_AMOUNT(), deploy.cfg().baseFeeVaultMinimumWithdrawalAmount());
assertEq(baseFeeVault.minWithdrawalAmount(), deploy.cfg().baseFeeVaultMinimumWithdrawalAmount());
assertEq(uint8(baseFeeVault.WITHDRAWAL_NETWORK()), uint8(FeeVault.WithdrawalNetwork.L1)); assertEq(uint8(baseFeeVault.WITHDRAWAL_NETWORK()), uint8(FeeVault.WithdrawalNetwork.L1));
assertEq(uint8(baseFeeVault.withdrawalNetwork()), uint8(FeeVault.WithdrawalNetwork.L1));
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment