Skip to content

Commit

Permalink
Add L1ForceOnlyReverseCustomGatewayTest tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gvladika committed Jan 26, 2024
1 parent 55ccc23 commit c762a3c
Show file tree
Hide file tree
Showing 2 changed files with 322 additions and 14 deletions.
313 changes: 313 additions & 0 deletions test-foundry/L1ForceOnlyReverseCustomGateway.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,313 @@
// SPDX-License-Identifier: Apache-2.0

pragma solidity ^0.8.0;

import "./L1ReverseCustomGateway.t.sol";
import {L1ForceOnlyReverseCustomGateway} from
"contracts/tokenbridge/ethereum/gateway/L1ForceOnlyReverseCustomGateway.sol";
import {
MintableTestCustomTokenL1,
ReverseTestCustomTokenL1
} from "contracts/tokenbridge/test/TestCustomTokenL1.sol";
import {ERC20PresetMinterPauser} from
"@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";

contract L1ForceOnlyReverseCustomGatewayTest is L1ReverseCustomGatewayTest {
function setUp() public virtual override {
inbox = address(new InboxMock());

l1Gateway = new L1ForceOnlyReverseCustomGateway();
L1ForceOnlyReverseCustomGateway(address(l1Gateway)).initialize(
l2Gateway, router, inbox, owner
);

token = IERC20(address(new TestERC20()));

maxSubmissionCost = 20;
retryableCost = maxSubmissionCost + gasPriceBid * maxGas;

// fund user and router
vm.prank(user);
TestERC20(address(token)).mint();
vm.deal(router, 100 ether);
vm.deal(address(token), 100 ether);
vm.deal(owner, 100 ether);
}

/* solhint-disable func-name-mixedcase */
function test_calculateL2TokenAddress(address l1Token, address l2Token)
public
virtual
override
{
vm.assume(l1Token != FOUNDRY_CHEATCODE_ADDRESS && l2Token != FOUNDRY_CHEATCODE_ADDRESS);
vm.deal(l1Token, 100 ether);

// register token to gateway
// register token to gateway
address[] memory l1Tokens = new address[](1);
l1Tokens[0] = l1Token;
address[] memory l2Tokens = new address[](1);
l2Tokens[0] = l2Token;

vm.prank(owner);
uint256 seqNum0 = L1CustomGateway(address(l1Gateway)).forceRegisterTokenToL2{
value: retryableCost
}(l1Tokens, l2Tokens, maxGas, gasPriceBid, maxSubmissionCost);

assertEq(l1Gateway.calculateL2TokenAddress(l1Token), l2Token, "Invalid L2 token address");
}

function test_outboundTransfer() public override {
// fund user with tokens
MintableTestCustomTokenL1 bridgedToken =
new ReverseTestCustomTokenL1(address(l1Gateway), router);
vm.prank(address(user));
bridgedToken.mint();

// snapshot state before
uint256 userBalanceBefore = bridgedToken.balanceOf(user);

uint256 amount = 300;
bytes memory callHookData = "";
bytes memory routerEncodedData = buildRouterEncodedData(callHookData);

// register token to gateway
address[] memory l1Tokens = new address[](1);
l1Tokens[0] = address(bridgedToken);
address[] memory l2Tokens = new address[](1);
l2Tokens[0] = makeAddr("tokenL2Address");

vm.prank(owner);
uint256 seqNum0 = L1CustomGateway(address(l1Gateway)).forceRegisterTokenToL2{
value: retryableCost
}(l1Tokens, l2Tokens, maxGas, gasPriceBid, maxSubmissionCost);

// approve token
vm.prank(user);
bridgedToken.approve(address(l1Gateway), amount);

// event checkers
vm.expectEmit(true, true, true, true);
emit TicketData(maxSubmissionCost);

vm.expectEmit(true, true, true, true);
emit RefundAddresses(user, user);

vm.expectEmit(true, true, true, true);
emit InboxRetryableTicket(
address(l1Gateway),
l2Gateway,
0,
maxGas,
l1Gateway.getOutboundCalldata(address(bridgedToken), user, user, amount, callHookData)
);

vm.expectEmit(true, true, true, true);
emit DepositInitiated(address(bridgedToken), user, user, 1, amount);

// trigger transfer
vm.prank(router);
bytes memory seqNum1 = l1Gateway.outboundTransfer{value: retryableCost}(
address(bridgedToken), user, amount, maxGas, gasPriceBid, routerEncodedData
);

// check tokens are burned
uint256 userBalanceAfter = bridgedToken.balanceOf(user);
assertEq(userBalanceBefore - userBalanceAfter, amount, "Wrong user balance");

assertEq(seqNum0, 0, "Invalid seqNum0");
assertEq(seqNum1, abi.encode(1), "Invalid seqNum1");
}

function test_outboundTransferCustomRefund() public override {
// fund user with tokens
MintableTestCustomTokenL1 bridgedToken =
new ReverseTestCustomTokenL1(address(l1Gateway), router);
vm.prank(address(user));
bridgedToken.mint();

// snapshot state before
uint256 userBalanceBefore = bridgedToken.balanceOf(user);

uint256 amount = 450;
bytes memory callHookData = "";
bytes memory routerEncodedData = buildRouterEncodedData(callHookData);

// register token to gateway
address[] memory l1Tokens = new address[](1);
l1Tokens[0] = address(bridgedToken);
address[] memory l2Tokens = new address[](1);
l2Tokens[0] = makeAddr("tokenL2Address");

vm.prank(owner);
uint256 seqNum0 = L1CustomGateway(address(l1Gateway)).forceRegisterTokenToL2{
value: retryableCost
}(l1Tokens, l2Tokens, maxGas, gasPriceBid, maxSubmissionCost);

// approve token
vm.prank(user);
bridgedToken.approve(address(l1Gateway), amount);

// event checkers
vm.expectEmit(true, true, true, true);
emit TicketData(maxSubmissionCost);

vm.expectEmit(true, true, true, true);
emit RefundAddresses(creditBackAddress, user);

vm.expectEmit(true, true, true, true);
emit InboxRetryableTicket(
address(l1Gateway),
l2Gateway,
0,
maxGas,
l1Gateway.getOutboundCalldata(address(bridgedToken), user, user, amount, callHookData)
);

vm.expectEmit(true, true, true, true);
emit DepositInitiated(address(bridgedToken), user, user, 1, amount);

// trigger deposit
vm.prank(router);
bytes memory seqNum1 = l1Gateway.outboundTransferCustomRefund{value: retryableCost}(
address(bridgedToken),
creditBackAddress,
user,
amount,
maxGas,
gasPriceBid,
routerEncodedData
);

// check tokens are escrowed
uint256 userBalanceAfter = bridgedToken.balanceOf(user);
assertEq(userBalanceBefore - userBalanceAfter, amount, "Wrong user balance");

assertEq(seqNum0, 0, "Invalid seqNum0");
assertEq(seqNum1, abi.encode(1), "Invalid seqNum1");
}

function test_outboundTransferCustomRefund_revert_InsufficientAllowance() public override {
// fund user with tokens
MintableTestCustomTokenL1 bridgedToken =
new ReverseTestCustomTokenL1(address(l1Gateway), router);
vm.prank(address(user));
bridgedToken.mint();

uint256 tooManyTokens = 500 ether;

// register token to gateway
address[] memory l1Tokens = new address[](1);
l1Tokens[0] = address(bridgedToken);
address[] memory l2Tokens = new address[](1);
l2Tokens[0] = makeAddr("tokenL2Address");

vm.prank(owner);
uint256 seqNum0 = L1CustomGateway(address(l1Gateway)).forceRegisterTokenToL2{
value: retryableCost
}(l1Tokens, l2Tokens, maxGas, gasPriceBid, maxSubmissionCost);

vm.prank(router);
vm.expectRevert("ERC20: burn amount exceeds balance");
l1Gateway.outboundTransferCustomRefund{value: 1 ether}(
address(bridgedToken),
user,
user,
tooManyTokens,
0.1 ether,
0.01 ether,
buildRouterEncodedData("")
);
}

function test_outboundTransferCustomRefund_revert_NoL2TokenSet() public virtual override {
uint256 tooManyTokens = 500 ether;

// register token to gateway
address[] memory l1Tokens = new address[](1);
l1Tokens[0] = address(token);
address[] memory l2Tokens = new address[](1);
l2Tokens[0] = address(0);

vm.prank(owner);
L1CustomGateway(address(l1Gateway)).forceRegisterTokenToL2{value: retryableCost}(
l1Tokens, l2Tokens, maxGas, gasPriceBid, maxSubmissionCost
);

vm.prank(router);
vm.expectRevert("NO_L2_TOKEN_SET");
l1Gateway.outboundTransferCustomRefund{value: 1 ether}(
address(token),
user,
user,
tooManyTokens,
0.1 ether,
0.01 ether,
buildRouterEncodedData("")
);
}

function test_outboundTransferCustomRefund_revert_Reentrancy() public override {
// fund user with tokens
MintableTestCustomTokenL1 bridgedToken =
new ReverseTestCustomTokenL1(address(l1Gateway), router);
vm.prank(address(user));
bridgedToken.mint();

// register token to gateway
address[] memory l1Tokens = new address[](1);
l1Tokens[0] = address(bridgedToken);
address[] memory l2Tokens = new address[](1);
l2Tokens[0] = makeAddr("tokenL2Address");

vm.prank(owner);
L1CustomGateway(address(l1Gateway)).forceRegisterTokenToL2{value: retryableCost}(
l1Tokens, l2Tokens, maxGas, gasPriceBid, maxSubmissionCost
);

// approve token
uint256 amount = 450;
vm.prank(user);
bridgedToken.approve(address(l1Gateway), amount);

// trigger re-entrancy
MockReentrantERC20 mockReentrantERC20 = new MockReentrantERC20();
vm.etch(address(bridgedToken), address(mockReentrantERC20).code);

vm.expectRevert("ReentrancyGuard: reentrant call");
vm.prank(router);
l1Gateway.outboundTransferCustomRefund{value: retryableCost}(
address(bridgedToken),
creditBackAddress,
user,
amount,
maxGas,
gasPriceBid,
buildRouterEncodedData("")
);
}

function test_registerTokenToL2(address, address l2Token) public virtual override {
vm.expectRevert("REGISTER_TOKEN_ON_L2_DISABLED");
L1CustomGateway(address(l1Gateway)).registerTokenToL2{value: retryableCost}(
l2Token, maxGas, gasPriceBid, maxSubmissionCost
);
}

function test_registerTokenToL2_CustomRefund(address, address) public virtual override {
0; // N/A
}

function test_registerTokenToL2_UpdateToSameAddress(address, address) public virtual override {
0; // N/A
}

function test_registerTokenToL2_revert_NoUpdateToDifferentAddress() public virtual override {
0; // N/A
}

function test_registerTokenToL2_revert_NotArbEnabled() public virtual override {
0; // N/A
}
}
23 changes: 9 additions & 14 deletions test-foundry/L1ReverseCustomGateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

pragma solidity ^0.8.0;

import {L1CustomGatewayTest, InboxMock, IERC20, IInbox, TestERC20} from "./L1CustomGateway.t.sol";
import "./L1CustomGateway.t.sol";
import {L1ReverseCustomGateway} from
"contracts/tokenbridge/ethereum/gateway/L1ReverseCustomGateway.sol";
import {
Expand Down Expand Up @@ -33,7 +33,7 @@ contract L1ReverseCustomGatewayTest is L1CustomGatewayTest {
}

/* solhint-disable func-name-mixedcase */
function test_finalizeInboundTransfer() public override {
function test_finalizeInboundTransfer() public virtual override {
// fund gateway with bridged tokens
MintableTestCustomTokenL1 bridgedToken =
new MintableTestCustomTokenL1(address(l1Gateway), router);
Expand Down Expand Up @@ -61,7 +61,7 @@ contract L1ReverseCustomGatewayTest is L1CustomGatewayTest {
assertEq(userBalanceAfter - userBalanceBefore, amount, "Wrong user balance");
}

function test_outboundTransfer() public override {
function test_outboundTransfer() public virtual override {
// fund user with tokens
MintableTestCustomTokenL1 bridgedToken =
new ReverseTestCustomTokenL1(address(l1Gateway), router);
Expand Down Expand Up @@ -124,7 +124,7 @@ contract L1ReverseCustomGatewayTest is L1CustomGatewayTest {
assertEq(seqNum1, abi.encode(1), "Invalid seqNum1");
}

function test_outboundTransferCustomRefund() public override {
function test_outboundTransferCustomRefund() public virtual override {
// fund user with tokens
MintableTestCustomTokenL1 bridgedToken =
new ReverseTestCustomTokenL1(address(l1Gateway), router);
Expand Down Expand Up @@ -193,7 +193,11 @@ contract L1ReverseCustomGatewayTest is L1CustomGatewayTest {
assertEq(seqNum1, abi.encode(1), "Invalid seqNum1");
}

function test_outboundTransferCustomRefund_revert_InsufficientAllowance() public override {
function test_outboundTransferCustomRefund_revert_InsufficientAllowance()
public
virtual
override
{
// fund user with tokens
MintableTestCustomTokenL1 bridgedToken =
new ReverseTestCustomTokenL1(address(l1Gateway), router);
Expand Down Expand Up @@ -272,12 +276,3 @@ contract L1ReverseCustomGatewayTest is L1CustomGatewayTest {
);
}
}

contract MockReentrantERC20 {
function bridgeBurn(address, uint256) external {
// re-enter
L1ReverseCustomGateway(msg.sender).outboundTransferCustomRefund(
address(100), address(100), address(100), 2, 2, 3, bytes("")
);
}
}

0 comments on commit c762a3c

Please sign in to comment.