diff --git a/contracts/NFTPool/impl/FilterAdmin.sol b/contracts/NFTPool/impl/FilterAdmin.sol index 7710267..8b70970 100644 --- a/contracts/NFTPool/impl/FilterAdmin.sol +++ b/contracts/NFTPool/impl/FilterAdmin.sol @@ -46,6 +46,9 @@ contract FilterAdmin is InitializableInternalMintableERC20 { for (uint256 i = 0; i < filters.length; i++) { _FILTER_REGISTRY_[filters[i]] = true; } + + //event FilterAdminInit(addres owner, uint256 feeRate) + } function mintFragTo(address to, uint256 rawAmount) external returns (uint256) { @@ -108,10 +111,12 @@ contract FilterAdmin is InitializableInternalMintableERC20 { } //================= Owner ================ - function addFilter(address filter) external onlyOwner { - require(!isRegisteredFilter(filter), "FILTER_ALREADY_EXIST"); - _FILTERS_.push(filter); - _FILTER_REGISTRY_[filter] = true; + function addFilter(address[] memory filters) external onlyOwner { + for(uint256 i = 0; i < filters.length; i++) { + require(!isRegisteredFilter(filters[i]), "FILTER_ALREADY_EXIST"); + _FILTERS_.push(filter[i]); + _FILTER_REGISTRY_[filter[i]] = true; + } } function changeFeeRate(uint256 newFeeRate) external onlyOwner { diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index 1cc53a2..4471a0f 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -41,6 +41,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { for (uint256 i = 0; i < spreadIds.length; i++) { _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; } + + //event FilterInit(address filterAdmin, address nftCollection, string memory name); } // ================= Trading ================ diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index a8a3493..243c11d 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -43,6 +43,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { for (uint256 i = 0; i < spreadIds.length; i++) { _SPREAD_IDS_REGISTRY_[spreadIds[i]] = true; } + + //event FilterInit(address filterAdmin, address nftCollection, string memory name); } // ================= Trading ================ @@ -62,6 +64,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { ); _NFT_IDS_.push(tokenId); _NFT_RESERVE_[tokenId] = 1; + + //event Erc721TokenIn(uint256 index, uint256 tokenId); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; (uint256 rawReceive, ) = queryNFTIn(tokenIds.length); @@ -112,6 +116,9 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; + + //idx,oldTokenId,newTokenId + //event Erc721Out(uint256 index, uint256 tokenId) } function emergencyWithdraw( diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index edb8e27..46fcc1b 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -64,16 +64,20 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { } uint256 received = IFilter(filter).ERC721In(tokenIds, to); require(received >= minMintAmount, "MINT_AMOUNT_NOT_ENOUGH"); + + //event Erc721In(address filter, address to, uint256 received); } function erc721TargetOut( address filter, - uint256[] memory indexes, + uint256[] memory indexes,//tokenId address to, uint256 maxBurnAmount ) external { uint256 paid = IFilter(filter).ERC721TargetOut(indexes, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + + //event Erc721TargetOut(address filter, address to, uint256 paid); } function erc721RandomOut( @@ -84,6 +88,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { ) external { uint256 paid = IFilter(filter).ERC721RandomOut(amount, to); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); + + //event Erc721TargetOut(address filter, address to, uint256 paid); } // ================== ERC1155 In and Out =================== @@ -162,6 +168,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { _MAINTAINER_, filters ); + + //event CreateNFTPool(address newFilterAdmin, address filterV1); } // ================== Create Filter =================== @@ -185,6 +193,8 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { priceRules, spreadIds ); + + //event CreateFilterV1(address newFilterV1, uint256 filterTemplateKey); } @@ -230,6 +240,7 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { function changeController(address newControllerModel) external onlyOwner { _CONTROLLER_ = newControllerModel; + //event ChangeContoller(address newController); } function setFilterTemplate(uint256 idx, address newFilterTemplate) external onlyOwner { diff --git a/test/NFTPool/erc721NftPool.test.ts b/test/NFTPool/erc721NftPool.test.ts new file mode 100644 index 0000000..1183eb0 --- /dev/null +++ b/test/NFTPool/erc721NftPool.test.ts @@ -0,0 +1,204 @@ +/* + + Copyright 2021 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +// import * as assert from 'assert'; + +import { decimalStr, MAX_UINT256 } from '../utils/Converter'; +import { logGas } from '../utils/Log'; +import { NFTPoolContext, getNFTPoolContext } from '../utils/NFTPoolContext'; +import { assert } from 'chai'; +import BigNumber from 'bignumber.js'; +const truffleAssert = require('truffle-assertions'); + + +//createNFTPool + +//erc721In + +//erc721TargetOut + +//erc721RandomOut + +//createFilter + +describe("ERC721-NFTPool", () => { + let snapshotId: string; + let ctx: DVMContext; + + before(async () => { + ctx = await getNFTPoolContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("buy shares", () => { + + it("buy shares from init states", async () => { + + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await logGas(ctx.DVM.methods.buyShares(lp), ctx.sendParam(lp), "buy shares"); + + // vault balances + assert.equal( + await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call(), + decimalStr("10") + ); + assert.equal( + await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call(), + decimalStr("0") + ); + assert.equal( + await ctx.DVM.methods._BASE_RESERVE_().call(), + decimalStr("10") + ) + assert.equal( + await ctx.DVM.methods._QUOTE_RESERVE_().call(), + decimalStr("0") + ) + + // shares number + assert.equal(await ctx.DVM.methods.balanceOf(lp).call(), decimalStr("10")) + }); + + it("buy shares from init states with quote != 0", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.transferQuoteToDVM(lp, decimalStr("100")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)); + assert.equal(await ctx.DVM.methods.balanceOf(lp).call(), decimalStr("10")) + assert.equal(await ctx.DVM.methods.getMidPrice().call(), "102078438912577213500") + }) + + it("buy shares with balanced input", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + await ctx.transferQuoteToDVM(trader, decimalStr("200")) + await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + + var vaultBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call()) + var vaultQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call()) + var increaseRatio = new BigNumber("0.1") + + await ctx.transferBaseToDVM(trader, vaultBaseBalance.multipliedBy(increaseRatio).toFixed(0)) + await ctx.transferQuoteToDVM(trader, vaultQuoteBalance.multipliedBy(increaseRatio).toFixed(0)) + await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + + assert.equal( + await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call(), + "8852116395368015179" + ); + assert.equal( + await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call(), + "220000000000000000000" + ); + + assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "999999999999999990") + }) + + it("buy shares with unbalanced input (less quote)", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + await ctx.transferQuoteToDVM(trader, decimalStr("200")) + await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + + var vaultBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call()) + var vaultQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call()) + var increaseRatio = new BigNumber("0.1") + + await ctx.transferBaseToDVM(trader, vaultBaseBalance.multipliedBy(increaseRatio).toFixed(0)) + await ctx.transferQuoteToDVM(trader, vaultQuoteBalance.multipliedBy(increaseRatio).div(2).toFixed(0)) + await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + + assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "500000000000000000") + }) + + it("buy shares with unbalanced input (less base)", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + await ctx.transferQuoteToDVM(trader, decimalStr("200")) + await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + + var vaultBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(ctx.DVM.options.address).call()) + var vaultQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(ctx.DVM.options.address).call()) + var increaseRatio = new BigNumber("0.1") + + await ctx.transferBaseToDVM(trader, vaultBaseBalance.multipliedBy(increaseRatio).div(2).toFixed(0)) + await ctx.transferQuoteToDVM(trader, vaultQuoteBalance.multipliedBy(increaseRatio).toFixed(0)) + await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + + assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "499999999999999990") + }) + }); + + describe("sell shares", () => { + it("not the last one sell shares", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.transferQuoteToDVM(lp, decimalStr("100")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + await ctx.transferBaseToDVM(trader, decimalStr("1")) + await ctx.transferQuoteToDVM(trader, decimalStr("10")) + await ctx.DVM.methods.buyShares(trader).send(ctx.sendParam(trader)) + + var vaultShares = new BigNumber(await ctx.DVM.methods.balanceOf(lp).call()) + var bob = ctx.SpareAccounts[5] + await ctx.DVM.methods.sellShares(vaultShares.div(2).toFixed(0), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)) + assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("5")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("50")) + + await ctx.DVM.methods.sellShares(vaultShares.div(2).toFixed(0), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)) + assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("10")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("100")) + }) + + it("the last one sell shares", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.transferQuoteToDVM(lp, decimalStr("100")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + var vaultShares = await ctx.DVM.methods.balanceOf(lp).call() + var bob = ctx.SpareAccounts[5] + await ctx.DVM.methods.sellShares(vaultShares, bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)) + assert.equal(await ctx.BASE.methods.balanceOf(bob).call(), decimalStr("10")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bob).call(), decimalStr("100")) + }) + + it("revert cases", async () => { + await ctx.transferBaseToDVM(lp, decimalStr("10")) + await ctx.transferQuoteToDVM(lp, decimalStr("100")) + await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + + var vaultShares = await ctx.DVM.methods.balanceOf(lp).call() + var bob = ctx.SpareAccounts[5] + await truffleAssert.reverts( + ctx.DVM.methods.sellShares(new BigNumber(vaultShares).multipliedBy(2), bob, 0, 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)), + "DLP_NOT_ENOUGH" + ) + await truffleAssert.reverts( + ctx.DVM.methods.sellShares(vaultShares, bob, decimalStr("100"), 0, "0x", MAX_UINT256).send(ctx.sendParam(lp)), + "WITHDRAW_NOT_ENOUGH" + ) + await truffleAssert.reverts( + ctx.DVM.methods.sellShares(vaultShares, bob, 0, decimalStr("10000"), "0x", MAX_UINT256).send(ctx.sendParam(lp)), + "WITHDRAW_NOT_ENOUGH" + ) + await truffleAssert.reverts( + ctx.DVM.methods.sellShares(vaultShares, bob, 0, decimalStr("10000"), "0x", "0").send(ctx.sendParam(lp)), + "TIME_EXPIRED" + ) + }) + }) +}); diff --git a/test/utils/NFTPoolContext.ts b/test/utils/NFTPoolContext.ts index 5949f03..a0f7301 100644 --- a/test/utils/NFTPoolContext.ts +++ b/test/utils/NFTPoolContext.ts @@ -77,7 +77,7 @@ export class NFTPoolContext { this.Controller.options.address, this.Deployer, this.DODONFTApprove.options.address, - "" //TODO:ERC721 => ERC20 + "" //TODO:ERC721 => ERC20 DODOApprove ] ) @@ -104,7 +104,7 @@ export class NFTPoolContext { } } -export async function getNFTPoolContext(weth: string): Promise { +export async function getNFTPoolContext(): Promise { var context = new NFTPoolContext(); await context.init(); return context;