feat: update ICA to v3 (#3314)

### Description

- Update ICA to accept _hookMetadata per call and per batch of calls.
- Allow ICA to accept msg.value

### Drive-by changes

- Moved IGP from MockMailbox to the InterchainAccountRouterTest contract
as it's not needed in the former and doesn't break any tests

### Related issues

- Fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3197

### Backward compatibility

Yes with V3

### Testing

Unit tests with focus on igp as the hook as this will be the one used in
production
pull/3412/head
Kunal Arora 8 months ago committed by GitHub
parent 524297919e
commit f9d67aa0b5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 160
      solidity/contracts/middleware/InterchainAccountRouter.sol
  2. 3
      solidity/contracts/mock/MockHyperlaneEnvironment.sol
  3. 4
      solidity/contracts/test/TestInterchainGasPaymaster.sol
  4. 340
      solidity/test/InterchainAccountRouter.t.sol
  5. 3
      typescript/sdk/src/middleware/account/accounts.hardhat-test.ts

@ -1,12 +1,25 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
/*@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@ HYPERLANE @@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@@
@@@@@@@@@ @@@@@@@@*/
// ============ Internal Imports ============
import {OwnableMulticall} from "./libs/OwnableMulticall.sol";
import {InterchainAccountMessage} from "./libs/InterchainAccountMessage.sol";
import {CallLib} from "./libs/Call.sol";
import {MinimalProxy} from "../libs/MinimalProxy.sol";
import {TypeCasts} from "../libs/TypeCasts.sol";
import {StandardHookMetadata} from "../hooks/libs/StandardHookMetadata.sol";
import {EnumerableMapExtended} from "../libs/EnumerableMapExtended.sol";
import {Router} from "../client/Router.sol";
@ -82,17 +95,17 @@ contract InterchainAccountRouter is Router {
/**
* @notice Initializes the contract with HyperlaneConnectionClient contracts
* @param _interchainGasPaymaster Unused but required by HyperlaneConnectionClient
* @param _customHook used by the Router to set the hook to override with
* @param _interchainSecurityModule The address of the local ISM contract
* @param _owner The address with owner privileges
*/
function initialize(
address _interchainGasPaymaster,
address _customHook,
address _interchainSecurityModule,
address _owner
) external initializer {
_MailboxClient_initialize(
_interchainGasPaymaster,
_customHook,
_interchainSecurityModule,
_owner
);
@ -157,7 +170,7 @@ contract InterchainAccountRouter is Router {
address _to,
uint256 _value,
bytes memory _data
) external returns (bytes32) {
) external payable returns (bytes32) {
bytes32 _router = routers(_destination);
bytes32 _ism = isms[_destination];
bytes memory _body = InterchainAccountMessage.encode(
@ -170,6 +183,44 @@ contract InterchainAccountRouter is Router {
return _dispatchMessage(_destination, _router, _ism, _body);
}
/**
* @notice Dispatches a single remote call to be made by an owner's
* interchain account on the destination domain
* @dev Uses the default router and ISM addresses for the destination
* domain, reverting if none have been configured
* @param _destination The remote domain of the chain to make calls on
* @param _to The address of the contract to call
* @param _value The value to include in the call
* @param _data The calldata
* @param _hookMetadata The hook metadata to override with for the hook set by the owner
* @return The Hyperlane message ID
*/
function callRemote(
uint32 _destination,
address _to,
uint256 _value,
bytes memory _data,
bytes memory _hookMetadata
) external payable returns (bytes32) {
bytes32 _router = routers(_destination);
bytes32 _ism = isms[_destination];
bytes memory _body = InterchainAccountMessage.encode(
msg.sender,
_ism,
_to,
_value,
_data
);
return
_dispatchMessageWithMetadata(
_destination,
_router,
_ism,
_body,
_hookMetadata
);
}
/**
* @notice Dispatches a sequence of remote calls to be made by an owner's
* interchain account on the destination domain
@ -183,10 +234,44 @@ contract InterchainAccountRouter is Router {
function callRemote(
uint32 _destination,
CallLib.Call[] calldata _calls
) external returns (bytes32) {
) external payable returns (bytes32) {
bytes32 _router = routers(_destination);
bytes32 _ism = isms[_destination];
return callRemoteWithOverrides(_destination, _router, _ism, _calls);
bytes memory _body = InterchainAccountMessage.encode(
msg.sender,
_ism,
_calls
);
return _dispatchMessage(_destination, _router, _ism, _body);
}
/**
* @notice Dispatches a sequence of remote calls to be made by an owner's
* interchain account on the destination domain
* @dev Uses the default router and ISM addresses for the destination
* domain, reverting if none have been configured
* @dev Recommend using CallLib.build to format the interchain calls.
* @param _destination The remote domain of the chain to make calls on
* @param _calls The sequence of calls to make
* @param _hookMetadata The hook metadata to override with for the hook set by the owner
* @return The Hyperlane message ID
*/
function callRemote(
uint32 _destination,
CallLib.Call[] calldata _calls,
bytes calldata _hookMetadata
) external payable returns (bytes32) {
bytes32 _router = routers(_destination);
bytes32 _ism = isms[_destination];
return
callRemoteWithOverrides(
_destination,
_router,
_ism,
_calls,
_hookMetadata
);
}
/**
@ -395,7 +480,7 @@ contract InterchainAccountRouter is Router {
bytes32 _router,
bytes32 _ism,
CallLib.Call[] calldata _calls
) public returns (bytes32) {
) public payable returns (bytes32) {
bytes memory _body = InterchainAccountMessage.encode(
msg.sender,
_ism,
@ -404,6 +489,39 @@ contract InterchainAccountRouter is Router {
return _dispatchMessage(_destination, _router, _ism, _body);
}
/**
* @notice Dispatches a sequence of remote calls to be made by an owner's
* interchain account on the destination domain
* @dev Recommend using CallLib.build to format the interchain calls
* @param _destination The remote domain of the chain to make calls on
* @param _router The remote router address
* @param _ism The remote ISM address
* @param _calls The sequence of calls to make
* @param _hookMetadata The hook metadata to override with for the hook set by the owner
* @return The Hyperlane message ID
*/
function callRemoteWithOverrides(
uint32 _destination,
bytes32 _router,
bytes32 _ism,
CallLib.Call[] calldata _calls,
bytes memory _hookMetadata
) public payable returns (bytes32) {
bytes memory _body = InterchainAccountMessage.encode(
msg.sender,
_ism,
_calls
);
return
_dispatchMessageWithMetadata(
_destination,
_router,
_ism,
_body,
_hookMetadata
);
}
// ============ Internal Functions ============
/**
@ -474,7 +592,33 @@ contract InterchainAccountRouter is Router {
) private returns (bytes32) {
require(_router != bytes32(0), "no router specified for destination");
emit RemoteCallDispatched(_destination, msg.sender, _router, _ism);
return mailbox.dispatch(_destination, _router, _body);
return mailbox.dispatch{value: msg.value}(_destination, _router, _body);
}
/**
* @notice Dispatches an InterchainAccountMessage to the remote router with hook metadata
* @param _destination The remote domain
* @param _router The address of the remote InterchainAccountRouter
* @param _ism The address of the remote ISM
* @param _body The InterchainAccountMessage body
* @param _hookMetadata The hook metadata to override with for the hook set by the owner
*/
function _dispatchMessageWithMetadata(
uint32 _destination,
bytes32 _router,
bytes32 _ism,
bytes memory _body,
bytes memory _hookMetadata
) private returns (bytes32) {
require(_router != bytes32(0), "no router specified for destination");
emit RemoteCallDispatched(_destination, msg.sender, _router, _ism);
return
mailbox.dispatch{value: msg.value}(
_destination,
_router,
_body,
_hookMetadata
);
}
/**

@ -31,9 +31,6 @@ contract MockHyperlaneEnvironment {
originMailbox.setDefaultIsm(address(isms[originDomain]));
destinationMailbox.setDefaultIsm(address(isms[destinationDomain]));
igps[originDomain] = new TestInterchainGasPaymaster();
igps[destinationDomain] = new TestInterchainGasPaymaster();
originMailbox.transferOwnership(msg.sender);
destinationMailbox.transferOwnership(msg.sender);

@ -17,4 +17,8 @@ contract TestInterchainGasPaymaster is InterchainGasPaymaster {
) public pure override returns (uint256) {
return gasPrice * gasAmount;
}
function getDefaultGasUsage() public pure returns (uint256) {
return DEFAULT_GAS_USAGE;
}
}

@ -1,13 +1,16 @@
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.13;
import {Test} from "forge-std/Test.sol";
import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import "forge-std/Test.sol";
import "../contracts/mock/MockMailbox.sol";
import "../contracts/mock/MockHyperlaneEnvironment.sol";
import {StandardHookMetadata} from "../contracts/hooks/libs/StandardHookMetadata.sol";
import {MockMailbox} from "../contracts/mock/MockMailbox.sol";
import {MockHyperlaneEnvironment} from "../contracts/mock/MockHyperlaneEnvironment.sol";
import {TypeCasts} from "../contracts/libs/TypeCasts.sol";
import {IInterchainSecurityModule} from "../contracts/interfaces/IInterchainSecurityModule.sol";
import {IInterchainGasPaymaster} from "../contracts/interfaces/IInterchainGasPaymaster.sol";
import {TestInterchainGasPaymaster} from "../contracts/test/TestInterchainGasPaymaster.sol";
import {IPostDispatchHook} from "../contracts/interfaces/hooks/IPostDispatchHook.sol";
import {CallLib, OwnableMulticall, InterchainAccountRouter} from "../contracts/middleware/InterchainAccountRouter.sol";
import {InterchainAccountIsm} from "../contracts/isms/routing/InterchainAccountIsm.sol";
@ -45,29 +48,26 @@ contract InterchainAccountRouterTest is Test {
address account
);
struct Bytes32Pair {
bytes32 a;
bytes32 b;
}
MockHyperlaneEnvironment environment;
MockHyperlaneEnvironment internal environment;
uint32 origin = 1;
uint32 destination = 2;
uint32 internal origin = 1;
uint32 internal destination = 2;
InterchainAccountIsm icaIsm;
InterchainAccountRouter originRouter;
InterchainAccountRouter destinationRouter;
bytes32 ismOverride;
bytes32 routerOverride;
TestInterchainGasPaymaster internal igp;
InterchainAccountIsm internal icaIsm;
InterchainAccountRouter internal originRouter;
InterchainAccountRouter internal destinationRouter;
bytes32 internal ismOverride;
bytes32 internal routerOverride;
uint256 gasPaymentQuote;
OwnableMulticall ica;
OwnableMulticall internal ica;
Callable target;
Callable internal target;
function deployProxiedIcaRouter(
MockMailbox _mailbox,
IInterchainGasPaymaster _igp,
IPostDispatchHook _customHook,
IInterchainSecurityModule _ism,
address _owner
) public returns (InterchainAccountRouter) {
@ -80,7 +80,7 @@ contract InterchainAccountRouterTest is Test {
address(1), // no proxy owner necessary for testing
abi.encodeWithSelector(
InterchainAccountRouter.initialize.selector,
address(_igp),
address(_customHook),
address(_ism),
_owner
)
@ -92,6 +92,12 @@ contract InterchainAccountRouterTest is Test {
function setUp() public {
environment = new MockHyperlaneEnvironment(origin, destination);
igp = new TestInterchainGasPaymaster();
gasPaymentQuote = igp.quoteGasPayment(
destination,
igp.getDefaultGasUsage()
);
icaIsm = new InterchainAccountIsm(
address(environment.mailboxes(destination))
);
@ -110,6 +116,8 @@ contract InterchainAccountRouterTest is Test {
owner
);
environment.mailboxes(origin).setDefaultHook(address(igp));
routerOverride = TypeCasts.addressToBytes32(address(destinationRouter));
ismOverride = TypeCasts.addressToBytes32(
address(environment.isms(destination))
@ -124,80 +132,102 @@ contract InterchainAccountRouterTest is Test {
target = new Callable();
}
function testConstructor() public {
// The deployed ICA should be owned by the router
destinationRouter.getDeployedInterchainAccount(
origin,
address(this),
address(originRouter),
address(environment.isms(destination))
);
assertEq(ica.owner(), address(destinationRouter));
function testFuzz_constructor(address _localOwner) public {
OwnableMulticall _account = destinationRouter
.getDeployedInterchainAccount(
origin,
_localOwner,
address(originRouter),
address(environment.isms(destination))
);
assertEq(_account.owner(), address(destinationRouter));
}
function testGetRemoteInterchainAccount() public {
assertEq(
originRouter.getRemoteInterchainAccount(
address(this),
address(destinationRouter),
address(environment.isms(destination))
),
address(ica)
function testFuzz_getRemoteInterchainAccount(
address _localOwner,
address _ism
) public {
address _account = originRouter.getRemoteInterchainAccount(
address(_localOwner),
address(destinationRouter),
_ism
);
originRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
TypeCasts.addressToBytes32(_ism)
);
assertEq(
originRouter.getRemoteInterchainAccount(destination, address(this)),
address(ica)
originRouter.getRemoteInterchainAccount(
destination,
address(_localOwner)
),
_account
);
}
function testEnrollRemoteRouters(
function testFuzz_enrollRemoteRouters(
uint8 count,
uint32 domain,
bytes32 router
) public {
vm.assume(count > 0 && count < uint256(router) && count < domain);
// arrange
// count - # of domains and routers
uint32[] memory domains = new uint32[](count);
bytes32[] memory routers = new bytes32[](count);
for (uint256 i = 0; i < count; i++) {
domains[i] = domain - uint32(i);
routers[i] = bytes32(uint256(router) - i);
}
// act
originRouter.enrollRemoteRouters(domains, routers);
// assert
uint32[] memory actualDomains = originRouter.domains();
assertEq(actualDomains.length, domains.length);
assertEq(abi.encode(originRouter.domains()), abi.encode(domains));
for (uint256 i = 0; i < count; i++) {
bytes32 actualRouter = originRouter.routers(domains[i]);
bytes32 actualIsm = originRouter.isms(domains[i]);
assertEq(actualRouter, routers[i]);
assertEq(actualIsm, bytes32(0));
assertEq(actualDomains[i], domains[i]);
}
assertEq(abi.encode(originRouter.domains()), abi.encode(domains));
}
function testEnrollRemoteRouterAndIsm(bytes32 router, bytes32 ism) public {
function testFuzz_enrollRemoteRouterAndIsm(
bytes32 router,
bytes32 ism
) public {
vm.assume(router != bytes32(0));
// arrange pre-condition
bytes32 actualRouter = originRouter.routers(destination);
bytes32 actualIsm = originRouter.isms(destination);
assertEq(actualRouter, bytes32(0));
assertEq(actualIsm, bytes32(0));
// act
originRouter.enrollRemoteRouterAndIsm(destination, router, ism);
// assert
actualRouter = originRouter.routers(destination);
actualIsm = originRouter.isms(destination);
assertEq(actualRouter, router);
assertEq(actualIsm, ism);
}
function testEnrollRemoteRouterAndIsms(
function testFuzz_enrollRemoteRouterAndIsms(
uint32[] calldata destinations,
bytes32[] calldata routers,
bytes32[] calldata isms
) public {
// check reverts
if (
destinations.length != routers.length ||
destinations.length != isms.length
@ -207,7 +237,10 @@ contract InterchainAccountRouterTest is Test {
return;
}
// act
originRouter.enrollRemoteRouterAndIsms(destinations, routers, isms);
// assert
for (uint256 i = 0; i < destinations.length; i++) {
bytes32 actualRouter = originRouter.routers(destinations[i]);
bytes32 actualIsm = originRouter.isms(destinations[i]);
@ -216,27 +249,35 @@ contract InterchainAccountRouterTest is Test {
}
}
function testEnrollRemoteRouterAndIsmImmutable(
function testFuzz_enrollRemoteRouterAndIsmImmutable(
bytes32 routerA,
bytes32 ismA,
bytes32 routerB,
bytes32 ismB
) public {
vm.assume(routerA != bytes32(0) && routerB != bytes32(0));
// act
originRouter.enrollRemoteRouterAndIsm(destination, routerA, ismA);
// assert
vm.expectRevert(
bytes("router and ISM defaults are immutable once set")
);
originRouter.enrollRemoteRouterAndIsm(destination, routerB, ismB);
}
function testEnrollRemoteRouterAndIsmNonOwner(
function testFuzz_enrollRemoteRouterAndIsmNonOwner(
address newOwner,
bytes32 router,
bytes32 ism
) public {
vm.assume(newOwner != address(0) && newOwner != originRouter.owner());
// act
originRouter.transferOwnership(newOwner);
// assert
vm.expectRevert(bytes("Ownable: caller is not the owner"));
originRouter.enrollRemoteRouterAndIsm(destination, router, ism);
}
@ -245,6 +286,7 @@ contract InterchainAccountRouterTest is Test {
bytes32 data
) private view returns (CallLib.Call[] memory) {
vm.assume(data != bytes32(0));
CallLib.Call memory call = CallLib.Call(
TypeCasts.addressToBytes32(address(target)),
0,
@ -268,89 +310,240 @@ contract InterchainAccountRouterTest is Test {
assertEq(target.data(address(ica)), data);
}
function testSingleCallRemoteWithDefault(bytes32 data) public {
function assertIgpPayment(
uint256 balanceBefore,
uint256 balanceAfter,
uint256 gasLimit
) private {
uint256 expectedGasPayment = gasLimit * igp.gasPrice();
assertEq(balanceBefore - balanceAfter, expectedGasPayment);
assertEq(address(igp).balance, expectedGasPayment);
}
function testFuzz_singleCallRemoteWithDefault(bytes32 data) public {
// arrange
originRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
uint256 balanceBefore = address(this).balance;
// act
CallLib.Call[] memory calls = getCalls(data);
originRouter.callRemote(
originRouter.callRemote{value: gasPaymentQuote}(
destination,
TypeCasts.bytes32ToAddress(calls[0].to),
calls[0].value,
calls[0].data
);
// assert
uint256 balanceAfter = address(this).balance;
assertRemoteCallReceived(data);
assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage());
}
function testCallRemoteWithDefault(bytes32 data) public {
function testFuzz_callRemoteWithDefault(bytes32 data) public {
// arrange
originRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
originRouter.callRemote(destination, getCalls(data));
uint256 balanceBefore = address(this).balance;
// act
originRouter.callRemote{value: gasPaymentQuote}(
destination,
getCalls(data)
);
// assert
uint256 balanceAfter = address(this).balance;
assertRemoteCallReceived(data);
assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage());
}
function testOverrideAndCallRemote(bytes32 data) public {
function testFuzz_overrideAndCallRemote(bytes32 data) public {
// arrange
originRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
originRouter.callRemote(destination, getCalls(data));
uint256 balanceBefore = address(this).balance;
// act
originRouter.callRemote{value: gasPaymentQuote}(
destination,
getCalls(data)
);
// assert
uint256 balanceAfter = address(this).balance;
assertRemoteCallReceived(data);
assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage());
}
function testCallRemoteWithoutDefaults(bytes32 data) public {
function testFuzz_callRemoteWithoutDefaults_revert_noRouter(
bytes32 data
) public {
// assert error
CallLib.Call[] memory calls = getCalls(data);
vm.expectRevert(bytes("no router specified for destination"));
originRouter.callRemote(destination, calls);
}
function testCallRemoteWithOverrides(bytes32 data) public {
originRouter.callRemoteWithOverrides(
function testFuzz_customMetadata_forIgp(
uint64 gasLimit,
uint64 overpayment,
bytes32 data
) public {
// arrange
bytes memory metadata = StandardHookMetadata.formatMetadata(
0,
gasLimit,
address(this),
""
);
originRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
uint256 balanceBefore = address(this).balance;
// act
originRouter.callRemote{value: gasLimit * igp.gasPrice() + overpayment}(
destination,
getCalls(data),
metadata
);
// assert
uint256 balanceAfter = address(this).balance;
assertRemoteCallReceived(data);
assertIgpPayment(balanceBefore, balanceAfter, gasLimit);
}
function testFuzz_customMetadata_reverts_underpayment(
uint64 gasLimit,
uint64 payment,
bytes32 data
) public {
vm.assume(payment < gasLimit * igp.gasPrice());
// arrange
bytes memory metadata = StandardHookMetadata.formatMetadata(
0,
gasLimit,
address(this),
""
);
originRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
// act
vm.expectRevert("IGP: insufficient interchain gas payment");
originRouter.callRemote{value: payment}(
destination,
getCalls(data),
metadata
);
}
function testFuzz_callRemoteWithOverrides_default(bytes32 data) public {
// arrange
uint256 balanceBefore = address(this).balance;
// act
originRouter.callRemoteWithOverrides{value: gasPaymentQuote}(
destination,
routerOverride,
ismOverride,
getCalls(data)
);
// assert
uint256 balanceAfter = address(this).balance;
assertRemoteCallReceived(data);
assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage());
}
function testFuzz_callRemoteWithOverrides_metadata(
uint64 gasLimit,
bytes32 data
) public {
// arrange
bytes memory metadata = StandardHookMetadata.formatMetadata(
0,
gasLimit,
address(this),
""
);
uint256 balanceBefore = address(this).balance;
// act
originRouter.callRemoteWithOverrides{value: gasLimit * igp.gasPrice()}(
destination,
routerOverride,
ismOverride,
getCalls(data),
metadata
);
// assert
uint256 balanceAfter = address(this).balance;
assertRemoteCallReceived(data);
assertIgpPayment(balanceBefore, balanceAfter, gasLimit);
}
function testCallRemoteWithFailingIsmOverride(bytes32 data) public {
function testFuzz_callRemoteWithFailingIsmOverride(bytes32 data) public {
// arrange
string memory failureMessage = "failing ism";
bytes32 failingIsm = TypeCasts.addressToBytes32(
address(new FailingIsm(failureMessage))
);
originRouter.callRemoteWithOverrides(
// act
originRouter.callRemoteWithOverrides{value: gasPaymentQuote}(
destination,
routerOverride,
failingIsm,
getCalls(data)
getCalls(data),
""
);
// assert
vm.expectRevert(bytes(failureMessage));
environment.processNextPendingMessage();
}
function testCallRemoteWithFailingDefaultIsm(bytes32 data) public {
function testFuzz_callRemoteWithFailingDefaultIsm(bytes32 data) public {
// arrange
string memory failureMessage = "failing ism";
FailingIsm failingIsm = new FailingIsm(failureMessage);
// act
environment.mailboxes(destination).setDefaultIsm(address(failingIsm));
originRouter.callRemoteWithOverrides(
originRouter.callRemoteWithOverrides{value: gasPaymentQuote}(
destination,
routerOverride,
bytes32(0),
getCalls(data)
getCalls(data),
""
);
// assert
vm.expectRevert(bytes(failureMessage));
environment.processNextPendingMessage();
}
function testGetLocalInterchainAccount(bytes32 data) public {
function testFuzz_getLocalInterchainAccount(bytes32 data) public {
// check
OwnableMulticall destinationIca = destinationRouter
.getLocalInterchainAccount(
origin,
@ -369,21 +562,23 @@ contract InterchainAccountRouterTest is Test {
)
)
);
assertEq(address(destinationIca).code.length, 0);
originRouter.callRemoteWithOverrides(
// act
originRouter.callRemoteWithOverrides{value: gasPaymentQuote}(
destination,
routerOverride,
ismOverride,
getCalls(data)
getCalls(data),
""
);
assertRemoteCallReceived(data);
// recheck
assertRemoteCallReceived(data);
assert(address(destinationIca).code.length != 0);
}
function testReceiveValue(uint256 value) public {
function testFuzz_receiveValue(uint256 value) public {
vm.assume(value > 1 && value <= address(this).balance);
// receive value before deployed
assert(address(ica).code.length == 0);
@ -408,7 +603,7 @@ contract InterchainAccountRouterTest is Test {
assertEq(value, msg.value);
}
function testSendValue(uint256 value) public {
function testFuzz_sendValue(uint256 value) public {
vm.assume(value > 0 && value <= address(this).balance);
payable(address(ica)).transfer(value);
@ -417,13 +612,16 @@ contract InterchainAccountRouterTest is Test {
CallLib.Call[] memory calls = new CallLib.Call[](1);
calls[0] = call;
originRouter.callRemoteWithOverrides(
originRouter.callRemoteWithOverrides{value: gasPaymentQuote}(
destination,
routerOverride,
ismOverride,
calls
calls,
""
);
vm.expectCall(address(this), value, data);
environment.processNextPendingMessage();
}
receive() external payable {}
}

@ -78,11 +78,12 @@ describe.skip('InterchainAccounts', async () => {
ethers.constants.AddressZero,
);
await local['callRemote(uint32,address,uint256,bytes)'](
await local['callRemote(uint32,address,uint256,bytes,bytes)'](
multiProvider.getDomainId(remoteChain),
recipient.address,
0,
data,
'',
);
await coreApp.processMessages();
expect(await recipient.lastCallMessage()).to.eql(fooMessage);

Loading…
Cancel
Save