Skip to content

Commit

Permalink
fix(world-modules): SystemSwitch properly calls systems from root (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
yonadaaa committed Feb 7, 2024
1 parent 2b8c0bc commit c4fc850
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 16 deletions.
5 changes: 5 additions & 0 deletions .changeset/light-carrots-applaud.md
@@ -0,0 +1,5 @@
---
"@latticexyz/world-modules": minor
---

Fixed `SystemSwitch` to properly call non-root systems from root systems.
15 changes: 6 additions & 9 deletions packages/world-modules/src/utils/SystemSwitch.sol
Expand Up @@ -11,6 +11,7 @@ import { AccessControl } from "@latticexyz/world/src/AccessControl.sol";
import { ROOT_NAMESPACE } from "@latticexyz/world/src/constants.sol";
import { revertWithBytes } from "@latticexyz/world/src/revertWithBytes.sol";
import { BEFORE_CALL_SYSTEM, AFTER_CALL_SYSTEM } from "@latticexyz/world/src/systemHookTypes.sol";
import { SystemCall } from "@latticexyz/world/src/SystemCall.sol";

import { IWorldErrors } from "@latticexyz/world/src/IWorldErrors.sol";
import { ISystemHook } from "@latticexyz/world/src/ISystemHook.sol";
Expand Down Expand Up @@ -39,17 +40,13 @@ library SystemSwitch {
function call(ResourceId systemId, bytes memory callData) internal returns (bytes memory returnData) {
address worldAddress = WorldContextConsumerLib._world();

// If we're in the World context, call the system directly via delegatecall
// If we're in the World context, call via the internal library
if (address(this) == worldAddress) {
(address systemAddress, ) = Systems.get(systemId);
// Check if the system exists
if (systemAddress == address(0)) revert IWorldErrors.World_ResourceNotFound(systemId, systemId.toString());

bool success;
(success, returnData) = WorldContextProviderLib.delegatecallWithContext({
msgSender: WorldContextConsumerLib._msgSender(),
msgValue: WorldContextConsumerLib._msgValue(),
target: systemAddress,
(success, returnData) = SystemCall.call({
caller: WorldContextConsumerLib._msgSender(),
value: 0,
systemId: systemId,
callData: callData
});

Expand Down
63 changes: 56 additions & 7 deletions packages/world-modules/test/SystemSwitch.t.sol
Expand Up @@ -4,6 +4,11 @@ pragma solidity >=0.8.24;
import { Test } from "forge-std/Test.sol";
import { GasReporter } from "@latticexyz/gas-report/src/GasReporter.sol";

import { IStoreErrors } from "@latticexyz/store/src/IStoreErrors.sol";
import { ResourceIds, ResourceIdsTableId } from "@latticexyz/store/src/codegen/tables/ResourceIds.sol";
import { Schema } from "@latticexyz/store/src/Schema.sol";
import { StoreCore } from "@latticexyz/store/src/StoreCore.sol";

import { System } from "@latticexyz/world/src/System.sol";
import { IBaseWorld } from "@latticexyz/world/src/codegen/interfaces/IBaseWorld.sol";
import { World } from "@latticexyz/world/src/World.sol";
Expand All @@ -22,7 +27,11 @@ contract EchoSystem is System {
return _world();
}

function echo(string memory message) public view returns (string memory) {
function readTable() public view returns (Schema) {
return StoreCore.getKeySchema(ResourceIdsTableId);
}

function echo(string memory message) public pure returns (string memory) {
return message;
}

Expand Down Expand Up @@ -128,6 +137,12 @@ contract SystemSwitchTest is Test, GasReporter {
assertEq(abi.decode(returnData, (string)), "hello");
}

function testCallRootFromRootReadTable() public {
vm.prank(caller);
bytes memory returnData = _executeFromSystemA(rootSystemBId, abi.encodeCall(EchoSystem.readTable, ()));
assertEq(Schema.unwrap(abi.decode(returnData, (Schema))), Schema.unwrap(ResourceIds.getKeySchema()));
}

// - ROOT FROM NON ROOT ---------------------------------------------------------------------------- //

function testCallRootFromNonRootMsgSender() public {
Expand Down Expand Up @@ -161,6 +176,12 @@ contract SystemSwitchTest is Test, GasReporter {
assertEq(abi.decode(returnData, (string)), "hello");
}

function testCallRootFromNonRootReadTable() public {
vm.prank(caller);
bytes memory returnData = _executeFromSystemA(rootSystemBId, abi.encodeCall(EchoSystem.readTable, ()));
assertEq(Schema.unwrap(abi.decode(returnData, (Schema))), Schema.unwrap(ResourceIds.getKeySchema()));
}

// - NON ROOT FROM ROOT ---------------------------------------------------------------------------- //

function testCallNonRootFromRootMsgSender() public {
Expand All @@ -169,19 +190,19 @@ contract SystemSwitchTest is Test, GasReporter {
assertEq(abi.decode(returnData, (address)), caller);
}

function testNonCallRootFromRootWorld() public {
function testCallNonRootFromRootWorld() public {
vm.prank(caller);
bytes memory returnData = _executeFromRootSystemA(systemBId, abi.encodeCall(EchoSystem.world, ()));
assertEq(abi.decode(returnData, (address)), address(world));
}

function testNonCallRootFromRootEcho() public {
function testCallNonRootFromRootEcho() public {
vm.prank(caller);
bytes memory returnData = _executeFromRootSystemA(systemBId, abi.encodeCall(EchoSystem.echo, ("hello")));
assertEq(abi.decode(returnData, (string)), "hello");
}

function testNonCallRootFromRootWorldSelector() public {
function testCallNonRootFromRootWorldSelector() public {
bytes4 worldFunctionSelector = world.registerRootFunctionSelector(
systemBId,
"echo(string)",
Expand All @@ -194,6 +215,20 @@ contract SystemSwitchTest is Test, GasReporter {
assertEq(abi.decode(returnData, (string)), "hello");
}

function testCallNonRootFromRootReadTable() public {
vm.prank(caller);

// Call reverts because the non-root system storage does not have table schemas
vm.expectRevert(
abi.encodeWithSelector(
IStoreErrors.Store_TableNotFound.selector,
ResourceIdsTableId,
string(abi.encodePacked(ResourceIdsTableId))
)
);
world.call(systemAId, abi.encodeCall(EchoSystem.call, (systemBId, abi.encodeCall(EchoSystem.readTable, ()))));
}

// - NON ROOT FROM NON ROOT ---------------------------------------------------------------------------- //

function testCallNonRootFromNonRootMsgSender() public {
Expand All @@ -202,19 +237,19 @@ contract SystemSwitchTest is Test, GasReporter {
assertEq(abi.decode(returnData, (address)), address(systemA));
}

function testNonCallRootFromNonRootWorld() public {
function testCallNonRootFromNonRootWorld() public {
vm.prank(caller);
bytes memory returnData = _executeFromSystemA(systemBId, abi.encodeCall(EchoSystem.world, ()));
assertEq(abi.decode(returnData, (address)), address(world));
}

function testNonCallRootFromNonRootEcho() public {
function testCallNonRootFromNonRootEcho() public {
vm.prank(caller);
bytes memory returnData = _executeFromSystemA(systemBId, abi.encodeCall(EchoSystem.echo, ("hello")));
assertEq(abi.decode(returnData, (string)), "hello");
}

function testNonCallRootFromNonRootWorldSelector() public {
function testCallNonRootFromNonRootWorldSelector() public {
bytes4 worldFunctionSelector = world.registerRootFunctionSelector(
systemBId,
"echo(string)",
Expand All @@ -226,4 +261,18 @@ contract SystemSwitchTest is Test, GasReporter {
bytes memory returnData = _executeFromSystemA(callData);
assertEq(abi.decode(returnData, (string)), "hello");
}

function testCallNonRootFromNonRootReadTable() public {
vm.prank(caller);

// Call reverts because the non-root system storage does not have table schemas
vm.expectRevert(
abi.encodeWithSelector(
IStoreErrors.Store_TableNotFound.selector,
ResourceIdsTableId,
string(abi.encodePacked(ResourceIdsTableId))
)
);
world.call(systemAId, abi.encodeCall(EchoSystem.call, (systemBId, abi.encodeCall(EchoSystem.readTable, ()))));
}
}

0 comments on commit c4fc850

Please sign in to comment.