diff --git a/test/integration/child/withdrawals/ChildAxelarBridgeWithdraw.t.sol b/test/integration/child/withdrawals/ChildAxelarBridgeWithdraw.t.sol index 6faaaecd..9f69c2d2 100644 --- a/test/integration/child/withdrawals/ChildAxelarBridgeWithdraw.t.sol +++ b/test/integration/child/withdrawals/ChildAxelarBridgeWithdraw.t.sol @@ -20,26 +20,20 @@ contract ChildERC20BridgeWithdrawIntegrationTest is IChildAxelarBridgeAdaptorErrors, Utils { - address constant CHILD_BRIDGE = address(3); - address constant CHILD_BRIDGE_ADAPTOR = address(4); - string constant CHILD_CHAIN_NAME = "test"; - address constant IMX_TOKEN_ADDRESS = address(0xccc); - address constant NATIVE_ETH = address(0xeee); - address constant WRAPPED_ETH = address(0xddd); - uint256 constant withdrawFee = 200; uint256 constant withdrawAmount = 99999999999; ChildERC20Bridge public childBridge; ChildAxelarBridgeAdaptor public axelarAdaptor; address public rootToken; - address public rootImxToken; - ChildERC20 public childTokenTemplate; - MockAxelarGasService public axelarGasService; + MockAxelarGasService public mockAxelarGasService; MockAxelarGateway public mockAxelarGateway; + address receiver = address(0xabcd); + function setUp() public { - (childBridge, axelarAdaptor, rootToken, rootImxToken, childTokenTemplate, axelarGasService, mockAxelarGateway) = + ChildERC20 childTokenTemplate; + (childBridge, axelarAdaptor, rootToken,, childTokenTemplate, mockAxelarGasService, mockAxelarGateway) = childIntegrationSetup(); } @@ -87,10 +81,10 @@ contract ChildERC20BridgeWithdrawIntegrationTest is bytes memory predictedPayload = abi.encode(WITHDRAW_SIG, rootToken, address(this), address(this), withdrawAmount); vm.expectCall( - address(axelarGasService), + address(mockAxelarGasService), withdrawFee, abi.encodeWithSelector( - axelarGasService.payNativeGasForContractCall.selector, + mockAxelarGasService.payNativeGasForContractCall.selector, address(axelarAdaptor), childBridge.rootChain(), childBridge.rootERC20BridgeAdaptor(), @@ -117,12 +111,12 @@ contract ChildERC20BridgeWithdrawIntegrationTest is ChildERC20 childToken = ChildERC20(childBridge.rootTokenToChildToken(rootToken)); uint256 preBal = childToken.balanceOf(address(this)); - uint256 preGasBal = address(axelarGasService).balance; + uint256 preGasBal = address(mockAxelarGasService).balance; childBridge.withdraw{value: withdrawFee}(childToken, withdrawAmount); uint256 postBal = childToken.balanceOf(address(this)); - uint256 postGasBal = address(axelarGasService).balance; + uint256 postGasBal = address(mockAxelarGasService).balance; assertEq(postBal, preBal - withdrawAmount, "Balance not reduced"); assertEq(postGasBal, preGasBal + withdrawFee, "Gas not transferred"); diff --git a/test/integration/child/withdrawals/ChildAxelarBridgeWithdrawTo.t.sol b/test/integration/child/withdrawals/ChildAxelarBridgeWithdrawTo.t.sol index 0065e515..69ece184 100644 --- a/test/integration/child/withdrawals/ChildAxelarBridgeWithdrawTo.t.sol +++ b/test/integration/child/withdrawals/ChildAxelarBridgeWithdrawTo.t.sol @@ -1 +1,127 @@ +// SPDX-License-Identifier: Apache 2.0 pragma solidity 0.8.19; + +import {Test} from "forge-std/Test.sol"; +import {MockAxelarGateway} from "../../../../src/test/root/MockAxelarGateway.sol"; +import {MockAxelarGasService} from "../../../../src/test/root/MockAxelarGasService.sol"; +import {ChildERC20Bridge, IChildERC20BridgeEvents} from "../../../../src/child/ChildERC20Bridge.sol"; +import { + ChildAxelarBridgeAdaptor, + IChildAxelarBridgeAdaptorEvents, + IChildAxelarBridgeAdaptorErrors +} from "../../../../src/child/ChildAxelarBridgeAdaptor.sol"; +import {Utils} from "../../../utils.t.sol"; +import {ChildERC20} from "../../../../src/child/ChildERC20.sol"; + +contract ChildERC20BridgeWithdrawToIntegrationTest is + Test, + IChildERC20BridgeEvents, + IChildAxelarBridgeAdaptorEvents, + IChildAxelarBridgeAdaptorErrors, + Utils +{ + uint256 constant withdrawFee = 200; + uint256 constant withdrawAmount = 99999999999; + + ChildERC20Bridge public childBridge; + ChildAxelarBridgeAdaptor public axelarAdaptor; + address public rootToken; + MockAxelarGasService public mockAxelarGasService; + MockAxelarGateway public mockAxelarGateway; + + address receiver = address(0xabcd); + + function setUp() public { + ChildERC20 childTokenTemplate; + (childBridge, axelarAdaptor, rootToken,, childTokenTemplate, mockAxelarGasService, mockAxelarGateway) = + childIntegrationSetup(); + } + + /** + * @dev A future test will assert that the computed childToken is the same as what gets deployed on L2. + * This test uses the same code as the mapToken function does to calculate this address, so we can + * not consider it sufficient. + */ + function test_withdrawTo_CallsBridgeAdaptor() public { + ChildERC20 childToken = ChildERC20(childBridge.rootTokenToChildToken(rootToken)); + + bytes memory predictedPayload = abi.encode(WITHDRAW_SIG, rootToken, address(this), receiver, withdrawAmount); + vm.expectCall( + address(axelarAdaptor), + withdrawFee, + abi.encodeWithSelector(axelarAdaptor.sendMessage.selector, predictedPayload, address(this)) + ); + + childBridge.withdrawTo{value: withdrawFee}(childToken, receiver, withdrawAmount); + } + + function test_withdrawTo_CallsAxelarGateway() public { + ChildERC20 childToken = ChildERC20(childBridge.rootTokenToChildToken(rootToken)); + + bytes memory predictedPayload = abi.encode(WITHDRAW_SIG, rootToken, address(this), receiver, withdrawAmount); + vm.expectCall( + address(mockAxelarGateway), + 0, + abi.encodeWithSelector( + mockAxelarGateway.callContract.selector, + childBridge.rootChain(), + childBridge.rootERC20BridgeAdaptor(), + predictedPayload + ) + ); + + childBridge.withdrawTo{value: withdrawFee}(childToken, receiver, withdrawAmount); + } + + function test_withdrawTo_CallsGasService() public { + ChildERC20 childToken = ChildERC20(childBridge.rootTokenToChildToken(rootToken)); + + bytes memory predictedPayload = abi.encode(WITHDRAW_SIG, rootToken, address(this), receiver, withdrawAmount); + vm.expectCall( + address(mockAxelarGasService), + withdrawFee, + abi.encodeWithSelector( + mockAxelarGasService.payNativeGasForContractCall.selector, + address(axelarAdaptor), + childBridge.rootChain(), + childBridge.rootERC20BridgeAdaptor(), + predictedPayload, + address(this) + ) + ); + + childBridge.withdrawTo{value: withdrawFee}(childToken, receiver, withdrawAmount); + } + + function test_withdrawTo_EmitsAxelarMessageSentEvent() public { + ChildERC20 childToken = ChildERC20(childBridge.rootTokenToChildToken(rootToken)); + + bytes memory predictedPayload = abi.encode(WITHDRAW_SIG, rootToken, address(this), receiver, withdrawAmount); + + vm.expectEmit(address(axelarAdaptor)); + emit AxelarMessageSent(childBridge.rootChain(), childBridge.rootERC20BridgeAdaptor(), predictedPayload); + childBridge.withdrawTo{value: withdrawFee}(childToken, receiver, withdrawAmount); + } + + function test_withdrawTo_BurnsFundsAndTransfersGas() public { + ChildERC20 childToken = ChildERC20(childBridge.rootTokenToChildToken(rootToken)); + + uint256 preBal = childToken.balanceOf(address(this)); + uint256 preGasBal = address(mockAxelarGasService).balance; + + childBridge.withdrawTo{value: withdrawFee}(childToken, receiver, withdrawAmount); + + uint256 postBal = childToken.balanceOf(address(this)); + uint256 postGasBal = address(mockAxelarGasService).balance; + + assertEq(postBal, preBal - withdrawAmount, "Balance not reduced"); + assertEq(postGasBal, preGasBal + withdrawFee, "Gas not transferred"); + } + + function test_RevertIf_WithdrawToCalledWithNoGas() public { + ChildERC20 childToken = ChildERC20(childBridge.rootTokenToChildToken(rootToken)); + + vm.expectRevert(NoGas.selector); + childBridge.withdrawTo(childToken, receiver, withdrawAmount); + } +}