1. Software

1.2 Clarinet

clarinet new my-project && cd my-project ;; Create a new project
clarinet contract new mycoolcontract ;; Create new contract in project
clarinet check ;; Check the syntax of Clarity
clarinet test ;; Test
clarinet console ;;
clarinet integrate ;; Launch Devnet with default configuration

Clarity.toml (configuration file of the project), contains a reference to contract files and dependencies.

Files in settings are used for deployment:

  • Devnet.toml - configuration used for tests and console mode.
  • Mocknet.toml
  • Mainnet.toml

1.2.1.Clarinet console

In the clarinet console you have multiple useful commands like:

(contract-call? contract-identifier function-name param-1 param-2 ...)
::set_tx_sender ST1SJ3DTE5DN7X54YDH5D64R3BCB6A2AG2ZQ8YPD5
::get_assets_maps

In REPLC blocks dont increment by themselves so if needed we can simulate mining with ::advance_chain_tip 10, that will increment the block height with 10.

2. Basics

;; Comments start with 2 semicolons.
(concat "Hello" " World!") ;;  

3.Types

List of types

Types are strictly enforced and can’t be mixed: (+ 2 u3) isn’t accepted.
3 categories of types: Primitives, Sequences and Composites.

3.1 Type: Primitives

These are signed and unsigned integers, booleans and principals

(* 4 (+ 15 10));; The result is 100.
(/ u99 u100) ;; Decimals are dropped not rounded up. The result will be 0.
(not true) ;; Inverts a boolean. The result is false
(and true true true) ;; 'and' returns true if all inputs are true. The result is true.
(or false true false) ;; 'or' returns true if at least one input is true. The result is true.

Principals

Represent a Stacks address on the blockchain.

'ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE ;; A Stacks address
'ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE.my-awesome-contract ;; A Stacks contract
(stx-get-balance 'ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE) ;; Checks balance of a Stacks address

3.2 Type: Sequences

3 types of sequences:

buffers:

;; Buffers start with 0x followed by a hexadecimal string
0x68656c6c6f21

strings:

"ACII string"
u"UTF-8 string"

lists:

(list 4 8 15 16 23 42) ;; Types cannot mix in a list
(list "Hello" "World" "!")

;; map applies an input function to each element
(map not (list true true false false)) ;; This returns [false false true true]

;; fold applies an input function to each element AND the output value of the previous application
(fold + (list u1 u2 u3) u4) ;; This returns 10

(len "How long is this string?")

;; Position counts start at 0
;; Functions that might or might not return a value tend to return an optional type named `some`
(element-at (list 4 8 15 16 23 42) u3) ;; Returns (some 16)
(index-of (list 4 8 15 16 23 42) 23) ;; Returns (some u4)
(index-of (list 4 8 15 16 23 42) 24) ;; Returns none

3.3 Type: Composites

Optionals

The type system in Clarity does not allow for empty values, so an integer always contains a number. To express a variable that could have some value or nothing we use the keyword some.

(some u5)
(some "An optional containing a string.")

;; To access the value contained within an optional, you have to unwrap it first:
(unwrap-panic (some u10)) ;; Returns (some u10)

Tuples

;; Declare a tuple
{
	id: u5, ;; a uint
	username: "ClarityIsAwesome", ;; an ASCI string
	address: 'ST1HTBVD3JG9C05J7HBJTHGR0GGW7KXW28M5JS8QE ;; and a principal
}
;; Get a value in a tuple:
(get username { id: 5, username: "bob" }) ;; Returns bob

;;Merge (to change) a tuple:
(merge
	{id: 5, score: 10, username: "bob"}
	{score: u20, active: true}
)
;; Returns {id: 5, score: 20, username: "bob", active: true}

Responses

;; Responses can be 'ok' or 'err'
(err u5)
(ok true)
(unwrap-panic (ok true)) ;; Returns true

Using the err response type we can be sure than any changes will revert if the error is called for.

3.4 Type Signatures

Type signature define the admitted type for variable or function argument

Type Signature
Buffer (buff max-len)
ASCII string (string-ascii max-len)
UTF-8 string (string-utf8 max-len)
List (list max-len element-type)
Optional (optional some-type)
Tuple {key1: type1, key2: type2}
Response (response ok-type err-type)

4. Keywords

List of all keywords in Clarity

block-height ;; Stacks block number
burn-block-height ;; Bitcoin's block number
tx-sender ;; Principal that sent the transaction
(as-contract tx-sender)
contract-caller ;; Contains the principal that called the function. It can be a standard principal or contract principal. If the contract is called via a signed transaction directly, then tx-sender and contract-caller will be the same. If the contract calls another contract in turn, then contract-caller will be equal to the previous contract in the chain.
contract-owner ;; Contains

5. Storing data

3 kinds of storage: Constants, Variables and Maps

5.1 Storing data: Constants

(define-constant my-constant "This is a constant")
(print my-constant)

5.2 Storing data: Variables

(define-data-var my-number uint u0)
(print (var-get my-number)) ;; Print the value
(var-set my-number u5000) ;; Change the value

5.3 Storing data: Maps

Maps are hash tables.
Maps are not iterable, you cannot loop though it to retrieve all values. The only way to access a value in a map is to enter the key.

(define-map map-name key-type value-type)

Set and delete values:

(map-set map-name tx-sender u100)    ;; map-set sets or overwrites a value
(map-insert map-name tx-sender u100) ;; map-insert fails if the key already exists
(map-delete map-name tx-sender)

Retrieve value:

(print (map-get? invoice u1))

Another more complex map:

(define-map listings
	uint
	{
		maker: principal,
		taker: (optional principal),
		token-id: uint
	}
)

6. Functions

3 kinds of functions: list of functions:

Public functions Private functions Read-only functions
Can be called externally. Only called by the current contract Can be called externally,
Calls require sending a transaction (fees).   but may not change the chain state (no fees)
(define-public function-signature function-body)

** Function signature ** Defines the name of the function and any input parameters.

(function-name (param1-name param1-type) (param2-name param2-type) ...)

Example:

(define-public (hello (name (string-ascii 30)))
	(ok (concat "Hola " name))
)

(print (hello "Clarity"))

** Function body ** The variadic begin function takes an arbitrary amount of inputs and will return the result of the last expression.

(define-public (print-twice (first (string-ascii 40)) (second (string-ascii 40)))
	(begin
		(print first)
		(print second)
		(ok true)
	)
)

(print-twice "Hello world!" "Multiple prints!")

Outputs:

"Hello world!"
"Multiple prints!"

7. Control flow functions

7.1 asserts!

If the boolean expression evaluates to true, then asserts! returns true and execution continues, but if the expression evaluates to false then asserts! will return the throw value and exit the current control flow.

(asserts! boolean-expression throw-value)

7.2 try!

The try! function takes an optional or a response type and will attempt to unwrap it.

(try! (some "wrapped string"))

try! can only unwrap some and ok values.
If it receives a none or an err, it will return it and exit the current control flow.

7.3 unwrap

The unwrap function takes an optional or a response type and will attempt to unwrap it, same as try!, but instead of propagating the noneor the err it will return the throw value instead.

(unwrap! (some "wrapped string") (err "unwrap failed"))

7.4 unwrap-err!

Take a response type and if its and err it will return the wrapped value, if not returns the throw value and exits.

7.5 unwrap-panic

Takes an optional or a response type and if it fails to unwarp it throws a runtime error and exists the current flow.

(unwrap-panic (ok true))

7.6 unwrap-err-panic

The input is unwrapped if its is an err, if not a runtime error in thrown

8 NFTs and FTs

8.1 SIP009: The NFT Standard

get-last-token-id
get-token-uri
get-owner
transfer

8.2 NFT stuff

(define-non-fungible-token asset-name identifier-type)
(nft-mint? asset-name toke-id recipient)
(nft-get-owner? asset-name token-id)
(nft-transfer? asset-name token-id sender recipient)
(nft-burn? asset-name token-id owner)

8.3 SIP010: The FT Standard

SIP010 defines the standard and is deployed to mainnet on:
SP3FBR2AGK5H9QBDH3EEN6DF8EK8JY7RX8QJ5SVTE.sip-010-trait-ft-standard

9 Traits

(define-trait my-trait
	(
		(function-name-1 (param-types-1) response-type-1)
		(function-name-2 (param-types-2) response-type-2)
	)
)

Latest NFT Trait on Mainnet according to SIP-009. They are older NFT Traits on Mainnet that shouldn’t be used anymore.

SIP-010 defined the Fungible Token Standard Trait on Mainnet.

I can implement a trait of another principal:

(impl-trait 'SP2PABAF9FTAJYNFZH93XENAJ8FVY99RRM50D2JG9.nft-trait.nft-trait

or one defined in the same one:

(impl-trait .multiplier-trait.multiplier)

In Clarinet.toml, the property depends_on = ["contract1", "contract2] contains the lists of contracts that a given contract depends on.

[contracts.mycoolcontract]
path = "contracts/mycoolcontract.clar"
depends_on = ["contract1", "contract2"]

(use-trait trait-alias trait-identifier)
(contract-call? .example-contract claim-wallet .timelocked-wallet)

10. Miscellaneous

is-eq: (is-eq a b) returns true if a=b
is-none: (is-none a) returns true if a is none (null).
>: (> a b) return true is a > b.
block-height returns block-height
(as-contract ...) So a smart contract can operate on assets it owns. This function executes the expression that is passed as an argument, with the tx-sender set to the contract’s principal instead of the current sender.
let ()
In this example, match conditionally calls print if the passed memo is some:

(match (some "inner string")
	inner-str (print inner-str)
	(print "got nothing")
)

11. Deploy a contract on tesnet

You can copy-paste and deploy here