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

Add ERC1155 Hooks #982

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

### Added

- before_update and after_update hooks to ERC1155Component (#982)

### Changed (Breaking)

- ERC1155Component implementations now require an ERC1155HooksTrait implementation in scope.

## 0.12.0 (2024-04-21)

### Added
Expand Down
36 changes: 36 additions & 0 deletions docs/modules/ROOT/pages/api/erc1155.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,17 @@ ERC1155 component implementing <<IERC1155,IERC1155>> and <<IERC1155MetadataURI,I

NOTE: {src5-component-required-note}

NOTE: See xref:#ERC1155Component-Hooks[Hooks] to understand how are hooks used.

[.contract-index]
.Hooks
--
[.sub-index#ERC1155Component-ERC1155HooksTrait]
.ERC1155HooksTrait
* xref:#ERC1155Component-before_update[`++before_update(self, from, to, token_ids, values)++`]
* xref:#ERC1155Component-after_update[`++after_update(self, from, to, token_ids, values)++`]
--

[.contract-index#ERC1155Component-Embeddable-Mixin-Impl]
.{mixin-impls}

Expand Down Expand Up @@ -228,6 +239,28 @@ NOTE: {src5-component-required-note}
* xref:#ERC1155Component-URI[`++URI(value, id)++`]
--

[#ERC1155Component-Hooks]
==== Hooks

Hooks are functions which implementations can extend the functionality of the component source code. Every contract
using ERC1155Component is expected to provide an implementation of the ERC1155HooksTrait. For basic token contracts, an
empty implementation with no logic must be provided.
Comment on lines +246 to +247
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we highlight ERC1155Component and/or ERC1155HooksTrait or do you think they're good as is?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might add a link to the ERC1155HooksTrait, but since we don't have a doc entry for it, I don't think it is worth it.

I created a page in notion for style guidelines for as to start adding an following the rules in there, and I added rule No. 1 for doc-site being module names shouldn't be coded unless the are code units (e. g. use clauses).


TIP: You can use `openzeppelin::token::erc1155::ERC1155HooksEmptyImpl` which is already available as part of the library
for this purpose.

[.contract-item]
[[ERC1155Component-before_update]]
==== `[.contract-item-name]#++before_update++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span<u256>, values: Span<u256>)++` [.item-kind]#hook#

Function executed at the beginning of the xref:#ERC1155Component-update[update] function prior to any other logic.

[.contract-item]
[[ERC1155Component-after_update]]
==== `[.contract-item-name]#++after_update++#++(ref self: ContractState, from: ContractAddress, to: ContractAddress, token_ids: Span<u256>, values: Span<u256>)++` [.item-kind]#hook#

Function executed at the end of the xref:#ERC1155Component-update[update] function.

==== Embeddable functions

[.contract-item]
Expand Down Expand Up @@ -382,6 +415,9 @@ Requirements:
Emits a <<ERC1155Component-TransferSingle,TransferSingle>> event if the arrays contain one element,
and <<ERC1155Component-TransferBatch,TransferBatch>> otherwise.

NOTE: This function can be extended using the xref:ERC1155Component-ERC1155HooksTrait[ERC1155HooksTrait], to add
functionality before and/or after the transfer, mint, or burn.

NOTE: The ERC1155 acceptance check is not performed in this function.
See <<ERC1155Component-update_with_acceptance_check,update_with_acceptance_check>> instead.

Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/api/erc20.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -436,7 +436,7 @@ Possibly emits an <<ERC20Component-Approval,Approval>> event.
Transfers an `amount` of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is
the zero address.

This function can be extended using the xref:ERC20Component-ERC20HooksTrait[ERC20HooksTrait], to add
NOTE: This function can be extended using the xref:ERC20Component-ERC20HooksTrait[ERC20HooksTrait], to add
functionality before and/or after the transfer, mint, or burn.

Emits a <<ERC20Component-Transfer,Transfer>> event.
Expand Down
2 changes: 1 addition & 1 deletion docs/modules/ROOT/pages/erc1155.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ Here's an example of a basic contract:
#[starknet::contract]
mod MyERC1155 {
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc1155::ERC1155Component;
use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};
use starknet::ContractAddress;

component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);
Expand Down
2 changes: 1 addition & 1 deletion src/presets/erc1155.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
mod ERC1155Upgradeable {
use openzeppelin::access::ownable::OwnableComponent;
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc1155::ERC1155Component;
use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};
use openzeppelin::upgrades::UpgradeableComponent;
use openzeppelin::upgrades::interface::IUpgradeable;
use starknet::{ContractAddress, ClassHash};
Expand Down
6 changes: 3 additions & 3 deletions src/tests/mocks/erc1155_mocks.cairo
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#[starknet::contract]
mod DualCaseERC1155Mock {
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc1155::ERC1155Component;
use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};
use starknet::ContractAddress;

component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);
Expand Down Expand Up @@ -54,7 +54,7 @@ mod DualCaseERC1155Mock {
#[starknet::contract]
mod SnakeERC1155Mock {
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc1155::ERC1155Component;
use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};
use starknet::ContractAddress;

component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);
Expand Down Expand Up @@ -105,7 +105,7 @@ mod SnakeERC1155Mock {
#[starknet::contract]
mod CamelERC1155Mock {
use openzeppelin::introspection::src5::SRC5Component;
use openzeppelin::token::erc1155::ERC1155Component;
use openzeppelin::token::erc1155::{ERC1155Component, ERC1155HooksEmptyImpl};
use starknet::ContractAddress;

component!(path: ERC1155Component, storage: erc1155, event: ERC1155Event);
Expand Down
1 change: 1 addition & 0 deletions src/token/erc1155.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ mod erc1155_receiver;
mod interface;

use erc1155::ERC1155Component;
use erc1155::ERC1155HooksEmptyImpl;
use erc1155_receiver::ERC1155ReceiverComponent;
55 changes: 55 additions & 0 deletions src/token/erc1155/erc1155.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts for Cairo v0.12.0 (token/erc1155/erc1155.cairo)

use starknet::ContractAddress;

/// # ERC1155 Component
///
/// The ERC1155 component provides an implementation of the basic standard multi-token.
Expand Down Expand Up @@ -94,6 +96,28 @@ mod ERC1155Component {
const SAFE_TRANSFER_FAILED: felt252 = 'ERC1155: safe transfer failed';
}

//
// Hooks
//

trait ERC1155HooksTrait<TContractState> {
fn before_update(
ref self: ComponentState<TContractState>,
from: ContractAddress,
to: ContractAddress,
token_ids: Span<u256>,
values: Span<u256>
);

fn after_update(
ref self: ComponentState<TContractState>,
from: ContractAddress,
to: ContractAddress,
token_ids: Span<u256>,
values: Span<u256>
);
}

//
// External
//
Expand All @@ -103,6 +127,7 @@ mod ERC1155Component {
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+ERC1155HooksTrait<TContractState>,
+Drop<TContractState>
> of interface::IERC1155<ComponentState<TContractState>> {
/// Returns the amount of `token_id` tokens owned by `account`.
Expand Down Expand Up @@ -237,6 +262,7 @@ mod ERC1155Component {
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+ERC1155HooksTrait<TContractState>,
+Drop<TContractState>
> of interface::IERC1155MetadataURI<ComponentState<TContractState>> {
/// This implementation returns the same URI for *all* token types. It relies
Expand All @@ -256,6 +282,7 @@ mod ERC1155Component {
TContractState,
+HasComponent<TContractState>,
+SRC5Component::HasComponent<TContractState>,
+ERC1155HooksTrait<TContractState>,
+Drop<TContractState>
> of interface::IERC1155Camel<ComponentState<TContractState>> {
fn balanceOf(
Expand Down Expand Up @@ -316,6 +343,7 @@ mod ERC1155Component {
TContractState,
+HasComponent<TContractState>,
impl SRC5: SRC5Component::HasComponent<TContractState>,
impl Hooks: ERC1155HooksTrait<TContractState>,
+Drop<TContractState>
> of InternalTrait<TContractState> {
/// Initializes the contract by setting the `base_uri` for all tokens,
Expand All @@ -338,6 +366,9 @@ mod ERC1155Component {
///
/// Emits a `TransferSingle` event if the arrays contain one element, and `TransferBatch` otherwise.
///
/// NOTE: This function can be extended using the `ERC1155HooksTrait`, to add
/// functionality before and/or after the transfer, mint, or burn.
///
/// NOTE: The ERC1155 acceptance check is not performed in this function.
/// See `update_with_acceptance_check` instead.
fn update(
Expand All @@ -347,6 +378,8 @@ mod ERC1155Component {
token_ids: Span<u256>,
values: Span<u256>
) {
Hooks::before_update(ref self, from, to, token_ids, values);

assert(token_ids.len() == values.len(), Errors::INVALID_ARRAY_LENGTH);

let mut index = 0;
Expand Down Expand Up @@ -378,6 +411,8 @@ mod ERC1155Component {
} else {
self.emit(TransferBatch { operator, from, to, ids: token_ids, values });
}

Hooks::after_update(ref self, from, to, token_ids, values);
}

/// Version of `update` that performs the token acceptance check by calling
Expand Down Expand Up @@ -554,6 +589,7 @@ mod ERC1155Component {
TContractState,
+HasComponent<TContractState>,
impl SRC5: SRC5Component::HasComponent<TContractState>,
+ERC1155HooksTrait<TContractState>,
+Drop<TContractState>
> of interface::ERC1155ABI<ComponentState<TContractState>> {
// IERC1155
Expand Down Expand Up @@ -668,3 +704,22 @@ mod ERC1155Component {
}
}
}

/// An empty implementation of the ERC1155 hooks to be used in basic ERC1155 preset contracts.
impl ERC1155HooksEmptyImpl<TContractState> of ERC1155Component::ERC1155HooksTrait<TContractState> {
fn before_update(
ref self: ERC1155Component::ComponentState<TContractState>,
from: ContractAddress,
to: ContractAddress,
token_ids: Span<u256>,
values: Span<u256>
) {}

fn after_update(
ref self: ERC1155Component::ComponentState<TContractState>,
from: ContractAddress,
to: ContractAddress,
token_ids: Span<u256>,
values: Span<u256>
) {}
}
2 changes: 1 addition & 1 deletion src/token/erc20/erc20.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ mod ERC20Component {
/// Transfers an `amount` of tokens from `from` to `to`, or alternatively mints (or burns) if `from` (or `to`) is
/// the zero address.
///
/// This function can be extended using the `ERC20HooksTrait`, to add
/// NOTE: This function can be extended using the `ERC20HooksTrait`, to add
/// functionality before and/or after the transfer, mint, or burn.
///
/// Emits a `Transfer` event.
Expand Down
Loading