mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2025-09-25 22:09:00 +02:00
Update cairo.html.markdown
This commit is contained in:
@@ -8,10 +8,10 @@ contributors:
|
||||
# Cairo
|
||||
|
||||
Cairo is a Turing-complete language that allows you write provable programs
|
||||
(where one party can prove to another that a certain computation
|
||||
was executed correctly) on StarkNet.
|
||||
(where one party can prove to another that a certain computation was executed
|
||||
correctly) on StarkNet.
|
||||
|
||||
# StarkNet
|
||||
## StarkNet
|
||||
|
||||
StarkNet is a decentralized ZK-rollup that operates as an Ethereum layer 2
|
||||
chain.
|
||||
@@ -24,7 +24,7 @@ syntax and how you could create and deploy a Cairo smart contract on StarkNet.
|
||||
want to check the [official docs](https://www.cairo-lang.org/docs) to confirm
|
||||
this document is still up-to-date. Pull requests are welcome!**
|
||||
|
||||
# Setting Up A Development Environment
|
||||
## Setting Up A Development Environment
|
||||
|
||||
Before we get started writing codes, we will need to setup a Cairo development
|
||||
environment, for writing, compiling and deploying our contracts to StarkNet.
|
||||
@@ -44,23 +44,23 @@ Protostar version displayed on the screen.
|
||||
## Initializing a new project
|
||||
|
||||
Protostar similar to Truffle for solidity development can be installed once and
|
||||
used for multiple projects.
|
||||
To initialize a new Protostar project, run the following command:
|
||||
used for multiple projects. To initialize a new Protostar project, run the
|
||||
following command:
|
||||
|
||||
```
|
||||
protostar init
|
||||
```
|
||||
|
||||
2. It would then request the project's name and the library's directory name,
|
||||
you'd need to fill in this, and a new project will be initialized
|
||||
successfully.
|
||||
It would then request the project's name and the library's directory name,
|
||||
you'd need to fill in this, and a new project will be initialized successfully.
|
||||
|
||||
## Compiling, Declaring, Deploying and Interacting with StarkNet Contracts
|
||||
|
||||
# Compiling, Declaring, Deploying And Interacting With StarkNet Contracts
|
||||
Within the `src` folder you'll find a boilerplate contract that comes with
|
||||
initializing a new Protostar project, `main.cairo`. We are going to be
|
||||
compiling, declaring and deploying this contract.
|
||||
|
||||
## Compiling Contracts
|
||||
### Compiling Contracts
|
||||
|
||||
To compile a Cairo contract using Protostar, ensure a path to the contract is
|
||||
specified in the `[contracts]` section of the `protostar.toml` file. Once
|
||||
@@ -74,12 +74,11 @@ And you should get an output similar to what you see below, with a `main.json`
|
||||
and `main_abi.json` files created in the `build` folder.
|
||||
<img src="./cairo_assets/build.png" alt="building your contract">
|
||||
|
||||
## Declaring Contracts
|
||||
### Declaring Contracts
|
||||
|
||||
With the recent StarkNet update to 0.10.3, the DEPLOY transaction was
|
||||
deprecated and no longer works. To deploy a transaction, you must first declare
|
||||
a Contract to obtain the class hash, then deploy the declared contract using
|
||||
the
|
||||
a Contract to obtain the class hash, then deploy the declared contract using the
|
||||
[Universal Deployer Contract](https://community.starknet.io/t/universal-deployer-contract-proposal/1864).
|
||||
|
||||
Before declaring or deploying your contract using Protostar, you should set the
|
||||
@@ -90,12 +89,14 @@ terminal. To set your private key in the terminal, run the command:
|
||||
export PROTOSTAR_ACCOUNT_PRIVATE_KEY=[YOUR PRIVATE KEY HERE]
|
||||
```
|
||||
|
||||
Then to declare our contract using Protostar run the following command:
|
||||
Then to declare our contract using Protostar run the following command (for
|
||||
visual clarity, the backslash sign symbolizes the continuing line):
|
||||
|
||||
```
|
||||
protostar declare ./build/main.json --network testnet --account
|
||||
0x0691622bBFD29e835bA4004e7425A4e9630840EbD11c5269DE51C16774585b16 --max-fee
|
||||
auto
|
||||
protostar declare ./build/main.json \
|
||||
--network testnet \
|
||||
--account 0x0691622bBFD29e835bA4004e7425A4e9630840EbD11c5269DE51C16774585b16 \
|
||||
--max-fee auto
|
||||
```
|
||||
|
||||
where `network` specifies the network we are deploying to, `account` specifies
|
||||
@@ -104,48 +105,47 @@ be paid for the transaction. You should get the class hash outputted as seen
|
||||
below:
|
||||
<img src="./cairo_assets/declare.png" alt="declaring your contract">
|
||||
|
||||
## Deploying Contracts
|
||||
### Deploying Contracts
|
||||
|
||||
After obtaining our class hash from declaring, we can now deploy using the
|
||||
below command:
|
||||
command below:
|
||||
|
||||
```
|
||||
protostar deploy
|
||||
0x02a5de1b145e18dfeb31c7cd7ff403714ededf5f3fdf75f8b0ac96f2017541bc --network
|
||||
testnet --account
|
||||
0x0691622bBFD29e835bA4004e7425A4e9630840EbD11c5269DE51C16774585b16 --max-fee
|
||||
auto
|
||||
protostar \
|
||||
deploy 0x02a5de1b145e18dfeb31c7cd7ff403714ededf5f3fdf75f8b0ac96f2017541bc \
|
||||
--network testnet \
|
||||
--account 0x0691622bBFD29e835bA4004e7425A4e9630840EbD11c5269DE51C16774585b16 \
|
||||
--max-fee auto
|
||||
```
|
||||
|
||||
where `0x02a5de1b145e18dfeb31c7cd7ff403714ededf5f3fdf75f8b0ac96f2017541bc` is
|
||||
the class hash of our contract.
|
||||
<img src="./cairo_assets/deploy.png" alt="deploying your contract">
|
||||
|
||||
## Interacting With Contracts
|
||||
### Interacting with Contracts
|
||||
|
||||
To interact with your deployed contract, we will be using Argent X
|
||||
(alternative - Braavos), and Starkscan (alternative - Voyager). To install and
|
||||
setup Argent X, check out this
|
||||
To interact with your deployed contract, we will be using `Argent X`
|
||||
(alternative: `Braavos`), and `Starkscan` (alternative: `Voyager`). To install
|
||||
and setup `Argent X`, see this
|
||||
[guide](https://www.argent.xyz/learn/how-to-create-an-argent-x-wallet/).
|
||||
|
||||
Copy your contract address, displayed on screen from the previous step, and
|
||||
head over to [Starkscan](https://testnet.starkscan.co/) to search for the
|
||||
contract. Once found, you can make write calls to the contract by following the
|
||||
steps below:
|
||||
contract. Once found, you can make write calls to the contract in the following
|
||||
sequence:
|
||||
|
||||
1. Click on the "connect wallet" button
|
||||
+ click on the "connect wallet" button,
|
||||
<img src="./cairo_assets/connect.png" alt="connect wallet">
|
||||
2. Select Argent X and approve the connection
|
||||
+ select `Argent X` and approve the connection
|
||||
<img src="./cairo_assets/connect2.png" alt="connect to argentX">
|
||||
3. You can now make read and write calls easily.
|
||||
+ you can now make read and write calls easily.
|
||||
|
||||
# Let's learn Cairo
|
||||
## Let's learn Cairo
|
||||
|
||||
First let's look at a default contract that comes with Protostar
|
||||
First let's look at a default contract that comes with Protostar which allows
|
||||
you to set balance on deployment, increase, and get the balance.
|
||||
|
||||
```cairo
|
||||
// Allows you to set balance on deployment, increase, and get the balance.
|
||||
|
||||
// Language directive - instructs compiler its a StarkNet contract
|
||||
%lang starknet
|
||||
|
||||
@@ -157,8 +157,7 @@ from starkware.cairo.common.cairo_builtins import HashBuiltin
|
||||
// @storage_var is a decorator that instructs the compiler the function
|
||||
// below it is a storage variable.
|
||||
@storage_var
|
||||
func balance() -> (res: felt) {
|
||||
}
|
||||
func balance() -> (res: felt){}
|
||||
|
||||
// @dev Constructor writes the balance variable to 0 on deployment
|
||||
// Constructors sets storage variables on deployment. Can accept arguments too.
|
||||
@@ -191,148 +190,142 @@ func get_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,
|
||||
let (res) = balance.read();
|
||||
return (res,);
|
||||
}
|
||||
|
||||
// before proceeding, try to build, deploy and interact with this contract!
|
||||
// NB: Should be at main.cairo if you are using Protostar.
|
||||
```
|
||||
|
||||
Now unto the main lessons
|
||||
Before proceeding to the main lessons, try to build, deploy and interact with
|
||||
this contract.
|
||||
NB: You should be at `main.cairo` if you are using Protostar.
|
||||
|
||||
### 1. THE FELT DATA TYPE
|
||||
### 1. The Felt data type
|
||||
|
||||
Unlike solidity, where you have access to various data types, Cairo comes with
|
||||
just a single data type `..felts`. Felts stands for Field elements, and are a
|
||||
252 bit integer in the range `0<=x<=P` where `P` is a prime number. You can
|
||||
create a `Uint256` in Cairo by utlizing a struct of two 128 bits felts.
|
||||
|
||||
```cairo
|
||||
// Unlike solidity, where you have access to various data types, Cairo
|
||||
// comes with just a single data type..felts
|
||||
// Felts stands for Field elements, and are a 252 bit integer in the range
|
||||
// 0<=x<=P where P is a prime number.
|
||||
// You can create a Uint256 in Cairo by utlizing a struct of two 128 bits
|
||||
// felts.
|
||||
|
||||
struct Uint256{
|
||||
low: felt, // The low 128 bits of the value.
|
||||
high: felt, // The high 128 bits of the value.
|
||||
}
|
||||
|
||||
// To avoid running into issues with divisions, it's safer to work with the
|
||||
// unsigned_div_rem method from Cairo-lang's library.
|
||||
```
|
||||
|
||||
### 2. LANG DIRECTIVE AND IMPORTS
|
||||
To avoid running into issues with divisions, it's safer to work with the
|
||||
`unsigned_div_rem` method from Cairo-lang's library.
|
||||
|
||||
### 2. Lang Directive and Imports
|
||||
|
||||
To get started with writing a StarkNet contract, you must specify the directive:
|
||||
|
||||
```cairo
|
||||
// To get started with writing a StarkNet contract, you must specify the
|
||||
// directive:
|
||||
|
||||
%lang starknet
|
||||
```
|
||||
|
||||
// This directive informs the compiler you are writing a contract and not a
|
||||
// program.
|
||||
// The difference between both is contracts have access to StarkNet's
|
||||
// storage, programs don't and as such are stateless.
|
||||
This directive informs the compiler you are writing a contract and not a
|
||||
program. The difference between both is contracts have access to StarkNet's
|
||||
storage, programs don't and as such are stateless.
|
||||
|
||||
// There are important functions you might need to import from the official
|
||||
// Cairo-lang library or Openzeppelin's. e.g.
|
||||
There are important functions you might need to import from the official
|
||||
Cairo-lang library or Openzeppelin's, e.g.
|
||||
|
||||
```cairo
|
||||
from starkware.cairo.common.cairo_builtins import HashBuiltin
|
||||
from cairo_contracts.src.openzeppelin.token.erc20.library import ERC20
|
||||
from starkware.cairo.common.uint256 import Uint256
|
||||
from starkware.cairo.common.bool import TRUE
|
||||
```
|
||||
|
||||
### 3. DATA STRUCTURES
|
||||
### 3. Data Structures
|
||||
|
||||
+ Storage variables: Cairo's storage is a map with `2^251` slots, where each
|
||||
slot is a felt which is initialized to `0`. You create one using the
|
||||
`@storage_var` decorator
|
||||
|
||||
```cairo
|
||||
// A. STORAGE VARIABLES
|
||||
// Cairo's storage is a map with 2^251 slots, where each slot is a felt
|
||||
// which is initialized to 0.
|
||||
// You create one using the @storage_var decorator
|
||||
|
||||
@storage_var
|
||||
func names() -> (name: felt){
|
||||
}
|
||||
func names() -> (name: felt){}
|
||||
```
|
||||
|
||||
// B. STORAGE MAPPINGS
|
||||
// Unlike soldity where mappings have a separate keyword, in Cairo you
|
||||
// create mappings using storage variables.
|
||||
+ Storage mappings: Unlike soldity where mappings have a separate keyword, in
|
||||
Cairo you create mappings using storage variables.
|
||||
|
||||
```cairo
|
||||
@storage_var
|
||||
func names(address: felt) -> (name: felt){
|
||||
}
|
||||
func names(address: felt) -> (name: felt){}
|
||||
```
|
||||
|
||||
// C. STRUCTS
|
||||
// Structs are a means to create custom data types in Cairo.
|
||||
// A Struct has a size, which is the sum of the sizes of its members. The
|
||||
// size can be retrieved using MyStruct.SIZE.
|
||||
// You create a struct in Cairo using the `struct` keyword.
|
||||
+ Structs: are a means to create custom data types in Cairo. A `struct` has a
|
||||
size, which is the sum of the sizes of its members. The size can be
|
||||
retrieved using `MyStruct.SIZE`. You create a struct in Cairo using the
|
||||
`struct` keyword.
|
||||
|
||||
```cairo
|
||||
struct Person {
|
||||
name: felt,
|
||||
age: felt,
|
||||
address: felt,
|
||||
}
|
||||
```
|
||||
|
||||
// D. CONSTANTS
|
||||
// Constants are fixed and as such can't be altered after being set.
|
||||
// They evaluate to an integer (field element) at compile time.
|
||||
// To create a constant in Cairo, you use the `const` keyword.
|
||||
// Its proper practice to capitalize constant names.
|
||||
+ Constants: Constants are fixed and as such can't be altered after being set.
|
||||
They evaluate to an integer (field element) at compile time. To create a
|
||||
constant in Cairo, you use the `const` keyword. Its proper practice to
|
||||
capitalize constant names.
|
||||
|
||||
const USER =
|
||||
0x01C6cfC1DB2ae90dACEA243F0a8C2F4e32560F7cDD398e4dA2Cc56B733774E9b
|
||||
```cairo
|
||||
const USER = 0x01C6cfC1DB2ae90dACEA243F0a8C2F4e32560F7cDD398e4dA2Cc56B733774E9b
|
||||
```
|
||||
|
||||
// E. ARRAYS
|
||||
// Arrays can be defined as a pointer(felt*) to the first element of the
|
||||
//array.
|
||||
// As an array is populated, its elements take up contigous memory cells.
|
||||
// The `alloc` keyword can be used to dynamically allocate a new memory
|
||||
// segment, which can be used to store an array
|
||||
+ Arrays: Arrays can be defined as a `pointer(felt*)` to the first element of
|
||||
the array. As an array is populated, its elements take up contigous memory
|
||||
cells. The `alloc` keyword can be used to dynamically allocate a new memory
|
||||
segment, which can be used to store an array:
|
||||
|
||||
```cairo
|
||||
let (myArray: felt*) = alloc ();
|
||||
assert myArray[0] = 1;
|
||||
assert myArray[1] = 2;
|
||||
assert myArray[3] = 3;
|
||||
```
|
||||
|
||||
// You can also use the `new` operator to create fixed-size arrays using
|
||||
//tuples
|
||||
// The new operator is useful as it enables you allocate memory and
|
||||
// initialize the object in one instruction
|
||||
You can also use the `new` operator to create fixed-size arrays using
|
||||
tuples. The new operator is useful as it enables you allocate memory and
|
||||
initialize the object in one instruction
|
||||
|
||||
```cairo
|
||||
func foo() {
|
||||
tempvar arr: felt* = new (1, 1, 2, 3, 5);
|
||||
assert arr[4] = 5;
|
||||
return ();
|
||||
}
|
||||
```
|
||||
|
||||
// F. TUPLES
|
||||
// A tuple is a finite, ordered, unchangeable list of elements
|
||||
// It is represented as a comma-separated list of elements enclosed by
|
||||
// parentheses
|
||||
// Their elements may be of any combination of valid types.
|
||||
+ Tuples: A tuple is a finite, ordered, unchangeable list of elements. It is
|
||||
represented as a comma-separated list of elements enclosed by parentheses.
|
||||
Their elements may be of any combination of valid types.
|
||||
|
||||
```cairo
|
||||
local tuple0: (felt, felt, felt) = (7, 9, 13);
|
||||
```
|
||||
|
||||
// G. EVENTS
|
||||
// Events allows a contract emit information during the course of its
|
||||
// execution, that can be used outside of StarkNet.
|
||||
// To create an event:
|
||||
+ Events: Events allows a contract emit information during the course of its
|
||||
execution, that can be used outside of StarkNet. An event can be created,
|
||||
subsequently emitted:
|
||||
|
||||
```cairo
|
||||
@event
|
||||
func name_stored(address, name) {
|
||||
}
|
||||
|
||||
// To emit an event:
|
||||
func name_stored(address, name) {}
|
||||
|
||||
name_stored.emit(address, name);
|
||||
```
|
||||
|
||||
### 4. CONSTRUCTORS, EXTERNAL AND VIEW FUNCTIONS
|
||||
### 4. Constructors, External and View functions
|
||||
|
||||
+ Constructors: Constructors are a way to intialize state variables on
|
||||
contract deployment. You create a constructor using the `@constructor`
|
||||
decorator.
|
||||
|
||||
```cairo
|
||||
// A. CONSTRUCTORS
|
||||
// Constructors are a way to intialize state variables on contract
|
||||
// deployment
|
||||
// You create a constructor using the @constructor decorator
|
||||
|
||||
@constructor
|
||||
func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,
|
||||
range_check_ptr}(_name: felt) {
|
||||
@@ -340,11 +333,13 @@ Now unto the main lessons
|
||||
names.write(caller, _name);
|
||||
return ();
|
||||
}
|
||||
```
|
||||
|
||||
// B. EXTERNAL FUNCTIONS
|
||||
// External functions are functions that modifies the state of the network
|
||||
// You create an external function using the @external decorator
|
||||
+ External functions: External functions are functions that modifies the state
|
||||
of the network. You create an external function using the `@external`
|
||||
decorator:
|
||||
|
||||
```cairo
|
||||
@external
|
||||
func store_name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,
|
||||
range_check_ptr}(_name: felt){
|
||||
@@ -353,88 +348,80 @@ Now unto the main lessons
|
||||
stored_name.emit(caller, _name);
|
||||
return ();
|
||||
}
|
||||
```
|
||||
|
||||
// C. VIEW FUNCTIONS
|
||||
// View functions do not modify the state of the blockchain
|
||||
// You can create a view function using the @view decorator
|
||||
+ View functions: View functions do not modify the state of the blockchain.
|
||||
You can create a view function using the `@view` decorator.
|
||||
|
||||
```cairo
|
||||
@view
|
||||
func get_name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,
|
||||
range_check_ptr}(_address: felt) -> (name: felt){
|
||||
let (name) = names.read(_address);
|
||||
return (name,);
|
||||
}
|
||||
|
||||
// NB: Unlike Solidity, Cairo supports just External and View function
|
||||
// types.
|
||||
// You can alternatively also create an internal function by not adding any
|
||||
// decorator to the function.
|
||||
```
|
||||
|
||||
### 5. DECORATORS
|
||||
NB: Unlike Solidity, Cairo supports just External and View function types.
|
||||
You can alternatively also create an internal function by not adding any
|
||||
decorator to the function.
|
||||
|
||||
### 5. Decorators
|
||||
|
||||
All functions in Cairo are specified by the `func` keyword, which can be
|
||||
confusing. Decorators are used by the compiler to distinguish between these
|
||||
functions.
|
||||
|
||||
Here are the most common decorators you'll encounter in Cairo:
|
||||
|
||||
+ `@storage_var` — used for specifying state variables.
|
||||
+ `@constructor` — used for specifying constructors.
|
||||
+ `@external` — used for specifying functions that write to a state variable.
|
||||
+ `@event` — used for specifying events
|
||||
+ `@view` — used to specify functions reading from a state variable
|
||||
+ `@contract_interface` — used for specifying function interfaces.
|
||||
+ `@l1_handler` — used for specifying functions that processes message sent from
|
||||
an L1 contract in a messaging bridge.
|
||||
|
||||
### 6. BUILTINS, HINTS & IMPLICIT Arguments
|
||||
|
||||
+ `BUILTINS` are predefined optimized low-level execution units, which are
|
||||
added to Cairo’s CPU board. They help perform predefined computations like
|
||||
pedersen hashing, bitwise operations etc, which are expensive to perform in
|
||||
Vanilla Cairo. Each builtin in Cairo is assigned a separate memory location,
|
||||
accessible through regular Cairo memory calls using implicit parameters. You
|
||||
specify them using the `%builtins` directive
|
||||
|
||||
Here is a list of available builtins in Cairo:
|
||||
|
||||
+ `output` — the output builtin is used for writing program outputs
|
||||
+ `pedersen` — the pedersen builtin is used for pedersen hashing
|
||||
computations
|
||||
+ `range_check` — This builtin is mostly used for integer comparisons,
|
||||
and facilitates check to confirm that a field element is within a range
|
||||
`[0, 2^128)`
|
||||
+ `ecdsa` — the ecdsa builtin is used for verifying ECDSA signatures
|
||||
+ `bitwise` — the bitwise builtin is used for carrying out bitwise
|
||||
operations on felts
|
||||
|
||||
+ `HINTS` are pieces of Python codes, which contains instructions that only
|
||||
the prover sees and executes. From the point of view of the verifier these
|
||||
hints do not exist. To specify a hint in Cairo, you need to encapsulate it
|
||||
within `%{` and `%}`. It is good practice to avoid using hints as much as
|
||||
you can in your contracts, as hints are not added to the bytecode, and thus
|
||||
do not count in the total number of execution steps.
|
||||
|
||||
```cairo
|
||||
// All functions in Cairo are specified by the `func` keyword, which can be
|
||||
// confusing.
|
||||
// Decorators are used by the compiler to distinguish between these
|
||||
// functions.
|
||||
|
||||
// Here are the most common decorators you'll encounter in Cairo:
|
||||
|
||||
// 1. @storage_var — used for specifying state variables.
|
||||
// 2. @constructor — used for specifying constructors.
|
||||
// 3. @external — used for specifying functions that write to a state
|
||||
// variable.
|
||||
// 4. @event — used for specifying events
|
||||
// 5. @view — used for specifying functions that reads from a state
|
||||
// variable.
|
||||
// 6. @contract_interface - used for specifying function interfaces.
|
||||
// 7. @l1_handler — used for specifying functions that processes message
|
||||
// sent from an L1 contract in a messaging bridge.
|
||||
```
|
||||
|
||||
### 6. BUILTINS, HINTS & IMPLICIT ARGUMENTS
|
||||
|
||||
```cairo
|
||||
// A. BUILTINS
|
||||
// Builtins are predefined optimized low-level execution units, which are
|
||||
// added to Cairo’s CPU board.
|
||||
// They help perform predefined computations like pedersen hashing, bitwise
|
||||
// operations etc, which are expensive to perform in Vanilla Cairo.
|
||||
// Each builtin in Cairo, is assigned a separate memory location,
|
||||
// accessible through regular Cairo memory calls using implicit parameters.
|
||||
// You specify them using the %builtins directive
|
||||
|
||||
// Here is a list of available builtins in Cairo:
|
||||
// 1. output — the output builtin is used for writing program outputs
|
||||
// 2. pedersen — the pedersen builtin is used for pedersen hashing
|
||||
// computations
|
||||
// 3. range_check — This builtin is mostly used for integer comparisons,
|
||||
// and facilitates check to confirm that a field element is within a range [0,
|
||||
// 2^128)
|
||||
// 4. ecdsa — the ecdsa builtin is used for verifying ECDSA signatures
|
||||
// 5. bitwise — the bitwise builtin is used for carrying out bitwise
|
||||
// operations on felts
|
||||
|
||||
// B. HINTS
|
||||
// Hints are pieces of Python codes, which contains instructions that only
|
||||
// the prover sees and executes
|
||||
// From the point of view of the verifier these hints do not exist
|
||||
// To specify a hint in Cairo, you need to encapsulate it within %{ and%}
|
||||
// Its good practice to avoid using hints as much as you can in your
|
||||
// contracts, as hints are not added to the bytecode, and thus do not count in the
|
||||
// total number of execution steps.
|
||||
|
||||
%{
|
||||
# Python hint goes here
|
||||
%}
|
||||
```
|
||||
|
||||
// C. IMPLICIT ARGUMENTS
|
||||
// Implicit arguments are not restricted to the function body, but can be
|
||||
// inherited by other functions calls that require them.
|
||||
// Implicit arguments are passed in between curly bracelets, like you can
|
||||
// see below:
|
||||
+ `IMPLICIT ARGUMENTS` are not restricted to the function body, but can be
|
||||
inherited by other functions calls that require them. Implicit arguments are
|
||||
passed in between curly bracelets, like you can see below:
|
||||
|
||||
```cairo
|
||||
func store_name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,
|
||||
range_check_ptr}(_name: felt){
|
||||
let (caller) = get_caller_address();
|
||||
@@ -444,21 +431,19 @@ Now unto the main lessons
|
||||
}
|
||||
```
|
||||
|
||||
### 7. ERROR MESSAGES & ACCESS CONTROLS
|
||||
### 7. Error Messages and Access Controls
|
||||
|
||||
You can create custom errors in Cairo which is outputted to the user upon failed
|
||||
execution. This can be very useful for implementing checks and proper access
|
||||
control mechanisms. An example is preventing a user to call a function except
|
||||
user is `admin`.
|
||||
|
||||
```cairo
|
||||
// You can create custom errors in Cairo which is outputted to the user
|
||||
// upon failed execution.
|
||||
// This can be very useful for implementing checks and proper access
|
||||
// control mechanisms.
|
||||
// An example is preventing a user to call a function except user is admin.
|
||||
|
||||
// imports
|
||||
from starkware.starknet.common.syscalls import get_caller_address
|
||||
|
||||
// create an admin constant
|
||||
const ADMIN =
|
||||
0x01C6cfC1DB2ae90dACEA243F0a8C2F4e32560F7cDD398e4dA2Cc56B733774E9b
|
||||
const ADMIN = 0x01C6cfC1DB2ae90dACEA243F0a8C2F4e32560F7cDD398e4dA2Cc56B733774E9b
|
||||
|
||||
// implement access control
|
||||
with_attr error_message("You do not have access to make this action!"){
|
||||
@@ -470,13 +455,13 @@ Now unto the main lessons
|
||||
// returning the specified error.
|
||||
```
|
||||
|
||||
### 8. CONTRACT INTERFACES
|
||||
### 8. Contract Interfaces
|
||||
|
||||
Contract interfaces provide a means for one contract to invoke or call the
|
||||
external function of another contract. To create a contract interface, you use
|
||||
the `@contract_interface` keyword:
|
||||
|
||||
```cairo
|
||||
// Contract interfaces provide a means for one contract to invoke or call
|
||||
// the external function of another contract.
|
||||
// To create a contract interface, you use the @contract_interface keyword
|
||||
|
||||
@contract_interface
|
||||
namespace IENS {
|
||||
func store_name(_name: felt) {
|
||||
@@ -485,27 +470,28 @@ Now unto the main lessons
|
||||
func get_name(_address: felt) -> (name: felt) {
|
||||
}
|
||||
}
|
||||
|
||||
// Once a contract interface is specified, any contract can make calls to
|
||||
// that contract passing in the contract address as the first parameter like this:
|
||||
|
||||
IENS.store_name(contract_address, _name);
|
||||
|
||||
// Note that Interfaces excludes the function body/logic and the implicit
|
||||
// arguments.
|
||||
```
|
||||
|
||||
### 9. RECURSIONS
|
||||
Once a contract interface is specified, any contract can make calls to that
|
||||
contract passing in the contract address as the first parameter like this:
|
||||
|
||||
```cairo
|
||||
// Due to the unavailability of loops, Recursions are the go-to for similar
|
||||
// operations.
|
||||
// In simple terms, a recursive function is one which calls itself
|
||||
// repeatedly.
|
||||
IENS.store_name(contract_address, _name);
|
||||
```
|
||||
|
||||
// A good example to demonstrate this is writing a function for getting the
|
||||
// nth fibonacci number:
|
||||
Note that Interfaces excludes the function body/logic and the implicit
|
||||
arguments.
|
||||
|
||||
### 9. Recursions
|
||||
|
||||
Due to the unavailability of loops, Recursions are the go-to for similar
|
||||
operations. In simple terms, a recursive function is one which calls itself
|
||||
repeatedly.
|
||||
|
||||
A good example to demonstrate this is writing a function for getting the nth
|
||||
fibonacci number:
|
||||
|
||||
```cairo
|
||||
@external
|
||||
func fibonacci{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,
|
||||
range_check_ptr}(n : felt) -> (result : felt){
|
||||
@@ -520,41 +506,38 @@ Now unto the main lessons
|
||||
let (local y) = fibonacci(n - 2);
|
||||
return (result=(x + y));
|
||||
}
|
||||
|
||||
// The nth fibonacci term is the sum of the nth - 1 and the nth - 2
|
||||
// numbers, that's why we get these two as (x, y) using recursion.
|
||||
// NB: when implementing recursive functions, always remember to implement
|
||||
// a base case (n==0, n==1 in our case), to prevent stack overflow.
|
||||
```
|
||||
|
||||
Some low-level stuffs
|
||||
The nth fibonacci term is the sum of the `nth - 1` and the `nth - 2` numbers,
|
||||
that's why we get these two as `(x,y)` using recursion.
|
||||
|
||||
### 10. REGISTERS
|
||||
NB: when implementing recursive functions, always remember to implement a base
|
||||
case (`n==0`, `n==1` in our case), to prevent stack overflow.
|
||||
|
||||
### 10. Registers
|
||||
|
||||
Registers holds values that may change over time. There are 3 major types of
|
||||
registers:
|
||||
|
||||
+ `ap` (allocation pointer) points to a yet unused memory. Temporary variables
|
||||
created using `let`, `tempvar` are held here, and thus susceptible to being
|
||||
revoked.
|
||||
+ `fp` (frame pointer) points to the frame of the current function. The address
|
||||
of all the function arguments and local variables are relative to this
|
||||
register and as such can never be revoked.
|
||||
+ `pc` (program counter) points to the current instruction.
|
||||
|
||||
### 11. Revoked References
|
||||
|
||||
Revoked references occurs when there is a call instruction to another function,
|
||||
between the definition of a reference variable that depends on `ap`(temp
|
||||
variables) and its usage. This occurs as the compiler may not be able to compute
|
||||
the change of `ap` (as one may jump to the label from another place in the
|
||||
program, or call a function that might change ap in an unknown way).
|
||||
|
||||
Here is an example to demonstrate what I mean:
|
||||
|
||||
```cairo
|
||||
// Registers holds values that may change over time.
|
||||
|
||||
// There are 3 major types of Registers:
|
||||
// 1. ap (allocation pointer) points to a yet unused memory. Temporary
|
||||
// variables created using `let`, `tempvar` are held here, and thus susceptible to
|
||||
// being revoked
|
||||
// 2. fp (frame pointer) points to the frame of the current function. The
|
||||
// address of all the function arguments and local variables are relative to this
|
||||
// register and as such can never be revoked
|
||||
// 3. pc (program counter) points to the current instruction
|
||||
```
|
||||
|
||||
### 11. REVOKED REFERENCES
|
||||
|
||||
```cairo
|
||||
// Revoked references occurs when there is a call instruction to another
|
||||
// function, between the definition of a reference variable that depends on
|
||||
// `ap`(temp variables) and its usage. This occurs as the compiler may not be able
|
||||
// to compute the change of `ap` (as one may jump to the label from another place
|
||||
// in the program, or call a function that might change ap in an unknown way).
|
||||
|
||||
// Here is an example to demonstrate what I mean:
|
||||
|
||||
@external
|
||||
func get_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,
|
||||
range_check_ptr}() -> (res: felt) {
|
||||
@@ -569,15 +552,17 @@ Some low-level stuffs
|
||||
let new_balance = balance * multiplier;
|
||||
return (res=new_balance);
|
||||
}
|
||||
```
|
||||
|
||||
// If you run that code, you'll run into the revoked reference error as we
|
||||
// are trying to access the `multiplier` variable after calling the get_balance
|
||||
// function;
|
||||
If you run that code, you'll run into the revoked reference error as we are
|
||||
trying to access the `multiplier` variable after calling the `get_balance`
|
||||
function.
|
||||
|
||||
// To solve revoked references, In simple cases you can resolve this issue,
|
||||
// by adding the keyword, `alloc_locals` within function scopes, but in most
|
||||
// complex cases you might need to create a local variable to resolve it.
|
||||
In simple cases you can resolve revoked references by adding the keyword
|
||||
`alloc_locals` within function scopes. In most complex cases you might need to
|
||||
create a local variable to resolve it.
|
||||
|
||||
```cairo
|
||||
// resolving the `double_balance` function:
|
||||
@external
|
||||
func double_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,
|
||||
@@ -590,36 +575,25 @@ Some low-level stuffs
|
||||
}
|
||||
```
|
||||
|
||||
Miscellaneous
|
||||
### 12. Understanding Cairo's Punctuations
|
||||
|
||||
### 12. Understanding Cairo's punctuations
|
||||
+ `;` (semicolon). Used at the end of each instruction
|
||||
+ `()` (parentheses). Used in a function declaration, if statements, and in a
|
||||
tuple declaration
|
||||
+ `{}` (curly braces). Used in a declaration of implicit arguments and to define
|
||||
code blocks.
|
||||
+ `[]` (square brackets). Standalone brackets represent the value at a
|
||||
particular address location (such as the allocation pointer, `[ap]`). Brackets
|
||||
following a pointer or a tuple act as a subscript operator, where `x[2]`
|
||||
represents the element with index `2` in `x`.
|
||||
+ `*` (single asterisk). Refers to the pointer of an expression.
|
||||
+ `%` (percent sign). Appears at the start of a directive, such as `%builtins`
|
||||
or `%lang`.
|
||||
+ `%{` and `%}` represent Python hints.
|
||||
+ `_` (underscore). A placeholder to handle values that are not used, such as an
|
||||
unused function return value.
|
||||
|
||||
```cairo
|
||||
// ; (semicolon). Used at the end of each instruction
|
||||
|
||||
// ( ) (parentheses). Used in a function declaration, if statements, and in
|
||||
// a tuple declaration
|
||||
|
||||
// { } (curly brackets). Used in a declaration of implicit arguments and to
|
||||
// define code blocks.
|
||||
|
||||
// [ ] (square brackets). Standalone brackets represent the value at a
|
||||
// particular address location (such as the allocation pointer, [ap]). Brackets
|
||||
// following a pointer or a tuple act as a subscript operator, where x[2]
|
||||
// represents the element with index 2 in x.
|
||||
|
||||
// * Single asterisk. Refers to the pointer of an expression.
|
||||
|
||||
// % Percent sign. Appears at the start of a directive, such as %builtins
|
||||
// or %lang.
|
||||
|
||||
// %{ %} Represents Python hints.
|
||||
|
||||
// _ (underscore). A placeholder to handle values that are not used, such
|
||||
// as an unused function return value.
|
||||
```
|
||||
|
||||
# FULL CONTRACT EXAMPLE
|
||||
## Full Contract Example
|
||||
|
||||
Below is a simple automated market maker contract example that implements most
|
||||
of what we just learnt! Re-write, deploy, have fun!
|
||||
@@ -635,11 +609,9 @@ of what we just learnt! Re-write, deploy, have fun!
|
||||
from starkware.starknet.common.syscalls import (get_caller_address,
|
||||
storage_read, storage_write)
|
||||
|
||||
//
|
||||
|
||||
// CONSTANTS
|
||||
//
|
||||
|
||||
|
||||
// @dev the maximum amount of each token that belongs to the AMM
|
||||
const BALANCE_UPPER_BOUND = 2 ** 64;
|
||||
|
||||
@@ -650,24 +622,20 @@ of what we just learnt! Re-write, deploy, have fun!
|
||||
const POOL_UPPER_BOUND = 2 ** 30;
|
||||
const ACCOUNT_BALANCE_BOUND = 1073741; // (2 ** 30 / 1000)
|
||||
|
||||
//
|
||||
|
||||
// STORAGE VARIABLES
|
||||
//
|
||||
|
||||
// @dev A map from account and token type to corresponding balance
|
||||
@storage_var
|
||||
func account_balance(account_id: felt, token_type: felt) -> (balance: felt){
|
||||
}
|
||||
func account_balance(account_id: felt, token_type: felt) -> (balance: felt){}
|
||||
|
||||
// @dev a map from token type to corresponding pool balance
|
||||
@storage_var
|
||||
func pool_balance(token_type: felt) -> (balance: felt) {
|
||||
}
|
||||
func pool_balance(token_type: felt) -> (balance: felt) {}
|
||||
|
||||
|
||||
//
|
||||
// GETTERS
|
||||
//
|
||||
|
||||
// @dev returns account balance for a given token
|
||||
// @param account_id Account to be queried
|
||||
// @param token_type Token to be queried
|
||||
@@ -689,10 +657,9 @@ of what we just learnt! Re-write, deploy, have fun!
|
||||
return pool_balance.read(token_type);
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
// EXTERNALS
|
||||
//
|
||||
|
||||
// @dev set pool balance for a given token
|
||||
// @param token_type Token whose balance is to be set
|
||||
// @param balance Amount to be set as balance
|
||||
@@ -784,10 +751,8 @@ of what we just learnt! Re-write, deploy, have fun!
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// INTERNALS
|
||||
//
|
||||
|
||||
// @dev internal function that updates account balance for a given token
|
||||
// @param account_id Account whose balance is to be modified
|
||||
// @param token_type Token type to be modified
|
||||
@@ -823,8 +788,7 @@ of what we just learnt! Re-write, deploy, have fun!
|
||||
// get pool balance
|
||||
let (local amm_from_balance) = get_pool_token_balance(token_type =
|
||||
token_from);
|
||||
let (local amm_to_balance) =
|
||||
get_pool_token_balance(token_type=token_to);
|
||||
let (local amm_to_balance) = get_pool_token_balance(token_type=token_to);
|
||||
|
||||
// calculate swap amount
|
||||
let (local amount_to, _) = unsigned_div_rem((amm_to_balance *
|
||||
@@ -845,6 +809,7 @@ of what we just learnt! Re-write, deploy, have fun!
|
||||
return (amount_to=amount_to);
|
||||
}
|
||||
|
||||
|
||||
// @dev internal function to get the opposite token type
|
||||
// @param token_type Token whose opposite pair needs to be gotten
|
||||
func get_opposite_token(token_type: felt) -> (t: felt) {
|
||||
@@ -856,42 +821,42 @@ of what we just learnt! Re-write, deploy, have fun!
|
||||
}
|
||||
```
|
||||
|
||||
# Additional Resources
|
||||
## Additional Resources
|
||||
|
||||
1. [Official documentation](https://www.cairo-lang.org/docs/)
|
||||
2. [Starknet EDU](https://medium.com/starknet-edu)
|
||||
3. [Journey through Cairo](https://medium.com/@darlingtonnnam/journey-through-cairo-i-setting-up-protostar-and-argentx-for-local-development-ba40ae6c5524)
|
||||
4. [Demystifying Cairo whitepaper](https://medium.com/@pban/demystifying-cairo-white-paper-part-i-b71976ad0108)
|
||||
5. [Learn about StarkNet with Argent](https://www.argent.xyz/learn/tag/starknet/)
|
||||
+ [Official documentation](https://www.cairo-lang.org/docs/)
|
||||
+ [Starknet EDU](https://medium.com/starknet-edu)
|
||||
+ [Journey through Cairo](https://medium.com/@darlingtonnnam/journey-through-cairo-i-setting-up-protostar-and-argentx-for-local-development-ba40ae6c5524)
|
||||
+ [Demystifying Cairo whitepaper](https://medium.com/@pban/demystifying-cairo-white-paper-part-i-b71976ad0108)
|
||||
+ [Learn about StarkNet with Argent](https://www.argent.xyz/learn/tag/starknet/)
|
||||
|
||||
# Development Frameworks
|
||||
## Development Frameworks
|
||||
|
||||
1. [Protostar](https://docs.swmansion.com/protostar/docs/tutorials/installation)
|
||||
2. [Nile](https://github.com/OpenZeppelin/nile)
|
||||
3. [StarkNet CLI](https://www.cairo-lang.org/docs/quickstart.html)
|
||||
+ [Protostar](https://docs.swmansion.com/protostar/docs/tutorials/installation)
|
||||
+ [Nile](https://github.com/OpenZeppelin/nile)
|
||||
+ [StarkNet CLI](https://www.cairo-lang.org/docs/quickstart.html)
|
||||
|
||||
# Helpful Libraries
|
||||
## Helpful Libraries
|
||||
|
||||
1. [Cairo-lang](https://github.com/starkware-libs/cairo-lang)
|
||||
2. [Openzeppelin](https://github.com/OpenZeppelin/cairo-contracts)
|
||||
+ [Cairo-lang](https://github.com/starkware-libs/cairo-lang)
|
||||
+ [Openzeppelin](https://github.com/OpenZeppelin/cairo-contracts)
|
||||
|
||||
# Educational Repos
|
||||
## Educational Repos
|
||||
|
||||
1. [StarkNet Cairo 101](https://github.com/starknet-edu/starknet-cairo-101)
|
||||
2. [StarkNet ERC721](https://github.com/starknet-edu/starknet-erc721)
|
||||
3. [StarkNet ERC20](https://github.com/starknet-edu/starknet-erc20)
|
||||
4. [L1 -> L2 Messaging](https://github.com/starknet-edu/starknet-messaging-bridge)
|
||||
5. [StarkNet Debug](https://github.com/starknet-edu/starknet-debug)
|
||||
6. [StarkNet Accounts](https://github.com/starknet-edu/starknet-accounts)
|
||||
7. [Min-Starknet](https://github.com/Darlington02/min-starknet)
|
||||
+ [StarkNet Cairo 101](https://github.com/starknet-edu/starknet-cairo-101)
|
||||
+ [StarkNet ERC721](https://github.com/starknet-edu/starknet-erc721)
|
||||
+ [StarkNet ERC20](https://github.com/starknet-edu/starknet-erc20)
|
||||
+ [L1 -> L2 Messaging](https://github.com/starknet-edu/starknet-messaging-bridge)
|
||||
+ [StarkNet Debug](https://github.com/starknet-edu/starknet-debug)
|
||||
+ [StarkNet Accounts](https://github.com/starknet-edu/starknet-accounts)
|
||||
+ [Min-Starknet](https://github.com/Darlington02/min-starknet)
|
||||
|
||||
# Security
|
||||
## Security
|
||||
|
||||
1. [Amarna static analysis for Cairo programs](https://blog.trailofbits.com/2022/04/20/amarna-static-analysis-for-cairo-programs/)
|
||||
2. [Cairo and StarkNet security by Ctrl03](https://ctrlc03.github.io/)
|
||||
3. [How to hack almost any Cairo smart contract](https://medium.com/ginger-security/how-to-hack-almost-any-starknet-cairo-smart-contract-67b4681ac0f6)
|
||||
4. [Analyzing Cairo code using Armana](https://dic0de.substack.com/p/analyzing-cairo-code-using-amarna?sd=pf)
|
||||
+ [Amarna static analysis for Cairo programs](https://blog.trailofbits.com/2022/04/20/amarna-static-analysis-for-cairo-programs/)
|
||||
+ [Cairo and StarkNet security by Ctrl03](https://ctrlc03.github.io/)
|
||||
+ [How to hack almost any Cairo smart contract](https://medium.com/ginger-security/how-to-hack-almost-any-starknet-cairo-smart-contract-67b4681ac0f6)
|
||||
+ [Analyzing Cairo code using Armana](https://dic0de.substack.com/p/analyzing-cairo-code-using-amarna?sd=pf)
|
||||
|
||||
# Future TO-DOs
|
||||
## Future TO-DOs
|
||||
|
||||
Update tutorial to fit Cairo 1.0
|
||||
|
Reference in New Issue
Block a user