From 1b7004b50489351d58adcf7ed2c35e3adcaf4090 Mon Sep 17 00:00:00 2001 From: Karolis Ramanauskas Date: Wed, 7 Aug 2024 17:41:24 +0300 Subject: [PATCH] refactor(cli,world): move helper functions into world package, add getWorldAbi (#3012) --- packages/cli/src/deploy/ensureFunctions.ts | 9 +++-- packages/cli/src/deploy/getSystems.ts | 11 ++++-- packages/world/package.json | 2 ++ packages/world/ts/common.ts | 9 +++++ packages/world/ts/debug.ts | 10 ++++++ packages/world/ts/exports/internal.ts | 3 ++ .../world/ts/functionSignatureToAbiItem.ts | 6 ++++ .../src/deploy => world/ts}/getFunctions.ts | 34 +++++++++++-------- packages/world/ts/getWorldAbi.ts | 27 +++++++++++++++ pnpm-lock.yaml | 25 +++++++++++--- 10 files changed, 112 insertions(+), 24 deletions(-) create mode 100644 packages/world/ts/common.ts create mode 100644 packages/world/ts/debug.ts create mode 100644 packages/world/ts/functionSignatureToAbiItem.ts rename packages/{cli/src/deploy => world/ts}/getFunctions.ts (79%) create mode 100644 packages/world/ts/getWorldAbi.ts diff --git a/packages/cli/src/deploy/ensureFunctions.ts b/packages/cli/src/deploy/ensureFunctions.ts index aaea68a512..3f29bd65ce 100644 --- a/packages/cli/src/deploy/ensureFunctions.ts +++ b/packages/cli/src/deploy/ensureFunctions.ts @@ -1,8 +1,8 @@ import { Client, Transport, Chain, Account, Hex } from "viem"; import { hexToResource, writeContract } from "@latticexyz/common"; +import { getFunctions } from "@latticexyz/world/internal"; import { WorldDeploy, WorldFunction, worldAbi } from "./common"; import { debug } from "./debug"; -import { getFunctions } from "./getFunctions"; import pRetry from "p-retry"; import { wait } from "@latticexyz/common/utils"; @@ -15,7 +15,12 @@ export async function ensureFunctions({ readonly worldDeploy: WorldDeploy; readonly functions: readonly WorldFunction[]; }): Promise { - const worldFunctions = await getFunctions({ client, worldDeploy }); + const worldFunctions = await getFunctions({ + client, + worldAddress: worldDeploy.address, + fromBlock: worldDeploy.deployBlock, + toBlock: worldDeploy.stateBlock, + }); const worldSelectorToFunction = Object.fromEntries(worldFunctions.map((func) => [func.selector, func])); const toSkip = functions.filter((func) => worldSelectorToFunction[func.selector]); diff --git a/packages/cli/src/deploy/getSystems.ts b/packages/cli/src/deploy/getSystems.ts index 70b88132d2..02455408b6 100644 --- a/packages/cli/src/deploy/getSystems.ts +++ b/packages/cli/src/deploy/getSystems.ts @@ -1,10 +1,10 @@ import { DeployedSystem, WorldDeploy } from "./common"; import { Client } from "viem"; -import { getResourceIds } from "./getResourceIds"; import { hexToResource, resourceToLabel } from "@latticexyz/common"; +import { getFunctions } from "@latticexyz/world/internal"; +import { getResourceIds } from "./getResourceIds"; import { getTableValue } from "./getTableValue"; import { debug } from "./debug"; -import { getFunctions } from "./getFunctions"; import { getResourceAccess } from "./getResourceAccess"; import worldConfig from "@latticexyz/world/mud.config"; @@ -17,7 +17,12 @@ export async function getSystems({ }): Promise { const [resourceIds, functions, resourceAccess] = await Promise.all([ getResourceIds({ client, worldDeploy }), - getFunctions({ client, worldDeploy }), + getFunctions({ + client, + worldAddress: worldDeploy.address, + fromBlock: worldDeploy.deployBlock, + toBlock: worldDeploy.stateBlock, + }), getResourceAccess({ client, worldDeploy }), ]); const systems = resourceIds.map(hexToResource).filter((resource) => resource.type === "system"); diff --git a/packages/world/package.json b/packages/world/package.json index 589408d4a2..c5ae128cf6 100644 --- a/packages/world/package.json +++ b/packages/world/package.json @@ -67,11 +67,13 @@ "@latticexyz/store": "workspace:*", "abitype": "1.0.0", "arktype": "1.0.29-alpha", + "debug": "^4.3.4", "viem": "2.9.20" }, "devDependencies": { "@latticexyz/abi-ts": "workspace:*", "@latticexyz/gas-report": "workspace:*", + "@types/debug": "^4.1.7", "@types/ejs": "^3.1.1", "@types/mocha": "^9.1.1", "@types/node": "^18.15.11", diff --git a/packages/world/ts/common.ts b/packages/world/ts/common.ts new file mode 100644 index 0000000000..7769b1bc36 --- /dev/null +++ b/packages/world/ts/common.ts @@ -0,0 +1,9 @@ +import { Hex } from "viem"; + +export type WorldFunction = { + readonly signature: string; + readonly selector: Hex; + readonly systemId: Hex; + readonly systemFunctionSignature: string; + readonly systemFunctionSelector: Hex; +}; diff --git a/packages/world/ts/debug.ts b/packages/world/ts/debug.ts new file mode 100644 index 0000000000..7ae5bfce61 --- /dev/null +++ b/packages/world/ts/debug.ts @@ -0,0 +1,10 @@ +import createDebug from "debug"; + +export const debug = createDebug("mud:world"); +export const error = createDebug("mud:world"); + +// Pipe debug output to stdout instead of stderr +debug.log = console.debug.bind(console); + +// Pipe error output to stderr +error.log = console.error.bind(console); diff --git a/packages/world/ts/exports/internal.ts b/packages/world/ts/exports/internal.ts index 91e9a57d21..9899b7b47a 100644 --- a/packages/world/ts/exports/internal.ts +++ b/packages/world/ts/exports/internal.ts @@ -7,4 +7,7 @@ export * from "../actions/callFrom"; export * from "../callWithSignatureTypes"; +export * from "../getFunctions"; +export * from "../getWorldAbi"; + export { resolveTableId, resolveWithContext } from "../config/v2/dynamicResolution"; diff --git a/packages/world/ts/functionSignatureToAbiItem.ts b/packages/world/ts/functionSignatureToAbiItem.ts new file mode 100644 index 0000000000..e123aeecee --- /dev/null +++ b/packages/world/ts/functionSignatureToAbiItem.ts @@ -0,0 +1,6 @@ +import { AbiItem, parseAbiItem } from "viem"; + +export function functionSignatureToAbiItem(functionSignature: string): AbiItem { + const formattedSignature = `function ${functionSignature}`; + return parseAbiItem(formattedSignature); +} diff --git a/packages/cli/src/deploy/getFunctions.ts b/packages/world/ts/getFunctions.ts similarity index 79% rename from packages/cli/src/deploy/getFunctions.ts rename to packages/world/ts/getFunctions.ts index 19993a7cb1..34dc39363a 100644 --- a/packages/cli/src/deploy/getFunctions.ts +++ b/packages/world/ts/getFunctions.ts @@ -1,5 +1,5 @@ -import { Client, parseAbiItem } from "viem"; -import { WorldDeploy, WorldFunction } from "./common"; +import { Client, parseAbiItem, Address } from "viem"; +import { WorldFunction } from "./common"; import { debug } from "./debug"; import { storeSetRecordEvent } from "@latticexyz/store"; import { getLogs } from "viem/actions"; @@ -10,22 +10,26 @@ import { getSchemaTypes, getValueSchema, } from "@latticexyz/protocol-parser/internal"; -import worldConfig from "@latticexyz/world/mud.config"; +import worldConfig from "../mud.config"; export async function getFunctions({ client, - worldDeploy, + worldAddress, + fromBlock, + toBlock, }: { readonly client: Client; - readonly worldDeploy: WorldDeploy; + readonly worldAddress: Address; + readonly fromBlock: bigint; + readonly toBlock: bigint; }): Promise { // This assumes we only use `FunctionSelectors._set(...)`, which is true as of this writing. - debug("looking up function selectors for", worldDeploy.address); + debug("looking up function selectors for", worldAddress); const selectorLogs = await getLogs(client, { strict: true, - fromBlock: worldDeploy.deployBlock, - toBlock: worldDeploy.stateBlock, - address: worldDeploy.address, + fromBlock, + toBlock, + address: worldAddress, event: parseAbiItem(storeSetRecordEvent), args: { tableId: worldConfig.namespaces.world.tables.FunctionSelectors.tableId }, }); @@ -42,15 +46,15 @@ export async function getFunctions({ ), }; }); - debug("found", selectors.length, "function selectors for", worldDeploy.address); + debug("found", selectors.length, "function selectors for", worldAddress); // This assumes we only use `FunctionSignatures._set(...)`, which is true as of this writing. - debug("looking up function signatures for", worldDeploy.address); + debug("looking up function signatures for", worldAddress); const signatureLogs = await getLogs(client, { strict: true, - fromBlock: worldDeploy.deployBlock, - toBlock: worldDeploy.stateBlock, - address: worldDeploy.address, + fromBlock, + toBlock, + address: worldAddress, event: parseAbiItem(storeSetRecordEvent), args: { tableId: worldConfig.namespaces.world.tables.FunctionSignatures.tableId }, }); @@ -69,7 +73,7 @@ export async function getFunctions({ ]; }), ); - debug("found", signatureLogs.length, "function signatures for", worldDeploy.address); + debug("found", signatureLogs.length, "function signatures for", worldAddress); const functions = selectors.map(({ worldFunctionSelector, systemFunctionSelector, systemId }) => ({ selector: worldFunctionSelector, diff --git a/packages/world/ts/getWorldAbi.ts b/packages/world/ts/getWorldAbi.ts new file mode 100644 index 0000000000..8509782333 --- /dev/null +++ b/packages/world/ts/getWorldAbi.ts @@ -0,0 +1,27 @@ +import { Client, Abi, Address, getAddress } from "viem"; +import IBaseWorldAbi from "../out/IBaseWorld.sol/IBaseWorld.abi.json"; +import { functionSignatureToAbiItem } from "./functionSignatureToAbiItem"; +import { getFunctions } from "./getFunctions"; + +export async function getWorldAbi({ + client, + worldAddress, + fromBlock, + toBlock, +}: { + readonly client: Client; + readonly worldAddress: Address; + readonly fromBlock: bigint; + readonly toBlock: bigint; +}): Promise { + const worldFunctions = await getFunctions({ + client, + worldAddress: getAddress(worldAddress), + fromBlock, + toBlock, + }); + const worldFunctionsAbi = worldFunctions.map((func) => functionSignatureToAbiItem(func.signature)); + const abi = [...IBaseWorldAbi, ...worldFunctionsAbi]; + + return abi; +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 375aa87b0a..cab002d6d7 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -650,10 +650,10 @@ importers: version: 8.3.4 jest: specifier: ^29.3.1 - version: 29.5.0(@types/node@18.15.11) + version: 29.5.0(@types/node@20.12.12) ts-jest: specifier: ^29.0.5 - version: 29.0.5(@babel/core@7.21.4)(@jest/types@29.5.0)(babel-jest@29.5.0(@babel/core@7.21.4))(jest@29.5.0(@types/node@18.15.11))(typescript@5.4.2) + version: 29.0.5(@babel/core@7.21.4)(@jest/types@29.5.0)(babel-jest@29.5.0(@babel/core@7.21.4))(jest@29.5.0(@types/node@20.12.12))(typescript@5.4.2) tsup: specifier: ^6.7.0 version: 6.7.0(postcss@8.4.23)(typescript@5.4.2) @@ -995,10 +995,10 @@ importers: version: 27.4.1 jest: specifier: ^29.3.1 - version: 29.5.0(@types/node@20.12.12) + version: 29.5.0(@types/node@18.15.11) ts-jest: specifier: ^29.0.5 - version: 29.0.5(@babel/core@7.21.4)(@jest/types@29.5.0)(babel-jest@29.5.0(@babel/core@7.21.4))(jest@29.5.0(@types/node@20.12.12))(typescript@5.4.2) + version: 29.0.5(@babel/core@7.21.4)(@jest/types@29.5.0)(babel-jest@29.5.0(@babel/core@7.21.4))(jest@29.5.0(@types/node@18.15.11))(typescript@5.4.2) tsup: specifier: ^6.7.0 version: 6.7.0(postcss@8.4.23)(typescript@5.4.2) @@ -1029,6 +1029,9 @@ importers: arktype: specifier: 1.0.29-alpha version: 1.0.29-alpha + debug: + specifier: ^4.3.4 + version: 4.3.4 viem: specifier: 2.9.20 version: 2.9.20(typescript@5.4.2)(zod@3.23.7) @@ -1039,6 +1042,9 @@ importers: '@latticexyz/gas-report': specifier: workspace:* version: link:../gas-report + '@types/debug': + specifier: ^4.1.7 + version: 4.1.7 '@types/ejs': specifier: ^3.1.1 version: 3.1.1 @@ -2030,6 +2036,7 @@ packages: '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} @@ -2037,6 +2044,7 @@ packages: '@humanwhocodes/object-schema@2.0.2': resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==} + deprecated: Use @eslint/object-schema instead '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} @@ -2753,6 +2761,7 @@ packages: abab@2.0.6: resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==} + deprecated: Use your platform's native atob() and btoa() methods instead abbrev@1.1.1: resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} @@ -2914,6 +2923,7 @@ packages: are-we-there-yet@3.0.1: resolution: {integrity: sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} @@ -3094,6 +3104,7 @@ packages: bun@1.0.11: resolution: {integrity: sha512-cKyQAQOfWNIP511UpQjkABUp7z/5+1ci2kXfhjL9PozHoCaCtnYFtVjeqU1LovpqEP1agAsMiDpGNKbJP89RIw==} + cpu: [arm64, x64] os: [darwin, linux] hasBin: true @@ -3482,6 +3493,7 @@ packages: domexception@4.0.0: resolution: {integrity: sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==} engines: {node: '>=12'} + deprecated: Use your platform's native DOMException instead dotenv@16.0.3: resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==} @@ -4024,6 +4036,7 @@ packages: gauge@4.0.4: resolution: {integrity: sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} @@ -4280,6 +4293,7 @@ packages: inflight@1.0.6: resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. inherits@2.0.3: resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} @@ -5110,6 +5124,7 @@ packages: npmlog@6.0.2: resolution: {integrity: sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + deprecated: This package is no longer supported. number-is-nan@1.0.1: resolution: {integrity: sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==} @@ -5740,10 +5755,12 @@ packages: rimraf@2.6.3: resolution: {integrity: sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true rollup@3.21.8: