diff --git a/.changeset/shy-poets-battle.md b/.changeset/shy-poets-battle.md new file mode 100644 index 0000000000..f9da606e53 --- /dev/null +++ b/.changeset/shy-poets-battle.md @@ -0,0 +1,6 @@ +--- +"@latticexyz/world": patch +--- + +Updated `callFrom` action to automatically translate `batchCall` to `batchCallFrom`. +Also fixed `encodeSystemCallFrom` and `encodeSystemCallsFrom` to return the right format for use with `batchCall` and `batchCallFrom` respectively. diff --git a/packages/world/ts/actions/callFrom.ts b/packages/world/ts/actions/callFrom.ts index e3217e51e3..6ea4ba1cdb 100644 --- a/packages/world/ts/actions/callFrom.ts +++ b/packages/world/ts/actions/callFrom.ts @@ -6,9 +6,10 @@ import { type Account, type Hex, type WalletActions, + type Client, + type PublicActions, + type WriteContractParameters, type EncodeFunctionDataParameters, - Client, - PublicActions, } from "viem"; import { getAction, encodeFunctionData } from "viem/utils"; import { readContract, writeContract as viem_writeContract } from "viem/actions"; @@ -21,6 +22,7 @@ import { encodeKey, } from "@latticexyz/protocol-parser/internal"; import worldConfig from "../../mud.config"; +import { worldCallAbi } from "../worldCallAbi"; type CallFromParameters = { worldAddress: Hex; @@ -46,7 +48,6 @@ export function callFrom( client: Client, ) => Pick, "writeContract"> { return (client) => ({ - // Applies to: `client.writeContract`, `getContract(client, ...).write` async writeContract(writeArgs) { const _writeContract = getAction(client, viem_writeContract, "writeContract"); @@ -55,11 +56,28 @@ export function callFrom( writeArgs.address !== params.worldAddress || writeArgs.functionName === "call" || writeArgs.functionName === "callFrom" || + writeArgs.functionName === "batchCallFrom" || writeArgs.functionName === "callWithSignature" ) { return _writeContract(writeArgs); } + // Wrap system calls from `batchCall` with delegator for a `batchCallFrom` + // TODO: remove this specific workaround once https://github.com/latticexyz/mud/pull/3506 lands + if (writeArgs.functionName === "batchCall") { + const batchCallArgs = writeArgs as unknown as WriteContractParameters; + const [systemCalls] = batchCallArgs.args; + if (!systemCalls.length) { + throw new Error("`batchCall` should have at least one system call."); + } + + return _writeContract({ + ...batchCallArgs, + functionName: "batchCallFrom", + args: [systemCalls.map((systemCall) => ({ from: params.delegatorAddress, ...systemCall }))], + }); + } + // Encode the World's calldata (which includes the World's function selector). const worldCalldata = encodeFunctionData({ abi: writeArgs.abi, @@ -81,15 +99,12 @@ export function callFrom( // Use `readHex` instead of `slice` to prevent out-of-bounds errors with calldata that has no args. const systemCalldata = concat([systemFunctionSelector, readHex(worldCalldata, 4)]); - // Construct args for `callFrom`. - const callFromArgs: typeof writeArgs = { - ...writeArgs, + // Call `writeContract` with the new args. + return _writeContract({ + ...(writeArgs as unknown as WriteContractParameters), functionName: "callFrom", args: [params.delegatorAddress, systemId, systemCalldata], - }; - - // Call `writeContract` with the new args. - return _writeContract(callFromArgs); + }); }, }); } diff --git a/packages/world/ts/encodeSystemCall.ts b/packages/world/ts/encodeSystemCall.ts index 8de1bb4ea1..9cbb47e8a4 100644 --- a/packages/world/ts/encodeSystemCall.ts +++ b/packages/world/ts/encodeSystemCall.ts @@ -22,6 +22,6 @@ export function encodeSystemCall), + } as EncodeFunctionDataParameters), ]; } diff --git a/packages/world/ts/encodeSystemCallFrom.ts b/packages/world/ts/encodeSystemCallFrom.ts index 39393dee21..68c86440ef 100644 --- a/packages/world/ts/encodeSystemCallFrom.ts +++ b/packages/world/ts/encodeSystemCallFrom.ts @@ -27,6 +27,6 @@ export function encodeSystemCallFrom), + } as EncodeFunctionDataParameters), ]; } diff --git a/packages/world/ts/encodeSystemCalls.test.ts b/packages/world/ts/encodeSystemCalls.test.ts index c0261b6b08..3906721248 100644 --- a/packages/world/ts/encodeSystemCalls.test.ts +++ b/packages/world/ts/encodeSystemCalls.test.ts @@ -21,8 +21,10 @@ describe("SystemCalls", () => { ).toMatchInlineSnapshot(` [ [ - "0x7379000000000000000000000000000000000000000000000000000000000000", - "0x40554c3a746200000000000000000000000000006d795461626c65000000000000000000000000000000000000000000943728592c20aed37a35c15235466f7a7cd00bd0", + { + "callData": "0x40554c3a746200000000000000000000000000006d795461626c65000000000000000000000000000000000000000000943728592c20aed37a35c15235466f7a7cd00bd0", + "systemId": "0x7379000000000000000000000000000000000000000000000000000000000000", + }, ], ] `); @@ -47,8 +49,10 @@ describe("SystemCalls", () => { ).toMatchInlineSnapshot(` [ [ - "0x7379000000000000000000000000000000000000000000000000000000000000", - "0x0ba51f49746200000000000000000000000000006d795461626c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046b6579310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046b6579310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066669656c6431000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066669656c64320000000000000000000000000000000000000000000000000000", + { + "callData": "0x0ba51f49746200000000000000000000000000006d795461626c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046b6579310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046b6579310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066669656c6431000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066669656c64320000000000000000000000000000000000000000000000000000", + "systemId": "0x7379000000000000000000000000000000000000000000000000000000000000", + }, ], ] `); diff --git a/packages/world/ts/encodeSystemCalls.ts b/packages/world/ts/encodeSystemCalls.ts index cf8bb74a5f..f364ba2615 100644 --- a/packages/world/ts/encodeSystemCalls.ts +++ b/packages/world/ts/encodeSystemCalls.ts @@ -1,5 +1,5 @@ -import { Abi, type ContractFunctionName } from "viem"; -import { SystemCall, encodeSystemCall } from "./encodeSystemCall"; +import { Abi, EncodeFunctionDataParameters, encodeFunctionData, type ContractFunctionName } from "viem"; +import { SystemCall } from "./encodeSystemCall"; import type { AbiParametersToPrimitiveTypes, ExtractAbiFunction } from "abitype"; import { worldCallAbi } from "./worldCallAbi"; @@ -7,6 +7,15 @@ import { worldCallAbi } from "./worldCallAbi"; export function encodeSystemCalls>( abi: abi, systemCalls: readonly Omit, "abi">[], -): AbiParametersToPrimitiveTypes["inputs"]>[] { - return systemCalls.map((systemCall) => encodeSystemCall({ ...systemCall, abi } as SystemCall)); +): AbiParametersToPrimitiveTypes["inputs"]> { + return [ + systemCalls.map(({ systemId, functionName, args }) => ({ + systemId, + callData: encodeFunctionData({ + abi, + functionName, + args, + } as EncodeFunctionDataParameters), + })), + ]; } diff --git a/packages/world/ts/encodeSystemCallsFrom.ts b/packages/world/ts/encodeSystemCallsFrom.ts index cc8e265d1f..4b655e0da9 100644 --- a/packages/world/ts/encodeSystemCallsFrom.ts +++ b/packages/world/ts/encodeSystemCallsFrom.ts @@ -1,5 +1,5 @@ -import { Abi, Address, type ContractFunctionName } from "viem"; -import { SystemCallFrom, encodeSystemCallFrom } from "./encodeSystemCallFrom"; +import { Abi, Address, EncodeFunctionDataParameters, encodeFunctionData, type ContractFunctionName } from "viem"; +import { SystemCallFrom } from "./encodeSystemCallFrom"; import type { AbiParametersToPrimitiveTypes, ExtractAbiFunction } from "abitype"; import { worldCallAbi } from "./worldCallAbi"; @@ -8,8 +8,16 @@ export function encodeSystemCallsFrom, "abi" | "from">[], -): AbiParametersToPrimitiveTypes["inputs"]>[] { - return systemCalls.map((systemCall) => - encodeSystemCallFrom({ ...systemCall, abi, from } as SystemCallFrom), - ); +): AbiParametersToPrimitiveTypes["inputs"]> { + return [ + systemCalls.map(({ systemId, functionName, args }) => ({ + from, + systemId, + callData: encodeFunctionData({ + abi, + functionName, + args, + } as EncodeFunctionDataParameters), + })), + ]; } diff --git a/packages/world/ts/worldCallAbi.ts b/packages/world/ts/worldCallAbi.ts index 7fa386b2f4..949f72f16f 100644 --- a/packages/world/ts/worldCallAbi.ts +++ b/packages/world/ts/worldCallAbi.ts @@ -2,6 +2,73 @@ // generating full TS files rather than DTS export const worldCallAbi = [ + { + type: "function", + name: "batchCall", + inputs: [ + { + name: "systemCalls", + type: "tuple[]", + internalType: "struct SystemCallData[]", + components: [ + { + name: "systemId", + type: "bytes32", + internalType: "ResourceId", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + ], + }, + ], + outputs: [ + { + name: "returnDatas", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "batchCallFrom", + inputs: [ + { + name: "systemCalls", + type: "tuple[]", + internalType: "struct SystemCallFromData[]", + components: [ + { + name: "from", + type: "address", + internalType: "address", + }, + { + name: "systemId", + type: "bytes32", + internalType: "ResourceId", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + ], + }, + ], + outputs: [ + { + name: "returnDatas", + type: "bytes[]", + internalType: "bytes[]", + }, + ], + stateMutability: "nonpayable", + }, { type: "function", name: "call", @@ -55,6 +122,40 @@ export const worldCallAbi = [ ], stateMutability: "payable", }, + { + type: "function", + name: "callWithSignature", + inputs: [ + { + name: "signer", + type: "address", + internalType: "address", + }, + { + name: "systemId", + type: "bytes32", + internalType: "ResourceId", + }, + { + name: "callData", + type: "bytes", + internalType: "bytes", + }, + { + name: "signature", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "bytes", + internalType: "bytes", + }, + ], + stateMutability: "payable", + }, ] as const; export type worldCallAbi = typeof worldCallAbi;