Skip to content

Commit

Permalink
Components chapter: wording, grammar, formatting fixes (#210)
Browse files Browse the repository at this point in the history
* how-to fixes

* Clean up dependencies

* collisions > fixes

* ownable minor fixes

* remove error.log

* fix typo

* add comma after 'first'

* add 'the' before cairo book

---------

Co-authored-by: Nenad <nenad@better.giving>
  • Loading branch information
0xNeshi and Nenad authored Jun 5, 2024
1 parent 8cc51f5 commit c0ab62f
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 30 deletions.
6 changes: 3 additions & 3 deletions listings/applications/components/src/ownable.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -67,17 +67,17 @@ pub mod ownable_component {
> of OwnableInternalTrait<TContractState> {
fn _assert_only_owner(self: @ComponentState<TContractState>) {
let caller = get_caller_address();
assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER);
assert(caller.is_non_zero(), Errors::ZERO_ADDRESS_CALLER);
assert(caller == self.ownable_owner.read(), Errors::UNAUTHORIZED);
}

fn _init(ref self: ComponentState<TContractState>, owner: ContractAddress) {
assert(!owner.is_zero(), Errors::ZERO_ADDRESS_OWNER);
assert(owner.is_non_zero(), Errors::ZERO_ADDRESS_OWNER);
self.ownable_owner.write(owner);
}

fn _transfer_ownership(ref self: ComponentState<TContractState>, new: ContractAddress) {
assert(!new.is_zero(), Errors::ZERO_ADDRESS_OWNER);
assert(new.is_non_zero(), Errors::ZERO_ADDRESS_OWNER);
let previous = self.ownable_owner.read();
self.ownable_owner.write(new);
self
Expand Down
12 changes: 6 additions & 6 deletions po/es.po
Original file line number Diff line number Diff line change
Expand Up @@ -7139,20 +7139,20 @@ msgid ""
" > of OwnableInternalTrait<TContractState> {\n"
" fn _assert_only_owner(self: @ComponentState<TContractState>) {\n"
" let caller = get_caller_address();\n"
" assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER);\n"
" assert(caller.is_non_zero(), Errors::ZERO_ADDRESS_CALLER);\n"
" assert(caller == self.ownable_owner.read(), Errors::"
"UNAUTHORIZED);\n"
" }\n"
"\n"
" fn _init(ref self: ComponentState<TContractState>, owner: "
"ContractAddress) {\n"
" assert(!owner.is_zero(), Errors::ZERO_ADDRESS_OWNER);\n"
" assert(owner.is_non_zero(), Errors::ZERO_ADDRESS_OWNER);\n"
" self.ownable_owner.write(owner);\n"
" }\n"
"\n"
" fn _transfer_ownership(ref self: ComponentState<TContractState>, "
"new: ContractAddress) {\n"
" assert(!new.is_zero(), Errors::ZERO_ADDRESS_OWNER);\n"
" assert(new.is_non_zero(), Errors::ZERO_ADDRESS_OWNER);\n"
" let previous = self.ownable_owner.read();\n"
" self.ownable_owner.write(new);\n"
" self\n"
Expand Down Expand Up @@ -7243,20 +7243,20 @@ msgstr ""
" > of OwnableInternalTrait<TContractState> {\n"
" fn _assert_only_owner(self: @ComponentState<TContractState>) {\n"
" let caller = get_caller_address();\n"
" assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER);\n"
" assert(caller.is_non_zero(), Errors::ZERO_ADDRESS_CALLER);\n"
" assert(caller == self.ownable_owner.read(), Errors::"
"UNAUTHORIZED);\n"
" }\n"
"\n"
" fn _init(ref self: ComponentState<TContractState>, owner: "
"ContractAddress) {\n"
" assert(!owner.is_zero(), Errors::ZERO_ADDRESS_OWNER);\n"
" assert(owner.is_non_zero(), Errors::ZERO_ADDRESS_OWNER);\n"
" self.ownable_owner.write(owner);\n"
" }\n"
"\n"
" fn _transfer_ownership(ref self: ComponentState<TContractState>, "
"new: ContractAddress) {\n"
" assert(!new.is_zero(), Errors::ZERO_ADDRESS_OWNER);\n"
" assert(new.is_non_zero(), Errors::ZERO_ADDRESS_OWNER);\n"
" let previous = self.ownable_owner.read();\n"
" self.ownable_owner.write(new);\n"
" self\n"
Expand Down
4 changes: 3 additions & 1 deletion src/components/collisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Components can declare their own storage variables.

When a contract use a component, the component storage is merged with the contract storage.
When a contract uses a component, the component storage is merged with the contract storage.
The storage layout is only determined by the variables names, so variables with the same name will collide.

> In a future release, the `#[substorage(v1)]` will determine the storage layout based on the component as well, so collisions will be avoided.
Expand All @@ -14,11 +14,13 @@ A good practice is to prefix the component storage variables with the component
Here's an example of a collision on the `switchable_value` storage variable of the `Switchable` component.

Interface:

```rust
{{#include ../../listings/applications/components/src/contracts/switch_collision.cairo:interface}}
```

Here's the storage of the contract (you can expand the code snippet to see the full contract):

```rust
{{#rustdoc_include ../../listings/applications/components/src/contracts/switch_collision.cairo:storage}}
```
Expand Down
23 changes: 10 additions & 13 deletions src/components/dependencies.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
# Components Dependencies
# Component Dependencies

A component with a dependency on a trait T can be used in a contract as long as the contract implements the trait T.
A component with a dependency on a trait `T` can be used in a contract as long as the contract implements the trait `T`.

We will use a new `Countable` component as an example:

```rust
{{#include ../../listings/applications/components/src/countable.cairo}}
```

We want to add a way to enable or disable the counter, in a way that calling `increment` on a disabled counter will not increment the counter.
We want to add a way to enable or disable the counter, in a way that calling `increment` on a disabled counter will not increment it.
But we don't want to add this switch logic to the `Countable` component itself.
We instead add the trait `Switchable` as a dependency to the `Countable` component.
Instead, we add the trait `Switchable` as a dependency to the `Countable` component.

#### Implementation of the trait in the contract

We first define the `ISwitchable` trait:
First, we import the `ISwitchable` trait defined in chapter ["Components How-To"](./how_to.md):

```rust
{{#include ../../listings/applications/components/src/switchable.cairo:interface}}
Expand All @@ -36,19 +36,19 @@ A contract that uses the `Countable` component must implement the `ISwitchable`

In the previous example, we implemented the `ISwitchable` trait in the contract.

We already implemented a [`Switchable`](./how_to.md) component that provide an implementation of the `ISwitchable` trait.
By using the `Switchable` component in a contract, we embed the implementation of the `ISwitchable` trait in the contract and resolve the dependency on the `ISwitchable` trait.
We already implemented a [`Switchable`](./how_to.md) component that provides an implementation of the `ISwitchable` trait.
By using the `Switchable` component in a contract, we can embed the implementation of the `ISwitchable` trait in the contract and resolve the dependency on the `ISwitchable` trait.

```rust
{{#rustdoc_include ../../listings/applications/components_dependencies/src/contract_countable_switchable.cairo:contract}}
```

#### Dependency on other components internal functions
#### Dependency on other component's internal functions

The previous example shows how to use the `ISwitchable` trait implementation from the `Switchable` component inside the `Countable` component by embedding the implementation in the contract.
However, suppose we would like to turn off the switch after each increment. There's no `set` function in the `ISwitchable` trait, so we can't do it directly.

But the Switchable component implements the internal function `_off` from the `SwitchableInternalTrait` that set the switch to `false`.
But the `Switchable` component implements the internal function `_off` from the `SwitchableInternalTrait` that set the switch to `false`.
We can't embed `SwitchableInternalImpl`, but we can add `switchable::HasComponent<TContractState>` as a dependency inside `CountableImpl`.

We make the `Countable` component depend on the `Switchable` component.
Expand All @@ -58,7 +58,4 @@ This will allow to do `switchable::ComponentState<TContractState>` -> `TContract
{{#rustdoc_include ../../listings/applications/components_dependencies/src/countable_internal_dep_switch.cairo:contract}}
```

The contract remains the same that the previous example, but the implementation of the `Countable` component is different:
```rust
{{#rustdoc_include ../../listings/applications/components_dependencies/src/contract_countable_switchable_internal.cairo:contract}}
```
The `CountableContract` contract remains the same as in the previous example, only the implementation of the `Countable` component is different.
14 changes: 8 additions & 6 deletions src/components/how_to.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ They are used to separate the core logic from common functionalities, simplifyin
It also reduces the risk of bugs and vulnerabilities by using well-tested components.

Key characteristics:

- Modularity: Easily pluggable into multiple contracts.
- Reusable Logic: Encapsulates specific functionalities.
- Not Standalone: Cannot be declared or deployed independently.
Expand All @@ -20,19 +21,20 @@ It contains a storage variable `switchable_value`, a function `switch` and an ev
{{#include ../../listings/applications/components/src/switchable.cairo:component}}
```

A component in itself is really similar to a contract, it *can* also have:
A component is really similar to a contract and can also have:

- An interface defining entrypoints (`ISwitchableComponent<TContractState>`)
- A Storage struct
- Events
- Internal functions

It don't have a constructor, but you can create a `_init` internal function and call it from the contract's constructor. In the previous example, the `_off` function is used this way.
It doesn't have a constructor, but you can create an `_init` internal function and call it from the contract's constructor. In the previous example, the `_off` function will be used this way.

> It's currently not possible to use the same component multiple times in the same contract.
> This is a known limitation that may be lifted in the future.
>
> For now, you can view components as an implementation of a specific interface/feature (`Ownable`, `Upgradeable`, ... `~able`).
> This is why we called it `Switchable` and not `Switch`; The contract *is switchable*, not *has a switch*.
>
> For now, you can view components as implementations of a specific interfaces or features (`Ownable`, `Upgradeable`, ... `~able`).
> This is why we called the component in the above example `Switchable`, and not `Switch`; the contract _is switchable_, it does not _have a switch_.
## How to use a component

Expand All @@ -45,4 +47,4 @@ The following contract incorporates the `Switchable` component:

## Deep dive into components

You can find more in-depth information about components in the [Cairo book - Components](https://book.cairo-lang.org/ch99-01-05-00-components.html).
You can find more in-depth information about components in [The Cairo book - Components](https://book.cairo-lang.org/ch16-02-00-composability-and-components.html).
2 changes: 1 addition & 1 deletion src/components/ownable.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Ownable

The following `Ownable` component is a simple component that allows the contract to set an owner and provides a `_assert_is_owner` function that can be used to ensure that the caller is the owner.
The following `Ownable` component is a simple component that allows the contract to set an owner and provides an `_assert_is_owner` function that can be used to ensure that the caller is the owner.

It can also be used to renounce ownership of a contract, meaning that no one will be able to satisfy the `_assert_is_owner` function.

Expand Down

0 comments on commit c0ab62f

Please sign in to comment.