This PR is creating a new structure for Mint Gold Dust smart contract
The main goal of this branch was add the auction functionality for our platform. But after raise some requirements and had analysed the code, I though better to break the Marketplace smart contract in more contracts that we can bring some good patterns like:
reusability
single responsibility
dependence inversion
inheritance and composition
low coupling
So now our structure have 6 smart contracts:
MGDCompany.sol
This smart contract is responsible by the functionalities related with MGD Management.
Like:
set validators
whitelisting/blacklisting
update the fees.
MGDnft.sol
This smart contract is responsible by mint new MGD Nfts. Actually this contract is an ERC721.
MGDMarketplace.sol
This is an abstract smart contract that have the purchase function that will be used by its children (Set Price Market and Auction) and also a virtual list function that will be implemented in each way for each children. Here also there are some events and modifiers for general marketplace actions.
This is smart contract is composed by the MGDCompany and MGDnft. So this way is possible to do the required verification and actions based on these contracts.
MGDSetPrice.sol
This is a children of MGDMarketplace and have some specfic functionalities related with a set fixed price market.
Some functions like:
list
delist and
update a listed item,
MDGAuction.sol
This is also a children of MGDMarketplace. Here we have all the functionalities related with an auction like:
list for auction
place a bid
end auction
MGDMemoir.sol
A contract responsible by allow new address to create a memoir.
IMPORTANT!!
Deprecate code
The idea is stop to use the old smart contract created:
GDMarketplace.sol and its interface IGD.sol
GDAuction.sol and IGDAuction.sol that I was creating before this restructuring.
GD.ts (Now I changed to GD.md to not be executed at the moment of tests) and
GDAuction.ts (That I also have changed to GDAuction.md to not be executed at the moment of tests) and
So now we have to do the required changes to adequate Backend + Frontend and the new Smart Contracts structure.
Tests
Also these PR is adding more 4 important files that contain the tests for the described smart contracts. Until now we have a total of 56 tests passing.
The files are:
MGDAuction.ts
MGDCompany.ts
MGDnft.ts
MGDSetPrice.ts
MGDMemoir.ts
Executing the tetst
To verify the tests. Go to this branch and run:
npx hardhat test
The results would be the followings:
MGDAuction.sol Smart Contract
___________________________
This smart contract is responsible by all functionalities related with the marketplace auction.
_________________________________ Tests related with listing a NFT for Auction _________________________________
ARTIST BALANCE BEFORE LIST: 9999.99969
ARTIST BALANCE AFTER LIST: 9999.99948
So the gas estimation was more less: 0.53397141539906
✔ Should track newly listed item, transfer NFT from seller to MGD marketplace and emit the NftListed event. (74ms)
✔ Should revert the transaction if an artist is not the owner of the token and try to list on the gold dust marketplace.
✔ Should track a creation of an auction without a reserve price that expect the following conditions:
- Expect emit the AuctionCreated event;
- Expect auction structure attributes match with all passed to create auction function;
- Auction end time should not be started yet to 24 hours and should be zero.
- The auction price (initial price) should be zero. This way after any bid greater than zero the time of 24 hours should starts. (42ms)
✔ Should track a creation of an auction with a reserve price that expect the following conditions:
- Expect emit the AuctionCreated event;
- Expect auction structure attributes match with all passed to create auction function;
- Auction end time should not be started yet to 24 hours and should be zero.
- The auction price (initial price) should be zero. This way after any bid greater than zero the time of 24 hours should starts.
______________________________ PLACE A BID GENERAL UNHAPPY PATHS ______________________________
✔ Should revert with an AuctionEndedAlready() error when some user tries to bid in a timed auction that have ended already. (4092ms)
✔ Should revert with an AuctionCreatorCannotBid() error if the auction creator (NFT Owner) tries to place a bid. (44ms)
✔ Should revert with an LastBidderCannotBidAgain() error if the last bidder tries to place a bid again. (55ms)
✔ Should revert with an BidTooLow() error when some user tries to place bid with a value equal the highest bid. (56ms)
✔ Should revert with an BidTooLow() error when some user tries to place bid with a value less than the highest bid. (57ms)
✔ Should revert with an BidTooLow() error when some user tries to place the first bid with a value less than the reserve price in an auction with a reserve price.
✔ Should revert with an BidTooLow() error when some user tries to place the first bid with a value equal zero in an auction without a reserve price. (41ms)
_________________________________ PLACE A BID HAPPY PATHS _________________________________
------------------ AUCTION WITH A RESERVE PRICE ------------------
HIGHEST BID BEFORE FIRST BID: BigNumber { value: "0" }
AUCTION END TIME BEFORE BID: BigNumber { value: "0" }
GAS PRICE TO PLACE A BID: BigNumber { value: "1000000028" }
GAS LIMIT TO PLACE A BID: BigNumber { value: "122439" }
TOTAL GAS ESTIMATION (USD): 0.0030609750857073
AUCTION CONTRACT BALANCE BEFORE BID: 0
AUCTION CONTRACT BALANCE AFTER BID: 4
BIDDER BALANCE BEFORE BID: 9983.999358697965
BIDDER BALANCE AFTER BID: 9979.999236258962
BIDDER BALANCE AFTER + GAS + PRICE SHOULD BE EQUALS BALANCE BEFORE: 9983.999358697965
AUCTION END TIME AFTER BID: 1681363421
HIGHEST AFTER FIRST BID: 4
✔ Should place a first bid and:
- Verify if the highest bid was updated.
- Verify if the endAuction time was updated.
- Verify if the bidder balance was decreased the gas fee plus the auction price.
- Verify if the auction contract balance was added by the bid value. (209ms)
------------------ AUCTION WITHOUT A RESERVE PRICE ------------------
HIGHEST BID BEFORE FIRST BID: BigNumber { value: "0" }
AUCTION END TIME BEFORE BID: BigNumber { value: "0" }
GAS PRICE TO PLACE A BID: BigNumber { value: "1000000009" }
GAS LIMIT TO PLACE A BID: BigNumber { value: "142346" }
TOTAL GAS ESTIMATION (USD): 0.00355865003202785
AUCTION CONTRACT BALANCE BEFORE BID: 0
AUCTION CONTRACT BALANCE AFTER BID: 4
BIDDER BALANCE BEFORE BID: 9979.999236258962
BIDDER BALANCE AFTER BID: 9975.99909391296
BIDDER BALANCE AFTER + GAS + PRICE SHOULD BE EQUALS BALANCE BEFORE: 9979.999236258962
AUCTION END TIME AFTER BID: 1681363433
HIGHEST AFTER FIRST BID: 4
✔ Should place a first bid and:
- Verify if the highest bid was updated.
- Verify if the endAuction time was updated.
- Verify if the bidder balance was decreased the gas fee plus the auction price.
- Verify if the auction contract balance was added by the bid value. (193ms)
------------------ SECOND BID BUT BEFORE THE LAST 5 MINUTES ------------------
HIGHEST BID BEFORE FIRST BID: BigNumber { value: "0" }
AUCTION END TIME BEFORE BID: BigNumber { value: "0" }
HIGHEST BID AFTER FIRST BID: 4
AUCTION END TIME AFTER FIRST BID: 1681363445
AUCTION CONTRACT BALANCE BEFORE FIRST BID: 0
AUCTION CONTRACT BALANCE AFTER FIRST BID: 4
AUCTION CONTRACT BALANCE AFTER SECOND BID: 6
BIDDER 1 BALANCE BEFORE FIRST BID: 9975.99909391296
BIDDER 1 BALANCE AFTER FIRST BID: 9971.998951566959
BIDDER 1 BALANCE AFTER + GAS + PRICE SHOULD BE EQUALS BALANCE BEFORE: 9975.99909391296
BIDDER 1 BALANCE AFTER SECOND BID SHOULD BE REFUNDED: 9975.998951566959
BIDDER 2 BALANCE BEFORE FIRST BID: 9999.999875339932
BIDDER 2 BALANCE AFTER FIRST BID: 9993.999800386933
BIDDER 2 BALANCE AFTER + GAS + PRICE SHOULD BE EQUALS BALANCE BEFORE: 9999.999875339932
HIGHEST AFTER SECOND BID: 6
AUCTION END TIME AFTER SECOND BID SHOULD KEEP THE SAME: 1681363445
✔ Should place a first bid and:
- Verify if the highest bid was updated.
- Verify if the endAuction time was updated.
- Verify if the bidder balance was decreased the gas fee plus the auction price.
- Verify if the auction contract balance was added by the bid value. (342ms)
------------------ SECOND BID NOW IN THE LAST 5 MINUTES ------------------
AUCTION END TIME BEFORE FIRST BID: 0
AUCTION END TIME AFTER FIRST BID: 1681363458
Waiting until the last 5 minutes of the auction...
AUCTION END TIME AFTER SECOND BID SHOULD BE INCREASED (In our test, we're using 8 seconds for the last time like example, 10 second for auction duration and 3000 seconds of timeout between the two bids): 1681363466
✔ Should place a second bid in the last 5 minutes and:
- Verify if the end time was increased by more 5 minutes. (3170ms)
_________________________________ END AUCTION UNHAPPY PATHS _________________________________
✔ Should revert with MGDMarketplaceItemIsNotListed error if the end auction function is called and the tokenId was not listed on MGDAuction.
✔ Should revert with AuctionTimeNotStartedYet error if the end auction function is called and the auction have not received any bids yet.
✔ Should revert with AuctionCannotBeEndedYet error if the end auction function is called before the time of duration of the auction be ended. (51ms)
✔ Should revert with MGDMarketFunctionForSetPriceListedNFT error if the purchaseNFT function with one parameter is called to buy an item that is listed to Auction. For that the purchseNFT function with two params must be called and its function is internal and just can be called by the childrens smart contracts like the MGDAuction.
_________________________________ END AUCTION FOR PRIMARY SALE _________________________________
GAS PRICE END AUCTION: BigNumber { value: "1000000008" }
GAS LIMIT END AUCTION: BigNumber { value: "149551" }
TOTAL GAS ESTIMATION END AUCTION (USD): 0.0037387750299102004
AUCTION HIGHEST BID: 20
Primary Market fee: 3
Collector fee: 0.6
Marketplace owner fee: 3.6
Balance to seller: 16.4
MARKETPLACE OWNER BALANCE BEFORE SALE: 9999.894533028086
(OBS: IT'S A LITTLE BIT LESS THAN IT SHOULD BE BECAUSE THE EXECUTION OF ENDAUCTION FUNCTION + PURCHASENFT FUNCTION GAS)
MARKETPLACE OWNER BALANCE AFTER SALE: 10003.494389098083
MARKETPLACE OWNER BALANCE AFTER SALE SHOULD BE: 10003.494533028084
SELLER BALANCE BEFORE SALE: 9999.992448900028
SELLER BALANCE AFTER SALE: 10016.392448900027
AUCTION WINNER BALANCE BEFORE SALE: 9971.998655387957
AUCTION WINNER BALANCE AFTER SALE: 9951.998532948955
✔ Should:
- Simulate a primary sale that transfer an NFT to the buyer;
- Verify if the item changed status for sale;
- Verify if the seller balance increases;
- Verify if the marketplace's owner receives the fee;
- Verify if the isSecondarySale attribute was set to true;
- Verify if the buyer balance was deacresed exactly the gas fee + the token price; (3465ms)
------------------ END AUCTION NFT FOR SECONDARY MARKET ------------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "139579" }
TOTAL GAS ESTIMATION (USD): 0.0034894750279157996
ITEM PRICE: 20
Secondary Market fee: 1
Royalty fee: 1
Balance to seller: 18
MARKETPLACE OWNER BALANCE BEFORE AUCTION: 10006.909221026042
MARKETPLACE OWNER BALANCE AFTER AUCTION: 10007.909087197042
SELLER BALANCE BEFORE AUCTION: 9932.998305462954
SELLER BALANCE AFTER AUCTION: 9950.998305462954
ARTIST BALANCE BEFORE AUCTION: 10031.972088097024
ARTIST BALANCE AFTER AUCTION WITH THE ROYALTY: 10032.972088097024
AUCTION WINNER BALANCE BEFORE AUCTION: 9987.999720052932
AUCTION WINNER BALANCE AFTER AUCTION: 9967.99959761393
✔ Should simulate a secondary sale that transfer an NFT to the buyer, verify if the item changed status for sale, verify if the seller balance increases and also if the marketplace's owner receives the fee and verify if the artist creator have received the royalty. (6581ms)
MGDCompany.sol Smart Contract
_________________________________________
This smart contract is responsible by the functionalities related with MGD Management.
--------------- Tests related with the set validator functionality ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "47791" }
TOTAL GAS ESTIMATION (USD): 0.0011947750095582
✔ Should set a new validator if is the owner and this new validator should can whitelist or blacklist an artist. (78ms)
✔ Should revert with a MGDCompanyUnauthorized error if an address that is not the owner try to set a new validator.
--------------- Tests related with whitelist/blacklist artist ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "47929" }
TOTAL GAS ESTIMATION (USD): 0.0011982250095858
✔ Should whitelist an after blacklist artist. (100ms)
✔ Should revert with a MGDCompanyUnauthorized error if an address that is not the owner try to whitelist or blacklist an artist.
--------------- Tests related with the update primary sale fee functionality ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "28707" }
TOTAL GAS ESTIMATION (USD): 0.0007176750057414
✔ Should update the fee if is the owner. (43ms)
✔ Should revert with a MGDCompanyUnauthorized error if an address that is not the owner try to update the primary sale fee.
--------------- Tests related with the update secondary sale fee functionality ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "28672" }
TOTAL GAS ESTIMATION (USD): 0.0007168000057344
✔ Should update the secondary fee if is the owner. (44ms)
✔ Should revert with a MGDCompanyUnauthorized error if an address that is not the owner try to update the secondary sale fee.
--------------- Tests related with the update collector fee functionality ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "28652" }
TOTAL GAS ESTIMATION (USD): 0.0007163000057304
✔ Should update the collector fee if is the owner. (47ms)
✔ Should revert with a MGDCompanyUnauthorized error if an address that is not the owner try to update the collector fee.
--------------- Tests related with the update max royalty fee functionality ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "28707" }
TOTAL GAS ESTIMATION (USD): 0.0007176750057414
✔ Should update the max_royalty_fee if is the owner. (60ms)
✔ Should revert with a MGDCompanyUnauthorized error if an address that is not the owner try to update the max royalty fee.
✔ Should be possible to mint a NFT with a new maximum royalty set. (51ms)
✔ Should revert with a MGDnftRoyaltyInvalidPercentage error if some artist try to mint with a royalty percent greater than new max royalty that is 25. (56ms)
--------------- Tests related with the update auction duration functionality ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "28657" }
TOTAL GAS ESTIMATION (USD): 0.0007164250057314
✔ Should update the auction duration if is the owner. (47ms)
✔ Should revert with a MGDCompanyUnauthorized error if an address that is not the owner try to update the auction duration time.
--------------- Tests related with the update auction final time functionality ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "28591" }
TOTAL GAS ESTIMATION (USD): 0.0007147750057182001
✔ Should update the auction final time duration if is the owner. (41ms)
✔ Should revert with a MGDCompanyUnauthorized error if an address that is not the owner try to update the auction final time. duration. (39ms)
MGDSetPrice.sol Smart Contract
___________________________________________________
This smart contract is responsible by all functionalities related with the fixed price market.
--------------- Tests related witn the list NFT functionality ---------------
ARTIST BALANCE BEFORE LIST: 10032.97143
ARTIST BALANCE AFTER LIST: 10032.97129
So the gas estimation was more less: 0.35549250284394
✔ Should track newly listed item, transfer NFT from seller to MGD marketplace and emit the NftListed event. (41ms)
✔ Should revert the transaction if an artist tries to list its nft with price less than or equal zero.
✔ Should revert the transaction if an artist is not the owner of the token and try to list on the gold dust marketplace.
--------------- Tests related with the update a listed NFT functionality ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "85309" }
TOTAL GAS ESTIMATION (USD): 0.0021327250170618
✔ Should track if a listed item was correctly updated and emit the NftListedItemUpdated event. We should remember that in this moment the owner of the NFT is the marketplace. (275ms)
✔ Should revert the transaction with InvalidInput error if the marketplace owner tries to update some listed nft with price less than or equal zero.
✔ Should revert the transaction with an GDNFTMarketplace__Unauthorized error if some address that is not the seller try to update the NFT listed.
✔ Should revert the transaction with an MGDMarketplaceItemIsNotListed error if some user tries to update an item that is not on sale.
--------------- Tests related with delist NFT functionality ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "98027" }
TOTAL GAS ESTIMATION (USD): 0.0024506750196054
✔ Should delist a NFT from the marketplace and emit the NFTRemovedFromMarketplace event. (183ms)
✔ Should revert with a GDNFTMarketplace__Unauthorized error if some address that is not the item seller try to delist its NFT from marketplace.
--------------- Purchase NFT on primary market ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "156476" }
TOTAL GAS ESTIMATION (USD): 0.003911900031295199
ITEM PRICE: 20
Primary Market fee: 3
Collector fee: 0.6
Marketplace owner fee: 3.6
Balance to seller: 16.4
MARKETPLACE OWNER BALANCE BEFORE SALE: 10008.01636309146
MARKETPLACE OWNER BALANCE AFTER SALE: 10011.61636309146
SELLER BALANCE BEFORE SALE: 10033.788104594993
SELLER BALANCE AFTER SALE: 10050.188104594992
BUYER BALANCE BEFORE SALE: 9949.998022936952
BUYER BALANCE AFTER SALE: 9929.997869842951
✔ Should:
- Simulate a primary sale that transfer an NFT to the buyer;
- Verify if the item changed status for sale;
- Verify if the seller balance increases;
- Verify if the marketplace's owner receives the fee;
- Verify if the isSecondarySale attribute was set to true;
- Verify if the buyer balance was deacresed exactly the gas fee + the token price; (362ms)
✔ Should revert with MGDMarketplaceItemIsNotListed error if the user tries to buy a NFT that was already sold.
✔ Should revert with MGDMarketplaceIncorrectAmountSent if the user tries to buy an itemId with an amount greater than the item's price.
✔ Should revert with MGDMarketplaceIncorrectAmountSent if the user tries to buy an itemId with an amount less than the item's price.
--------------- Purchase NFT on secondary market ---------------
GAS PRICE: BigNumber { value: "1000000008" }
GAS LIMIT: BigNumber { value: "129189" }
TOTAL GAS ESTIMATION (USD): 0.0032297250258378
ITEM PRICE: 20
Secondary Market fee: 1
Royalty fee: 1
Balance to seller: 18
MARKETPLACE OWNER BALANCE BEFORE SALE: 10018.80250148335
MARKETPLACE OWNER BALANCE AFTER SALE: 10019.80250148335
SELLER BALANCE BEFORE SALE: 9889.997475545948
SELLER BALANCE AFTER SALE: 9907.997475545948
ARTIST BALANCE BEFORE: 10082.986661922982
ARTIST BALANCE AFTER ROYALTY: 10083.986661922982
BUYER BALANCE BEFORE SALE: 9967.99950079093
BUYER BALANCE AFTER SALE: 9947.999374885929
✔ Should simulate a secondary sale that transfer an NFT to the buyer, verify if the item changed status for sale, verify if the seller balance increases and also if the marketplace's owner receives the fee and verify if the artist creator have received the royalty. (382ms)
MGDnft.sol Smart Contract
______________________________________________
This smart contract is responsible by mint new MGD Nfts. Actually this contract is an ERC721.
--------------- Test related with the mint NFT functionality ---------------
ARTIST BALANCE BEFORE MINT: 10083.98666
ARTIST BALANCE AFTER MINT: 10083.98649
So the gas estimation was more less: 0.43046500344372
✔ Should track each minted NFT. This is verifying if:
- The tokenURI was set correctly.
- The tokenId was bound with the artist for future royalties payments.
- The artist is the owner of the token.
- The royalty percentage was set correctly.
- The balanceOf the artists that mint an NFT was increased. (500ms)
✔ Should revert with a MGDnftRoyaltyInvalidPercentage error if some artist try to mint with a royalty percent greater than 20.
✔ Should revert with a MGDnftUnauthorized error if some not whitelisted artist try to mint a NFT.
56 passing (36s)
This PR is creating a new structure for Mint Gold Dust smart contract
The main goal of this branch was add the auction functionality for our platform. But after raise some requirements and had analysed the code, I though better to break the Marketplace smart contract in more contracts that we can bring some good patterns like:
So now our structure have 6 smart contracts:
MGDCompany.sol
This smart contract is responsible by the functionalities related with MGD Management. Like:
MGDnft.sol
This smart contract is responsible by mint new MGD Nfts. Actually this contract is an ERC721.
MGDMarketplace.sol
This is an abstract smart contract that have the purchase function that will be used by its children (Set Price Market and Auction) and also a virtual list function that will be implemented in each way for each children. Here also there are some events and modifiers for general marketplace actions.
This is smart contract is composed by the MGDCompany and MGDnft. So this way is possible to do the required verification and actions based on these contracts.
MGDSetPrice.sol
This is a children of MGDMarketplace and have some specfic functionalities related with a set fixed price market. Some functions like:
MDGAuction.sol
This is also a children of MGDMarketplace. Here we have all the functionalities related with an auction like:
MGDMemoir.sol
A contract responsible by allow new address to create a memoir.
IMPORTANT!!
Deprecate code
The idea is stop to use the old smart contract created:
So now we have to do the required changes to adequate Backend + Frontend and the new Smart Contracts structure.
Tests
Also these PR is adding more 4 important files that contain the tests for the described smart contracts. Until now we have a total of 56 tests passing.
The files are:
Executing the tetst
To verify the tests. Go to this branch and run:
The results would be the followings: