diff --git a/packages/world/ts/functionSignatureToAbiItem.ts b/packages/world/ts/functionSignatureToAbiItem.ts index e123aeecee..3309c14a78 100644 --- a/packages/world/ts/functionSignatureToAbiItem.ts +++ b/packages/world/ts/functionSignatureToAbiItem.ts @@ -1,6 +1,12 @@ -import { AbiItem, parseAbiItem } from "viem"; +import { AbiFunction, parseAbiItem } from "viem"; -export function functionSignatureToAbiItem(functionSignature: string): AbiItem { +export function functionSignatureToAbiItem(functionSignature: string): AbiFunction { const formattedSignature = `function ${functionSignature}`; - return parseAbiItem(formattedSignature); + const abiItem = parseAbiItem(formattedSignature); + + if (abiItem.type !== "function") { + throw new Error(`Expected function signature, got ${abiItem.type}`); + } + + return abiItem; } diff --git a/packages/world/ts/getWorldAbi.test.ts b/packages/world/ts/getWorldAbi.test.ts new file mode 100644 index 0000000000..6658d33ec8 --- /dev/null +++ b/packages/world/ts/getWorldAbi.test.ts @@ -0,0 +1,51 @@ +import { describe, expect, it, vi } from "vitest"; +import { createTestClient, http } from "viem"; +import { getWorldAbi } from "./getWorldAbi"; +import { foundry } from "viem/chains"; + +vi.mock("./getFunctions", () => { + const mockGetFunctionsResult = [{ signature: "setNumber(bool)" }, { signature: "batchCall((bytes32,bytes)[])" }]; + const getFunctions = vi.fn(); + getFunctions.mockResolvedValue(mockGetFunctionsResult); + + return { + getFunctions, + }; +}); + +describe("World ABI", () => { + it("should concat base and world ABI", async () => { + const client = createTestClient({ + chain: foundry, + mode: "anvil", + transport: http(), + }); + + const abi = await getWorldAbi({ + client, + worldAddress: "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB", + fromBlock: 0n, + toBlock: 0n, + }); + + expect(abi).toContainEqual({ + inputs: [ + { + type: "bool", + }, + ], + name: "setNumber", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }); + + expect(abi).not.toContainEqual({ + name: "batchCall", + type: "function", + stateMutability: "nonpayable", + inputs: [{ type: "tuple[]", components: [{ type: "bytes32" }, { type: "bytes" }] }], + outputs: [], + }); + }); +}); diff --git a/packages/world/ts/getWorldAbi.ts b/packages/world/ts/getWorldAbi.ts index 8509782333..8ac8f1aadb 100644 --- a/packages/world/ts/getWorldAbi.ts +++ b/packages/world/ts/getWorldAbi.ts @@ -1,8 +1,12 @@ -import { Client, Abi, Address, getAddress } from "viem"; +import { Client, Abi, AbiItem, AbiFunction, Address, getAddress, toFunctionSelector } from "viem"; import IBaseWorldAbi from "../out/IBaseWorld.sol/IBaseWorld.abi.json"; import { functionSignatureToAbiItem } from "./functionSignatureToAbiItem"; import { getFunctions } from "./getFunctions"; +function isAbiFunction(abiItem: AbiItem): abiItem is AbiFunction { + return abiItem.type === "function"; +} + export async function getWorldAbi({ client, worldAddress, @@ -20,7 +24,10 @@ export async function getWorldAbi({ fromBlock, toBlock, }); - const worldFunctionsAbi = worldFunctions.map((func) => functionSignatureToAbiItem(func.signature)); + const baseFunctionSelectors = (IBaseWorldAbi as Abi).filter(isAbiFunction).map(toFunctionSelector); + const worldFunctionsAbi = worldFunctions + .map((func) => functionSignatureToAbiItem(func.signature)) + .filter((abiItem) => !baseFunctionSelectors.includes(toFunctionSelector(abiItem))); const abi = [...IBaseWorldAbi, ...worldFunctionsAbi]; return abi;