Skip to content

Commit

Permalink
Add new test files
Browse files Browse the repository at this point in the history
  • Loading branch information
Benjimmutable committed Nov 3, 2023
1 parent 1bf7911 commit e3efc7d
Show file tree
Hide file tree
Showing 3 changed files with 312 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.21;

import {Test, console2} from "forge-std/Test.sol";
import {ERC20PresetMinterPauser} from "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {MockAxelarGateway} from "../../../../src/test/root/MockAxelarGateway.sol";
import {MockAxelarGasService} from "../../../../src/test/root/MockAxelarGasService.sol";
import {RootERC20Bridge, IRootERC20BridgeEvents, IERC20Metadata} from "../../../../src/root/RootERC20Bridge.sol";
import {RootAxelarBridgeAdaptor, IRootAxelarBridgeAdaptorEvents} from "../../../../src/root/RootAxelarBridgeAdaptor.sol";
import {Utils} from "../../../utils.t.sol";
import {WETH} from "../../../../src/test/root/WETH.sol";

contract RootERC20BridgeWithdrawIntegrationTest is Test, IRootERC20BridgeEvents, IRootAxelarBridgeAdaptorEvents, Utils {
address constant CHILD_BRIDGE = address(3);
address constant CHILD_BRIDGE_ADAPTOR = address(4);
string constant CHILD_CHAIN_NAME = "CHILD";
address constant IMX_TOKEN_ADDRESS = address(0xccc);
address constant NATIVE_ETH = address(0xeee);
address constant WRAPPED_ETH = address(0xddd);

uint256 constant withdrawAmount = 0.5 ether;

ERC20PresetMinterPauser public token;
ERC20PresetMinterPauser public imxToken;
RootERC20Bridge public rootBridge;
RootAxelarBridgeAdaptor public axelarAdaptor;
MockAxelarGateway public mockAxelarGateway;
MockAxelarGasService public axelarGasService;

function setUp() public {
deployCodeTo("WETH.sol", abi.encode("Wrapped ETH", "WETH"), WRAPPED_ETH);

(imxToken, token, rootBridge, axelarAdaptor, mockAxelarGateway, axelarGasService) =
rootIntegrationSetup(CHILD_BRIDGE, CHILD_BRIDGE_ADAPTOR, CHILD_CHAIN_NAME, IMX_TOKEN_ADDRESS, WRAPPED_ETH);

// Need to first map the token.
rootBridge.mapToken{value:1}(token);
// And give the bridge some tokens
token.transfer(address(rootBridge), 100 ether);
}

function test_withdraw_TransfersTokens() public {
bytes memory data = abi.encode(WITHDRAW_SIG, address(token), address(this), address(this), withdrawAmount);

bytes32 commandId = bytes32("testCommandId");
string memory sourceAddress = rootBridge.childBridgeAdaptor();

uint256 thisPreBal = token.balanceOf(address(this));
uint256 bridgePreBal = token.balanceOf(address(rootBridge));

axelarAdaptor.execute(commandId, CHILD_CHAIN_NAME, sourceAddress, data);

uint256 thisPostBal = token.balanceOf(address(this));
uint256 bridgePostBal = token.balanceOf(address(rootBridge));

assertEq(thisPostBal, thisPreBal + withdrawAmount, "Incorrect user balance after withdraw");
assertEq(bridgePostBal, bridgePreBal - withdrawAmount, "Incorrect bridge balance after withdraw");
}

function test_withdraw_TransfersTokens_DifferentReceiver() public {
address receiver = address(987654321);
bytes memory data = abi.encode(WITHDRAW_SIG, address(token), address(this), receiver, withdrawAmount);

bytes32 commandId = bytes32("testCommandId");
string memory sourceAddress = rootBridge.childBridgeAdaptor();

uint256 receiverPreBal = token.balanceOf(receiver);
uint256 bridgePreBal = token.balanceOf(address(rootBridge));

axelarAdaptor.execute(commandId, CHILD_CHAIN_NAME, sourceAddress, data);

uint256 receiverPostBal = token.balanceOf(receiver);
uint256 bridgePostBal = token.balanceOf(address(rootBridge));

assertEq(receiverPostBal, receiverPreBal + withdrawAmount, "Incorrect user balance after withdraw");
assertEq(bridgePostBal, bridgePreBal - withdrawAmount, "Incorrect bridge balance after withdraw");
}

function test_withdraw_EmitsRootChainERC20WithdrawEvent() public {
bytes memory data = abi.encode(WITHDRAW_SIG, address(token), address(this), address(this), withdrawAmount);

bytes32 commandId = bytes32("testCommandId");
string memory sourceAddress = rootBridge.childBridgeAdaptor();

vm.expectEmit();
emit RootChainERC20Withdraw(address(token), rootBridge.rootTokenToChildToken(address(token)), address(this), address(this), withdrawAmount);
axelarAdaptor.execute(commandId, CHILD_CHAIN_NAME, sourceAddress, data);
}
}
54 changes: 54 additions & 0 deletions test/unit/root/withdrawals/RootAxelarBridgeAdaptorWithdraw.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.21;

import {Test, console2} from "forge-std/Test.sol";
import {ERC20PresetMinterPauser} from "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {MockAxelarGateway} from "../../../../src/test/root/MockAxelarGateway.sol";
import {MockAxelarGasService} from "../../../../src/test/root/MockAxelarGasService.sol";
import {
RootAxelarBridgeAdaptor,
IRootAxelarBridgeAdaptorEvents,
IRootAxelarBridgeAdaptorErrors
} from "../../../../src/root/RootAxelarBridgeAdaptor.sol";
import {StubRootBridge} from "../../../../src/test/root/StubRootBridge.sol";

contract RootAxelarBridgeWithdrawAdaptorTest is Test, IRootAxelarBridgeAdaptorEvents, IRootAxelarBridgeAdaptorErrors {
address constant CHILD_BRIDGE = address(3);
string public childBridgeAdaptor;
string constant CHILD_CHAIN_NAME = "test";
bytes32 public constant MAP_TOKEN_SIG = keccak256("MAP_TOKEN");

ERC20PresetMinterPauser public token;
RootAxelarBridgeAdaptor public axelarAdaptor;
MockAxelarGateway public mockAxelarGateway;
MockAxelarGasService public axelarGasService;
StubRootBridge public stubRootBridge;

function setUp() public {
token = new ERC20PresetMinterPauser("Test", "TST");
mockAxelarGateway = new MockAxelarGateway();
axelarGasService = new MockAxelarGasService();
stubRootBridge = new StubRootBridge();
childBridgeAdaptor = stubRootBridge.childBridgeAdaptor();

axelarAdaptor = new RootAxelarBridgeAdaptor(address(mockAxelarGateway));
axelarAdaptor.initialize(address(stubRootBridge), CHILD_CHAIN_NAME, address(axelarGasService));
vm.deal(address(stubRootBridge), 99999999999);
}


function test_execute_callsBridge() public {
bytes32 commandId = bytes32("testCommandId");
string memory sourceChain = "test";
string memory sourceAddress = Strings.toHexString(address(123));
bytes memory payload = abi.encodePacked("payload");

// We expect to call the bridge's onMessageReceive function.
vm.expectCall(
address(stubRootBridge),
abi.encodeWithSelector(stubRootBridge.onMessageReceive.selector, sourceChain, sourceAddress, payload)
);
axelarAdaptor.execute(commandId, sourceChain, sourceAddress, payload);
}
}
167 changes: 167 additions & 0 deletions test/unit/root/withdrawals/RootERC20BridgeWithdraw.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
// SPDX-License-Identifier: Apache 2.0
pragma solidity ^0.8.21;

import {Test, console2} from "forge-std/Test.sol";
import {ERC20PresetMinterPauser} from "@openzeppelin/contracts/token/ERC20/presets/ERC20PresetMinterPauser.sol";
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
import {
RootERC20Bridge,
IRootERC20BridgeEvents,
IRootERC20BridgeErrors
} from "../../../../src/root/RootERC20Bridge.sol";
import {MockAxelarGateway} from "../../../../src/test/root/MockAxelarGateway.sol";
import {MockAxelarGasService} from "../../../../src/test/root/MockAxelarGasService.sol";
import {MockAdaptor} from "../../../../src/test/root/MockAdaptor.sol";
import {Utils} from "../../../utils.t.sol";

contract RootERC20BridgeWithdrawUnitTest is Test, IRootERC20BridgeEvents, IRootERC20BridgeErrors, Utils {
address constant CHILD_BRIDGE = address(3);
address constant CHILD_BRIDGE_ADAPTOR = address(4);
string CHILD_BRIDGE_ADAPTOR_STRING = Strings.toHexString(CHILD_BRIDGE_ADAPTOR);
string constant CHILD_CHAIN_NAME = "test";
address constant IMX_TOKEN = address(0xccc);
address constant WRAPPED_ETH = address(0xddd);
uint256 constant mapTokenFee = 300;
uint256 constant withdrawAmount = 0.5 ether;

ERC20PresetMinterPauser public token;
RootERC20Bridge public rootBridge;
MockAdaptor public mockAxelarAdaptor;
MockAxelarGateway public mockAxelarGateway;
MockAxelarGasService public axelarGasService;

function setUp() public {
token = new ERC20PresetMinterPauser("Test", "TST");
token.mint(address(this), 100 ether);
deployCodeTo("ERC20PresetMinterPauser.sol", abi.encode("ImmutableX", "IMX"), IMX_TOKEN);

deployCodeTo("WETH.sol", abi.encode("Wrapped ETH", "WETH"), WRAPPED_ETH);

rootBridge = new RootERC20Bridge();
mockAxelarGateway = new MockAxelarGateway();
axelarGasService = new MockAxelarGasService();

mockAxelarAdaptor = new MockAdaptor();

// The specific ERC20 token template does not matter for these unit tests
rootBridge.initialize(
address(mockAxelarAdaptor),
CHILD_BRIDGE,
CHILD_BRIDGE_ADAPTOR_STRING,
address(token),
IMX_TOKEN,
WRAPPED_ETH,
CHILD_CHAIN_NAME
);
}

function test_RevertsIf_WithdrawWithInvalidSender() public {
bytes memory data = abi.encode(WITHDRAW_SIG, IMX_TOKEN, address(this), address(this), withdrawAmount);

vm.expectRevert(NotBridgeAdaptor.selector);
rootBridge.onMessageReceive(CHILD_CHAIN_NAME, CHILD_BRIDGE_ADAPTOR_STRING, data);
}

function test_RevertsIf_OnMessageReceiveWithInvalidSourceChain() public {
bytes memory data = abi.encode(WITHDRAW_SIG, IMX_TOKEN, address(this), address(this), withdrawAmount);

vm.prank(address(mockAxelarAdaptor));
vm.expectRevert(InvalidSourceChain.selector);
rootBridge.onMessageReceive("ding_dong", CHILD_BRIDGE_ADAPTOR_STRING, data);
}

function test_RevertsIf_OnMessageReceiveWithInvalidSourceAddress() public {
bytes memory data = abi.encode(WITHDRAW_SIG, IMX_TOKEN, address(this), address(this), withdrawAmount);

console2.log(CHILD_CHAIN_NAME);
console2.log(rootBridge.childChain());
vm.prank(address(mockAxelarAdaptor));
vm.expectRevert(InvalidSourceAddress.selector);
rootBridge.onMessageReceive(CHILD_CHAIN_NAME, "DING_DONG", data);
}

function test_RevertsIf_OnMessageReceiveWithZeroDataLength() public {
bytes memory data;

vm.prank(address(mockAxelarAdaptor));
vm.expectRevert(InvalidData.selector);
rootBridge.onMessageReceive(CHILD_CHAIN_NAME, CHILD_BRIDGE_ADAPTOR_STRING, data);
}

function test_RevertsIf_OnMessageReceiveWithInvalidSignature() public {
bytes memory data = abi.encode(keccak256("RANDOM"), IMX_TOKEN, address(this), address(this), withdrawAmount);
vm.prank(address(mockAxelarAdaptor));
vm.expectRevert(InvalidData.selector);
rootBridge.onMessageReceive(CHILD_CHAIN_NAME, CHILD_BRIDGE_ADAPTOR_STRING, data);
}

function test_RevertsIf_OnMessageReceiveWithUnmappedToken() public {
bytes memory data = abi.encode(WITHDRAW_SIG, IMX_TOKEN, address(this), address(this), withdrawAmount);
vm.prank(address(mockAxelarAdaptor));
vm.expectRevert(NotMapped.selector);
rootBridge.onMessageReceive(CHILD_CHAIN_NAME, CHILD_BRIDGE_ADAPTOR_STRING, data);
}

function test_onMessageReceive_TransfersTokens() public {
// Need to first map the token.
rootBridge.mapToken(token);
// And give the bridge some tokens
token.transfer(address(rootBridge), 100 ether);

uint256 thisPreBal = token.balanceOf(address(this));
uint256 bridgePreBal = token.balanceOf(address(rootBridge));

bytes memory data = abi.encode(WITHDRAW_SIG, token, address(this), address(this), withdrawAmount);
vm.prank(address(mockAxelarAdaptor));
rootBridge.onMessageReceive(CHILD_CHAIN_NAME, CHILD_BRIDGE_ADAPTOR_STRING, data);

assertEq(token.balanceOf(address(this)), thisPreBal + withdrawAmount, "Tokens not transferred to receiver");
assertEq(token.balanceOf(address(rootBridge)), bridgePreBal - withdrawAmount, "Tokens not transferred from bridge");
}

function test_onMessageReceive_TransfersTokens_DifferentReceiver() public {
address receiver = address(123456);
// Need to first map the token.
rootBridge.mapToken(token);
// And give the bridge some tokens
token.transfer(address(rootBridge), 100 ether);

uint256 receiverPreBal = token.balanceOf(receiver);
uint256 bridgePreBal = token.balanceOf(address(rootBridge));

bytes memory data = abi.encode(WITHDRAW_SIG, token, address(this), receiver, withdrawAmount);
vm.prank(address(mockAxelarAdaptor));
rootBridge.onMessageReceive(CHILD_CHAIN_NAME, CHILD_BRIDGE_ADAPTOR_STRING, data);

assertEq(token.balanceOf(receiver), receiverPreBal + withdrawAmount, "Tokens not transferred to receiver");
assertEq(token.balanceOf(address(rootBridge)), bridgePreBal - withdrawAmount, "Tokens not transferred from bridge");
}

function test_onMessageReceive_EmitsRootChainERC20WithdrawEvent() public {
// Need to first map the token.
rootBridge.mapToken(token);
// And give the bridge some tokens
token.transfer(address(rootBridge), 100 ether);

bytes memory data = abi.encode(WITHDRAW_SIG, token, address(this), address(this), withdrawAmount);
vm.expectEmit();
emit RootChainERC20Withdraw(address(token), rootBridge.rootTokenToChildToken(address(token)), address(this), address(this), withdrawAmount);
vm.prank(address(mockAxelarAdaptor));
rootBridge.onMessageReceive(CHILD_CHAIN_NAME, CHILD_BRIDGE_ADAPTOR_STRING, data);
}

function test_onMessageReceive_EmitsRootChainERC20WithdrawEvent_DifferentReceiver() public {
address receiver = address(123456);
// Need to first map the token.
rootBridge.mapToken(token);
// And give the bridge some tokens
token.transfer(address(rootBridge), 100 ether);

bytes memory data = abi.encode(WITHDRAW_SIG, token, address(this), receiver, withdrawAmount);
vm.expectEmit();
emit RootChainERC20Withdraw(address(token), rootBridge.rootTokenToChildToken(address(token)), address(this), receiver, withdrawAmount);
vm.prank(address(mockAxelarAdaptor));
rootBridge.onMessageReceive(CHILD_CHAIN_NAME, CHILD_BRIDGE_ADAPTOR_STRING, data);
}

}

0 comments on commit e3efc7d

Please sign in to comment.