Cannonfile Steps

Cannonfiles contain a series of steps defined in a TOML file.

Each step has a type and a name. Each type accepts a specific set of inputs and modifies a return object. The return object is accessible in steps executed at later steps. The resulting return object is provided to any cannonfile that imports it with the import step.

⚠️ Use the depends input to specify an array of steps that a particular step relies upon. For example, you must include depends = ["contract.myStorage"] on an invoke step which calls a function on a contract deployed by the contract.myStorage step. Note that two steps which effect the same state need to have one depend on the other (like two transfer functions that may effect the same user balances).

Every step updates the return object by adding an entry to the txns key with the step's name. The value of the entry is an object with the properties hash (which is the hash of this transstep) and events (which is an array of objects with the name of each event emitted by this call and the corresponding event data as args).

For example, the step below has the type contract and is named myStorage. It requires the input artifact, which is the name of the contract to deploy. (In this example, it's the contract named Storage.)

artifact = "Storage"

This updates the return object such that, for example, a later invoke step could call this contract with the input target = ["<%= contracts.myStorage.address %>"].

There are five types of steps you can use in a Cannonfile:

  • The contract step deploys a contract.
  • The import step will import a cannonfile from a package hosted with the package manager.
  • The invoke step calls a specified function on your node.
  • The provision step attempts to deploy the specified package (unlike the import command, which only injects existing deployment data). Third-party packages can execute arbitrary code on your computer when provisioning. Only provision packages that you have verified or trust.
  • The run step executes a custom script. This script is passed a ChainBuilder object as parameter. The run command breaks composability. Only use this as a last resort. Use a custom Cannon plug-in if this is necessary for your deployment.

A setting may also be defined, which is a user-configurable option that can be referenced in other steps' inputs. For example, a cannonfile may define [setting.sampleSetting] and then reference sampleValue as "<%= settings.sampleSetting %>" after running npx hardhat cannon sampleSetting="sampleValue"

Base Cannonfile Config

Available properties for top level config

namestringName of the package
versionstringversion of the package
description?stringDescription for the package
keywords?[string]keywords for search indexing
setting?{ description?: string; type?: "string" | "number" | "boolean" | undefined; defaultValue?: string; }\Object that allows the definition of values for use in next steps toml [settings.owner] defaultValue: "some-eth-address"


Available properties for run step

execstringThe javascript (or typescript) file to load
funcstringThe function to call in this file
modified[string]An array of files and directories that this script depends on. The cache of the cannonfile's build is recreated when these files change.
args?[string]Arguments passed to the function (after the ChainBuilder object)
env?[string]Environment variables to be set on the script
depends?[string]List of steps that this action depends on


Available properties for contract step

artifactstringArtifact name or path of the target contract
create2?booleanDetermines whether to deploy the contract using create2
from?stringContract deployer address. Must match the ethereum address format
abi?stringAbi of the contract being deployed
abiOf?[string]An array of contract artifacts that have already been deployed with Cannon. This is useful when deploying proxy contracts.
args?(string | number | boolean | (string | number | boolean)[] | string | number | boolean\ | string | number | boolean[])[]Constructor or initializer args
libraries?string\An array of contract action names that deploy libraries this contract depends on.
salt?stringUsed to force new copy of a contract (not actually used)
value?stringNative currency value to send in the transaction
overrides?{ gasLimit?: string; }Override transaction settings
depends?[string]List of steps that this action depends on


Available properties for import step

sourcestringSource of the cannonfile package to import from Can be a cannonfile step name or package name
chainId?numberID of the chain to import the package from
preset?stringPreset label of the package being imported
depends?[string]Previous steps this step is dependent on toml depends = ['contract.Storage', 'import.Contract']


Available properties for invoke step

targetObjectNames of the contract to call or contract action that deployed the contract to call
funcstringName of the function to call on the contract
abi?stringJSON file of the contract ABI Required if the target contains an address rather than a contract action name.
args?(string | number | boolean | (string | number | boolean)[] | string | number | boolean\ | string | number | boolean[])[]Arguments to use when invoking this call.
from?stringThe calling address to use when invoking this call.
fromCall?{ func: string ; args?: (string | number | boolean | (string | number | boolean)[] | string | number | boolean\ | string | number | boolean[])[] }Specify a function to use as the 'from' value in a function call. Example owner().
fromCall.funcstringThe name of a view function to call on this contract. The result will be used as the from input.
fromCall.args?(string | number | boolean | (string | number | boolean)[] | string | number | boolean\ | string | number | boolean[])[]The arguments to pass into the function being called.
value?stringThe amount of ether/wei to send in the transaction.
overrides?{ gasLimit: string }Override transaction settings
overrides.gasLimitstringGas limit to send along with the transaction
extra?{ event: string ; arg: number ; allowEmptyEvents?: boolean }\Object defined to hold extra transaction result data. For now its limited to getting event data so it can be reused in other steps
factory?{ event: string ; arg: number ; artifact?: string ; abiOf?: [string]; constructorArgs?: (string | number | boolean | (string | number | boolean)[] | string | number | boolean\ | string | number | boolean[])[] ; allowEmptyEvents?: boolean }\Object defined to hold deployment transaction result data. For now its limited to getting deployment event data so it can be reused in other steps
depends?[string]Previous steps this step is dependent on


Available properties for provision step

sourcestringName of the package to provision
chainId?numberID of the chain to import the package from Default - 13370
sourcePreset?stringOverride the preset to use when provisioning this package. Default - "main"
targetPreset?stringSet the new preset to use for this package. Default - "main"
options?string\The settings to be used when initializing this Cannonfile. Overrides any defaults preset in the source package.
tags?[string]Additional tags to set on the registry for when this provisioned package is published.
depends?[string]Previous steps this step is dependent on


Available properties for router step

contracts[string]Set of contracts that will be passed to the router
from?stringAddress to pass to the from call
salt?stringUsed to force new copy of a contract (not actually used)
depends?[string]List of steps that this action depends on

Advanced Usage

Referencing Factory-deployed Contracts

Smart contracts may have functions which deploy other smart contracts. Contracts which deploy others are typically referred to as factory contracts. You can reference contracts deployed by factories in your cannonfile.

For example, if the deployPool function below deploys a contract, the following invoke command registers that contract based on event data emitted from that call.

target = ["PoolFactory"]
func = "deployPool"
factory.MyPoolDeployment.artifact = "Pool"
# alternatively, if the code for the deployed contract is not available in your artifacts, you can also reference the ABI like:
# factory.MyPoolDeployment.abiOf = "PreviousPool"
factory.MyPoolDeployment.event = "NewDeployment"
factory.MyPoolDeployment.arg = 0

Specifically, this would anticipate this invoke call will emit an event named NewDeployment with a contract address as the first data argument (per arg, a zero-based index). This contract should implement the Pool contract. Now, a subsequent invoke step could set target = ["MyPoolDeployment"].

To reference contract information for a contract deployed on a previous invoke step such as the example shown above call the contracts object inside your cannonfile. For example <%= contracts.MyPoolDeployment.address %> would return the address of the Pool contract deployed by the PoolFactory contract.

If the invoked function deploys multiple contracts of the same name, you can specify them by index through the contracts object.

  • <%= contracts.MyPoolDeployment.address %> would return the first deployed Pool contract address
  • <%= contracts.MyPoolDeployment_0.address %> would return the second deployed Pool contract address

These contracts are added to the return object as they would be if deployed by a contract step.

Referencing Extra Event Data

If an invoked function emits an event, cannon can parse the event data in your cannonfile by using the extras property, This lets you reference previously emitted event's data in subsequent invoke steps.

For example, to track the NewDeployment event data from the PoolFactory deployment from the example above, add the extra property and set an attribute for the event like so:

target = ["PoolFactory"]

extra.NewDeploymentEvent.event = "NewDeployment"
extra.NewDeploymentEvent.arg = 0

Now, calling "<% = extras.NewDeploymentEvent %>" in a subsequent invoke step would return the first data argument for NewDeployment.

If an invoked function emits multiple events you can specify them by index.

For example if the PoolFactory emitted multiple NewDeployment events:

  • <%= extras.NewDeploymentEvent_0 %> would return the first emitted event of this kind
  • <% = extras.NewDeploymentEvent_4 %> would reference the fifth emitted event of this kind

Event Error Logging

If an event is specified in the cannonfile but the invoke function does not emit any events or emits an event that doesn't match the one specified in the cannonfile, the invoke step will fail with an error.

You can bypass the event error logging by setting it like extras.NewDeploymentEvent.allowEmptyEvents = true or factory.MyPoolDeployment.allowEmptyEvents = true under the factory or extra property that throws an error.

An useful example would for this would be when an event is only emitted under certain conditions but you still need to reference it when it is emitted or don't want to halt execution when it's not emitted.

Keep in mind you wont be able to reference event or contract data through the contracts or extras properties if a matching event wasnt emitted

Supported byCannonRetroPGF