-
Notifications
You must be signed in to change notification settings - Fork 92
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* chore: small doc addition * feat: add simple factory example * fix: use abi(embed_v0) #91 * feat: added contract class/instance separation
- Loading branch information
Showing
11 changed files
with
241 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
target |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
[package] | ||
name = "factory" | ||
version = "0.1.0" | ||
|
||
[dependencies] | ||
starknet = ">=2.3.0-rc0" | ||
|
||
[[target.starknet-contract]] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
mod simple_factory; | ||
mod target; | ||
|
||
#[cfg(test)] | ||
mod tests; |
64 changes: 64 additions & 0 deletions
64
listings/ch00-getting-started/factory/src/simple_factory.cairo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
use starknet::{ContractAddress, ClassHash}; | ||
|
||
#[starknet::interface] | ||
trait ICounterFactory<TContractState> { | ||
/// Create a new counter contract from stored arguments | ||
fn create_counter(ref self: TContractState) -> ContractAddress; | ||
|
||
/// Create a new counter contract from the given arguments | ||
fn create_counter_at(ref self: TContractState, init_value: u128) -> ContractAddress; | ||
|
||
/// Update the argument | ||
fn update_init_value(ref self: TContractState, init_value: u128); | ||
|
||
/// Update the class hash of the Counter contract to deploy when creating a new counter | ||
fn update_counter_class_hash(ref self: TContractState, counter_class_hash: ClassHash); | ||
} | ||
|
||
#[starknet::contract] | ||
mod CounterFactory { | ||
use starknet::{ContractAddress, ClassHash}; | ||
use starknet::syscalls::deploy_syscall; | ||
|
||
#[storage] | ||
struct Storage { | ||
/// Store the constructor arguments of the contract to deploy | ||
init_value: u128, | ||
/// Store the class hash of the contract to deploy | ||
counter_class_hash: ClassHash, | ||
} | ||
|
||
#[constructor] | ||
fn constructor(ref self: ContractState, init_value: u128, class_hash: ClassHash) { | ||
self.init_value.write(init_value); | ||
self.counter_class_hash.write(class_hash); | ||
} | ||
|
||
#[abi(embed_v0)] | ||
impl Factory of super::ICounterFactory<ContractState> { | ||
fn create_counter_at(ref self: ContractState, init_value: u128) -> ContractAddress { | ||
// Contructor arguments | ||
let mut constructor_calldata: Array::<felt252> = array![init_value.into()]; | ||
|
||
// Contract deployment | ||
let (deployed_address, _) = deploy_syscall( | ||
self.counter_class_hash.read(), 0, constructor_calldata.span(), false | ||
) | ||
.expect('failed to deploy counter'); | ||
|
||
deployed_address | ||
} | ||
|
||
fn create_counter(ref self: ContractState) -> ContractAddress { | ||
self.create_counter_at(self.init_value.read()) | ||
} | ||
|
||
fn update_init_value(ref self: ContractState, init_value: u128) { | ||
self.init_value.write(init_value); | ||
} | ||
|
||
fn update_counter_class_hash(ref self: ContractState, counter_class_hash: ClassHash) { | ||
self.counter_class_hash.write(counter_class_hash); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#[starknet::interface] | ||
trait ISimpleCounter<TContractState> { | ||
fn get_current_count(self: @TContractState) -> u128; | ||
fn increment(ref self: TContractState); | ||
fn decrement(ref self: TContractState); | ||
} | ||
|
||
#[starknet::contract] | ||
mod SimpleCounter { | ||
#[storage] | ||
struct Storage { | ||
// Counter variable | ||
counter: u128, | ||
} | ||
|
||
#[constructor] | ||
fn constructor(ref self: ContractState, init_value: u128) { | ||
// Store initial value | ||
self.counter.write(init_value); | ||
} | ||
|
||
#[abi(embed_v0)] | ||
impl SimpleCounter of super::ISimpleCounter<ContractState> { | ||
fn get_current_count(self: @ContractState) -> u128 { | ||
self.counter.read() | ||
} | ||
|
||
fn increment(ref self: ContractState) { | ||
// Store counter value + 1 | ||
let mut counter: u128 = self.counter.read() + 1; | ||
self.counter.write(counter); | ||
} | ||
fn decrement(ref self: ContractState) { | ||
// Store counter value - 1 | ||
let mut counter: u128 = self.counter.read() - 1; | ||
self.counter.write(counter); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
mod tests { | ||
use core::option::OptionTrait; | ||
use core::traits::Into; | ||
use core::traits::TryInto; | ||
use starknet::{ContractAddress, ClassHash, contract_address_const}; | ||
use starknet::syscalls::deploy_syscall; | ||
|
||
use factory::simple_factory::{ | ||
CounterFactory, ICounterFactoryDispatcher, ICounterFactoryDispatcherTrait | ||
}; | ||
use factory::target::{SimpleCounter, ISimpleCounterDispatcher, ISimpleCounterDispatcherTrait}; | ||
|
||
/// Deploy a counter factory contract | ||
fn deploy_factory( | ||
counter_class_hash: ClassHash, init_value: u128 | ||
) -> ICounterFactoryDispatcher { | ||
let caller_address: ContractAddress = contract_address_const::<'caller'>(); | ||
let deployed_contract_address = contract_address_const::<'market_factory'>(); | ||
|
||
let mut constructor_calldata: Array::<felt252> = array![ | ||
init_value.into(), counter_class_hash.into() | ||
]; | ||
|
||
let (factory_address, _) = deploy_syscall( | ||
CounterFactory::TEST_CLASS_HASH.try_into().unwrap(), | ||
0, | ||
constructor_calldata.span(), | ||
false | ||
) | ||
.expect('failed to deploy factory'); | ||
|
||
ICounterFactoryDispatcher { contract_address: factory_address } | ||
} | ||
|
||
#[test] | ||
#[available_gas(20000000)] | ||
fn test_deploy_counter_constructor() { | ||
let init_value = 10; | ||
|
||
let counter_class_hash: ClassHash = SimpleCounter::TEST_CLASS_HASH.try_into().unwrap(); | ||
let factory = deploy_factory(counter_class_hash, init_value); | ||
|
||
let counter_address = factory.create_counter(); | ||
let counter = ISimpleCounterDispatcher { contract_address: counter_address }; | ||
|
||
assert(counter.get_current_count() == init_value, 'Incorrect initial value'); | ||
} | ||
|
||
#[test] | ||
#[available_gas(20000000)] | ||
fn test_deploy_counter_argument() { | ||
let init_value = 10; | ||
let argument_value = 20; | ||
|
||
let counter_class_hash: ClassHash = SimpleCounter::TEST_CLASS_HASH.try_into().unwrap(); | ||
let factory = deploy_factory(counter_class_hash, init_value); | ||
|
||
let counter_address = factory.create_counter_at(argument_value); | ||
let counter = ISimpleCounterDispatcher { contract_address: counter_address }; | ||
|
||
assert(counter.get_current_count() == argument_value, 'Incorrect argument value'); | ||
} | ||
|
||
#[test] | ||
#[available_gas(20000000)] | ||
fn test_deploy_multiple() { | ||
let init_value = 10; | ||
let argument_value = 20; | ||
|
||
let counter_class_hash: ClassHash = SimpleCounter::TEST_CLASS_HASH.try_into().unwrap(); | ||
let factory = deploy_factory(counter_class_hash, init_value); | ||
|
||
let mut counter_address = factory.create_counter(); | ||
let counter_1 = ISimpleCounterDispatcher { contract_address: counter_address }; | ||
|
||
counter_address = factory.create_counter_at(argument_value); | ||
let counter_2 = ISimpleCounterDispatcher { contract_address: counter_address }; | ||
|
||
assert(counter_1.get_current_count() == init_value, 'Incorrect initial value'); | ||
assert(counter_2.get_current_count() == argument_value, 'Incorrect argument value'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
# Factory Pattern | ||
|
||
The factory pattern is a well known pattern in object oriented programming. It provides an abstraction on how to instantiate a class. | ||
|
||
In the case of smart contracts, we can use this pattern by defining a factory contract that have the sole responsibility of creating and managing other contracts. | ||
|
||
## Class hash and contract instance | ||
|
||
In Starknet, there's a separation between contract's classes and instances. A contract class serves as a blueprint, defined by the underling Cairo bytecode, contract's entrypoints, ABI and Sierra program hash. The contract class is identified by a class hash. When you want to add a new class to the network, you first need to declare it. | ||
|
||
When deploying a contract, you need to specify the class hash of the contract you want to deploy. Each instance of a contract has their own storage regardless of the class hash. | ||
|
||
Using the factory pattern, we can deploy multiple instances of the same contract class and handle upgrades easily. | ||
|
||
## Minimal example | ||
|
||
Here's a minimal example of a factory contract that deploy the `SimpleCounter` contract: | ||
|
||
```rust | ||
{{#include ../../../listings/ch00-getting-started/factory/src/simple_factory.cairo}} | ||
``` | ||
|
||
<!-- This is not ready for "Open in remix" because we need multiple files --> | ||
|
||
This factory can be used to deploy multiple instances of the `SimpleCounter` contract by calling the `create_counter` and `create_counter_at` functions. | ||
|
||
The `SimpleCounter` class hash is stored inside the factory, and can be upgraded with the `update_counter_class_hash` function which allows to reuse the same factory contract when the `SimpleCounter` contract is upgraded. | ||
|
||
This minimal example lacks several useful features such as access control, tracking of deployed contracts, events, ... | ||
|
||
<!-- TODO maybe add a more complete example at the end of this section or in the `Applications examples` chapter --> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
# Interacting with contracts | ||
# Deploy and interact with contracts | ||
|
||
In this chapter, we will see how to interact with contracts. | ||
In this chapter, we will see how to deploy and interact with contracts. |