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

687 lines
21 KiB

// 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 {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 {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";
contract Callable {
mapping(address => bytes32) public data;
function set(bytes32 _data) external {
data[msg.sender] = _data;
}
}
contract FailingIsm is IInterchainSecurityModule {
string public failureMessage;
uint8 public moduleType;
constructor(string memory _failureMessage) {
failureMessage = _failureMessage;
}
function verify(
bytes calldata,
bytes calldata
) external view returns (bool) {
revert(failureMessage);
}
}
contract InterchainAccountRouterTestBase is Test {
using TypeCasts for address;
event InterchainAccountCreated(
uint32 indexed origin,
bytes32 indexed owner,
address ism,
address account
);
MockHyperlaneEnvironment internal environment;
uint32 internal origin = 1;
uint32 internal destination = 2;
TestInterchainGasPaymaster internal igp;
InterchainAccountIsm internal icaIsm;
InterchainAccountRouter internal originIcaRouter;
InterchainAccountRouter internal destinationIcaRouter;
bytes32 internal ismOverride;
bytes32 internal routerOverride;
uint256 gasPaymentQuote;
feat:ICA support in SDK (#3483) ### Description - Adding 'deployAccount` and `getCallRemote` helpers in InterchainAccount.ts for ease of use for the governor - Adding resolveAccountOwner/resolveInterchainAccountAsOwner wherever it's ambiguous if the owner is address (status quo) or AccountConfig (from this PR) - Added icaApp to HyperlaneDeployer for easier access to ICA ownership for any deployer - resolveAccountOwner vs resolveInterchainAccountAsOwner: the former is usable outside the deployers like the checker, etc whereas the latter is where we're already using the deployer and have the required localRouter already cached - includes the formatting change for the account verification file - owner needs casting in infra/config because of the type Address | AccountConfig type (cannot use resolveAccountOwner here which requires multiprovider) - Add quoteGasPayment with gas limit override to the ICA router contract ### Drive-by changes - HyperlaneDeployer.cacheAddressesMap appends addresses two keys deep instead of overridding the whole cache - callData in utils now supports value to be compatible with CallData[] in the ICA Router contract and generally for V3 which allows msg.value on dispatch - `appFromAddressesMapHelper` doesn't intersect multiprovider with contractMap chains (this causes issues if the contracts map doesn't include chains we may want to read something like domainId from like ismfactory or ICADeployer) - HyperlaneHookDeployer is now using the owner as owner vs it was earlier using the deployer earlier for unknown reason - ### Related issues - fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3505 ### Backward compatibility No (call value) ### Testing Unit testing/e2e testing through governor --------- Signed-off-by: Paul Balaji <paul@hyperlane.xyz> Co-authored-by: Paul Balaji <paul@hyperlane.xyz> Co-authored-by: J M Rossy <jm.rossy@gmail.com> Co-authored-by: Trevor Porter <trkporter@ucdavis.edu> Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com>
8 months ago
uint256 internal constant GAS_LIMIT_OVERRIDE = 60000;
OwnableMulticall internal ica;
Callable internal target;
function deployProxiedIcaRouter(
MockMailbox _mailbox,
IPostDispatchHook _customHook,
IInterchainSecurityModule _ism,
address _owner
) public returns (InterchainAccountRouter) {
InterchainAccountRouter implementation = new InterchainAccountRouter(
address(_mailbox)
);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
address(1), // no proxy owner necessary for testing
abi.encodeWithSelector(
InterchainAccountRouter.initialize.selector,
address(_customHook),
address(_ism),
_owner
)
);
return InterchainAccountRouter(address(proxy));
}
function setUp() public virtual {
environment = new MockHyperlaneEnvironment(origin, destination);
igp = new TestInterchainGasPaymaster();
gasPaymentQuote = igp.quoteGasPayment(
destination,
igp.getDefaultGasUsage()
);
icaIsm = new InterchainAccountIsm(
address(environment.mailboxes(destination))
);
address owner = address(this);
originIcaRouter = deployProxiedIcaRouter(
environment.mailboxes(origin),
environment.igps(destination),
icaIsm,
owner
);
destinationIcaRouter = deployProxiedIcaRouter(
environment.mailboxes(destination),
environment.igps(destination),
icaIsm,
owner
);
environment.mailboxes(origin).setDefaultHook(address(igp));
routerOverride = TypeCasts.addressToBytes32(
address(destinationIcaRouter)
);
ismOverride = TypeCasts.addressToBytes32(
address(environment.isms(destination))
);
ica = destinationIcaRouter.getLocalInterchainAccount(
origin,
address(this),
address(originIcaRouter),
address(environment.isms(destination))
);
target = new Callable();
}
receive() external payable {}
}
contract InterchainAccountRouterTest is InterchainAccountRouterTestBase {
using TypeCasts for address;
function testFuzz_constructor(address _localOwner) public {
OwnableMulticall _account = destinationIcaRouter
.getDeployedInterchainAccount(
origin,
_localOwner,
address(originIcaRouter),
address(environment.isms(destination))
);
assertEq(_account.owner(), address(destinationIcaRouter));
}
function testFuzz_getRemoteInterchainAccount(
address _localOwner,
address _ism
) public {
address _account = originIcaRouter.getRemoteInterchainAccount(
address(_localOwner),
address(destinationIcaRouter),
_ism
);
originIcaRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
TypeCasts.addressToBytes32(_ism)
);
assertEq(
originIcaRouter.getRemoteInterchainAccount(
destination,
address(_localOwner)
),
_account
);
}
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
originIcaRouter.enrollRemoteRouters(domains, routers);
// assert
uint32[] memory actualDomains = originIcaRouter.domains();
assertEq(actualDomains.length, domains.length);
assertEq(abi.encode(originIcaRouter.domains()), abi.encode(domains));
for (uint256 i = 0; i < count; i++) {
bytes32 actualRouter = originIcaRouter.routers(domains[i]);
bytes32 actualIsm = originIcaRouter.isms(domains[i]);
assertEq(actualRouter, routers[i]);
assertEq(actualIsm, bytes32(0));
assertEq(actualDomains[i], domains[i]);
}
}
function testFuzz_enrollRemoteRouterAndIsm(
bytes32 router,
bytes32 ism
) public {
vm.assume(router != bytes32(0));
// arrange pre-condition
bytes32 actualRouter = originIcaRouter.routers(destination);
bytes32 actualIsm = originIcaRouter.isms(destination);
assertEq(actualRouter, bytes32(0));
assertEq(actualIsm, bytes32(0));
// act
originIcaRouter.enrollRemoteRouterAndIsm(destination, router, ism);
// assert
actualRouter = originIcaRouter.routers(destination);
actualIsm = originIcaRouter.isms(destination);
assertEq(actualRouter, router);
assertEq(actualIsm, ism);
}
function testFuzz_enrollRemoteRouterAndIsms(
uint32[] calldata destinations,
bytes32[] calldata routers,
bytes32[] calldata isms
) public {
// check reverts
if (
destinations.length != routers.length ||
destinations.length != isms.length
) {
vm.expectRevert(bytes("length mismatch"));
originIcaRouter.enrollRemoteRouterAndIsms(
destinations,
routers,
isms
);
return;
}
// act
originIcaRouter.enrollRemoteRouterAndIsms(destinations, routers, isms);
// assert
for (uint256 i = 0; i < destinations.length; i++) {
bytes32 actualRouter = originIcaRouter.routers(destinations[i]);
bytes32 actualIsm = originIcaRouter.isms(destinations[i]);
assertEq(actualRouter, routers[i]);
assertEq(actualIsm, isms[i]);
}
}
function testFuzz_enrollRemoteRouterAndIsmImmutable(
bytes32 routerA,
bytes32 ismA,
bytes32 routerB,
bytes32 ismB
) public {
vm.assume(routerA != bytes32(0) && routerB != bytes32(0));
// act
originIcaRouter.enrollRemoteRouterAndIsm(destination, routerA, ismA);
// assert
vm.expectRevert(
bytes("router and ISM defaults are immutable once set")
);
originIcaRouter.enrollRemoteRouterAndIsm(destination, routerB, ismB);
}
function testFuzz_enrollRemoteRouterAndIsmNonOwner(
address newOwner,
bytes32 router,
bytes32 ism
) public {
vm.assume(
newOwner != address(0) && newOwner != originIcaRouter.owner()
);
// act
originIcaRouter.transferOwnership(newOwner);
// assert
vm.expectRevert(bytes("Ownable: caller is not the owner"));
originIcaRouter.enrollRemoteRouterAndIsm(destination, router, ism);
}
function getCalls(
bytes32 data
) private view returns (CallLib.Call[] memory) {
vm.assume(data != bytes32(0));
CallLib.Call memory call = CallLib.Call(
TypeCasts.addressToBytes32(address(target)),
0,
abi.encodeCall(target.set, (data))
);
CallLib.Call[] memory calls = new CallLib.Call[](1);
calls[0] = call;
return calls;
}
function assertRemoteCallReceived(bytes32 data) private {
assertEq(target.data(address(this)), bytes32(0));
vm.expectEmit(true, true, false, true, address(destinationIcaRouter));
emit InterchainAccountCreated(
origin,
address(this).addressToBytes32(),
TypeCasts.bytes32ToAddress(ismOverride),
address(ica)
);
environment.processNextPendingMessage();
assertEq(target.data(address(ica)), data);
}
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_getDeployedInterchainAccount_checkAccountOwners(
address owner
) public {
// act
ica = destinationIcaRouter.getDeployedInterchainAccount(
origin,
owner,
address(originIcaRouter),
address(environment.isms(destination))
);
(uint32 domain, bytes32 ownerBytes) = destinationIcaRouter
.accountOwners(address(ica));
// assert
assertEq(domain, origin);
assertEq(ownerBytes, owner.addressToBytes32());
}
function test_quoteGasPayment() public {
// arrange
originIcaRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
// assert
assertEq(originIcaRouter.quoteGasPayment(destination), gasPaymentQuote);
}
feat:ICA support in SDK (#3483) ### Description - Adding 'deployAccount` and `getCallRemote` helpers in InterchainAccount.ts for ease of use for the governor - Adding resolveAccountOwner/resolveInterchainAccountAsOwner wherever it's ambiguous if the owner is address (status quo) or AccountConfig (from this PR) - Added icaApp to HyperlaneDeployer for easier access to ICA ownership for any deployer - resolveAccountOwner vs resolveInterchainAccountAsOwner: the former is usable outside the deployers like the checker, etc whereas the latter is where we're already using the deployer and have the required localRouter already cached - includes the formatting change for the account verification file - owner needs casting in infra/config because of the type Address | AccountConfig type (cannot use resolveAccountOwner here which requires multiprovider) - Add quoteGasPayment with gas limit override to the ICA router contract ### Drive-by changes - HyperlaneDeployer.cacheAddressesMap appends addresses two keys deep instead of overridding the whole cache - callData in utils now supports value to be compatible with CallData[] in the ICA Router contract and generally for V3 which allows msg.value on dispatch - `appFromAddressesMapHelper` doesn't intersect multiprovider with contractMap chains (this causes issues if the contracts map doesn't include chains we may want to read something like domainId from like ismfactory or ICADeployer) - HyperlaneHookDeployer is now using the owner as owner vs it was earlier using the deployer earlier for unknown reason - ### Related issues - fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3505 ### Backward compatibility No (call value) ### Testing Unit testing/e2e testing through governor --------- Signed-off-by: Paul Balaji <paul@hyperlane.xyz> Co-authored-by: Paul Balaji <paul@hyperlane.xyz> Co-authored-by: J M Rossy <jm.rossy@gmail.com> Co-authored-by: Trevor Porter <trkporter@ucdavis.edu> Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com>
8 months ago
function test_quoteGasPayment_gasLimitOverride() public {
// arrange
originIcaRouter.enrollRemoteRouterAndIsm(
feat:ICA support in SDK (#3483) ### Description - Adding 'deployAccount` and `getCallRemote` helpers in InterchainAccount.ts for ease of use for the governor - Adding resolveAccountOwner/resolveInterchainAccountAsOwner wherever it's ambiguous if the owner is address (status quo) or AccountConfig (from this PR) - Added icaApp to HyperlaneDeployer for easier access to ICA ownership for any deployer - resolveAccountOwner vs resolveInterchainAccountAsOwner: the former is usable outside the deployers like the checker, etc whereas the latter is where we're already using the deployer and have the required localRouter already cached - includes the formatting change for the account verification file - owner needs casting in infra/config because of the type Address | AccountConfig type (cannot use resolveAccountOwner here which requires multiprovider) - Add quoteGasPayment with gas limit override to the ICA router contract ### Drive-by changes - HyperlaneDeployer.cacheAddressesMap appends addresses two keys deep instead of overridding the whole cache - callData in utils now supports value to be compatible with CallData[] in the ICA Router contract and generally for V3 which allows msg.value on dispatch - `appFromAddressesMapHelper` doesn't intersect multiprovider with contractMap chains (this causes issues if the contracts map doesn't include chains we may want to read something like domainId from like ismfactory or ICADeployer) - HyperlaneHookDeployer is now using the owner as owner vs it was earlier using the deployer earlier for unknown reason - ### Related issues - fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3505 ### Backward compatibility No (call value) ### Testing Unit testing/e2e testing through governor --------- Signed-off-by: Paul Balaji <paul@hyperlane.xyz> Co-authored-by: Paul Balaji <paul@hyperlane.xyz> Co-authored-by: J M Rossy <jm.rossy@gmail.com> Co-authored-by: Trevor Porter <trkporter@ucdavis.edu> Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com>
8 months ago
destination,
routerOverride,
ismOverride
);
// assert
assertEq(
originIcaRouter.quoteGasPayment(
destination,
"",
GAS_LIMIT_OVERRIDE
),
feat:ICA support in SDK (#3483) ### Description - Adding 'deployAccount` and `getCallRemote` helpers in InterchainAccount.ts for ease of use for the governor - Adding resolveAccountOwner/resolveInterchainAccountAsOwner wherever it's ambiguous if the owner is address (status quo) or AccountConfig (from this PR) - Added icaApp to HyperlaneDeployer for easier access to ICA ownership for any deployer - resolveAccountOwner vs resolveInterchainAccountAsOwner: the former is usable outside the deployers like the checker, etc whereas the latter is where we're already using the deployer and have the required localRouter already cached - includes the formatting change for the account verification file - owner needs casting in infra/config because of the type Address | AccountConfig type (cannot use resolveAccountOwner here which requires multiprovider) - Add quoteGasPayment with gas limit override to the ICA router contract ### Drive-by changes - HyperlaneDeployer.cacheAddressesMap appends addresses two keys deep instead of overridding the whole cache - callData in utils now supports value to be compatible with CallData[] in the ICA Router contract and generally for V3 which allows msg.value on dispatch - `appFromAddressesMapHelper` doesn't intersect multiprovider with contractMap chains (this causes issues if the contracts map doesn't include chains we may want to read something like domainId from like ismfactory or ICADeployer) - HyperlaneHookDeployer is now using the owner as owner vs it was earlier using the deployer earlier for unknown reason - ### Related issues - fixes https://github.com/hyperlane-xyz/hyperlane-monorepo/issues/3505 ### Backward compatibility No (call value) ### Testing Unit testing/e2e testing through governor --------- Signed-off-by: Paul Balaji <paul@hyperlane.xyz> Co-authored-by: Paul Balaji <paul@hyperlane.xyz> Co-authored-by: J M Rossy <jm.rossy@gmail.com> Co-authored-by: Trevor Porter <trkporter@ucdavis.edu> Co-authored-by: Daniel Savu <23065004+daniel-savu@users.noreply.github.com>
8 months ago
igp.quoteGasPayment(destination, GAS_LIMIT_OVERRIDE)
);
}
function testFuzz_singleCallRemoteWithDefault(bytes32 data) public {
// arrange
originIcaRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
uint256 balanceBefore = address(this).balance;
// act
CallLib.Call[] memory calls = getCalls(data);
originIcaRouter.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 testFuzz_callRemoteWithDefault(bytes32 data) public {
// arrange
originIcaRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
uint256 balanceBefore = address(this).balance;
// act
originIcaRouter.callRemote{value: gasPaymentQuote}(
destination,
getCalls(data)
);
// assert
uint256 balanceAfter = address(this).balance;
assertRemoteCallReceived(data);
assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage());
}
function testFuzz_overrideAndCallRemote(bytes32 data) public {
// arrange
originIcaRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
uint256 balanceBefore = address(this).balance;
// act
originIcaRouter.callRemote{value: gasPaymentQuote}(
destination,
getCalls(data)
);
// assert
uint256 balanceAfter = address(this).balance;
assertRemoteCallReceived(data);
assertIgpPayment(balanceBefore, balanceAfter, igp.getDefaultGasUsage());
}
function testFuzz_callRemoteWithoutDefaults_revert_noRouter(
bytes32 data
) public {
// assert error
CallLib.Call[] memory calls = getCalls(data);
vm.expectRevert(bytes("no router specified for destination"));
originIcaRouter.callRemote(destination, calls);
}
function testFuzz_customMetadata_forIgp(
uint64 gasLimit,
uint64 overpayment,
bytes32 data
) public {
// arrange
bytes memory metadata = StandardHookMetadata.formatMetadata(
0,
gasLimit,
address(this),
""
);
originIcaRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
uint256 balanceBefore = address(this).balance;
// act
originIcaRouter.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 {
CallLib.Call[] memory calls = getCalls(data);
vm.assume(payment < gasLimit * igp.gasPrice());
// arrange
bytes memory metadata = StandardHookMetadata.formatMetadata(
0,
gasLimit,
address(this),
""
);
originIcaRouter.enrollRemoteRouterAndIsm(
destination,
routerOverride,
ismOverride
);
// act
vm.expectRevert("IGP: insufficient interchain gas payment");
originIcaRouter.callRemote{value: payment}(
destination,
calls,
metadata
);
}
function testFuzz_callRemoteWithOverrides_default(bytes32 data) public {
// arrange
uint256 balanceBefore = address(this).balance;
// act
originIcaRouter.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
originIcaRouter.callRemoteWithOverrides{
value: gasLimit * igp.gasPrice()
}(destination, routerOverride, ismOverride, getCalls(data), metadata);
// assert
uint256 balanceAfter = address(this).balance;
assertRemoteCallReceived(data);
assertIgpPayment(balanceBefore, balanceAfter, gasLimit);
}
function testFuzz_callRemoteWithFailingIsmOverride(bytes32 data) public {
// arrange
string memory failureMessage = "failing ism";
bytes32 failingIsm = TypeCasts.addressToBytes32(
address(new FailingIsm(failureMessage))
);
// act
originIcaRouter.callRemoteWithOverrides{value: gasPaymentQuote}(
destination,
routerOverride,
failingIsm,
getCalls(data),
""
);
// assert
vm.expectRevert(bytes(failureMessage));
environment.processNextPendingMessage();
}
function testFuzz_callRemoteWithFailingDefaultIsm(bytes32 data) public {
// arrange
string memory failureMessage = "failing ism";
FailingIsm failingIsm = new FailingIsm(failureMessage);
// act
environment.mailboxes(destination).setDefaultIsm(address(failingIsm));
originIcaRouter.callRemoteWithOverrides{value: gasPaymentQuote}(
destination,
routerOverride,
bytes32(0),
getCalls(data),
""
);
// assert
vm.expectRevert(bytes(failureMessage));
environment.processNextPendingMessage();
}
function testFuzz_getLocalInterchainAccount(bytes32 data) public {
// check
OwnableMulticall destinationIca = destinationIcaRouter
.getLocalInterchainAccount(
origin,
address(this),
address(originIcaRouter),
address(environment.isms(destination))
);
assertEq(
address(destinationIca),
address(
destinationIcaRouter.getLocalInterchainAccount(
origin,
TypeCasts.addressToBytes32(address(this)),
TypeCasts.addressToBytes32(address(originIcaRouter)),
address(environment.isms(destination))
)
)
);
assertEq(address(destinationIca).code.length, 0);
// act
originIcaRouter.callRemoteWithOverrides{value: gasPaymentQuote}(
destination,
routerOverride,
ismOverride,
getCalls(data),
""
);
// recheck
assertRemoteCallReceived(data);
assert(address(destinationIca).code.length != 0);
}
function testFuzz_receiveValue(uint256 value) public {
vm.assume(value > 1 && value <= address(this).balance);
// receive value before deployed
assert(address(ica).code.length == 0);
bool success;
(success, ) = address(ica).call{value: value / 2}("");
require(success, "transfer before deploy failed");
// receive value after deployed
destinationIcaRouter.getDeployedInterchainAccount(
origin,
address(this),
address(originIcaRouter),
address(environment.isms(destination))
);
assert(address(ica).code.length > 0);
(success, ) = address(ica).call{value: value / 2}("");
require(success, "transfer after deploy failed");
}
function receiveValue(uint256 value) external payable {
assertEq(value, msg.value);
}
function testFuzz_sendValue(uint256 value) public {
vm.assume(
value > 0 && value <= address(this).balance - gasPaymentQuote
);
payable(address(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;
originIcaRouter.callRemoteWithOverrides{value: gasPaymentQuote}(
destination,
routerOverride,
ismOverride,
calls,
""
);
vm.expectCall(address(this), value, data);
environment.processNextPendingMessage();
}
}