Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update extensibility docs #823

Merged
merged 25 commits into from
Dec 13, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
600737a
add extensibility docs
andrew-fleming Nov 21, 2023
54cfe2a
Merge branch 'main' into extensibility-docs
andrew-fleming Nov 21, 2023
22019dc
start setup guide
andrew-fleming Nov 22, 2023
34ca611
change file name
andrew-fleming Nov 24, 2023
f409e0e
Apply suggestions from code review
andrew-fleming Nov 26, 2023
46191e2
fix name
andrew-fleming Nov 26, 2023
4f7035b
Merge branch 'main' into extensibility-docs
andrew-fleming Nov 29, 2023
a9f3079
change title
andrew-fleming Nov 30, 2023
007d633
change title to component impl
andrew-fleming Nov 30, 2023
8ddd61d
update changelog
andrew-fleming Nov 30, 2023
30669d2
add comp storage section
andrew-fleming Nov 30, 2023
97b2c7f
finish new structure
andrew-fleming Dec 1, 2023
441697e
finish edits, add customization section
andrew-fleming Dec 2, 2023
cad8e88
simplify titles
andrew-fleming Dec 2, 2023
269ff1e
fix conflicts
andrew-fleming Dec 6, 2023
a0a8833
fix conflicts
andrew-fleming Dec 7, 2023
15c29d9
finish custom impl section
andrew-fleming Dec 11, 2023
ed2b11c
fix conflicts
andrew-fleming Dec 11, 2023
a56dc3f
Apply suggestions from code review
andrew-fleming Dec 11, 2023
8165f92
fix comp storage section
andrew-fleming Dec 12, 2023
d714caf
change section title to setup, minor edits
andrew-fleming Dec 12, 2023
4032962
add cmp storage link, clean up impl section
andrew-fleming Dec 12, 2023
319b3b2
add cairo book link
andrew-fleming Dec 12, 2023
a0b5edd
add api design tip
andrew-fleming Dec 12, 2023
6e0e45b
Apply suggestions from code review
martriay Dec 13, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/modules/ROOT/nav.adoc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
* xref:index.adoc[Overview]
//* xref:wizard.adoc[Wizard]
//* xref:extensibility.adoc[Extensibility]
* xref:components.adoc[Using Components]
martriay marked this conversation as resolved.
Show resolved Hide resolved
* xref:interfaces.adoc[Interfaces and Dispatchers]
* xref:upgrades.adoc[Upgrades]
** xref:/api/upgrades.adoc[API Reference]
Expand Down
186 changes: 186 additions & 0 deletions docs/modules/ROOT/pages/components.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
= Using Components

The following documentation provides reasoning and examples on how to use Contracts for Cairo components.

== Components

:shamans-post: https://community.starknet.io/t/cairo-components/101136#components-1[Starknet Shamans post]

Starknet components are separate modules that contain storage, events, and implementations that can be integrated into a contract.
Components themselves cannot be declared or deployed.
Another way to think of components is that they are abstract modules that must be instantiated.

TIP: For more information on the construction and design of Starknet components, see the {shamans-post}.

=== Setting up

:initializable-component: xref:/security.adoc#initializable[InitializableComponent]

Integrating a component first requires that the contract declares the component with the `component!` macro:

[,javascript]
----
#[starknet::contract]
mod MyContract {
// Import the component
use openzeppelin::security::InitializableComponent;

// Declare the component
component!(path: InitializableComponent, storage: initializable, event: InitializableEvent);
}
----

The `path` argument should be the imported component itself (in this case, {initializable-component}).
Notice that the `storage` and `event` arguments are representations set within the macro.
In other words, the `initializable` and `InitializableEvent` names follow the Contracts for Cairo convention, but they can be renamed.

=== Accessing component state

Before a contract can access a component's state, the contract must integrate the implementations that will be used.
martriay marked this conversation as resolved.
Show resolved Hide resolved
For example:

[,javascript]
----
#[starknet::contract]
mod MyContract {
use openzeppelin::security::InitializableComponent;

component!(path: InitializableComponent, storage: initializable, event: InitializableEvent);

// Gives the contract access to the implementation methods
impl InitializableImpl =
InitializableComponent::InitializableImpl<ContractState>;
impl InternalImpl = InitializableComponent::InternalImpl<ContractState>;
}
----

Defining the ``impl``s give the contract access to the methods within those implementations from the component.
For example, `initialize` is defined in the `InternalImpl`.
It can, therefore, be called like this:

[,javascript]
----
#[starknet::contract]
mod MyContract {
(...)

fn initialize_contract(ref self: ContractState) {
self.initializable.initialize();
martriay marked this conversation as resolved.
Show resolved Hide resolved
martriay marked this conversation as resolved.
Show resolved Hide resolved
}
}
----

TIP: Defining the `InternalImpl` in a contract also gives the contract indirect access to the component's `Storage`.

=== Embedding implementations

A contract can embed implementations into the ABI which will expose the methods of the implementation.
To embed implementations, add the `#[abi(embed_v0)]` attribute above the `impl`:

[,javascript]
----
#[starknet::contract]
mod MyContract {
(...)

// This attribute exposes the methods of the `impl`
#[abi(embed_v0)]
impl InitializableImpl =
InitializableComponent::InitializableImpl<ContractState>;
}
----

`InitializableImpl` defines the `is_initialized` method in the component.
By adding the embed attribute, `is_initialized` becomes a contract entrypoint for `MyContract`.

=== Component storage and events

The component's storage and events must be added to the contract's `Storage` struct and `Event` enum respectively.
If the component doesn't define any events, the compiler will still create an empty event enum inside the component module.
martriay marked this conversation as resolved.
Show resolved Hide resolved

[,javascript]
----
#[starknet::contract]
mod MyContract {
use openzeppelin::security::InitializableComponent;

component!(path: InitializableComponent, storage: initializable, event: InitializableEvent);

(...)

#[storage]
struct Storage {
#[substorage(v0)]
initializable: InitializableComponent::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
InitializableEvent: InitializableComponent::Event
}
}
----

The `#[substorage(v0)]` attribute must be included for each component in the `Storage` trait.
This allows the contract to have indirect access to the component's storage.

The `#[flat]` attribute for events in the `Event` enum, however, is not required.
Component events are not flattened in the component itself to offer greater flexibility regarding how events are handled.
Note that if contracts do not flatten component events, the first key in the event log will be the component ID.
By flattening the component event, the first key will be the event ID.
martriay marked this conversation as resolved.
Show resolved Hide resolved

=== Component dependencies

:access-component: xref:/api/access.adoc#AccessControlComponent[AccessControlComponent]
:src5-component: xref:/api/introspection.adoc#SRC5Component[SRC5Component]

Some components include dependencies of other components.
Contracts that integrate components with dependencies must also include the component dependency.
For instance, {access-component} depends on {src5-component}.
Creating a contract with `AccessControlComponent` should look like this:

[,javascript]
----
#[starknet::contract]
mod MyContract {
use openzeppelin::access::accesscontrol::AccessControlComponent;
use openzeppelin::introspection::src5::SRC5Component;

component!(path: AccessControlComponent, storage: accesscontrol, event: AccessControlEvent);
component!(path: SRC5Component, storage: src5, event: SRC5Event);

// AccessControl
#[abi(embed_v0)]
impl AccessControlImpl =
AccessControlComponent::AccessControlImpl<ContractState>;
#[abi(embed_v0)]
impl AccessControlCamelImpl =
AccessControlComponent::AccessControlCamelImpl<ContractState>;
impl AccessControlInternalImpl = AccessControlComponent::InternalImpl<ContractState>;

// SRC5
#[abi(embed_v0)]
impl SRC5Impl = SRC5Component::SRC5Impl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
accesscontrol: AccessControlComponent::Storage,
#[substorage(v0)]
src5: SRC5Component::Storage
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
#[flat]
AccessControlEvent: AccessControlComponent::Event,
#[flat]
SRC5Event: SRC5Component::Event
}

(...)
}
----
166 changes: 0 additions & 166 deletions docs/modules/ROOT/pages/extensibility.adoc

This file was deleted.