Introduction to Starkli, Scarb and Katana

In this chapter, you’ll learn how to compile, deploy, and interact with a Starknet smart contract written in Cairo using starkli, scarb and katana.

First, confirm that the following commands work on your system. If they don’t, refer to Basic Installation in this chapter.

    scarb --version  # For Cairo code compilation
    starkli --version  # To interact with Starknet
    katana --version # To declare and deploy on local development

Crafting a Starknet Smart Contract

Important: Before we proceed with this example, please ensure that the versions of both katana and starkli match the specified versions provided below.

    katana --version  # 0.6.0-alpha.7
    starkli --version  # 0.2.8 (f59724e)

If this is not your case, you have to install them like this:

    dojoup -v 0.6.0-alpha.7
    starkliup -v 0.2.8

Now begin by initiating a Scarb project:

scarb new my_contract

Configure Environment Variables and the Scarb.toml File

Review the my_contract project. Its structure appears as:

    src/
      lib.cairo
    .gitignore
    Scarb.toml

Amend the Scarb.toml file to integrate the starknet dependency and introduce the starknet-contract target:

    [dependencies]
    starknet = ">=2.5.4"

    [[target.starknet-contract]]

For streamlined Starkli command execution, establish environment variables. Two primary variables are essential:

  • One for your account, a pre-funded account on the local development network
  • Another for designating the network, specifically the local katana devnet

In the src/ directory, create a .env file with the following:

export STARKNET_ACCOUNT=katana-0
export STARKNET_RPC=http://0.0.0.0:5050

These settings streamline Starkli command operations.

Declaring Smart Contracts in Starknet

Deploying a Starknet smart contract requires two primary steps:

  • Declare the contract's code.
  • Deploy an instance of that declared code.

Begin with the src/lib.cairo file, which provides a foundational template. Remove its contents and insert the following:

#![allow(unused)]
fn main() {
#[starknet::interface]
trait IHello<T> {
    fn get_name(self: @T) -> felt252;
    fn set_name(ref self: T, name: felt252);
}

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

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

    #[abi(embed_v0)]
    impl HelloImpl of super::IHello<ContractState> {
        fn get_name(self: @ContractState) -> felt252 {
            self.name.read()
        }

        fn set_name(ref self: ContractState, name: felt252) {
            self.name.write(name);
        }
    }
}
}

This rudimentary smart contract serves as a starting point.

Compile the contract with the Scarb compiler. If Scarb isn't installed, consult the Installation section.

scarb build

The above command results in a compiled contract under target/dev/, named "my_contract_hello.contract_class.json" (check Scarb's subchapter for more details).

Having compiled the smart contract, it's time to declare it with Starkli and katana. First, ensure your project acknowledges the environmental variables:

source .env

Next, launch Katana. In a separate terminal, run (more details in the Katan subchapter):

katana

To declare your contract, execute:

starkli declare target/dev/my_contract_hello.contract_class.json

Facing an "Error: Invalid contract class"? It indicates a version mismatch between Scarb's compiler and Starkli. Refer to the earlier steps to sync the versions. Typically, Starkli supports compiler versions approved by mainnet, even if the most recent Scarb version isn't compatible.

Upon successful command execution, you'll obtain a contract class hash: This unique hash serves as the identifier for your contract class within Starknet. For example:

Class hash declared: 0x00bfb49ff80fd7ef5e84662d6d256d49daf75e0c5bd279b20a786f058ca21418

Consider this hash as the contract class's address.

If you try to declare an already existing contract class, don't fret. Just proceed. You might see:

Not declaring class as its already declared. Class hash:
0x00bfb49ff80fd7ef5e84662d6d256d49daf75e0c5bd279b20a786f058ca21418

Deploying Starknet Smart Contracts

To deploy a smart contract on the katana local devnet, use the following command. It primarily requires:

  1. Your contract's class hash.
  2. Constructor arguments your contract needs (in our example, a name of type felt252).

Here's the command structure:

    starkli deploy \
        <CLASS_HASH> \
        <CONSTRUCTOR_INPUTS>

Notice the constructor inputs are in felt format. So we need to convert a short string to a felt252 format. We can use the to-cairo-string command for this:

    starkli to-cairo-string <STRING>

In this case, we'll use the string "starknetbook" as the name:

    starkli to-cairo-string starknetbook

The output:

    0x737461726b6e6574626f6f6b

Now deploy using a class hash and constructor input:

    starkli deploy \
        0x00bfb49ff80fd7ef5e84662d6d256d49daf75e0c5bd279b20a786f058ca21418 \
        0x737461726b6e6574626f6f6b

After running, expect an output similar to:

    Deploying class 0x00bfb49ff80fd7ef5e84662d6d256d49daf75e0c5bd279b20a786f058ca21418 with salt 0x054645c0d1e766ddd927b3bde150c0a3dc0081af7fb82160c1582e05f6018794...
    The contract will be deployed at address 0x07cdd583619462c2b14532eddb2b169b8f8d94b63bfb5271dae6090f95147a44
    Contract deployment transaction: 0x00413d9638fecb75eb07593b5c76d13a68e4af7962c368c5c2e810e7a310d54c
    Contract deployed: 0x07cdd583619462c2b14532eddb2b169b8f8d94b63bfb5271dae6090f95147a44

Interacting with Starknet Contracts

Using Starkli, you can interact with smart contracts through two primary methods:

  • call: For read-only functions.
  • invoke: For functions that alter the state.

Reading Data with call

The call command let's you query contract functions without transacting. For instance, if you want to determine the current contract owner using the get_name function, which requires no arguments:

    starkli call \
        <CONTRACT_ADDRESS> \
        get_name

Replace <CONTRACT_ADDRESS> with the address of your contract. The command will return the owner’s address, which was initially set during the contract’s deployment:

    [
        "0x0000000000000000000000000000000000000000737461726b6e6574626f6f6b"
    ]

But what is this lengthy output? In Starknet, we use the felt252 data type to represent strings. This can be decoded into its string representation:

starkli parse-cairo-string 0x737461726b6e6574626f6f6b

The result:

starknetbook

Modifying Contract State with invoke

To alter the contract's state, use the invoke command. For instance, if you want to update the name field in the storage, utilize the set_name function:

    starkli invoke \
        <CONTRACT_ADDRESS> \
        set_name \
        <felt252>

Where:

  • <CONTRACT_ADDRESS> is the address of your contract.
  • <felt252> is the new value for the name field, in felt252 format.

For example, to update the name to "Omar", first convert the string "Omar" to its felt252 representation:

    starkli to-cairo-string Omar

This will return:

    0x4f6d6172

Now, proceed with the invoke command:

    starkli invoke 0x07cdd583619462c2b14532eddb2b169b8f8d94b63bfb5271dae6090f95147a44 set_name 0x4f6d6172

Bravo! You've adeptly modified and interfaced with your Starknet contract.