Skip to content

Instantly share code, notes, and snippets.

@codeWhizperer
Created August 12, 2023 05:47
Show Gist options
  • Select an option

  • Save codeWhizperer/c88ce44537d1310407f659858d08e0b5 to your computer and use it in GitHub Desktop.

Select an option

Save codeWhizperer/c88ce44537d1310407f659858d08e0b5 to your computer and use it in GitHub Desktop.

Starknet Smart Contracts

A Starknet contract can be created by annotating a Cairo module with the #[starknet::contract] attribute.

Starknet contract don't require a main function. Functions in the contract module may be annotated as #[external(v0)] function. External functions can be called by the users of Starknet, and by other contracts.

The functions outside of these blocks are internal and cannot be accessed by users nor by other contract.

Functions in starknet contract have to explicitly define when writing to storage or reading from storage, by specifying the ref self:ContractState when writing to storage or self:@ContractState when reading from state.

e.g

fn write(ref self: ContractState){}

fn read (self: ContractState) -> T{}

where self stands for the contract state, seeing the self pass by reference as arguement tell us that it mutates state, as it gives access to the contract storage.

Contract Interface

The Contract interfaces is annotated with the #[starknet::interface] attribute. The constructor is not a part of the interface nor are internal function part of the interface.

Contract annotations

Here are the attributes/decorators available to annotate smart contracts.

| Annotation | Target | Description | | ----------------------- | ---------- | ----------------------------------------------- | --- | | #[starknet::contract] | module | Marks a module as a contract | | #[constructor] | function | Contract constructor, runs (only) on deployment | | | #[external(v0)] | function | An endpoint that modifies state | | #[external(v0)] | impl | Methods that modify state |

Storage

Contract storage is represented as a struct with name Storage and annotated with the #[storage] attribute.

#[storage]
struct Storage {
	store: felt252, // Can be any type

	// A u32 mapping to a bool
	mapped: LegacyMap::<u32, bool>,

	// Use `tuple`s to have multiple values mapping
	// Here a `ContractAddress` and a `u32` mapping to a Job struct
	multi_map: LegacyMap::<(ContractAddress, u32), Job>,
}

The keys of the struct are made into their own modules, these modules have read and write functions.

#[external(v0)]
fn play(ref self: ContractState) {
	self.store.write( 592 );
	let value = self.store.read(); // 592
}

LegacyMap types require a parameter with type matching the first generic type of the map when read/write.

#[external(v0)]
fn play(ref self:ContractState) {
	self.mapped.write( 83_u32, true );
	let value = self.multi_map.read( 83_u32 ); // true
}

ContractAddress type

ContractAddress type is a semantic type for wallet/contract addresses.

use starknet::ContractAddress;
use traits::TryInto; // Base TryInto trait
use starknet::Felt252TryIntoContractAddress; // felt > ContractAddress impl
use starknet::contract_address_to_felt252;
use option::OptionTrait; // To unwrap
use starknet::get_caller_address; // Gets caller address

use debug::PrintTrait; // Just for printing
#[test]
#[available_gas(2000000000)]
fn play() {
	let owner_felt: felt252 = 0x0390595E0f30299328F610C689fcFf5B0ee48eE971f0742b5568e5Dd1DE6e324;

	// Felt to ContractAddress
	let owner_addr: ContractAddress = owner_felt.try_into().unwrap();
	owner_addr.print();

	let caller = get_caller_address();
	// ContractAddress to Felt
	let owner_felt: felt252 = contract_address_to_felt252(caller);
	owner_felt.print();
}

Further information

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment