Skip to content

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:

toml
[[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 = []

# Resolve static contract forwarding (see below).
forwarding = false

Usage ​

To enable Starknet contract compilation for a package, write a following line in Scarb.toml:

toml
[[target.starknet-contract]]

Then, declare a dependency on the starknet package. Its version is coupled to Cairo version included in Scarb.

toml
[dependencies]
starknet = ">=2.19.6"

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.

Static contract forwarding ​

Static contract forwarding lets a contract expose another contract's interface by delegating calls to its class via library_call. Unlike a regular proxy, the target class hash is embedded at compile time instead of being read from storage.

Cairo generates a <Trait>ForwardImpl for every #[starknet::interface], and Scarb generates a <contract>__class_hash__::ForwardingClassHashImpl for every #[starknet::contract]:

cairo
#[starknet::contract]
#[feature("forward-impl")]
pub mod static_proxy {
    #[storage]
    struct Storage {}

    impl CounterClassHash =
        super::counter_contract__class_hash__::ForwardingClassHashImpl<ContractState>;

    #[abi(embed_v0)]
    impl ForwardedImpl = super::ICounterContractForwardImpl<ContractState>;
}

Since a class hash is only known after compilation, forwarding = true makes Scarb compile contracts in dependency order, injecting class hashes from earlier passes into later ones:

toml
[[target.starknet-contract]]
forwarding = true

Building a contract that uses static forwarding without this property enabled is an error.

The source code must also opt in to Cairo's #[feature("forward-impl")] gate. Every forwarded contract must be part of the same build set, either because it is defined in the package or because it is listed in build-external-contracts. Forwarding cycles and forwarding to a contract that embeds its own generated class hash are compile-time errors.

Test targets support the same forwarding property, so contracts compiled for tests embed the same class hashes. Auto-detected test targets inherit it from the package's starknet-contract targets, like they do with build-external-contracts.

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:

toml
[dependencies]
starknet = ">=2.19.6"
openzeppelin = "0.19.0"

[[target.starknet-contract]]
build-external-contracts = ["openzeppelin_presets::account::AccountUpgradeable"]

Wildcard support ​

It is possible to request building many contracts from a module tree at once. For example, the following snippet:

toml
[[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:

toml
[[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:

json
{
  "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 the Scarb.toml, some of the values might be null.

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:

toml
[[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:

toml
[[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.