From 0032e7bdbd2964c551e7d4f73cc248da762d6722 Mon Sep 17 00:00:00 2001 From: Fraser Scott Date: Tue, 21 May 2024 18:26:49 +0100 Subject: [PATCH] feat: abigen --- .../commands/{interfacegen.ts => abigen.ts} | 16 +-- packages/cli/src/commands/index.ts | 4 +- .../{interfacegen.ts => abigen.ts} | 39 +------- .../ts/node/render-solidity/abigenInner.ts | 99 +++++++++++++++++++ .../world/ts/node/render-solidity/index.ts | 2 +- 5 files changed, 113 insertions(+), 47 deletions(-) rename packages/cli/src/commands/{interfacegen.ts => abigen.ts} (77%) rename packages/world/ts/node/render-solidity/{interfacegen.ts => abigen.ts} (53%) create mode 100644 packages/world/ts/node/render-solidity/abigenInner.ts diff --git a/packages/cli/src/commands/interfacegen.ts b/packages/cli/src/commands/abigen.ts similarity index 77% rename from packages/cli/src/commands/interfacegen.ts rename to packages/cli/src/commands/abigen.ts index 112b6a5e34..c226f4ba73 100644 --- a/packages/cli/src/commands/interfacegen.ts +++ b/packages/cli/src/commands/abigen.ts @@ -2,10 +2,10 @@ import type { CommandModule, InferredOptionTypes } from "yargs"; import { Hex, createWalletClient, http } from "viem"; import { getSystems } from "../deploy/getSystems"; import { getWorldDeploy } from "../deploy/getWorldDeploy"; -import { interfacegen } from "@latticexyz/world/node"; +import { abigen } from "@latticexyz/world/node"; import { getRpcUrl } from "@latticexyz/common/foundry"; -const interfacegenOptions = { +const abigenOptions = { worldAddress: { type: "string", required: true, desc: "Verify an existing World at the given address" }, profile: { type: "string", desc: "The foundry profile to use" }, rpc: { @@ -14,24 +14,24 @@ const interfacegenOptions = { }, } as const; -type Options = InferredOptionTypes; +type Options = InferredOptionTypes; const commandModule: CommandModule = { - command: "interfacegen", + command: "abigen", describe: "Autogenerate interfaces for Systems and World based on RPC", builder(yargs) { - return yargs.options(interfacegenOptions); + return yargs.options(abigenOptions); }, async handler(args) { - await interfacegenHandler(args); + await abigenHandler(args); process.exit(0); }, }; -export async function interfacegenHandler(opts: Options) { +export async function abigenHandler(opts: Options) { const worldAddress = opts.worldAddress as Hex; const profile = opts.profile ?? process.env.FOUNDRY_PROFILE; const rpc = opts.rpc ?? (await getRpcUrl(profile)); @@ -45,7 +45,7 @@ export async function interfacegenHandler(opts: Options) { const systems = await getSystems({ client, worldDeploy }); // generate new interfaces - await interfacegen(systems); + await abigen(systems); } export default commandModule; diff --git a/packages/cli/src/commands/index.ts b/packages/cli/src/commands/index.ts index 81ce9a2119..fa403d1427 100644 --- a/packages/cli/src/commands/index.ts +++ b/packages/cli/src/commands/index.ts @@ -14,7 +14,7 @@ import test from "./test"; import trace from "./trace"; import devContracts from "./dev-contracts"; import verify from "./verify"; -import interfacegen from "./interfacegen"; +import abigen from "./abigen"; // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Each command has different options export const commands: CommandModule[] = [ @@ -31,5 +31,5 @@ export const commands: CommandModule[] = [ devContracts, abiTs, verify, - interfacegen, + abigen, ]; diff --git a/packages/world/ts/node/render-solidity/interfacegen.ts b/packages/world/ts/node/render-solidity/abigen.ts similarity index 53% rename from packages/world/ts/node/render-solidity/interfacegen.ts rename to packages/world/ts/node/render-solidity/abigen.ts index 77a0b4708d..c196775da2 100644 --- a/packages/world/ts/node/render-solidity/interfacegen.ts +++ b/packages/world/ts/node/render-solidity/abigen.ts @@ -1,4 +1,4 @@ -import { renderSystemInterface } from "./renderSystemInterface"; +import { abigenInner } from "./abigenInner"; import { Abi, Address, Hex } from "viem"; type DeterministicContract = { @@ -47,39 +47,6 @@ type DeployedSystem = Omit { - const match = func.systemFunctionSignature.match(/^([a-zA-Z_]\w*)\((.*)\)$/); - if (!match) { - throw new Error("Invalid signature"); - } - - const name = match[1]; - const argsString = match[2]; - - const parameters = argsString - .split(",") - .filter((arg) => arg.trim().length > 0) - .map((parameter, i) => (parameter[0] === "(" ? `${name}${i}Struct` : parameter)); - - return { - name, - parameters, - stateMutability: "", - returnParameters: [], - }; - }); - - const systemInterfaceName = `I${system.name}`; - const output = renderSystemInterface({ - name: systemInterfaceName, - functionPrefix: "", - functions, - errors: [], - imports: [], - }); - - console.log(output); - } +export function abigen(systems: readonly DeployedSystem[]) { + return systems.map((system) => abigenInner(system)); } diff --git a/packages/world/ts/node/render-solidity/abigenInner.ts b/packages/world/ts/node/render-solidity/abigenInner.ts new file mode 100644 index 0000000000..af41c54b82 --- /dev/null +++ b/packages/world/ts/node/render-solidity/abigenInner.ts @@ -0,0 +1,99 @@ +import { Abi, AbiItem, Address, Hex } from "viem"; + +type DeterministicContract = { + readonly prepareDeploy: ( + deployer: Address, + libraries: readonly Library[], + ) => { + readonly address: Address; + readonly bytecode: Hex; + }; + readonly deployedBytecodeSize: number; + readonly abi: Abi; +}; + +type Library = DeterministicContract & { + /** + * Path to library source file, e.g. `src/libraries/SomeLib.sol` + */ + path: string; + /** + * Library name, e.g. `SomeLib` + */ + name: string; +}; + +// map over each and decode signature +type WorldFunction = { + readonly signature: string; + readonly selector: Hex; + readonly systemId: Hex; + readonly systemFunctionSignature: string; + readonly systemFunctionSelector: Hex; +}; + +type System = DeterministicContract & { + readonly namespace: string; + readonly name: string; + readonly systemId: Hex; + readonly allowAll: boolean; + readonly allowedAddresses: readonly Hex[]; + readonly allowedSystemIds: readonly Hex[]; + readonly functions: readonly WorldFunction[]; +}; + +type DeployedSystem = Omit & { + address: Address; +}; + +export function abigenInner(system: DeployedSystem): Abi { + const abi = system.functions.map((func) => { + const match = func.systemFunctionSignature.match(/^([a-zA-Z_]\w*)\((.*)\)$/); + if (!match) { + throw new Error("Invalid signature"); + } + + const name = match[1]; + const argsString = match[2]; + + const args = argsString.split(","); + + const item: AbiItem = { + type: "function", + name, + inputs: args.map((type, i) => { + const isTuple = type[0] === "("; + if (!isTuple) { + return { + name: "", + type, + internalType: type, + }; + } else { + return { + name: "", + type: "tuple[]", + internalType: `${name}${i}Struct[]`, + components: type + .replace("(", "") + .replace(")", "") + .split(",") + .map((member, j) => ({ + name: `value${j}`, + type: member, + internalType: member, + })), + }; + } + }), + outputs: [], + stateMutability: "nonpayable", + }; + + console.log(argsString, item); + + return item; + }); + + return abi; +} diff --git a/packages/world/ts/node/render-solidity/index.ts b/packages/world/ts/node/render-solidity/index.ts index c720341662..4eebf4355d 100644 --- a/packages/world/ts/node/render-solidity/index.ts +++ b/packages/world/ts/node/render-solidity/index.ts @@ -2,4 +2,4 @@ export * from "./renderSystemInterface"; export * from "./renderWorldInterface"; export * from "./types"; export * from "./worldgen"; -export * from "./interfacegen"; +export * from "./abigen";