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): verify command #2662

Merged
merged 69 commits into from
Apr 25, 2024
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
69 commits
Select commit Hold shift + click to select a range
41adf1d
feat(cli): verify command
yonadaa Apr 15, 2024
8a95d38
feat: testing example on holesky
yonadaa Apr 15, 2024
ed21e92
chore: remove filter
yonadaa Apr 15, 2024
4776836
feat: verify based on folder
yonadaa Apr 15, 2024
20ba4ef
refactor: get contracts
yonadaa Apr 15, 2024
3fe79c5
fix: promise.all
yonadaa Apr 15, 2024
14fa5b6
fix: system
yonadaa Apr 15, 2024
973ef3e
fix: get systems from world config
yonadaa Apr 15, 2024
b7f082b
feat: modules
yonadaa Apr 15, 2024
0974aa4
feat: use cwd
yonadaa Apr 16, 2024
663b738
chore: remove old rpc
yonadaa Apr 16, 2024
38bbb1c
chore: changeset
yonadaa Apr 16, 2024
255add6
refactor: no worldAddress
yonadaa Apr 17, 2024
41783a5
refactor: get world factory contracts
yonadaa Apr 17, 2024
f48f29c
refactor: contract
yonadaa Apr 17, 2024
d63a067
fix: brackets, options
yonadaa Apr 17, 2024
5419673
feat: verify world
yonadaa Apr 17, 2024
1bd2885
refactor: rpc url
yonadaa Apr 17, 2024
005dcc7
worlds.json
yonadaa Apr 17, 2024
436242f
docs: worldaddress
yonadaa Apr 17, 2024
ebe5775
refactor: hardcoded deployer
yonadaa Apr 17, 2024
05dd33a
refactor: require worldaddress
yonadaa Apr 17, 2024
7edceaf
feat: verifier
yonadaa Apr 17, 2024
f2b7b3c
refactor: names
yonadaa Apr 17, 2024
503511a
refactor: getWorldFactoryContracts
yonadaa Apr 17, 2024
3561ff3
refactor: seperate file
yonadaa Apr 17, 2024
49a0b22
docs: change description to mud config
yonadaa Apr 17, 2024
b4a39e4
Merge remote-tracking branch 'origin/main' into yonadaaa/verify-world
yonadaa Apr 18, 2024
f404cce
refactor: move verify contract to seperate file
yonadaa Apr 18, 2024
26dd948
feat: verifier url
yonadaa Apr 18, 2024
1e497a6
refactor: push
yonadaa Apr 18, 2024
1819319
refactor: verificationOptions
yonadaa Apr 18, 2024
9e65089
fix: option
yonadaa Apr 18, 2024
ba67a3b
refactor: use v2 config more
yonadaa Apr 18, 2024
1b0d2b9
rename: config
yonadaa Apr 18, 2024
f186580
chore: comment
yonadaa Apr 18, 2024
e7aacf9
feat: promise.all
yonadaa Apr 18, 2024
29c3bcc
feat: pqueue
yonadaa Apr 18, 2024
4b160a5
reset example
yonadaa Apr 18, 2024
6977d45
refactor: constant
yonadaa Apr 18, 2024
de481cf
fix: type error
yonadaa Apr 18, 2024
3d62480
fix: world address is required
yonadaa Apr 18, 2024
3ef2a30
delete process.exit comment
yonadaa Apr 18, 2024
3d9d25b
refactor: default salt
yonadaa Apr 18, 2024
e90af40
refactor: verify takes union type
yonadaa Apr 18, 2024
0f8147d
refactor
yonadaa Apr 18, 2024
544f7af
Merge remote-tracking branch 'origin/main' into yonadaaa/verify-world
yonadaa Apr 19, 2024
d7f3652
Merge remote-tracking branch 'origin/main' into yonadaaa/verify-world
yonadaa Apr 22, 2024
94833f1
Merge remote-tracking branch 'origin/main' into yonadaaa/verify-world
yonadaa Apr 24, 2024
c7e2ae5
refactor: map to add tasks
yonadaa Apr 24, 2024
bdc1122
Merge remote-tracking branch 'origin/main' into yonadaaa/verify-world
yonadaa Apr 24, 2024
e84d7cc
gm
yonadaa Apr 24, 2024
2ecfec4
feat: use getDeployer
yonadaa Apr 24, 2024
7ffb401
fix: types
yonadaa Apr 24, 2024
12afd82
docs: comment
yonadaa Apr 24, 2024
d613460
refactor: rpc
yonadaa Apr 24, 2024
1165867
chore: prettier
yonadaa Apr 24, 2024
dc0411a
refactor: verify folder
yonadaa Apr 24, 2024
e90e221
refactor: address type
yonadaa Apr 24, 2024
a2a30be
refactor: verify contract takes address
yonadaa Apr 24, 2024
2f8e065
tweak
yonadaa Apr 24, 2024
3f9b08b
feat: figure out if proxy
yonadaa Apr 24, 2024
7ec78cb
docs: add comment on proxy
yonadaa Apr 24, 2024
1eba103
refactor: infer types
yonadaa Apr 24, 2024
2b9f85d
Merge remote-tracking branch 'origin/main' into yonadaaa/verify-world
yonadaa Apr 25, 2024
9099bee
fix: world address should be verified as proxy
yonadaa Apr 25, 2024
a37f051
fix: proxy verifies proxy and implementation
yonadaa Apr 25, 2024
18e24dc
chore: remove unused arg
yonadaa Apr 25, 2024
29b9233
refactor: no public client
yonadaa Apr 25, 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/many-bulldogs-pay.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@latticexyz/cli": patch
---

Added a new `mud verify` command which verifies all contracts in a project. This includes systems, modules, the WorldFactory and World.
1 change: 1 addition & 0 deletions packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"glob": "^8.0.3",
"nice-grpc-web": "^2.0.1",
"openurl": "^1.1.1",
"p-queue": "^7.4.1",
"p-retry": "^5.1.2",
"path": "^0.12.7",
"rxjs": "7.5.5",
Expand Down
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 setVersion from "./set-version";
import test from "./test";
import trace from "./trace";
import devContracts from "./dev-contracts";
import verify from "./verify";

// 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>[] = [
trace,
devContracts,
abiTs,
verify,
];
125 changes: 125 additions & 0 deletions packages/cli/src/commands/verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import type { CommandModule } from "yargs";
import { verify } from "../verify";
import { loadConfig } from "@latticexyz/config/node";
import { World as WorldConfig } from "@latticexyz/world";
import { resolveWorldConfig } from "@latticexyz/world/internal";
import { worldToV1 } from "@latticexyz/world/config/v2";
import { getOutDirectory, getRpcUrl, getSrcDirectory } from "@latticexyz/common/foundry";
import { getExistingContracts } from "../utils/getExistingContracts";
import { getContractData } from "../utils/getContractData";
import { defaultModuleContracts } from "../utils/defaultModuleContracts";
import { Hex, createWalletClient, http } from "viem";
import chalk from "chalk";

type Options = {
deployerAddress?: string;
worldAddress: string;
configPath?: string;
profile?: string;
rpc?: string;
rpcBatch?: boolean;
srcDir?: string;
verifier?: string;
verifierUrl?: string;
useProxy?: boolean;
};
yonadaa marked this conversation as resolved.
Show resolved Hide resolved

const commandModule: CommandModule<Options, Options> = {
command: "verify",

describe: "Verify contracts",

builder(yargs) {
return yargs.options({
deployerAddress: {
type: "string",
desc: "Deploy using an existing deterministic deployer (https://github.com/Arachnid/deterministic-deployment-proxy)",
},
worldAddress: { type: "string", required: true, desc: "Verify an existing World at the given address" },
configPath: { type: "string", desc: "Path to the MUD config file" },
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" },
rpcBatch: {
type: "boolean",
desc: "Enable batch processing of RPC requests in viem client (defaults to batch size of 100 and wait of 1s)",
},
srcDir: { type: "string", desc: "Source directory. Defaults to foundry src directory." },
verifier: { type: "string", desc: "The verifier to use" },
verifierUrl: {
type: "string",
desc: "The verification provider.",
},
holic marked this conversation as resolved.
Show resolved Hide resolved
useProxy: {
type: "boolean",
desc: "Whether the World was deployed with a proxy.",
},
});
},

async handler(opts) {
const profile = opts.profile ?? process.env.FOUNDRY_PROFILE;

const configV2 = (await loadConfig(opts.configPath)) as WorldConfig;
const config = worldToV1(configV2);
holic marked this conversation as resolved.
Show resolved Hide resolved

const srcDir = opts.srcDir ?? (await getSrcDirectory(profile));
yonadaa marked this conversation as resolved.
Show resolved Hide resolved
const outDir = await getOutDirectory(profile);

const rpc = opts.rpc ?? (await getRpcUrl(profile));
console.log(
chalk.bgBlue(
chalk.whiteBright(`\n Verifying MUD contracts${profile ? " with profile " + profile : ""} to RPC ${rpc} \n`),
),
);

const client = createWalletClient({
transport: http(rpc, {
batch: opts.rpcBatch
? {
batchSize: 100,
wait: 1000,
}
: undefined,
}),
});

const contractNames = getExistingContracts(srcDir).map(({ basename }) => basename);
const resolvedWorldConfig = resolveWorldConfig(config, contractNames);

const systems = Object.keys(resolvedWorldConfig.systems).map((name) => {
const contractData = getContractData(`${name}.sol`, name, outDir);

return {
name,
bytecode: contractData.bytecode,
};
});

// Get modules
const modules = config.modules.map((mod) => {
const contractData =
defaultModuleContracts.find((defaultMod) => defaultMod.name === mod.name) ??
getContractData(`${mod.name}.sol`, mod.name, outDir);

return {
name: mod.name,
bytecode: contractData.bytecode,
};
});

await verify({
client,
rpc,
foundryProfile: profile,
systems,
modules,
deployerAddress: opts.deployerAddress as Hex | undefined,
worldAddress: opts.worldAddress as Hex,
verifier: opts.verifier,
verifierUrl: opts.verifierUrl,
useProxy: opts.useProxy,
});
},
};

export default commandModule;
129 changes: 129 additions & 0 deletions packages/cli/src/verify.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import { Chain, Client, Hex, Transport, getCreate2Address } from "viem";
import { getWorldFactoryContracts } from "./deploy/getWorldFactoryContracts";
import { verifyContract } from "./verify/verifyContract";
import PQueue from "p-queue";
import { getWorldProxyFactoryContracts } from "./deploy/getWorldProxyFactoryContracts";
import { getDeployer } from "./deploy/getDeployer";
import { MUDError } from "@latticexyz/common/errors";
import { salt } from "./deploy/common";

type VerifyOptions = {
client: Client<Transport, Chain | undefined>;
rpc: string;
foundryProfile?: string;
verifier?: string;
verifierUrl?: string;
systems: { name: string; bytecode: Hex }[];
modules: { name: string; bytecode: Hex }[];
worldAddress: Hex;
/**
* Address of determinstic deployment proxy: https://github.com/Arachnid/deterministic-deployment-proxy
* By default, we look for a deployment at 0x4e59b44847b379578588920ca78fbf26c0b4956c.
* If it is not deployed or the target chain does not support legacy transactions, the user must set the deployer manually.
*/
deployerAddress?: Hex;
useProxy?: boolean;
holic marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

@holic holic Apr 25, 2024

Choose a reason for hiding this comment

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

still need to remove this CLI arg nevermind, this is a utility arg, not a CLI arg

};

export async function verify({
client,
rpc,
foundryProfile = process.env.FOUNDRY_PROFILE,
systems,
modules,
worldAddress,
deployerAddress: initialDeployerAddress,
verifier,
verifierUrl,
useProxy,
}: VerifyOptions): Promise<void> {
const deployerAddress = initialDeployerAddress ?? (await getDeployer(client));
if (!deployerAddress) {
throw new MUDError(`No deployer`);
}

const verifyQueue = new PQueue({ concurrency: 1 });

systems.map(({ name, bytecode }) =>
verifyQueue.add(() =>
verifyContract(
{
name,
rpc,
verifier,
verifierUrl,
address: getCreate2Address({
from: deployerAddress,
bytecode: bytecode,
salt,
}),
},
{ profile: foundryProfile },
).catch((error) => {
console.error(`Error verifying system contract ${name}:`, error);
}),
),
);

Object.entries(
useProxy ? getWorldProxyFactoryContracts(deployerAddress) : getWorldFactoryContracts(deployerAddress),
).map(([name, { bytecode }]) =>
verifyQueue.add(() =>
verifyContract(
{
name,
rpc,
verifier,
verifierUrl,
address: getCreate2Address({
from: deployerAddress,
bytecode: bytecode,
salt,
}),
},
{
profile: foundryProfile,
cwd: "node_modules/@latticexyz/world",
},
).catch((error) => {
console.error(`Error verifying world factory contract ${name}:`, error);
}),
),
);

modules.map(({ name, bytecode }) =>
verifyQueue.add(() =>
verifyContract(
{
name: name,
rpc,
verifier,
verifierUrl,
address: getCreate2Address({
from: deployerAddress,
bytecode: bytecode,
salt,
}),
},
{
profile: foundryProfile,
cwd: "node_modules/@latticexyz/world-modules",
},
).catch((error) => {
console.error(`Error verifying module contract ${name}:`, error);
}),
),
);
holic marked this conversation as resolved.
Show resolved Hide resolved

verifyQueue.add(() =>
verifyContract(
{ name: "World", rpc, verifier, verifierUrl, address: worldAddress },
{
profile: foundryProfile,
cwd: "node_modules/@latticexyz/world",
},
).catch((error) => {
console.error(`Error verifying World contract:`, error);
}),
);
}
24 changes: 24 additions & 0 deletions packages/cli/src/verify/verifyContract.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { forge } from "@latticexyz/common/foundry";
import { Address } from "viem";

type VerifyContractOptions = {
name: string;
rpc: string;
verifier?: string;
verifierUrl?: string;
address: Address;
};

type ForgeOptions = { profile?: string; silent?: boolean; env?: NodeJS.ProcessEnv; cwd?: string };

export async function verifyContract(options: VerifyContractOptions, forgeOptions?: ForgeOptions) {
const args = ["verify-contract", options.address, options.name, "--rpc-url", options.rpc];

if (options.verifier) {
args.push("--verifier", options.verifier);
}
if (options.verifierUrl) {
args.push("--verifier-url", options.verifierUrl);
}
await forge(args, forgeOptions);
}
3 changes: 2 additions & 1 deletion packages/common/src/foundry/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,13 @@ export async function getRemappings(profile?: string): Promise<[string, string][
*/
export async function forge(
args: string[],
options?: { profile?: string; silent?: boolean; env?: NodeJS.ProcessEnv },
options?: { profile?: string; silent?: boolean; env?: NodeJS.ProcessEnv; cwd?: string },
): Promise<void> {
const execOptions: Options<string> = {
env: { FOUNDRY_PROFILE: options?.profile, ...options?.env },
stdout: "inherit",
stderr: "pipe",
cwd: options?.cwd,
};

await (options?.silent ? execa("forge", args, execOptions) : execLog("forge", args, execOptions));
Expand Down
3 changes: 3 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading