diff options
| author | Darlington Nnam <Darlingtonnnam@gmail.com> | 2023-01-31 21:49:44 +0100 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2023-01-31 21:49:44 +0100 | 
| commit | 824fed572a1703b5130b8fbfce4b14fef28ede38 (patch) | |
| tree | 06b8a8c3ed21b812ac80206ae1a86421bfa9230a /cairo.html.markdown | |
| parent | 921aa02cc5162fae66bcc80026cb08d6938f60ed (diff) | |
Update cairo.html.markdown
Diffstat (limited to 'cairo.html.markdown')
| -rw-r--r-- | cairo.html.markdown | 1277 | 
1 files changed, 621 insertions, 656 deletions
| diff --git a/cairo.html.markdown b/cairo.html.markdown index f106860a..18ac81af 100644 --- a/cairo.html.markdown +++ b/cairo.html.markdown @@ -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 @@ -155,22 +155,21 @@ from starkware.cairo.common.cairo_builtins import HashBuiltin  // @dev Storage variable that stores the balance of a user.  // @storage_var is a decorator that instructs the compiler the function -// below it is a storage variable. +//   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.  @constructor  func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -    range_check_ptr}() {balance.write(0); return (); -} +  range_check_ptr}() {balance.write(0); return(); +  }  // @dev increase_balance updates the balance variable  // @param amount the amount you want to add to balance  // @external is a decorator that specifies the func below it is an external -// function. +//   function.  @external  func increase_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,    range_check_ptr}(amount: felt){ @@ -178,10 +177,10 @@ func increase_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,        assert_nn(amount);      } -  let (res) = balance.read(); -  balance.write(res + amount); -  return (); -} +    let (res) = balance.read(); +    balance.write(res + amount); +    return (); +  }  // @dev returns the balance variable  // @view is a decorator that specifies the func below it is a view function. @@ -190,708 +189,674 @@ func get_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*,    range_check_ptr}() -> (res: felt) {      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 - -```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. -``` +### 1. The Felt data type -### 2. LANG DIRECTIVE AND IMPORTS +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 -    // To get started with writing a StarkNet contract, you must specify the -    // directive: - -    %lang starknet +struct Uint256{ +  low: felt, // The low 128 bits of the value. +  high: felt, // The high 128 bits of the value. +  } +``` -    // 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. +To avoid running into issues with divisions, it's safer to work with the +`unsigned_div_rem` method from Cairo-lang's library. -    // There are important functions you might need to import from the official -    // Cairo-lang library or Openzeppelin's. e.g. +### 2. Lang Directive and Imports -    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 +To get started with writing a StarkNet contract, you must specify the directive:  ```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){ -        } - -    // B. STORAGE MAPPINGS -    // Unlike soldity where mappings have a separate keyword, in Cairo you -    // create mappings using storage variables. - -        @storage_var -        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. - -        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. - -        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 - -        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 - -        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. - -        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: - -        @event -        func name_stored(address, name) { -        } - -    // To emit an event: - -        name_stored.emit(address, name); +%lang starknet  ``` -### 4. CONSTRUCTORS, EXTERNAL AND VIEW FUNCTIONS +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.  ```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) { -            let (caller) = get_caller_address(); -            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 -        func store_name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -        range_check_ptr}(_name: felt){ -            let (caller) = get_caller_address(); -            names.write(caller, _name); -            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 -        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. +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  ``` -### 5. DECORATORS +### 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 +  @storage_var +  func names() -> (name: felt){} +  ``` + ++ 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){} +  ``` + ++ 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, +  } +  ``` + ++ 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. + +  ```cairo +  const USER = 0x01C6cfC1DB2ae90dACEA243F0a8C2F4e32560F7cDD398e4dA2Cc56B733774E9b +  ``` + ++ 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 + +  ```cairo +  func foo() { +    tempvar arr: felt* = new (1, 1, 2, 3, 5); +    assert arr[4] = 5; +    return (); +  } +  ``` + ++ 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); +  ``` + ++ 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) {} + +  name_stored.emit(address, name); +  ``` + +### 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 +  @constructor +  func constructor{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +  range_check_ptr}(_name: felt) { +    let (caller) = get_caller_address(); +    names.write(caller, _name); +    return (); +  } +  ``` + ++ 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){ +    let (caller) = get_caller_address(); +    names.write(caller, _name); +    stored_name.emit(caller, _name); +    return (); +  } +  ``` + ++ 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 + +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 +  %{ +    # Python hint goes here +  %} +  ``` + ++ `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(); +      names.write(caller, _name); +      stored_name.emit(caller, _name); +      return (); +  } +  ``` + +### 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 -    // 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. -``` +// imports +from starkware.starknet.common.syscalls import get_caller_address -### 6. BUILTINS, HINTS & IMPLICIT ARGUMENTS +// create an admin constant +const ADMIN = 0x01C6cfC1DB2ae90dACEA243F0a8C2F4e32560F7cDD398e4dA2Cc56B733774E9b -```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: - -        func store_name{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -        range_check_ptr}(_name: felt){ -            let (caller) = get_caller_address(); -            names.write(caller, _name); -            stored_name.emit(caller, _name); -            return (); -        } +// implement access control +with_attr error_message("You do not have access to make this action!"){ +  let (caller) = get_caller_address(); +  assert ADMIN = caller; +  } + +// using an assert statement throws if condition is not true, thus +// returning the specified error.  ``` -### 7. ERROR MESSAGES & ACCESS CONTROLS +### 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 -    // 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 - -    // implement access control -    with_attr error_message("You do not have access to make this action!"){ -        let (caller) = get_caller_address(); -        assert ADMIN = caller; +@contract_interface +  namespace IENS { +    func store_name(_name: felt) {      } -    // using an assert statement throws if condition is not true, thus -    // returning the specified error. +    func get_name(_address: felt) -> (name: felt) { +    } +  }  ``` -### 8. CONTRACT INTERFACES +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 -    // 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) { -            } - -            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. +IENS.store_name(contract_address, _name);  ``` -### 9. RECURSIONS +Note that Interfaces excludes the function body/logic and the implicit +arguments. -```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. - -    // A good example to demonstrate this is writing a function for getting the -    // nth fibonacci number: - -        @external -        func fibonacci{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -        range_check_ptr}(n : felt) -> (result : felt){ -            alloc_locals; -            if (n == 0){ -                return (0); -            } -            if (n == 1){ -                return (1); -            } -            let (local x) = fibonacci(n - 1); -            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. -``` +### 9. Recursions -Some low-level stuffs +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. -### 10. REGISTERS +A good example to demonstrate this is writing a function for getting the nth +fibonacci number:  ```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 +@external +func fibonacci{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +  range_check_ptr}(n : felt) -> (result : felt){ +    alloc_locals; +    if (n == 0){ +      return (0); +    } +    if (n == 1){ +      return (1); +    } +    let (local x) = fibonacci(n - 1); +    let (local y) = fibonacci(n - 2); +    return (result=(x + y)); +  }  ``` -### 11. REVOKED REFERENCES +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. -```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) { -            return (res=100); -        } - -        @external -        func double_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -        range_check_ptr}() -> (res: felt) { -            let multiplier = 2; -            let (balance) = get_balance(); -            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; - -    // 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. - -    // resolving the `double_balance` function: -        @external -        func double_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -        range_check_ptr}() -> (res: felt) { -            alloc_locals; -            let multiplier = 2; -            let (balance) = get_balance(); -            let new_balance = balance * multiplier; -            return (res=new_balance); -        } -``` +NB: when implementing recursive functions, always remember to implement a base +case (`n==0`, `n==1` in our case), to prevent stack overflow. -Miscellaneous +### 10. Registers -### 12. Understanding Cairo's punctuations +Registers holds values that may change over time. There are 3 major types of +registers: -```cairo -    // ; (semicolon). Used at the end of each instruction ++ `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. -    // ( ) (parentheses). Used in a function declaration, if statements, and in -    // a tuple declaration +### 11. Revoked References -    // { } (curly brackets). Used in a declaration of implicit arguments and to -    // define code blocks. +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). -    // [ ] (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. +Here is an example to demonstrate what I mean: -    // * Single asterisk. Refers to the pointer of an expression. +```cairo +@external +func get_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +  range_check_ptr}() -> (res: felt) { +    return (res=100); +  } -    // % Percent sign. Appears at the start of a directive, such as %builtins -    // or %lang. +@external +func double_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +  range_check_ptr}() -> (res: felt) { +    let multiplier = 2; +    let (balance) = get_balance(); +    let new_balance = balance * multiplier; +    return (res=new_balance); +  } +``` -    // %{ %} Represents Python hints. +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. -    // _ (underscore). A placeholder to handle values that are not used, such -    // as an unused function return value. +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*, +  range_check_ptr}() -> (res: felt) { +    alloc_locals; +    let multiplier = 2; +    let (balance) = get_balance(); +    let new_balance = balance * multiplier; +    return (res=new_balance); +  }  ``` -# FULL CONTRACT EXAMPLE +### 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. + +## 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!  ```cairo -    %lang starknet +%lang starknet -    from starkware.cairo.common.cairo_builtins import HashBuiltin -    from starkware.cairo.common.hash import hash2 -    from starkware.cairo.common.alloc import alloc -    from starkware.cairo.common.math import (assert_le, assert_nn_le, -    unsigned_div_rem) -    from starkware.starknet.common.syscalls import (get_caller_address, -    storage_read, storage_write) +from starkware.cairo.common.cairo_builtins import HashBuiltin +from starkware.cairo.common.hash import hash2 +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.math import (assert_le, assert_nn_le, +  unsigned_div_rem) +from starkware.starknet.common.syscalls import (get_caller_address, +  storage_read, storage_write) -    // -    // CONSTANTS -    // +// CONSTANTS +// +// @dev the maximum amount of each token that belongs to the AMM +const BALANCE_UPPER_BOUND = 2 ** 64; -    // @dev the maximum amount of each token that belongs to the AMM -    const BALANCE_UPPER_BOUND = 2 ** 64; +const TOKEN_TYPE_A = 1; +const TOKEN_TYPE_B = 2; -    const TOKEN_TYPE_A = 1; -    const TOKEN_TYPE_B = 2; +// @dev Ensure the user's balances are much smaller than the pool's balance +const POOL_UPPER_BOUND = 2 ** 30; +const ACCOUNT_BALANCE_BOUND = 1073741; // (2 ** 30 / 1000) -    // @dev Ensure the user's balances are much smaller than the pool's balance -    const POOL_UPPER_BOUND = 2 ** 30; -    const ACCOUNT_BALANCE_BOUND = 1073741; // (2 ** 30 / 1000) -    // -    // STORAGE VARIABLES -    // +// 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){} -    // @dev A map from account and token type to corresponding balance -    @storage_var -    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) {} -    // @dev a map from token type to corresponding pool balance -    @storage_var -    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 -    @view -    func get_account_token_balance{syscall_ptr: felt*, pedersen_ptr: -    HashBuiltin*, range_check_ptr}( -        account_id: felt, token_type: felt +// GETTERS +// +// @dev returns account balance for a given token +// @param account_id Account to be queried +// @param token_type Token to be queried +@view +func get_account_token_balance{syscall_ptr: felt*, pedersen_ptr: +  HashBuiltin*, range_check_ptr}( +    account_id: felt, token_type: felt      ) -> (balance: felt) { -        return account_balance.read(account_id, token_type); -    } +    return account_balance.read(account_id, token_type); +  } -    // @dev return the pool's balance -    // @param token_type Token type to get pool balance -    @view -    func get_pool_token_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -    range_check_ptr}( -        token_type: felt +// @dev return the pool's balance +// @param token_type Token type to get pool balance +@view +func get_pool_token_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +  range_check_ptr}( +    token_type: felt      ) -> (balance: felt) { -        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 -    @external -    func set_pool_token_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -    range_check_ptr}( -        token_type: felt, balance: felt -    ) { -        with_attr error_message("exceeds maximum allowed tokens!"){ -            assert_nn_le(balance, BALANCE_UPPER_BOUND - 1); -        } +    return pool_balance.read(token_type); +  } -        pool_balance.write(token_type, balance); -        return (); -    } - -    // @dev add demo token to the given account -    // @param token_a_amount amount of token a to be added -    // @param token_b_amount amount of token b to be added -    @external -    func add_demo_token{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -    range_check_ptr}( -        token_a_amount: felt, token_b_amount: felt -    ) { -        alloc_locals; -        let (account_id) = get_caller_address(); - -        modify_account_balance(account_id=account_id, token_type=TOKEN_TYPE_A, -        amount=token_a_amount); -        modify_account_balance(account_id=account_id, token_type=TOKEN_TYPE_B, -        amount=token_b_amount); - -        return (); -    } -    // @dev intialize AMM -    // @param token_a amount of token a to be set in pool -    // @param token_b amount of token b to be set in pool -    @external -    func init_pool{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -    range_check_ptr}( -        token_a: felt, token_b: felt +// 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 +@external +func set_pool_token_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +  range_check_ptr}( +    token_type: felt, balance: felt      ) { -        with_attr error_message("exceeds maximum allowed tokens!"){ -            assert_nn_le(token_a, POOL_UPPER_BOUND - 1); -            assert_nn_le(token_b, POOL_UPPER_BOUND - 1); -        } - -        set_pool_token_balance(token_type=TOKEN_TYPE_A, balance=token_a); -        set_pool_token_balance(token_type=TOKEN_TYPE_B, balance=token_b); - -        return (); +    with_attr error_message("exceeds maximum allowed tokens!"){ +      assert_nn_le(balance, BALANCE_UPPER_BOUND - 1);      } +  pool_balance.write(token_type, balance); +  return (); +  } -    // @dev swaps token between the given account and the pool -    // @param token_from token to be swapped -    // @param amount_from amount of token to be swapped -    // @return amount_to the token swapped to -    @external -    func swap{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( -        token_from: felt, amount_from: felt -    ) -> (amount_to: felt) { -        alloc_locals; -        let (account_id) = get_caller_address(); - -        // verify token_from is TOKEN_TYPE_A or TOKEN_TYPE_B -        with_attr error_message("token not allowed in pool!"){ -            assert (token_from - TOKEN_TYPE_A) * (token_from - TOKEN_TYPE_B) = 0; -        } - -        // check requested amount_from is valid -        with_attr error_message("exceeds maximum allowed tokens!"){ -            assert_nn_le(amount_from, BALANCE_UPPER_BOUND - 1); -        } - -        // check user has enough funds -        let (account_from_balance) = -        get_account_token_balance(account_id=account_id, token_type=token_from); -        with_attr error_message("insufficient balance!"){ -            assert_le(amount_from, account_from_balance); -        } - -        let (token_to) = get_opposite_token(token_type=token_from); -        let (amount_to) = do_swap(account_id=account_id, token_from=token_from, -        token_to=token_to, amount_from=amount_from); - -        return (amount_to=amount_to); -    } - - -    // -    // 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 -    // @param amount Amount Amount to be added -    func modify_account_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -    range_check_ptr}( -        account_id: felt, token_type: felt, amount: felt +// @dev add demo token to the given account +// @param token_a_amount amount of token a to be added +// @param token_b_amount amount of token b to be added +@external +func add_demo_token{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +  range_check_ptr}( +    token_a_amount: felt, token_b_amount: felt +  ) { +    alloc_locals; +    let (account_id) = get_caller_address(); + +    modify_account_balance(account_id=account_id, token_type=TOKEN_TYPE_A, +      amount=token_a_amount); +    modify_account_balance(account_id=account_id, token_type=TOKEN_TYPE_B, +      amount=token_b_amount); + +    return (); +  } + +// @dev intialize AMM +// @param token_a amount of token a to be set in pool +// @param token_b amount of token b to be set in pool +@external +func init_pool{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +  range_check_ptr}( +    token_a: felt, token_b: felt      ) { -        let (current_balance) = account_balance.read(account_id, token_type); -        tempvar new_balance = current_balance + amount; +      with_attr error_message("exceeds maximum allowed tokens!"){ +        assert_nn_le(token_a, POOL_UPPER_BOUND - 1); +        assert_nn_le(token_b, POOL_UPPER_BOUND - 1); +      } -        with_attr error_message("exceeds maximum allowed tokens!"){ -            assert_nn_le(new_balance, BALANCE_UPPER_BOUND - 1); -        } +      set_pool_token_balance(token_type=TOKEN_TYPE_A, balance=token_a); +      set_pool_token_balance(token_type=TOKEN_TYPE_B, balance=token_b); -        account_balance.write(account_id=account_id, token_type=token_type, -        value=new_balance); -        return (); -    } +      return (); +  } -    // @dev internal function that swaps tokens between the given account and -    // the pool -    // @param account_id Account whose tokens are to be swapped -    // @param token_from Token type to be swapped from -    // @param token_to Token type to be swapped to -    // @param amount_from Amount to be swapped -    func do_swap{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, -    range_check_ptr}( -        account_id: felt, token_from: felt, token_to: felt, amount_from: felt -    ) -> (amount_to: felt) { -        alloc_locals; - -        // 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); - -        // calculate swap amount -        let (local amount_to, _) = unsigned_div_rem((amm_to_balance * -        amount_from), (amm_from_balance + amount_from)); - -        // update token_from balances -        modify_account_balance(account_id=account_id, token_type=token_from, -        amount=-amount_from); -        set_pool_token_balance(token_type=token_from, balance=(amm_from_balance -        + amount_from)); - -        // update token_to balances -        modify_account_balance(account_id=account_id, token_type=token_to, -        amount=amount_to); -        set_pool_token_balance(token_type=token_to, balance=(amm_to_balance - -        amount_to)); - -        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) { -        if(token_type == TOKEN_TYPE_A) { -            return (t=TOKEN_TYPE_B); -        } else { -            return (t=TOKEN_TYPE_A); -        } +// @dev swaps token between the given account and the pool +// @param token_from token to be swapped +// @param amount_from amount of token to be swapped +// @return amount_to the token swapped to +@external +func swap{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, range_check_ptr}( +  token_from: felt, amount_from: felt +  ) -> (amount_to: felt) { +    alloc_locals; +    let (account_id) = get_caller_address(); + +    // verify token_from is TOKEN_TYPE_A or TOKEN_TYPE_B +    with_attr error_message("token not allowed in pool!"){ +      assert (token_from - TOKEN_TYPE_A) * (token_from - TOKEN_TYPE_B) = 0; +      } + +    // check requested amount_from is valid +    with_attr error_message("exceeds maximum allowed tokens!"){ +      assert_nn_le(amount_from, BALANCE_UPPER_BOUND - 1); +      } + +    // check user has enough funds +    let (account_from_balance) = +      get_account_token_balance(account_id=account_id, token_type=token_from); +    with_attr error_message("insufficient balance!"){ +      assert_le(amount_from, account_from_balance); +      } + +    let (token_to) = get_opposite_token(token_type=token_from); +    let (amount_to) = do_swap(account_id=account_id, token_from=token_from, +      token_to=token_to, amount_from=amount_from); + +    return (amount_to=amount_to); +  } + + +// 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 +// @param amount Amount Amount to be added +func modify_account_balance{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +range_check_ptr}( +  account_id: felt, token_type: felt, amount: felt +  ) { +    let (current_balance) = account_balance.read(account_id, token_type); +    tempvar new_balance = current_balance + amount; + +    with_attr error_message("exceeds maximum allowed tokens!"){ +      assert_nn_le(new_balance, BALANCE_UPPER_BOUND - 1); +      } + +    account_balance.write(account_id=account_id, token_type=token_type, +      value=new_balance); +    return (); +  } + +// @dev internal function that swaps tokens between the given account and +// the pool +// @param account_id Account whose tokens are to be swapped +// @param token_from Token type to be swapped from +// @param token_to Token type to be swapped to +// @param amount_from Amount to be swapped +func do_swap{syscall_ptr: felt*, pedersen_ptr: HashBuiltin*, +range_check_ptr}( +  account_id: felt, token_from: felt, token_to: felt, amount_from: felt +  ) -> (amount_to: felt) { +    alloc_locals; + +    // 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); + +    // calculate swap amount +    let (local amount_to, _) = unsigned_div_rem((amm_to_balance * +      amount_from), (amm_from_balance + amount_from)); + +    // update token_from balances +    modify_account_balance(account_id=account_id, token_type=token_from, +      amount=-amount_from); +    set_pool_token_balance(token_type=token_from, balance=(amm_from_balance +      + amount_from)); + +    // update token_to balances +    modify_account_balance(account_id=account_id, token_type=token_to, +      amount=amount_to); +    set_pool_token_balance(token_type=token_to, balance=(amm_to_balance - +      amount_to)); + +    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) { +    if(token_type == TOKEN_TYPE_A) { +      return (t=TOKEN_TYPE_B); +    } else { +      return (t=TOKEN_TYPE_A);      } +  }  ``` -# 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 | 
