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 upgradestarkliup
itself, run thecurl
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.
Previously, Starkli supported using both JSON-RPC and the sequencer gateway for accessing the network. However, following the deprecation of the sequencer gateway, support for using the sequencer gateway has been dropped. Therefore, the only provider supported now is JSON-RPC.
There are two ways to specify a JSON-RPC provider, either directly or through predefined networks.
💡 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
ℹ️ Note
When no provider option is supplied, Starkli falls back to using the
sepolia
network. If the network is not already defined, a free RPC vendor is used.You're advised against relying on the fallback to use the
sepolia
network, as the default network might change over time. Therefore, a warning is shown each time the fallback is used.
Using an RPC URL directly
There are a few options to obtain access to a JSON-RPC endpoint:
- hosting your own node with
pathfinder
orjuno
; or - using a third-party JSON-RPC API provider like Infura, Alchemy, Chainstack, or Nethermind.
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
While using
--rpc
orSTARKNET_RPC
is convenient for one-off command invocations, using predefined networks is recommended for more complicated use cases.
Using a predefined network
Networks can be defined in profiles. Each network is uniquely identified by an identifier within a profile. When the --network
option, or the STARKNET_NETWORK
environment variable, is used, Starkli looks up the network identifier in the current active profile, and uses its provider settings to connect to the network. See the profiles page for details on defining networks.
If the supplied network identifier is not found, Starkli terminates with an error, unless the network is eligible for free RPC vendors, in which case Starkli automatically creates the network and persists it into the profile.
For example, to check the block height of a predefined network 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.
ℹ️ Note
--rpc
orSTARKNET_RPC
take precedence over--network
orSTARKNET_NETWORK
. When both options are supplied,--network
(STARKNET_NETWORK
) is ignored, and a warning message is shown.
Free RPC vendors
Historically, the now-deprecated-and-removed sequencer gateway provider allowed new Starkli users to start interacting with Starknet without going through the hassle of obtaining a JSON-RPC endpoint. However, following the deprecation of the sequencer gateway, this is no longer an option. To maintain the same zero-setup experience, support for free RPC vendors was added.
The following 3 networks are eligible for free RPC vendors:
mainnet
sepolia
When using these networks, and when the network is not already defined in the active profile, a free vendor will be randomly chosen from below:
Once selected, the vendor choice is persisted in the profile. A message is printed to the console when this happens. All subsequent invocations under the same network use the already chosen vendor automatically.
💡 Tips
You can always change the automatically assigned free RPC vendor for a network by editing the profiles.
Profiles
Starkli supports profiles, where custom networks can be defined.
ℹ️ Note
Profiles only allow defining custom networks at the moment. More features will be added soon.
ℹ️ Note
Currently, only one profile
default
is supported. Defining additional profiles results in an error.
The profiles file
Depending on what operating system you're using, the profiles file are located at:
- Linux and Mac:
~/.config/starkli/profiles.toml
- Windows:
%AppData%\starkli\profiles.toml
💡 Tips
The profiles file is created automatically the first time you use a free RPC vendor. You can take the automatically generated file as a starting point for adding new networks.
Defining custom networks
Custom networks can be defined as <PROFILE_ID>.networks.<NETWORK_ID>
. Since only the default
profile is supported at the moment, networks should be defined as default.networks.<NETWORK_ID>
.
Each network contains the following properties:
Field | Mandatory | Type | Description |
---|---|---|---|
name | No | String | Human-readable network name, currently unused |
chain_id | Yes | String | String representation of the chain ID |
provider | Yes | String /Object | Provider configuration |
Provider configuration
The provider
field can be either a String
or an Object
. When the provider
value is an Object
, it must contain a type
field, whose value must be one of the following:
Value | Description |
---|---|
rpc | Use the JSON-RPC provider by specifying an endpoint URL |
free | Use a free RPC vendor |
rpc
provider variant
Field | Mandatory | Type | Description |
---|---|---|---|
type | Yes | String | Value must be rpc |
url | Yes | String | URL to the JSON-RPC endpoint |
free
provider variant
Field | Mandatory | Type | Description |
---|---|---|---|
type | Yes | String | Value must be free |
vendor | Yes | String | Must be one of blast and nethermind |
rpc
provider shorthand
The provider
value can also be a String
. When this is the case, it's used as a shorthand for the rpc
variant. So this value:
provider = "https://example.com/"
is the same as this:
provider = { type = "rpc", url = "https://example.com/" }
Example network configurations
This section contains a few example network configurations.
Network with the RPC provider
[default.networks.mainnet]
chain_id = "SN_MAIN"
provider = { type = "rpc", url = "https://example.com/" }
Network with the RPC provider shorthand
[default.networks.mainnet]
chain_id = "SN_MAIN"
provider = "https://example.com/"
Network with the free RPC vendor
[default.networks.mainnet]
chain_id = "SN_MAIN"
provider = { type = "free", vendor = "blast" }
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 butfalse
.
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. See this section for the supported variants.
Accounts can be created and managed through the starkli account
command. Variant-specific commands are available under starkli account <VARIANT>
(where <VARIANT>
is the unique identifier of the variant).
Supported account variants
The current version of Starkli supports these account variants (by alphabetical order):
Account creation
Before creating an account, you must first decide on the variant to use. As an example here, we will use the OpenZeppelin variant, whose identifier is oz
.
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 theSTARKNET_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.
Unlike the
init
command,deploy
needs to access the network, so a provider must also be configured.
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
Historically, Starknet transaction fees could only be paid with ETH. With the introduction of v3 transactions, however, users now have the option to pay fees in STRK if they so choose.
By default, Starkli sends transactions with ETH fees. The fee token can be set by using the --fee-token
option (e.g., --fee-token STRK
). Alternatively, you can also use the --eth
or --strk
shorthands.
ℹ️ Note
While it might seem to be the case, it's not true that v3 transactions provide the option to choose between fee tokens. Instead, v3 transactions are always paid with STRK. Starkli abstracts this away by automatically falling back to using older transaction versions when users choose to pay fees in ETH.
Setting transaction fees manually
By default, Starkli requests a fee estimate from the provider, and a 50% buffer is added on top of the estimate to avoid failures due to price fluctuations. However, sometimes it might be desirable to manually specify the fees instead. Some common reasons to do this include:
- The fee estimation returned by the provider is inaccurate;
- You want to speed up command execution by skipping fee estimation; or
- The transaction in question is flaky, so the estimation might fail.
Since transactions paying in ETH and STRK are priced differently, the options for manually setting fees are different depending on which token you're paying fees with.
Setting ETH fees manually
Transactions that pay fees in ETH are priced using a single max_fee
value, which indicates the maximum amount of fees (in Wei
) that an account is willing to pay.
Users can set the max_fee
value by using 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
, you can also 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
Setting STRK fees manually
Transactions that pay fees in STRK are priced differently from those that pay with ETH. Specifically, the fee is made up of two components: gas
and gas_price
, which indicate the maximum amount of L1 gas and the maximum L1 gas price a user is willing to pay, respectively.
To set the gas
value, simply use the --gas
option. For example, to manually set a gas
value of 50000
:
starkli invoke --strk eth transfer 0x1234 u256:100 --gas 50000
💡 Tips
When
gas
is manually set butgas_price
is not, Starkli does not perform a fee estimation and instead sources thegas_price
value directly from the pending block header. Therefore, transaction failure will not be detected until it's reverted on-chain.
To set the gas_price
value, use the --gas-price
option, which accepts a decimal value in STRK (18 decimals). For example, to set the L1 gas price at 0.0001
STRK:
starkli invoke --strk eth transfer 0x1234 u256:100 --gas-price 0.0001
Similar to setting max_fee
for ETH-priced transactions, the gas_price
value can also be set with raw values by using --gas-price-raw
. This command is equivalent to the one shown above:
starkli invoke --strk eth transfer 0x1234 u256:100 --gas-price-raw 100000000000000
💡 Tips
When
gas_price
is manually set butgas
is not, Starkli will still perform a fee estimation to determine how much L1 gas is needed.
Estimating the 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:
Key | Value |
---|---|
u256_max | 0xffffffffffffffffffffffffffffffff, 0xffffffffffffffffffffffffffffffff |
felt_max | 0x0800000000000011000000000000000000000000000000000000000000000000 |
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 thestarkli 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
andamount
) but we actually need to enter 3 (note the0
at the end). This is becauseamount
is of typeu256
, 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.
- Preparing your cryptographic keys and account.
- Compiling your Cairo contract with a simple example contract.
- Declaring a new class on Starknet.
- 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
sepolia
network fallback with free RPC vendor, but relying on the network fallback 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 theSTARKNET_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 withstr: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
.