From 643f4919c98263385f70ededd166245eb5ae9028 Mon Sep 17 00:00:00 2001 From: Daniel Beal Date: Wed, 12 Feb 2025 14:25:18 +1100 Subject: [PATCH 1/2] fix(builder): validation of template accesses which depend on `chainId` or other globals this basically turned into a refactor because we have to kind of rethink how we are supplying values to the ChainDefinition. But I think its a move in the right direction. Unfortunately refactors like this usually lead to breakage, so probably best to save this for next release. --- .../sample-foundry-project/cannonfile.toml | 2 +- examples/sample-foundry-project/package.json | 2 +- .../cannonfile.consumer.toml | 4 +- packages/builder/src/access-recorder.test.ts | 62 ++++--- packages/builder/src/access-recorder.ts | 173 +++++++++--------- packages/builder/src/actions.ts | 10 +- packages/builder/src/definition.ts | 75 ++++---- packages/builder/src/index.ts | 2 +- packages/builder/src/package.ts | 32 ++-- packages/builder/src/steps/clone.test.ts | 3 +- packages/builder/src/steps/clone.ts | 44 +++-- packages/builder/src/steps/deploy.test.ts | 3 +- packages/builder/src/steps/deploy.ts | 52 +++--- packages/builder/src/steps/diamond.ts | 28 +-- packages/builder/src/steps/invoke.test.ts | 17 +- packages/builder/src/steps/invoke.ts | 60 +++--- packages/builder/src/steps/pull.ts | 24 ++- packages/builder/src/steps/router.ts | 12 +- packages/builder/src/steps/var.ts | 8 +- packages/cli/src/commands/build.ts | 78 ++++---- packages/cli/src/custom-steps/run.ts | 12 +- packages/cli/src/helpers.ts | 20 +- .../Deploy/TransactionDetailsPage.tsx | 8 +- .../features/Packages/CannonfileExplorer.tsx | 2 +- .../src/features/Packages/CannonfileGraph.tsx | 9 +- .../Packages/PackageAccordionHelper/index.tsx | 8 +- .../website/src/helpers/chain-definition.ts | 10 +- packages/website/src/hooks/cannon.ts | 7 +- .../src/workers/chain-definition.worker.ts | 6 +- 29 files changed, 415 insertions(+), 358 deletions(-) diff --git a/examples/sample-foundry-project/cannonfile.toml b/examples/sample-foundry-project/cannonfile.toml index 8c14476dc..8471a7d03 100644 --- a/examples/sample-foundry-project/cannonfile.toml +++ b/examples/sample-foundry-project/cannonfile.toml @@ -1,4 +1,4 @@ -name = "greeter-foundry" +name = "greeter" version = "<%= package.version %>" description = "Simple project to verify the functionality of cannon" keywords = ["sample", "greeter"] diff --git a/examples/sample-foundry-project/package.json b/examples/sample-foundry-project/package.json index de9168daa..d6885369c 100644 --- a/examples/sample-foundry-project/package.json +++ b/examples/sample-foundry-project/package.json @@ -1,6 +1,6 @@ { "name": "sample-foundry-project", - "version": "2.11.18", + "version": "2.11.20", "private": true, "scripts": { "test": "forge test", diff --git a/examples/sample-hardhat-project/cannonfile.consumer.toml b/examples/sample-hardhat-project/cannonfile.consumer.toml index 47ec156eb..157438824 100644 --- a/examples/sample-hardhat-project/cannonfile.consumer.toml +++ b/examples/sample-hardhat-project/cannonfile.consumer.toml @@ -15,7 +15,7 @@ options.salt = "second" options.msg = "a message from second greeter set" [invoke.do_change_greeting2] -target = ["greeters.Greeter"] +target = ["greeters.Greeterz"] func = "setGreeting" args = ["<%= settings.change_greeting2 %>"] @@ -40,11 +40,9 @@ args = ["<%= contracts.cloned.address %>", "<%= formatBytes32String('New Greetin var.NewCloneGreeting.event = "NewGreetingAdded" var.NewCloneGreeting.arg = 0 -var.NewCloneGreeting.artifact = "Greeter" var.OldCloneGreeting.event = "OldGreetingRemoved" var.OldCloneGreeting.arg = 0 -var.OldCloneGreeting.artifact = "Greeter" # test to parse through previous emitted event values [invoke.set_new_greeting_for_next_clones] diff --git a/packages/builder/src/access-recorder.test.ts b/packages/builder/src/access-recorder.test.ts index 3c9372f43..3b4a57b02 100644 --- a/packages/builder/src/access-recorder.test.ts +++ b/packages/builder/src/access-recorder.test.ts @@ -1,51 +1,52 @@ -import { computeTemplateAccesses } from './access-recorder'; +import { TemplateContext } from './access-recorder'; describe('access-recorder.ts', () => { - describe('computeTemplateAccesses()', () => { + const templateContext = new TemplateContext({ chainId: 45, timestamp: 0, package: { version: '0.0.0' } }); + describe('TemplateContext.computeAccesses()', () => { it('computes dependency with addition operation', () => { - expect(computeTemplateAccesses('<%= settings.value1 + settings.value2 %>')).toEqual({ + expect(templateContext.computeAccesses('<%= settings.value1 + settings.value2 %>')).toEqual({ accesses: ['settings.value1', 'settings.value2'], unableToCompute: false, }); }); it('computes dependency with addition operation using extras', () => { - expect(computeTemplateAccesses('<%= extras.value1 + extras.value2 %>')).toEqual({ + expect(templateContext.computeAccesses('<%= extras.value1 + extras.value2 %>')).toEqual({ accesses: ['extras.value1', 'extras.value2'], unableToCompute: false, }); }); it('computes dependency with usage of allowed global variables', () => { - expect(computeTemplateAccesses('<%= parseEther(String(0.3)) %>')).toEqual({ + expect(templateContext.computeAccesses('<%= parseEther(String(0.3)) %>')).toEqual({ accesses: [], unableToCompute: false, }); }); it('computes simple addition', () => { - expect(computeTemplateAccesses('<%= 1 + 1 %>')).toEqual({ + expect(templateContext.computeAccesses('<%= 1 + 1 %>')).toEqual({ accesses: [], unableToCompute: false, }); }); it('computes dependency with subtraction operation', () => { - expect(computeTemplateAccesses('<%= settings.value1 - settings.value2 %>')).toEqual({ + expect(templateContext.computeAccesses('<%= settings.value1 - settings.value2 %>')).toEqual({ accesses: ['settings.value1', 'settings.value2'], unableToCompute: false, }); }); it('computes dependency with multiplication operation', () => { - expect(computeTemplateAccesses('<%= settings.value1 * settings.value2 %>')).toEqual({ + expect(templateContext.computeAccesses('<%= settings.value1 * settings.value2 %>')).toEqual({ accesses: ['settings.value1', 'settings.value2'], unableToCompute: false, }); }); it('computes dependency with division operation', () => { - expect(computeTemplateAccesses('<%= settings.value1 / settings.value2 %>')).toEqual({ + expect(templateContext.computeAccesses('<%= settings.value1 / settings.value2 %>')).toEqual({ accesses: ['settings.value1', 'settings.value2'], unableToCompute: false, }); @@ -53,7 +54,7 @@ describe('access-recorder.ts', () => { it('computes dependency with complex math operation', () => { expect( - computeTemplateAccesses('<%= (settings.value1 + settings.value2) * settings.value3 / settings.value4 %>') + templateContext.computeAccesses('<%= (settings.value1 + settings.value2) * settings.value3 / settings.value4 %>') ).toEqual({ accesses: ['settings.value1', 'settings.value2', 'settings.value3', 'settings.value4'], unableToCompute: false, @@ -61,14 +62,14 @@ describe('access-recorder.ts', () => { }); it('computes multiple dependencies on different template tags', () => { - expect(computeTemplateAccesses('<%= settings.woot %>-<%= settings.woot2 %>')).toEqual({ + expect(templateContext.computeAccesses('<%= settings.woot %>-<%= settings.woot2 %>')).toEqual({ accesses: ['settings.woot', 'settings.woot2'], unableToCompute: false, }); }); it('computes simple dependency', () => { - expect(computeTemplateAccesses('<%= settings.woot %>')).toEqual({ + expect(templateContext.computeAccesses('<%= settings.woot %>')).toEqual({ accesses: ['settings.woot'], unableToCompute: false, }); @@ -76,7 +77,7 @@ describe('access-recorder.ts', () => { it('computes array dependency', () => { expect( - computeTemplateAccesses( + templateContext.computeAccesses( '["<%= settings.camelotSwapPublisherAdmin1 %>","<%= settings.camelotSwapPublisherAdmin2 %>"]' ) ).toEqual({ @@ -86,7 +87,7 @@ describe('access-recorder.ts', () => { }); it('computes dependency using simple CannonHelperContext', () => { - expect(computeTemplateAccesses('<%= parseEther(settings.woot) %>')).toEqual({ + expect(templateContext.computeAccesses('<%= parseEther(settings.woot) %>')).toEqual({ accesses: ['settings.woot'], unableToCompute: false, }); @@ -94,7 +95,7 @@ describe('access-recorder.ts', () => { it('computes dependency using complex CannonHelperContext', () => { expect( - computeTemplateAccesses( + templateContext.computeAccesses( '<%= defaultAbiCoder.encode(parseEther(settings.woot)) %> + <%= defaultAbiCoder.decode(contracts.compound) %>' ) ).toEqual({ @@ -102,32 +103,39 @@ describe('access-recorder.ts', () => { unableToCompute: false, }); }); + + it('computes dependency with chainId as a dynamic value search', () => { + expect(templateContext.computeAccesses('<%= settings[`my_${chainId}`] %>')).toEqual({ + accesses: ['settings.my_45'], + unableToCompute: false, + }); + }) }); describe('computeTemplateAccesses() syntax validation', () => { it('handles invalid template syntax - unmatched brackets', () => { - expect(computeTemplateAccesses('<%= settings.value) %>')).toEqual({ + expect(templateContext.computeAccesses('<%= settings.value) %>')).toEqual({ accesses: [], unableToCompute: true, }); }); it('handles empty template tags', () => { - expect(computeTemplateAccesses('<%=%>')).toEqual({ + expect(templateContext.computeAccesses('<%=%>')).toEqual({ accesses: [], unableToCompute: true, }); }); it('handles multiple template tags with mixed validity', () => { - expect(computeTemplateAccesses('<%= settings.valid %> and <% invalid.syntax')).toEqual({ + expect(templateContext.computeAccesses('<%= settings.valid %> and <% invalid.syntax')).toEqual({ accesses: ['settings.valid'], unableToCompute: false, }); }); it('handles template with only whitespace', () => { - expect(computeTemplateAccesses('<%= %>')).toEqual({ + expect(templateContext.computeAccesses('<%= %>')).toEqual({ accesses: [], unableToCompute: true, }); @@ -136,42 +144,42 @@ describe('access-recorder.ts', () => { describe('computeTemplateAccesses() security', () => { it('prevents direct code execution', () => { - expect(computeTemplateAccesses('<%= process.exit(1) %>')).toEqual({ + expect(templateContext.computeAccesses('<%= process.exit(1) %>')).toEqual({ accesses: [], unableToCompute: true, }); }); it('prevents access to global objects', () => { - expect(computeTemplateAccesses('<%= global.process %>')).toEqual({ + expect(templateContext.computeAccesses('<%= global.process %>')).toEqual({ accesses: [], unableToCompute: true, }); }); it('prevents require/import statements', () => { - expect(computeTemplateAccesses('<%= require("fs") %>')).toEqual({ + expect(templateContext.computeAccesses('<%= require("fs") %>')).toEqual({ accesses: [], unableToCompute: true, }); }); it('prevents eval usage', () => { - expect(computeTemplateAccesses('<%= eval("console.log(\'REKT\')") %>')).toEqual({ + expect(templateContext.computeAccesses('<%= eval("console.log(\'REKT\')") %>')).toEqual({ accesses: [], unableToCompute: true, }); }); it('prevents Function constructor usage', () => { - expect(computeTemplateAccesses('<%= new Function("return process")() %>')).toEqual({ + expect(templateContext.computeAccesses('<%= new Function("return process")() %>')).toEqual({ accesses: [], unableToCompute: true, }); }); it('prevents setTimeout/setInterval usage', () => { - expect(computeTemplateAccesses('<%= setTimeout(() => {}, 1000) %>')).toEqual({ + expect(templateContext.computeAccesses('<%= setTimeout(() => {}, 1000) %>')).toEqual({ accesses: [], unableToCompute: true, }); @@ -179,7 +187,7 @@ describe('access-recorder.ts', () => { it('prevents overriding console.log', () => { expect( - computeTemplateAccesses('<%= console.log=function(n){require("fs").writeFileSync("./exploit.log",n)} %>') + templateContext.computeAccesses('<%= console.log=function(n){require("fs").writeFileSync("./exploit.log",n)} %>') ).toEqual({ accesses: [], unableToCompute: true, @@ -187,7 +195,7 @@ describe('access-recorder.ts', () => { }); it('prevents assignment of values', () => { - expect(computeTemplateAccesses('<%= const value = 1 + 2 %>')).toEqual({ + expect(templateContext.computeAccesses('<%= const value = 1 + 2 %>')).toEqual({ accesses: [], unableToCompute: true, }); diff --git a/packages/builder/src/access-recorder.ts b/packages/builder/src/access-recorder.ts index ffa31814f..015a9e10d 100644 --- a/packages/builder/src/access-recorder.ts +++ b/packages/builder/src/access-recorder.ts @@ -7,12 +7,12 @@ import { CannonHelperContext } from './types'; const debug = Debug('cannon:builder:access-recorder'); class ExtendableProxy { - readonly accessed = new Map(); + accessed = new Map(); constructor(defaultValue?: any) { return new Proxy(this, { get: (obj: any, prop: string) => { - if (prop === 'accessed' || prop === 'getAccesses') { + if (prop === 'accessed' || prop === 'getAccesses' || prop === 'clearAccesses') { return obj[prop]; } if (!this.accessed.has(prop)) { @@ -46,56 +46,111 @@ export class AccessRecorder extends ExtendableProxy { ...this.accessed .get(k)! .getAccesses(depth, (cur || 1) + 1) - .map((a) => `${k}.${a}`) + .map((a) => `${k}.${a}`), ); } return acc; } + + clearAccesses(depth: number, cur = 1) { + if (cur == depth) { + return Array.from(this.accessed.keys()); + } + + for (const k in this.accessed.keys()) { + this.accessed.get(k)!.clearAccesses(depth, (cur || 1) + 1); + } + + this.accessed.clear(); + } } export type AccessComputationResult = { accesses: string[]; unableToCompute: boolean }; type AccessRecorderMap = { [k: string]: AccessRecorder }; -type TemplateContext = { +type TemplateContextData = { [k: string]: AccessRecorder | AccessRecorderMap | unknown; }; -/** - * Setup the template context. - * @param possibleNames - The possible names to setup the context for - * @returns The template context - */ -function setupTemplateContext(possibleNames: string[] = []): TemplateContext { - // Create a fake helper context, so the render works but no real functions are called - const fakeHelperContext = _createDeepNoopObject(CannonHelperContext); - - const recorders: TemplateContext = { - // Include base context variables, no access recording as they are always available - chainId: 0, - timestamp: 0, - package: { version: '0.0.0' }, - ...fakeHelperContext, - - // Add access recorders for the base context variables, these are the ones - // used to calculate dependencies beween steps - contracts: new AccessRecorder(), - imports: new AccessRecorder(), - extras: new AccessRecorder(), - txns: new AccessRecorder(), - // For settings, we give it a zeroAddress as a best case scenarion that is going - // to be working for most cases. - // e.g., when calculating a setting value for 'settings.owners.split(',')' or 'settings.someNumber' will work. - settings: new AccessRecorder(viem.zeroAddress), - }; +export class TemplateContext { + readonly possibleNames: string[]; + readonly recorders: TemplateContextData; + + constructor( + overrides: { chainId: number; timestamp: number; package: { version: string } }, + possibleNames: string[] = [], + ) { + // Create a fake helper context, so the render works but no real functions are called + const fakeHelperContext = _createDeepNoopObject(CannonHelperContext); + + this.possibleNames = possibleNames; + this.recorders = { + // Include base context variables, no access recording as they are always available + chainId: overrides.chainId, + timestamp: overrides.timestamp, + package: overrides.package, + ...fakeHelperContext, + + // Add access recorders for the base context variables, these are the ones + // used to calculate dependencies beween steps + contracts: new AccessRecorder(), + imports: new AccessRecorder(), + extras: new AccessRecorder(), + txns: new AccessRecorder(), + // For settings, we give it a zeroAddress as a best case scenarion that is going + // to be working for most cases. + // e.g., when calculating a setting value for 'settings.owners.split(',')' or 'settings.someNumber' will work. + settings: new AccessRecorder(viem.zeroAddress), + }; + + // add possible names + for (const n of possibleNames) { + this.recorders[n] = new AccessRecorder(); + } + } - // add possible names - for (const n of possibleNames) { - recorders[n] = new AccessRecorder(); + computeAccesses(str?: string) { + if (!str) { + return { accesses: [], unableToCompute: false }; + } + + try { + // we give it "true" for safeContext to avoid cloning and freezing of the object + // this is because we want to keep the access recorder, and is not a security risk + // if the user can modify that object + template(str, this.recorders, true); + const accesses = this.collectAccesses(); + return { accesses, unableToCompute: false }; + } catch (err) { + debug('Template execution failed:', err); + return { accesses: [], unableToCompute: true }; + } } - return recorders; + /** + * Collect the accesses from the recorders. + * @param recorders - The recorders to collect accesses from + * @param possibleNames - The possible names to collect accesses from + * @returns The accesses + */ + collectAccesses(): string[] { + const accesses: string[] = []; + + for (const recorder of Object.keys(this.recorders)) { + if (this.recorders[recorder] instanceof AccessRecorder) { + if (this.possibleNames.includes(recorder) && this.recorders[recorder].accessed.size > 0) { + accesses.push(recorder); + } else { + accesses.push(...Array.from(this.recorders[recorder].accessed.keys()).map((a: string) => `${recorder}.${a}`)); + } + this.recorders[recorder].clearAccesses(2); + } + } + + return accesses; + } } export function _createDeepNoopObject(obj: T): T { @@ -114,54 +169,6 @@ export function _createDeepNoopObject(obj: T): T { return obj; } -/** - * Collect the accesses from the recorders. - * @param recorders - The recorders to collect accesses from - * @param possibleNames - The possible names to collect accesses from - * @returns The accesses - */ -function collectAccesses(recorders: TemplateContext, possibleNames: string[]): string[] { - const accesses: string[] = []; - - for (const recorder of Object.keys(recorders)) { - if (recorders[recorder] instanceof AccessRecorder) { - if (possibleNames.includes(recorder) && recorders[recorder].accessed.size > 0) { - accesses.push(recorder); - } else { - accesses.push(...Array.from(recorders[recorder].accessed.keys()).map((a: string) => `${recorder}.${a}`)); - } - } - } - - return accesses; -} - -/** - * Compute the accesses from the template. - * @param str - The template to compute accesses from - * @param possibleNames - The possible names to compute accesses from - * @returns The accesses - */ -export function computeTemplateAccesses(str?: string, possibleNames: string[] = []): AccessComputationResult { - if (!str) { - return { accesses: [], unableToCompute: false }; - } - - const recorders = setupTemplateContext(possibleNames); - - try { - // we give it "true" for safeContext to avoid cloning and freezing of the object - // this is because we want to keep the access recorder, and is not a security risk - // if the user can modify that object - template(str, recorders, true); - const accesses = collectAccesses(recorders, possibleNames); - return { accesses, unableToCompute: false }; - } catch (err) { - debug('Template execution failed:', err); - return { accesses: [], unableToCompute: true }; - } -} - /** * Merge two template access computation results. * @param r1 - The first result diff --git a/packages/builder/src/actions.ts b/packages/builder/src/actions.ts index 6559488b8..d92592ff9 100644 --- a/packages/builder/src/actions.ts +++ b/packages/builder/src/actions.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { AccessComputationResult } from './access-recorder'; +import { AccessComputationResult, TemplateContext } from './access-recorder'; import { handleZodErrors } from './error/zod'; import { ChainBuilderRuntime } from './runtime'; import { chainDefinitionSchema } from './schemas'; @@ -27,13 +27,13 @@ export interface CannonAction { runtime: ChainBuilderRuntime, ctx: ChainBuilderContext, config: Config, - packageState: PackageState + packageState: PackageState, ) => Promise; /** * Returns a list of state keys that this operation consumes (used for dependency inference) */ - getInputs?: (config: Config, possibleFields: string[], packageState: PackageState) => AccessComputationResult; + getInputs?: (config: Config, templateContext: TemplateContext, packageState: PackageState) => AccessComputationResult; /** * Returns a list of state keys this operation produces (used for dependency inference) @@ -44,7 +44,7 @@ export interface CannonAction { runtime: ChainBuilderRuntime, ctx: ChainBuilderContext, config: Config, - packageState: PackageState + packageState: PackageState, ) => Promise; importExisting?: ( @@ -52,7 +52,7 @@ export interface CannonAction { ctx: ChainBuilderContext, config: Config, packageState: PackageState, - existingKeys: string[] + existingKeys: string[], ) => Promise; // Takes in any schema as long as the base type is ZodSchema diff --git a/packages/builder/src/definition.ts b/packages/builder/src/definition.ts index deb17fc08..68f89271c 100644 --- a/packages/builder/src/definition.ts +++ b/packages/builder/src/definition.ts @@ -10,6 +10,7 @@ import { template } from './utils/template'; import { PackageReference } from './package-reference'; import { ZodIssue } from 'zod'; +import { TemplateContext } from '.'; const debug = Debug('cannon:builder:definition'); const debugVerbose = Debug('cannon:verbose:builder:definition'); @@ -61,7 +62,9 @@ export function validatePackageVersion(v: string) { export class ChainDefinition { private raw: RawChainDefinition; private sensitiveDependencies: boolean; + private accessRecordTemplateContext: TemplateContext; + readonly clashes: OutputClashCheckResult[]; readonly allActionNames: string[]; private _roots: Set = new Set(); @@ -75,7 +78,15 @@ export class ChainDefinition { readonly danglingDependencies = new Set<`${string}:${string}`>(); - constructor(def: RawChainDefinition, sensitiveDependencies = false) { + constructor( + def: RawChainDefinition, + sensitiveDependencies = false, + overrides: { chainId: number; timestamp: number; package: { version: string } } = { + chainId: 0, + timestamp: Date.now(), + package: { version: '0.0.0' }, + }, + ) { debug('begin chain def init'); this.raw = def; this.sensitiveDependencies = sensitiveDependencies; @@ -108,6 +119,25 @@ export class ChainDefinition { // do some preindexing this.allActionNames = _.sortBy(actions, _.identity); + // checking for output clashes will also fill out the dependency map + this.clashes = this.checkOutputClash(); + + const possibleFields: string[] = []; + for (const k of this.dependencyFor.keys()) { + const baseName = k.split('.')[0]; + if ( + baseName !== 'contracts' && + baseName !== 'imports' && + baseName !== 'settings' && + baseName !== 'extras' && + baseName !== 'txns' + ) { + possibleFields.push(baseName); + } + } + + this.accessRecordTemplateContext = new TemplateContext(overrides); + debug('finished chain def init'); } @@ -153,7 +183,7 @@ export class ChainDefinition { if (!action) { throw new Error( - `action kind plugin not installed: "${kind}" (for action: "${n}"). please install the plugin necessary to build this package.` + `action kind plugin not installed: "${kind}" (for action: "${n}"). please install the plugin necessary to build this package.`, ); } @@ -173,7 +203,7 @@ export class ChainDefinition { n: string, runtime: ChainBuilderRuntime, ctx: ChainBuilderContext, - tainted: boolean + tainted: boolean, ): Promise { const kind = n.split('.')[0] as keyof typeof ActionKinds; @@ -220,7 +250,7 @@ export class ChainDefinition { source: template(d.source, ctx), chainId: d.chainId || ctx.chainId, preset: d.preset ? template(d.preset, ctx) : 'main', - })) + })), ); } @@ -254,20 +284,7 @@ export class ChainDefinition { } if (ActionKinds[n].getInputs) { - const possibleFields: string[] = []; - for (const k of this.dependencyFor.keys()) { - const baseName = k.split('.')[0]; - if ( - baseName !== 'contracts' && - baseName !== 'imports' && - baseName !== 'settings' && - baseName !== 'extras' && - baseName !== 'txns' - ) { - possibleFields.push(baseName); - } - } - const accessComputationResults = ActionKinds[n].getInputs!(_.get(this.raw, node), possibleFields, { + const accessComputationResults = ActionKinds[n].getInputs!(_.get(this.raw, node), this.accessRecordTemplateContext, { ref: null, currentLabel: node, }); @@ -276,8 +293,8 @@ export class ChainDefinition { if (this.sensitiveDependencies && accessComputationResults.unableToCompute && !_.get(this.raw, node).depends) { throw new Error( `Unable to compute dependencies for [${node}] because of advanced logic in template strings. Specify dependencies manually, like "depends = ['${_.uniq( - _.uniq(accessComputationResults.accesses).map((a) => `${this.dependencyFor.get(a)}`) - ).join("', '")}']"` + _.uniq(accessComputationResults.accesses).map((a) => `${this.dependencyFor.get(a)}`), + ).join("', '")}']"`, ); } @@ -325,13 +342,9 @@ export class ChainDefinition { initializeComputedDependencies = _.memoize(() => { const computeDepsDebug = Debug('cannon:builder:dependencies'); computeDepsDebug('start compute dependencies'); - // checking for output clashes will also fill out the dependency map - const clashes = this.checkOutputClash(); - - computeDepsDebug('finished checking clashes'); - if (clashes.length) { - throw new Error(`cannot generate dependency tree: output clashes exist: ${JSON.stringify(clashes)}`); + if (this.clashes.length) { + throw new Error(`cannot generate dependency tree: output clashes exist: ${JSON.stringify(this.clashes)}`); } // get all dependencies, and filter out the extraneous @@ -356,8 +369,8 @@ export class ChainDefinition { .map((n) => this.getDependencies(n)) .flatten() .uniq() - .value() - ) + .value(), + ), ); if (computeDepsDebug.enabled) { @@ -403,7 +416,7 @@ export class ChainDefinition { const cycle = missing.length ? [] : this.checkCycles(); // TODO: this check seems to be a bit too sensitive atm const extraneousDeps: { node: string; extraneous: string; inDep: string }[] = []; // this.checkExtraneousDependencies(); - const outputClashes = this.checkOutputClash(); + const outputClashes = this.clashes; for (const extDep of extraneousDeps) { const deps = this.resolvedDependencies.get(extDep.node)!; @@ -479,7 +492,7 @@ export class ChainDefinition { checkCycles( actions = this.allActionNames, seenNodes = new Set(), - currentPath = new Set() + currentPath = new Set(), ): string[] | null { // resolved dependencies gets set during dependency computation this.initializeComputedDependencies(); @@ -689,7 +702,7 @@ export class ChainDefinition { } return Math.max( layers[n].actions.length + 2, - _.sumBy(layers[n].depends, (d) => this.getPrintLinesUsed(d, layers)) + _.sumBy(layers[n].depends, (d) => this.getPrintLinesUsed(d, layers)), ); } diff --git a/packages/builder/src/index.ts b/packages/builder/src/index.ts index a462ad431..798b1cbde 100644 --- a/packages/builder/src/index.ts +++ b/packages/builder/src/index.ts @@ -6,7 +6,7 @@ if (!Object.prototype.hasOwnProperty.call(BigInt.prototype, 'toJSON')) { } export { createInitialContext, build, getArtifacts, addOutputsToContext, getOutputs } from './builder'; -export { computeTemplateAccesses, mergeTemplateAccesses } from './access-recorder'; +export { TemplateContext, mergeTemplateAccesses } from './access-recorder'; export { registerAction, ActionKinds } from './actions'; export type { CannonAction, RawChainDefinition } from './actions'; export { ChainDefinition } from './definition'; diff --git a/packages/builder/src/package.ts b/packages/builder/src/package.ts index 5250c7ff6..921573086 100644 --- a/packages/builder/src/package.ts +++ b/packages/builder/src/package.ts @@ -43,7 +43,7 @@ export async function forPackageTree Promise, context?: BundledOutput | null, - onlyResultProvisioned = true + onlyResultProvisioned = true, ): Promise { const results: T[] = []; @@ -58,8 +58,12 @@ export async function forPackageTree, tags: Array, - chainId?: number + chainId?: number, ) { const checkKeyPreset = deployInfo.def.preset || context?.preset || 'main'; @@ -96,7 +100,11 @@ export async function pinIpfs( return alreadyCopiedIpfs.get(checkKey); } - const def = new ChainDefinition(deployInfo.def); + const def = new ChainDefinition(deployInfo.def, false, { + chainId: deployInfo.chainId || 0, + timestamp: deployInfo.timestamp || 0, + package: { version: '0.0.0' }, + }); const pkgChainId = chainId || deployInfo.chainId || 0; @@ -156,7 +164,7 @@ export async function pinIpfs( const returnVal = { packagesNames: _.uniq([def.getVersion(preCtx) || 'latest', ...(context && context.tags ? context.tags : tags)]).map( - (t: string) => `${def.getName(preCtx)}:${t}@${context && context.preset ? context.preset : packageReference.preset}` + (t: string) => `${def.getName(preCtx)}:${t}@${context && context.preset ? context.preset : packageReference.preset}`, ), chainId: pkgChainId, url, @@ -191,13 +199,13 @@ export async function preparePublishPackage({ if (!deployData) { throw new Error( - `could not find deployment artifact for ${packageReference.fullPackageRef} with chain id "${chainId}". Please double check your settings, and rebuild your package.` + `could not find deployment artifact for ${packageReference.fullPackageRef} with chain id "${chainId}". Please double check your settings, and rebuild your package.`, ); } // We call this regardless of includeProvisioned because we want to ALWAYS upload the subpackages ipfs data. const calls: PackagePublishCall[] = (await forPackageTree(fromStorage, deployData, pinPackagesToIpfs)).filter( - (v: any) => !!v + (v: any) => !!v, ); return includeProvisioned ? calls : [_.last(calls)!]; @@ -231,7 +239,7 @@ export async function findUpgradeFromPackage( provider: viem.PublicClient, packageReference: PackageReference, chainId: number, - deployers: viem.Address[] + deployers: viem.Address[], ) { debug('find upgrade from onchain store'); let oldDeployHash: string | null = null; @@ -244,7 +252,7 @@ export async function findUpgradeFromPackage( await storeRead( provider, addr, - viem.keccak256(viem.stringToBytes(`${packageReference.name}@${packageReference.preset}`)) + viem.keccak256(viem.stringToBytes(`${packageReference.name}@${packageReference.preset}`)), ) ).split('_'); @@ -255,7 +263,7 @@ export async function findUpgradeFromPackage( } catch (err) { debug('failure while trying to read from onchain store', err); } - }) + }), ); if (!oldDeployHash) { @@ -272,6 +280,6 @@ export async function writeUpgradeFromInfo(runtime: ChainBuilderRuntime, package runtime.provider, await runtime.getDefaultSigner({}), viem.keccak256(viem.stringToBytes(`${packageRef.name}@${packageRef.preset}`)), - `${Math.floor(Date.now() / 1000)}_${deployUrl}` + `${Math.floor(Date.now() / 1000)}_${deployUrl}`, ); } diff --git a/packages/builder/src/steps/clone.test.ts b/packages/builder/src/steps/clone.test.ts index 31b0064c4..85f1d3daf 100644 --- a/packages/builder/src/steps/clone.test.ts +++ b/packages/builder/src/steps/clone.test.ts @@ -5,6 +5,7 @@ import action from './clone'; import deployAction from './deploy'; import { fakeCtx, fakeRuntime } from './utils.test.helper'; import { PackageReference } from '../package-reference'; +import { TemplateContext } from '..'; jest.mock('../loader'); jest.mock('./deploy'); @@ -60,7 +61,7 @@ describe('steps/clone.ts', () => { var: { woot: '<%= settings.b %>', wah: '<%= settings.c %>' }, options: { woot: '<%= settings.d %>', wah: '<%= settings.e %>', tags: '<%= settings.f %>' }, }, - [] + new TemplateContext({ chainId: 0, timestamp: 0, package: { version: '0.0.0.0' } }) ) ).toEqual({ accesses: ['settings.a', 'settings.b', 'settings.c', 'settings.d', 'settings.e', 'settings.f'], diff --git a/packages/builder/src/steps/clone.ts b/packages/builder/src/steps/clone.ts index 42ba9acce..f59dccc8d 100644 --- a/packages/builder/src/steps/clone.ts +++ b/packages/builder/src/steps/clone.ts @@ -3,7 +3,7 @@ import Debug from 'debug'; import _ from 'lodash'; import { z } from 'zod'; import pkg from '../../package.json'; -import { computeTemplateAccesses, mergeTemplateAccesses } from '../access-recorder'; +import { mergeTemplateAccesses } from '../access-recorder'; import { build, createInitialContext, getOutputs } from '../builder'; import { CANNON_CHAIN_ID } from '../constants'; import { ChainDefinition } from '../definition'; @@ -77,28 +77,22 @@ const cloneSpec = { return config; }, - getInputs(config, possibleFields) { - let accesses = computeTemplateAccesses(config.source); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.target, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.sourcePreset, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.targetPreset, possibleFields)); + getInputs(config, templateContext) { + let accesses = templateContext.computeAccesses(config.source); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.target)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.sourcePreset)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.targetPreset)); if (config.var) { - _.forEach(config.var, (a) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(a, possibleFields)))); + _.forEach(config.var, (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(a)))); } if (config.options) { - _.forEach( - config.options, - (a) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(a, possibleFields))) - ); + _.forEach(config.options, (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(a)))); } if (config.tags) { - _.forEach( - config.tags, - (a) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(a, possibleFields))) - ); + _.forEach(config.tags, (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(a)))); } return accesses; @@ -124,7 +118,7 @@ const cloneSpec = { runtime.emit( Events.Notice, packageState.currentLabel, - 'To prevent unexpected upgrades, it is strongly recommended to lock the version of the source package by specifying a version in the `source` field.' + 'To prevent unexpected upgrades, it is strongly recommended to lock the version of the source package by specifying a version in the `source` field.', ); } @@ -132,7 +126,7 @@ const cloneSpec = { runtime.emit( Events.Notice, packageState.currentLabel, - `Deploying cloned package to default preset ${targetRef.preset}` + `Deploying cloned package to default preset ${targetRef.preset}`, ); } @@ -142,7 +136,7 @@ const cloneSpec = { throw new Error( `deployment not found: ${source}. please make sure it exists for preset ${ sourcePreset || sourceRef.preset - } and network ${chainId}.` + } and network ${chainId}.`, ); } @@ -155,7 +149,11 @@ const cloneSpec = { deployInfo.def.version = targetRef.version; deployInfo.def.preset = targetRef.preset; - const def = new ChainDefinition(deployInfo.def); + const def = new ChainDefinition(deployInfo.def, false, { + chainId, + timestamp: Date.now(), + package: { version: '0.0.0' }, + }); // always treat upstream state as what is used if its available. otherwise, we might have a state from a previous upgrade. // if all else fails, we can load from scratch (aka this is first deployment) @@ -176,8 +174,8 @@ const cloneSpec = { debug( `[clone.${importLabel}]`, yellow( - 'There is a pre-existing deployment for this preset and chain id. This build will overwrite. Did you mean `import`?' - ) + 'There is a pre-existing deployment for this preset and chain id. This build will overwrite. Did you mean `import`?', + ), ); } @@ -222,7 +220,7 @@ const cloneSpec = { debug( `[clone.${importLabel}]`, 'built state is exactly equal to previous state. skip generation of new deploy url', - importLabel + importLabel, ); return { imports: { @@ -262,7 +260,7 @@ const cloneSpec = { [target, ...(config.tags || ['latest']).map((t) => config.source.split(':')[0] + ':' + t)], runtime.chainId, newSubDeployUrl, - (await runtime.registry.getMetaUrl(source, chainId)) || '' + (await runtime.registry.getMetaUrl(source, chainId)) || '', ); } diff --git a/packages/builder/src/steps/deploy.test.ts b/packages/builder/src/steps/deploy.test.ts index 5b30e6fdc..2af1db516 100644 --- a/packages/builder/src/steps/deploy.test.ts +++ b/packages/builder/src/steps/deploy.test.ts @@ -5,6 +5,7 @@ import { ContractArtifact } from '../types'; import action from './deploy'; import { fakeCtx, fakeRuntime, makeFakeSigner } from './utils.test.helper'; import { PackageReference } from '../package-reference'; +import { TemplateContext } from '..'; const DEFAULT_ARACHNID_ADDRESS = '0x4e59b44847b379578588920cA78FbF26c0B4956C'; @@ -159,7 +160,7 @@ describe('steps/deploy.ts', () => { args: ['<%= contracts.h %>', '<%= contracts.i %>'], salt: '<%= contracts.j %>', }, - [] + new TemplateContext({ chainId: 0, timestamp: 0, package: { version: '0.0.0' }}) ) .accesses.sort() ).toEqual([ diff --git a/packages/builder/src/steps/deploy.ts b/packages/builder/src/steps/deploy.ts index 15eeb4c7d..239225185 100644 --- a/packages/builder/src/steps/deploy.ts +++ b/packages/builder/src/steps/deploy.ts @@ -2,7 +2,7 @@ import Debug from 'debug'; import _ from 'lodash'; import * as viem from 'viem'; import { z } from 'zod'; -import { computeTemplateAccesses, mergeTemplateAccesses } from '../access-recorder'; +import { mergeTemplateAccesses } from '../access-recorder'; import { ARACHNID_DEFAULT_DEPLOY_ADDR, ensureArachnidCreate2Exists, makeArachnidCreate2Txn } from '../create2'; import { CannonError, handleTxnError } from '../error'; import { deploySchema } from '../schemas'; @@ -28,7 +28,7 @@ export interface ContractOutputs { function resolveBytecode( artifactData: ContractArtifact, - config: Config + config: Config, ): [viem.Hex, { [sourceName: string]: { [libName: string]: string } }] { let injectedBytecode = artifactData.bytecode; const linkedLibraries: { [sourceName: string]: { [libName: string]: string } } = {}; @@ -70,7 +70,7 @@ function checkConstructorArgs(abi: viem.Abi, args: any[] | undefined) { if (suppliedArgs.length !== neededArgs.length) { throw new Error( - `incorrect number of constructor arguments to deploy contract. supplied: ${suppliedArgs.length}, expected: ${neededArgs.length}` + `incorrect number of constructor arguments to deploy contract. supplied: ${suppliedArgs.length}, expected: ${neededArgs.length}`, ); } } @@ -82,7 +82,7 @@ function generateOutputs( deployTxn: viem.TransactionReceipt | null, deployTxnBlock: viem.Block | null, deployAddress: viem.Address, - currentLabel: string + currentLabel: string, ): ChainArtifacts { const [, linkedLibraries] = resolveBytecode(artifactData, config); @@ -195,37 +195,31 @@ const deploySpec = { return config; }, - getInputs(config, possibleFields) { - let accesses = computeTemplateAccesses(config.from); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.nonce, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.artifact, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.value, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.abi, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.salt, possibleFields)); + getInputs(config, templateContext) { + let accesses = templateContext.computeAccesses(config.from); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.nonce)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.artifact)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.value)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.abi)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.salt)); if (config.abiOf) { - _.forEach( - config.abiOf, - (v) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(v, possibleFields))) - ); + _.forEach(config.abiOf, (v) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(v)))); } if (config.args) { _.forEach( config.args, - (v) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(JSON.stringify(v), possibleFields))) + (v) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(JSON.stringify(v)))), ); } if (config.libraries) { - _.forEach( - config.libraries, - (v) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(v, possibleFields))) - ); + _.forEach(config.libraries, (v) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(v)))); } if (config?.overrides?.gasLimit) { - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.overrides.gasLimit, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.overrides.gasLimit)); } return accesses; @@ -250,7 +244,7 @@ const deploySpec = { if (!artifactData) { throw new Error( - `bytecode/abi for artifact ${config.artifact} not found. please double check the contract name and your build configuration` + `bytecode/abi for artifact ${config.artifact} not found. please double check the contract name and your build configuration`, ); } @@ -292,7 +286,7 @@ const deploySpec = { if (config.create2) { const arachnidDeployerAddress = await ensureArachnidCreate2Exists( runtime, - typeof config.create2 === 'string' ? (config.create2 as viem.Address) : ARACHNID_DEFAULT_DEPLOY_ADDR + typeof config.create2 === 'string' ? (config.create2 as viem.Address) : ARACHNID_DEFAULT_DEPLOY_ADDR, ); debug('performing arachnid create2'); @@ -309,7 +303,7 @@ const deploySpec = { if (config.ifExists !== 'continue') { throw new CannonError( `The contract at the create2 destination ${addr} is already deployed, but the Cannon state does not recognize that this contract has already been deployed. This typically indicates incorrect upgrade configuration. Please confirm if this contract should already be deployed or not, and if you want to continue the build as-is, add 'ifExists = "continue"' to the step definition`, - 'CREATE2_COLLISION' + 'CREATE2_COLLISION', ); } } else { @@ -363,15 +357,15 @@ const deploySpec = { // if the code goes here, it means that the Create2 deployment failed // and prepareTransactionRequest will throw an error with the underlying revert message await runtime.provider.prepareTransactionRequest( - _.assign(txn, overrides, { account: signer.wallet.account || signer.address }) + _.assign(txn, overrides, { account: signer.wallet.account || signer.address }), ); throw new Error( - 'The CREATE2 contract seems to be failing in the constructor. However, we were not able to get a stack trace.' + 'The CREATE2 contract seems to be failing in the constructor. However, we were not able to get a stack trace.', ); } else { const preparedTxn = await runtime.provider.prepareTransactionRequest( - _.assign(txn, overrides, { account: signer.wallet.account || signer.address }) + _.assign(txn, overrides, { account: signer.wallet.account || signer.address }), ); const hash = await signer.wallet.sendTransaction(preparedTxn as any); @@ -407,7 +401,7 @@ const deploySpec = { null, // note: send zero address since there is no contract address viem.zeroAddress, - packageState.currentLabel + packageState.currentLabel, ); return await handleTxnError(contractArtifact, runtime.provider, error); @@ -422,7 +416,7 @@ const deploySpec = { async importExisting(runtime, ctx, config, packageState, existingKeys) { if (existingKeys.length != 1) { throw new Error( - 'a contract can only be deployed on one transaction, so you can only supply one hash transaction to import' + 'a contract can only be deployed on one transaction, so you can only supply one hash transaction to import', ); } diff --git a/packages/builder/src/steps/diamond.ts b/packages/builder/src/steps/diamond.ts index 8e8ad8aa2..5aacda28d 100644 --- a/packages/builder/src/steps/diamond.ts +++ b/packages/builder/src/steps/diamond.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; import * as viem from 'viem'; import { z } from 'zod'; import { ARACHNID_DEFAULT_DEPLOY_ADDR, ensureArachnidCreate2Exists, makeArachnidCreate2Txn } from '../create2'; -import { computeTemplateAccesses, mergeTemplateAccesses } from '../access-recorder'; +import { mergeTemplateAccesses } from '../access-recorder'; import { ChainBuilderRuntime } from '../runtime'; import { diamondSchema } from '../schemas'; import { ContractArtifact, ContractMap, PackageState } from '../types'; @@ -94,23 +94,23 @@ const diamondStep = { return config; }, - getInputs(config, possibleFields) { - let accesses = computeTemplateAccesses(config.diamondArgs.owner, possibleFields); + getInputs(config, templateContext) { + let accesses = templateContext.computeAccesses(config.diamondArgs.owner); if (config.diamondArgs.init) { - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.diamondArgs.init, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.diamondArgs.init)); } if (config.diamondArgs.initCalldata) { - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.diamondArgs.initCalldata, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.diamondArgs.initCalldata)); } - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.salt, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.salt)); accesses.accesses.push( - ...config.contracts.map((c) => (c.includes('.') ? `imports.${c.split('.')[0]}` : `contracts.${c}`)) + ...config.contracts.map((c) => (c.includes('.') ? `imports.${c.split('.')[0]}` : `contracts.${c}`)), ); if (config?.overrides) { - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.overrides.gasLimit, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.overrides.gasLimit)); } return accesses; @@ -225,7 +225,7 @@ const diamondStep = { }; } catch (err) { throw new Error( - `failed to cut (upgrade) the diamond which is already deployed. This could happen for a few reasons:\n* the diamond owner has been changed and is now incorrect.\n* the diamond was previously made immutable and can no longer can be upgraded.\noriginal error: ${err}` + `failed to cut (upgrade) the diamond which is already deployed. This could happen for a few reasons:\n* the diamond owner has been changed and is now incorrect.\n* the diamond was previously made immutable and can no longer can be upgraded.\noriginal error: ${err}`, ); } }, @@ -234,13 +234,13 @@ const diamondStep = { async function firstTimeDeploy( runtime: ChainBuilderRuntime, config: Config, - packageState: PackageState + packageState: PackageState, ): Promise { const stepName = packageState.currentLabel.split('.')[1]; const signer = await runtime.getDefaultSigner( { data: viem.keccak256(viem.encodePacked(['string'], [config.salt])) as viem.Hex }, - config.salt + config.salt, ); debug('using deploy signer with address', signer.address); @@ -253,7 +253,7 @@ async function firstTimeDeploy( contract: ContractArtifact, deployedContractLabel: string, constructorArgs: any[], - salt = '' + salt = '', ) { debug('deploy contract', contract.contractName, deployedContractLabel, constructorArgs, salt); runtime.reportContractArtifact(`${contract.sourceName}:${contract.contractName}`, { @@ -299,7 +299,7 @@ async function firstTimeDeploy( if (!bytecode) { const hash = await signer.wallet.sendTransaction( - _.assign({ account: signer.wallet.account || signer.address }, create2Txn as any) + _.assign({ account: signer.wallet.account || signer.address }, create2Txn as any), ); const receipt = await runtime.provider.waitForTransactionReceipt({ hash }); const block = await runtime.provider.getBlock({ blockHash: receipt.blockHash }); @@ -358,7 +358,7 @@ async function firstTimeDeploy( (await import('../abis/diamond/Diamond.json')) as any, stepName, [addFacets, config.diamondArgs], - config.salt || '' + config.salt || '', ); return outputContracts; diff --git a/packages/builder/src/steps/invoke.test.ts b/packages/builder/src/steps/invoke.test.ts index c3d2e0402..1e9343766 100644 --- a/packages/builder/src/steps/invoke.test.ts +++ b/packages/builder/src/steps/invoke.test.ts @@ -3,6 +3,7 @@ import { validateConfig } from '../actions'; import action from './invoke'; import { fakeCtx, fakeRuntime } from './utils.test.helper'; import { PackageReference } from '../package-reference'; +import { TemplateContext } from '..'; describe('steps/invoke.ts', () => { const fakeContractInfo = { @@ -66,7 +67,7 @@ describe('steps/invoke.ts', () => { it('fails when setting invalid value', () => { expect(() => validateConfig(action.validate, { target: 'owner()', invalid: ['something'] })).toThrow( - "Unrecognized key(s) in object: 'invalid'" + "Unrecognized key(s) in object: 'invalid'", ); }); }); @@ -110,7 +111,7 @@ describe('steps/invoke.ts', () => { target: ['Woot'], func: 'something', }, - { ref: null, currentLabel: 'invoke.Invoke' } + { ref: null, currentLabel: 'invoke.Invoke' }, ); expect(result).toContainEqual({ @@ -131,7 +132,7 @@ describe('steps/invoke.ts', () => { args: ['split', { wave: 'form' }], value: '1234', }, - { ref: null, currentLabel: 'invoke.Invoke' } + { ref: null, currentLabel: 'invoke.Invoke' }, ); expect(result).toContainEqual({ @@ -158,9 +159,9 @@ describe('steps/invoke.ts', () => { args: ['<%= contracts.h %>', '<%= contracts.i %>'], overrides: { gasLimit: '<%= contracts.j %>' }, }, - [] + new TemplateContext({ chainId: 0, timestamp: 0, package: { version: '0.0.0' } }), ) - .accesses.sort() + .accesses.sort(), ).toEqual([ 'contracts.a', 'contracts.b', @@ -186,8 +187,8 @@ describe('steps/invoke.ts', () => { factory: { something: { event: 'whoop', arg: 0 } }, extra: { else: { event: 'arst', arg: 1 } }, }, - { ref: new PackageReference('fun:1.0.0'), currentLabel: 'invoke.Hello' } - ) + { ref: new PackageReference('fun:1.0.0'), currentLabel: 'invoke.Hello' }, + ), ).toEqual(['txns.Hello', 'contracts.something', 'something', 'settings.else', 'extras.else']); }); }); @@ -239,7 +240,7 @@ describe('steps/invoke.ts', () => { }, }, }, - { ref: new PackageReference('fun:1.0.0'), currentLabel: 'invoke.something' } + { ref: new PackageReference('fun:1.0.0'), currentLabel: 'invoke.something' }, ); expect(result.contracts).toStrictEqual({ diff --git a/packages/builder/src/steps/invoke.ts b/packages/builder/src/steps/invoke.ts index 81e594592..d9b63707b 100644 --- a/packages/builder/src/steps/invoke.ts +++ b/packages/builder/src/steps/invoke.ts @@ -2,7 +2,7 @@ import Debug from 'debug'; import _ from 'lodash'; import * as viem from 'viem'; import { z } from 'zod'; -import { computeTemplateAccesses, mergeTemplateAccesses } from '../access-recorder'; +import { mergeTemplateAccesses } from '../access-recorder'; import { invokeSchema } from '../schemas'; import { CannonSigner, @@ -59,7 +59,7 @@ async function runTxn( config: Config, contract: Contract, signer: CannonSigner, - packageState: PackageState + packageState: PackageState, ): Promise<[viem.TransactionReceipt, EncodedTxnEvents]> { let txn: viem.Hash; @@ -68,7 +68,7 @@ async function runTxn( // if invoke calls succeeding when no action was actually performed. if ((await runtime.provider.getCode({ address: contract.address })) === '0x') { throw new Error( - `contract ${contract.address} for ${packageState.currentLabel} has no bytecode. This is most likely a missing dependency or bad state.` + `contract ${contract.address} for ${packageState.currentLabel} has no bytecode. This is most likely a missing dependency or bad state.`, ); } @@ -97,7 +97,7 @@ async function runTxn( // Attempt to encode data so that if any arguments have any type mismatches, we can catch them and present them to the user. const functionList = assembleFunctionSignatures(contract.abi); const neededFuncAbi = functionList.find( - (f) => config.func == f[1] || config.func == f[1].split('(')[0] + (f) => config.func == f[1] || config.func == f[1].split('(')[0], )?.[0] as viem.AbiFunction; if (!neededFuncAbi) { throw new Error( @@ -106,8 +106,8 @@ async function runTxn( }". List of recognized functions is:\n${functionList .map((v) => v[1]) .join( - '\n' - )}\n\nIf this is a proxy contract, make sure you've specified abiOf for the contract action in the cannonfile that deploys it. If you’re calling an overloaded function, update func to include parentheses.` + '\n', + )}\n\nIf this is a proxy contract, make sure you've specified abiOf for the contract action in the cannonfile that deploys it. If you’re calling an overloaded function, update func to include parentheses.`, ); } @@ -115,17 +115,17 @@ async function runTxn( debug('resolve from address', contract.address); const neededOwnerFuncAbi = functionList.find( - (f) => config.fromCall!.func == f[1] || config.fromCall!.func == f[1].split('(')[0] + (f) => config.fromCall!.func == f[1] || config.fromCall!.func == f[1].split('(')[0], )?.[0] as viem.AbiFunction; if (!neededOwnerFuncAbi) { throw new Error( `contract ${contract.address} for ${packageState.currentLabel} does not contain the function "${ config.fromCall.func }" to determine owner. List of recognized functions is:\n${Object.keys( - contract.abi.filter((v) => v.type === 'function').map((v) => (v as viem.AbiFunction).name) + contract.abi.filter((v) => v.type === 'function').map((v) => (v as viem.AbiFunction).name), ).join( - '\n' - )}\n\nIf this is a proxy contract, make sure you’ve specified abiOf for the contract action in the cannonfile that deploys it.` + '\n', + )}\n\nIf this is a proxy contract, make sure you’ve specified abiOf for the contract action in the cannonfile that deploys it.`, ); } const addressCall = await runtime.provider.simulateContract({ @@ -170,7 +170,7 @@ async function runTxn( const eventAbi = viem.getAbiItem({ abi: contract!.abi, name: l.eventName }) as any; return { name: l.eventName, args: eventAbi.inputs.map((i: any) => (l.args as any)[i.name]) }; }), - 'name' + 'name', ); debug('decoded events', txnEvents); @@ -198,7 +198,7 @@ function parseEventOutputs(config: Config['var'], txnEvents: EncodedTxnEvents[]) if (!config[name].allowEmptyEvents) { if (events.length === 0) { throw new Error( - `Event specified in cannonfile:\n\n ${expectedEvent} \n\ndoesn't exist or match an event emitted by the invoked function of the contract.` + `Event specified in cannonfile:\n\n ${expectedEvent} \n\ndoesn't exist or match an event emitted by the invoked function of the contract.`, ); } } @@ -230,7 +230,7 @@ async function importTxnData( ctx: ChainBuilderContext, config: Config, packageState: PackageState, - txns: TransactionMap + txns: TransactionMap, ) { const contracts: ChainArtifacts['contracts'] = {}; @@ -275,7 +275,7 @@ async function importTxnData( if (!abi) { throw new Error( - `factory."${topLabel}": must specify at least one of "artifact", "abi", or "abiOf" to resolve the contract ABI for the created contract.` + `factory."${topLabel}": must specify at least one of "artifact", "abi", or "abiOf" to resolve the contract ABI for the created contract.`, ); } @@ -433,11 +433,11 @@ const invokeSpec = { return config; }, - getInputs(config, possibleFields) { - let accesses = computeTemplateAccesses(config.abi, possibleFields); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.func, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.from, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.value, possibleFields)); + getInputs(config, templateContext) { + let accesses = templateContext.computeAccesses(config.abi); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.func)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.from)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.value)); if (typeof config.target === 'string') { config.target = [config.target as string]; @@ -452,7 +452,7 @@ const invokeSpec = { accesses.accesses.push(`imports.${target.split('.')[0]}`); } else if (isTemplateString(target)) { for (const match of getTemplateMatches(target)) { - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(match, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(match)); } } } @@ -460,37 +460,37 @@ const invokeSpec = { if (config.args) { _.forEach( config.args, - (a) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(JSON.stringify(a), possibleFields))) + (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(JSON.stringify(a)))), ); } if (config.fromCall) { - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.fromCall.func, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.fromCall.func)); _.forEach( config.fromCall.args, - (a) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(JSON.stringify(a), possibleFields))) + (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(JSON.stringify(a)))), ); } if (config?.overrides) { - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.overrides.gasLimit, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.overrides.gasLimit)); } for (const name in config.factory) { const f = config.factory[name]; - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(f.event, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(f.artifact, possibleFields)); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(f.abi, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(f.event)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(f.artifact)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(f.abi)); - _.forEach(f.abiOf, (a) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(a, possibleFields)))); + _.forEach(f.abiOf, (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(a)))); } const varsConfig = config.var || config.extra; for (const name in varsConfig) { const f = varsConfig[name]; - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(f.event, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(f.event)); } return accesses; @@ -640,7 +640,7 @@ ${getAllContractPaths(ctx).join('\n')}`); const eventAbi = viem.getAbiItem({ abi: contract!.abi, name: l.eventName }) as any; return { name: l.eventName, args: eventAbi.inputs.map((i: any) => (l.args as any)[i.name]) }; }), - 'name' + 'name', ); txns[label] = { diff --git a/packages/builder/src/steps/pull.ts b/packages/builder/src/steps/pull.ts index 790747211..4a566ccac 100644 --- a/packages/builder/src/steps/pull.ts +++ b/packages/builder/src/steps/pull.ts @@ -2,7 +2,7 @@ import Debug from 'debug'; import _ from 'lodash'; import { z } from 'zod'; import { Events } from '../runtime'; -import { computeTemplateAccesses, mergeTemplateAccesses } from '../access-recorder'; +import { mergeTemplateAccesses } from '../access-recorder'; import { getOutputs } from '../builder'; import { ChainDefinition } from '../definition'; import { PackageReference } from '../package-reference'; @@ -59,9 +59,9 @@ const pullSpec = { return config; }, - getInputs(config, possibleFields) { - let accesses = computeTemplateAccesses(config.source, possibleFields); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.preset, possibleFields)); + getInputs(config, templateContext) { + let accesses = templateContext.computeAccesses(config.source); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.preset)); return accesses; }, @@ -82,7 +82,7 @@ const pullSpec = { runtime.emit( Events.Notice, packageState.currentLabel, - 'To prevent unexpected upgrades, it is strongly recommended to lock the version of the source package by specifying a version in the `source` field.' + 'To prevent unexpected upgrades, it is strongly recommended to lock the version of the source package by specifying a version in the `source` field.', ); } @@ -92,13 +92,13 @@ const pullSpec = { if (!deployInfo) { throw new Error( - `deployment not found: ${source}. please make sure it exists for the cannon network and ${preset} preset.` + `deployment not found: ${source}. please make sure it exists for the cannon network and ${preset} preset.`, ); } if (deployInfo.status === 'partial') { throw new Error( - `deployment status is incomplete for ${source}. cannot generate artifacts safely. please complete deployment to continue import.` + `deployment status is incomplete for ${source}. cannot generate artifacts safely. please complete deployment to continue import.`, ); } @@ -106,7 +106,15 @@ const pullSpec = { imports: { [importLabel]: { url: (await runtime.registry.getUrl(source, chainId))!, // todo: duplication - ...(await getOutputs(runtime, new ChainDefinition(deployInfo.def), deployInfo.state))!, + ...(await getOutputs( + runtime, + new ChainDefinition(deployInfo.def, false, { + chainId, + timestamp: deployInfo.timestamp || 0, + package: { version: '0.0.0' }, + }), + deployInfo.state, + ))!, }, }, }; diff --git a/packages/builder/src/steps/router.ts b/packages/builder/src/steps/router.ts index bf5666873..dc2042938 100644 --- a/packages/builder/src/steps/router.ts +++ b/packages/builder/src/steps/router.ts @@ -3,7 +3,7 @@ import _ from 'lodash'; import * as viem from 'viem'; import { z } from 'zod'; import { generateRouter } from '@usecannon/router'; -import { computeTemplateAccesses, mergeTemplateAccesses } from '../access-recorder'; +import { mergeTemplateAccesses } from '../access-recorder'; import { routerSchema } from '../schemas'; import { ContractMap } from '../types'; import { @@ -104,15 +104,15 @@ const routerStep = { return config; }, - getInputs(config, possibleFields) { - let accesses = computeTemplateAccesses(config.from); - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.salt, possibleFields)); + getInputs(config, templateContext) { + let accesses = templateContext.computeAccesses(config.from); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.salt)); accesses.accesses.push( - ...config.contracts.map((c) => (c.includes('.') ? `imports.${c.split('.')[0]}` : `contracts.${c}`)) + ...config.contracts.map((c) => (c.includes('.') ? `imports.${c.split('.')[0]}` : `contracts.${c}`)), ); if (config?.overrides) { - accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(config.overrides.gasLimit, possibleFields)); + accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.overrides.gasLimit)); } return accesses; diff --git a/packages/builder/src/steps/var.ts b/packages/builder/src/steps/var.ts index 7c24e1297..d6283db0f 100644 --- a/packages/builder/src/steps/var.ts +++ b/packages/builder/src/steps/var.ts @@ -1,7 +1,7 @@ import Debug from 'debug'; import _ from 'lodash'; import { z } from 'zod'; -import { computeTemplateAccesses, mergeTemplateAccesses } from '../access-recorder'; +import { mergeTemplateAccesses } from '../access-recorder'; import { varSchema } from '../schemas'; import { template } from '../utils/template'; import { CannonAction } from '../actions'; @@ -42,11 +42,11 @@ const varSpec = { return config; }, - getInputs(config, possibleFields) { - let accesses = computeTemplateAccesses('', possibleFields); + getInputs(config, templateContext) { + let accesses = templateContext.computeAccesses(''); for (const c in _.omit(config, 'depends')) { - const fields = computeTemplateAccesses(config[c], possibleFields); + const fields = templateContext.computeAccesses(config[c]); accesses = mergeTemplateAccesses(accesses, fields); } diff --git a/packages/cli/src/commands/build.ts b/packages/cli/src/commands/build.ts index 9dd1069f4..e31245836 100644 --- a/packages/cli/src/commands/build.ts +++ b/packages/cli/src/commands/build.ts @@ -95,7 +95,7 @@ export async function build({ if (dryRun && rpcUrl) { log( - yellowBright(bold('⚠️ This is a simulation. No changes will be made to the chain. No package data will be saved.\n')) + yellowBright(bold('⚠️ This is a simulation. No changes will be made to the chain. No package data will be saved.\n')), ); } @@ -104,7 +104,7 @@ export async function build({ const packageReference = PackageReference.from( packageDefinition.name, packageDefinition.version, - packageDefinition.preset + packageDefinition.preset, ); const { fullPackageRef, packageRef } = packageReference; @@ -160,7 +160,7 @@ export async function build({ runtime.provider, packageReference, runtime.chainId, - def.getDeployers() + def.getDeployers(), ); if (oldDeployHash) { log(green(bold(`Found deployment state via on-chain store: ${oldDeployHash}`))); @@ -182,7 +182,11 @@ export async function build({ const resolvedSettings = _.pickBy(_.assign((!wipe && oldDeployData?.options) || {}, packageDefinition.settings)); - def = def || (oldDeployData ? new ChainDefinition(oldDeployData!.def) : undefined); + def = + def || + (oldDeployData + ? new ChainDefinition(oldDeployData!.def, false, { chainId, timestamp: Date.now(), package: { version: '0.0.0' } }) + : undefined); if (!def) { throw new Error('no deployment definition to build'); @@ -252,8 +256,8 @@ export async function build({ yellowBright( `${' '.repeat(d)} \u26A0\uFE0F Skipping [${n}] (${ typeof err === 'object' && err.toString === Object.prototype.toString ? JSON.stringify(err) : err.toString() - })` - ) + })`, + ), ); }); runtime.on(Events.Notice, (n, msg) => { @@ -266,7 +270,7 @@ export async function build({ log( `${' '.repeat(d)} ${green('\u2714')} Successfully called ${c.func}(${c?.args ?.map((arg: any) => (typeof arg === 'object' && arg !== null ? JSON.stringify(arg) : arg)) - .join(', ')})` + .join(', ')})`, ); } else { log(`${' '.repeat(d)} ${green('\u2714')} Successfully performed operation`); @@ -286,9 +290,9 @@ export async function build({ log( gray( `${' '.repeat(d)} Transaction Cost: ${viem.formatEther( - cost - )} ${nativeCurrencySymbol} (${txn.gasUsed.toLocaleString()} gas)` - ) + cost, + )} ${nativeCurrencySymbol} (${txn.gasUsed.toLocaleString()} gas)`, + ), ); } for (const contractKey in o.contracts) { @@ -297,7 +301,7 @@ export async function build({ log( `${' '.repeat(d)} ${green('\u2714')} Successfully deployed ${contract.contractName}${ c.create2 ? ' using CREATE2' : '' - }` + }`, ); log(gray(`${' '.repeat(d)} Contract Address: ${contract.address}`)); log(gray(`${' '.repeat(d)} Transaction Hash: ${contract.deployTxnHash}`)); @@ -306,9 +310,9 @@ export async function build({ log( gray( `${' '.repeat(d)} Transaction Cost: ${viem.formatEther( - cost - )} ${nativeCurrencySymbol} (${contract.gasUsed.toLocaleString()} gas)` - ) + cost, + )} ${nativeCurrencySymbol} (${contract.gasUsed.toLocaleString()} gas)`, + ), ); } } @@ -325,10 +329,10 @@ export async function build({ }); runtime.on(Events.ResolveDeploy, (packageName, preset, chainId, registry, d) => - log(magenta(`${' '.repeat(d)} Resolving ${packageName} (Chain ID: ${chainId}) via ${registry}...`)) + log(magenta(`${' '.repeat(d)} Resolving ${packageName} (Chain ID: ${chainId}) via ${registry}...`)), ); runtime.on(Events.DownloadDeploy, (hash, gateway, d) => - log(gray(`${' '.repeat(d)} Downloading ${hash} via ${gateway}`)) + log(gray(`${' '.repeat(d)} Downloading ${hash} via ${gateway}`)), ); // attach control-c handler @@ -374,7 +378,7 @@ export async function build({ }); const cliError = new Error( - `An error occured during build. A file with comprehensive information pertaining to this error has been written to ${dumpFilePath}. Please include this file when reporting an issue.` + `An error occured during build. A file with comprehensive information pertaining to this error has been written to ${dumpFilePath}. Please include this file when reporting an issue.`, ); throw mergeErrors(cliError, buildErr); @@ -437,9 +441,9 @@ export async function build({ error( red( bold( - `Failed to write state on-chain. The next time you upgrade your package, you should include the option --upgrade-from ${deployUrl}.` - ) - ) + `Failed to write state on-chain. The next time you upgrade your package, you should include the option --upgrade-from ${deployUrl}.`, + ), + ), ); } } @@ -457,14 +461,14 @@ export async function build({ log( yellowBright( bold( - '\n\u26A0\uFE0F Your deployment was not fully completed. Please inspect the issues listed above and resolve as necessary.' - ) - ) + '\n\u26A0\uFE0F Your deployment was not fully completed. Please inspect the issues listed above and resolve as necessary.', + ), + ), ); log(gray(`Total Cost: ${viem.formatEther(totalCost)} ${nativeCurrencySymbol}`)); log(''); log( - '- Rerunning the build command will attempt to execute skipped operations. It will not rerun executed operations. (To rerun executed operations, delete the partial build package generated by this run by adding the --wipe flag to the build command on the next run.)' + '- Rerunning the build command will attempt to execute skipped operations. It will not rerun executed operations. (To rerun executed operations, delete the partial build package generated by this run by adding the --wipe flag to the build command on the next run.)', ); if (upgradeFrom) { log(bold(' Remove the --upgrade-from option to continue from the partial build.')); @@ -473,7 +477,7 @@ export async function build({ log( '- Run ' + bold(`cannon pin ${deployUrl}`) + - ' to pin the partial deployment package on IPFS. Then use https://usecannon.com/deploy to collect signatures from a Safe for the skipped operations in the partial deployment package.' + ' to pin the partial deployment package on IPFS. Then use https://usecannon.com/deploy to collect signatures from a Safe for the skipped operations in the partial deployment package.', ); } else { if (dryRun) { @@ -485,8 +489,8 @@ export async function build({ bold( `Package data would be stored locally${ filteredSettings.writeIpfsUrl && ' and pinned to ' + filteredSettings.writeIpfsUrl - }` - ) + }`, + ), ); log(); @@ -505,8 +509,8 @@ export async function build({ bold( `Package data has been stored locally${ filteredSettings.writeIpfsUrl && ' and pinned to ' + filteredSettings.writeIpfsUrl - }` - ) + }`, + ), ); } log( @@ -514,7 +518,7 @@ export async function build({ ['Deployment Data', deployUrl], ['Package Code', miscUrl], ['Metadata', metaUrl], - ]) + ]), ); const isMainPreset = preset === PackageReference.DEFAULT_PRESET; @@ -523,15 +527,15 @@ export async function build({ if (isMainPreset) { log( bold( - `Publish ${bold(`${packageRef}`)} to the registry and pin the IPFS data to ${filteredSettings.publishIpfsUrl}` - ) + `Publish ${bold(`${packageRef}`)} to the registry and pin the IPFS data to ${filteredSettings.publishIpfsUrl}`, + ), ); log(`> cannon publish ${packageRef} --chain-id ${chainId}`); } else { log( bold( - `Publish ${bold(fullPackageRef)} to the registry and pin the IPFS data to ${filteredSettings.publishIpfsUrl}` - ) + `Publish ${bold(fullPackageRef)} to the registry and pin the IPFS data to ${filteredSettings.publishIpfsUrl}`, + ), ); log(`> cannon publish ${fullPackageRef} --chain-id ${chainId}`); } @@ -554,9 +558,9 @@ export async function build({ yellow( `Chain state could not be saved via ${runtime.loaders[ runtime.defaultLoaderScheme - ].getLabel()}. Try a writable endpoint by setting ipfsUrl through \`cannon setup\`.` - ) - ) + ].getLabel()}. Try a writable endpoint by setting ipfsUrl through \`cannon setup\`.`, + ), + ), ); } diff --git a/packages/cli/src/custom-steps/run.ts b/packages/cli/src/custom-steps/run.ts index 76983d14b..7357010e7 100644 --- a/packages/cli/src/custom-steps/run.ts +++ b/packages/cli/src/custom-steps/run.ts @@ -4,11 +4,11 @@ import { ChainArtifacts, ChainBuilderContext, ChainBuilderRuntimeInfo, - computeTemplateAccesses, mergeTemplateAccesses, PackageState, registerAction, template, + TemplateContext, } from '@usecannon/builder'; import crypto from 'crypto'; import Debug from 'debug'; @@ -137,12 +137,12 @@ const runAction = { return config; }, - getInputs(config: Config) { - let accesses = computeTemplateAccesses(config.exec); + getInputs(config: Config, templateContext: TemplateContext) { + let accesses = templateContext.computeAccesses(config.exec); - _.forEach(config.modified, (a) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(a)))); - _.forEach(config.args, (a) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(a)))); - _.forEach(config.env, (a) => (accesses = mergeTemplateAccesses(accesses, computeTemplateAccesses(a)))); + _.forEach(config.modified, (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(a)))); + _.forEach(config.args, (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(a)))); + _.forEach(config.env, (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(a)))); return accesses; }, diff --git a/packages/cli/src/helpers.ts b/packages/cli/src/helpers.ts index 8dece2cd9..82066c364 100644 --- a/packages/cli/src/helpers.ts +++ b/packages/cli/src/helpers.ts @@ -230,8 +230,12 @@ export async function loadCannonfile(filepath: string) { } // second argument ensures "sensitive" dependency verification--which ensures users are always specifying dependencies when they cant be reliably determined - const def = new ChainDefinition(rawDef, true); const pkg = loadPackageJson(path.join(path.dirname(path.resolve(filepath)), 'package.json')); + const def = new ChainDefinition(rawDef, true, { + chainId: CANNON_CHAIN_ID, + timestamp: Date.now(), + package: { version: pkg.version || '0.0.0' }, + }); // TODO: there should be a helper in the builder to create the initial ctx const ctx: ChainBuilderContext = { @@ -264,7 +268,7 @@ export async function loadCannonfile(filepath: string) { async function loadChainDefinitionToml(filepath: string, trace: string[]): Promise<[Partial, Buffer]> { if (!fs.existsSync(filepath)) { throw new Error( - `Chain definition TOML '${filepath}' not found. Include trace:\n${trace.map((p) => ' => ' + p).join('\n')}` + `Chain definition TOML '${filepath}' not found. Include trace:\n${trace.map((p) => ' => ' + p).join('\n')}`, ); } @@ -335,11 +339,11 @@ export async function ensureChainIdConsistency(rpcUrl?: string, chainId?: number log( red( `Error: The chainId (${providerChainId}) obtained from the ${bold('--rpc-url')} does not match with ${bold( - '--chain-id' + '--chain-id', )} value (${chainId}). Please ensure that the ${bold( - '--chain-id' - )} value matches the network your RPC is connected to.` - ) + '--chain-id', + )} value matches the network your RPC is connected to.`, + ), ); process.exit(1); @@ -453,7 +457,7 @@ export function checkAndNormalizePrivateKey(privateKey: string | viem.Hex | unde normalizedPrivateKeys.forEach((key: viem.Hex) => { if (!isPrivateKey(key)) { throw new Error( - 'Invalid private key found. Please verify the CANNON_PRIVATE_KEY environment variable, review your settings file, or check the value supplied to the --private-key flag' + 'Invalid private key found. Please verify the CANNON_PRIVATE_KEY environment variable, review your settings file, or check the value supplied to the --private-key flag', ); } }); @@ -483,7 +487,7 @@ export async function getPackageReference(packageRef: string, givenChainId: any, export async function getPackageInfo( packageRef: string, givenChainId: any, - givenRpcUrl: string + givenRpcUrl: string, ): Promise<{ fullPackageRef: string; chainId: number }> { const ipfsUrl = getIpfsUrl(packageRef); const parsedChainId = Number(givenChainId) || undefined; diff --git a/packages/website/src/features/Deploy/TransactionDetailsPage.tsx b/packages/website/src/features/Deploy/TransactionDetailsPage.tsx index e082b562b..09cb76675 100644 --- a/packages/website/src/features/Deploy/TransactionDetailsPage.tsx +++ b/packages/website/src/features/Deploy/TransactionDetailsPage.tsx @@ -222,9 +222,11 @@ function TransactionDetailsPage() { const getChainDef = async () => { if (!cannonDefInfo.def) return; - chainDefinitionRef.current = await getChainDefinitionFromWorker( - cannonDefInfo.def - ); + chainDefinitionRef.current = await getChainDefinitionFromWorker({ + def: cannonDefInfo.def, + chainId, + timestamp: Date.now(), + }); }; void getChainDef(); diff --git a/packages/website/src/features/Packages/CannonfileExplorer.tsx b/packages/website/src/features/Packages/CannonfileExplorer.tsx index 019abdf94..7b2c1d0db 100644 --- a/packages/website/src/features/Packages/CannonfileExplorer.tsx +++ b/packages/website/src/features/Packages/CannonfileExplorer.tsx @@ -123,7 +123,7 @@ export const CannonfileExplorer: FC<{ pkg: ApiPackage }> = ({ pkg }) => { className={`${displayMode === 1 ? 'block h-full' : 'hidden'} `} >
- +
diff --git a/packages/website/src/features/Packages/CannonfileGraph.tsx b/packages/website/src/features/Packages/CannonfileGraph.tsx index 5a11da649..4ace9e553 100644 --- a/packages/website/src/features/Packages/CannonfileGraph.tsx +++ b/packages/website/src/features/Packages/CannonfileGraph.tsx @@ -4,7 +4,7 @@ import * as d3 from 'd3'; import { createGlobalStyle } from 'styled-components'; import { useStepModalContext } from '@/providers/stepModalProvider'; import { getChainDefinitionFromWorker } from '@/helpers/chain-definition'; -import { RawChainDefinition } from '@usecannon/builder'; +import { DeploymentInfo } from '@usecannon/builder'; // Define global styles const GlobalStyles = createGlobalStyle` @@ -47,8 +47,9 @@ interface ExtendedSimulationNodeDatum extends d3.SimulationNodeDatum { } export const CannonfileGraph: FC<{ - deploymentDefinition: RawChainDefinition; -}> = ({ deploymentDefinition }) => { + deployInfo: DeploymentInfo; +}> = ({ deployInfo }) => { + const deploymentDefinition = deployInfo.def; const nodes: ExtendedSimulationNodeDatum[] = []; const links: d3.SimulationLinkDatum[] = []; @@ -59,7 +60,7 @@ export const CannonfileGraph: FC<{ async function initializeGraph() { try { const { allActionNames, resolvedDependencies } = - await getChainDefinitionFromWorker(deploymentDefinition); + await getChainDefinitionFromWorker(deployInfo); // Clear existing nodes and links nodes.length = 0; diff --git a/packages/website/src/features/Packages/PackageAccordionHelper/index.tsx b/packages/website/src/features/Packages/PackageAccordionHelper/index.tsx index e31ddbbe2..5014717f7 100644 --- a/packages/website/src/features/Packages/PackageAccordionHelper/index.tsx +++ b/packages/website/src/features/Packages/PackageAccordionHelper/index.tsx @@ -63,9 +63,11 @@ export default function PackageAccordionHelper({ const { ['run']: _, ...filteredDefinition } = deploymentData.data .def as any; - const chainDefinition = await getChainDefinitionFromWorker( - filteredDefinition - ); + const chainDefinition = await getChainDefinitionFromWorker({ + def: filteredDefinition, + chainId, + timestamp: Date.now(), + }); setChainDefinition(chainDefinition); } diff --git a/packages/website/src/helpers/chain-definition.ts b/packages/website/src/helpers/chain-definition.ts index db8c904a0..1fda3e62a 100644 --- a/packages/website/src/helpers/chain-definition.ts +++ b/packages/website/src/helpers/chain-definition.ts @@ -1,6 +1,6 @@ -import { ChainDefinition, RawChainDefinition } from '@usecannon/builder'; +import { ChainDefinition, DeploymentInfo } from '@usecannon/builder'; -export const getChainDefinitionFromWorker = (deployInfo: RawChainDefinition) => { +export const getChainDefinitionFromWorker = (deployInfo: Pick) => { return new Promise((resolve, reject) => { const worker = new Worker(new URL('@/workers/chain-definition.worker.ts', import.meta.url)); @@ -8,7 +8,11 @@ export const getChainDefinitionFromWorker = (deployInfo: RawChainDefinition) => if ('error' in event.data) { worker.terminate(); // in case of error, fallback to non-worker execution - const def = new ChainDefinition(deployInfo); + const def = new ChainDefinition(deployInfo.def, false, { + chainId: deployInfo.chainId || 0, + timestamp: deployInfo.timestamp, + package: { version: '0.0.0' }, + }); def.initializeComputedDependencies(); resolve(def); } else { diff --git a/packages/website/src/hooks/cannon.ts b/packages/website/src/hooks/cannon.ts index 176a0a076..ef0627049 100644 --- a/packages/website/src/hooks/cannon.ts +++ b/packages/website/src/hooks/cannon.ts @@ -28,7 +28,6 @@ import { publishPackage, findUpgradeFromPackage, ChainBuilderContext, - RawChainDefinition, CannonRegistry, getIpfsUrl, } from '@usecannon/builder'; @@ -597,9 +596,9 @@ export function useMergedCannonDefInfo(gitInfo: CannonfileGitInfo, partialDeploy return null; } - const deployInfo = partialDeployInfo?.ipfsQuery.data?.deployInfo?.def || originalCannonDefInfo.def; + const deployInfo = partialDeployInfo?.ipfsQuery.data?.deployInfo || originalCannonDefInfo; - return await getChainDefinitionFromWorker(deployInfo as RawChainDefinition); + return await getChainDefinitionFromWorker(deployInfo as DeploymentInfo); }, enabled: Boolean(partialDeployInfo?.ipfsQuery.data?.deployInfo || originalCannonDefInfo.def), }); @@ -644,7 +643,7 @@ export function useCannonPackageContracts(packageRef?: string, chainId?: number) { ipfs: loader } ); - const chainDefinition = await getChainDefinitionFromWorker(info.def); + const chainDefinition = await getChainDefinitionFromWorker(info); const outputs = await getOutputs(readRuntime, chainDefinition, info.state); diff --git a/packages/website/src/workers/chain-definition.worker.ts b/packages/website/src/workers/chain-definition.worker.ts index 9c7bb728f..2d418a7fd 100644 --- a/packages/website/src/workers/chain-definition.worker.ts +++ b/packages/website/src/workers/chain-definition.worker.ts @@ -6,7 +6,11 @@ self.onmessage = (event) => { throw new Error('No chain definition data provided'); } - const def = new ChainDefinition(event.data); + const def = new ChainDefinition(event.data.def, false, { + chainId: event.data.chainId, + timestamp: event.data.timestamp, + package: { version: '0.0.0.0' }, + }); if (!def) { throw new Error('Failed to create chain definition'); } From 7d5a54a8c1dc46dcd55294da15db67a59020325c Mon Sep 17 00:00:00 2001 From: Daniel Beal Date: Mon, 24 Feb 2025 11:22:34 +1100 Subject: [PATCH 2/2] fix lint --- packages/builder/src/access-recorder.test.ts | 2 +- packages/builder/src/access-recorder.ts | 4 +- packages/builder/src/actions.ts | 6 +- packages/builder/src/definition.ts | 20 +++--- packages/builder/src/package.ts | 20 +++--- packages/builder/src/steps/clone.ts | 14 ++-- packages/builder/src/steps/deploy.test.ts | 2 +- packages/builder/src/steps/deploy.ts | 24 +++---- packages/builder/src/steps/diamond.ts | 14 ++-- packages/builder/src/steps/invoke.test.ts | 16 ++--- packages/builder/src/steps/invoke.ts | 32 ++++----- packages/builder/src/steps/pull.ts | 8 +-- packages/builder/src/steps/router.ts | 2 +- packages/cli/src/commands/build.ts | 72 ++++++++++---------- packages/cli/src/helpers.ts | 14 ++-- 15 files changed, 125 insertions(+), 125 deletions(-) diff --git a/packages/builder/src/access-recorder.test.ts b/packages/builder/src/access-recorder.test.ts index 3b4a57b02..635676b03 100644 --- a/packages/builder/src/access-recorder.test.ts +++ b/packages/builder/src/access-recorder.test.ts @@ -109,7 +109,7 @@ describe('access-recorder.ts', () => { accesses: ['settings.my_45'], unableToCompute: false, }); - }) + }); }); describe('computeTemplateAccesses() syntax validation', () => { diff --git a/packages/builder/src/access-recorder.ts b/packages/builder/src/access-recorder.ts index 015a9e10d..bec7ef497 100644 --- a/packages/builder/src/access-recorder.ts +++ b/packages/builder/src/access-recorder.ts @@ -46,7 +46,7 @@ export class AccessRecorder extends ExtendableProxy { ...this.accessed .get(k)! .getAccesses(depth, (cur || 1) + 1) - .map((a) => `${k}.${a}`), + .map((a) => `${k}.${a}`) ); } @@ -80,7 +80,7 @@ export class TemplateContext { constructor( overrides: { chainId: number; timestamp: number; package: { version: string } }, - possibleNames: string[] = [], + possibleNames: string[] = [] ) { // Create a fake helper context, so the render works but no real functions are called const fakeHelperContext = _createDeepNoopObject(CannonHelperContext); diff --git a/packages/builder/src/actions.ts b/packages/builder/src/actions.ts index d92592ff9..138e3a458 100644 --- a/packages/builder/src/actions.ts +++ b/packages/builder/src/actions.ts @@ -27,7 +27,7 @@ export interface CannonAction { runtime: ChainBuilderRuntime, ctx: ChainBuilderContext, config: Config, - packageState: PackageState, + packageState: PackageState ) => Promise; /** @@ -44,7 +44,7 @@ export interface CannonAction { runtime: ChainBuilderRuntime, ctx: ChainBuilderContext, config: Config, - packageState: PackageState, + packageState: PackageState ) => Promise; importExisting?: ( @@ -52,7 +52,7 @@ export interface CannonAction { ctx: ChainBuilderContext, config: Config, packageState: PackageState, - existingKeys: string[], + existingKeys: string[] ) => Promise; // Takes in any schema as long as the base type is ZodSchema diff --git a/packages/builder/src/definition.ts b/packages/builder/src/definition.ts index 68f89271c..cae884d57 100644 --- a/packages/builder/src/definition.ts +++ b/packages/builder/src/definition.ts @@ -85,7 +85,7 @@ export class ChainDefinition { chainId: 0, timestamp: Date.now(), package: { version: '0.0.0' }, - }, + } ) { debug('begin chain def init'); this.raw = def; @@ -183,7 +183,7 @@ export class ChainDefinition { if (!action) { throw new Error( - `action kind plugin not installed: "${kind}" (for action: "${n}"). please install the plugin necessary to build this package.`, + `action kind plugin not installed: "${kind}" (for action: "${n}"). please install the plugin necessary to build this package.` ); } @@ -203,7 +203,7 @@ export class ChainDefinition { n: string, runtime: ChainBuilderRuntime, ctx: ChainBuilderContext, - tainted: boolean, + tainted: boolean ): Promise { const kind = n.split('.')[0] as keyof typeof ActionKinds; @@ -250,7 +250,7 @@ export class ChainDefinition { source: template(d.source, ctx), chainId: d.chainId || ctx.chainId, preset: d.preset ? template(d.preset, ctx) : 'main', - })), + })) ); } @@ -293,8 +293,8 @@ export class ChainDefinition { if (this.sensitiveDependencies && accessComputationResults.unableToCompute && !_.get(this.raw, node).depends) { throw new Error( `Unable to compute dependencies for [${node}] because of advanced logic in template strings. Specify dependencies manually, like "depends = ['${_.uniq( - _.uniq(accessComputationResults.accesses).map((a) => `${this.dependencyFor.get(a)}`), - ).join("', '")}']"`, + _.uniq(accessComputationResults.accesses).map((a) => `${this.dependencyFor.get(a)}`) + ).join("', '")}']"` ); } @@ -369,8 +369,8 @@ export class ChainDefinition { .map((n) => this.getDependencies(n)) .flatten() .uniq() - .value(), - ), + .value() + ) ); if (computeDepsDebug.enabled) { @@ -492,7 +492,7 @@ export class ChainDefinition { checkCycles( actions = this.allActionNames, seenNodes = new Set(), - currentPath = new Set(), + currentPath = new Set() ): string[] | null { // resolved dependencies gets set during dependency computation this.initializeComputedDependencies(); @@ -702,7 +702,7 @@ export class ChainDefinition { } return Math.max( layers[n].actions.length + 2, - _.sumBy(layers[n].depends, (d) => this.getPrintLinesUsed(d, layers)), + _.sumBy(layers[n].depends, (d) => this.getPrintLinesUsed(d, layers)) ); } diff --git a/packages/builder/src/package.ts b/packages/builder/src/package.ts index 921573086..9122041de 100644 --- a/packages/builder/src/package.ts +++ b/packages/builder/src/package.ts @@ -43,7 +43,7 @@ export async function forPackageTree Promise, context?: BundledOutput | null, - onlyResultProvisioned = true, + onlyResultProvisioned = true ): Promise { const results: T[] = []; @@ -63,7 +63,7 @@ export async function forPackageTree, tags: Array, - chainId?: number, + chainId?: number ) { const checkKeyPreset = deployInfo.def.preset || context?.preset || 'main'; @@ -164,7 +164,7 @@ export async function pinIpfs( const returnVal = { packagesNames: _.uniq([def.getVersion(preCtx) || 'latest', ...(context && context.tags ? context.tags : tags)]).map( - (t: string) => `${def.getName(preCtx)}:${t}@${context && context.preset ? context.preset : packageReference.preset}`, + (t: string) => `${def.getName(preCtx)}:${t}@${context && context.preset ? context.preset : packageReference.preset}` ), chainId: pkgChainId, url, @@ -199,13 +199,13 @@ export async function preparePublishPackage({ if (!deployData) { throw new Error( - `could not find deployment artifact for ${packageReference.fullPackageRef} with chain id "${chainId}". Please double check your settings, and rebuild your package.`, + `could not find deployment artifact for ${packageReference.fullPackageRef} with chain id "${chainId}". Please double check your settings, and rebuild your package.` ); } // We call this regardless of includeProvisioned because we want to ALWAYS upload the subpackages ipfs data. const calls: PackagePublishCall[] = (await forPackageTree(fromStorage, deployData, pinPackagesToIpfs)).filter( - (v: any) => !!v, + (v: any) => !!v ); return includeProvisioned ? calls : [_.last(calls)!]; @@ -239,7 +239,7 @@ export async function findUpgradeFromPackage( provider: viem.PublicClient, packageReference: PackageReference, chainId: number, - deployers: viem.Address[], + deployers: viem.Address[] ) { debug('find upgrade from onchain store'); let oldDeployHash: string | null = null; @@ -252,7 +252,7 @@ export async function findUpgradeFromPackage( await storeRead( provider, addr, - viem.keccak256(viem.stringToBytes(`${packageReference.name}@${packageReference.preset}`)), + viem.keccak256(viem.stringToBytes(`${packageReference.name}@${packageReference.preset}`)) ) ).split('_'); @@ -263,7 +263,7 @@ export async function findUpgradeFromPackage( } catch (err) { debug('failure while trying to read from onchain store', err); } - }), + }) ); if (!oldDeployHash) { @@ -280,6 +280,6 @@ export async function writeUpgradeFromInfo(runtime: ChainBuilderRuntime, package runtime.provider, await runtime.getDefaultSigner({}), viem.keccak256(viem.stringToBytes(`${packageRef.name}@${packageRef.preset}`)), - `${Math.floor(Date.now() / 1000)}_${deployUrl}`, + `${Math.floor(Date.now() / 1000)}_${deployUrl}` ); } diff --git a/packages/builder/src/steps/clone.ts b/packages/builder/src/steps/clone.ts index f59dccc8d..921c28663 100644 --- a/packages/builder/src/steps/clone.ts +++ b/packages/builder/src/steps/clone.ts @@ -118,7 +118,7 @@ const cloneSpec = { runtime.emit( Events.Notice, packageState.currentLabel, - 'To prevent unexpected upgrades, it is strongly recommended to lock the version of the source package by specifying a version in the `source` field.', + 'To prevent unexpected upgrades, it is strongly recommended to lock the version of the source package by specifying a version in the `source` field.' ); } @@ -126,7 +126,7 @@ const cloneSpec = { runtime.emit( Events.Notice, packageState.currentLabel, - `Deploying cloned package to default preset ${targetRef.preset}`, + `Deploying cloned package to default preset ${targetRef.preset}` ); } @@ -136,7 +136,7 @@ const cloneSpec = { throw new Error( `deployment not found: ${source}. please make sure it exists for preset ${ sourcePreset || sourceRef.preset - } and network ${chainId}.`, + } and network ${chainId}.` ); } @@ -174,8 +174,8 @@ const cloneSpec = { debug( `[clone.${importLabel}]`, yellow( - 'There is a pre-existing deployment for this preset and chain id. This build will overwrite. Did you mean `import`?', - ), + 'There is a pre-existing deployment for this preset and chain id. This build will overwrite. Did you mean `import`?' + ) ); } @@ -220,7 +220,7 @@ const cloneSpec = { debug( `[clone.${importLabel}]`, 'built state is exactly equal to previous state. skip generation of new deploy url', - importLabel, + importLabel ); return { imports: { @@ -260,7 +260,7 @@ const cloneSpec = { [target, ...(config.tags || ['latest']).map((t) => config.source.split(':')[0] + ':' + t)], runtime.chainId, newSubDeployUrl, - (await runtime.registry.getMetaUrl(source, chainId)) || '', + (await runtime.registry.getMetaUrl(source, chainId)) || '' ); } diff --git a/packages/builder/src/steps/deploy.test.ts b/packages/builder/src/steps/deploy.test.ts index 2af1db516..30da38beb 100644 --- a/packages/builder/src/steps/deploy.test.ts +++ b/packages/builder/src/steps/deploy.test.ts @@ -160,7 +160,7 @@ describe('steps/deploy.ts', () => { args: ['<%= contracts.h %>', '<%= contracts.i %>'], salt: '<%= contracts.j %>', }, - new TemplateContext({ chainId: 0, timestamp: 0, package: { version: '0.0.0' }}) + new TemplateContext({ chainId: 0, timestamp: 0, package: { version: '0.0.0' } }) ) .accesses.sort() ).toEqual([ diff --git a/packages/builder/src/steps/deploy.ts b/packages/builder/src/steps/deploy.ts index 239225185..f0eea7492 100644 --- a/packages/builder/src/steps/deploy.ts +++ b/packages/builder/src/steps/deploy.ts @@ -28,7 +28,7 @@ export interface ContractOutputs { function resolveBytecode( artifactData: ContractArtifact, - config: Config, + config: Config ): [viem.Hex, { [sourceName: string]: { [libName: string]: string } }] { let injectedBytecode = artifactData.bytecode; const linkedLibraries: { [sourceName: string]: { [libName: string]: string } } = {}; @@ -70,7 +70,7 @@ function checkConstructorArgs(abi: viem.Abi, args: any[] | undefined) { if (suppliedArgs.length !== neededArgs.length) { throw new Error( - `incorrect number of constructor arguments to deploy contract. supplied: ${suppliedArgs.length}, expected: ${neededArgs.length}`, + `incorrect number of constructor arguments to deploy contract. supplied: ${suppliedArgs.length}, expected: ${neededArgs.length}` ); } } @@ -82,7 +82,7 @@ function generateOutputs( deployTxn: viem.TransactionReceipt | null, deployTxnBlock: viem.Block | null, deployAddress: viem.Address, - currentLabel: string, + currentLabel: string ): ChainArtifacts { const [, linkedLibraries] = resolveBytecode(artifactData, config); @@ -210,7 +210,7 @@ const deploySpec = { if (config.args) { _.forEach( config.args, - (v) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(JSON.stringify(v)))), + (v) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(JSON.stringify(v)))) ); } @@ -244,7 +244,7 @@ const deploySpec = { if (!artifactData) { throw new Error( - `bytecode/abi for artifact ${config.artifact} not found. please double check the contract name and your build configuration`, + `bytecode/abi for artifact ${config.artifact} not found. please double check the contract name and your build configuration` ); } @@ -286,7 +286,7 @@ const deploySpec = { if (config.create2) { const arachnidDeployerAddress = await ensureArachnidCreate2Exists( runtime, - typeof config.create2 === 'string' ? (config.create2 as viem.Address) : ARACHNID_DEFAULT_DEPLOY_ADDR, + typeof config.create2 === 'string' ? (config.create2 as viem.Address) : ARACHNID_DEFAULT_DEPLOY_ADDR ); debug('performing arachnid create2'); @@ -303,7 +303,7 @@ const deploySpec = { if (config.ifExists !== 'continue') { throw new CannonError( `The contract at the create2 destination ${addr} is already deployed, but the Cannon state does not recognize that this contract has already been deployed. This typically indicates incorrect upgrade configuration. Please confirm if this contract should already be deployed or not, and if you want to continue the build as-is, add 'ifExists = "continue"' to the step definition`, - 'CREATE2_COLLISION', + 'CREATE2_COLLISION' ); } } else { @@ -357,15 +357,15 @@ const deploySpec = { // if the code goes here, it means that the Create2 deployment failed // and prepareTransactionRequest will throw an error with the underlying revert message await runtime.provider.prepareTransactionRequest( - _.assign(txn, overrides, { account: signer.wallet.account || signer.address }), + _.assign(txn, overrides, { account: signer.wallet.account || signer.address }) ); throw new Error( - 'The CREATE2 contract seems to be failing in the constructor. However, we were not able to get a stack trace.', + 'The CREATE2 contract seems to be failing in the constructor. However, we were not able to get a stack trace.' ); } else { const preparedTxn = await runtime.provider.prepareTransactionRequest( - _.assign(txn, overrides, { account: signer.wallet.account || signer.address }), + _.assign(txn, overrides, { account: signer.wallet.account || signer.address }) ); const hash = await signer.wallet.sendTransaction(preparedTxn as any); @@ -401,7 +401,7 @@ const deploySpec = { null, // note: send zero address since there is no contract address viem.zeroAddress, - packageState.currentLabel, + packageState.currentLabel ); return await handleTxnError(contractArtifact, runtime.provider, error); @@ -416,7 +416,7 @@ const deploySpec = { async importExisting(runtime, ctx, config, packageState, existingKeys) { if (existingKeys.length != 1) { throw new Error( - 'a contract can only be deployed on one transaction, so you can only supply one hash transaction to import', + 'a contract can only be deployed on one transaction, so you can only supply one hash transaction to import' ); } diff --git a/packages/builder/src/steps/diamond.ts b/packages/builder/src/steps/diamond.ts index 5aacda28d..4f258655c 100644 --- a/packages/builder/src/steps/diamond.ts +++ b/packages/builder/src/steps/diamond.ts @@ -106,7 +106,7 @@ const diamondStep = { accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.salt)); accesses.accesses.push( - ...config.contracts.map((c) => (c.includes('.') ? `imports.${c.split('.')[0]}` : `contracts.${c}`)), + ...config.contracts.map((c) => (c.includes('.') ? `imports.${c.split('.')[0]}` : `contracts.${c}`)) ); if (config?.overrides) { @@ -225,7 +225,7 @@ const diamondStep = { }; } catch (err) { throw new Error( - `failed to cut (upgrade) the diamond which is already deployed. This could happen for a few reasons:\n* the diamond owner has been changed and is now incorrect.\n* the diamond was previously made immutable and can no longer can be upgraded.\noriginal error: ${err}`, + `failed to cut (upgrade) the diamond which is already deployed. This could happen for a few reasons:\n* the diamond owner has been changed and is now incorrect.\n* the diamond was previously made immutable and can no longer can be upgraded.\noriginal error: ${err}` ); } }, @@ -234,13 +234,13 @@ const diamondStep = { async function firstTimeDeploy( runtime: ChainBuilderRuntime, config: Config, - packageState: PackageState, + packageState: PackageState ): Promise { const stepName = packageState.currentLabel.split('.')[1]; const signer = await runtime.getDefaultSigner( { data: viem.keccak256(viem.encodePacked(['string'], [config.salt])) as viem.Hex }, - config.salt, + config.salt ); debug('using deploy signer with address', signer.address); @@ -253,7 +253,7 @@ async function firstTimeDeploy( contract: ContractArtifact, deployedContractLabel: string, constructorArgs: any[], - salt = '', + salt = '' ) { debug('deploy contract', contract.contractName, deployedContractLabel, constructorArgs, salt); runtime.reportContractArtifact(`${contract.sourceName}:${contract.contractName}`, { @@ -299,7 +299,7 @@ async function firstTimeDeploy( if (!bytecode) { const hash = await signer.wallet.sendTransaction( - _.assign({ account: signer.wallet.account || signer.address }, create2Txn as any), + _.assign({ account: signer.wallet.account || signer.address }, create2Txn as any) ); const receipt = await runtime.provider.waitForTransactionReceipt({ hash }); const block = await runtime.provider.getBlock({ blockHash: receipt.blockHash }); @@ -358,7 +358,7 @@ async function firstTimeDeploy( (await import('../abis/diamond/Diamond.json')) as any, stepName, [addFacets, config.diamondArgs], - config.salt || '', + config.salt || '' ); return outputContracts; diff --git a/packages/builder/src/steps/invoke.test.ts b/packages/builder/src/steps/invoke.test.ts index 1e9343766..738a0d4b1 100644 --- a/packages/builder/src/steps/invoke.test.ts +++ b/packages/builder/src/steps/invoke.test.ts @@ -67,7 +67,7 @@ describe('steps/invoke.ts', () => { it('fails when setting invalid value', () => { expect(() => validateConfig(action.validate, { target: 'owner()', invalid: ['something'] })).toThrow( - "Unrecognized key(s) in object: 'invalid'", + "Unrecognized key(s) in object: 'invalid'" ); }); }); @@ -111,7 +111,7 @@ describe('steps/invoke.ts', () => { target: ['Woot'], func: 'something', }, - { ref: null, currentLabel: 'invoke.Invoke' }, + { ref: null, currentLabel: 'invoke.Invoke' } ); expect(result).toContainEqual({ @@ -132,7 +132,7 @@ describe('steps/invoke.ts', () => { args: ['split', { wave: 'form' }], value: '1234', }, - { ref: null, currentLabel: 'invoke.Invoke' }, + { ref: null, currentLabel: 'invoke.Invoke' } ); expect(result).toContainEqual({ @@ -159,9 +159,9 @@ describe('steps/invoke.ts', () => { args: ['<%= contracts.h %>', '<%= contracts.i %>'], overrides: { gasLimit: '<%= contracts.j %>' }, }, - new TemplateContext({ chainId: 0, timestamp: 0, package: { version: '0.0.0' } }), + new TemplateContext({ chainId: 0, timestamp: 0, package: { version: '0.0.0' } }) ) - .accesses.sort(), + .accesses.sort() ).toEqual([ 'contracts.a', 'contracts.b', @@ -187,8 +187,8 @@ describe('steps/invoke.ts', () => { factory: { something: { event: 'whoop', arg: 0 } }, extra: { else: { event: 'arst', arg: 1 } }, }, - { ref: new PackageReference('fun:1.0.0'), currentLabel: 'invoke.Hello' }, - ), + { ref: new PackageReference('fun:1.0.0'), currentLabel: 'invoke.Hello' } + ) ).toEqual(['txns.Hello', 'contracts.something', 'something', 'settings.else', 'extras.else']); }); }); @@ -240,7 +240,7 @@ describe('steps/invoke.ts', () => { }, }, }, - { ref: new PackageReference('fun:1.0.0'), currentLabel: 'invoke.something' }, + { ref: new PackageReference('fun:1.0.0'), currentLabel: 'invoke.something' } ); expect(result.contracts).toStrictEqual({ diff --git a/packages/builder/src/steps/invoke.ts b/packages/builder/src/steps/invoke.ts index d9b63707b..0ba178b86 100644 --- a/packages/builder/src/steps/invoke.ts +++ b/packages/builder/src/steps/invoke.ts @@ -59,7 +59,7 @@ async function runTxn( config: Config, contract: Contract, signer: CannonSigner, - packageState: PackageState, + packageState: PackageState ): Promise<[viem.TransactionReceipt, EncodedTxnEvents]> { let txn: viem.Hash; @@ -68,7 +68,7 @@ async function runTxn( // if invoke calls succeeding when no action was actually performed. if ((await runtime.provider.getCode({ address: contract.address })) === '0x') { throw new Error( - `contract ${contract.address} for ${packageState.currentLabel} has no bytecode. This is most likely a missing dependency or bad state.`, + `contract ${contract.address} for ${packageState.currentLabel} has no bytecode. This is most likely a missing dependency or bad state.` ); } @@ -97,7 +97,7 @@ async function runTxn( // Attempt to encode data so that if any arguments have any type mismatches, we can catch them and present them to the user. const functionList = assembleFunctionSignatures(contract.abi); const neededFuncAbi = functionList.find( - (f) => config.func == f[1] || config.func == f[1].split('(')[0], + (f) => config.func == f[1] || config.func == f[1].split('(')[0] )?.[0] as viem.AbiFunction; if (!neededFuncAbi) { throw new Error( @@ -106,8 +106,8 @@ async function runTxn( }". List of recognized functions is:\n${functionList .map((v) => v[1]) .join( - '\n', - )}\n\nIf this is a proxy contract, make sure you've specified abiOf for the contract action in the cannonfile that deploys it. If you’re calling an overloaded function, update func to include parentheses.`, + '\n' + )}\n\nIf this is a proxy contract, make sure you've specified abiOf for the contract action in the cannonfile that deploys it. If you’re calling an overloaded function, update func to include parentheses.` ); } @@ -115,17 +115,17 @@ async function runTxn( debug('resolve from address', contract.address); const neededOwnerFuncAbi = functionList.find( - (f) => config.fromCall!.func == f[1] || config.fromCall!.func == f[1].split('(')[0], + (f) => config.fromCall!.func == f[1] || config.fromCall!.func == f[1].split('(')[0] )?.[0] as viem.AbiFunction; if (!neededOwnerFuncAbi) { throw new Error( `contract ${contract.address} for ${packageState.currentLabel} does not contain the function "${ config.fromCall.func }" to determine owner. List of recognized functions is:\n${Object.keys( - contract.abi.filter((v) => v.type === 'function').map((v) => (v as viem.AbiFunction).name), + contract.abi.filter((v) => v.type === 'function').map((v) => (v as viem.AbiFunction).name) ).join( - '\n', - )}\n\nIf this is a proxy contract, make sure you’ve specified abiOf for the contract action in the cannonfile that deploys it.`, + '\n' + )}\n\nIf this is a proxy contract, make sure you’ve specified abiOf for the contract action in the cannonfile that deploys it.` ); } const addressCall = await runtime.provider.simulateContract({ @@ -170,7 +170,7 @@ async function runTxn( const eventAbi = viem.getAbiItem({ abi: contract!.abi, name: l.eventName }) as any; return { name: l.eventName, args: eventAbi.inputs.map((i: any) => (l.args as any)[i.name]) }; }), - 'name', + 'name' ); debug('decoded events', txnEvents); @@ -198,7 +198,7 @@ function parseEventOutputs(config: Config['var'], txnEvents: EncodedTxnEvents[]) if (!config[name].allowEmptyEvents) { if (events.length === 0) { throw new Error( - `Event specified in cannonfile:\n\n ${expectedEvent} \n\ndoesn't exist or match an event emitted by the invoked function of the contract.`, + `Event specified in cannonfile:\n\n ${expectedEvent} \n\ndoesn't exist or match an event emitted by the invoked function of the contract.` ); } } @@ -230,7 +230,7 @@ async function importTxnData( ctx: ChainBuilderContext, config: Config, packageState: PackageState, - txns: TransactionMap, + txns: TransactionMap ) { const contracts: ChainArtifacts['contracts'] = {}; @@ -275,7 +275,7 @@ async function importTxnData( if (!abi) { throw new Error( - `factory."${topLabel}": must specify at least one of "artifact", "abi", or "abiOf" to resolve the contract ABI for the created contract.`, + `factory."${topLabel}": must specify at least one of "artifact", "abi", or "abiOf" to resolve the contract ABI for the created contract.` ); } @@ -460,7 +460,7 @@ const invokeSpec = { if (config.args) { _.forEach( config.args, - (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(JSON.stringify(a)))), + (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(JSON.stringify(a)))) ); } @@ -469,7 +469,7 @@ const invokeSpec = { _.forEach( config.fromCall.args, - (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(JSON.stringify(a)))), + (a) => (accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(JSON.stringify(a)))) ); } @@ -640,7 +640,7 @@ ${getAllContractPaths(ctx).join('\n')}`); const eventAbi = viem.getAbiItem({ abi: contract!.abi, name: l.eventName }) as any; return { name: l.eventName, args: eventAbi.inputs.map((i: any) => (l.args as any)[i.name]) }; }), - 'name', + 'name' ); txns[label] = { diff --git a/packages/builder/src/steps/pull.ts b/packages/builder/src/steps/pull.ts index 4a566ccac..bb3adf6cb 100644 --- a/packages/builder/src/steps/pull.ts +++ b/packages/builder/src/steps/pull.ts @@ -82,7 +82,7 @@ const pullSpec = { runtime.emit( Events.Notice, packageState.currentLabel, - 'To prevent unexpected upgrades, it is strongly recommended to lock the version of the source package by specifying a version in the `source` field.', + 'To prevent unexpected upgrades, it is strongly recommended to lock the version of the source package by specifying a version in the `source` field.' ); } @@ -92,13 +92,13 @@ const pullSpec = { if (!deployInfo) { throw new Error( - `deployment not found: ${source}. please make sure it exists for the cannon network and ${preset} preset.`, + `deployment not found: ${source}. please make sure it exists for the cannon network and ${preset} preset.` ); } if (deployInfo.status === 'partial') { throw new Error( - `deployment status is incomplete for ${source}. cannot generate artifacts safely. please complete deployment to continue import.`, + `deployment status is incomplete for ${source}. cannot generate artifacts safely. please complete deployment to continue import.` ); } @@ -113,7 +113,7 @@ const pullSpec = { timestamp: deployInfo.timestamp || 0, package: { version: '0.0.0' }, }), - deployInfo.state, + deployInfo.state ))!, }, }, diff --git a/packages/builder/src/steps/router.ts b/packages/builder/src/steps/router.ts index dc2042938..6f882b0b0 100644 --- a/packages/builder/src/steps/router.ts +++ b/packages/builder/src/steps/router.ts @@ -108,7 +108,7 @@ const routerStep = { let accesses = templateContext.computeAccesses(config.from); accesses = mergeTemplateAccesses(accesses, templateContext.computeAccesses(config.salt)); accesses.accesses.push( - ...config.contracts.map((c) => (c.includes('.') ? `imports.${c.split('.')[0]}` : `contracts.${c}`)), + ...config.contracts.map((c) => (c.includes('.') ? `imports.${c.split('.')[0]}` : `contracts.${c}`)) ); if (config?.overrides) { diff --git a/packages/cli/src/commands/build.ts b/packages/cli/src/commands/build.ts index e31245836..4d057540b 100644 --- a/packages/cli/src/commands/build.ts +++ b/packages/cli/src/commands/build.ts @@ -95,7 +95,7 @@ export async function build({ if (dryRun && rpcUrl) { log( - yellowBright(bold('⚠️ This is a simulation. No changes will be made to the chain. No package data will be saved.\n')), + yellowBright(bold('⚠️ This is a simulation. No changes will be made to the chain. No package data will be saved.\n')) ); } @@ -104,7 +104,7 @@ export async function build({ const packageReference = PackageReference.from( packageDefinition.name, packageDefinition.version, - packageDefinition.preset, + packageDefinition.preset ); const { fullPackageRef, packageRef } = packageReference; @@ -160,7 +160,7 @@ export async function build({ runtime.provider, packageReference, runtime.chainId, - def.getDeployers(), + def.getDeployers() ); if (oldDeployHash) { log(green(bold(`Found deployment state via on-chain store: ${oldDeployHash}`))); @@ -256,8 +256,8 @@ export async function build({ yellowBright( `${' '.repeat(d)} \u26A0\uFE0F Skipping [${n}] (${ typeof err === 'object' && err.toString === Object.prototype.toString ? JSON.stringify(err) : err.toString() - })`, - ), + })` + ) ); }); runtime.on(Events.Notice, (n, msg) => { @@ -270,7 +270,7 @@ export async function build({ log( `${' '.repeat(d)} ${green('\u2714')} Successfully called ${c.func}(${c?.args ?.map((arg: any) => (typeof arg === 'object' && arg !== null ? JSON.stringify(arg) : arg)) - .join(', ')})`, + .join(', ')})` ); } else { log(`${' '.repeat(d)} ${green('\u2714')} Successfully performed operation`); @@ -290,9 +290,9 @@ export async function build({ log( gray( `${' '.repeat(d)} Transaction Cost: ${viem.formatEther( - cost, - )} ${nativeCurrencySymbol} (${txn.gasUsed.toLocaleString()} gas)`, - ), + cost + )} ${nativeCurrencySymbol} (${txn.gasUsed.toLocaleString()} gas)` + ) ); } for (const contractKey in o.contracts) { @@ -301,7 +301,7 @@ export async function build({ log( `${' '.repeat(d)} ${green('\u2714')} Successfully deployed ${contract.contractName}${ c.create2 ? ' using CREATE2' : '' - }`, + }` ); log(gray(`${' '.repeat(d)} Contract Address: ${contract.address}`)); log(gray(`${' '.repeat(d)} Transaction Hash: ${contract.deployTxnHash}`)); @@ -310,9 +310,9 @@ export async function build({ log( gray( `${' '.repeat(d)} Transaction Cost: ${viem.formatEther( - cost, - )} ${nativeCurrencySymbol} (${contract.gasUsed.toLocaleString()} gas)`, - ), + cost + )} ${nativeCurrencySymbol} (${contract.gasUsed.toLocaleString()} gas)` + ) ); } } @@ -329,10 +329,10 @@ export async function build({ }); runtime.on(Events.ResolveDeploy, (packageName, preset, chainId, registry, d) => - log(magenta(`${' '.repeat(d)} Resolving ${packageName} (Chain ID: ${chainId}) via ${registry}...`)), + log(magenta(`${' '.repeat(d)} Resolving ${packageName} (Chain ID: ${chainId}) via ${registry}...`)) ); runtime.on(Events.DownloadDeploy, (hash, gateway, d) => - log(gray(`${' '.repeat(d)} Downloading ${hash} via ${gateway}`)), + log(gray(`${' '.repeat(d)} Downloading ${hash} via ${gateway}`)) ); // attach control-c handler @@ -378,7 +378,7 @@ export async function build({ }); const cliError = new Error( - `An error occured during build. A file with comprehensive information pertaining to this error has been written to ${dumpFilePath}. Please include this file when reporting an issue.`, + `An error occured during build. A file with comprehensive information pertaining to this error has been written to ${dumpFilePath}. Please include this file when reporting an issue.` ); throw mergeErrors(cliError, buildErr); @@ -441,9 +441,9 @@ export async function build({ error( red( bold( - `Failed to write state on-chain. The next time you upgrade your package, you should include the option --upgrade-from ${deployUrl}.`, - ), - ), + `Failed to write state on-chain. The next time you upgrade your package, you should include the option --upgrade-from ${deployUrl}.` + ) + ) ); } } @@ -461,14 +461,14 @@ export async function build({ log( yellowBright( bold( - '\n\u26A0\uFE0F Your deployment was not fully completed. Please inspect the issues listed above and resolve as necessary.', - ), - ), + '\n\u26A0\uFE0F Your deployment was not fully completed. Please inspect the issues listed above and resolve as necessary.' + ) + ) ); log(gray(`Total Cost: ${viem.formatEther(totalCost)} ${nativeCurrencySymbol}`)); log(''); log( - '- Rerunning the build command will attempt to execute skipped operations. It will not rerun executed operations. (To rerun executed operations, delete the partial build package generated by this run by adding the --wipe flag to the build command on the next run.)', + '- Rerunning the build command will attempt to execute skipped operations. It will not rerun executed operations. (To rerun executed operations, delete the partial build package generated by this run by adding the --wipe flag to the build command on the next run.)' ); if (upgradeFrom) { log(bold(' Remove the --upgrade-from option to continue from the partial build.')); @@ -477,7 +477,7 @@ export async function build({ log( '- Run ' + bold(`cannon pin ${deployUrl}`) + - ' to pin the partial deployment package on IPFS. Then use https://usecannon.com/deploy to collect signatures from a Safe for the skipped operations in the partial deployment package.', + ' to pin the partial deployment package on IPFS. Then use https://usecannon.com/deploy to collect signatures from a Safe for the skipped operations in the partial deployment package.' ); } else { if (dryRun) { @@ -489,8 +489,8 @@ export async function build({ bold( `Package data would be stored locally${ filteredSettings.writeIpfsUrl && ' and pinned to ' + filteredSettings.writeIpfsUrl - }`, - ), + }` + ) ); log(); @@ -509,8 +509,8 @@ export async function build({ bold( `Package data has been stored locally${ filteredSettings.writeIpfsUrl && ' and pinned to ' + filteredSettings.writeIpfsUrl - }`, - ), + }` + ) ); } log( @@ -518,7 +518,7 @@ export async function build({ ['Deployment Data', deployUrl], ['Package Code', miscUrl], ['Metadata', metaUrl], - ]), + ]) ); const isMainPreset = preset === PackageReference.DEFAULT_PRESET; @@ -527,15 +527,15 @@ export async function build({ if (isMainPreset) { log( bold( - `Publish ${bold(`${packageRef}`)} to the registry and pin the IPFS data to ${filteredSettings.publishIpfsUrl}`, - ), + `Publish ${bold(`${packageRef}`)} to the registry and pin the IPFS data to ${filteredSettings.publishIpfsUrl}` + ) ); log(`> cannon publish ${packageRef} --chain-id ${chainId}`); } else { log( bold( - `Publish ${bold(fullPackageRef)} to the registry and pin the IPFS data to ${filteredSettings.publishIpfsUrl}`, - ), + `Publish ${bold(fullPackageRef)} to the registry and pin the IPFS data to ${filteredSettings.publishIpfsUrl}` + ) ); log(`> cannon publish ${fullPackageRef} --chain-id ${chainId}`); } @@ -558,9 +558,9 @@ export async function build({ yellow( `Chain state could not be saved via ${runtime.loaders[ runtime.defaultLoaderScheme - ].getLabel()}. Try a writable endpoint by setting ipfsUrl through \`cannon setup\`.`, - ), - ), + ].getLabel()}. Try a writable endpoint by setting ipfsUrl through \`cannon setup\`.` + ) + ) ); } diff --git a/packages/cli/src/helpers.ts b/packages/cli/src/helpers.ts index 82066c364..e9e771a89 100644 --- a/packages/cli/src/helpers.ts +++ b/packages/cli/src/helpers.ts @@ -268,7 +268,7 @@ export async function loadCannonfile(filepath: string) { async function loadChainDefinitionToml(filepath: string, trace: string[]): Promise<[Partial, Buffer]> { if (!fs.existsSync(filepath)) { throw new Error( - `Chain definition TOML '${filepath}' not found. Include trace:\n${trace.map((p) => ' => ' + p).join('\n')}`, + `Chain definition TOML '${filepath}' not found. Include trace:\n${trace.map((p) => ' => ' + p).join('\n')}` ); } @@ -339,11 +339,11 @@ export async function ensureChainIdConsistency(rpcUrl?: string, chainId?: number log( red( `Error: The chainId (${providerChainId}) obtained from the ${bold('--rpc-url')} does not match with ${bold( - '--chain-id', + '--chain-id' )} value (${chainId}). Please ensure that the ${bold( - '--chain-id', - )} value matches the network your RPC is connected to.`, - ), + '--chain-id' + )} value matches the network your RPC is connected to.` + ) ); process.exit(1); @@ -457,7 +457,7 @@ export function checkAndNormalizePrivateKey(privateKey: string | viem.Hex | unde normalizedPrivateKeys.forEach((key: viem.Hex) => { if (!isPrivateKey(key)) { throw new Error( - 'Invalid private key found. Please verify the CANNON_PRIVATE_KEY environment variable, review your settings file, or check the value supplied to the --private-key flag', + 'Invalid private key found. Please verify the CANNON_PRIVATE_KEY environment variable, review your settings file, or check the value supplied to the --private-key flag' ); } }); @@ -487,7 +487,7 @@ export async function getPackageReference(packageRef: string, givenChainId: any, export async function getPackageInfo( packageRef: string, givenChainId: any, - givenRpcUrl: string, + givenRpcUrl: string ): Promise<{ fullPackageRef: string; chainId: number }> { const ipfsUrl = getIpfsUrl(packageRef); const parsedChainId = Number(givenChainId) || undefined;