Skip to content

Commit fcbc56a

Browse files
committed
add dependency examples
1 parent 95661ff commit fcbc56a

20 files changed

+686
-1
lines changed

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,25 @@
1-
# component-examples
1+
# component-examples
2+
3+
This repo contains a few examples of the potential interaction between components. For a review on components, see the [community forum post](https://community.starknet.io/t/cairo-components/101136). Build with `scarb build --workspace`, or a specific package via `scarb build --package dependency3`.
4+
5+
## Examples in the repo
6+
7+
### basic_component
8+
9+
An example of a basic upgradability component with no dependency
10+
11+
### dependency1
12+
13+
Upgradable depends on an `OwnableTrait` implementation to limit access to the `upgrade` function. The contract which uses the component directly implements `OwnableTrait` in order to use the upgradability component.
14+
15+
### dependency2
16+
17+
We now have two components, `Ownable` and `Upgradable`. `Ownable` has the internal function `initialize_owner`, and two external functions that are defined in the `IOwnable` interface. `Upgradable` depends on the contract implementing `IOwnable` to limit to the `upgrade` function. The contract uses both components. By using `Ownable`, we create an impl of `IOwnable<ContractState>`, which is need to use `Upgradable`.
18+
19+
### dependency3
20+
21+
Same as above, but now the `is_owner` function is moved to the internal trait `OwnableInternal`. This means that we need to access "internal" functions (i.e. functions in a none embeddable impl) of component A inside component B.
22+
23+
Since both `upgrade` and `is_owner` are now functions that expect `upgradable::ComponentState` and `ownable:ComponentState` respectively, we need a way to move from one to the other. To do this, we add a dependency on an implementation of `ownable::HasComponent<TContractState>` to `UpgradableImpl`. This allows us to do the following transition: `upgradable::ComponentState<TContractState>` &rarr; `TcontractState` &rarr; `ownable::ComponentState<TContractState>`.
24+
25+
In order to keep the `upgrade` function brief, we placed this transition logic inside a new impl called `GetOwnable`.

basic_component/scarb.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "basic_component"
3+
version = "0.1.0"
4+
5+
[[target.starknet-contract]]
6+
allowed-libfuncs-list.name = "audited"
7+
casm = true
8+
9+
[dependencies]
10+
starknet = ">=2.3.0-rc0"
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#[starknet::interface]
2+
trait ICounterContract<TContractState> {
3+
fn increase_counter(ref self: TContractState, amount: u128);
4+
fn decrease_counter(ref self: TContractState, amount: u128);
5+
fn get_counter(self: @TContractState) -> u128;
6+
}
7+
8+
#[starknet::contract]
9+
mod counter_contract {
10+
use basic_component::upgradable::upgradable as upgradable_component;
11+
12+
component!(path: upgradable_component, storage: upgradable, event: UpgradableEvent);
13+
14+
#[abi(embed_v0)]
15+
impl Upgradable = upgradable_component::UpgradableImpl<ContractState>;
16+
17+
#[storage]
18+
struct Storage {
19+
counter: u128,
20+
#[substorage(v0)]
21+
upgradable: upgradable_component::Storage
22+
}
23+
24+
#[event]
25+
#[derive(Drop, starknet::Event)]
26+
enum Event {
27+
CounterIncreased: CounterIncreased,
28+
CounterDecreased: CounterDecreased,
29+
UpgradableEvent: upgradable_component::Event
30+
}
31+
32+
#[derive(Drop, starknet::Event)]
33+
struct CounterIncreased {
34+
amount: u128
35+
}
36+
37+
#[derive(Drop, starknet::Event)]
38+
struct CounterDecreased {
39+
amount: u128
40+
}
41+
42+
#[constructor]
43+
fn constructor(ref self: ContractState, initial_counter: u128) {
44+
self.counter.write(initial_counter);
45+
}
46+
47+
#[external(v0)]
48+
impl CounterContract of super::ICounterContract<ContractState> {
49+
fn get_counter(self: @ContractState) -> u128 {
50+
self.counter.read()
51+
}
52+
53+
fn increase_counter(ref self: ContractState, amount: u128) {
54+
let current = self.counter.read();
55+
self.counter.write(current + amount);
56+
self.emit(CounterIncreased { amount });
57+
}
58+
59+
fn decrease_counter(ref self: ContractState, amount: u128) {
60+
let current = self.counter.read();
61+
self.counter.write(current - amount);
62+
self.emit(CounterDecreased { amount });
63+
}
64+
}
65+
}

basic_component/src/lib.cairo

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mod counter_contract;
2+
mod upgradable;
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
use starknet::ClassHash;
2+
3+
#[starknet::interface]
4+
trait IUpgradable<TContractState> {
5+
fn upgrade(ref self: TContractState, new_class_hash: ClassHash);
6+
}
7+
8+
#[starknet::component]
9+
mod upgradable {
10+
use starknet::ClassHash;
11+
use starknet::syscalls::replace_class_syscall;
12+
13+
#[storage]
14+
struct Storage {
15+
current_implementation: ClassHash
16+
}
17+
18+
#[event]
19+
#[derive(Drop, starknet::Event)]
20+
enum Event {
21+
ContractUpgraded: ContractUpgraded
22+
}
23+
24+
#[derive(Drop, starknet::Event)]
25+
struct ContractUpgraded {
26+
old_class_hash: ClassHash,
27+
new_class_hash: ClassHash
28+
}
29+
30+
#[embeddable_as(UpgradableImpl)]
31+
impl Upgradable<
32+
TContractState, +HasComponent<TContractState>
33+
> of super::IUpgradable<ComponentState<TContractState>> {
34+
fn upgrade(ref self: ComponentState<TContractState>, new_class_hash: ClassHash) {
35+
replace_class_syscall(new_class_hash).unwrap();
36+
let old_class_hash = self.current_implementation.read();
37+
self.emit(ContractUpgraded { old_class_hash, new_class_hash });
38+
self.current_implementation.write(new_class_hash);
39+
}
40+
}
41+
}

dependency1/scarb.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "dependency1"
3+
version = "0.1.0"
4+
5+
[[target.starknet-contract]]
6+
allowed-libfuncs-list.name = "audited"
7+
casm = true
8+
9+
[dependencies]
10+
starknet = ">=2.3.0-rc0"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
use starknet::ContractAddress;
2+
3+
#[starknet::interface]
4+
trait ICounterContract<TContractState> {
5+
fn increase_counter(ref self: TContractState, amount: u128);
6+
fn decrease_counter(ref self: TContractState, amount: u128);
7+
fn get_counter(self: @TContractState) -> u128;
8+
}
9+
10+
11+
#[starknet::contract]
12+
mod counter_contract {
13+
use starknet::{ContractAddress, get_caller_address};
14+
15+
use dependency1::upgradable::upgradable as upgradable_component;
16+
17+
component!(path: upgradable_component, storage: upgradable, event: UpgradableEvent);
18+
19+
#[abi(embed_v0)]
20+
impl Upgradable = upgradable_component::UpgradableImpl<ContractState>;
21+
22+
impl Ownable of dependency1::upgradable::OwnableTrait<ContractState> {
23+
fn is_owner(self: @ContractState, address: ContractAddress) -> bool {
24+
let caller = get_caller_address();
25+
let owner = self.owner_address.read();
26+
caller == owner
27+
}
28+
}
29+
30+
#[storage]
31+
struct Storage {
32+
counter: u128,
33+
owner_address: ContractAddress,
34+
#[substorage(v0)]
35+
upgradable: upgradable_component::Storage
36+
}
37+
38+
#[event]
39+
#[derive(Drop, starknet::Event)]
40+
enum Event {
41+
CounterIncreased: CounterIncreased,
42+
CounterDecreased: CounterDecreased,
43+
UpgradableEvent: upgradable_component::Event
44+
}
45+
46+
#[derive(Drop, starknet::Event)]
47+
struct CounterIncreased {
48+
amount: u128
49+
}
50+
51+
#[derive(Drop, starknet::Event)]
52+
struct CounterDecreased {
53+
amount: u128
54+
}
55+
56+
#[constructor]
57+
fn constructor(ref self: ContractState, initial_counter: u128) {
58+
self.counter.write(initial_counter);
59+
}
60+
61+
#[external(v0)]
62+
impl CounterContract of super::ICounterContract<ContractState> {
63+
fn get_counter(self: @ContractState) -> u128 {
64+
self.counter.read()
65+
}
66+
67+
fn increase_counter(ref self: ContractState, amount: u128) {
68+
if self.is_owner(get_caller_address()) {
69+
let current = self.counter.read();
70+
self.counter.write(current + amount);
71+
self.emit(CounterIncreased { amount });
72+
}
73+
}
74+
75+
fn decrease_counter(ref self: ContractState, amount: u128) {
76+
if self.is_owner(get_caller_address()) {
77+
let current = self.counter.read();
78+
self.counter.write(current - amount);
79+
self.emit(CounterDecreased { amount });
80+
}
81+
}
82+
}
83+
}

dependency1/src/lib.cairo

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
mod counter_contract;
2+
mod upgradable;

dependency1/src/upgradable.cairo

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use starknet::{ClassHash, ContractAddress};
2+
3+
#[starknet::interface]
4+
trait IUpgradable<TContractState> {
5+
fn upgrade(ref self: TContractState, new_class_hash: ClassHash);
6+
}
7+
8+
trait OwnableTrait<TContractState> {
9+
fn is_owner(self: @TContractState, address: ContractAddress) -> bool;
10+
}
11+
12+
#[starknet::component]
13+
mod upgradable {
14+
use starknet::{get_caller_address, ClassHash, ContractAddress};
15+
use starknet::syscalls::replace_class_syscall;
16+
17+
#[storage]
18+
struct Storage {
19+
current_implementation: ClassHash
20+
}
21+
22+
#[event]
23+
#[derive(Drop, starknet::Event)]
24+
enum Event {
25+
ContractUpgraded: ContractUpgraded
26+
}
27+
28+
#[derive(Drop, starknet::Event)]
29+
struct ContractUpgraded {
30+
old_class_hash: ClassHash,
31+
new_class_hash: ClassHash
32+
}
33+
34+
#[embeddable_as(UpgradableImpl)]
35+
impl Upgradable<
36+
TContractState, +HasComponent<TContractState>, +super::OwnableTrait<TContractState>
37+
> of super::IUpgradable<ComponentState<TContractState>> {
38+
fn upgrade(ref self: ComponentState<TContractState>, new_class_hash: ClassHash) {
39+
let is_owner = self.get_contract().is_owner(get_caller_address());
40+
if is_owner {
41+
replace_class_syscall(new_class_hash).unwrap();
42+
let old_class_hash = self.current_implementation.read();
43+
self.emit(ContractUpgraded { old_class_hash, new_class_hash });
44+
self.current_implementation.write(new_class_hash);
45+
}
46+
}
47+
}
48+
}

dependency2/scarb.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "dependency2"
3+
version = "0.1.0"
4+
5+
[[target.starknet-contract]]
6+
allowed-libfuncs-list.name = "audited"
7+
casm = true
8+
9+
[dependencies]
10+
starknet = ">=2.3.0-rc0"

0 commit comments

Comments
 (0)