Starknet Contract Target
The starknet-contract
target allows to build the package as a Starknet Contract. It searches for all contract classes in the package, and builds a separate compiled JSON file each found class. Generated file will be named with following pattern: [target name]_[contract name].contract_class.json
.
This target accepts several configuration arguments, beside ones shared among all targets, with default values for the default profile:
[[target.starknet-contract]]
# Enable Sierra codegen.
sierra = true
# Enable CASM codegen.
casm = false
# Emit Python-powered hints in order to run compiled CASM class with legacy Cairo VM.
casm-add-pythonic-hints = false
# Enable allowed libfuncs validation.
allowed-libfuncs = true
# Raise errors instead of warnings if disallowed libfuncs are found.
allowed-libfuncs-deny = false
# Reference to the libfuncs allowlist used for validation.
# - Use `allowed-libfuncs-list.name` to use built-in named allowlist.
# - Use `allowed-libfuncs-list.path` to read an allowlist from file.
allowed-libfuncs-list = {} # Cairo compiler defined
# Emit Starknet artifacts for contracts defined in dependencies.
build-external-contracts = []
Usage
To enable Starknet contract compilation for a package, write a following line in Scarb.toml
:
[[target.starknet-contract]]
Then, declare a dependency on the starknet
package. Its version is coupled to Cairo version included in Scarb.
[dependencies]
starknet = ">=2.8.4"
Sierra contract class generation
The enabled by default property sierra
determines whether this target builds a Sierra Contract Class file.
CASM contract class generation
Historically, contract classes have been defined in terms of Cairo assembly, or CASM for short (the class definition also included more information needed for execution, e.g., hint data). The novelty of Cairo is the introduction of Sierra, an intermediate layer between Cairo and CASM.
When executing a contract on Starknet, the Sequencer downloads a [Contract Class] which contains Sierra bytecode. It is a role of the Sequencer to compile it to CASM, which is a language that you can physically execute and generate proof of such execution. If for any reason, there is a need to compile Sierra contract to CASM locally, it can be done by turning on the casm
property, which by default is set to false. If enabled, Scarb will emit the CASM Contract Class file in the target directory to a file named with following pattern: [package name]_[contract name].compiled_contract_class.json
.
Historically, Cairo used to use Python as the language powering the Hints. CASM contract classes can be still executed on the legacy Python-based Cairo VM, under condition that they include Python version of hints generated by Sierra, which now is an optional feature. The off by default casm-add-pythonic-hints
property enables Scarb to add it to produced artifacts.
Compiling external contracts
While compiling the Scarb project, by default no artifacts are emitted for contracts defined in dependencies. To override this behaviour for certain contracts from other packages, you can use build-external-contracts
property. It accepts a list of strings, each of which is a reference to a contract defined in a dependency. The package that implements this contracts need to be declared as a dependency of the project in [dependencies]
section. The reference to a contract is a full cairo path to the contract module. External contracts will be built in the same way as the contracts defined in the project. The artifacts will be emitted under [target name]_[contract name].[sierra|casm].json
names. In case there is a contract name collision , those colliding contract names will be replaced with full cairo paths.
For example, to build Account
contract defined in openzeppelin
package, add following definitions to the Scarb.toml
:
[dependencies]
starknet = ">=2.8.4"
openzeppelin = { git = "https://github.com/OpenZeppelin/cairo-contracts.git", branch = "cairo-2" }
[[target.starknet-contract]]
build-external-contracts = ["openzeppelin::account::account::Account"]
Wildcard support
It is possible to request building many contracts from a module tree at once. For example, the following snippet:
[[target.starknet-contract]]
build-external-contracts = [
"dojo_erc::erc721::components::Balance",
"dojo_erc::erc721::components::OperatorApproval",
"dojo_erc::erc721::components::Owner",
"dojo_erc::erc721::components::TokenApproval",
"dojo_erc::erc721::erc721::ERC721",
"dojo_erc::erc721::systems::erc721_approve",
"dojo_erc::erc721::systems::erc721_burn",
"dojo_erc::erc721::systems::erc721_mint",
"dojo_erc::erc721::systems::erc721_set_approval_for_all",
"dojo_erc::erc721::systems::erc721_transfer_from",
]
can be written as:
[[target.starknet-contract]]
build-external-contracts = [
"dojo_erc::erc721::*",
]
When using a wildcard in the build-external-contracts
property, Scarb will match the contract path before the wildcard and look for all contracts whose paths start with that prefix. The wildcard can only be used as the last character in the contract path, and each external contract path can have at most one wildcard.
Starknet Artifacts
As part of building Starknet contracts, contract target generates a [target_name].starknet_artifacts.json
file containing a machine-readable data about built artifacts.
Version 1 of this file has a structure like this:
{
"version": 1,
"contracts": [
{
"id": "<opaque>",
"package_name": "mypackage",
"contract_name": "Contract2",
"module_path": "mypackage::path::to::module::Contract2",
"artifacts": {
"sierra": "mypackage_Contract2.contract_class.json",
"casm": null
}
},
{
"id": "<opaque>",
"package_name": "mypackage",
"contract_name": "Contract1",
"module_path": "mypackage::Contract1",
"artifacts": {
"sierra": "mypackage_Contract1.contract_class.json",
"casm": null
}
}
]
}
id
is an identifier of the item in"contracts"
list. Use it to reference items, as it has the highest chance of being a unique value.package_name
is the name of the package in which the contract has been implemented.contract_name
is the name of the contract module.module_path
is the module path of compiled contract.artifacts
paths are relative to this file path. Depending on the targets defined in[[target.starknet-contract]]
section of theScarb.toml
, some of the values might benull
.
Allowed libfuncs validation
Not all Sierra libfuncs emitted by the Cairo compiler can be deployed to Starknet, as some are not audited yet, or others are meant for development use and would be unsafe when run in contract context. Therefore, the Starknet contract target runs a pass after compilation that checks if every emitted libfunc is present in a provided allowed libfuncs list. By default, this pass emits compilation warnings when it spots unexpected libfuncs.
This validation pass can be disabled entirely by writing allowed-libfuncs = false
in target configuration. On the other hand, its severity can be elevated to compilation errors, by writing allowed-libfuncs-deny = true
.
The default allow-list is compiler-provided and corresponds to list of all audited libfuncs allowed on Starknet.
Built-in allow-lists
The Starknet contract compiler contains several built-in allowed libfuncs lists, that can be identified by name via the allowed-libfuncs-list.name
property. For example, to use the experimental
allow-list, type the following:
[[target.starknet-contract]]
allowed-libfuncs-list.name = "experimental"
All built-in lists can be located in the Cairo repository.
External allow-lists
It is also possible to use a user-provided allow-list, via the allowed-libfuncs-list.path
property:
[[target.starknet-contract]]
allowed-libfuncs-list.path = "path/to/list.json"
User-provided lists should follow the same format as built-in ones. For reference, consult the built-in audited
list.