From 31caecc95be72fe94efd1df8cba2b5435fa39bb4 Mon Sep 17 00:00:00 2001 From: Kevin Ingersoll Date: Tue, 27 Aug 2024 17:00:50 +0100 Subject: [PATCH] feat(cli,world): register system ABI onchain (#3050) --- .changeset/ten-foxes-shave.md | 5 ++ .gitignore | 15 +++- e2e/.gitignore | 2 - examples/local-explorer/.gitignore | 7 +- packages/cli/src/build.ts | 4 +- packages/cli/src/deploy/common.ts | 6 +- packages/cli/src/deploy/deploy.ts | 7 +- packages/cli/src/deploy/resolveConfig.ts | 16 +++- .../src/codegen/utils/contractToInterface.ts | 13 ++- packages/common/src/utils/indent.ts | 3 + packages/common/src/utils/index.ts | 1 + packages/world/ts/debug.ts | 5 +- .../world/ts/node/buildSystemsManifest.ts | 84 ++++++++++++++++++ packages/world/ts/node/common.ts | 28 ++++++ packages/world/ts/node/debug.ts | 3 + .../world/ts/node/findContractArtifacts.ts | 85 +++++++++++++++++++ packages/world/ts/node/index.ts | 2 + packages/world/ts/node/loadSystemsManifest.ts | 36 ++++++++ .../world/ts/node/render-solidity/worldgen.ts | 4 +- packages/world/ts/node/resolveSystems.ts | 8 +- packages/world/ts/node/types.ts | 35 ++++++++ templates/phaser/.gitignore | 7 +- templates/react-ecs/.gitignore | 7 +- templates/react/.gitignore | 7 +- templates/threejs/.gitignore | 7 +- templates/vanilla/.gitignore | 7 +- test/mock-game-contracts/.gitignore | 3 +- 27 files changed, 362 insertions(+), 45 deletions(-) create mode 100644 .changeset/ten-foxes-shave.md create mode 100644 packages/common/src/utils/indent.ts create mode 100644 packages/world/ts/node/buildSystemsManifest.ts create mode 100644 packages/world/ts/node/common.ts create mode 100644 packages/world/ts/node/debug.ts create mode 100644 packages/world/ts/node/findContractArtifacts.ts create mode 100644 packages/world/ts/node/loadSystemsManifest.ts create mode 100644 packages/world/ts/node/types.ts diff --git a/.changeset/ten-foxes-shave.md b/.changeset/ten-foxes-shave.md new file mode 100644 index 0000000000..a9261af31e --- /dev/null +++ b/.changeset/ten-foxes-shave.md @@ -0,0 +1,5 @@ +--- +"@latticexyz/cli": patch +--- + +In addition to table labels, system labels and ABIs are now registered onchain during deploy. diff --git a/.gitignore b/.gitignore index 9e381979cd..0a1e02f28a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,15 +11,22 @@ yarn.lock lerna-debug.log yarn-error.log .turbo +.attest +.tstrace + +.env* # We don't want projects created from templates to ignore their lockfiles, but we don't # want to check them in here, so we'll ignore them from the root. templates/*/pnpm-lock.yaml -.env - +# mud test data test-data/world-logs-bulk-*.json test-data/world-logs-query.json -.attest -.tstrace +# mud artifacts +.mud + +# sqlite indexer data +*.db +*.db-journal diff --git a/e2e/.gitignore b/e2e/.gitignore index c7ce05d6b0..3c3629e647 100644 --- a/e2e/.gitignore +++ b/e2e/.gitignore @@ -1,3 +1 @@ node_modules -*.db -*.db-journal diff --git a/examples/local-explorer/.gitignore b/examples/local-explorer/.gitignore index 5eb2fb939a..3737e58258 100644 --- a/examples/local-explorer/.gitignore +++ b/examples/local-explorer/.gitignore @@ -1,2 +1,7 @@ -indexer.db node_modules + +# mud artifacts +.mud +# sqlite indexer data +*.db +*.db-journal diff --git a/packages/cli/src/build.ts b/packages/cli/src/build.ts index 1413aae58c..1e9775c642 100644 --- a/packages/cli/src/build.ts +++ b/packages/cli/src/build.ts @@ -1,5 +1,5 @@ import { tablegen } from "@latticexyz/store/codegen"; -import { worldgen } from "@latticexyz/world/node"; +import { buildSystemsManifest, worldgen } from "@latticexyz/world/node"; import { World as WorldConfig } from "@latticexyz/world"; import { forge } from "@latticexyz/common/foundry"; import { execa } from "execa"; @@ -21,7 +21,7 @@ export async function build({ foundryProfile = process.env.FOUNDRY_PROFILE, }: BuildOptions): Promise { await Promise.all([tablegen({ rootDir, config }), worldgen({ rootDir, config })]); - await forge(["build"], { profile: foundryProfile }); + await buildSystemsManifest({ rootDir, config }); await execa("mud", ["abi-ts"], { stdio: "inherit" }); } diff --git a/packages/cli/src/deploy/common.ts b/packages/cli/src/deploy/common.ts index 08c696eb49..30c762843c 100644 --- a/packages/cli/src/deploy/common.ts +++ b/packages/cli/src/deploy/common.ts @@ -94,12 +94,16 @@ export type System = DeterministicContract & { readonly allowedAddresses: readonly Hex[]; readonly allowedSystemIds: readonly Hex[]; // world registration + // TODO: replace this with system manifest data readonly worldFunctions: readonly WorldFunction[]; + // human readable ABIs to register onchain + readonly abi: readonly string[]; + readonly worldAbi: readonly string[]; }; export type DeployedSystem = Omit< System, - "label" | "namespaceLabel" | "abi" | "prepareDeploy" | "deployedBytecodeSize" | "allowedSystemIds" + "label" | "namespaceLabel" | "abi" | "worldAbi" | "prepareDeploy" | "deployedBytecodeSize" | "allowedSystemIds" > & { address: Address; }; diff --git a/packages/cli/src/deploy/deploy.ts b/packages/cli/src/deploy/deploy.ts index cb50f94e45..e881e3d1c7 100644 --- a/packages/cli/src/deploy/deploy.ts +++ b/packages/cli/src/deploy/deploy.ts @@ -127,11 +127,16 @@ export async function deploy({ }); const tableTags = tables.map(({ tableId: resourceId, label }) => ({ resourceId, tag: "label", value: label })); + const systemTags = systems.flatMap(({ systemId: resourceId, label, abi, worldAbi }) => [ + { resourceId, tag: "label", value: label }, + { resourceId, tag: "abi", value: abi.join("\n") }, + { resourceId, tag: "worldAbi", value: worldAbi.join("\n") }, + ]); const tagTxs = await ensureResourceTags({ client, worldDeploy, - tags: tableTags, + tags: [...tableTags, ...systemTags], valueToHex: stringToHex, }); diff --git a/packages/cli/src/deploy/resolveConfig.ts b/packages/cli/src/deploy/resolveConfig.ts index 9209456f2f..d49b00e95e 100644 --- a/packages/cli/src/deploy/resolveConfig.ts +++ b/packages/cli/src/deploy/resolveConfig.ts @@ -1,5 +1,5 @@ import path from "path"; -import { resolveSystems } from "@latticexyz/world/node"; +import { loadSystemsManifest, resolveSystems } from "@latticexyz/world/node"; import { Library, System, WorldFunction } from "./common"; import { Hex, isHex, toFunctionSelector, toFunctionSignature } from "viem"; import { getContractData } from "../utils/getContractData"; @@ -40,12 +40,21 @@ export async function resolveConfig({ .map(toFunctionSignature); const configSystems = await resolveSystems({ rootDir, config }); + const systemsManifest = await loadSystemsManifest({ rootDir, config }); const systems = configSystems .filter((system) => !system.deploy.disabled) .map((system): System => { + const manifest = systemsManifest.systems.find(({ systemId }) => systemId === system.systemId); + if (!manifest) { + throw new Error( + `System "${system.label}" not found in systems manifest. Run \`mud build\` before trying again.`, + ); + } + const contractData = getContractData(`${system.label}.sol`, system.label, forgeOutDir); + // TODO: replace this with manifest const worldFunctions = system.deploy.registerWorldFunctions ? contractData.abi .filter((item): item is typeof item & { type: "function" } => item.type === "function") @@ -64,7 +73,7 @@ export async function resolveConfig({ }) : []; - // TODO: move to resolveSystems? + // TODO: move to resolveSystems? or system manifest? const allowedAddresses = system.accessList.filter((target): target is Hex => isHex(target)); const allowedSystemIds = system.accessList .filter((target) => !isHex(target)) @@ -80,8 +89,9 @@ export async function resolveConfig({ allowedSystemIds, prepareDeploy: createPrepareDeploy(contractData.bytecode, contractData.placeholders), deployedBytecodeSize: contractData.deployedBytecodeSize, - abi: contractData.abi, worldFunctions, + abi: manifest.abi, + worldAbi: manifest.worldAbi, }; }); diff --git a/packages/common/src/codegen/utils/contractToInterface.ts b/packages/common/src/codegen/utils/contractToInterface.ts index 2e1e83e907..a896404fac 100644 --- a/packages/common/src/codegen/utils/contractToInterface.ts +++ b/packages/common/src/codegen/utils/contractToInterface.ts @@ -27,21 +27,20 @@ interface SymbolImport { /** * Parse the contract data to get the functions necessary to generate an interface, * and symbols to import from the original contract. - * @param data contents of a file with the solidity contract + * @param source contents of a file with the solidity contract * @param contractName name of the contract * @returns interface data */ export function contractToInterface( - data: string, + source: string, contractName: string, ): { functions: ContractInterfaceFunction[]; errors: ContractInterfaceError[]; symbolImports: SymbolImport[]; } { - const ast = parse(data); - - const contractNode = findContractNode(parse(data), contractName); + const ast = parse(source); + const contractNode = findContractNode(ast, contractName); let symbolImports: SymbolImport[] = []; const functions: ContractInterfaceFunction[] = []; const errors: ContractInterfaceError[] = []; @@ -107,8 +106,8 @@ export function contractToInterface( }; } -function findContractNode(ast: SourceUnit, contractName: string): ContractDefinition | undefined { - let contract = undefined; +export function findContractNode(ast: SourceUnit, contractName: string): ContractDefinition | undefined { + let contract: ContractDefinition | undefined = undefined; visit(ast, { ContractDefinition(node) { diff --git a/packages/common/src/utils/indent.ts b/packages/common/src/utils/indent.ts new file mode 100644 index 0000000000..39e691bb8a --- /dev/null +++ b/packages/common/src/utils/indent.ts @@ -0,0 +1,3 @@ +export function indent(message: string, indentation = " "): string { + return message.replaceAll(/(^|\n)/g, `$1${indentation}`); +} diff --git a/packages/common/src/utils/index.ts b/packages/common/src/utils/index.ts index 2e4b585754..699c4cd91f 100644 --- a/packages/common/src/utils/index.ts +++ b/packages/common/src/utils/index.ts @@ -6,6 +6,7 @@ export * from "./chunk"; export * from "./groupBy"; export * from "./identity"; export * from "./includes"; +export * from "./indent"; export * from "./isDefined"; export * from "./isNotNull"; export * from "./iteratorToArray"; diff --git a/packages/world/ts/debug.ts b/packages/world/ts/debug.ts index 7ae5bfce61..6f68f284a0 100644 --- a/packages/world/ts/debug.ts +++ b/packages/world/ts/debug.ts @@ -1,10 +1,7 @@ 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 +export const error = createDebug("mud:world"); error.log = console.error.bind(console); diff --git a/packages/world/ts/node/buildSystemsManifest.ts b/packages/world/ts/node/buildSystemsManifest.ts new file mode 100644 index 0000000000..6bc40771e3 --- /dev/null +++ b/packages/world/ts/node/buildSystemsManifest.ts @@ -0,0 +1,84 @@ +import { mkdir, writeFile } from "node:fs/promises"; +import { ResolvedSystem, resolveSystems } from "./resolveSystems"; +import { World } from "../config/v2"; +import { ContractArtifact, systemsManifestFilename } from "./common"; +import { findContractArtifacts } from "./findContractArtifacts"; +import { getOutDirectory as getForgeOutDirectory } from "@latticexyz/common/foundry"; +import path from "node:path"; +import { Abi, Hex, isHex } from "viem"; +import { formatAbi, formatAbiItem } from "abitype"; +import { debug } from "./debug"; +import { type } from "arktype"; + +export const SystemsManifest = type({ + systems: [ + { + // labels + namespaceLabel: "string", + label: "string", + // resource ID + namespace: "string", + name: "string", + systemId: ["string", ":", (s): s is Hex => isHex(s, { strict: false })], + // abi + abi: "string[]", + worldAbi: "string[]", + }, + "[]", + ], + createdAt: "number", +}); + +export async function buildSystemsManifest(opts: { rootDir: string; config: World }): Promise { + // we have to import these at runtime because they may not yet exist at build time + const { default: IBaseWorldAbi } = await import("../../out/IBaseWorld.sol/IBaseWorld.abi.json"); + const { default: SystemAbi } = await import("../../out/System.sol/System.abi.json"); + const excludedAbi = formatAbi([ + ...IBaseWorldAbi.filter((item) => item.type === "event" || item.type === "error"), + ...SystemAbi, + ] as Abi); + + const systems = await resolveSystems(opts); + + // TODO: expose a `cwd` option to make sure this runs relative to `rootDir` + const forgeOutDir = await getForgeOutDirectory(); + const contractArtifacts = await findContractArtifacts({ forgeOutDir }); + + function getSystemArtifact(system: ResolvedSystem): ContractArtifact { + const artifact = contractArtifacts.find((a) => a.sourcePath === system.sourcePath && a.name === system.label); + if (!artifact) { + throw new Error( + `Could not find build artifact for system \`${system.label}\` at \`${system.sourcePath}\`. Did \`forge build\` run successfully?`, + ); + } + return artifact; + } + + const manifest = { + systems: systems.map((system): (typeof SystemsManifest)["infer"]["systems"][number] => { + const artifact = getSystemArtifact(system); + const abi = artifact.abi.filter((item) => !excludedAbi.includes(formatAbiItem(item))); + const worldAbi = system.deploy.registerWorldFunctions + ? abi.map((item) => (item.type === "function" ? { ...item, name: `${system.namespace}__${item.name}` } : item)) + : []; + return { + // labels + namespaceLabel: system.namespaceLabel, + label: system.label, + // resource ID + namespace: system.namespace, + name: system.name, + systemId: system.systemId, + // abi + abi: formatAbi(abi).sort((a, b) => a.localeCompare(b)), + worldAbi: formatAbi(worldAbi).sort((a, b) => a.localeCompare(b)), + }; + }), + createdAt: Date.now(), + } satisfies typeof SystemsManifest.infer; + + const outFile = path.join(opts.rootDir, systemsManifestFilename); + await mkdir(path.dirname(outFile), { recursive: true }); + await writeFile(outFile, JSON.stringify(manifest, null, 2) + "\n"); + debug("Wrote systems manifest to", systemsManifestFilename); +} diff --git a/packages/world/ts/node/common.ts b/packages/world/ts/node/common.ts new file mode 100644 index 0000000000..2c4c615cbb --- /dev/null +++ b/packages/world/ts/node/common.ts @@ -0,0 +1,28 @@ +import { Abi, Hex } from "viem"; + +// https://eips.ethereum.org/EIPS/eip-170 +export const contractSizeLimit = parseInt("6000", 16); + +// relative to project root dir (`rootDir`) +export const systemsManifestFilename = ".mud/local/systems.json"; + +export type ReferenceIdentifier = { + /** + * Path to source file, e.g. `src/SomeLib.sol` + */ + sourcePath: string; + /** + * Reference name, e.g. `SomeLib` + */ + name: string; +}; + +export type PendingBytecode = readonly (Hex | ReferenceIdentifier)[]; + +export type ContractArtifact = { + readonly sourcePath: string; + readonly name: string; + // TODO: rename `createCode` or `creationBytecode` to better differentiate from deployed bytecode? + readonly bytecode: PendingBytecode; + readonly abi: Abi; +}; diff --git a/packages/world/ts/node/debug.ts b/packages/world/ts/node/debug.ts new file mode 100644 index 0000000000..ab1807cb00 --- /dev/null +++ b/packages/world/ts/node/debug.ts @@ -0,0 +1,3 @@ +import { debug as parentDebug } from "../debug"; + +export const debug = parentDebug.extend("codegen"); diff --git a/packages/world/ts/node/findContractArtifacts.ts b/packages/world/ts/node/findContractArtifacts.ts new file mode 100644 index 0000000000..85b97e1cb0 --- /dev/null +++ b/packages/world/ts/node/findContractArtifacts.ts @@ -0,0 +1,85 @@ +import path from "node:path"; +import { readFile } from "node:fs/promises"; +import { glob } from "glob"; +import { type } from "arktype"; +import { indent, isDefined } from "@latticexyz/common/utils"; +import { Hex, size, sliceHex } from "viem"; +import { ContractArtifact, ReferenceIdentifier, contractSizeLimit } from "./common"; +import { types } from "./types"; +import { debug } from "./debug"; + +export type Input = { + readonly forgeOutDir: string; +}; + +export type Output = readonly ContractArtifact[]; + +const parseArtifact = type("string").pipe.try((s) => JSON.parse(s), types.Artifact); +type Artifact = typeof types.Artifact.infer; + +function hasMetadata( + artifact: Artifact, +): artifact is Artifact & { [k in "metadata"]-?: Exclude } { + return artifact.metadata !== undefined; +} + +export async function findContractArtifacts({ forgeOutDir }: Input): Promise { + const files = ( + await glob("**/*.sol/*.json", { + ignore: ["**/*.abi.json", "**/*.t.sol/*.json", "**/*.s.sol/*.json"], + cwd: forgeOutDir, + }) + ).sort(); + const artifactsJson = await Promise.all( + files.map(async (filename) => ({ + filename, + json: await readFile(path.join(forgeOutDir, filename), "utf8"), + })), + ); + + return artifactsJson + .map(({ filename, json }) => { + const artifact = parseArtifact(json); + if (artifact instanceof type.errors) { + debug(`Skipping invalid artifact at "${filename}":\n${indent(artifact.message)}`); + return; + } + return artifact; + }) + .filter(isDefined) + .filter(hasMetadata) + .map((artifact) => { + artifact; + const sourcePath = Object.keys(artifact.metadata.settings.compilationTarget)[0]; + const name = artifact.metadata.settings.compilationTarget[sourcePath]; + const deployedBytecodeSize = size(artifact.deployedBytecode.object); + + if (deployedBytecodeSize > contractSizeLimit) { + console.warn( + // eslint-disable-next-line max-len + `\nBytecode for \`${name}\` at \`${sourcePath}\` (${deployedBytecodeSize} bytes) is over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`, + ); + } else if (deployedBytecodeSize > contractSizeLimit * 0.95) { + console.warn( + // eslint-disable-next-line max-len + `\nBytecode for \`${name}\` at \`${sourcePath}\` (${deployedBytecodeSize} bytes) is almost over the contract size limit (${contractSizeLimit} bytes). Run \`forge build --sizes\` for more info.\n`, + ); + } + + const bytecode = artifact.bytecode.object; + const placeholders = Object.entries(artifact.bytecode.linkReferences ?? {}).flatMap(([sourcePath, names]) => + Object.entries(names).flatMap(([name, slices]) => slices.map((slice) => ({ sourcePath, name, ...slice }))), + ); + + const pendingBytecode: (Hex | ReferenceIdentifier)[] = []; + let offset = 0; + for (const { sourcePath, name, start, length } of placeholders) { + pendingBytecode.push(sliceHex(bytecode, offset, start)); + pendingBytecode.push({ sourcePath, name }); + offset = start + length; + } + pendingBytecode.push(sliceHex(bytecode, offset)); + + return { sourcePath, name, bytecode: pendingBytecode, abi: artifact.abi }; + }); +} diff --git a/packages/world/ts/node/index.ts b/packages/world/ts/node/index.ts index e73f48effc..bbfa3a8013 100644 --- a/packages/world/ts/node/index.ts +++ b/packages/world/ts/node/index.ts @@ -2,3 +2,5 @@ export * from "./render-solidity"; export * from "./findSolidityFiles"; export * from "./getSystemContracts"; export * from "./resolveSystems"; +export * from "./buildSystemsManifest"; +export * from "./loadSystemsManifest"; diff --git a/packages/world/ts/node/loadSystemsManifest.ts b/packages/world/ts/node/loadSystemsManifest.ts new file mode 100644 index 0000000000..9147e14a7f --- /dev/null +++ b/packages/world/ts/node/loadSystemsManifest.ts @@ -0,0 +1,36 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import { type } from "arktype"; +import { World } from "../config/v2"; +import { systemsManifestFilename } from "./common"; +import { SystemsManifest } from "./buildSystemsManifest"; +import { indent } from "@latticexyz/common/utils"; + +const parseManifest = type("string").pipe.try((s) => JSON.parse(s), SystemsManifest); + +export async function loadSystemsManifest(opts: { + rootDir: string; + // config is optional in case we want to load manifest within a dependency, like store/world packages + config?: World; +}): Promise { + const outFile = path.join(opts.rootDir, systemsManifestFilename); + try { + await fs.access(outFile, fs.constants.F_OK | fs.constants.R_OK); + } catch (error) { + throw new Error( + `Systems manifest at "${systemsManifestFilename}" not found or not readable. Run \`mud build\` before trying again.`, + ); + } + + const json = await fs.readFile(outFile, "utf8"); + const manifest = parseManifest(json); + if (manifest instanceof type.errors) { + throw new Error( + `Invalid systems manifest at "${systemsManifestFilename}". Run \`mud build\` before trying again.\n${indent(manifest.message)}`, + ); + } + + // TODO: validate that the manifest and config agree (if provided) + + return manifest; +} diff --git a/packages/world/ts/node/render-solidity/worldgen.ts b/packages/world/ts/node/render-solidity/worldgen.ts index 097558e681..fcb62bf6c4 100644 --- a/packages/world/ts/node/render-solidity/worldgen.ts +++ b/packages/world/ts/node/render-solidity/worldgen.ts @@ -49,9 +49,9 @@ export async function worldgen({ await Promise.all( systems.map(async (system) => { - const data = await fs.readFile(path.join(rootDir, system.sourcePath), "utf8"); + const source = await fs.readFile(path.join(rootDir, system.sourcePath), "utf8"); // get external functions from a contract - const { functions, errors, symbolImports } = contractToInterface(data, system.label); + const { functions, errors, symbolImports } = contractToInterface(source, system.label); const imports = symbolImports.map( ({ symbol, path: importPath }): ImportDatum => ({ symbol, diff --git a/packages/world/ts/node/resolveSystems.ts b/packages/world/ts/node/resolveSystems.ts index eada4b459f..152c1bb93a 100644 --- a/packages/world/ts/node/resolveSystems.ts +++ b/packages/world/ts/node/resolveSystems.ts @@ -18,13 +18,7 @@ export async function resolveSystems({ const systemContracts = await getSystemContracts({ rootDir, config }); // validate every system in config refers to an existing system contract - const configSystems = Object.values(config.namespaces).flatMap((namespace) => - Object.values(namespace.systems).map((system) => ({ - ...system, - // TODO: remove this once config outputs namespace labels of resources - namespaceLabel: namespace.label, - })), - ); + const configSystems = Object.values(config.namespaces).flatMap((namespace) => Object.values(namespace.systems)); const missingSystems = configSystems.filter( (system) => !systemContracts.some((s) => s.namespaceLabel === system.namespace && s.systemLabel === system.label), ); diff --git a/packages/world/ts/node/types.ts b/packages/world/ts/node/types.ts new file mode 100644 index 0000000000..fd137aa51a --- /dev/null +++ b/packages/world/ts/node/types.ts @@ -0,0 +1,35 @@ +import { scope, type } from "arktype"; +import { Hex, Abi, isHex } from "viem"; + +export const types = scope({ + Bytecode: type("string").narrow( + (input, ctx): input is Hex => isHex(input, { strict: false }) || ctx.mustBe("a hex string"), + ), + Slice: { start: "number", length: "number" }, + LinkReferences: { + // key is source filename like `src/WorldResourceId.sol` + "[string]": { + // key is library name like `WorldResourceIdLib` + "[string]": "Slice[]", + }, + }, + ArtifactBytecode: { + object: "Bytecode", + "linkReferences?": "LinkReferences", + }, + Artifact: { + bytecode: "ArtifactBytecode", + deployedBytecode: "ArtifactBytecode", + // TODO: improve narrowing with `isAbi` or import arktype type from abitype (when either are available) + abi: type("unknown[]").pipe((input) => input as Abi), + "metadata?": { + settings: { + compilationTarget: { + // key is source filename name like `src/WorldResourceId.sol` + // value is contract name like `WorldResourceIdLib` + "[string]": "string", + }, + }, + }, + }, +}).export(); diff --git a/templates/phaser/.gitignore b/templates/phaser/.gitignore index c16a9da0e3..3737e58258 100644 --- a/templates/phaser/.gitignore +++ b/templates/phaser/.gitignore @@ -1,4 +1,7 @@ node_modules -# mud sqlite indexer data -indexer.db +# mud artifacts +.mud +# sqlite indexer data +*.db +*.db-journal diff --git a/templates/react-ecs/.gitignore b/templates/react-ecs/.gitignore index c16a9da0e3..3737e58258 100644 --- a/templates/react-ecs/.gitignore +++ b/templates/react-ecs/.gitignore @@ -1,4 +1,7 @@ node_modules -# mud sqlite indexer data -indexer.db +# mud artifacts +.mud +# sqlite indexer data +*.db +*.db-journal diff --git a/templates/react/.gitignore b/templates/react/.gitignore index c16a9da0e3..3737e58258 100644 --- a/templates/react/.gitignore +++ b/templates/react/.gitignore @@ -1,4 +1,7 @@ node_modules -# mud sqlite indexer data -indexer.db +# mud artifacts +.mud +# sqlite indexer data +*.db +*.db-journal diff --git a/templates/threejs/.gitignore b/templates/threejs/.gitignore index c16a9da0e3..3737e58258 100644 --- a/templates/threejs/.gitignore +++ b/templates/threejs/.gitignore @@ -1,4 +1,7 @@ node_modules -# mud sqlite indexer data -indexer.db +# mud artifacts +.mud +# sqlite indexer data +*.db +*.db-journal diff --git a/templates/vanilla/.gitignore b/templates/vanilla/.gitignore index c16a9da0e3..3737e58258 100644 --- a/templates/vanilla/.gitignore +++ b/templates/vanilla/.gitignore @@ -1,4 +1,7 @@ node_modules -# mud sqlite indexer data -indexer.db +# mud artifacts +.mud +# sqlite indexer data +*.db +*.db-journal diff --git a/test/mock-game-contracts/.gitignore b/test/mock-game-contracts/.gitignore index da6ec0e4dc..af9b4a09ff 100644 --- a/test/mock-game-contracts/.gitignore +++ b/test/mock-game-contracts/.gitignore @@ -5,6 +5,7 @@ bindings/ artifacts/ broadcast/ -# Ignore all MUD deploy artifacts +# mud build artifacts +.mud deploys worlds.json*