From d78482b367764812f7d21ea803f6f126471e8027 Mon Sep 17 00:00:00 2001 From: Eric Nordelo Date: Fri, 9 Aug 2024 15:45:40 +0200 Subject: [PATCH] feat: add tests --- CHANGELOG.md | 4 + packages/token/src/erc2981/erc2981.cairo | 8 +- .../src/tests/erc2981/test_erc2981.cairo | 396 ++++++++++-------- .../token/src/tests/mocks/erc2981_mocks.cairo | 4 +- 4 files changed, 236 insertions(+), 176 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5531becf4..2bcba1ddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +### Added + +- ERC2981 (NFT Royalty Standard) component (#) + ## 0.15.0 (2024-08-08) ### Added diff --git a/packages/token/src/erc2981/erc2981.cairo b/packages/token/src/erc2981/erc2981.cairo index d6ec4bf45..d279530c8 100644 --- a/packages/token/src/erc2981/erc2981.cairo +++ b/packages/token/src/erc2981/erc2981.cairo @@ -19,10 +19,10 @@ #[starknet::component] pub mod ERC2981Component { use core::num::traits::Zero; - use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; - use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; - use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc2981::interface::{IERC2981, IERC2981_ID}; + use openzeppelin_introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait; + use openzeppelin_introspection::src5::SRC5Component::SRC5Impl; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc2981::interface::{IERC2981, IERC2981_ID}; use starknet::ContractAddress; use starknet::storage::Map; diff --git a/packages/token/src/tests/erc2981/test_erc2981.cairo b/packages/token/src/tests/erc2981/test_erc2981.cairo index b9335ffdd..80d4e8d92 100644 --- a/packages/token/src/tests/erc2981/test_erc2981.cairo +++ b/packages/token/src/tests/erc2981/test_erc2981.cairo @@ -1,170 +1,226 @@ -// // use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait}; -// use openzeppelin::introspection::src5::SRC5Component::SRC5Impl; - -// use openzeppelin::tests::mocks::erc2981_mocks::ERC2981Mock; -// use openzeppelin::tests::utils::constants::{OTHER, OWNER, ZERO}; -// use openzeppelin::token::common::erc2981::ERC2981Component::{ERC2981Impl, InternalImpl}; -// use openzeppelin::token::common::erc2981::ERC2981Component; -// use openzeppelin::token::common::erc2981::interface::IERC2981_ID; -// use openzeppelin::token::common::erc2981::{IERC2981Dispatcher, IERC2981DispatcherTrait}; - -// use starknet::{ContractAddress, contract_address_const}; - -// type ComponentState = ERC2981Component::ComponentState; - -// fn CONTRACT_STATE() -> ERC2981Mock::ContractState { -// ERC2981Mock::contract_state_for_testing() -// } - -// fn COMPONENT_STATE() -> ComponentState { -// ERC2981Component::component_state_for_testing() -// } - -// fn DEFAULT_RECEIVER() -> ContractAddress { -// contract_address_const::<'DEFAULT_RECEIVER'>() -// } - -// fn RECEIVER() -> ContractAddress { -// contract_address_const::<'RECEIVER'>() -// } - -// // 0.5% (default denominator is 10000) -// const DEFAULT_FEE_NUMERATOR: u256 = 50; -// // 5% (default denominator is 10000) -// const FEE_NUMERATOR: u256 = 500; - -// fn setup() -> ComponentState { -// let mut state = COMPONENT_STATE(); -// state.initializer(DEFAULT_RECEIVER(), DEFAULT_FEE_NUMERATOR); -// state -// } - -// #[test] -// fn test_default_royalty() { -// let mut state = setup(); -// let token_id = 12; -// let sale_price = 1_000_000; -// let (receiver, amount) = state.royalty_info(token_id, sale_price); -// assert_eq!(receiver, DEFAULT_RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 5000, "Default fees incorrect"); - -// state._set_default_royalty(RECEIVER(), FEE_NUMERATOR); - -// let (receiver, amount) = state.royalty_info(token_id, sale_price); -// assert_eq!(receiver, RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 50000, "Default fees incorrect"); -// } - -// #[test] -// fn test_token_royalty_token() { -// let mut state = setup(); -// let token_id = 12; -// let another_token_id = 13; -// let sale_price = 1_000_000; -// let (receiver, amount) = state.royalty_info(token_id, sale_price); -// assert_eq!(receiver, DEFAULT_RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 5000, "Wrong royalty amount"); -// let (receiver, amount) = state.royalty_info(another_token_id, sale_price); -// assert_eq!(receiver, DEFAULT_RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 5000, "Wrong royalty amount"); - -// state._set_token_royalty(token_id, RECEIVER(), FEE_NUMERATOR); -// let (receiver, amount) = state.royalty_info(another_token_id, sale_price); -// assert_eq!(receiver, DEFAULT_RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 5000, "Wrong royalty amount"); -// let (receiver, amount) = state.royalty_info(token_id, sale_price); -// assert_eq!(receiver, RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 50000, "Wrong royalty amount"); - -// state._reset_token_royalty(token_id); -// let (receiver, amount) = state.royalty_info(token_id, sale_price); -// assert_eq!(receiver, DEFAULT_RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 5000, "Wrong royalty amount"); -// } - -// // -// // check IERC2981_ID is registered - -// #[test] -// fn test_token_royalty_set_twice() { -// let mut state = setup(); -// let token_id = 12; -// let sale_price = 1_000_000; - -// state._set_token_royalty(token_id, RECEIVER(), FEE_NUMERATOR); -// let (receiver, amount) = state.royalty_info(token_id, sale_price); -// assert_eq!(receiver, RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 50000, "Wrong royalty amount"); - -// state._set_token_royalty(token_id, OTHER(), FEE_NUMERATOR); -// let (receiver, amount) = state.royalty_info(token_id, sale_price); -// assert_eq!(receiver, OTHER(), "Default receiver incorrect"); -// assert_eq!(amount, 50000, "Wrong royalty amount"); -// } - -// #[test] -// #[should_panic(expected: ("Invalid token royalty receiver",))] -// fn test_token_royalty_with_zero_receiver() { -// let mut state = setup(); -// let token_id = 12; -// state._set_token_royalty(token_id, ZERO(), FEE_NUMERATOR); -// } - -// #[test] -// fn test_token_royalty_with_zero_royalty_fraction() { -// let mut state = setup(); -// let token_id = 12; -// let sale_price = 1_000_000; - -// state._set_token_royalty(token_id, RECEIVER(), 0); -// let (receiver, amount) = state.royalty_info(token_id, sale_price); -// assert_eq!(receiver, RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 0, "Wrong royalty amount"); -// } - -// #[test] -// #[should_panic(expected: ("Invalid token royalty",))] -// fn test_token_royalty_with_invalid_fee_numerator() { -// let mut state = setup(); -// let token_id = 12; -// state._set_token_royalty(token_id, RECEIVER(), state._fee_denominator() + 1); -// } - -// #[test] -// #[should_panic(expected: ("Invalid default royalty receiver",))] -// fn test_default_royalty_with_zero_receiver() { -// let mut state = setup(); - -// state._set_default_royalty(ZERO(), FEE_NUMERATOR); -// } - -// #[test] -// fn test_default_royalty_with_zero_royalty_fraction() { -// let mut state = setup(); -// let token_id = 12; -// let sale_price = 1_000_000; - -// state._set_default_royalty(DEFAULT_RECEIVER(), 0); -// let (receiver, amount) = state.royalty_info(token_id, sale_price); -// assert_eq!(receiver, DEFAULT_RECEIVER(), "Default receiver incorrect"); -// assert_eq!(amount, 0, "Wrong royalty amount"); -// } - -// #[test] -// #[should_panic(expected: ("Invalid default royalty",))] -// fn test_default_royalty_with_invalid_fee_numerator() { -// let mut state = setup(); - -// state._set_default_royalty(DEFAULT_RECEIVER(), state._fee_denominator() + 1); -// } - -// #[test] -// fn test_check_ierc2981_interface_is_registered() { -// let _state = setup(); -// let mock_state = CONTRACT_STATE(); - -// let supports_ierc2981 = mock_state.supports_interface(IERC2981_ID); -// assert!(supports_ierc2981); -// } - - +use openzeppelin_introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait}; +use openzeppelin_introspection::src5::SRC5Component::SRC5Impl; +use openzeppelin_testing::constants::{OTHER, ZERO, RECIPIENT}; +use openzeppelin_token::erc2981::ERC2981Component::{ERC2981Impl, InternalImpl}; +use openzeppelin_token::erc2981::interface::IERC2981_ID; +use openzeppelin_token::erc2981::{ERC2981Component, ERC2981ImmutableDefault}; +use openzeppelin_token::tests::mocks::erc2981_mocks::ERC2981Mock; +use starknet::{ContractAddress, contract_address_const}; + +type ComponentState = ERC2981Component::ComponentState; + +fn CONTRACT_STATE() -> ERC2981Mock::ContractState { + ERC2981Mock::contract_state_for_testing() +} + +fn COMPONENT_STATE() -> ComponentState { + ERC2981Component::component_state_for_testing() +} + +fn DEFAULT_RECEIVER() -> ContractAddress { + contract_address_const::<'DEFAULT_RECEIVER'>() +} + +// 0.5% (default denominator is 10000) +const DEFAULT_FEE_NUMERATOR: u256 = 50; +// 5% (default denominator is 10000) +const FEE_NUMERATOR: u256 = 500; + +fn setup() -> ComponentState { + let mut state = COMPONENT_STATE(); + state.initializer(DEFAULT_RECEIVER(), DEFAULT_FEE_NUMERATOR); + state +} + +// +// Initializer +// + +#[test] +fn test_initializer() { + let mut state = COMPONENT_STATE(); + let mock_state = CONTRACT_STATE(); + + state.initializer(DEFAULT_RECEIVER(), DEFAULT_FEE_NUMERATOR); + + let (receiver, numerator, denominator) = state.default_royalty(); + + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(numerator, DEFAULT_FEE_NUMERATOR); + assert_eq!(denominator, ERC2981Component::DEFAULT_FEE_DENOMINATOR); + + let supports_ierc2981 = mock_state.supports_interface(IERC2981_ID); + assert!(supports_ierc2981); +} + +// +// Getters +// + +#[test] +fn test_royalty_info_default_royalty() { + let mut state = setup(); + let token_id = 12; + let sale_price = 1_000_000; + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(amount, 5000); +} + +#[test] +fn test_royalty_info_token_royalty_set() { + let mut state = setup(); + let token_id = 12; + let sale_price = 1_000_000; + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(amount, 5000); + + state.set_token_royalty(token_id, RECIPIENT(), FEE_NUMERATOR); + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, RECIPIENT()); + assert_eq!(amount, 50000); +} + +// +// Internals +// + +#[test] +fn test_default_royalty() { + let mut state = setup(); + + let (receiver, numerator, denominator) = state.default_royalty(); + + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(numerator, DEFAULT_FEE_NUMERATOR); + assert_eq!(denominator, ERC2981Component::DEFAULT_FEE_DENOMINATOR); +} + +#[test] +fn test_set_default_royalty() { + let mut state = setup(); + let token_id = 12; + let sale_price = 1_000_000; + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(amount, 5000); + + state.set_default_royalty(RECIPIENT(), FEE_NUMERATOR); + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, RECIPIENT()); + assert_eq!(amount, 50000); +} + +#[test] +fn test_set_default_royalty_with_zero_royalty_fraction() { + let mut state = setup(); + let token_id = 12; + let sale_price = 1_000_000; + + state.set_default_royalty(DEFAULT_RECEIVER(), 0); + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(amount, 0); +} + +#[test] +#[should_panic(expected: ('ERC2981: invalid receiver',))] +fn test_set_default_royalty_with_zero_receiver() { + let mut state = setup(); + + state.set_default_royalty(ZERO(), FEE_NUMERATOR); +} + +#[test] +#[should_panic(expected: ('ERC2981: invalid royalty',))] +fn test_set_default_royalty_with_invalid_fee_numerator() { + let mut state = setup(); + let fee_denominator = ERC2981Component::DEFAULT_FEE_DENOMINATOR; + + state.set_default_royalty(DEFAULT_RECEIVER(), fee_denominator + 1); +} + +#[test] +fn test_set_token_royalty() { + let mut state = setup(); + let token_id = 12; + let another_token_id = 13; + let sale_price = 1_000_000; + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(amount, 5000); + + let (receiver, amount) = state.royalty_info(another_token_id, sale_price); + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(amount, 5000); + + state.set_token_royalty(token_id, RECIPIENT(), FEE_NUMERATOR); + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, RECIPIENT()); + assert_eq!(amount, 50000); + + let (receiver, amount) = state.royalty_info(another_token_id, sale_price); + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(amount, 5000); +} + +#[test] +fn test_set_token_royalty_with_zero_royalty_fraction() { + let mut state = setup(); + let token_id = 12; + let sale_price = 1_000_000; + + state.set_token_royalty(token_id, RECIPIENT(), 0); + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, RECIPIENT()); + assert_eq!(amount, 0); +} + +#[test] +#[should_panic(expected: ('ERC2981: invalid receiver',))] +fn test_set_token_royalty_with_zero_receiver() { + let mut state = setup(); + let token_id = 12; + state.set_token_royalty(token_id, ZERO(), FEE_NUMERATOR); +} + +#[test] +#[should_panic(expected: ('ERC2981: invalid royalty',))] +fn test_set_token_royalty_with_invalid_fee_numerator() { + let mut state = setup(); + let token_id = 12; + let fee_denominator = ERC2981Component::DEFAULT_FEE_DENOMINATOR; + + state.set_token_royalty(token_id, RECIPIENT(), fee_denominator + 1); +} + +#[test] +fn test_reset_token_royalty() { + let mut state = setup(); + let token_id = 12; + let sale_price = 1_000_000; + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(amount, 5000); + + state.set_token_royalty(token_id, RECIPIENT(), FEE_NUMERATOR); + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, RECIPIENT()); + assert_eq!(amount, 50000); + + state.reset_token_royalty(token_id); + + let (receiver, amount) = state.royalty_info(token_id, sale_price); + assert_eq!(receiver, DEFAULT_RECEIVER()); + assert_eq!(amount, 5000); +} diff --git a/packages/token/src/tests/mocks/erc2981_mocks.cairo b/packages/token/src/tests/mocks/erc2981_mocks.cairo index df653fc39..de7daffa0 100644 --- a/packages/token/src/tests/mocks/erc2981_mocks.cairo +++ b/packages/token/src/tests/mocks/erc2981_mocks.cairo @@ -1,7 +1,7 @@ #[starknet::contract] pub(crate) mod ERC2981Mock { - use openzeppelin::introspection::src5::SRC5Component; - use openzeppelin::token::erc2981::{ERC2981Component, ERC2981ImmutableDefault}; + use openzeppelin_introspection::src5::SRC5Component; + use openzeppelin_token::erc2981::{ERC2981Component, ERC2981ImmutableDefault}; use starknet::ContractAddress; component!(path: ERC2981Component, storage: erc2981, event: ERC2981Event);