From 51f35434d53762eba5cc3a7ae078f89811b2fc31 Mon Sep 17 00:00:00 2001
From: Brian Sin <briansinw3b@gmail.com>
Date: Mon, 20 May 2024 10:45:11 +0700
Subject: [PATCH 1/3] feat/erc721-open-edition-multi-metadata

---
 .../src/marketplace/openedition.cairo         |   4 +
 .../ERC721_open_edition_multi_metadata.cairo  | 352 ++++++++++
 .../marketplace/openedition/FlexDrop.cairo    |  13 +-
 .../erc721/ERC721MultiMetadata.cairo          | 629 ++++++++++++++++++
 4 files changed, 994 insertions(+), 4 deletions(-)
 create mode 100644 flex_marketplace/src/marketplace/openedition/ERC721_open_edition_multi_metadata.cairo
 create mode 100644 flex_marketplace/src/marketplace/openedition/erc721/ERC721MultiMetadata.cairo

diff --git a/flex_marketplace/src/marketplace/openedition.cairo b/flex_marketplace/src/marketplace/openedition.cairo
index e58fe95..d58a24c 100644
--- a/flex_marketplace/src/marketplace/openedition.cairo
+++ b/flex_marketplace/src/marketplace/openedition.cairo
@@ -7,13 +7,17 @@ mod interfaces {
 
 mod ERC721_open_edition;
 
+mod ERC721_open_edition_multi_metadata;
+
 mod FlexDrop;
 
 mod erc721 {
     mod ERC721;
+    mod ERC721MultiMetadata;
 }
 
 use erc721::ERC721;
+use erc721::ERC721MultiMetadata;
 use interfaces::IFlexDrop::IFlexDrop;
 use interfaces::INonFungibleFlexDropToken::INonFungibleFlexDropToken;
 
diff --git a/flex_marketplace/src/marketplace/openedition/ERC721_open_edition_multi_metadata.cairo b/flex_marketplace/src/marketplace/openedition/ERC721_open_edition_multi_metadata.cairo
new file mode 100644
index 0000000..530a355
--- /dev/null
+++ b/flex_marketplace/src/marketplace/openedition/ERC721_open_edition_multi_metadata.cairo
@@ -0,0 +1,352 @@
+#[starknet::contract]
+mod ERC721OpenEditionMultiMetadata {
+    use alexandria_storage::list::ListTrait;
+    use openzeppelin::access::ownable::OwnableComponent;
+    use openzeppelin::introspection::src5::SRC5Component;
+    use openzeppelin::security::reentrancyguard::ReentrancyGuardComponent;
+    use flex::marketplace::openedition::ERC721MultiMetadata::ERC721MultiMetadataComponent;
+    use flex::marketplace::openedition::interfaces::IFlexDrop::{
+        IFlexDropDispatcher, IFlexDropDispatcherTrait
+    };
+    use flex::marketplace::openedition::interfaces::INonFungibleFlexDropToken::{
+        INonFungibleFlexDropToken, I_NON_FUNGIBLE_FLEX_DROP_TOKEN_ID
+    };
+    use flex::marketplace::utils::openedition::{PhaseDrop, MultiConfigureStruct};
+    use alexandria_storage::list::List;
+    use starknet::{ContractAddress, get_caller_address, get_contract_address};
+    use integer::BoundedU64;
+
+    component!(path: SRC5Component, storage: src5, event: SRC5Event);
+    component!(path: ERC721MultiMetadataComponent, storage: erc721, event: ERC721Event);
+    component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
+    component!(
+        path: ReentrancyGuardComponent, storage: reentrancy_guard, event: ReentrancyGuardEvent
+    );
+
+    #[abi(embed_v0)]
+    impl ERC721Impl = ERC721MultiMetadataComponent::ERC721Impl<ContractState>;
+
+    #[abi(embed_v0)]
+    impl ERC721CamelImpl =
+        ERC721MultiMetadataComponent::ERC721CamelOnlyImpl<ContractState>;
+
+    #[abi(embed_v0)]
+    impl ERC721MetadataImpl =
+        ERC721MultiMetadataComponent::ERC721MetadataImpl<ContractState>;
+
+    #[abi(embed_v0)]
+    impl ERC721MetadataCamelImpl =
+        ERC721MultiMetadataComponent::ERC721MetadataCamelOnlyImpl<ContractState>;
+
+    #[abi(embed_v0)]
+    impl FlexDropContractMetadataImpl =
+        ERC721MultiMetadataComponent::FlexDropContractMetadataImpl<ContractState>;
+
+    #[abi(embed_v0)]
+    impl SRC5Impl = SRC5Component::SRC5Impl<ContractState>;
+
+    #[abi(embed_v0)]
+    impl SRC5CamelImple = SRC5Component::SRC5CamelImpl<ContractState>;
+
+    #[abi(embed_v0)]
+    impl OwnableImpl = OwnableComponent::OwnableImpl<ContractState>;
+
+    impl ERC721InternalImpl = ERC721MultiMetadataComponent::InternalImpl<ContractState>;
+
+    impl OwnableInternalImpl = OwnableComponent::InternalImpl<ContractState>;
+
+    impl ReentrancyGuardInternalImpl = ReentrancyGuardComponent::InternalImpl<ContractState>;
+
+    impl SRC5Internal = SRC5Component::InternalImpl<ContractState>;
+
+
+    #[storage]
+    struct Storage {
+        current_token_id: u256,
+        // mapping allowed FlexDrop contract
+        allowed_flex_drop: LegacyMap::<ContractAddress, bool>,
+        total_minted: u64,
+        // mapping total minted per minter
+        total_minted_per_wallet: LegacyMap::<ContractAddress, u64>,
+        // Track the enumerated allowed FlexDrop address
+        enumerated_allowed_flex_drop: List<ContractAddress>,
+        current_phase_id: u64,
+        #[substorage(v0)]
+        erc721: ERC721MultiMetadataComponent::Storage,
+        #[substorage(v0)]
+        src5: SRC5Component::Storage,
+        #[substorage(v0)]
+        ownable: OwnableComponent::Storage,
+        #[substorage(v0)]
+        reentrancy_guard: ReentrancyGuardComponent::Storage
+    }
+
+    #[constructor]
+    fn constructor(
+        ref self: ContractState,
+        creator: ContractAddress,
+        name: ByteArray,
+        symbol: ByteArray,
+        token_base_uri: ByteArray,
+        allowed_flex_drop: Array::<ContractAddress>,
+    ) {
+        self.ownable.initializer(creator);
+        self.erc721.initializer(name, symbol, creator, token_base_uri);
+        self.current_token_id.write(1);
+        self.current_phase_id.write(1);
+
+        self.src5.register_interface(I_NON_FUNGIBLE_FLEX_DROP_TOKEN_ID);
+
+        let mut enumerate_allowed_flex_drop = self.enumerated_allowed_flex_drop.read();
+        enumerate_allowed_flex_drop.from_array(@allowed_flex_drop);
+        self.enumerated_allowed_flex_drop.write(enumerate_allowed_flex_drop);
+
+        let allowed_flex_drop_length: u32 = allowed_flex_drop.len().try_into().unwrap();
+        let mut index: u32 = 0;
+        loop {
+            if (index == allowed_flex_drop_length) {
+                break;
+            }
+
+            let flex_drop = allowed_flex_drop.at(index);
+            self.allowed_flex_drop.write(*flex_drop, true);
+            index += 1;
+        };
+    }
+
+    #[event]
+    #[derive(Drop, starknet::Event)]
+    enum Event {
+        UpdateAllowedFlexDrop: UpdateAllowedFlexDrop,
+        #[flat]
+        ERC721Event: ERC721MultiMetadataComponent::Event,
+        #[flat]
+        SRC5Event: SRC5Component::Event,
+        #[flat]
+        OwnableEvent: OwnableComponent::Event,
+        #[flat]
+        ReentrancyGuardEvent: ReentrancyGuardComponent::Event
+    }
+
+    #[derive(Drop, starknet::Event)]
+    struct UpdateAllowedFlexDrop {
+        new_flex_drop: Array::<ContractAddress>,
+    }
+
+    #[abi(embed_v0)]
+    impl NonFungibleFlexDropTokenImpl of INonFungibleFlexDropToken<ContractState> {
+        // update FlexDrop contract addresses
+        fn update_allowed_flex_drop(
+            ref self: ContractState, allowed_flex_drop: Array::<ContractAddress>
+        ) {
+            self.ownable.assert_only_owner();
+            let mut enumerated_allowed_flex_drop = self.enumerated_allowed_flex_drop.read();
+            let enumerated_allowed_flex_drop_length = enumerated_allowed_flex_drop.len();
+            let new_allowed_flex_drop_length = allowed_flex_drop.len();
+
+            // Reset the old mapping.
+            let mut index_enumerate: u32 = 0;
+            let cp_enumerated_allowed = enumerated_allowed_flex_drop.array();
+            loop {
+                if index_enumerate == enumerated_allowed_flex_drop_length {
+                    break;
+                }
+                let old_allowed_flex_drop = cp_enumerated_allowed.at(index_enumerate);
+                self.allowed_flex_drop.write(*old_allowed_flex_drop, false);
+                index_enumerate += 1;
+            };
+
+            // Set the new mapping for allowed FlexDrop contracts.
+            let mut index_new_allowed: u32 = 0;
+            let cp_new_allowed = allowed_flex_drop.clone();
+            loop {
+                if index_new_allowed == new_allowed_flex_drop_length {
+                    break;
+                }
+
+                self.allowed_flex_drop.write(*cp_new_allowed.at(index_new_allowed), true);
+                index_new_allowed += 1;
+            };
+
+            enumerated_allowed_flex_drop.from_array(@allowed_flex_drop);
+            self.enumerated_allowed_flex_drop.write(enumerated_allowed_flex_drop);
+            self.emit(UpdateAllowedFlexDrop { new_flex_drop: allowed_flex_drop })
+        }
+
+        // mint tokens, restricted to the FlexDrop contract
+        fn mint_flex_drop(ref self: ContractState, minter: ContractAddress, quantity: u64) {
+            self.reentrancy_guard.start();
+            let flex_drop = get_caller_address();
+            self.assert_allowed_flex_drop(flex_drop);
+
+            assert(
+                self.get_total_minted() + quantity <= self.get_max_supply(),
+                'Exceeds maximum total supply'
+            );
+
+            self.safe_mint_flex_drop(minter, quantity);
+            self.reentrancy_guard.end();
+        }
+
+        fn create_new_phase_drop(
+            ref self: ContractState,
+            flex_drop: ContractAddress,
+            phase_detail: PhaseDrop,
+            fee_recipient: ContractAddress,
+        ) {
+            self.ownable.assert_only_owner();
+            self.assert_allowed_flex_drop(flex_drop);
+            let current_phase_id = self.current_phase_id.read();
+            self.current_phase_id.write(current_phase_id + 1);
+
+            IFlexDropDispatcher { contract_address: flex_drop }
+                .start_new_phase_drop(current_phase_id, phase_detail, fee_recipient)
+        }
+
+
+        fn update_phase_drop(
+            ref self: ContractState,
+            flex_drop: ContractAddress,
+            phase_id: u64,
+            phase_detail: PhaseDrop
+        ) {
+            self.assert_owner_or_self();
+
+            self.assert_allowed_flex_drop(flex_drop);
+            IFlexDropDispatcher { contract_address: flex_drop }
+                .update_phase_drop(phase_id, phase_detail);
+        }
+
+        fn update_creator_payout(
+            ref self: ContractState, flex_drop: ContractAddress, payout_address: ContractAddress
+        ) {
+            self.assert_owner_or_self();
+
+            self.assert_allowed_flex_drop(flex_drop);
+
+            IFlexDropDispatcher { contract_address: flex_drop }
+                .update_creator_payout_address(payout_address);
+        }
+
+        // update payer address for paying gas fee of minting NFT
+        fn update_payer(
+            ref self: ContractState,
+            flex_drop: ContractAddress,
+            payer: ContractAddress,
+            allowed: bool
+        ) {
+            self.assert_owner_or_self();
+
+            self.assert_allowed_flex_drop(flex_drop);
+
+            IFlexDropDispatcher { contract_address: flex_drop }.update_payer(payer, allowed);
+        }
+
+        fn multi_configure(ref self: ContractState, config: MultiConfigureStruct) {
+            self.ownable.assert_only_owner();
+
+            let mut max_supply = config.max_supply;
+            if max_supply != 0 {
+                self.set_max_supply(max_supply);
+            }
+
+            if config.base_uri.len() > 0 {
+                self.set_base_uri(config.base_uri);
+            }
+
+            if config.contract_uri.len() > 0 {
+                self.set_contract_uri(config.contract_uri);
+            }
+
+            let phase_drop = config.phase_drop;
+            if phase_drop.phase_type != 0
+                && phase_drop.start_time != 0
+                && phase_drop.end_time != 0 {
+                if config.new_phase {
+                    self.create_new_phase_drop(config.flex_drop, phase_drop, config.fee_recipient);
+                } else {
+                    let current_id = self.current_phase_id.read();
+                    self.update_phase_drop(config.flex_drop, current_id, phase_drop);
+                }
+            }
+
+            if !config.creator_payout_address.is_zero() {
+                self.update_creator_payout(config.flex_drop, config.creator_payout_address);
+            }
+
+            if config.allowed_payers.len() > 0 {
+                let cp_allowed_payers = config.allowed_payers.clone();
+                let mut index: u32 = 0;
+                loop {
+                    if index == cp_allowed_payers.len() {
+                        break;
+                    }
+                    self.update_payer(config.flex_drop, *cp_allowed_payers.at(index), true);
+                    index += 1;
+                };
+            }
+
+            if config.disallowed_payers.len() > 0 {
+                let cp_disallowed_payers = config.disallowed_payers.clone();
+                let mut index: u32 = 0;
+                loop {
+                    if index == cp_disallowed_payers.len() {
+                        break;
+                    }
+                    self.update_payer(config.flex_drop, *cp_disallowed_payers.at(index), false);
+                    index += 1;
+                };
+            }
+        }
+
+        // return (number minted, current total supply, max supply)
+        fn get_mint_state(self: @ContractState, minter: ContractAddress) -> (u64, u64, u64) {
+            let total_minted = self.total_minted_per_wallet.read(minter);
+            let current_total_supply = self.get_total_minted();
+            let max_supply = self.get_max_supply();
+            (total_minted, current_total_supply, max_supply)
+        }
+
+        fn get_current_token_id(self: @ContractState) -> u256 {
+            self.current_token_id.read()
+        }
+    }
+
+    #[generate_trait]
+    impl InternalFlexDropToken of InternalFlexDropTokenTrait {
+        fn safe_mint_flex_drop(ref self: ContractState, to: ContractAddress, quantity: u64) {
+            let mut current_token_id = self.get_current_token_id();
+
+            self
+                .total_minted_per_wallet
+                .write(to, self.total_minted_per_wallet.read(to) + quantity);
+            self.current_token_id.write(current_token_id + quantity.into());
+            self.total_minted.write(self.get_total_minted() + quantity);
+
+            let mut index: u64 = 0;
+            loop {
+                if index == quantity {
+                    break;
+                }
+                self.erc721._safe_mint(to, current_token_id, ArrayTrait::<felt252>::new().span());
+                current_token_id += 1;
+                index += 1;
+            }
+        }
+
+        fn assert_allowed_flex_drop(self: @ContractState, flex_drop: ContractAddress) {
+            assert(self.allowed_flex_drop.read(flex_drop), 'Only allowed FlexDrop');
+        }
+
+        fn get_total_minted(self: @ContractState) -> u64 {
+            self.total_minted.read()
+        }
+
+        fn assert_owner_or_self(self: @ContractState) {
+            let caller = get_caller_address();
+            assert(
+                caller == self.ownable.owner() || caller == get_contract_address(), 'Only owner'
+            );
+        }
+    }
+}
diff --git a/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo b/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo
index c4b97a2..3bb2758 100644
--- a/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo
+++ b/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo
@@ -171,8 +171,10 @@ mod FlexDrop {
                 minter = minter_if_not_payer.clone();
             }
 
+            let mut is_payer: bool = false;
             if minter != get_caller_address() {
                 self.assert_allowed_payer(nft_address, get_caller_address());
+                is_payer = true;
             }
 
             self
@@ -188,6 +190,7 @@ mod FlexDrop {
                     nft_address,
                     get_caller_address(),
                     minter,
+                    is_payer,
                     quantity,
                     phase_drop.currency,
                     total_mint_price,
@@ -208,7 +211,7 @@ mod FlexDrop {
             let nft_address = get_caller_address();
 
             let phase_detail = self.phase_drops.read((nft_address, phase_drop_id));
-            assert!(phase_detail.phase_type == 0, "FlexDrop: Phase have not started");
+            assert!(phase_detail.phase_type == 0, "FlexDrop: Phase have been started");
             self.validate_new_phase_drop(@phase_drop);
 
             let new_phase_fee = self.new_phase_fee.read();
@@ -412,7 +415,7 @@ mod FlexDrop {
             assert!(*phase_drop.phase_type == 1, "FlexDrop: Currently supported public phase");
             assert!(
                 *phase_drop.start_time >= get_block_timestamp()
-                    + 86400 && *phase_drop.start_time
+                    + 1800 && *phase_drop.start_time
                     + 3600 <= *phase_drop.end_time,
                 "FlexDrop: Wrong start and end time"
             );
@@ -475,6 +478,7 @@ mod FlexDrop {
             nft_address: ContractAddress,
             payer: ContractAddress,
             minter: ContractAddress,
+            is_payer: bool,
             quantity: u64,
             currency_address: ContractAddress,
             total_mint_price: u256,
@@ -482,7 +486,7 @@ mod FlexDrop {
         ) {
             self
                 .split_payout(
-                    payer, nft_address, fee_recipient, currency_address, total_mint_price
+                    payer, is_payer, nft_address, fee_recipient, currency_address, total_mint_price
                 );
 
             INonFungibleFlexDropTokenDispatcher { contract_address: nft_address }
@@ -505,6 +509,7 @@ mod FlexDrop {
         fn split_payout(
             ref self: ContractState,
             from: ContractAddress,
+            is_payer: bool,
             nft_address: ContractAddress,
             fee_recipient: ContractAddress,
             currency_address: ContractAddress,
@@ -518,7 +523,7 @@ mod FlexDrop {
                 fee_currency_contract.transfer_from(from, fee_recipient, fee_mint);
             }
 
-            if total_mint_price > 0 {
+            if total_mint_price > 0 && !is_payer {
                 let currency_contract = IERC20Dispatcher { contract_address: currency_address };
                 let creator_payout_address = self.creator_payout_address.read(nft_address);
                 assert!(
diff --git a/flex_marketplace/src/marketplace/openedition/erc721/ERC721MultiMetadata.cairo b/flex_marketplace/src/marketplace/openedition/erc721/ERC721MultiMetadata.cairo
new file mode 100644
index 0000000..611e5c0
--- /dev/null
+++ b/flex_marketplace/src/marketplace/openedition/erc721/ERC721MultiMetadata.cairo
@@ -0,0 +1,629 @@
+/// # ERC721 Component
+///
+/// The ERC721 component provides implementations for the IERC721 interface,
+/// the IERC721Metadata interface and IFlexDropContractMetadata interface.
+#[starknet::component]
+mod ERC721MultiMetadataComponent {
+    use core::byte_array::ByteArrayTrait;
+    use openzeppelin::account;
+    use openzeppelin::introspection::dual_src5::{DualCaseSRC5, DualCaseSRC5Trait};
+    use openzeppelin::introspection::src5::SRC5Component::InternalTrait as SRC5InternalTrait;
+    use openzeppelin::introspection::src5::SRC5Component;
+    use openzeppelin::token::erc721::dual721_receiver::{
+        DualCaseERC721Receiver, DualCaseERC721ReceiverTrait
+    };
+    use flex::marketplace::openedition::interfaces::IFlexDropContractMetadata;
+    use flex::marketplace::openedition::interfaces::IERC721;
+    use starknet::ContractAddress;
+    use starknet::get_caller_address;
+    use integer::{U64PartialOrd, BoundedU64};
+
+
+    #[storage]
+    struct Storage {
+        ERC721_name: ByteArray,
+        ERC721_symbol: ByteArray,
+        ERC721_owners: LegacyMap<u256, ContractAddress>,
+        ERC721_balances: LegacyMap<ContractAddress, u256>,
+        ERC721_token_approvals: LegacyMap<u256, ContractAddress>,
+        ERC721_operator_approvals: LegacyMap<(ContractAddress, ContractAddress), bool>,
+        ERC721_base_uri: ByteArray,
+        ERC721_max_supply: u64,
+        ERC721_contract_uri: ByteArray,
+        ERC721_creator: ContractAddress
+    }
+
+    #[event]
+    #[derive(Drop, starknet::Event)]
+    enum Event {
+        Transfer: Transfer,
+        Approval: Approval,
+        ApprovalForAll: ApprovalForAll,
+    }
+
+    /// Emitted when `token_id` token is transferred from `from` to `to`.
+    #[derive(Drop, starknet::Event)]
+    struct Transfer {
+        #[key]
+        from: ContractAddress,
+        #[key]
+        to: ContractAddress,
+        #[key]
+        token_id: u256
+    }
+
+    /// Emitted when `owner` enables `approved` to manage the `token_id` token.
+    #[derive(Drop, starknet::Event)]
+    struct Approval {
+        #[key]
+        owner: ContractAddress,
+        #[key]
+        approved: ContractAddress,
+        #[key]
+        token_id: u256
+    }
+
+    /// Emitted when `owner` enables or disables (`approved`) `operator` to manage
+    /// all of its assets.
+    #[derive(Drop, starknet::Event)]
+    struct ApprovalForAll {
+        #[key]
+        owner: ContractAddress,
+        #[key]
+        operator: ContractAddress,
+        approved: bool
+    }
+
+    mod Errors {
+        const INVALID_TOKEN_ID: felt252 = 'ERC721: invalid token ID';
+        const INVALID_ACCOUNT: felt252 = 'ERC721: invalid account';
+        const UNAUTHORIZED: felt252 = 'ERC721: unauthorized caller';
+        const APPROVAL_TO_OWNER: felt252 = 'ERC721: approval to owner';
+        const SELF_APPROVAL: felt252 = 'ERC721: self approval';
+        const INVALID_RECEIVER: felt252 = 'ERC721: invalid receiver';
+        const ALREADY_MINTED: felt252 = 'ERC721: token already minted';
+        const WRONG_SENDER: felt252 = 'ERC721: wrong sender';
+        const SAFE_MINT_FAILED: felt252 = 'ERC721: safe mint failed';
+        const SAFE_TRANSFER_FAILED: felt252 = 'ERC721: safe transfer failed';
+        const NOT_CREATOR: felt252 = 'Caller is not the creator';
+        const ZERO_ADDRESS_CALLER: felt252 = 'Caller is the zero address';
+        const ZERO_ADDRESS_CREATOR: felt252 = 'New creator is the zero address';
+    }
+
+    //
+    // External
+    //
+
+    #[embeddable_as(ERC721Impl)]
+    impl ERC721<
+        TContractState,
+        +HasComponent<TContractState>,
+        +SRC5Component::HasComponent<TContractState>,
+        +Drop<TContractState>
+    > of IERC721::IERC721<ComponentState<TContractState>> {
+        /// Returns the number of NFTs owned by `account`.
+        fn balance_of(self: @ComponentState<TContractState>, account: ContractAddress) -> u256 {
+            assert(!account.is_zero(), Errors::INVALID_ACCOUNT);
+            self.ERC721_balances.read(account)
+        }
+
+        /// Returns the owner address of `token_id`.
+        ///
+        /// Requirements:
+        ///
+        /// - `token_id` exists.
+        fn owner_of(self: @ComponentState<TContractState>, token_id: u256) -> ContractAddress {
+            self._owner_of(token_id)
+        }
+
+        /// Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`.
+        ///
+        /// `data` is additional data, it has no specified format and it is sent in call to `to`.
+        ///
+        /// Requirements:
+        ///
+        /// - Caller is either approved or the `token_id` owner.
+        /// - `to` is not the zero address.
+        /// - `from` is not the zero address.
+        /// - `token_id` exists.
+        /// - `to` is either an account contract or supports the `IERC721Receiver` interface.
+        ///
+        /// Emits a `Transfer` event.
+        fn safe_transfer_from(
+            ref self: ComponentState<TContractState>,
+            from: ContractAddress,
+            to: ContractAddress,
+            token_id: u256,
+            data: Span<felt252>
+        ) {
+            assert(
+                self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED
+            );
+            self._safe_transfer(from, to, token_id, data);
+        }
+
+        /// Transfers ownership of `token_id` from `from` to `to`.
+        ///
+        /// Requirements:
+        ///
+        /// - Caller is either approved or the `token_id` owner.
+        /// - `to` is not the zero address.
+        /// - `from` is not the zero address.
+        /// - `token_id` exists.
+        ///
+        /// Emits a `Transfer` event.
+        fn transfer_from(
+            ref self: ComponentState<TContractState>,
+            from: ContractAddress,
+            to: ContractAddress,
+            token_id: u256
+        ) {
+            assert(
+                self._is_approved_or_owner(get_caller_address(), token_id), Errors::UNAUTHORIZED
+            );
+            self._transfer(from, to, token_id);
+        }
+
+        /// Change or reaffirm the approved address for an NFT.
+        ///
+        /// Requirements:
+        ///
+        /// - The caller is either an approved operator or the `token_id` owner.
+        /// - `to` cannot be the token owner.
+        /// - `token_id` exists.
+        ///
+        /// Emits an `Approval` event.
+        fn approve(ref self: ComponentState<TContractState>, to: ContractAddress, token_id: u256) {
+            let owner = self._owner_of(token_id);
+
+            let caller = get_caller_address();
+            assert(
+                owner == caller || self.is_approved_for_all(owner, caller), Errors::UNAUTHORIZED
+            );
+            self._approve(to, token_id);
+        }
+
+        /// Enable or disable approval for `operator` to manage all of the
+        /// caller's assets.
+        ///
+        /// Requirements:
+        ///
+        /// - `operator` cannot be the caller.
+        ///
+        /// Emits an `Approval` event.
+        fn set_approval_for_all(
+            ref self: ComponentState<TContractState>, operator: ContractAddress, approved: bool
+        ) {
+            self._set_approval_for_all(get_caller_address(), operator, approved)
+        }
+
+        /// Returns the address approved for `token_id`.
+        ///
+        /// Requirements:
+        ///
+        /// - `token_id` exists.
+        fn get_approved(self: @ComponentState<TContractState>, token_id: u256) -> ContractAddress {
+            assert(self._exists(token_id), Errors::INVALID_TOKEN_ID);
+            self.ERC721_token_approvals.read(token_id)
+        }
+
+        /// Query if `operator` is an authorized operator for `owner`.
+        fn is_approved_for_all(
+            self: @ComponentState<TContractState>, owner: ContractAddress, operator: ContractAddress
+        ) -> bool {
+            self.ERC721_operator_approvals.read((owner, operator))
+        }
+    }
+
+    #[embeddable_as(ERC721MetadataImpl)]
+    impl ERC721Metadata<
+        TContractState,
+        +HasComponent<TContractState>,
+        +SRC5Component::HasComponent<TContractState>,
+        +Drop<TContractState>
+    > of IERC721::IERC721Metadata<ComponentState<TContractState>> {
+        /// Returns the NFT name.
+        fn name(self: @ComponentState<TContractState>) -> ByteArray {
+            self.ERC721_name.read()
+        }
+
+        /// Returns the NFT symbol.
+        fn symbol(self: @ComponentState<TContractState>) -> ByteArray {
+            self.ERC721_symbol.read()
+        }
+
+        /// Returns the Uniform Resource Identifier (URI) for the `token_id` token.
+        /// If the URI is not set for the `token_id`, the return value will be `0`.
+        ///
+        /// Requirements:
+        ///
+        /// - `token_id` exists.
+        fn token_uri(self: @ComponentState<TContractState>, token_id: u256) -> ByteArray {
+            assert(self._exists(token_id), Errors::INVALID_TOKEN_ID);
+            let base_uri = self._base_uri();
+            if base_uri.len() == 0 {
+                return "";
+            } else {
+                return format!("{}{}", base_uri, token_id);
+            }
+        }
+    }
+
+    /// Adds camelCase support for `IERC721`.
+    #[embeddable_as(ERC721CamelOnlyImpl)]
+    impl ERC721CamelOnly<
+        TContractState,
+        +HasComponent<TContractState>,
+        +SRC5Component::HasComponent<TContractState>,
+        +Drop<TContractState>
+    > of IERC721::IERC721CamelOnly<ComponentState<TContractState>> {
+        fn balanceOf(self: @ComponentState<TContractState>, account: ContractAddress) -> u256 {
+            self.balance_of(account)
+        }
+
+        fn ownerOf(self: @ComponentState<TContractState>, tokenId: u256) -> ContractAddress {
+            self.owner_of(tokenId)
+        }
+
+        fn safeTransferFrom(
+            ref self: ComponentState<TContractState>,
+            from: ContractAddress,
+            to: ContractAddress,
+            tokenId: u256,
+            data: Span<felt252>
+        ) {
+            self.safe_transfer_from(from, to, tokenId, data)
+        }
+
+        fn transferFrom(
+            ref self: ComponentState<TContractState>,
+            from: ContractAddress,
+            to: ContractAddress,
+            tokenId: u256
+        ) {
+            self.transfer_from(from, to, tokenId)
+        }
+
+        fn setApprovalForAll(
+            ref self: ComponentState<TContractState>, operator: ContractAddress, approved: bool
+        ) {
+            self.set_approval_for_all(operator, approved)
+        }
+
+        fn getApproved(self: @ComponentState<TContractState>, tokenId: u256) -> ContractAddress {
+            self.get_approved(tokenId)
+        }
+
+        fn isApprovedForAll(
+            self: @ComponentState<TContractState>, owner: ContractAddress, operator: ContractAddress
+        ) -> bool {
+            self.is_approved_for_all(owner, operator)
+        }
+    }
+
+    /// Adds camelCase support for `IERC721Metadata`.
+    #[embeddable_as(ERC721MetadataCamelOnlyImpl)]
+    impl ERC721MetadataCamelOnly<
+        TContractState,
+        +HasComponent<TContractState>,
+        +SRC5Component::HasComponent<TContractState>,
+        +Drop<TContractState>
+    > of IERC721::IERC721MetadataCamelOnly<ComponentState<TContractState>> {
+        fn tokenURI(self: @ComponentState<TContractState>, tokenId: u256) -> ByteArray {
+            self.token_uri(tokenId)
+        }
+    }
+
+    #[embeddable_as(FlexDropContractMetadataImpl)]
+    impl FlexDropContractMetadata<
+        TContractState,
+        +HasComponent<TContractState>,
+        +SRC5Component::HasComponent<TContractState>,
+        +Drop<TContractState>
+    > of IFlexDropContractMetadata::IFlexDropContractMetadata<ComponentState<TContractState>> {
+        fn set_base_uri(ref self: ComponentState<TContractState>, new_token_uri: ByteArray) {
+            self._assert_only_creator();
+            self._set_base_uri(new_token_uri);
+        }
+
+        fn set_contract_uri(ref self: ComponentState<TContractState>, new_contract_uri: ByteArray) {
+            self._assert_only_creator();
+            self._set_contract_uri(new_contract_uri);
+        }
+
+        fn set_max_supply(ref self: ComponentState<TContractState>, new_max_supply: u64) {
+            self._assert_only_creator();
+            self._set_max_supply(new_max_supply);
+        }
+
+        fn get_base_uri(self: @ComponentState<TContractState>) -> ByteArray {
+            self._base_uri()
+        }
+
+        fn get_contract_uri(self: @ComponentState<TContractState>) -> ByteArray {
+            self._contract_uri()
+        }
+
+        fn get_max_supply(self: @ComponentState<TContractState>) -> u64 {
+            self._get_max_supply()
+        }
+    }
+
+    //
+    // Internal
+    //
+
+    #[generate_trait]
+    impl InternalImpl<
+        TContractState,
+        +HasComponent<TContractState>,
+        impl SRC5: SRC5Component::HasComponent<TContractState>,
+        +Drop<TContractState>
+    > of InternalTrait<TContractState> {
+        /// Initializes the contract by setting the token name and symbol.
+        /// This should only be used inside the contract's constructor.
+        fn initializer(
+            ref self: ComponentState<TContractState>,
+            name: ByteArray,
+            symbol: ByteArray,
+            creator: ContractAddress,
+            token_base_uri: ByteArray
+        ) {
+            self.ERC721_creator.write(creator);
+            self.ERC721_name.write(name);
+            self.ERC721_symbol.write(symbol);
+            self.ERC721_base_uri.write(token_base_uri);
+
+            let mut src5_component = get_dep_component_mut!(ref self, SRC5);
+            src5_component.register_interface(IERC721::IERC721_ID);
+            src5_component.register_interface(IERC721::IERC721_METADATA_ID);
+        }
+
+        /// Returns the owner address of `token_id`.
+        ///
+        /// Requirements:
+        ///
+        /// - `token_id` exists.
+        fn _owner_of(self: @ComponentState<TContractState>, token_id: u256) -> ContractAddress {
+            let owner = self.ERC721_owners.read(token_id);
+            match owner.is_zero() {
+                bool::False(()) => owner,
+                bool::True(()) => panic_with_felt252(Errors::INVALID_TOKEN_ID)
+            }
+        }
+
+        /// Returns whether `token_id` exists.
+        fn _exists(self: @ComponentState<TContractState>, token_id: u256) -> bool {
+            !self.ERC721_owners.read(token_id).is_zero()
+        }
+
+        /// Returns whether `spender` is allowed to manage `token_id`.
+        ///
+        /// Requirements:
+        ///
+        /// - `token_id` exists.
+        fn _is_approved_or_owner(
+            self: @ComponentState<TContractState>, spender: ContractAddress, token_id: u256
+        ) -> bool {
+            let owner = self._owner_of(token_id);
+            let is_approved_for_all = self.is_approved_for_all(owner, spender);
+            owner == spender || is_approved_for_all || spender == self.get_approved(token_id)
+        }
+
+        /// Changes or reaffirms the approved address for an NFT.
+        ///
+        /// Internal function without access restriction.
+        ///
+        /// Requirements:
+        ///
+        /// - `token_id` exists.
+        /// - `to` is not the current token owner.
+        ///
+        /// Emits an `Approval` event.
+        fn _approve(ref self: ComponentState<TContractState>, to: ContractAddress, token_id: u256) {
+            let owner = self._owner_of(token_id);
+            assert(owner != to, Errors::APPROVAL_TO_OWNER);
+
+            self.ERC721_token_approvals.write(token_id, to);
+            self.emit(Approval { owner, approved: to, token_id });
+        }
+
+        /// Enables or disables approval for `operator` to manage
+        /// all of the `owner` assets.
+        ///
+        /// Requirements:
+        ///
+        /// - `operator` cannot be the caller.
+        ///
+        /// Emits an `Approval` event.
+        fn _set_approval_for_all(
+            ref self: ComponentState<TContractState>,
+            owner: ContractAddress,
+            operator: ContractAddress,
+            approved: bool
+        ) {
+            assert(owner != operator, Errors::SELF_APPROVAL);
+            self.ERC721_operator_approvals.write((owner, operator), approved);
+            self.emit(ApprovalForAll { owner, operator, approved });
+        }
+
+        /// Mints `token_id` and transfers it to `to`.
+        /// Internal function without access restriction.
+        ///
+        /// Requirements:
+        ///
+        /// - `to` is not the zero address.
+        /// - `token_id` does not exist.
+        ///
+        /// Emits a `Transfer` event.
+        fn _mint(ref self: ComponentState<TContractState>, to: ContractAddress, token_id: u256) {
+            assert(!to.is_zero(), Errors::INVALID_RECEIVER);
+            assert(!self._exists(token_id), Errors::ALREADY_MINTED);
+
+            self.ERC721_balances.write(to, self.ERC721_balances.read(to) + 1);
+            self.ERC721_owners.write(token_id, to);
+
+            self.emit(Transfer { from: Zeroable::zero(), to, token_id });
+        }
+
+        /// Transfers `token_id` from `from` to `to`.
+        ///
+        /// Internal function without access restriction.
+        ///
+        /// Requirements:
+        ///
+        /// - `to` is not the zero address.
+        /// - `from` is the token owner.
+        /// - `token_id` exists.
+        ///
+        /// Emits a `Transfer` event.
+        fn _transfer(
+            ref self: ComponentState<TContractState>,
+            from: ContractAddress,
+            to: ContractAddress,
+            token_id: u256
+        ) {
+            assert(!to.is_zero(), Errors::INVALID_RECEIVER);
+            let owner = self._owner_of(token_id);
+            assert(from == owner, Errors::WRONG_SENDER);
+
+            // Implicit clear approvals, no need to emit an event
+            self.ERC721_token_approvals.write(token_id, Zeroable::zero());
+
+            self.ERC721_balances.write(from, self.ERC721_balances.read(from) - 1);
+            self.ERC721_balances.write(to, self.ERC721_balances.read(to) + 1);
+            self.ERC721_owners.write(token_id, to);
+
+            self.emit(Transfer { from, to, token_id });
+        }
+
+        /// Destroys `token_id`. The approval is cleared when the token is burned.
+        ///
+        /// This internal function does not check if the caller is authorized
+        /// to operate on the token.
+        ///
+        /// Requirements:
+        ///
+        /// - `token_id` exists.
+        ///
+        /// Emits a `Transfer` event.
+        fn _burn(ref self: ComponentState<TContractState>, token_id: u256) {
+            let owner = self._owner_of(token_id);
+
+            // Implicit clear approvals, no need to emit an event
+            self.ERC721_token_approvals.write(token_id, Zeroable::zero());
+
+            self.ERC721_balances.write(owner, self.ERC721_balances.read(owner) - 1);
+            self.ERC721_owners.write(token_id, Zeroable::zero());
+
+            self.emit(Transfer { from: owner, to: Zeroable::zero(), token_id });
+        }
+
+        /// Mints `token_id` if `to` is either an account or `IERC721Receiver`.
+        ///
+        /// `data` is additional data, it has no specified format and it is sent in call to `to`.
+        ///
+        /// Requirements:
+        ///
+        /// - `token_id` does not exist.
+        /// - `to` is either an account contract or supports the `IERC721Receiver` interface.
+        ///
+        /// Emits a `Transfer` event.
+        fn _safe_mint(
+            ref self: ComponentState<TContractState>,
+            to: ContractAddress,
+            token_id: u256,
+            data: Span<felt252>
+        ) {
+            self._mint(to, token_id);
+            assert(
+                _check_on_erc721_received(Zeroable::zero(), to, token_id, data),
+                Errors::SAFE_MINT_FAILED
+            );
+        }
+
+        /// Transfers ownership of `token_id` from `from` if `to` is either an account or `IERC721Receiver`.
+        ///
+        /// `data` is additional data, it has no specified format and it is sent in call to `to`.
+        ///
+        /// Requirements:
+        ///
+        /// - `to` cannot be the zero address.
+        /// - `from` must be the token owner.
+        /// - `token_id` exists.
+        /// - `to` is either an account contract or supports the `IERC721Receiver` interface.
+        ///
+        /// Emits a `Transfer` event.
+        fn _safe_transfer(
+            ref self: ComponentState<TContractState>,
+            from: ContractAddress,
+            to: ContractAddress,
+            token_id: u256,
+            data: Span<felt252>
+        ) {
+            self._transfer(from, to, token_id);
+            assert(
+                _check_on_erc721_received(from, to, token_id, data), Errors::SAFE_TRANSFER_FAILED
+            );
+        }
+
+        /// Sets the base URI.
+        fn _set_base_uri(ref self: ComponentState<TContractState>, base_uri: ByteArray) {
+            self.ERC721_base_uri.write(base_uri);
+        }
+
+        /// Base URI for computing `token_uri`.
+        ///
+        /// If set, the resulting URI for each token will be the concatenation of the base URI and the token ID.
+        /// Returns an empty `ByteArray` if not set.
+        fn _base_uri(self: @ComponentState<TContractState>) -> ByteArray {
+            self.ERC721_base_uri.read()
+        }
+
+        fn _set_contract_uri(
+            ref self: ComponentState<TContractState>, new_contract_uri: ByteArray
+        ) {
+            self.ERC721_contract_uri.write(new_contract_uri);
+        }
+
+        fn _contract_uri(self: @ComponentState<TContractState>) -> ByteArray {
+            self.ERC721_contract_uri.read()
+        }
+
+        fn _set_max_supply(ref self: ComponentState<TContractState>, new_max_supply: u64) {
+            assert(
+                U64PartialOrd::lt(new_max_supply, BoundedU64::max()),
+                'Cannot Exceed MaxSupply Of U64'
+            );
+            self.ERC721_max_supply.write(new_max_supply);
+        }
+
+        fn _get_max_supply(self: @ComponentState<TContractState>) -> u64 {
+            self.ERC721_max_supply.read()
+        }
+
+        fn _assert_only_creator(self: @ComponentState<TContractState>) {
+            let creator: ContractAddress = self.ERC721_creator.read();
+            let caller: ContractAddress = get_caller_address();
+
+            assert(!caller.is_zero(), Errors::ZERO_ADDRESS_CALLER);
+            assert(caller == creator, Errors::NOT_CREATOR);
+        }
+    }
+
+    /// Checks if `to` either is an account contract or has registered support
+    /// for the `IERC721Receiver` interface through SRC5.
+    fn _check_on_erc721_received(
+        from: ContractAddress, to: ContractAddress, token_id: u256, data: Span<felt252>
+    ) -> bool {
+        if (DualCaseSRC5 { contract_address: to }
+            .supports_interface(IERC721::IERC721_RECEIVER_ID)) {
+            DualCaseERC721Receiver { contract_address: to }
+                .on_erc721_received(
+                    get_caller_address(), from, token_id, data
+                ) == IERC721::IERC721_RECEIVER_ID
+        } else {
+            DualCaseSRC5 { contract_address: to }.supports_interface(account::interface::ISRC6_ID)
+        }
+    }
+}

From 792983505f873d2ced305a50c1bb7d95e3cc0989 Mon Sep 17 00:00:00 2001
From: Brian Sin <briansinw3b@gmail.com>
Date: Wed, 22 May 2024 14:04:00 +0700
Subject: [PATCH 2/3] remove total supply of ERC721

---
 .../openedition/ERC721_open_edition.cairo     | 22 +++++++------------
 .../ERC721_open_edition_multi_metadata.cairo  | 21 ++++++------------
 .../marketplace/openedition/FlexDrop.cairo    |  4 +---
 .../openedition/erc721/ERC721.cairo           | 22 -------------------
 .../erc721/ERC721MultiMetadata.cairo          | 22 -------------------
 .../IFlexDropContractMetadata.cairo           |  2 --
 .../INonFungibleFlexDropToken.cairo           |  3 ++-
 .../src/marketplace/utils/openedition.cairo   |  1 -
 8 files changed, 18 insertions(+), 79 deletions(-)

diff --git a/flex_marketplace/src/marketplace/openedition/ERC721_open_edition.cairo b/flex_marketplace/src/marketplace/openedition/ERC721_open_edition.cairo
index 057305f..6cc192a 100644
--- a/flex_marketplace/src/marketplace/openedition/ERC721_open_edition.cairo
+++ b/flex_marketplace/src/marketplace/openedition/ERC721_open_edition.cairo
@@ -1,5 +1,6 @@
 #[starknet::contract]
 mod ERC721OpenEdition {
+    use core::array::ArrayTrait;
     use alexandria_storage::list::ListTrait;
     use openzeppelin::access::ownable::OwnableComponent;
     use openzeppelin::introspection::src5::SRC5Component;
@@ -177,11 +178,6 @@ mod ERC721OpenEdition {
             let flex_drop = get_caller_address();
             self.assert_allowed_flex_drop(flex_drop);
 
-            assert(
-                self.get_total_minted() + quantity <= self.get_max_supply(),
-                'Exceeds maximum total supply'
-            );
-
             self.safe_mint_flex_drop(minter, quantity);
             self.reentrancy_guard.end();
         }
@@ -243,11 +239,6 @@ mod ERC721OpenEdition {
         fn multi_configure(ref self: ContractState, config: MultiConfigureStruct) {
             self.ownable.assert_only_owner();
 
-            let mut max_supply = config.max_supply;
-            if max_supply != 0 {
-                self.set_max_supply(max_supply);
-            }
-
             if config.base_uri.len() > 0 {
                 self.set_base_uri(config.base_uri);
             }
@@ -297,17 +288,20 @@ mod ERC721OpenEdition {
             }
         }
 
-        // return (number minted, current total supply, max supply)
-        fn get_mint_state(self: @ContractState, minter: ContractAddress) -> (u64, u64, u64) {
+        // return (number minted, current total supply)
+        fn get_mint_state(self: @ContractState, minter: ContractAddress) -> (u64, u64) {
             let total_minted = self.total_minted_per_wallet.read(minter);
             let current_total_supply = self.get_total_minted();
-            let max_supply = self.get_max_supply();
-            (total_minted, current_total_supply, max_supply)
+            (total_minted, current_total_supply)
         }
 
         fn get_current_token_id(self: @ContractState) -> u256 {
             self.current_token_id.read()
         }
+
+        fn get_allowed_flex_drops(self: @ContractState) -> Span::<ContractAddress> {
+            self.enumerated_allowed_flex_drop.read().array().span()
+        }
     }
 
     #[generate_trait]
diff --git a/flex_marketplace/src/marketplace/openedition/ERC721_open_edition_multi_metadata.cairo b/flex_marketplace/src/marketplace/openedition/ERC721_open_edition_multi_metadata.cairo
index 530a355..aa2269d 100644
--- a/flex_marketplace/src/marketplace/openedition/ERC721_open_edition_multi_metadata.cairo
+++ b/flex_marketplace/src/marketplace/openedition/ERC721_open_edition_multi_metadata.cairo
@@ -179,11 +179,6 @@ mod ERC721OpenEditionMultiMetadata {
             let flex_drop = get_caller_address();
             self.assert_allowed_flex_drop(flex_drop);
 
-            assert(
-                self.get_total_minted() + quantity <= self.get_max_supply(),
-                'Exceeds maximum total supply'
-            );
-
             self.safe_mint_flex_drop(minter, quantity);
             self.reentrancy_guard.end();
         }
@@ -245,11 +240,6 @@ mod ERC721OpenEditionMultiMetadata {
         fn multi_configure(ref self: ContractState, config: MultiConfigureStruct) {
             self.ownable.assert_only_owner();
 
-            let mut max_supply = config.max_supply;
-            if max_supply != 0 {
-                self.set_max_supply(max_supply);
-            }
-
             if config.base_uri.len() > 0 {
                 self.set_base_uri(config.base_uri);
             }
@@ -299,17 +289,20 @@ mod ERC721OpenEditionMultiMetadata {
             }
         }
 
-        // return (number minted, current total supply, max supply)
-        fn get_mint_state(self: @ContractState, minter: ContractAddress) -> (u64, u64, u64) {
+        // return (number minted, current total supply)
+        fn get_mint_state(self: @ContractState, minter: ContractAddress) -> (u64, u64) {
             let total_minted = self.total_minted_per_wallet.read(minter);
             let current_total_supply = self.get_total_minted();
-            let max_supply = self.get_max_supply();
-            (total_minted, current_total_supply, max_supply)
+            (total_minted, current_total_supply)
         }
 
         fn get_current_token_id(self: @ContractState) -> u256 {
             self.current_token_id.read()
         }
+
+        fn get_allowed_flex_drops(self: @ContractState) -> Span::<ContractAddress> {
+            self.enumerated_allowed_flex_drop.read().array().span()
+        }
     }
 
     #[generate_trait]
diff --git a/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo b/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo
index 3bb2758..30ca8e5 100644
--- a/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo
+++ b/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo
@@ -454,8 +454,7 @@ mod FlexDrop {
         ) {
             assert(quantity > 0, 'Only non zero quantity');
 
-            let (total_minted, current_total_supply, max_supply) =
-                INonFungibleFlexDropTokenDispatcher {
+            let (total_minted, _) = INonFungibleFlexDropTokenDispatcher {
                 contract_address: *nft_address
             }
                 .get_mint_state(*minter);
@@ -463,7 +462,6 @@ mod FlexDrop {
             assert(
                 total_minted + quantity <= max_total_mint_per_wallet, 'Exceeds maximum total minted'
             );
-            assert(quantity + current_total_supply <= max_supply, 'Exceeds maximum total supply');
         }
 
         fn assert_allowed_fee_recipient(self: @ContractState, fee_recipient: @ContractAddress,) {
diff --git a/flex_marketplace/src/marketplace/openedition/erc721/ERC721.cairo b/flex_marketplace/src/marketplace/openedition/erc721/ERC721.cairo
index 4bca428..492b22f 100644
--- a/flex_marketplace/src/marketplace/openedition/erc721/ERC721.cairo
+++ b/flex_marketplace/src/marketplace/openedition/erc721/ERC721.cairo
@@ -28,7 +28,6 @@ mod ERC721Component {
         ERC721_token_approvals: LegacyMap<u256, ContractAddress>,
         ERC721_operator_approvals: LegacyMap<(ContractAddress, ContractAddress), bool>,
         ERC721_base_uri: ByteArray,
-        ERC721_max_supply: u64,
         ERC721_contract_uri: ByteArray,
         ERC721_creator: ContractAddress
     }
@@ -331,11 +330,6 @@ mod ERC721Component {
             self._set_contract_uri(new_contract_uri);
         }
 
-        fn set_max_supply(ref self: ComponentState<TContractState>, new_max_supply: u64) {
-            self._assert_only_creator();
-            self._set_max_supply(new_max_supply);
-        }
-
         fn get_base_uri(self: @ComponentState<TContractState>) -> ByteArray {
             self._base_uri()
         }
@@ -343,10 +337,6 @@ mod ERC721Component {
         fn get_contract_uri(self: @ComponentState<TContractState>) -> ByteArray {
             self._contract_uri()
         }
-
-        fn get_max_supply(self: @ComponentState<TContractState>) -> u64 {
-            self._get_max_supply()
-        }
     }
 
     //
@@ -590,18 +580,6 @@ mod ERC721Component {
             self.ERC721_contract_uri.read()
         }
 
-        fn _set_max_supply(ref self: ComponentState<TContractState>, new_max_supply: u64) {
-            assert(
-                U64PartialOrd::lt(new_max_supply, BoundedU64::max()),
-                'Cannot Exceed MaxSupply Of U64'
-            );
-            self.ERC721_max_supply.write(new_max_supply);
-        }
-
-        fn _get_max_supply(self: @ComponentState<TContractState>) -> u64 {
-            self.ERC721_max_supply.read()
-        }
-
         fn _assert_only_creator(self: @ComponentState<TContractState>) {
             let creator: ContractAddress = self.ERC721_creator.read();
             let caller: ContractAddress = get_caller_address();
diff --git a/flex_marketplace/src/marketplace/openedition/erc721/ERC721MultiMetadata.cairo b/flex_marketplace/src/marketplace/openedition/erc721/ERC721MultiMetadata.cairo
index 611e5c0..8212bff 100644
--- a/flex_marketplace/src/marketplace/openedition/erc721/ERC721MultiMetadata.cairo
+++ b/flex_marketplace/src/marketplace/openedition/erc721/ERC721MultiMetadata.cairo
@@ -28,7 +28,6 @@ mod ERC721MultiMetadataComponent {
         ERC721_token_approvals: LegacyMap<u256, ContractAddress>,
         ERC721_operator_approvals: LegacyMap<(ContractAddress, ContractAddress), bool>,
         ERC721_base_uri: ByteArray,
-        ERC721_max_supply: u64,
         ERC721_contract_uri: ByteArray,
         ERC721_creator: ContractAddress
     }
@@ -331,11 +330,6 @@ mod ERC721MultiMetadataComponent {
             self._set_contract_uri(new_contract_uri);
         }
 
-        fn set_max_supply(ref self: ComponentState<TContractState>, new_max_supply: u64) {
-            self._assert_only_creator();
-            self._set_max_supply(new_max_supply);
-        }
-
         fn get_base_uri(self: @ComponentState<TContractState>) -> ByteArray {
             self._base_uri()
         }
@@ -343,10 +337,6 @@ mod ERC721MultiMetadataComponent {
         fn get_contract_uri(self: @ComponentState<TContractState>) -> ByteArray {
             self._contract_uri()
         }
-
-        fn get_max_supply(self: @ComponentState<TContractState>) -> u64 {
-            self._get_max_supply()
-        }
     }
 
     //
@@ -590,18 +580,6 @@ mod ERC721MultiMetadataComponent {
             self.ERC721_contract_uri.read()
         }
 
-        fn _set_max_supply(ref self: ComponentState<TContractState>, new_max_supply: u64) {
-            assert(
-                U64PartialOrd::lt(new_max_supply, BoundedU64::max()),
-                'Cannot Exceed MaxSupply Of U64'
-            );
-            self.ERC721_max_supply.write(new_max_supply);
-        }
-
-        fn _get_max_supply(self: @ComponentState<TContractState>) -> u64 {
-            self.ERC721_max_supply.read()
-        }
-
         fn _assert_only_creator(self: @ComponentState<TContractState>) {
             let creator: ContractAddress = self.ERC721_creator.read();
             let caller: ContractAddress = get_caller_address();
diff --git a/flex_marketplace/src/marketplace/openedition/interfaces/IFlexDropContractMetadata.cairo b/flex_marketplace/src/marketplace/openedition/interfaces/IFlexDropContractMetadata.cairo
index 9f93383..6e3b480 100644
--- a/flex_marketplace/src/marketplace/openedition/interfaces/IFlexDropContractMetadata.cairo
+++ b/flex_marketplace/src/marketplace/openedition/interfaces/IFlexDropContractMetadata.cairo
@@ -4,9 +4,7 @@ use starknet::ContractAddress;
 trait IFlexDropContractMetadata<TContractState> {
     fn set_base_uri(ref self: TContractState, new_token_uri: ByteArray);
     fn set_contract_uri(ref self: TContractState, new_contract_uri: ByteArray);
-    fn set_max_supply(ref self: TContractState, new_max_supply: u64);
     fn get_base_uri(self: @TContractState) -> ByteArray;
     fn get_contract_uri(self: @TContractState) -> ByteArray;
-    fn get_max_supply(self: @TContractState) -> u64;
 }
 
diff --git a/flex_marketplace/src/marketplace/openedition/interfaces/INonFungibleFlexDropToken.cairo b/flex_marketplace/src/marketplace/openedition/interfaces/INonFungibleFlexDropToken.cairo
index aceb775..bc795e4 100644
--- a/flex_marketplace/src/marketplace/openedition/interfaces/INonFungibleFlexDropToken.cairo
+++ b/flex_marketplace/src/marketplace/openedition/interfaces/INonFungibleFlexDropToken.cairo
@@ -31,6 +31,7 @@ trait INonFungibleFlexDropToken<TContractState> {
     );
     fn multi_configure(ref self: TContractState, config: MultiConfigureStruct);
     // return (number minted, current total supply, max supply)
-    fn get_mint_state(self: @TContractState, minter: ContractAddress) -> (u64, u64, u64);
+    fn get_mint_state(self: @TContractState, minter: ContractAddress) -> (u64, u64);
     fn get_current_token_id(self: @TContractState) -> u256;
+    fn get_allowed_flex_drops(self: @TContractState) -> Span::<ContractAddress>;
 }
diff --git a/flex_marketplace/src/marketplace/utils/openedition.cairo b/flex_marketplace/src/marketplace/utils/openedition.cairo
index 11f324c..52cbb5d 100644
--- a/flex_marketplace/src/marketplace/utils/openedition.cairo
+++ b/flex_marketplace/src/marketplace/utils/openedition.cairo
@@ -13,7 +13,6 @@ struct PhaseDrop {
 
 #[derive(Drop, Serde)]
 struct MultiConfigureStruct {
-    max_supply: u64,
     base_uri: ByteArray,
     contract_uri: ByteArray,
     flex_drop: ContractAddress,

From b614f51bfb05840cc623fbe8746db0b69fb624e7 Mon Sep 17 00:00:00 2001
From: Brian Sin <briansinw3b@gmail.com>
Date: Wed, 22 May 2024 18:29:56 +0700
Subject: [PATCH 3/3] feat/whitelist-mint

---
 .../src/marketplace/marketplace.cairo         |  20 +--
 .../marketplace/openedition/FlexDrop.cairo    | 101 ++++++++++-
 .../openedition/interfaces/IFlexDrop.cairo    |   8 +-
 .../src/marketplace/signature_checker2.cairo  | 167 +++++++++++++++++-
 .../src/marketplace/utils/openedition.cairo   |   7 +
 .../src/marketplace/utils/order_types.cairo   |   4 +-
 6 files changed, 285 insertions(+), 22 deletions(-)

diff --git a/flex_marketplace/src/marketplace/marketplace.cairo b/flex_marketplace/src/marketplace/marketplace.cairo
index 6c50f28..2e67cde 100644
--- a/flex_marketplace/src/marketplace/marketplace.cairo
+++ b/flex_marketplace/src/marketplace/marketplace.cairo
@@ -350,7 +350,7 @@ mod MarketPlace {
 
             self
                 .is_user_order_nonce_executed_or_cancelled
-                .write((maker_ask.signer, maker_ask.nonce), true);
+                .write((maker_ask.signer, maker_ask.salt_nonce), true);
 
             self
                 .transfer_fees_and_funds(
@@ -386,7 +386,7 @@ mod MarketPlace {
                 .emit(
                     TakerBid {
                         order_hash,
-                        order_nonce: maker_ask.nonce,
+                        order_nonce: maker_ask.salt_nonce,
                         taker: non_fungible_token_recipient,
                         maker: maker_ask.signer,
                         strategy: maker_ask.strategy,
@@ -434,7 +434,7 @@ mod MarketPlace {
 
             self
                 .is_user_order_nonce_executed_or_cancelled
-                .write((maker_bid.signer, maker_bid.nonce), true);
+                .write((maker_bid.signer, maker_bid.salt_nonce), true);
             self
                 .transfer_non_fungible_token(
                     maker_bid.collection, taker_ask.taker, maker_bid.signer, token_id, amount
@@ -459,7 +459,7 @@ mod MarketPlace {
                 .emit(
                     TakerAsk {
                         order_hash,
-                        order_nonce: maker_bid.nonce,
+                        order_nonce: maker_bid.salt_nonce,
                         taker: taker_ask.taker,
                         maker: maker_bid.signer,
                         strategy: maker_bid.strategy,
@@ -504,10 +504,10 @@ mod MarketPlace {
 
             self
                 .is_user_order_nonce_executed_or_cancelled
-                .write((maker_ask.signer, maker_ask.nonce), true);
+                .write((maker_ask.signer, maker_ask.salt_nonce), true);
             self
                 .is_user_order_nonce_executed_or_cancelled
-                .write((maker_bid.signer, maker_bid.nonce), true);
+                .write((maker_bid.signer, maker_bid.salt_nonce), true);
 
             self
                 .transfer_fees_and_funds(
@@ -534,7 +534,7 @@ mod MarketPlace {
                 .emit(
                     TakerBid {
                         order_hash,
-                        order_nonce: maker_ask.nonce,
+                        order_nonce: maker_ask.salt_nonce,
                         taker: maker_bid.signer,
                         maker: maker_ask.signer,
                         strategy: maker_ask.strategy,
@@ -715,14 +715,14 @@ mod MarketPlace {
             self: @ContractState, order: @MakerOrder, order_signature: Array<felt252>
         ) {
             let executed_order_cancelled = self
-                .get_is_user_order_nonce_executed_or_cancelled(*order.signer, *order.nonce);
+                .get_is_user_order_nonce_executed_or_cancelled(*order.signer, *order.salt_nonce);
             let min_nonce = self.get_user_min_order_nonce(*order.signer);
             assert!(!executed_order_cancelled, "MarketPlace: executed order is cancelled");
             assert!(
-                min_nonce <= *order.nonce,
+                min_nonce <= *order.salt_nonce,
                 "MarketPlace: min_nonce {} is higher than order nonce {}",
                 min_nonce,
-                *order.nonce
+                *order.salt_nonce
             );
             assert!(
                 !(*order.signer).is_zero(), "MarketPlace: invalid order signer {}", *order.signer
diff --git a/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo b/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo
index 30ca8e5..3a4f98c 100644
--- a/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo
+++ b/flex_marketplace/src/marketplace/openedition/FlexDrop.cairo
@@ -1,14 +1,15 @@
 #[starknet::contract]
 mod FlexDrop {
     use core::box::BoxTrait;
-    use flex::marketplace::utils::openedition::PhaseDrop;
+    use flex::marketplace::utils::openedition::{PhaseDrop, WhiteListParam};
     use flex::marketplace::openedition::IFlexDrop;
     use flex::marketplace::openedition::interfaces::INonFungibleFlexDropToken::{
         INonFungibleFlexDropTokenDispatcher, INonFungibleFlexDropTokenDispatcherTrait,
         I_NON_FUNGIBLE_FLEX_DROP_TOKEN_ID
     };
     use flex::marketplace::{
-        currency_manager::{ICurrencyManagerDispatcher, ICurrencyManagerDispatcherTrait}
+        currency_manager::{ICurrencyManagerDispatcher, ICurrencyManagerDispatcherTrait},
+        signature_checker2::{ISignatureChecker2Dispatcher, ISignatureChecker2DispatcherTrait},
     };
     use openzeppelin::token::erc20::interface::{IERC20Dispatcher, IERC20DispatcherTrait};
     use openzeppelin::access::ownable::OwnableComponent;
@@ -16,7 +17,7 @@ mod FlexDrop {
     use openzeppelin::security::reentrancyguard::ReentrancyGuardComponent;
     use openzeppelin::introspection::interface::{ISRC5Dispatcher, ISRC5DispatcherTrait};
     use alexandria_storage::list::{List, ListTrait};
-    use starknet::{ContractAddress, get_block_timestamp, get_caller_address, get_tx_info};
+    use starknet::{ContractAddress, get_block_timestamp, get_caller_address, get_tx_info, Zeroable};
     use array::{Array, ArrayTrait};
 
     component!(path: OwnableComponent, storage: ownable, event: OwnableEvent);
@@ -51,6 +52,14 @@ mod FlexDrop {
         fee_currency: ContractAddress,
         // start new phase fee
         new_phase_fee: u256,
+        // validator validating the proof of whitelist
+        validator: ContractAddress,
+        // domain hash
+        domain_hash: felt252,
+        // contract to verify proof
+        signature_checker: ContractAddress,
+        // mapping proof => is used
+        is_used_proof: LegacyMap::<felt252, bool>,
         // mapping nft address => enumerated allowed payer
         enumerated_allowed_payer: LegacyMap::<ContractAddress, List<ContractAddress>>,
         currency_manager: ICurrencyManagerDispatcher,
@@ -129,12 +138,18 @@ mod FlexDrop {
         fee_currency: ContractAddress,
         fee_mint: u256,
         new_phase_fee: u256,
+        domain_hash: felt252,
+        validator: ContractAddress,
+        signature_checker: ContractAddress,
         fee_recipients: Span::<ContractAddress>
     ) {
         self.ownable.initializer(owner);
         self.fee_currency.write(fee_currency);
         self.fee_mint.write(fee_mint);
         self.new_phase_fee.write(new_phase_fee);
+        self.validator.write(validator);
+        self.domain_hash.write(domain_hash);
+        self.signature_checker.write(signature_checker);
         self
             .currency_manager
             .write(ICurrencyManagerDispatcher { contract_address: currency_manager });
@@ -199,6 +214,51 @@ mod FlexDrop {
             self.reentrancy.end();
         }
 
+        fn whitelist_mint(
+            ref self: ContractState,
+            whitelist_data: WhiteListParam,
+            fee_recipient: ContractAddress,
+            proof: Array<felt252>
+        ) {
+            self.pausable.assert_not_paused();
+            self.reentrancy.start();
+            let phase_drop = self
+                .phase_drops
+                .read((whitelist_data.nft_address, whitelist_data.phase_id));
+            self.assert_active_phase_drop(@phase_drop);
+
+            let sig_checker_dis = ISignatureChecker2Dispatcher {
+                contract_address: self.get_signature_checker()
+            };
+
+            self.assert_allowed_fee_recipient(@fee_recipient);
+
+            sig_checker_dis
+                .verify_whitelist_mint_proof(
+                    self.get_domain_hash(), self.get_validator(), whitelist_data, proof
+                );
+            let mint_hash = sig_checker_dis
+                .compute_whitelist_mint_message_hash(
+                    self.get_domain_hash(), self.get_validator(), whitelist_data
+                );
+            assert(!self.is_used_proof.read(mint_hash), 'FlexDrop: Proof is used');
+
+            self.is_used_proof.write(mint_hash, true);
+
+            self
+                .mint_and_pay(
+                    whitelist_data.nft_address,
+                    whitelist_data.minter,
+                    whitelist_data.minter,
+                    false,
+                    1,
+                    phase_drop.currency,
+                    0,
+                    fee_recipient
+                );
+            self.reentrancy.end();
+        }
+
         fn start_new_phase_drop(
             ref self: ContractState,
             phase_drop_id: u64,
@@ -371,6 +431,41 @@ mod FlexDrop {
             self.new_phase_fee.write(new_fee)
         }
 
+        #[external(v0)]
+        fn update_validator(ref self: ContractState, new_validator: ContractAddress) {
+            self.ownable.assert_only_owner();
+            self.validator.write(new_validator);
+        }
+
+        #[external(v0)]
+        fn get_validator(self: @ContractState) -> ContractAddress {
+            self.validator.read()
+        }
+
+        #[external(v0)]
+        fn update_domain_hash(ref self: ContractState, new_domain_hash: felt252) {
+            self.ownable.assert_only_owner();
+            self.domain_hash.write(new_domain_hash);
+        }
+
+        #[external(v0)]
+        fn get_domain_hash(self: @ContractState) -> felt252 {
+            self.domain_hash.read()
+        }
+
+        #[external(v0)]
+        fn update_signature_checker(
+            ref self: ContractState, new_signature_checker: ContractAddress
+        ) {
+            self.ownable.assert_only_owner();
+            self.signature_checker.write(new_signature_checker);
+        }
+
+        #[external(v0)]
+        fn get_signature_checker(self: @ContractState) -> ContractAddress {
+            self.signature_checker.read()
+        }
+
         #[external(v0)]
         fn get_phase_drop(
             self: @ContractState, nft_address: ContractAddress, phase_id: u64
diff --git a/flex_marketplace/src/marketplace/openedition/interfaces/IFlexDrop.cairo b/flex_marketplace/src/marketplace/openedition/interfaces/IFlexDrop.cairo
index 1288fe1..a4088d8 100644
--- a/flex_marketplace/src/marketplace/openedition/interfaces/IFlexDrop.cairo
+++ b/flex_marketplace/src/marketplace/openedition/interfaces/IFlexDrop.cairo
@@ -1,5 +1,5 @@
 use starknet::ContractAddress;
-use flex::marketplace::utils::openedition::PhaseDrop;
+use flex::marketplace::utils::openedition::{PhaseDrop, WhiteListParam};
 
 #[starknet::interface]
 trait IFlexDrop<TContractState> {
@@ -11,6 +11,12 @@ trait IFlexDrop<TContractState> {
         minter_if_not_payer: ContractAddress,
         quantity: u64,
     );
+    fn whitelist_mint(
+        ref self: TContractState,
+        whitelist_data: WhiteListParam,
+        fee_recipient: ContractAddress,
+        proof: Array<felt252>
+    );
     fn start_new_phase_drop(
         ref self: TContractState,
         phase_drop_id: u64,
diff --git a/flex_marketplace/src/marketplace/signature_checker2.cairo b/flex_marketplace/src/marketplace/signature_checker2.cairo
index a5869ae..2e6b27e 100644
--- a/flex_marketplace/src/marketplace/signature_checker2.cairo
+++ b/flex_marketplace/src/marketplace/signature_checker2.cairo
@@ -1,35 +1,90 @@
 use starknet::ContractAddress;
 
 use flex::marketplace::utils::order_types::MakerOrder;
+use flex::marketplace::utils::openedition::WhiteListParam;
 
 const STARKNET_MESSAGE: felt252 = 110930206544689809660069706067448260453;
 
 const HASH_MESSAGE_SELECTOR: felt252 =
     563771258078353655219004671487831885088158240957819730493696170021701903504;
 
+const STARKNET_MAKER_ORDER_TYPE_HASH: felt252 =
+    selector!(
+        "MakerOrder(is_order_ask:u8,signer:felt,collection:felt,price:u128,seller:felt,token_id:u256,amount:u128,strategy:felt,currency:felt,salt_nonce:u128,start_time:u64,end_time:u64,min_percentage_to_ask:u128,params:felt)u256(low:felt,high:felt)"
+    );
+
+const STARKNET_WHITELIST_TYPE_HASH: felt252 =
+    selector!("WhiteListParam(phase_id:u64,nft_address:felt,minter:felt)");
+
+const U256_TYPE_HASH: felt252 = selector!("u256(low:felt,high:felt)");
+
 #[starknet::interface]
 trait ISignatureChecker2<TState> {
     fn compute_maker_order_hash(self: @TState, hash_domain: felt252, order: MakerOrder) -> felt252;
     fn verify_maker_order_signature(
         self: @TState, hash_domain: felt252, order: MakerOrder, order_signature: Array<felt252>
     );
+    fn compute_message_hash(self: @TState, domain_hash: felt252, order: MakerOrder) -> felt252;
+    fn verify_maker_order_signature_v2(
+        self: @TState, domain_hash: felt252, order: MakerOrder, order_signature: Array<felt252>
+    );
+
+    fn compute_whitelist_mint_message_hash(
+        self: @TState, domain_hash: felt252, signer: ContractAddress, whitelist_data: WhiteListParam
+    ) -> felt252;
+    fn verify_whitelist_mint_proof(
+        self: @TState,
+        domain_hash: felt252,
+        signer: ContractAddress,
+        whitelist_data: WhiteListParam,
+        proof: Array<felt252>
+    );
 }
 
 #[starknet::contract]
 mod SignatureChecker2 {
+    use openzeppelin::account::interface::AccountABIDispatcherTrait;
+    use core::option::OptionTrait;
+    use core::traits::Into;
+    use core::traits::TryInto;
+    use core::box::BoxTrait;
     use flex::marketplace::signature_checker2::ISignatureChecker2;
-    use starknet::ContractAddress;
+    use starknet::{ContractAddress, get_tx_info, contract_address_to_felt252};
     use poseidon::poseidon_hash_span;
-
+    use pedersen::PedersenTrait;
+    use hash::{HashStateTrait, HashStateExTrait};
+    use openzeppelin::account::interface::AccountABIDispatcher;
     use openzeppelin::account::interface::{ISRC6CamelOnlyDispatcher, ISRC6CamelOnlyDispatcherTrait};
-
-    use flex::marketplace::utils::order_types::MakerOrder;
+    use openzeppelin::account::interface::{ISRC6Dispatcher, ISRC6DispatcherTrait};
+    use super::{MakerOrder, WhiteListParam};
 
     #[storage]
     struct Storage {}
 
+    #[constructor]
+    fn constructor(ref self: ContractState) {}
+
+    #[derive(Drop, Copy, Serde, Hash)]
+    struct StarknetDomain {
+        name: felt252,
+        version: felt252,
+        chain_id: felt252,
+    }
+
     #[abi(embed_v0)]
     impl SignatureChecker2Impl of super::ISignatureChecker2<ContractState> {
+        fn compute_message_hash(
+            self: @ContractState, domain_hash: felt252, order: MakerOrder
+        ) -> felt252 {
+            let mut state = PedersenTrait::new(0);
+            state = state.update_with('StarkNet Message');
+            state = state.update_with(domain_hash);
+            state = state.update_with(order.signer);
+            state = state.update_with(order.hash_struct());
+            state = state.update_with(4);
+            state.finalize()
+        }
+
         fn compute_maker_order_hash(
             self: @ContractState, hash_domain: felt252, order: MakerOrder
         ) -> felt252 {
@@ -43,7 +98,7 @@ mod SignatureChecker2 {
                 order.amount.into(),
                 order.strategy.into(),
                 order.currency.into(),
-                order.nonce.into(),
+                order.salt_nonce.into(),
                 order.start_time.into(),
                 order.end_time.into(),
                 order.min_percentage_to_ask.into(),
@@ -65,10 +120,108 @@ mod SignatureChecker2 {
             order_signature: Array<felt252>
         ) {
             let hash = self.compute_maker_order_hash(hash_domain, order);
-            let result = ISRC6CamelOnlyDispatcher { contract_address: order.signer }
-                .isValidSignature(hash, order_signature);
+            let result = ISRC6Dispatcher { contract_address: order.signer }
+                .is_valid_signature(hash, order_signature);
 
             assert!(result == starknet::VALIDATED, "SignatureChecker: Invalid signature");
         }
+
+        fn verify_maker_order_signature_v2(
+            self: @ContractState,
+            domain_hash: felt252,
+            order: MakerOrder,
+            order_signature: Array<felt252>
+        ) {
+            let hash = self.compute_message_hash(domain_hash, order);
+            let account: AccountABIDispatcher = AccountABIDispatcher {
+                contract_address: order.signer
+            };
+            let result = account.is_valid_signature(hash, order_signature);
+
+            assert!(result == starknet::VALIDATED, "SignatureChecker: Invalid signature");
+        }
+
+        fn compute_whitelist_mint_message_hash(
+            self: @ContractState,
+            domain_hash: felt252,
+            signer: ContractAddress,
+            whitelist_data: WhiteListParam
+        ) -> felt252 {
+            let mut state = PedersenTrait::new(0);
+            state = state.update_with('StarkNet Message');
+            state = state.update_with(domain_hash);
+            state = state.update_with(signer);
+            state = state.update_with(whitelist_data.hash_struct());
+            state = state.update_with(4);
+            state.finalize()
+        }
+
+        fn verify_whitelist_mint_proof(
+            self: @ContractState,
+            domain_hash: felt252,
+            signer: ContractAddress,
+            whitelist_data: WhiteListParam,
+            proof: Array<felt252>
+        ) {
+            let hash = self
+                .compute_whitelist_mint_message_hash(domain_hash, signer, whitelist_data);
+            let account: AccountABIDispatcher = AccountABIDispatcher { contract_address: signer };
+            let result = account.is_valid_signature(hash, proof);
+
+            assert!(result == starknet::VALIDATED, "SignatureChecker: Invalid proof");
+        }
+    }
+
+    trait IStructHash<T> {
+        fn hash_struct(self: @T) -> felt252;
+    }
+
+    impl StructHashWhiteList of IStructHash<WhiteListParam> {
+        fn hash_struct(self: @WhiteListParam) -> felt252 {
+            let mut state = PedersenTrait::new(0);
+            state = state.update_with(super::STARKNET_WHITELIST_TYPE_HASH);
+            state = state.update_with(*self.phase_id);
+            state = state.update_with(contract_address_to_felt252(*self.nft_address));
+            state = state.update_with(contract_address_to_felt252(*self.minter));
+            state = state.update_with(4);
+            state.finalize()
+        }
+    }
+
+    impl StructHashMarkerOrder of IStructHash<MakerOrder> {
+        fn hash_struct(self: @MakerOrder) -> felt252 {
+            let mut state = PedersenTrait::new(0);
+            state = state.update_with(super::STARKNET_MAKER_ORDER_TYPE_HASH);
+            let mut is_order_ask_u8: u8 = 1;
+            if !(*self.is_order_ask) {
+                is_order_ask_u8 = 0;
+            }
+            state = state.update_with(is_order_ask_u8);
+            state = state.update_with(contract_address_to_felt252(*self.signer));
+            state = state.update_with(contract_address_to_felt252(*self.collection));
+            state = state.update_with(*self.price);
+            state = state.update_with(contract_address_to_felt252(*self.seller));
+            state = state.update_with(self.token_id.hash_struct());
+            state = state.update_with(*self.amount);
+            state = state.update_with(contract_address_to_felt252(*self.strategy));
+            state = state.update_with(contract_address_to_felt252(*self.currency));
+            state = state.update_with(*self.salt_nonce);
+            state = state.update_with(*self.start_time);
+            state = state.update_with(*self.end_time);
+            state = state.update_with(*self.min_percentage_to_ask);
+            state = state.update_with(*self.params);
+            state = state.update_with(15);
+            state.finalize()
+        }
+    }
+
+    impl StructHashU256 of IStructHash<u256> {
+        fn hash_struct(self: @u256) -> felt252 {
+            let mut state = PedersenTrait::new(0);
+            state = state.update_with(super::U256_TYPE_HASH);
+            state = state.update_with(*self);
+            state = state.update_with(3);
+            state.finalize()
+        }
     }
 }
diff --git a/flex_marketplace/src/marketplace/utils/openedition.cairo b/flex_marketplace/src/marketplace/utils/openedition.cairo
index 52cbb5d..24c3674 100644
--- a/flex_marketplace/src/marketplace/utils/openedition.cairo
+++ b/flex_marketplace/src/marketplace/utils/openedition.cairo
@@ -11,6 +11,13 @@ struct PhaseDrop {
     phase_type: u8 // 1 for public sale, 2 for private sale...
 }
 
+#[derive(Drop, Copy, Serde)]
+struct WhiteListParam {
+    phase_id: u64,
+    nft_address: ContractAddress,
+    minter: ContractAddress,
+}
+
 #[derive(Drop, Serde)]
 struct MultiConfigureStruct {
     base_uri: ByteArray,
diff --git a/flex_marketplace/src/marketplace/utils/order_types.cairo b/flex_marketplace/src/marketplace/utils/order_types.cairo
index 6ed85f2..817a9a4 100644
--- a/flex_marketplace/src/marketplace/utils/order_types.cairo
+++ b/flex_marketplace/src/marketplace/utils/order_types.cairo
@@ -7,11 +7,12 @@ struct MakerOrder {
     signer: ContractAddress, // signer of the maker order
     collection: ContractAddress, // collection address
     price: u128,
+    seller: ContractAddress,
     token_id: u256,
     amount: u128, // amount of tokens to sell/purchase (must be 1 for ERC721, 1+ for ERC1155)
     strategy: ContractAddress, // strategy address for trade execution (e.g. StandardSaleForFixedPrice)
     currency: ContractAddress, // currency address
-    nonce: u128, // order nonce (must be unique unless new maker order is meant to override existing one e.g. lower ask price)
+    salt_nonce: u128, // order nonce (must be unique unless new maker order is meant to override existing one e.g. lower ask price)
     start_time: u64, // startTime in timestamp
     end_time: u64, // endTime in timestamp
     min_percentage_to_ask: u128, // slippage protection (9000 = 90% of the final price must return to ask)
@@ -24,6 +25,7 @@ struct TakerOrder {
     taker: ContractAddress, // caller
     price: u128, // final price for the purchase
     token_id: u256,
+    amount: u128,
     min_percentage_to_ask: u128, // slippage protection (9000 = 90% of the final price must return to ask)
     params: felt252,
 }