-
Notifications
You must be signed in to change notification settings - Fork 184
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
Changes from all commits
5d50595
a1f03ec
5fcf8d4
d3b44ad
f2b425e
0032e7b
8aa75a7
b0d0313
a4ff4cd
6844e86
b28b723
d8fca7e
b9d2796
f41cfb5
216ff7d
3fe68e1
1b9c25b
98f34b4
0cbccff
3c59a73
069f96b
0f7419f
b2722bf
7ba1f43
a3d4d1d
812c752
56fa764
a142476
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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. |
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"; | ||
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) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thoughts on putting this into a separate dir like There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
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; |
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 }; |
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); | ||
} |
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; | ||
} |
There was a problem hiding this comment.
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/utilsThere was a problem hiding this comment.
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 theensureX
functions that actually deploy the contracts