Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cli): generate system ABI's from World address #2849

Closed
wants to merge 28 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5d50595
feat(cli): generate system interface from FunctionSignatures
yonadaaa May 21, 2024
a1f03ec
f to func
yonadaaa May 21, 2024
5fcf8d4
refactor: system function sig
yonadaaa May 21, 2024
d3b44ad
fix: parse with regex
yonadaaa May 21, 2024
f2b425e
wip: codegen struct names
yonadaaa May 21, 2024
0032e7b
feat: abigen
yonadaaa May 21, 2024
8aa75a7
fix: reorganise, types
yonadaaa May 21, 2024
b0d0313
refactor: simplify types
yonadaaa May 21, 2024
a4ff4cd
refactor: rename
yonadaaa May 21, 2024
6844e86
refactor: use parseAbiItem
yonadaaa May 22, 2024
b28b723
feat: write abis to file
yonadaaa May 22, 2024
d8fca7e
feat: resourceToLabel
yonadaaa May 22, 2024
b9d2796
feat: world abi
yonadaaa May 22, 2024
f41cfb5
refactor: worldRegisteredFunctions
yonadaaa May 22, 2024
216ff7d
refactor: describe
yonadaaa May 22, 2024
3fe68e1
refactor: move util
yonadaaa May 22, 2024
1b9c25b
refactor: systemToAbi
yonadaaa May 22, 2024
98f34b4
refactor: rename to generate-abi
yonadaaa May 22, 2024
0cbccff
chore: changeset
yonadaaa May 22, 2024
3c59a73
refactor: add helper
yonadaaa May 22, 2024
069f96b
fix: world ABI uses correct signature
yonadaaa May 22, 2024
0f7419f
fix: abi.json
yonadaaa May 22, 2024
b2722bf
feat: abis directory
yonadaaa May 22, 2024
7ba1f43
chore: do not generate system abis
yonadaaa May 22, 2024
a3d4d1d
refactor: constants
yonadaaa May 23, 2024
812c752
refactor: getWorldAbi helper
yonadaaa May 23, 2024
56fa764
fix: use await
yonadaaa Jun 3, 2024
a142476
export getWorldAbi + getWorldDeploy
karooolis Aug 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/breezy-peas-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/cli": patch
---

Added a new `mud generate-abi` command that generates an ABI for a given World by reading the onchain system registrations.
59 changes: 59 additions & 0 deletions packages/cli/src/commands/generate-abi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import type { CommandModule, InferredOptionTypes } from "yargs";
import { Hex, createWalletClient, http } from "viem";
import { getWorldDeploy } from "../deploy/getWorldDeploy";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should consider "lifting" these out of deploy dir if we're sharing them across different commands/utils

Copy link
Contributor Author

@yonadaaa yonadaaa May 23, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup, and I think having separate directories for each command makes less sense with these new tools. We'll be reusing everything from deploy except for the ensureX functions that actually deploy the contracts

import { getRpcUrl } from "@latticexyz/common/foundry";
import fs from "node:fs/promises";
import path from "node:path";
import { mkdirSync } from "node:fs";
import { getWorldAbi } from "../utils/getWorldAbi";

const ABI_DIRECTORY = "abis";
const ABI_FILE = "worldRegisteredFunctions.abi.json";

const generateAbiOptions = {
worldAddress: { type: "string", required: true, desc: "Verify an existing World at the given address" },
profile: { type: "string", desc: "The foundry profile to use" },
rpc: {
type: "string",
desc: "The RPC URL to use. Defaults to the RPC url from the local foundry.toml",
},
} as const;

type Options = InferredOptionTypes<typeof generateAbiOptions>;

const commandModule: CommandModule<Options, Options> = {
command: "generate-abi",

describe: "Generate ABI's for Systems and World based on World address",

builder(yargs) {
return yargs.options(generateAbiOptions);
},

async handler(args) {
await generateAbiHandler(args);
process.exit(0);
},
};

export async function generateAbiHandler(opts: Options) {
Copy link
Member

@holic holic May 22, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thoughts on putting this into a separate dir like packages/cli/src/init or something to prepare for more of these types of files that can eventually be combined into one or more CLI commands?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you mean by "combining" the CLI commands? Like just putting some of this logic in a helper function? I imagine if we have one command that combines the logic of other commands, they all still live in the commands directory but share helpers

const worldAddress = opts.worldAddress as Hex;
const profile = opts.profile ?? process.env.FOUNDRY_PROFILE;
const rpc = opts.rpc ?? (await getRpcUrl(profile));

const client = createWalletClient({
transport: http(rpc),
});

const worldDeploy = await getWorldDeploy(client, worldAddress);

mkdirSync(ABI_DIRECTORY);

// render World ABI
const worldAbi = await getWorldAbi({ client, worldDeploy });

const fullOutputPath = path.join(ABI_DIRECTORY, ABI_FILE);
await fs.writeFile(fullOutputPath, JSON.stringify(worldAbi));
}

export default commandModule;
2 changes: 2 additions & 0 deletions packages/cli/src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import test from "./test";
import trace from "./trace";
import devContracts from "./dev-contracts";
import verify from "./verify";
import generateAbi from "./generate-abi";

// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Each command has different options
export const commands: CommandModule<any, any>[] = [
Expand All @@ -30,4 +31,5 @@ export const commands: CommandModule<any, any>[] = [
devContracts,
abiTs,
verify,
generateAbi,
];
5 changes: 4 additions & 1 deletion packages/cli/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
// nothing to export
import { getWorldAbi } from "./utils/getWorldAbi";
import { getWorldDeploy } from "./deploy/getWorldDeploy";

export { getWorldAbi, getWorldDeploy };
6 changes: 6 additions & 0 deletions packages/cli/src/utils/functionSignatureToAbiItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { AbiItem, parseAbiItem } from "viem";

export function functionSignatureToAbiItem(functionSignature: string): AbiItem {
const formattedSignature = `function ${functionSignature}`;
return parseAbiItem(formattedSignature);
}
21 changes: 21 additions & 0 deletions packages/cli/src/utils/getWorldAbi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Abi } from "abitype";
import { getSystems } from "../deploy/getSystems";
import { functionSignatureToAbiItem } from "./functionSignatureToAbiItem";
import { Client } from "viem";
import { WorldDeploy } from "../deploy/common";

export async function getWorldAbi({
client,
worldDeploy,
}: {
readonly client: Client;
readonly worldDeploy: WorldDeploy;
}): Promise<Abi> {
const systems = await getSystems({ client, worldDeploy });

const worldAbi = systems.flatMap((system) =>
system.functions.map((func) => functionSignatureToAbiItem(func.signature)),
);

return worldAbi;
}
Loading