The home for Hyperlane core contracts, sdk packages, and other infrastructure
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hyperlane-monorepo/solidity/test/InterchainAccountRouter.t.sol

197 lines
6.5 KiB

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import "forge-std/Test.sol";
import "../contracts/mock/MockMailbox.sol";
import "../contracts/HyperlaneConnectionClient.sol";
import "../contracts/mock/MockHyperlaneEnvironment.sol";
import {TypeCasts} from "../contracts/libs/TypeCasts.sol";
import "../contracts/test/TestRecipient.sol";
import "../contracts/middleware/InterchainAccountRouter.sol";
import {TestHyperlaneConnectionClient} from "../contracts/test/TestHyperlaneConnectionClient.sol";
import {CallLib} from "../contracts/libs/Call.sol";
import {ERC1967Proxy} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol";
contract InterchainAccountRouterTest is Test {
using TypeCasts for address;
event InterchainAccountCreated(
uint32 indexed origin,
bytes32 sender,
address account
);
MockHyperlaneEnvironment environment;
uint32 originDomain = 1;
uint32 remoteDomain = 2;
InterchainAccountRouter originRouter;
InterchainAccountRouter remoteRouter;
TestRecipient recipient;
address payable ica;
TestHyperlaneConnectionClient ownable;
function setUp() public {
environment = new MockHyperlaneEnvironment(originDomain, remoteDomain);
recipient = new TestRecipient();
originRouter = new InterchainAccountRouter(address(0));
remoteRouter = new InterchainAccountRouter(address(0));
address owner = address(this);
originRouter.initialize(
address(environment.mailboxes(originDomain)),
address(environment.igps(originDomain)),
address(environment.isms(originDomain)),
owner
);
remoteRouter.initialize(
address(environment.mailboxes(remoteDomain)),
address(environment.igps(remoteDomain)),
address(environment.isms(remoteDomain)),
owner
);
originRouter.enrollRemoteRouter(
remoteDomain,
TypeCasts.addressToBytes32(address(remoteRouter))
);
remoteRouter.enrollRemoteRouter(
originDomain,
TypeCasts.addressToBytes32(address(originRouter))
);
ica = remoteRouter.getInterchainAccount(originDomain, address(this));
ownable = new TestHyperlaneConnectionClient();
}
function testConstructor() public {
address caller = address(this);
// nonzero caller
InterchainAccountRouter router = new InterchainAccountRouter(caller);
OwnableMulticall ica = router.getDeployedInterchainAccount(
originDomain,
address(this)
);
assertEq(ica.owner(), caller);
// zero caller
router = new InterchainAccountRouter(address(0));
ica = router.getDeployedInterchainAccount(originDomain, address(this));
assertEq(ica.owner(), address(router));
}
function dispatchTransferOwner(address newOwner) public {
vm.assume(newOwner != address(0x0));
CallLib.Call memory call = CallLib.build(
address(ownable),
0,
abi.encodeCall(ownable.transferOwnership, (newOwner))
);
CallLib.Call[] memory calls = new CallLib.Call[](1);
calls[0] = call;
originRouter.dispatch(remoteDomain, calls);
}
function testCannotSetOwner(address newOwner) public {
dispatchTransferOwner(newOwner);
vm.expectRevert(bytes("Ownable: caller is not the owner"));
environment.processNextPendingMessage();
}
function testSetOwner(address newOwner) public {
ownable.transferOwnership(ica);
dispatchTransferOwner(newOwner);
vm.expectEmit(true, false, false, true, address(remoteRouter));
emit InterchainAccountCreated(
originDomain,
address(this).addressToBytes32(),
ica
);
environment.processNextPendingMessage();
assertEq(ownable.owner(), newOwner);
}
function testCannotSetOwnerTwice(address newOwner) public {
vm.assume(newOwner != address(0x0) && newOwner != ica);
ownable.transferOwnership(ica);
dispatchTransferOwner(newOwner);
environment.processNextPendingMessage();
dispatchTransferOwner(address(this));
vm.expectRevert(bytes("Ownable: caller is not the owner"));
environment.processNextPendingMessage();
}
function testOwner() public {
OwnableMulticall remoteIca = remoteRouter.getDeployedInterchainAccount(
originDomain,
address(this)
);
assertEq(remoteIca.owner(), address(remoteRouter));
OwnableMulticall localIca = originRouter.getDeployedInterchainAccount(
remoteDomain,
address(this)
);
assertEq(localIca.owner(), address(originRouter));
}
function testBytes32Owner() public {
OwnableMulticall remoteIca = remoteRouter.getDeployedInterchainAccount(
originDomain,
address(this).addressToBytes32()
);
assertEq(remoteIca.owner(), address(remoteRouter));
OwnableMulticall localIca = originRouter.getDeployedInterchainAccount(
remoteDomain,
address(this).addressToBytes32()
);
assertEq(localIca.owner(), address(originRouter));
}
function testReceiveValue(uint256 value) public {
vm.assume(value > 1 && value <= address(this).balance);
// receive value before deployed
assert(ica.code.length == 0);
bool success;
(success, ) = ica.call{value: value / 2}("");
require(success, "transfer before deploy failed");
// receive value after deployed
remoteRouter.getDeployedInterchainAccount(originDomain, address(this));
assert(ica.code.length > 0);
(success, ) = ica.call{value: value / 2}("");
require(success, "transfer after deploy failed");
}
function receiveValue(uint256 value) external payable {
assertEq(value, msg.value);
}
function testSendValue(uint256 value) public {
vm.assume(value > 0 && value <= address(this).balance);
ica.transfer(value);
bytes memory data = abi.encodeCall(this.receiveValue, (value));
CallLib.Call memory call = CallLib.build(address(this), value, data);
CallLib.Call[] memory calls = new CallLib.Call[](1);
calls[0] = call;
originRouter.dispatch(remoteDomain, calls);
vm.expectCall(address(this), value, data);
environment.processNextPendingMessage();
}
}