From 013cbf66ca4a01140371f41114e32cd18d8474e9 Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 20 Sep 2021 14:52:23 +0800 Subject: [PATCH] add unit test && fix --- contracts/NFTPool/impl/BaseFilterV1.sol | 49 ++- contracts/NFTPool/impl/FilterERC1155V1.sol | 26 +- contracts/NFTPool/impl/FilterERC721V1.sol | 26 +- .../SmartRoute/proxies/DODONFTPoolProxy.sol | 24 +- contracts/lib/DecimalMath.sol | 2 +- test/NFTPool/erc1155NftPool.test.ts | 225 +++++++++++++ test/NFTPool/erc721NftPool.test.ts | 311 ++++++++++-------- test/utils/Contracts.ts | 4 +- test/utils/NFTPoolContext.ts | 2 +- truffle-test.sh | 11 +- 10 files changed, 491 insertions(+), 189 deletions(-) create mode 100644 test/NFTPool/erc1155NftPool.test.ts diff --git a/contracts/NFTPool/impl/BaseFilterV1.sol b/contracts/NFTPool/impl/BaseFilterV1.sol index 50a5d1b..43c6dea 100644 --- a/contracts/NFTPool/impl/BaseFilterV1.sol +++ b/contracts/NFTPool/impl/BaseFilterV1.sol @@ -117,11 +117,15 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { ) { require(NFTInAmount <= getAvaliableNFTInAmount(), "EXCEDD_IN_AMOUNT"); + (rawReceive, received) = _queryNFTIn(_TOTAL_NFT_AMOUNT_,_TOTAL_NFT_AMOUNT_ + NFTInAmount); + } + + function _queryNFTIn(uint256 start, uint256 end) internal view returns(uint256 rawReceive, uint256 received) { rawReceive = _geometricCalc( _GS_START_IN_, _CR_IN_, - _TOTAL_NFT_AMOUNT_, - _TOTAL_NFT_AMOUNT_ + NFTInAmount + start, + end ); (,, received) = IFilterAdmin(_OWNER_).queryMintFee(rawReceive); } @@ -135,11 +139,15 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { ) { require(NFTOutAmount <= getAvaliableNFTOutAmount(), "EXCEED_OUT_AMOUNT"); + (rawPay, pay) = _queryNFTTargetOut(_TOTAL_NFT_AMOUNT_ - NFTOutAmount, _TOTAL_NFT_AMOUNT_); + } + + function _queryNFTTargetOut(uint256 start, uint256 end) internal view returns(uint256 rawPay, uint256 pay) { rawPay = _geometricCalc( _GS_START_TARGET_OUT_, _CR_TARGET_OUT_, - _TOTAL_NFT_AMOUNT_ - NFTOutAmount, - _TOTAL_NFT_AMOUNT_ + start, + end ); (,, pay) = IFilterAdmin(_OWNER_).queryBurnFee(rawPay); } @@ -153,11 +161,15 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { ) { require(NFTOutAmount <= getAvaliableNFTOutAmount(), "EXCEED_OUT_AMOUNT"); + (rawPay, pay) = _queryNFTRandomOut(_TOTAL_NFT_AMOUNT_ - NFTOutAmount, _TOTAL_NFT_AMOUNT_); + } + + function _queryNFTRandomOut(uint256 start, uint256 end) internal view returns(uint256 rawPay, uint256 pay) { rawPay = _geometricCalc( _GS_START_RANDOM_OUT_, _CR_RANDOM_OUT_, - _TOTAL_NFT_AMOUNT_ - NFTOutAmount, - _TOTAL_NFT_AMOUNT_ + start, + end ); (,, pay) = IFilterAdmin(_OWNER_).queryBurnFee(rawPay); } @@ -165,22 +177,27 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { // ============ Math ============= function _geometricCalc( - uint256 a1, + uint256 a0, uint256 q, uint256 start, uint256 end ) internal view returns (uint256) { if (q == DecimalMath.ONE) { - return end.sub(start).mul(a1); - } - //Sn=a1*(q^n-1)/(q-1) - //Sn-Sm = a1*(q^n-q^m)/(q-1) - + return end.sub(start).mul(a0); + } //q^n uint256 qn = DecimalMath.powFloor(q, end); //q^m uint256 qm = DecimalMath.powFloor(q, start); - return a1.mul(qn.sub(qm)).div(q.sub(DecimalMath.ONE)); + if (q < DecimalMath.ONE) { + //Sn=a0*(1 - q^n)/(1-q) + //Sn-Sm = a0*(q^m - q^n)/(1-q) + return a0.mul(qm.sub(qn)).div(DecimalMath.ONE.sub(q)); + } else { + //Sn=a0*(q^n - 1)/(q - 1) + //Sn-Sm = a0*(q^n - q^m)/(q-1) + return a0.mul(qn.sub(qm)).div(q.sub(DecimalMath.ONE)); + } } function _getRandomNum() public view returns (uint256 randomNum) { @@ -204,7 +221,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { uint256 newCr, bool toggleFlag ) internal { - require(newCr > DecimalMath.ONE, "CR_INVALID"); + require(newCr != 0, "CR_INVALID"); _GS_START_IN_ = newGsStart; _CR_IN_ = newCr; _NFT_IN_TOGGLE_ = true; @@ -225,7 +242,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { uint256 newCr, bool toggleFlag ) internal { - require(newCr > DecimalMath.ONE, "CR_INVALID"); + require(newCr != 0, "CR_INVALID"); _GS_START_RANDOM_OUT_ = newGsStart; _CR_RANDOM_OUT_ = newCr; _NFT_RANDOM_OUT_TOGGLE_ = true; @@ -246,7 +263,7 @@ contract BaseFilterV1 is InitializableOwnable, ReentrancyGuard { uint256 newCr, bool toggleFlag ) internal { - require(newCr > DecimalMath.ONE, "CR_INVALID"); + require(newCr != 0, "CR_INVALID"); _GS_START_TARGET_OUT_ = newGsStart; _CR_TARGET_OUT_ = newCr; _NFT_TARGET_OUT_TOGGLE_ = true; diff --git a/contracts/NFTPool/impl/FilterERC1155V1.sol b/contracts/NFTPool/impl/FilterERC1155V1.sol index bfb99da..c67bd38 100644 --- a/contracts/NFTPool/impl/FilterERC1155V1.sol +++ b/contracts/NFTPool/impl/FilterERC1155V1.sol @@ -60,6 +60,9 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { preventReentrant returns (uint256 received) { + uint256 avaliableNFTInAmount = getAvaliableNFTInAmount(); + uint256 originTotalNftAmount = _TOTAL_NFT_AMOUNT_; + uint256 totalAmount = 0; for (uint256 i = 0; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; @@ -68,7 +71,8 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { totalAmount += inAmount; emit NftIn(tokenId, inAmount); } - (uint256 rawReceive, ) = queryNFTIn(totalAmount); + require(totalAmount <= avaliableNFTInAmount, "EXCEDD_IN_AMOUNT"); + (uint256 rawReceive, ) = _queryNFTIn(originTotalNftAmount, originTotalNftAmount + totalAmount); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); emit NftInOrder(to, received); @@ -79,13 +83,17 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256[] memory amounts, address to ) external preventReentrant returns (uint256 paid) { + uint256 avaliableNFTOutAmount = getAvaliableNFTOutAmount(); + uint256 originTotalNftAmount = _TOTAL_NFT_AMOUNT_; + uint256 totalAmount = 0; for (uint256 i = 0; i < tokenIds.length; i++) { totalAmount += amounts[i]; _transferOutERC1155(to, tokenIds[i], amounts[i]); emit TargetOut(tokenIds[i], amounts[i]); } - (uint256 rawPay, ) = queryNFTTargetOut(totalAmount); + require(totalAmount <= avaliableNFTOutAmount, "EXCEED_OUT_AMOUNT"); + (uint256 rawPay, ) = _queryNFTTargetOut(originTotalNftAmount - totalAmount, originTotalNftAmount); paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); emit TargetOutOrder(to, paid); @@ -102,10 +110,11 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { uint256 randomNum = _getRandomNum() % _TOTAL_NFT_AMOUNT_; uint256 sum; for (uint256 j = 0; j < _NFT_IDS_.length; j++) { - sum += _NFT_RESERVE_[_NFT_IDS_[j]]; + uint256 tokenId = _NFT_IDS_[j]; + sum += _NFT_RESERVE_[tokenId]; if (sum >= randomNum) { - _transferOutERC1155(to, _NFT_IDS_[j], 1); - emit RandomOut( _NFT_IDS_[j], 1); + _transferOutERC1155(to, tokenId, 1); + emit RandomOut(tokenId, 1); break; } } @@ -179,10 +188,13 @@ contract FilterERC1155V1 is IERC1155Receiver, BaseFilterV1 { _TOTAL_NFT_AMOUNT_ -= outAmount; if (currentAmount == 0) { uint256 index = _TOKENID_IDX_[tokenId] - 1; - _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; + if(index != _NFT_IDS_.length - 1) { + uint256 lastTokenId = _NFT_IDS_[_NFT_IDS_.length - 1]; + _NFT_IDS_[index] = lastTokenId; + _TOKENID_IDX_[lastTokenId] = index + 1; + } _NFT_IDS_.pop(); _TOKENID_IDX_[tokenId] = 0; - _TOKENID_IDX_[_NFT_IDS_[index]] = index + 1; } } diff --git a/contracts/NFTPool/impl/FilterERC721V1.sol b/contracts/NFTPool/impl/FilterERC721V1.sol index 860335d..94a2c90 100644 --- a/contracts/NFTPool/impl/FilterERC721V1.sol +++ b/contracts/NFTPool/impl/FilterERC721V1.sol @@ -62,6 +62,8 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { preventReentrant returns (uint256 received) { + require(tokenIds.length <= getAvaliableNFTInAmount(), "EXCEDD_IN_AMOUNT"); + uint256 originTotalNftAmount = _TOTAL_NFT_AMOUNT_; for (uint256 i = 0; i < tokenIds.length; i++) { uint256 tokenId = tokenIds[i]; require(isNFTIDValid(tokenId), "NFT_ID_NOT_SUPPORT"); @@ -77,7 +79,7 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { emit NftIn(tokenId); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; - (uint256 rawReceive, ) = queryNFTIn(tokenIds.length); + (uint256 rawReceive, ) = _queryNFTIn(originTotalNftAmount, originTotalNftAmount + tokenIds.length); received = IFilterAdmin(_OWNER_).mintFragTo(to, rawReceive); emit NftInOrder(to, received); @@ -108,10 +110,10 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { (uint256 rawPay, ) = queryNFTRandomOut(amount); paid = IFilterAdmin(_OWNER_).burnFragFrom(to, rawPay); for (uint256 i = 0; i < amount; i++) { - uint256 index = _getRandomNum() % _TOTAL_NFT_AMOUNT_; - _transferOutERC721(to, _NFT_IDS_[index]); - - emit RandomOut(_NFT_IDS_[index]); + uint256 index = _getRandomNum() % _NFT_IDS_.length; + uint256 tokenId = _NFT_IDS_[index]; + _transferOutERC721(to, tokenId); + emit RandomOut(tokenId); } _TOTAL_NFT_AMOUNT_ = _NFT_IDS_.length; @@ -134,11 +136,14 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { uint256 index = _TOKENID_IDX_[tokenId] - 1; require(index < _NFT_IDS_.length, "INDEX_INVALID"); IERC721(_NFT_COLLECTION_).safeTransferFrom(address(this), to, tokenId); - _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; + if(index != _NFT_IDS_.length - 1) { + uint256 lastTokenId = _NFT_IDS_[_NFT_IDS_.length - 1]; + _NFT_IDS_[index] = lastTokenId; + _TOKENID_IDX_[lastTokenId] = index + 1; + } _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; _TOKENID_IDX_[tokenId] = 0; - _TOKENID_IDX_[_NFT_IDS_[index]] = index + 1; } function emergencyWithdraw( @@ -157,11 +162,14 @@ contract FilterERC721V1 is IERC721Receiver, BaseFilterV1 { uint256 tokenId = tokenIds[i]; if (_NFT_RESERVE_[tokenId] > 0 && nftContract[i] == _NFT_COLLECTION_) { uint256 index = getNFTIndexById(tokenId); - _NFT_IDS_[index] = _NFT_IDS_[_NFT_IDS_.length - 1]; + if(index != _NFT_IDS_.length - 1) { + uint256 lastTokenId = _NFT_IDS_[_NFT_IDS_.length - 1]; + _NFT_IDS_[index] = lastTokenId; + _TOKENID_IDX_[lastTokenId] = index + 1; + } _NFT_IDS_.pop(); _NFT_RESERVE_[tokenId] = 0; _TOKENID_IDX_[tokenId] = 0; - _TOKENID_IDX_[_NFT_IDS_[index]] = index + 1; } IERC721(nftContract[i]).safeTransferFrom(address(this), to, tokenIds[i]); emit EmergencyWithdraw(nftContract[i],tokenIds[i],to); diff --git a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol index 6c6a793..f14c7cc 100644 --- a/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTPoolProxy.sol @@ -86,25 +86,25 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { function erc721TargetOut( address filter, uint256[] memory tokenIds, - address to, + // address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC721TargetOut(tokenIds, to); + uint256 paid = IFilter(filter).ERC721TargetOut(tokenIds, msg.sender); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - emit Erc721TargetOut(filter, to, paid); + emit Erc721TargetOut(filter, msg.sender, paid); } function erc721RandomOut( address filter, uint256 amount, - address to, + // address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC721RandomOut(amount, to); + uint256 paid = IFilter(filter).ERC721RandomOut(amount, msg.sender); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - emit Erc721RandomOut(filter, to, paid); + emit Erc721RandomOut(filter, msg.sender, paid); } // ================== ERC1155 In and Out =================== @@ -130,25 +130,25 @@ contract DODONFTPoolProxy is ReentrancyGuard, InitializableOwnable { address filter, uint256[] memory tokenIds, uint256[] memory amounts, - address to, + // address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC1155TargetOut(tokenIds, amounts, to); + uint256 paid = IFilter(filter).ERC1155TargetOut(tokenIds, amounts, msg.sender); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - emit Erc1155TargetOut(filter, to, paid); + emit Erc1155TargetOut(filter, msg.sender, paid); } function erc1155RandomOut( address filter, uint256 amount, - address to, + // address to, uint256 maxBurnAmount ) external { - uint256 paid = IFilter(filter).ERC1155RandomOut(amount, to); + uint256 paid = IFilter(filter).ERC1155RandomOut(amount, msg.sender); require(paid <= maxBurnAmount, "BURN_AMOUNT_EXCEED"); - emit Erc1155RandomOut(filter, to, paid); + emit Erc1155RandomOut(filter, msg.sender, paid); } diff --git a/contracts/lib/DecimalMath.sol b/contracts/lib/DecimalMath.sol index f740048..5a11899 100644 --- a/contracts/lib/DecimalMath.sol +++ b/contracts/lib/DecimalMath.sol @@ -48,7 +48,7 @@ library DecimalMath { function powFloor(uint256 target, uint256 e) internal pure returns (uint256) { if (e == 0) { - return 1; + return 10 ** 18; } else if (e == 1) { return target; } else { diff --git a/test/NFTPool/erc1155NftPool.test.ts b/test/NFTPool/erc1155NftPool.test.ts new file mode 100644 index 0000000..b0fb6ad --- /dev/null +++ b/test/NFTPool/erc1155NftPool.test.ts @@ -0,0 +1,225 @@ +/* + + 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 * as contracts from '../utils/Contracts'; +import BigNumber from 'bignumber.js'; +const truffleAssert = require('truffle-assertions'); + +let maker: string; +let user: string; + +async function init(ctx: NFTPoolContext): Promise { + maker = ctx.SpareAccounts[0]; + user = ctx.SpareAccounts[1]; +} + +async function createNFTPool(ctx: NFTPoolContext) { + var tx = await logGas(ctx.DODONFTPoolProxy.methods.createNewNFTPoolV1( + maker, + ctx.DodoNft1155.options.address, + 2, + ['Filter01', 'FRAG', 'FRAG'], + [decimalStr("10000000"), decimalStr("0.005")], + [true, true, true], + [0, 4, 12, 4], + [decimalStr("1"), decimalStr("0.9"), decimalStr("1"), decimalStr("0.9"), decimalStr("2"), decimalStr("0.9")], + [7] + ), ctx.sendParam(maker), "createNewNFTPoolV1"); + + var newFilterAdmin = tx.events['CreateNFTPool'].returnValues['newFilterAdmin'] + var filter = tx.events['CreateNFTPool'].returnValues['filter'] + + return [newFilterAdmin, filter]; +} + +async function mintNFT(ctx: NFTPoolContext, amount) { + var tx = await ctx.DodoNft1155.methods.mint( + "http://projectowen.oss-cn-beijing.aliyuncs.com/2021-09-19-035145.png", + amount + ).send(ctx.sendParam(user)); + + var tokenId = tx.events['DODONFTMint'].returnValues['tokenId'] + return tokenId +} + +async function erc1155In(ctx: NFTPoolContext) { + var [filterAdmin, filter] = await createNFTPool(ctx) + var tokenIds = [] + var amounts = [] + for (var i = 0; i < 5; i++) { + var curTokenId = await mintNFT(ctx, 2); + tokenIds.push(curTokenId); + amounts.push(2); + } + + await ctx.DodoNft1155.methods.setApprovalForAll( + ctx.DODONFTApprove.options.address, + true + ).send(ctx.sendParam(user)) + + await ctx.DODONFTPoolProxy.methods.erc1155In( + filter, + ctx.DodoNft1155.options.address, + tokenIds, + amounts, + user, + 1 + ).send(ctx.sendParam(user)); + + return [filterAdmin, filter] +} + +describe("ERC1155-NFTPool", () => { + let snapshotId: string; + let ctx: NFTPoolContext; + + before(async () => { + ctx = await getNFTPoolContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("ERC1155-NFTPool", () => { + it('erc1155In', async () => { + var [filterAdmin, filter] = await createNFTPool(ctx) + var tokenIds = [] + var amounts = [] + for (var i = 0; i < 4; i++) { + var curTokenId = await mintNFT(ctx, 2); + tokenIds.push(curTokenId); + amounts.push(2); + } + + var beforeBalanceTokenId0 = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(beforeBalanceTokenId0, 0) + + await logGas(ctx.DodoNft1155.methods.setApprovalForAll( + ctx.DODONFTApprove.options.address, + true + ), ctx.sendParam(user), "ApproveNFT"); + + var filterAdminInstance = contracts.getContractWithAddress(contracts.FILTER_ADMIN, filterAdmin); + + var beforeBalance = await filterAdminInstance.methods.balanceOf(user).call(); + console.log("beforeBalance:", beforeBalance); + + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc1155In( + filter, + ctx.DodoNft1155.options.address, + tokenIds, + amounts, + user, + 1 + ), ctx.sendParam(user), "erc1155In"); + + + assert.equal( + tx.events['Erc1155In'].returnValues['received'], + '5666851260500000000' + ) + + var afterBalanceTokenId0 = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(afterBalanceTokenId0, 2) + + }) + + it('ERC1155TargetOut', async () => { + var [, filter] = await erc1155In(ctx); + + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC1155_V1, filter); + + var beforeAmount = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(beforeAmount, 2) + + //maker targetout + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc1155TargetOut( + filter, + [0, 1, 3], + [2, 1, 1], + MAX_UINT256, + ), ctx.sendParam(maker), "Erc1155TargetOut"); + + var paid = tx.events['Erc1155TargetOut'].returnValues['paid'] + assert.equal(paid, "3673527453990000000"); + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 2); + assert.equal(totalNftAmount, 6); + + var afterAmount = await ctx.DodoNft1155.methods.balanceOf(maker, 0).call(); + assert.equal(afterAmount, 2) + }) + + + it('ERC721RandomOut', async () => { + var [, filter] = await erc1155In(ctx); + + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC1155_V1, filter); + + //maker randomOut + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc1155RandomOut( + filter, + 3, + MAX_UINT256, + ), ctx.sendParam(maker), "Erc1155RandomOut"); + + var paid = tx.events['Erc1155RandomOut'].returnValues['paid'] + assert.equal(paid, "1302665521995000000"); + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 3); + assert.equal(totalNftAmount, 7); + }) + + it('emergencyWithdraw', async () => { + var [filterAdmin, filter] = await erc1155In(ctx); + await ctx.Controller.methods.setEmergencyWithdraw(filter, true).send(ctx.sendParam(ctx.Deployer)); + + var beforeAmount = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(beforeAmount, 2) + + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC1155_V1, filter); + + await logGas(filterInstance.methods.emergencyWithdraw( + [ctx.DodoNft1155.options.address, ctx.DodoNft1155.options.address, ctx.DodoNft1155.options.address], + [0, 1, 4], + [1, 2, 2], + maker + ), ctx.sendParam(maker), "EmergencyWithdraw") + + + var afterAmount = await ctx.DodoNft1155.methods.balanceOf(maker, 0).call(); + assert.equal(afterAmount, 1) + afterAmount = await ctx.DodoNft1155.methods.balanceOf(filter, 0).call(); + assert.equal(afterAmount, 1) + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 1); + assert.equal(totalNftAmount, 5); + }) + }); +}); + diff --git a/test/NFTPool/erc721NftPool.test.ts b/test/NFTPool/erc721NftPool.test.ts index 1183eb0..74244e7 100644 --- a/test/NFTPool/erc721NftPool.test.ts +++ b/test/NFTPool/erc721NftPool.test.ts @@ -11,23 +11,74 @@ import { decimalStr, MAX_UINT256 } from '../utils/Converter'; import { logGas } from '../utils/Log'; import { NFTPoolContext, getNFTPoolContext } from '../utils/NFTPoolContext'; import { assert } from 'chai'; +import * as contracts from '../utils/Contracts'; import BigNumber from 'bignumber.js'; +import { StringLiteralLike } from 'typescript'; const truffleAssert = require('truffle-assertions'); +let maker: string; +let user: string; -//createNFTPool +async function init(ctx: NFTPoolContext): Promise { + maker = ctx.SpareAccounts[0]; + user = ctx.SpareAccounts[1]; +} -//erc721In +async function createNFTPool(ctx: NFTPoolContext) { + var tx = await logGas(ctx.DODONFTPoolProxy.methods.createNewNFTPoolV1( + maker, + ctx.DodoNft.options.address, + 1, + ['Filter01', 'FRAG', 'FRAG'], + [decimalStr("10000000"), decimalStr("0.005")], + [true, true, true], + [0, 4, 5, 1], + [decimalStr("1"), decimalStr("0.9"), decimalStr("1"), decimalStr("0.9"), decimalStr("2"), decimalStr("0.9")], + [7] + ), ctx.sendParam(maker), "createNewNFTPoolV1"); -//erc721TargetOut + var newFilterAdmin = tx.events['CreateNFTPool'].returnValues['newFilterAdmin'] + var filter = tx.events['CreateNFTPool'].returnValues['filter'] -//erc721RandomOut + return [newFilterAdmin, filter]; +} -//createFilter +async function mintNFT(ctx: NFTPoolContext) { + var tx = await ctx.DodoNft.methods.mint( + "http://projectowen.oss-cn-beijing.aliyuncs.com/2021-09-19-035145.png" + ).send(ctx.sendParam(user)); + + var tokenId = tx.events['DODONFTMint'].returnValues['tokenId'] + return tokenId +} + +async function erc721In(ctx: NFTPoolContext) { + var [filterAdmin, filter] = await createNFTPool(ctx) + var tokenIds = [] + for (var i = 0; i < 5; i++) { + var curTokenId = await mintNFT(ctx); + tokenIds.push(curTokenId); + } + + await ctx.DodoNft.methods.setApprovalForAll( + ctx.DODONFTApprove.options.address, + true + ).send(ctx.sendParam(user)) + + await ctx.DODONFTPoolProxy.methods.erc721In( + filter, + ctx.DodoNft.options.address, + tokenIds, + user, + 1 + ).send(ctx.sendParam(user)); + + return [filterAdmin, filter] +} describe("ERC721-NFTPool", () => { let snapshotId: string; - let ctx: DVMContext; + let ctx: NFTPoolContext; before(async () => { ctx = await getNFTPoolContext(); @@ -42,163 +93,147 @@ describe("ERC721-NFTPool", () => { await ctx.EVM.reset(snapshotId); }); - describe("buy shares", () => { + describe("ERC721-NFTPool", () => { - it("buy shares from init states", async () => { + it("createNewNFTPoolV1", async () => { + var tx = await logGas(ctx.DODONFTPoolProxy.methods.createNewNFTPoolV1( + maker, + ctx.DodoNft.options.address, + 1, + ['Filter01', 'FRAG', 'FRAG'], + [decimalStr("10000000"), decimalStr("0.005")], + [true, true, true], + [0, 3, 2, 1], + [decimalStr("1"), decimalStr("1.1"), decimalStr("1"), decimalStr("1.1"), decimalStr("2"), decimalStr("1.1")], + [5] + ), ctx.sendParam(maker), "createNewNFTPoolV1"); - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await logGas(ctx.DVM.methods.buyShares(lp), ctx.sendParam(lp), "buy shares"); + var newFilterAdmin = tx.events['CreateNFTPool'].returnValues['newFilterAdmin'] + var filter = tx.events['CreateNFTPool'].returnValues['filter'] + + console.log("newFilterAdmin:", newFilterAdmin) + console.log("filterV1:", filter) - // 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") + tx.events['CreateNFTPool'].returnValues['filterAdminOwner'], + maker ) - 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('erc721In', async () => { + var [filterAdmin, filter] = await createNFTPool(ctx) + var tokenIds = [] + for (var i = 0; i < 4; i++) { + var curTokenId = await mintNFT(ctx); + tokenIds.push(curTokenId); + } - it("buy shares with balanced input", async () => { - await ctx.transferBaseToDVM(lp, decimalStr("10")) - await ctx.DVM.methods.buyShares(lp).send(ctx.sendParam(lp)) + await logGas(ctx.DodoNft.methods.setApprovalForAll( + ctx.DODONFTApprove.options.address, + true + ), ctx.sendParam(user), "ApproveNFT"); - await ctx.transferQuoteToDVM(trader, decimalStr("200")) - await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + var filterAdminInstance = contracts.getContractWithAddress(contracts.FILTER_ADMIN, filterAdmin); - 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") + var beforeBalance = await filterAdminInstance.methods.balanceOf(user).call(); + console.log("beforeBalance:", beforeBalance); - 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)) + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc721In( + filter, + ctx.DodoNft.options.address, + tokenIds, + user, + 1 + ), ctx.sendParam(user), "erc721In"); + + var afterBalance = await filterAdminInstance.methods.balanceOf(user).call(); + console.log("afterBalance:", afterBalance); 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") + tx.events['Erc721In'].returnValues['received'], + '3421805000000000000' + ) }) - 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)) + it('ERC721TargetOut', async () => { + var [, filter] = await erc721In(ctx); - await ctx.transferQuoteToDVM(trader, decimalStr("200")) - await ctx.DVM.methods.sellQuote(trader).send(ctx.sendParam(trader)) + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC721_V1, filter); - 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") + var beforeOwner = await ctx.DodoNft.methods.ownerOf(0).call(); + assert.equal(beforeOwner, filter) - 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)) + //maker targetout + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc721TargetOut( + filter, + [0, 1, 3], + MAX_UINT256, + ), ctx.sendParam(maker), "Erc721TargetOut"); - assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "500000000000000000") + var paid = tx.events['Erc721TargetOut'].returnValues['paid'] + + assert.equal(paid, "4412151000000000000"); + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + var tokenId2 = await filterInstance.methods.getNFTIndexById(2).call(); + + assert.equal(maxNftOutAmount, 1); + assert.equal(totalNftAmount, 2); + assert.equal(tokenId2, 1); + + var afterOwner = await ctx.DodoNft.methods.ownerOf(0).call(); + assert.equal(afterOwner, maker) }) - 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)) + it('ERC721RandomOut', async () => { + var [, filter] = await erc721In(ctx); - 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") + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC721_V1, filter); - 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)) + //maker randomOut + var tx = await logGas(ctx.DODONFTPoolProxy.methods.erc721RandomOut( + filter, + 3, + MAX_UINT256, + ), ctx.sendParam(maker), "Erc721RandomOut"); - assert.equal(await ctx.DVM.methods.balanceOf(trader).call(), "499999999999999990") + var paid = tx.events['Erc721RandomOut'].returnValues['paid'] + assert.equal(paid, "2206075500000000000"); + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 1); + assert.equal(totalNftAmount, 2); + }) + + it('emergencyWithdraw', async () => { + var [filterAdmin, filter] = await erc721In(ctx); + await ctx.Controller.methods.setEmergencyWithdraw(filter, true).send(ctx.sendParam(ctx.Deployer)); + + var beforeOwner = await ctx.DodoNft.methods.ownerOf(0).call(); + assert.equal(beforeOwner, filter) + + var filterInstance = contracts.getContractWithAddress(contracts.FILTER_ERC721_V1, filter); + + await logGas(filterInstance.methods.emergencyWithdraw( + [ctx.DodoNft.options.address, ctx.DodoNft.options.address, ctx.DodoNft.options.address], + [0, 1, 4], + maker + ), ctx.sendParam(maker), "EmergencyWithdraw") + + + var afterOwner = await ctx.DodoNft.methods.ownerOf(0).call(); + assert.equal(afterOwner, maker) + + var maxNftOutAmount = await filterInstance.methods.getAvaliableNFTOutAmount().call(); + var totalNftAmount = await filterInstance.methods._TOTAL_NFT_AMOUNT_().call(); + + assert.equal(maxNftOutAmount, 1); + assert.equal(totalNftAmount, 2); }) }); - - 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/Contracts.ts b/test/utils/Contracts.ts index 939674b..0bbe568 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -77,8 +77,8 @@ export const DROPS_PROXY = "DODODropsProxy" export const DODO_NFT = "DODONFT" export const DODO_NFT_1155 = "DODONFT1155" -export const FILTER_ERC721_V1 = "FilterERC721" -export const FILTER_ERC1155_V1 = "FilterERC1155" +export const FILTER_ERC721_V1 = "FilterERC721V1" +export const FILTER_ERC1155_V1 = "FilterERC1155V1" export const FILTER_ADMIN = "FilterAdmin" export const CONTROLLER = "Controller" export const DODO_NFT_APPROVE = "DODONFTApprove" diff --git a/test/utils/NFTPoolContext.ts b/test/utils/NFTPoolContext.ts index a0f7301..410ada1 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 DODOApprove + "0x0000000000000000000000000000000000000000" //TODO:ERC721 => ERC20 DODOApprove ] ) diff --git a/truffle-test.sh b/truffle-test.sh index 9770c14..4df5dc4 100644 --- a/truffle-test.sh +++ b/truffle-test.sh @@ -1,5 +1,5 @@ #!/bin/bash -# truffle compile --all +truffle compile --all if [ "$1"x = "proxy-dpp"x ] then @@ -71,7 +71,12 @@ then truffle test ./test/DODODrops/dropsV2.test.ts fi -if [ "$1"x = "NFTPool"x ] +if [ "$1"x = "erc721NFTPool"x ] then - truffle test ./test/NFTPool/nftPool.test.ts + truffle test ./test/NFTPool/erc721NftPool.test.ts +fi + +if [ "$1"x = "erc1155NFTPool"x ] +then + truffle test ./test/NFTPool/erc1155NftPool.test.ts fi \ No newline at end of file