Learn CAIRO (starknet) syntax as a solidity dev

You’re a beginner in Cairo language, but you already knows solidity?

This article will give you all the basis of Cairo (compared) to solidity in less than 3 minutes.

Let’s start!

What is Cairo ?

Cairo is a new language for programming Starknet smart contracts.

Starknet a L2 scaling solution for Ethereum. (by using ZK-rollups)

Note that: cairo is NOT EVM compatible however there is a compiler from solidity to cairo which works quite well and is able to facilitate the migration.

Nice but how it works ?

To be simple, in Ethereum to verify a transaction all nodes of the network need to re execute the transactions (in order to assess if the transaction is valid) which are very costly in resources.

In the L2 Starkware, ONLY one node (of the full network) execute the transaction, and share a proof (using zero-knowledge) to others nodes that the transaction have been CORRECTLY executed.

The miners verify that the proof is valid which is way faster than replaying the transaction like in Ethereum.

Limitation of Cairo vs Solidity

In the Ethereum blockchain, your already solidity is a VERY limited langue.

  • You can’t generate random numbers securely without using an oracle. (Which can cost you a lot of funds…)
  • You can’t create smart contracts which the size once compiled excess 24kb.
  • You can’t do too costly calculations due to the block gas limit. (like machine learning for example)

Some of these limits disappear in Cairo but unfortunately others may spawn.

  • There is no loops is cairo language (although you can use recursive functions)
  • ALL calculations are performed in field elements (modulo p which is a large prime number), so if you divide 10/3 for example, you won’t get 3.333 nor 3 instead the result will be 10*(3^-1) [modulo p] which is a large number…
  • The cairo language is harder to learn than solidity

Solidity language is limited by scalability, Cairo language is also limited but by making provable programs (which is not easy).

Syntax of Cairo vs Solidity

The syntax of Cairo is very similar to Python, here is a quick “cheetsheet” of the Cairo syntax (compared with the solidity syntax).

▶ 0. Minimal code

pragram solidity ^0.8.0;contract Test { } // 2 lines of code needed for solidity.
%lang starknet // You can compile a starknet contract with only 1 line of code.

The goal of cairo is to create “provable programs”, so it’s not only used for Starknet but for others purposes too.

To create a Starknet contract, we have to explicitly indicate that by using “%lang starknet” directive.

▶ 1. Data Types

There is only one primitive type in Cairo: “felt” (stands for field element), this is a number modulus a prime number.

This is the sole primitive type of cairo. You can view at as an unt256 in solidity but modulo 2²⁵¹ + 2¹⁹²x17 + 1 (instead of 2²⁵⁶).

In computer science we can represent all type of data by numbers:

  • Addresses are numbers (between 0 and 16⁴⁰-1)
  • Booleans are numbers too (0 or 1)
  • Strings are numbers (by using the ASCII table)

Contrary to solidity there are pointers in cairo, represented by the type felt* (like in C).

A pointer is, a variable which contains the address of another variable in memory.

Structs exists too in cairo language, with some syntax differences.

Struct {
address a;
address b;
uint c;
}
struct:
member a: felt
member b: felt
member c: felt
end

▶ 2. Functions arguments/return values.

function test(address arg1,bool arg2) external returns(uint) {// External unction taking 2 arguments and returning an uint in solidity.
return(1)
}@external
func test(arg1: felt,arg2: felt) -> (x: felt):
# External function taking 2 arguments and returning an uint in cairo.
return(x=1)
end

Here are the main differences:

  • Some syntax differences.
  • Using of felt type for every type of data.
  • comments use the # identifier.
  • When you return a value, you have to specify the name of this value (here this is x), so instead of returning 1, you return x=1.

▶ 3. Declaring and using variables

uint test = 1 // soliditylet test = 1 // cairo 

In Starknet contracts, to declare a “variable” you need to use the keyword “let” most of the time.

But sometimes you have to use tempvar or local to avoid some issues.

▶ 4. Conditions/Loops

if (15 == true) {} else {}if (15 == true):else:end

Conditions are like in python, but with the end keyword. I don’t have much more to say.

uint total;
for (uint i =0, i< 10;i++) {
total += i
}
func compute_sum(n:felt) -> (sum:felt):
if n==0:
return (sum=0)
end
let (sum) = compute_sum(n=n-1)
let new_sum = sum+n
return (sum=new_sum)
end
# This function returns the sum of all integers bellow n

In cairo language, you need to use recursivity, in order to perform so kinds of loops.

Above, you have an example by using the sum of all integers from 1 to n.

▶ 6. Imports

import "file.name" // import in soldityfrom starkware.cairo.common.registers import get_ap // import in cairo

Here syntax is quite the same between cairo and python.

▶ 7. Storage Variable

name = 1 // in solidity this is a lot simpler
@storage_var
func balance() -> (res: felt):
end
@view
func get_balance{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr,
}
() -> (res : felt):
let (res) = balance.read()
return (res=res)
end

Storage variables are written like functions in cairo language, more over you need to add @storage_var before the definition of the function.

To read a storage variable, you need to call the read() method as above.

mapping (address => uint) balances // in solidity this is a lot simpler@storage_var
func balance() -> (res : felt):
end
# Increases the balance by the given amount.
@external
func increase_balance{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr,
}(amount : felt):
let (res) = balance.read()
balance.write(res + amount)
return ()
end

If you want to write a new value to the storage variable, you call the write() method and pass in the argument the value to write.

@storage_var
func balance(user : felt) -> (res : felt):
end
let (res) = balance.read(user= address )
balance.write(user,res)

If you want to implements mappings in cairo, you need to add arguments to the storage variable functions.

▶ 8. Getting msg.value and msg.data and transaction info

uint 256 val = msg.value
bytes dat = msg.data
from starkware.starknet.common.syscalls import get_caller_address
from starkware.starknet.common.syscalls import get_tx_info
...let val = get_caller_address()
let (block_number) = get_block_number()
let (block_timestamp) = get_block_timestamp()
let (tx_info) = get_tx_info() #tx info is a struct containing the desider informations
struct TxInfo:

member version : felt
member account_contract_address : felt
member max_fee : felt
member signature_len : felt
member signature : felt*
member transaction_hash : felt
member chain_id : felt
end

You can get all the informations, you need about transactions in cairo, most of the are accessible by calling the get_tx_info() function.

▶ 9. Constructors

@constructor
func constructor{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr,
}(arg1 : felt):
# constructor code
end

The constructor is a simple function, you just need to specify “@constructor ” before the declaration of the function.

▶ 10. Calling another smart contract

@contract_interface
namespace IContract:
func get_val() -> (res : felt):
end
end
@view
func call_get_val{syscall_ptr : felt*, range_check_ptr}(
contract_address : felt
) -> (res : felt):
let (res) = IContract.get_val(
contract_address=contract_address
)
return (res=res)
end

As in solidity, you need to create an interface and call the function with the contract address.

▶ 11. Events

@event
func f_called(
a : felt, b : felt
):
end

To declare an event, this is exactly the same as in solidity, you declare a function with parameters and their type (felt here).

Don’t forget the “@event “ before the function.

To emit the event, this is not harder. You need to call the emit method.

f_called.emit(1,2)

New Concepts

There is several new concepts in cairo language you need to grasp in order to profit from the full potential of the cairo language.

1. Memory is immutable.

Memory is immutable, you can’t delete memory at any address.

2. Implicit functions

You may seen is function definition between the name and the arguments, this “thing”:

{
syscall_ptr : felt*,
pedersen_ptr : HashBuiltin*,
range_check_ptr,
}

It’s called implicit arguments and they are needed for using some specials functions of cairo.

3. Address format

Addresses in Starknet use 32 bytes instead of 20 in Ethereum:

  • 0x0674ef0dd77276e0572c8c7f59a9732049dabc484b9cf7166d2992e2542d1bf5 in starknet
  • 0x9a8af21Ac492D5055eA7e1e49bD91BC9b5549334 is Ethereum

To go beyond…

Here are some link to master the cairo language.

--

--

Smart contract Auditor & Cybersecurity engineer, follow me on Twitter to get more value: https://twitter.com/TrustChain_DEFI

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
TrustChain

Smart contract Auditor & Cybersecurity engineer, follow me on Twitter to get more value: https://twitter.com/TrustChain_DEFI