Skip to content

Commit 56e65f6

Browse files
frolicdhvanipa
andauthored
feat(world): support batchCall in callFrom action (#2796)
Co-authored-by: Dhvani Patel <[email protected]>
1 parent dead80e commit 56e65f6

8 files changed

+169
-26
lines changed

.changeset/shy-poets-battle.md

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@latticexyz/world": patch
3+
---
4+
5+
Updated `callFrom` action to automatically translate `batchCall` to `batchCallFrom`.
6+
Also fixed `encodeSystemCallFrom` and `encodeSystemCallsFrom` to return the right format for use with `batchCall` and `batchCallFrom` respectively.

packages/world/ts/actions/callFrom.ts

+25-10
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ import {
66
type Account,
77
type Hex,
88
type WalletActions,
9+
type Client,
10+
type PublicActions,
11+
type WriteContractParameters,
912
type EncodeFunctionDataParameters,
10-
Client,
11-
PublicActions,
1213
} from "viem";
1314
import { getAction, encodeFunctionData } from "viem/utils";
1415
import { readContract, writeContract as viem_writeContract } from "viem/actions";
@@ -21,6 +22,7 @@ import {
2122
encodeKey,
2223
} from "@latticexyz/protocol-parser/internal";
2324
import worldConfig from "../../mud.config";
25+
import { worldCallAbi } from "../worldCallAbi";
2426

2527
type CallFromParameters = {
2628
worldAddress: Hex;
@@ -46,7 +48,6 @@ export function callFrom(
4648
client: Client<Transport, chain, account>,
4749
) => Pick<WalletActions<chain, account>, "writeContract"> {
4850
return (client) => ({
49-
// Applies to: `client.writeContract`, `getContract(client, ...).write`
5051
async writeContract(writeArgs) {
5152
const _writeContract = getAction(client, viem_writeContract, "writeContract");
5253

@@ -55,11 +56,28 @@ export function callFrom(
5556
writeArgs.address !== params.worldAddress ||
5657
writeArgs.functionName === "call" ||
5758
writeArgs.functionName === "callFrom" ||
59+
writeArgs.functionName === "batchCallFrom" ||
5860
writeArgs.functionName === "callWithSignature"
5961
) {
6062
return _writeContract(writeArgs);
6163
}
6264

65+
// Wrap system calls from `batchCall` with delegator for a `batchCallFrom`
66+
// TODO: remove this specific workaround once https://github.com/latticexyz/mud/pull/3506 lands
67+
if (writeArgs.functionName === "batchCall") {
68+
const batchCallArgs = writeArgs as unknown as WriteContractParameters<worldCallAbi, "batchCall">;
69+
const [systemCalls] = batchCallArgs.args;
70+
if (!systemCalls.length) {
71+
throw new Error("`batchCall` should have at least one system call.");
72+
}
73+
74+
return _writeContract({
75+
...batchCallArgs,
76+
functionName: "batchCallFrom",
77+
args: [systemCalls.map((systemCall) => ({ from: params.delegatorAddress, ...systemCall }))],
78+
});
79+
}
80+
6381
// Encode the World's calldata (which includes the World's function selector).
6482
const worldCalldata = encodeFunctionData({
6583
abi: writeArgs.abi,
@@ -81,15 +99,12 @@ export function callFrom(
8199
// Use `readHex` instead of `slice` to prevent out-of-bounds errors with calldata that has no args.
82100
const systemCalldata = concat([systemFunctionSelector, readHex(worldCalldata, 4)]);
83101

84-
// Construct args for `callFrom`.
85-
const callFromArgs: typeof writeArgs = {
86-
...writeArgs,
102+
// Call `writeContract` with the new args.
103+
return _writeContract({
104+
...(writeArgs as unknown as WriteContractParameters<worldCallAbi, "callFrom">),
87105
functionName: "callFrom",
88106
args: [params.delegatorAddress, systemId, systemCalldata],
89-
};
90-
91-
// Call `writeContract` with the new args.
92-
return _writeContract(callFromArgs);
107+
});
93108
},
94109
});
95110
}

packages/world/ts/encodeSystemCall.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,6 @@ export function encodeSystemCall<abi extends Abi, functionName extends ContractF
2222
abi,
2323
functionName,
2424
args,
25-
} as unknown as EncodeFunctionDataParameters<abi, functionName>),
25+
} as EncodeFunctionDataParameters<abi, functionName>),
2626
];
2727
}

packages/world/ts/encodeSystemCallFrom.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,6 @@ export function encodeSystemCallFrom<abi extends Abi, functionName extends Contr
2727
abi,
2828
functionName,
2929
args,
30-
} as unknown as EncodeFunctionDataParameters<abi, functionName>),
30+
} as EncodeFunctionDataParameters<abi, functionName>),
3131
];
3232
}

packages/world/ts/encodeSystemCalls.test.ts

+8-4
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,10 @@ describe("SystemCalls", () => {
2121
).toMatchInlineSnapshot(`
2222
[
2323
[
24-
"0x7379000000000000000000000000000000000000000000000000000000000000",
25-
"0x40554c3a746200000000000000000000000000006d795461626c65000000000000000000000000000000000000000000943728592c20aed37a35c15235466f7a7cd00bd0",
24+
{
25+
"callData": "0x40554c3a746200000000000000000000000000006d795461626c65000000000000000000000000000000000000000000943728592c20aed37a35c15235466f7a7cd00bd0",
26+
"systemId": "0x7379000000000000000000000000000000000000000000000000000000000000",
27+
},
2628
],
2729
]
2830
`);
@@ -47,8 +49,10 @@ describe("SystemCalls", () => {
4749
).toMatchInlineSnapshot(`
4850
[
4951
[
50-
"0x7379000000000000000000000000000000000000000000000000000000000000",
51-
"0x0ba51f49746200000000000000000000000000006d795461626c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046b6579310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046b6579310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066669656c6431000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066669656c64320000000000000000000000000000000000000000000000000000",
52+
{
53+
"callData": "0x0ba51f49746200000000000000000000000000006d795461626c6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001a000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000046b6579310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000046b6579310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000066669656c6431000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066669656c64320000000000000000000000000000000000000000000000000000",
54+
"systemId": "0x7379000000000000000000000000000000000000000000000000000000000000",
55+
},
5256
],
5357
]
5458
`);
+13-4
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,21 @@
1-
import { Abi, type ContractFunctionName } from "viem";
2-
import { SystemCall, encodeSystemCall } from "./encodeSystemCall";
1+
import { Abi, EncodeFunctionDataParameters, encodeFunctionData, type ContractFunctionName } from "viem";
2+
import { SystemCall } from "./encodeSystemCall";
33
import type { AbiParametersToPrimitiveTypes, ExtractAbiFunction } from "abitype";
44
import { worldCallAbi } from "./worldCallAbi";
55

66
/** Encode system calls to be passed as arguments into `World.batchCall` */
77
export function encodeSystemCalls<abi extends Abi, functionName extends ContractFunctionName<abi>>(
88
abi: abi,
99
systemCalls: readonly Omit<SystemCall<abi, functionName>, "abi">[],
10-
): AbiParametersToPrimitiveTypes<ExtractAbiFunction<worldCallAbi, "call">["inputs"]>[] {
11-
return systemCalls.map((systemCall) => encodeSystemCall({ ...systemCall, abi } as SystemCall<abi, functionName>));
10+
): AbiParametersToPrimitiveTypes<ExtractAbiFunction<worldCallAbi, "batchCall">["inputs"]> {
11+
return [
12+
systemCalls.map(({ systemId, functionName, args }) => ({
13+
systemId,
14+
callData: encodeFunctionData<abi, functionName>({
15+
abi,
16+
functionName,
17+
args,
18+
} as EncodeFunctionDataParameters<abi, functionName>),
19+
})),
20+
];
1221
}
+14-6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { Abi, Address, type ContractFunctionName } from "viem";
2-
import { SystemCallFrom, encodeSystemCallFrom } from "./encodeSystemCallFrom";
1+
import { Abi, Address, EncodeFunctionDataParameters, encodeFunctionData, type ContractFunctionName } from "viem";
2+
import { SystemCallFrom } from "./encodeSystemCallFrom";
33
import type { AbiParametersToPrimitiveTypes, ExtractAbiFunction } from "abitype";
44
import { worldCallAbi } from "./worldCallAbi";
55

@@ -8,8 +8,16 @@ export function encodeSystemCallsFrom<abi extends Abi, functionName extends Cont
88
abi: abi,
99
from: Address,
1010
systemCalls: readonly Omit<SystemCallFrom<abi, functionName>, "abi" | "from">[],
11-
): AbiParametersToPrimitiveTypes<ExtractAbiFunction<worldCallAbi, "callFrom">["inputs"]>[] {
12-
return systemCalls.map((systemCall) =>
13-
encodeSystemCallFrom({ ...systemCall, abi, from } as SystemCallFrom<abi, functionName>),
14-
);
11+
): AbiParametersToPrimitiveTypes<ExtractAbiFunction<worldCallAbi, "batchCallFrom">["inputs"]> {
12+
return [
13+
systemCalls.map(({ systemId, functionName, args }) => ({
14+
from,
15+
systemId,
16+
callData: encodeFunctionData<abi, functionName>({
17+
abi,
18+
functionName,
19+
args,
20+
} as EncodeFunctionDataParameters<abi, functionName>),
21+
})),
22+
];
1523
}

packages/world/ts/worldCallAbi.ts

+101
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,73 @@
22
// generating full TS files rather than DTS
33

44
export const worldCallAbi = [
5+
{
6+
type: "function",
7+
name: "batchCall",
8+
inputs: [
9+
{
10+
name: "systemCalls",
11+
type: "tuple[]",
12+
internalType: "struct SystemCallData[]",
13+
components: [
14+
{
15+
name: "systemId",
16+
type: "bytes32",
17+
internalType: "ResourceId",
18+
},
19+
{
20+
name: "callData",
21+
type: "bytes",
22+
internalType: "bytes",
23+
},
24+
],
25+
},
26+
],
27+
outputs: [
28+
{
29+
name: "returnDatas",
30+
type: "bytes[]",
31+
internalType: "bytes[]",
32+
},
33+
],
34+
stateMutability: "nonpayable",
35+
},
36+
{
37+
type: "function",
38+
name: "batchCallFrom",
39+
inputs: [
40+
{
41+
name: "systemCalls",
42+
type: "tuple[]",
43+
internalType: "struct SystemCallFromData[]",
44+
components: [
45+
{
46+
name: "from",
47+
type: "address",
48+
internalType: "address",
49+
},
50+
{
51+
name: "systemId",
52+
type: "bytes32",
53+
internalType: "ResourceId",
54+
},
55+
{
56+
name: "callData",
57+
type: "bytes",
58+
internalType: "bytes",
59+
},
60+
],
61+
},
62+
],
63+
outputs: [
64+
{
65+
name: "returnDatas",
66+
type: "bytes[]",
67+
internalType: "bytes[]",
68+
},
69+
],
70+
stateMutability: "nonpayable",
71+
},
572
{
673
type: "function",
774
name: "call",
@@ -55,6 +122,40 @@ export const worldCallAbi = [
55122
],
56123
stateMutability: "payable",
57124
},
125+
{
126+
type: "function",
127+
name: "callWithSignature",
128+
inputs: [
129+
{
130+
name: "signer",
131+
type: "address",
132+
internalType: "address",
133+
},
134+
{
135+
name: "systemId",
136+
type: "bytes32",
137+
internalType: "ResourceId",
138+
},
139+
{
140+
name: "callData",
141+
type: "bytes",
142+
internalType: "bytes",
143+
},
144+
{
145+
name: "signature",
146+
type: "bytes",
147+
internalType: "bytes",
148+
},
149+
],
150+
outputs: [
151+
{
152+
name: "",
153+
type: "bytes",
154+
internalType: "bytes",
155+
},
156+
],
157+
stateMutability: "payable",
158+
},
58159
] as const;
59160

60161
export type worldCallAbi = typeof worldCallAbi;

0 commit comments

Comments
 (0)