Welcome to Starkli

Starkli is a command line tool for interacting with Starknet, powered by starknet-rs.

Starkli allows you to query Starknet and make transactions with ease. It's fast, easy to install, and intuitive to use.

Installation

The easiest way to install Starkli is to use starkliup, a portable script that downloads prebuilt binaries and manages shell configuration for you. However, it might not be available depending on your device's platform and/or architecture.

Note that if you use any installation method other than starkliup, you will need to manually set up shell completions.

Using starkliup

If you're on Linux/macOS/WSL/Android, you can install stakrliup by running the following command:

curl https://get.starkli.sh | sh

You might need to restart your shell session for the starkliup command to become available. Once it's available, run the starkliup command:

starkliup

Running the commands installs starkli for you, and upgrades it to the latest release if it's already installed.

starkliup detects your device's platform and automatically downloads the right prebuilt binary. It also sets up shell completions. You might need to restart your shell session for the completions to start working.

ℹ️ Note

Over time, starkliup itself may change and require upgrading. To upgrade starkliup itself, run the curl command above again.

Prebuilt binaries

Prebuilt binaries are available with GitHub releases for certain platforms.

Prebuilt binaries are best managed with starkliup. However, if you're on a platform where starkliup isn't available (e.g. using starkli on Windows natively), you can manually download the prebuilt binaries and make them available from PATH.

Install from source

If you have Rust installed, it's also possible to install starkli directly from source. Installing from source might be necessary if you want to use an unreleased feature, for example.

ℹ️ Note

Shell completions would not be configured when you install starkli from source. You need to manually set up shell completions for it to work.

To install from GitHub:

cargo install --locked --git https://github.com/xJonathanLEI/starkli

ℹ️ Note

It's not recommended to install Starkli from crates.io, as Starkli is no longer published there there after v0.1.8.

This is because Starkli uses Git dependencies due to the need to bundle multiple SemVer-compatible versions of the Sierra compiler.

Shell completions

Automatic shell completions are critical to good user experience for CLI tools. Starkli supports shell completions for the following shells:

  • bash
  • elvish
  • fish
  • powershell
  • zsh

When installing with starkliup, shell completions are automatically set up for the following shells:

  • bash
  • zsh

and you don't need to configure them yourself.

Otherwise, you can generate shell completion files by running the following command:

starkli completions <SHELL>

which prints the content of the completion file to the standard output. For example, to generate the completion file for fish:

starkli completions fish

You can pipe the output into a completion file and place it at the folder expected by your shell. You might need to restart your shell session for the changes to take effect.

Providers

Starkli connects to Starknet through "providers". Many commands require a provider to be supplied.

Currently, two providers are supported: JSON-RPC and the sequencer gateway (deprecated).

ℹ️ Note

When no provider option is supplied, Starkli falls back to using the sequencer gateway provider for the goerli-1 network.

JSON-RPC

Starkli is centric around JSON-RPC, and the JSON-RPC provider is considered canonical. Users are strongly recommended to use JSON-RPC. There are a few options to obtain access to a JSON-RPC endpoint:

Once you have a URL to a JSON-RPC endpoint, you can use it via the --rpc <URL> option for commands that expect it. For example:

starkli block-number --rpc http://localhost:9545/

Alternatively, you can set the STARKNET_RPC environment variable to make command invocations easier:

export STARKNET_RPC="http://localhost:9545/"

and then, simply run:

starkli block-number

which is the same as the running with the --rpc option.

💡 Tips

Each Starkli version only works with one specific JSON-RPC specification version. To check the supported JSON-RPC version, run the verbose version output command:

starkli -vV

Sequencer gateway

⚠️ Warning

The sequencer gateway is deprecated and will be disabled by StarkWare soon. You're strongly recommended to use the JSON-RPC provider instead.

Historically, before the JSON-RPC API became available, access to the network had been possible only through a set of API offered by StarkWare known as the sequencer gateway. As of this writing, despite the wide availability of the JSON-RPC API, StarkWare still runs the sequencer gateway, but has declared it as deprecated, and encourages users to migrate to use JSON-RPC instead.

ℹ️ Note

To raise awareness of the deprecation, Starkli always displays a warning message when the sequencer gateway provider is used.

To use the sequencer gateway anyways, use the --network <NETWORK> option, where <NETWORK> is one of the following:

  • mainnet
  • goerli-1
  • goerli-2
  • integration

For example, to check the latest block number on mainnet:

starkli block-number --network mainnet

Alternatively, you can set the STARKNET_NETWORK environment variable to make command invocations easier:

export STARKNET_NETWORK="mainnet"

and then, simply run:

starkli block-number

which is the same as the running with the --network option.

Signers

Starkli uses "signers" to sign transactions. Technically speaking, a signer can be anything that can provide valid signatures for transactions. In practice, the following signer types are currently supported:

More signer types will be supported as they become available. As of this writing, the most secure signer type is encrypted keystores.

Signers can be created and managed through the starkli signer command.

Encrypted keystores

Encrypted keystores are JSON files that follow the Web3 secret storage definition. A password must be supplied to create a keystore, and is required for subsequently using the keystore.

⚠️ Warning

Keystores are encrypted, but they're only as secure as the password you used to create them.

To create a fresh keystore from scratch:

starkli signer keystore new /path/to/keystore

and a keystore file will be created at /path/to/keystore.

You can then use it via the --keystore <PATH> option for commands expecting a signer.

Alternatively, you can set the STARKNET_KEYSTORE environment variable to make command invocations easier:

export STARKNET_KEYSTORE="/path/to/keystore"

ℹ️ Note

Even when STARKNET_KEYSTORE is set, it would be ignored by Starkli when any other signer option is supplied via the command line, including using the --keystore <PATH> option.

Plain text private keys

⚠️ Warning

Using plain text private keys is highly insecure. Never use this for production.

Plain text private keys are only intended to be used for development purposes, where security of keys does not matter. To generate a private key, run the command:

starkli signer gen-keypair

For commands that expect a signer, you can then use the --private-key <KEY> option. Alternatively, you can set the STARKNET_PRIVATE_KEY environment variable to make command invocations easier.

ℹ️ Note

Starkli shows a warning when you use plain-text private keys. If you know what you're doing, you can suppress this warning by setting the STARKLI_NO_PLAIN_KEY_WARNING to anything but false.

Accounts

Starkli sends out transactions through accounts. Starknet natively supports account abstraction and all accounts are smart contracts. Therefore, there are many "flavors" of accounts and Starkli supports the most popular ones. Starkli refers to these "flavors" as variants.

Currently, the only supported variant is OpenZeppelin's account contract implementation, but many more are expected to be supported soon.

Accounts can be created and managed through the starkli account command. Variant-specific commands are available under starkli account <VARIANT>.

Account creation

Before creating an account, you must first decide on the variant to use. As of this writing, the only supported variant is oz, the OpenZeppelin account contract.

All variants come with an init subcommand that creates an account file ready to be deployed. For example, to create an oz account:

starkli account oz init /path/to/account

ℹ️ Note

The starkli account oz init <PATH> command requires a signer. Starkli would complain that a signer is missing when running the command as shown, unless a keystore is specified via the STARKNET_KEYSTORE environment variable. See the signers page page for more details.

Account deployment

Once you have an account file, you can deploy the account contract with the starkli account deploy command. This command sends a DEPLOY_ACCOUNT transaction, which requires the account to be funded with some ETH for paying for the transaction fee.

For example, to deploy the account we just created:

starkli account deploy /path/to/account

ℹ️ Note

This command also requires a signer. You must provide the same signer used for creating the account file in the first place.

When run, the command shows the address where the contract will be deployed on, and instructs the user to fund the account before proceeding. Here's an example command output:

The estimated account deployment fee is 0.000011483579723913 ETH. However, to avoid failure, fund at least:
    0.000017225369585869 ETH
to the following address:
    0x01cf4d57ba01109f018dec3ea079a38fc08b789e03de4df937ddb9e8a0ff853a
Press [ENTER] once you've funded the address.

Once the account deployment transaction is confirmed, the account file will be update to reflect the deployment status. It can then be used for commands where an account is expected. You can pass the account either with the --account parameter, or with the STARKNET_ACCOUNT environment variable.

Account fetching

Account fetching allows recreating the account file from on-chain data alone. This could be helpful when:

  • the account file is lost; or
  • migrating an account from another tool/application.

The starkli account fetch commands creates an account file using just the address provided:

starkli account fetch <ADDRESS> --output /path/to/account

Running the command above creates the account file at /path/to/account.

Transaction fees

Starknet transactions are priced using a single max_fee value, which indicates the maximum amount of fees (in Wei) that an account is willing to pay.

For commands that send out transactions, Starkli needs to come up with this value. By default, a fee estimate is requested from the provider, and a 50% buffer is added on top of the estimate to avoid failures due to price fluctuations.

Setting max_fee manually

It's possible to skip the entire fee estimation process by manually providing a max_fee value.

The recommended way to do it is through the --max-fee option, which accepts a decimal value in Ether (18 decimals). For example, to perform an ETH transfer with a max_fee of 0.01 ETH:

starkli invoke eth transfer 0x1234 u256:100 --max-fee 0.01

If you already have the max_fee value in Wei, it's also possible to use the raw value directly via the --max-fee-raw option. An equivalent command to the example above would be:

starkli invoke eth transfer 0x1234 u256:100 --max-fee-raw 10000000000000000

Estimating fee only (dry run)

Commands that send out transactions accept a --estimate-only flag, which stops command execution as soon as an estimate is generated.

An example command to estimate the fee for an ETH transfer:

starkli invoke eth transfer 0x1234 u256:100 --estimate-only

Argument resolution

To make argument input easier and less error-prone, Starkli supports argument resolution, the process of expanding simple, human-readable input into actual field element arguments expected by the network.

By default, arguments are only parsed as raw field elements, accepting both decimal and hexadecimal representations.

To make arguments expandable, and thus trigger the resolution process, use the format scheme:content, where scheme is one of the supported schemes, and content is the scheme-specific content.

Schemes

addr

The addr scheme resolves the address name provided as content into a full address using an address book under the current network ID. As of this writing, the actual address book feature hasn't been implemented, and a hard-coded address book is used instead, which contains only one entry eth for the ETH token address.

u256

The u256 scheme interprets content as an unsigned 256-bit integer and resolves into 2 field element arguments for the low and high 128 bits, respectively. This scheme is useful for working with contracts expecting u256 arguments, such as the standard ERC20 contract.

str

The str scheme encodes content as Cairo short string.

const

The const scheme uses content as the key to look up a hard-coded table to commonly used constant values. The current list of constants are:

KeyValue
u256_max0xffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffff
felt_max0x0800000000000011000000000000000000000000000000000000000000000000

selector

The selector scheme calculates the Starknet Keccak hash for the content to derive the function entryponit.

storage

This scheme is currently the same as selector, but support for offsets and maps (e.g. ERC20_balances[0x1234]) might be added in the future to differentiate it.

Scheme omission

Normally, the scheme: prefix is required for opting in to argument resolution. However, there are a few exceptions:

  • the addr: prefix can be omitted when an address is expected;
  • the selector: prefix can be omitted when a selector is expected;
  • the storage: prefix can be omitted in the starkli storage command.

As an example, consider the starkli invoke command. To use the addr and selector schemes, one would run:

starkli invoke addr:eth selector:transfer ...

However, since the first positional argument for the starkli invoke is always expected to be an address, and the second one a selector, this command can be simplified into:

starkli invoke eth transfer ...

Declaring classes

In Starknet, all deployed contracts are instances of certain declared classes. Therefore, the first step of deploying a contract is declaring a class, if it hasn't been declared already.

With Starkli, this is done with the starkli declare command.

ℹ️ Note

You need both a signer and an account for this. The commands shown in this page omit the signer and account options for better readability, and assume you've properly configured the environment variables.

You can declare the following types of contract artifacts:

  • Sierra classes: output of the starknet-compile command; and
  • (Deprecated) Legacy Cairo 0 classes: output of the starknet-compile-deprecated command

To declare any class, simply run:

starkli declare /path/to/class/file

Starkli is capable of determining the type of class provided. There are no separate commands for Sierra and legacy classes.

Once the declaration is successful, Starkli displays the class hash declared. The class hash is needed for deploying contracts.

Sierra class compilation

When declaring Sierra classes, Starknet requires a so-called CASM hash to be provided. This is important because as of this writing, the Sierra-to-CASM compilation process isn't proven by the OS. Should the CASM hash not be provided and signed by the user, a malicious sequencer would be able to claim anything to be the CASM output, effectively deploying arbitrary code.

To come up with the CASM hash, Starkli compiles the Sierra class provided under the hood. By default, it automatically chooses one of the compiler versions shipped with Starkli itself based on the network. Users can override the compiler version used by providing a --compiler-version <VERSION> option.

ℹ️ Note

Unless you're working with custom networks where it's infeasible for Starkli to detect the right compiler version, you shouldn't need to manually choose a version with --compiler-version.

If Starkli does choose the wrong compiler version, try upgrading Starkli, or file a bug if you're already on the latest release.

ℹ️ Note

For advanced users, it's possible to skip the Sierra-to-CASM compilation process by directly providing a --casm-hash <CASM_HASH>.

Deploying contracts

Once you obtain a class hash by declaring a class, it's possible to deploy instances of the class.

With Starkli, this is done with the starkli deploy command.

ℹ️ Note

You need both a signer and an account for this. The commands shown in this page omit the signer and account options for better readability, and assume you've properly configured the environment variables.

To deploy a contract with class hash <CLASS_HASH>, simply run:

starkli deploy <CLASS_HASH> <CTOR_ARGS>

where <CTOR_ARGS> is the list of constructor arguments, if any.

💡 Tips

You might be able to leverage argument resolution to simplify the argument list input.

Under the hood, Starkli sends an INVOKE transaction to the Universal Deployer Contract, as Starknet does not support native external contract deployment transactions.

Invoking contracts

With Starkli, this is done with the starkli invoke command.

ℹ️ Note

You need both a signer and an account for this. The commands shown in this page omit the signer and account options for better readability, and assume you've properly configured the environment variables.

The basic format of a starkli invoke command is the following:

starkli invoke <ADDRESS> <SELECTOR> <ARGS>

For example, to transfer 100 Wei of the ETH token to the address 0x1234, one can run:

starkli invoke 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 transfer 0x1234 100 0

ℹ️ Note

The transfer function takes 2 parameters (i.e. recipient and amount) but we actually need to enter 3 (note the 0 at the end). This is because amount is of type u256, which consists of 2 raw field elements.

See the simplifying invoke commands section below for ways to make entering this command easier.

Simplifying invoke commands

You might be able to simplify invoke commands by leveraging argument resolution. In this section, we will take the ETH transfer command above and try to simplify it.

First, since the 0x049d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7 address is a well-known address available on the built-in address book as the eth entry, we can replace it with the use of the addr scheme:

starkli invoke addr:eth transfer 0x1234 100 0

Furthermore, as the addr:eth is the first positional argument in an invoke command, it's eligible for scheme omission, which means we can further simplify it by dropping the addr: prefix:

starkli invoke eth transfer 0x1234 100 0

Manually entering u256 values as 2 separate field element values is tedious and error-prone, especially with larger values. We can leverage the u256 scheme to have Starkli automatically split the values for us:

starkli invoke eth transfer 0x1234 u256:100

For more information regarding argument resolution, check out the argument resolution page.

Multicall support

Starkli has seamless support for multicall. To use more than 1 contract call in an invoke command, simply separate the calls with /.

For example, to also approve the sending of 300 Wei for address 0x4321 in the same transaction:

starkli invoke eth transfer 0x1234 u256:100 / eth approve 0x4321 u256:300

Starkli 101

In this tutorial, you will be guided from scratch to deploy contracts on the Starknet testnet.

  1. Preparing your cryptographic keys and account.
  2. Compiling your Cairo contract with a simple example contract.
  3. Declaring a new class on Starknet.
  4. Deploying a contract instance of a class on Starknet.

ℹ️ Note

To make it easier to get started, this tutorial skips the step of choosing a provider and uses the sequencer gateway fallback, but this is discouraged in practice.

Make sure to visit the providers page to learn more once you finish the tutorial.

Prepare a signer and an account

To interact with the network, you need an account to sign transactions. This tutorial assumes that you have no signer or account setup.

Initialize a signer

The signer is in charge of signing transactions. You can create a new pair of cryptographic key, or import an existing private key. Check out the signers page for more details.

Let's create a new signer here.

$ starkli signer keystore new /path/to/key.json

This will prompt you to enter a password and save your encrypted private key into the key.json keystore file.

To then use this keystore at each command, you can export the environment variable like:

$ export STARKNET_KEYSTORE="/path/to/key.json"

Initialize an account

We then initialize a new account, using OpenZeppelin class already declared on Starknet:

$ starkli account oz init /path/to/account.json

ℹ️ Note

Note that we didn't need to pass the --keystore option since the STARKNET_KEYSTORE environment variable is already set.

Fund and deploy the account

Up to this point, only the parameters for deploying the account have been generated, but the account itself hasn't been deployed yet. You must deploy the account to be able to use it.

To deploy the account, run:

$ starkli account deploy /path/to/account.json

The command would then displays a message similar to this:

The estimated account deployment fee is 0.000004323000964029 ETH. However, to avoid failure, fund at least:
    0.000006484501446043 ETH
to the following address:
    0x077c********************************************************3f8a
Press [ENTER] once you've funded the address.

ℹ️ Note

The address above is redacted just in case you accidentally send funds to it.

As instructed, you must pre-fund the address with ETH to be able to continue. Send enough ETH to the address, and then press the Enter key.

💡 Tips

You can use the starkli balance command (probably in a separate terminal session) to check whether the destinated account address has been successfully funded.

An account deployment transaction will be sent out. Once the transaction is confirmed, your account will be ready to use.

Again, to avoid having to pass the account file to each command invocation, we can export the STARKNET_ACCOUNT variable:

$ export STARKNET_ACCOUNT="/path/to/account.json"

For more details about accounts, please refer to the accounts page.

Compile your Cairo contract

The next step is to compile a Cairo contract. There are a few options for compiling Cairo contracts, but in this tutorial, we'll use Scarb.

ℹ️ Note

This tutorial uses Scarb v0.6.1.

First, create a Scrab project:

$ mkdir my_contract
$ cd ./my_contract/
$ scarb init

Update the Scarb.toml file to include the starknet dependency and add the starknet-contract target:

[package]
name = "my_contract"
version = "0.1.0"

[dependencies]
starknet = "=2.1.0"

[[target.starknet-contract]]

Then, replace the ./src/lib.cairo file with the following content:

#![allow(unused)]
fn main() {
// ** ./src/lib.cairo **

#[starknet::interface]
trait MyContractInterface<T> {
    fn name_get(self: @T) -> felt252;
    fn name_set(ref self: T, name: felt252);
}

#[starknet::contract]
mod my_contract {
    #[storage]
    struct Storage {
        name: felt252,
    }

    #[event]
    #[derive(Drop, starknet::Event)]
    enum Event {
        NameChanged: NameChanged,
    }

    #[derive(Drop, starknet::Event)]
    struct NameChanged {
        previous: felt252,
        current: felt252,
    }

    #[constructor]
    fn constructor(ref self: ContractState, name: felt252) {
        self.name.write(name);
    }

    #[external(v0)]
    impl MyContract of super::MyContractInterface<ContractState> {
        fn name_get(self: @ContractState) -> felt252 {
            self.name.read()
        }

        fn name_set(ref self: ContractState, name: felt252) {
            let previous = self.name.read();
            self.name.write(name);
            self.emit(NameChanged { previous, current: name });
        }
    }
}
}

Now build the contract:

$ scarb build

and the built contract artifact (aka. Sierra class) will be available at ./target/dev/my_contract_my_contract.sierra.json.

Declare the new class

Contract classes are like blueprints where actual contract instances can be deployed from. Now it's time to declare the new Sierra class. To declare, simply run:

$ starkli declare --watch ./target/dev/my_contract_my_contract.sierra.json

ℹ️ Note

If the class hash already exists, Starkli will not send out a declaration transaction.

This is not considered an error, but if you want to try a declaration anyways, you can do so by making your class slightly different by changing the Cairo code.

Starkli will then output the Cairo 1 class hash (which can also be obtained using the starkli class-hash <FILE.json> command). You'll need this class hash for deploying the contract.

If you followed the exact same steps with the exact same tooling versions, you should be getting the class hash of 0x0756ea65987892072b836b9a56027230bbe8fbed5e0370cff22778d071a0798e. It's normal that you arrive at a different hash, so don't worry about that.

Deploy your contract

Once your new class is declared, you can deploy an instance of your contract. Concrete contract instances hold state (storage values) while classes (what you declared at the last step) define the logic.

With Starkli, this is done via the deploy command. To deploy, you will need the class hash obtained in the last step when declaring. Apart from the hash, you will also need to pass the constructor arguments to your contract:

$ starkli deploy <CLASS_HASH> <CTOR_ARGS>

In the case of my_contract we declared above, the contract is expecting a single felt252 to be used as the name. A common way to represent strings as felt252 is to use the Cairo short string format. For example, you can find the Cairo short string representation of "starkli" with the to-cairo-string command:

$ starkli to-cairo-string starkli
0x737461726b6c69

You can use this value directly as an argument to deploy the contract:

$ starkli deploy --watch 0x0756ea65987892072b836b9a56027230bbe8fbed5e0370cff22778d071a0798e 0x737461726b6c69

💡 Tips

You can leverage argument resolution to simplify the argument list:

$ starkli deploy --watch 0x0756ea65987892072b836b9a56027230bbe8fbed5e0370cff22778d071a0798e str:starkli

Note how 0x737461726b6c69 is replaced with str:starkli. Learn more about argument resolution here.

Starkli prints the deployed contract address in the command output. You can use the address for interacting with the deployed contract.

Here we will use the address 0x06d8e1f3ed72fc87aa896639a0f50a4b9e59adb24de8a42b477957e1a7996e1b. You will get a different address when you deploy the contract yourself. Simply replace the addresses in the following commands with your own.

Let's query the current name set for the contract:

$ starkli call 0x06d8e1f3ed72fc87aa896639a0f50a4b9e59adb24de8a42b477957e1a7996e1b name_get
[
    "0x00000000000000000000000000000000000000000000000000737461726b6c69"
]

which is the Cairo short string representation of the text "starkli".

Let's change it to "starknet" instead:

$ starkli invoke --watch 0x06d8e1f3ed72fc87aa896639a0f50a4b9e59adb24de8a42b477957e1a7996e1b name_set str:starknet

Now query the name again:

$ starkli call 0x06d8e1f3ed72fc87aa896639a0f50a4b9e59adb24de8a42b477957e1a7996e1b name_get
[
    "0x000000000000000000000000000000000000000000000000737461726b6e6574"
]

And the name returned has changed. We've successfully modified the state of our contract.

Commands

Starkli offers the following commands:

  • selector
  • class-hash
  • to-cairo-string
  • parse-cairo-string
  • mont
  • call
  • transaction
  • block-number
  • block-hash
  • block
  • block-time
  • state-update
  • transaction-receipt
  • chain-id
  • balance
  • nonce
  • storage
  • class-hash-at
  • class-by-hash
  • class-at
  • syncing
  • signer
  • account
  • invoke
  • declare
  • deploy
  • completions

To check usage of each command, run with the --help option.

🏗️ TODO

Document each command instead of asking users to run --help.