diff --git a/contracts/contracts/lib/CrossMsgHelper.sol b/contracts/contracts/lib/CrossMsgHelper.sol index b269b1027..144907add 100644 --- a/contracts/contracts/lib/CrossMsgHelper.sol +++ b/contracts/contracts/lib/CrossMsgHelper.sol @@ -181,11 +181,13 @@ library CrossMsgHelper { } address recipient = crossMsg.to.rawAddress.extractEvmAddress().normalize(); - // If the cross msg kind is Result, create result message should have handled the value correctly. - // If the execution is ok, value should be 0, else one should perform refund. - if (crossMsg.kind == IpcMsgKind.Transfer || crossMsg.kind == IpcMsgKind.Result) { + if (crossMsg.kind == IpcMsgKind.Transfer) { return supplySource.transferFunds({recipient: payable(recipient), value: crossMsg.value}); - } else if (crossMsg.kind == IpcMsgKind.Call) { + } else if (crossMsg.kind == IpcMsgKind.Call || crossMsg.kind == IpcMsgKind.Result) { + // For a Result message, the idea is to perform a call as this returns control back to the caller. + // If it's an account, there will be no code to invoke, so this will be have like a bare transfer. + // But if the original caller was a contract, this give it control so it can handle the result + // send the envelope directly to the entrypoint // use supplySource so the tokens in the message are handled successfully // and by the right supply source diff --git a/contracts/test/integration/L2PlusXNet.t.sol b/contracts/test/integration/L2PlusXNet.t.sol index 493faf3c7..8a1d21ccc 100644 --- a/contracts/test/integration/L2PlusXNet.t.sol +++ b/contracts/test/integration/L2PlusXNet.t.sol @@ -35,6 +35,7 @@ import {ActivityHelper} from "../helpers/ActivityHelper.sol"; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {ISubnetActor} from "../../contracts/interfaces/ISubnetActor.sol"; import {IPCMsgType} from "../../contracts/enums/IPCMsgType.sol"; +import {IIpcHandler} from "../../sdk/interfaces/IIpcHandler.sol"; import "forge-std/console.sol"; @@ -137,7 +138,7 @@ interface IGateway { function fundWithToken(SubnetID calldata subnetId, FvmAddress calldata to, uint256 amount) external; } -contract L2PlusSubnetTest is Test, IntegrationTestBase { +contract L2PlusSubnetTest is Test, IntegrationTestBase, IIpcHandler { using SubnetIDHelper for SubnetID; using CrossMsgHelper for IpcEnvelope; using GatewayFacetsHelper for GatewayDiamond; @@ -161,6 +162,17 @@ contract L2PlusSubnetTest is Test, IntegrationTestBase { vm.deal(address(this), 10 ether); } + // ======= implements ipc handler ======= + + /* solhint-disable-next-line unused-vars */ + function handleIpcMessage(IpcEnvelope calldata envelope) external payable returns (bytes memory ret) { + ret = bytes(""); + } + + receive() external payable {} + + // ======= internal util methods ======== + function executeTopdownMessages(IpcEnvelope[] memory msgs, GatewayDiamond gw) internal { uint256 minted_tokens; diff --git a/contracts/test/integration/MultiSubnet.t.sol b/contracts/test/integration/MultiSubnet.t.sol index 7e92b6b35..cafafff10 100644 --- a/contracts/test/integration/MultiSubnet.t.sol +++ b/contracts/test/integration/MultiSubnet.t.sol @@ -1150,16 +1150,18 @@ contract MultiSubnetTest is Test, IntegrationTestBase { vm.deal(tokenSubnet.subnetActorAddr, DEFAULT_COLLATERAL_AMOUNT); vm.deal(caller, 1 ether); - uint256 fundToSend = 10; + uint256 balance = 100; // Fund an account in the subnet. - token.transfer(caller, fundToSend); + token.transfer(caller, balance); vm.prank(caller); - token.approve(rootSubnet.gatewayAddr, fundToSend); + token.approve(rootSubnet.gatewayAddr, balance); vm.prank(tokenSubnet.subnetActorAddr); registerSubnetGW(DEFAULT_COLLATERAL_AMOUNT, tokenSubnet.subnetActorAddr, rootSubnet.gateway); + uint256 fundToSend = 10; + vm.prank(caller); rootSubnet.gateway.manager().fundWithToken(tokenSubnet.id, FvmAddressHelper.from(address(caller)), fundToSend); @@ -1190,14 +1192,14 @@ contract MultiSubnetTest is Test, IntegrationTestBase { // but the caller is MockIpcContractRevert, which reverts whatever // call made to `handleIpcMessage`, we should make sure: // 1. The submission of checkpoint is never blocked + // 2. The fund is locked in the gateway as execution is rejected submitBottomUpCheckpoint(checkpoint, tokenSubnet.subnetActor); - // because error result xnet message should have refunded the token - require(token.balanceOf(caller) == amount, "caller fund should not be locked in gateway"); require( - token.balanceOf(address(rootSubnet.gateway.manager())) == fundToSend - amount, - "fund should not be locked in gateway" + token.balanceOf(address(rootSubnet.gateway.manager())) == fundToSend, + "fund should still be locked in gateway" ); + require(token.balanceOf(caller) == balance - fundToSend, "fund should still be locked in gateway"); } function testMultiSubnet_Token_CallFromChildToParent() public {