Skip to content

Commit

Permalink
ERC721 Enumerable (dojoengine#71)
Browse files Browse the repository at this point in the history
* add erc721 component files and test templates

* Complete approval test

* - fix components
- add remaining tests

* fix tests

* scarb fmt

* remove components in lib for 0.6.0 fix

* - apply fixes
- add erc721_enumerable
- add tests
- begin migration to ByteArray

* - remove lib code apart from erc721_metdadata

* - apply fixes
- update to 0.7.0-alpha.4
- add ByteArray
- add ERC721Receiver logic
- remove enumerables for separate PR

* - add enumerable component
- add enumerable mint and burn preset
add tests

* add erc721 component files and test templates

* Complete approval test

* - fix components
- add remaining tests

* fix tests

* scarb fmt

* remove components in lib for 0.6.0 fix

* - apply fixes
- add erc721_enumerable
- add tests
- begin migration to ByteArray

* - remove lib code apart from erc721_metdadata

* - apply fixes
- update to 0.7.0-alpha.4
- add ByteArray
- add ERC721Receiver logic
- remove enumerables for separate PR

* - begin alpha.5 rebase

* - update to 0.7.0-alpha.5
- add ByteArrays for erc20
- fmt code

* - fmt
- update to 0.7.0-alpha.5

* move dojo model tags

* move dojo model tag

* - make initalizer assertion that world owner called
-  change _ naming convention to internal
- use initialize protection

* fmt whitespace

* - fix lib.cairo
- add initialize protection to enum_mint_burn
- add calldata to test deploy method}
- add tests for intitializing multiple + initializing fron non world owner

* format

* consolidate initialize logic

* format
  • Loading branch information
starknetdev authored Jun 10, 2024
1 parent ebc3677 commit ded3282
Show file tree
Hide file tree
Showing 9 changed files with 1,186 additions and 11 deletions.
3 changes: 3 additions & 0 deletions token/src/components/tests.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ mod mocks {
mod erc721 {
mod erc721_approval_mock;
mod erc721_balance_mock;
mod erc721_enumerable_mock;
mod erc721_metadata_mock;
mod erc721_mintable_burnable_mock;
mod erc721_receiver_mock;
Expand Down Expand Up @@ -50,6 +51,8 @@ mod token {
#[cfg(test)]
mod test_erc721_balance;
#[cfg(test)]
mod test_erc721_enumerable;
#[cfg(test)]
mod test_erc721_metadata;
#[cfg(test)]
mod test_erc721_mintable_burnable;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use starknet::{ContractAddress, ClassHash};
use dojo::world::IWorldDispatcher;

#[starknet::interface]
trait IERC721EnumerableMock<TState> {
// IERC721
fn total_supply(self: @TState) -> u256;
fn token_by_index(self: @TState, index: u256) -> u256;
fn token_of_owner_by_index(ref self: TState, owner: ContractAddress, index: u256,) -> u256;

// IERC721CamelOnly
fn totalSupply(self: @TState) -> u256;
fn tokenByIndex(self: @TState, index: u256) -> u256;
fn tokenOfOwnerByIndex(ref self: TState, owner: ContractAddress, index: u256,) -> u256;

// IWorldProvider
fn world(self: @TState,) -> IWorldDispatcher;
}

#[dojo::contract]
mod erc721_enumerable_mock {
use starknet::ContractAddress;

use token::components::token::erc721::erc721_approval::erc721_approval_component;
use token::components::token::erc721::erc721_balance::erc721_balance_component;
use token::components::token::erc721::erc721_enumerable::erc721_enumerable_component;
use token::components::token::erc721::erc721_owner::erc721_owner_component;

component!(
path: erc721_approval_component, storage: erc721_approval, event: ERC721ApprovalEvent
);
component!(path: erc721_balance_component, storage: erc721_balance, event: ERC721BalanceEvent);
component!(
path: erc721_enumerable_component, storage: erc721_enumerable, event: ERC721EnumerableEvent
);
component!(path: erc721_owner_component, storage: erc721_owner, event: ERC721OwnerEvent);


#[abi(embed_v0)]
impl ERC721ApprovalImpl =
erc721_approval_component::ERC721ApprovalImpl<ContractState>;

#[abi(embed_v0)]
impl ERC721ApprovalCamelImpl =
erc721_approval_component::ERC721ApprovalCamelImpl<ContractState>;

#[abi(embed_v0)]
impl ERC721BalanceImpl =
erc721_balance_component::ERC721BalanceImpl<ContractState>;

#[abi(embed_v0)]
impl ERC721BalanceCamelImpl =
erc721_balance_component::ERC721BalanceCamelImpl<ContractState>;

#[abi(embed_v0)]
impl ERC721EnumerableImpl =
erc721_enumerable_component::ERC721EnumerableImpl<ContractState>;

#[abi(embed_v0)]
impl ERC721EnumerableCamelImpl =
erc721_enumerable_component::ERC721EnumerableCamelImpl<ContractState>;

#[abi(embed_v0)]
impl ERC721OwnerImpl = erc721_owner_component::ERC721OwnerImpl<ContractState>;

impl ERC721ApprovalInternalImpl = erc721_approval_component::InternalImpl<ContractState>;
impl ERC721BalanceInternalImpl = erc721_balance_component::InternalImpl<ContractState>;
impl ERC721EnumerableInternalImpl = erc721_enumerable_component::InternalImpl<ContractState>;
impl ERC721OwnerInternalImpl = erc721_owner_component::InternalImpl<ContractState>;

#[storage]
struct Storage {
#[substorage(v0)]
erc721_approval: erc721_approval_component::Storage,
#[substorage(v0)]
erc721_balance: erc721_balance_component::Storage,
#[substorage(v0)]
erc721_enumerable: erc721_enumerable_component::Storage,
#[substorage(v0)]
erc721_owner: erc721_owner_component::Storage,
}

#[event]
#[derive(Drop, starknet::Event)]
enum Event {
ERC721ApprovalEvent: erc721_approval_component::Event,
ERC721BalanceEvent: erc721_balance_component::Event,
ERC721EnumerableEvent: erc721_enumerable_component::Event,
ERC721OwnerEvent: erc721_owner_component::Event,
}
}
168 changes: 168 additions & 0 deletions token/src/components/tests/token/erc721/test_erc721_enumerable.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
use starknet::testing;
use starknet::ContractAddress;
use integer::BoundedInt;
use dojo::world::{IWorldDispatcher, IWorldDispatcherTrait};
use dojo::test_utils::spawn_test_world;
use token::tests::constants::{ZERO, OWNER, SPENDER, RECIPIENT, VALUE, TOKEN_ID, TOKEN_ID_2};
use token::tests::utils;

use token::components::token::erc721::erc721_balance::{erc_721_balance_model, ERC721BalanceModel};
use token::components::token::erc721::erc721_balance::erc721_balance_component::{
Transfer, ERC721BalanceImpl, ERC721BalanceCamelImpl, InternalImpl as ERC721BalanceInternalImpl
};

use token::components::token::erc721::erc721_enumerable::{
erc_721_enumerable_index_model, ERC721EnumerableIndexModel,
erc_721_enumerable_owner_index_model, ERC721EnumerableOwnerIndexModel,
erc_721_enumerable_total_model, ERC721EnumerableTotalModel
};
use token::components::token::erc721::erc721_enumerable::erc721_enumerable_component::{
ERC721EnumerableImpl, InternalImpl as ERC721EnumerableInternalImpl
};
use token::components::tests::mocks::erc721::erc721_enumerable_mock::{
erc721_enumerable_mock, IERC721EnumerableMockDispatcher, IERC721EnumerableMockDispatcherTrait
};


use starknet::storage::{StorageMemberAccessTrait};

use debug::PrintTrait;

//
// initialize STATE
//

fn STATE() -> (IWorldDispatcher, erc721_enumerable_mock::ContractState) {
let world = spawn_test_world(
array![
erc_721_enumerable_index_model::TEST_CLASS_HASH,
erc_721_enumerable_owner_index_model::TEST_CLASS_HASH,
erc_721_enumerable_total_model::TEST_CLASS_HASH
]
);

let mut state = erc721_enumerable_mock::contract_state_for_testing();
state.world_dispatcher.write(world);

utils::drop_event(ZERO());

(world, state)
}

//
// set_total_supply
//

#[test]
fn test_erc721_enumerable_total() {
let (_world, mut state) = STATE();

testing::set_caller_address(OWNER());

state.erc721_enumerable.set_total_supply(VALUE);
assert(
state.erc721_enumerable.get_total_supply().total_supply.into() == VALUE, 'should be VALUE'
);
}

#[test]
fn test_erc721_enumerable_index() {
let (_world, mut state) = STATE();

testing::set_caller_address(OWNER());

state.erc721_enumerable.set_token_by_index(0, TOKEN_ID);
assert(
state.erc721_enumerable.get_token_by_index(0).token_id.into() == TOKEN_ID,
'should be TOKEN_ID'
);
}

#[test]
fn test_erc721_enumerable_owner_index() {
let (_world, mut state) = STATE();

testing::set_caller_address(OWNER());

state.erc721_enumerable.set_token_of_owner_by_index(OWNER(), 0, TOKEN_ID);
assert(
state.erc721_enumerable.get_token_of_owner_by_index(OWNER(), 0).token_id.into() == TOKEN_ID,
'should be TOKEN_ID'
);
}

#[test]
fn test_erc721_add_token_to_owner_enumeration() {
let (_world, mut state) = STATE();

testing::set_caller_address(OWNER());

state.erc721_balance.set_balance(OWNER(), 1);
state.erc721_enumerable.add_token_to_owner_enumeration(OWNER(), TOKEN_ID);
assert(
state.erc721_enumerable.get_token_of_owner_by_index(OWNER(), 0).token_id.into() == TOKEN_ID,
'should be TOKEN_ID'
);
assert(
state.erc721_enumerable.get_index_of_owner_by_token(OWNER(), TOKEN_ID).index == 0,
'should be 0'
);
}

#[test]
fn test_erc721_add_token_to_all_tokens_enumeration() {
let (_world, mut state) = STATE();

testing::set_caller_address(OWNER());

state.erc721_enumerable.add_token_to_all_tokens_enumeration(TOKEN_ID);
assert(
state.erc721_enumerable.get_token_by_index(0).token_id.into() == TOKEN_ID,
'should be TOKEN_ID'
);
assert(state.erc721_enumerable.get_index_by_token(TOKEN_ID).index == 0, 'should be 0');
}

#[test]
fn test_erc721_remove_token_from_owner_enumeration() {
let (_world, mut state) = STATE();

testing::set_caller_address(OWNER());

state.erc721_balance.set_balance(OWNER(), 1);
state.erc721_enumerable.add_token_to_owner_enumeration(OWNER(), TOKEN_ID);
state.erc721_balance.set_balance(OWNER(), 2);
state.erc721_enumerable.add_token_to_owner_enumeration(OWNER(), TOKEN_ID_2);
state.erc721_balance.set_balance(OWNER(), 1);
state.erc721_enumerable.remove_token_from_owner_enumeration(OWNER(), TOKEN_ID);
assert(
state
.erc721_enumerable
.get_token_of_owner_by_index(OWNER(), 0)
.token_id
.into() == TOKEN_ID_2,
'should be TOKEN_ID_2'
);
assert(
state.erc721_enumerable.get_index_of_owner_by_token(OWNER(), TOKEN_ID).index == 0,
'should be 0'
);
}

#[test]
fn test_erc721_remove_token_from_all_tokens_enumeration() {
let (_world, mut state) = STATE();

testing::set_caller_address(OWNER());

state.erc721_enumerable.add_token_to_all_tokens_enumeration(TOKEN_ID);
state.erc721_enumerable.set_total_supply(1);
state.erc721_enumerable.add_token_to_all_tokens_enumeration(TOKEN_ID_2);
state.erc721_enumerable.set_total_supply(2);
state.erc721_enumerable.remove_token_from_all_tokens_enumeration(TOKEN_ID);
assert(
state.erc721_enumerable.get_token_by_index(0).token_id.into() == TOKEN_ID_2,
'should be TOKEN_ID_2'
);
assert(state.erc721_enumerable.get_index_by_token(TOKEN_ID).index == 0, 'should be 0');
}
Loading

0 comments on commit ded3282

Please sign in to comment.