Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 67 additions & 2 deletions packages/contracts-bedrock/test/L1/OptimismPortal.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -1006,7 +1006,10 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
)
);

uint256 bobBalanceBefore = address(bob).balance;
// Fund the portal so that we can withdraw ETH.
vm.store(address(optimismPortal), bytes32(uint256(61)), bytes32(uint256(0xFFFFFFFF)));
vm.deal(address(optimismPortal), 0xFFFFFFFF);
uint256 bobBalanceBefore = bob.balance;

vm.expectEmit(true, true, true, true);
emit WithdrawalProven(_withdrawalHash_noData, alice, bob);
Expand All @@ -1019,7 +1022,69 @@ contract OptimismPortal_FinalizeWithdrawal_Test is CommonTest {
emit WithdrawalFinalized(_withdrawalHash_noData, true);
optimismPortal.finalizeWithdrawalTransaction(_defaultTx_noData);

assertEq(address(bob).balance, bobBalanceBefore + 100);
assertEq(bob.balance, bobBalanceBefore + 100);
}

/// @dev Tests that `finalizeWithdrawalTransaction` succeeds when _tx.data is empty and with a custom gas token.
function test_finalizeWithdrawalTransaction_noTxDataNonEtherGasToken_succeeds() external {
Types.WithdrawalTransaction memory _defaultTx_noData = Types.WithdrawalTransaction({
nonce: 0,
sender: alice,
target: bob,
value: 100,
gasLimit: 100_000,
data: hex""
});
// Get withdrawal proof data we can use for testing.
(
bytes32 _stateRoot_noData,
bytes32 _storageRoot_noData,
bytes32 _outputRoot_noData,
bytes32 _withdrawalHash_noData,
bytes[] memory _withdrawalProof_noData
) = ffi.getProveWithdrawalTransactionInputs(_defaultTx_noData);
// Setup a dummy output root proof for reuse.
Types.OutputRootProof memory _outputRootProof_noData = Types.OutputRootProof({
version: bytes32(uint256(0)),
stateRoot: _stateRoot_noData,
messagePasserStorageRoot: _storageRoot_noData,
latestBlockhash: bytes32(uint256(0))
});

// Configure the oracle to return the output root we've prepared.
vm.mockCall(
address(l2OutputOracle),
abi.encodePacked(IL2OutputOracle.getL2Output.selector),
abi.encode(
Types.OutputProposal(
_outputRoot_noData,
l2OutputOracle.getL2Output(_proposedOutputIndex).timestamp,
uint128(_proposedBlockNumber)
)
)
);

// Fund the portal so that we can withdraw ETH.
vm.store(address(optimismPortal), bytes32(uint256(61)), bytes32(uint256(0xFFFFFFFF)));
deal(address(L1Token), address(optimismPortal), 0xFFFFFFFF);
// modify the gas token to be non ether
vm.mockCall(
address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(L1Token), 18)
);
uint256 bobBalanceBefore = L1Token.balanceOf(bob);

vm.expectEmit(true, true, true, true);
emit WithdrawalProven(_withdrawalHash_noData, alice, bob);
optimismPortal.proveWithdrawalTransaction(
_defaultTx_noData, _proposedOutputIndex, _outputRootProof_noData, _withdrawalProof_noData
);

vm.warp(block.timestamp + l2OutputOracle.FINALIZATION_PERIOD_SECONDS() + 1);
vm.expectEmit(true, true, false, true);
emit WithdrawalFinalized(_withdrawalHash_noData, true);
optimismPortal.finalizeWithdrawalTransaction(_defaultTx_noData);

assertEq(L1Token.balanceOf(bob), bobBalanceBefore + 100);
}

/// @dev Tests that `finalizeWithdrawalTransaction` reverts if the finalization period
Expand Down
171 changes: 167 additions & 4 deletions packages/contracts-bedrock/test/L1/OptimismPortal2.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -802,6 +802,169 @@ contract OptimismPortal2_FinalizeWithdrawal_Test is CommonTest {
assert(address(bob).balance == bobBalanceBefore + 100);
}

/// @dev Tests that `finalizeWithdrawalTransaction` reverts if the target reverts and caller is the
/// ESTIMATION_ADDRESS.
function test_finalizeWithdrawalTransaction_targetFailsAndCallerIsEstimationAddress_reverts() external {
vm.etch(bob, hex"fe"); // Contract with just the invalid opcode.

vm.prank(alice);
vm.expectEmit(true, true, true, true);
emit WithdrawalProven(_withdrawalHash, alice, bob);
optimismPortal2.proveWithdrawalTransaction(_defaultTx, _proposedGameIndex, _outputRootProof, _withdrawalProof);

// Warp and resolve the dispute game.
game.resolveClaim(0, 0);
game.resolve();
vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1 seconds);

vm.startPrank(alice, Constants.ESTIMATION_ADDRESS);
vm.expectRevert(GasEstimation.selector);
optimismPortal2.finalizeWithdrawalTransaction(_defaultTx);
}

/// @dev Tests that `finalizeWithdrawalTransaction` succeeds when _tx.data is empty.
function test_finalizeWithdrawalTransaction_noTxData_succeeds() external {
Types.WithdrawalTransaction memory _defaultTx_noData = Types.WithdrawalTransaction({
nonce: 0,
sender: alice,
target: bob,
value: 100,
gasLimit: 100_000,
data: hex""
});
// Get withdrawal proof data we can use for testing.
(
bytes32 _stateRoot_noData,
bytes32 _storageRoot_noData,
bytes32 _outputRoot_noData,
bytes32 _withdrawalHash_noData,
bytes[] memory _withdrawalProof_noData
) = ffi.getProveWithdrawalTransactionInputs(_defaultTx_noData);
// Setup a dummy output root proof for reuse.
Types.OutputRootProof memory _outputRootProof_noData = Types.OutputRootProof({
version: bytes32(uint256(0)),
stateRoot: _stateRoot_noData,
messagePasserStorageRoot: _storageRoot_noData,
latestBlockhash: bytes32(uint256(0))
});
uint256 _proposedBlockNumber_noData = 0xFF;
IFaultDisputeGame game_noData = IFaultDisputeGame(
payable(
address(
disputeGameFactory.create(
optimismPortal2.respectedGameType(),
Claim.wrap(_outputRoot_noData),
abi.encode(_proposedBlockNumber_noData)
)
)
)
);
uint256 _proposedGameIndex_noData = disputeGameFactory.gameCount() - 1;
// Warp beyond the chess clocks and finalize the game.
vm.warp(block.timestamp + game_noData.maxClockDuration().raw() + 1 seconds);
// Fund the portal so that we can withdraw ETH.
vm.store(address(optimismPortal2), bytes32(uint256(61)), bytes32(uint256(0xFFFFFFFF)));
vm.deal(address(optimismPortal2), 0xFFFFFFFF);

uint256 bobBalanceBefore = bob.balance;

vm.expectEmit(address(optimismPortal2));
emit WithdrawalProven(_withdrawalHash_noData, alice, bob);
vm.expectEmit(address(optimismPortal2));
emit WithdrawalProvenExtension1(_withdrawalHash_noData, address(this));
optimismPortal2.proveWithdrawalTransaction({
_tx: _defaultTx_noData,
_disputeGameIndex: _proposedGameIndex_noData,
_outputRootProof: _outputRootProof_noData,
_withdrawalProof: _withdrawalProof_noData
});

// Warp and resolve the dispute game.
game_noData.resolveClaim(0, 0);
game_noData.resolve();
vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1 seconds);

vm.expectEmit(true, true, false, true);
emit WithdrawalFinalized(_withdrawalHash_noData, true);
optimismPortal2.finalizeWithdrawalTransaction(_defaultTx_noData);

assert(bob.balance == bobBalanceBefore + 100);
}

/// @dev Tests that `finalizeWithdrawalTransaction` succeeds when _tx.data is empty and with a custom gas token.
function test_finalizeWithdrawalTransaction_noTxDataNonEtherGasToken_succeeds() external {
Types.WithdrawalTransaction memory _defaultTx_noData = Types.WithdrawalTransaction({
nonce: 0,
sender: alice,
target: bob,
value: 100,
gasLimit: 100_000,
data: hex""
});
// Get withdrawal proof data we can use for testing.
(
bytes32 _stateRoot_noData,
bytes32 _storageRoot_noData,
bytes32 _outputRoot_noData,
bytes32 _withdrawalHash_noData,
bytes[] memory _withdrawalProof_noData
) = ffi.getProveWithdrawalTransactionInputs(_defaultTx_noData);
// Setup a dummy output root proof for reuse.
Types.OutputRootProof memory _outputRootProof_noData = Types.OutputRootProof({
version: bytes32(uint256(0)),
stateRoot: _stateRoot_noData,
messagePasserStorageRoot: _storageRoot_noData,
latestBlockhash: bytes32(uint256(0))
});
uint256 _proposedBlockNumber_noData = 0xFF;
IFaultDisputeGame game_noData = IFaultDisputeGame(
payable(
address(
disputeGameFactory.create(
optimismPortal2.respectedGameType(),
Claim.wrap(_outputRoot_noData),
abi.encode(_proposedBlockNumber_noData)
)
)
)
);
uint256 _proposedGameIndex_noData = disputeGameFactory.gameCount() - 1;
// Warp beyond the chess clocks and finalize the game.
vm.warp(block.timestamp + game_noData.maxClockDuration().raw() + 1 seconds);
// Fund the portal so that we can withdraw ETH.
vm.store(address(optimismPortal2), bytes32(uint256(61)), bytes32(uint256(0xFFFFFFFF)));
deal(address(L1Token), address(optimismPortal2), 0xFFFFFFFF);

// modify the gas token to be non ether
vm.mockCall(
address(systemConfig), abi.encodeCall(systemConfig.gasPayingToken, ()), abi.encode(address(L1Token), 18)
);

uint256 bobBalanceBefore = L1Token.balanceOf(bob);

vm.expectEmit(address(optimismPortal2));
emit WithdrawalProven(_withdrawalHash_noData, alice, bob);
vm.expectEmit(address(optimismPortal2));
emit WithdrawalProvenExtension1(_withdrawalHash_noData, address(this));
optimismPortal2.proveWithdrawalTransaction({
_tx: _defaultTx_noData,
_disputeGameIndex: _proposedGameIndex_noData,
_outputRootProof: _outputRootProof_noData,
_withdrawalProof: _withdrawalProof_noData
});

// Warp and resolve the dispute game.
game_noData.resolveClaim(0, 0);
game_noData.resolve();
vm.warp(block.timestamp + optimismPortal2.proofMaturityDelaySeconds() + 1 seconds);

vm.expectEmit(true, true, false, true);
emit WithdrawalFinalized(_withdrawalHash_noData, true);
optimismPortal2.finalizeWithdrawalTransaction(_defaultTx_noData);

assert(L1Token.balanceOf(bob) == bobBalanceBefore + 100);
}

/// @dev Tests that `finalizeWithdrawalTransaction` succeeds.
function test_finalizeWithdrawalTransaction_provenWithdrawalHashEther_succeeds() external {
uint256 bobBalanceBefore = address(bob).balance;
Expand Down Expand Up @@ -1575,7 +1738,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal
);

// Deposit the token into the portal
optimismPortal.depositERC20Transaction(_to, _mint, _value, _gasLimit, _isCreation, _data);
optimismPortal2.depositERC20Transaction(_to, _mint, _value, _gasLimit, _isCreation, _data);

// Assert final balance equals the deposited amount
assertEq(token.balanceOf(address(optimismPortal2)), _mint);
Expand Down Expand Up @@ -1657,7 +1820,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal
);

// Mock the token balance
vm.mockCall(address(token), abi.encodeCall(token.balanceOf, (address(optimismPortal))), abi.encode(0));
vm.mockCall(address(token), abi.encodeCall(token.balanceOf, (address(optimismPortal2))), abi.encode(0));

// Call minimumGasLimit(0) before vm.expectRevert to ensure vm.expectRevert is for depositERC20Transaction
uint64 gasLimit = optimismPortal2.minimumGasLimit(0);
Expand Down Expand Up @@ -1723,7 +1886,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal
);

// Deposit the token into the portal
optimismPortal2.depositERC20Transaction(address(0), _amount, 0, optimismPortal.minimumGasLimit(0), false, "");
optimismPortal2.depositERC20Transaction(address(0), _amount, 0, optimismPortal2.minimumGasLimit(0), false, "");

// Check that the balance has been correctly updated
assertEq(optimismPortal2.balance(), _amount);
Expand All @@ -1742,7 +1905,7 @@ contract OptimismPortal2WithMockERC20_Test is OptimismPortal2_FinalizeWithdrawal

// Deposit the token into the portal
optimismPortal2.depositERC20Transaction(
address(bob), _defaultTx.value, 0, optimismPortal.minimumGasLimit(0), false, ""
address(bob), _defaultTx.value, 0, optimismPortal2.minimumGasLimit(0), false, ""
);

assertEq(optimismPortal2.balance(), _defaultTx.value);
Expand Down