diff --git a/.changeset/light-seals-sip.md b/.changeset/light-seals-sip.md new file mode 100644 index 0000000000..260d6033ea --- /dev/null +++ b/.changeset/light-seals-sip.md @@ -0,0 +1,7 @@ +--- +"@latticexyz/store": patch +"@latticexyz/world-module-metadata": patch +"@latticexyz/world": patch +--- + +Support expectRevert and unusual nameless arguments in system libraries. diff --git a/packages/store/ts/flattenStoreLogs.test.ts b/packages/store/ts/flattenStoreLogs.test.ts index ea25fea7b0..13053441f3 100644 --- a/packages/store/ts/flattenStoreLogs.test.ts +++ b/packages/store/ts/flattenStoreLogs.test.ts @@ -154,8 +154,8 @@ describe("flattenStoreLogs", async () => { "Store_SetRecord store__ResourceIds (0x74626d657461646174610000000000005265736f757263655461670000000000)", "Store_SetRecord store__ResourceIds (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", "Store_SetRecord world__Systems (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", - "Store_SetRecord world__SystemRegistry (0x000000000000000000000000bdb70930001e32533d1adfa3b008962112f5ff95)", - "Store_SetRecord world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x000000000000000000000000bdb70930001e32533d1adfa3b008962112f5ff95)", + "Store_SetRecord world__SystemRegistry (0x00000000000000000000000080c073931547e92d858c7556a7b711f161354d29)", + "Store_SetRecord world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x00000000000000000000000080c073931547e92d858c7556a7b711f161354d29)", "Store_SetRecord world__FunctionSelector (0xff66f05f00000000000000000000000000000000000000000000000000000000)", "Store_SetRecord world__FunctionSignatur (0xc6972e9300000000000000000000000000000000000000000000000000000000)", "Store_SetRecord world__FunctionSignatur (0xff66f05f00000000000000000000000000000000000000000000000000000000)", @@ -165,7 +165,7 @@ describe("flattenStoreLogs", async () => { "Store_SetRecord world__FunctionSelector (0x5ce7ca1a00000000000000000000000000000000000000000000000000000000)", "Store_SetRecord world__FunctionSignatur (0xf128760200000000000000000000000000000000000000000000000000000000)", "Store_SetRecord world__FunctionSignatur (0x5ce7ca1a00000000000000000000000000000000000000000000000000000000)", - "Store_SetRecord world__InstalledModules (0x0000000000000000000000003604b87ecb7dfcc5ff9ee26763a5704e57377fe3,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", + "Store_SetRecord world__InstalledModules (0x00000000000000000000000086bf82828d798a6a795087a2a6190ba451a16d3a,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", "Store_SetRecord metadata__ResourceTag (0x737900000000000000000000000000004d6f766553797374656d000000000000,0x6162690000000000000000000000000000000000000000000000000000000000)", "Store_SetRecord metadata__ResourceTag (0x737900000000000000000000000000004d6f766553797374656d000000000000,0x776f726c64416269000000000000000000000000000000000000000000000000)", "Store_SetRecord Position (0x0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e)", diff --git a/packages/store/ts/getStoreLogs.test.ts b/packages/store/ts/getStoreLogs.test.ts index 6cecdbb192..8492bd04e0 100644 --- a/packages/store/ts/getStoreLogs.test.ts +++ b/packages/store/ts/getStoreLogs.test.ts @@ -172,7 +172,7 @@ describe("getStoreLogs", async () => { "Store_SetRecord world__FunctionSignatur (0x1fae630800000000000000000000000000000000000000000000000000000000)", "Store_SetRecord world__FunctionSignatur (0x1fae630800000000000000000000000000000000000000000000000000000000)", "Store_SpliceStaticData world__InstalledModules (0x00000000000000000000000051bd8d2de7017c23ee5bdc885e70dfdd0862b837,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", - "Store_SpliceStaticData world__UserDelegationCo (0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266,0x0000000000000000000000003604b87ecb7dfcc5ff9ee26763a5704e57377fe3)", + "Store_SpliceStaticData world__UserDelegationCo (0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266,0x00000000000000000000000086bf82828d798a6a795087a2a6190ba451a16d3a)", "Store_SpliceStaticData store__ResourceIds (0x6e736d6574616461746100000000000000000000000000000000000000000000)", "Store_SpliceStaticData world__NamespaceOwner (0x6e736d6574616461746100000000000000000000000000000000000000000000)", "Store_SpliceStaticData world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266)", @@ -180,8 +180,8 @@ describe("getStoreLogs", async () => { "Store_SpliceStaticData store__ResourceIds (0x74626d657461646174610000000000005265736f757263655461670000000000)", "Store_SpliceStaticData store__ResourceIds (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", "Store_SetRecord world__Systems (0x73796d657461646174610000000000004d6574616461746153797374656d0000)", - "Store_SpliceStaticData world__SystemRegistry (0x000000000000000000000000bdb70930001e32533d1adfa3b008962112f5ff95)", - "Store_SpliceStaticData world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x000000000000000000000000bdb70930001e32533d1adfa3b008962112f5ff95)", + "Store_SpliceStaticData world__SystemRegistry (0x00000000000000000000000080c073931547e92d858c7556a7b711f161354d29)", + "Store_SpliceStaticData world__ResourceAccess (0x6e736d6574616461746100000000000000000000000000000000000000000000,0x00000000000000000000000080c073931547e92d858c7556a7b711f161354d29)", "Store_SetRecord world__FunctionSelector (0xff66f05f00000000000000000000000000000000000000000000000000000000)", "Store_SetRecord world__FunctionSignatur (0xc6972e9300000000000000000000000000000000000000000000000000000000)", "Store_SetRecord world__FunctionSignatur (0xff66f05f00000000000000000000000000000000000000000000000000000000)", @@ -191,8 +191,8 @@ describe("getStoreLogs", async () => { "Store_SetRecord world__FunctionSelector (0x5ce7ca1a00000000000000000000000000000000000000000000000000000000)", "Store_SetRecord world__FunctionSignatur (0xf128760200000000000000000000000000000000000000000000000000000000)", "Store_SetRecord world__FunctionSignatur (0x5ce7ca1a00000000000000000000000000000000000000000000000000000000)", - "Store_SpliceStaticData world__InstalledModules (0x0000000000000000000000003604b87ecb7dfcc5ff9ee26763a5704e57377fe3,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", - "Store_DeleteRecord world__UserDelegationCo (0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266,0x0000000000000000000000003604b87ecb7dfcc5ff9ee26763a5704e57377fe3)", + "Store_SpliceStaticData world__InstalledModules (0x00000000000000000000000086bf82828d798a6a795087a2a6190ba451a16d3a,0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470)", + "Store_DeleteRecord world__UserDelegationCo (0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266,0x00000000000000000000000086bf82828d798a6a795087a2a6190ba451a16d3a)", "Store_SpliceDynamicData metadata__ResourceTag (0x737900000000000000000000000000004d6f766553797374656d000000000000,0x6162690000000000000000000000000000000000000000000000000000000000)", "Store_SpliceDynamicData metadata__ResourceTag (0x737900000000000000000000000000004d6f766553797374656d000000000000,0x776f726c64416269000000000000000000000000000000000000000000000000)", "Store_SetRecord Position (0x0000000000000000000000001d96f2f6bef1202e4ce1ff6dad0c2cb002861d3e)", diff --git a/packages/world-module-metadata/gas-report.json b/packages/world-module-metadata/gas-report.json index 53eefea016..5fa9c2238d 100644 --- a/packages/world-module-metadata/gas-report.json +++ b/packages/world-module-metadata/gas-report.json @@ -9,7 +9,7 @@ "file": "test/MetadataModule.t.sol:MetadataModuleTest", "test": "testInstall", "name": "install metadata module", - "gasUsed": 1053341 + "gasUsed": 1053458 }, { "file": "test/MetadataModule.t.sol:MetadataModuleTest", diff --git a/packages/world-module-metadata/src/codegen/experimental/systems/MetadataSystemLib.sol b/packages/world-module-metadata/src/codegen/experimental/systems/MetadataSystemLib.sol index 2c8e5ef388..9ee833ba79 100644 --- a/packages/world-module-metadata/src/codegen/experimental/systems/MetadataSystemLib.sol +++ b/packages/world-module-metadata/src/codegen/experimental/systems/MetadataSystemLib.sol @@ -41,7 +41,7 @@ library MetadataSystemLib { MetadataSystemType self, ResourceId resource, bytes32 tag - ) internal view returns (bytes memory) { + ) internal view returns (bytes memory __auxRet0) { return CallWrapper(self.toResourceId(), address(0)).getResourceTag(resource, tag); } @@ -57,7 +57,7 @@ library MetadataSystemLib { CallWrapper memory self, ResourceId resource, bytes32 tag - ) internal view returns (bytes memory) { + ) internal view returns (bytes memory __auxRet0) { // if the contract calling this function is a root system, it should use `callAsRoot` if (address(_world()) == address(this)) revert MetadataSystemLib_CallingFromRootSystem(); @@ -69,7 +69,10 @@ library MetadataSystemLib { if (!success) revertWithBytes(returnData); bytes memory result = abi.decode(returnData, (bytes)); - return abi.decode(result, (bytes)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes)); + } } function setResourceTag(CallWrapper memory self, ResourceId resource, bytes32 tag, bytes memory value) internal { @@ -99,11 +102,14 @@ library MetadataSystemLib { RootCallWrapper memory self, ResourceId resource, bytes32 tag - ) internal view returns (bytes memory) { + ) internal view returns (bytes memory __auxRet0) { bytes memory systemCall = abi.encodeCall(_getResourceTag_ResourceId_bytes32.getResourceTag, (resource, tag)); bytes memory result = SystemCall.staticcallOrRevert(self.from, self.systemId, systemCall); - return abi.decode(result, (bytes)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes)); + } } function setResourceTag(RootCallWrapper memory self, ResourceId resource, bytes32 tag, bytes memory value) internal { diff --git a/packages/world/src/codegen/experimental/systems/BatchCallSystemLib.sol b/packages/world/src/codegen/experimental/systems/BatchCallSystemLib.sol index 253f12f939..5e5003cfd3 100644 --- a/packages/world/src/codegen/experimental/systems/BatchCallSystemLib.sol +++ b/packages/world/src/codegen/experimental/systems/BatchCallSystemLib.sol @@ -64,7 +64,10 @@ library BatchCallSystemLib { bytes memory result = self.from == address(0) ? _world().call(self.systemId, systemCall) : _world().callFrom(self.from, self.systemId, systemCall); - return abi.decode(result, (bytes[])); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes[])); + } } function batchCallFrom( @@ -79,7 +82,10 @@ library BatchCallSystemLib { bytes memory result = self.from == address(0) ? _world().call(self.systemId, systemCall) : _world().callFrom(self.from, self.systemId, systemCall); - return abi.decode(result, (bytes[])); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes[])); + } } function batchCall( @@ -89,7 +95,10 @@ library BatchCallSystemLib { bytes memory systemCall = abi.encodeCall(_batchCall_SystemCallDataArray.batchCall, (systemCalls)); bytes memory result = SystemCall.callWithHooksOrRevert(self.from, self.systemId, systemCall, msg.value); - return abi.decode(result, (bytes[])); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes[])); + } } function batchCallFrom( @@ -99,7 +108,10 @@ library BatchCallSystemLib { bytes memory systemCall = abi.encodeCall(_batchCallFrom_SystemCallFromDataArray.batchCallFrom, (systemCalls)); bytes memory result = SystemCall.callWithHooksOrRevert(self.from, self.systemId, systemCall, msg.value); - return abi.decode(result, (bytes[])); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes[])); + } } function callFrom(BatchCallSystemType self, address from) internal pure returns (CallWrapper memory) { diff --git a/packages/world/src/codegen/experimental/systems/WorldRegistrationSystemLib.sol b/packages/world/src/codegen/experimental/systems/WorldRegistrationSystemLib.sol index 59434abc5c..7ce9beabc5 100644 --- a/packages/world/src/codegen/experimental/systems/WorldRegistrationSystemLib.sol +++ b/packages/world/src/codegen/experimental/systems/WorldRegistrationSystemLib.sol @@ -193,7 +193,10 @@ library WorldRegistrationSystemLib { bytes memory result = self.from == address(0) ? _world().call(self.systemId, systemCall) : _world().callFrom(self.from, self.systemId, systemCall); - return abi.decode(result, (bytes4)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes4)); + } } function registerRootFunctionSelector( @@ -213,7 +216,10 @@ library WorldRegistrationSystemLib { bytes memory result = self.from == address(0) ? _world().call(self.systemId, systemCall) : _world().callFrom(self.from, self.systemId, systemCall); - return abi.decode(result, (bytes4)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes4)); + } } function registerDelegation( @@ -320,7 +326,10 @@ library WorldRegistrationSystemLib { ); bytes memory result = SystemCall.callWithHooksOrRevert(self.from, self.systemId, systemCall, msg.value); - return abi.decode(result, (bytes4)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes4)); + } } function registerRootFunctionSelector( @@ -335,7 +344,10 @@ library WorldRegistrationSystemLib { ); bytes memory result = SystemCall.callWithHooksOrRevert(self.from, self.systemId, systemCall, msg.value); - return abi.decode(result, (bytes4)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (bytes4)); + } } function registerDelegation( diff --git a/packages/world/ts/node/render-solidity/renderSystemLibrary.ts b/packages/world/ts/node/render-solidity/renderSystemLibrary.ts index f5ffcaae3b..4a72dce810 100644 --- a/packages/world/ts/node/render-solidity/renderSystemLibrary.ts +++ b/packages/world/ts/node/render-solidity/renderSystemLibrary.ts @@ -26,7 +26,8 @@ export function renderSystemLibrary(options: RenderSystemLibraryOptions) { const functions = functionsInput.map((func) => ({ ...func, // Format parameters (add auxiliary argument names, replace calldata location) - parameters: formatParams(func.parameters), + parameters: formatParams("__auxArg", func.parameters), + returnParameters: formatParams("__auxRet", func.returnParameters), // Remove `payable` from stateMutability for library functions stateMutability: func.stateMutability.replace("payable", ""), })); @@ -308,7 +309,10 @@ function renderAbiDecode(expression: string, returnParameters: string[]) { const returnTypes = returnParameters.map((param) => param.split(" ")[0]).join(", "); return ` bytes memory result = ${expression}; - return abi.decode(result, (${returnTypes})); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (${returnTypes})); + } `; } @@ -318,15 +322,15 @@ function renderReturnParameters(returnParameters: string[]) { return `returns (${renderArguments(returnParameters)})`; } -function formatParams(params: string[]) { +function formatParams(auxPrefix: string, params: string[]) { // Use auxiliary argument names for arguments without names let auxCount = 0; return params - .map((arg) => arg.replace(/ calldata /, " memory ")) + .map((arg) => arg.replace(/ calldata( |$)/, " memory$1")) .map((arg) => { const items = arg.split(" "); - const needsAux = items.length === 1 || (items.length === 2 && items[1] === "memory"); - return needsAux ? `${arg} __aux${auxCount++}` : arg; + const needsAux = items.length === 1 || (items.length === 2 && ["memory", "payable"].includes(items[1])); + return needsAux ? `${arg} ${auxPrefix}${auxCount++}` : arg; }); } diff --git a/test/system-libraries/src/codegen/world/IASystem.sol b/test/system-libraries/src/codegen/world/IASystem.sol index 1160967ced..195865817d 100644 --- a/test/system-libraries/src/codegen/world/IASystem.sol +++ b/test/system-libraries/src/codegen/world/IASystem.sol @@ -28,6 +28,17 @@ interface IASystem { function a__setAddress() external returns (address); + function a__setWithNamelessParameters( + address payable, + bytes calldata b, + bytes calldata, + string[] memory + ) external returns (address payable, bytes calldata, string[] memory); + + function a__getValueWithRevert() external pure returns (uint256); + + function a__setAddressWithRevert() external returns (address); + function a__setValuesStaticArray(uint256[1] memory values) external; function a__setValuesStaticArray(uint256[2] memory values) external; diff --git a/test/system-libraries/src/namespaces/a/ASystem.sol b/test/system-libraries/src/namespaces/a/ASystem.sol index 664f501fb1..eab35db9b4 100644 --- a/test/system-libraries/src/namespaces/a/ASystem.sol +++ b/test/system-libraries/src/namespaces/a/ASystem.sol @@ -45,6 +45,27 @@ contract ASystem is System { return addr; } + function setWithNamelessParameters( + address payable, + bytes calldata b, + bytes calldata, + string[] memory + ) external returns (address payable, bytes calldata, string[] memory) { + address addr = _msgSender(); + AddressValue.set(addr); + return (payable(addr), b, new string[](0)); + } + + function getValueWithRevert() external pure returns (uint256) { + revert("reverted successfully"); + } + + function setAddressWithRevert() external returns (address) { + address addr = _msgSender(); + AddressValue.set(addr); + revert("reverted successfully"); + } + function setValuesStaticArray(uint256[1] memory values) external { Value.set(values[0]); } diff --git a/test/system-libraries/src/namespaces/a/codegen/systems/ASystemLib.sol b/test/system-libraries/src/namespaces/a/codegen/systems/ASystemLib.sol index 12ac5aeb37..646069d177 100644 --- a/test/system-libraries/src/namespaces/a/codegen/systems/ASystemLib.sol +++ b/test/system-libraries/src/namespaces/a/codegen/systems/ASystemLib.sol @@ -57,18 +57,36 @@ library ASystemLib { return CallWrapper(self.toResourceId(), address(0)).setPositions(positions); } - function getValue(ASystemType self) internal view returns (uint256) { + function getValue(ASystemType self) internal view returns (uint256 __auxRet0) { return CallWrapper(self.toResourceId(), address(0)).getValue(); } - function getTwoValues(ASystemType self) internal view returns (uint256, uint256) { + function getTwoValues(ASystemType self) internal view returns (uint256 __auxRet0, uint256 __auxRet1) { return CallWrapper(self.toResourceId(), address(0)).getTwoValues(); } - function setAddress(ASystemType self) internal returns (address) { + function setAddress(ASystemType self) internal returns (address __auxRet0) { return CallWrapper(self.toResourceId(), address(0)).setAddress(); } + function setWithNamelessParameters( + ASystemType self, + address payable __auxArg0, + bytes memory b, + bytes memory __auxArg1, + string[] memory __auxArg2 + ) internal returns (address payable __auxRet0, bytes memory __auxRet1, string[] memory __auxRet2) { + return CallWrapper(self.toResourceId(), address(0)).setWithNamelessParameters(__auxArg0, b, __auxArg1, __auxArg2); + } + + function getValueWithRevert(ASystemType self) internal view returns (uint256 __auxRet0) { + return CallWrapper(self.toResourceId(), address(0)).getValueWithRevert(); + } + + function setAddressWithRevert(ASystemType self) internal returns (address __auxRet0) { + return CallWrapper(self.toResourceId(), address(0)).setAddressWithRevert(); + } + function setValuesStaticArray(ASystemType self, uint256[1] memory values) internal { return CallWrapper(self.toResourceId(), address(0)).setValuesStaticArray(values); } @@ -131,7 +149,7 @@ library ASystemLib { : _world().callFrom(self.from, self.systemId, systemCall); } - function getValue(CallWrapper memory self) internal view returns (uint256) { + function getValue(CallWrapper memory self) internal view returns (uint256 __auxRet0) { // if the contract calling this function is a root system, it should use `callAsRoot` if (address(_world()) == address(this)) revert ASystemLib_CallingFromRootSystem(); @@ -143,10 +161,13 @@ library ASystemLib { if (!success) revertWithBytes(returnData); bytes memory result = abi.decode(returnData, (bytes)); - return abi.decode(result, (uint256)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (uint256)); + } } - function getTwoValues(CallWrapper memory self) internal view returns (uint256, uint256) { + function getTwoValues(CallWrapper memory self) internal view returns (uint256 __auxRet0, uint256 __auxRet1) { // if the contract calling this function is a root system, it should use `callAsRoot` if (address(_world()) == address(this)) revert ASystemLib_CallingFromRootSystem(); @@ -158,10 +179,13 @@ library ASystemLib { if (!success) revertWithBytes(returnData); bytes memory result = abi.decode(returnData, (bytes)); - return abi.decode(result, (uint256, uint256)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (uint256, uint256)); + } } - function setAddress(CallWrapper memory self) internal returns (address) { + function setAddress(CallWrapper memory self) internal returns (address __auxRet0) { // if the contract calling this function is a root system, it should use `callAsRoot` if (address(_world()) == address(this)) revert ASystemLib_CallingFromRootSystem(); @@ -170,7 +194,67 @@ library ASystemLib { bytes memory result = self.from == address(0) ? _world().call(self.systemId, systemCall) : _world().callFrom(self.from, self.systemId, systemCall); - return abi.decode(result, (address)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (address)); + } + } + + function setWithNamelessParameters( + CallWrapper memory self, + address payable __auxArg0, + bytes memory b, + bytes memory __auxArg1, + string[] memory __auxArg2 + ) internal returns (address payable __auxRet0, bytes memory __auxRet1, string[] memory __auxRet2) { + // if the contract calling this function is a root system, it should use `callAsRoot` + if (address(_world()) == address(this)) revert ASystemLib_CallingFromRootSystem(); + + bytes memory systemCall = abi.encodeCall( + _setWithNamelessParameters_address_bytes_bytes_stringArray.setWithNamelessParameters, + (__auxArg0, b, __auxArg1, __auxArg2) + ); + + bytes memory result = self.from == address(0) + ? _world().call(self.systemId, systemCall) + : _world().callFrom(self.from, self.systemId, systemCall); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (address, bytes, string[])); + } + } + + function getValueWithRevert(CallWrapper memory self) internal view returns (uint256 __auxRet0) { + // if the contract calling this function is a root system, it should use `callAsRoot` + if (address(_world()) == address(this)) revert ASystemLib_CallingFromRootSystem(); + + bytes memory systemCall = abi.encodeCall(_getValueWithRevert.getValueWithRevert, ()); + bytes memory worldCall = self.from == address(0) + ? abi.encodeCall(IWorldCall.call, (self.systemId, systemCall)) + : abi.encodeCall(IWorldCall.callFrom, (self.from, self.systemId, systemCall)); + (bool success, bytes memory returnData) = address(_world()).staticcall(worldCall); + if (!success) revertWithBytes(returnData); + + bytes memory result = abi.decode(returnData, (bytes)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (uint256)); + } + } + + function setAddressWithRevert(CallWrapper memory self) internal returns (address __auxRet0) { + // if the contract calling this function is a root system, it should use `callAsRoot` + if (address(_world()) == address(this)) revert ASystemLib_CallingFromRootSystem(); + + bytes memory systemCall = abi.encodeCall(_setAddressWithRevert.setAddressWithRevert, ()); + + bytes memory result = self.from == address(0) + ? _world().call(self.systemId, systemCall) + : _world().callFrom(self.from, self.systemId, systemCall); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (address)); + } } function setValuesStaticArray(CallWrapper memory self, uint256[1] memory values) internal { @@ -231,25 +315,73 @@ library ASystemLib { SystemCall.callWithHooksOrRevert(self.from, self.systemId, systemCall, msg.value); } - function getValue(RootCallWrapper memory self) internal view returns (uint256) { + function getValue(RootCallWrapper memory self) internal view returns (uint256 __auxRet0) { bytes memory systemCall = abi.encodeCall(_getValue.getValue, ()); bytes memory result = SystemCall.staticcallOrRevert(self.from, self.systemId, systemCall); - return abi.decode(result, (uint256)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (uint256)); + } } - function getTwoValues(RootCallWrapper memory self) internal view returns (uint256, uint256) { + function getTwoValues(RootCallWrapper memory self) internal view returns (uint256 __auxRet0, uint256 __auxRet1) { bytes memory systemCall = abi.encodeCall(_getTwoValues.getTwoValues, ()); bytes memory result = SystemCall.staticcallOrRevert(self.from, self.systemId, systemCall); - return abi.decode(result, (uint256, uint256)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (uint256, uint256)); + } } - function setAddress(RootCallWrapper memory self) internal returns (address) { + function setAddress(RootCallWrapper memory self) internal returns (address __auxRet0) { bytes memory systemCall = abi.encodeCall(_setAddress.setAddress, ()); bytes memory result = SystemCall.callWithHooksOrRevert(self.from, self.systemId, systemCall, msg.value); - return abi.decode(result, (address)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (address)); + } + } + + function setWithNamelessParameters( + RootCallWrapper memory self, + address payable __auxArg0, + bytes memory b, + bytes memory __auxArg1, + string[] memory __auxArg2 + ) internal returns (address payable __auxRet0, bytes memory __auxRet1, string[] memory __auxRet2) { + bytes memory systemCall = abi.encodeCall( + _setWithNamelessParameters_address_bytes_bytes_stringArray.setWithNamelessParameters, + (__auxArg0, b, __auxArg1, __auxArg2) + ); + + bytes memory result = SystemCall.callWithHooksOrRevert(self.from, self.systemId, systemCall, msg.value); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (address, bytes, string[])); + } + } + + function getValueWithRevert(RootCallWrapper memory self) internal view returns (uint256 __auxRet0) { + bytes memory systemCall = abi.encodeCall(_getValueWithRevert.getValueWithRevert, ()); + + bytes memory result = SystemCall.staticcallOrRevert(self.from, self.systemId, systemCall); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (uint256)); + } + } + + function setAddressWithRevert(RootCallWrapper memory self) internal returns (address __auxRet0) { + bytes memory systemCall = abi.encodeCall(_setAddressWithRevert.setAddressWithRevert, ()); + + bytes memory result = SystemCall.callWithHooksOrRevert(self.from, self.systemId, systemCall, msg.value); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (address)); + } } function setValuesStaticArray(RootCallWrapper memory self, uint256[1] memory values) internal { @@ -340,6 +472,23 @@ interface _setAddress { function setAddress() external; } +interface _setWithNamelessParameters_address_bytes_bytes_stringArray { + function setWithNamelessParameters( + address payable __auxArg0, + bytes memory b, + bytes memory __auxArg1, + string[] memory __auxArg2 + ) external; +} + +interface _getValueWithRevert { + function getValueWithRevert() external; +} + +interface _setAddressWithRevert { + function setAddressWithRevert() external; +} + interface _setValuesStaticArray_uint2560x5b315d { function setValuesStaticArray(uint256[1] memory values) external; } diff --git a/test/system-libraries/src/namespaces/b/codegen/systems/BSystemLib.sol b/test/system-libraries/src/namespaces/b/codegen/systems/BSystemLib.sol index 62e07b3e51..c71a28da5f 100644 --- a/test/system-libraries/src/namespaces/b/codegen/systems/BSystemLib.sol +++ b/test/system-libraries/src/namespaces/b/codegen/systems/BSystemLib.sol @@ -40,7 +40,7 @@ library BSystemLib { return CallWrapper(self.toResourceId(), address(0)).setValueInA(thing); } - function getValueFromA(BSystemType self) internal view returns (uint256) { + function getValueFromA(BSystemType self) internal view returns (uint256 __auxRet0) { return CallWrapper(self.toResourceId(), address(0)).getValueFromA(); } @@ -54,7 +54,7 @@ library BSystemLib { : _world().callFrom(self.from, self.systemId, systemCall); } - function getValueFromA(CallWrapper memory self) internal view returns (uint256) { + function getValueFromA(CallWrapper memory self) internal view returns (uint256 __auxRet0) { // if the contract calling this function is a root system, it should use `callAsRoot` if (address(_world()) == address(this)) revert BSystemLib_CallingFromRootSystem(); @@ -66,7 +66,10 @@ library BSystemLib { if (!success) revertWithBytes(returnData); bytes memory result = abi.decode(returnData, (bytes)); - return abi.decode(result, (uint256)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (uint256)); + } } function setValueInA(RootCallWrapper memory self, ASystemThing memory thing) internal { @@ -74,11 +77,14 @@ library BSystemLib { SystemCall.callWithHooksOrRevert(self.from, self.systemId, systemCall, msg.value); } - function getValueFromA(RootCallWrapper memory self) internal view returns (uint256) { + function getValueFromA(RootCallWrapper memory self) internal view returns (uint256 __auxRet0) { bytes memory systemCall = abi.encodeCall(_getValueFromA.getValueFromA, ()); bytes memory result = SystemCall.staticcallOrRevert(self.from, self.systemId, systemCall); - return abi.decode(result, (uint256)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (uint256)); + } } function callFrom(BSystemType self, address from) internal pure returns (CallWrapper memory) { diff --git a/test/system-libraries/src/namespaces/root/codegen/systems/RootSystemLib.sol b/test/system-libraries/src/namespaces/root/codegen/systems/RootSystemLib.sol index bfa2c61c27..56e69bc64b 100644 --- a/test/system-libraries/src/namespaces/root/codegen/systems/RootSystemLib.sol +++ b/test/system-libraries/src/namespaces/root/codegen/systems/RootSystemLib.sol @@ -42,7 +42,7 @@ library RootSystemLib { return CallWrapper(self.toResourceId(), address(0)).setValueInA(thing); } - function getValueFromA(RootSystemType self) internal view returns (uint256) { + function getValueFromA(RootSystemType self) internal view returns (uint256 __auxRet0) { return CallWrapper(self.toResourceId(), address(0)).getValueFromA(); } @@ -56,7 +56,7 @@ library RootSystemLib { : _world().callFrom(self.from, self.systemId, systemCall); } - function getValueFromA(CallWrapper memory self) internal view returns (uint256) { + function getValueFromA(CallWrapper memory self) internal view returns (uint256 __auxRet0) { // if the contract calling this function is a root system, it should use `callAsRoot` if (address(_world()) == address(this)) revert RootSystemLib_CallingFromRootSystem(); @@ -68,7 +68,10 @@ library RootSystemLib { if (!success) revertWithBytes(returnData); bytes memory result = abi.decode(returnData, (bytes)); - return abi.decode(result, (uint256)); + // skip decoding an empty result, which can happen after expectRevert + if (result.length != 0) { + return abi.decode(result, (uint256)); + } } function setValueInA(RootCallWrapper memory self, ASystemThing memory thing) internal { diff --git a/test/system-libraries/test/Libraries.t.sol b/test/system-libraries/test/Libraries.t.sol index 1c5b63e19a..6350f7658e 100644 --- a/test/system-libraries/test/Libraries.t.sol +++ b/test/system-libraries/test/Libraries.t.sol @@ -60,6 +60,15 @@ contract LibrariesTest is MudTest { assertEq(Value.get(), 2); aSystem.setValuesStaticArray([uint256(1), 2, 3]); assertEq(Value.get(), 3); + + bytes memory bytesArgument = "test bytes"; + (, bytes memory bytesResult, ) = aSystem.setWithNamelessParameters( + payable(0), + bytesArgument, + bytesArgument, + new string[](0) + ); + assertEq(bytesResult, bytesArgument); } function testCanCallSystemFromOtherSystem() public { @@ -95,4 +104,12 @@ contract LibrariesTest is MudTest { assertEq(aSystem.getValue(), value); assertEq(rootSystem.getValueFromA(), value); } + + function testCanExpectRevert() public { + vm.expectRevert("reverted successfully"); + aSystem.getValueWithRevert(); + + vm.expectRevert("reverted successfully"); + aSystem.setAddressWithRevert(); + } }