Skip to content

Commit

Permalink
Add ERC1155 Hooks (#982)
Browse files Browse the repository at this point in the history
* feat: add hooks

* fix: mock

* Update src/token/erc20/erc20.cairo

Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>

---------

Co-authored-by: Andrew Fleming <fleming.andrew@protonmail.com>
  • Loading branch information
ericnordelo and andrew-fleming committed May 13, 2024
1 parent 2f0dfe8 commit a0899b0
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 7 deletions.
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.

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

0 comments on commit a0899b0

Please sign in to comment.