From 0bace9f613059c2a031bf8497a2f059fda5d6fea Mon Sep 17 00:00:00 2001 From: owen05 Date: Mon, 12 Apr 2021 00:53:59 +0800 Subject: [PATCH] nft test ing --- config/kovan-config.js | 4 +- contracts/DODOFee/FeeDistributer.sol | 156 ++++++++----- contracts/SmartRoute/proxies/DODONFTProxy.sol | 17 +- contracts/intf/IFeeDistributor.sol | 2 + test/DODONFT/nftMainFlow.test.ts | 205 ++++++++++++++---- test/utils/NFTContext.ts | 99 ++++++++- truffle-test.sh | 2 +- 7 files changed, 361 insertions(+), 124 deletions(-) diff --git a/config/kovan-config.js b/config/kovan-config.js index 06dc4f1..da5e0c6 100644 --- a/config/kovan-config.js +++ b/config/kovan-config.js @@ -63,7 +63,7 @@ module.exports = { //================== NFT ==================== ConstFeeRateModel: "0xBDAcEcF886a4F0C509260d9678D5673C3E8fa4b7", - FeeDistributor: "0x1eD92Fe9c3AE1cC427004eC27AAd96df4928B3ec", + FeeDistributor: "", Fragment: "0x64193839f8b6e85A257411a945085783cbA9976B", NFTCollateralVault: "0xFDf7604649dfBb733e784afAEdC19892706cc683", DODONFTRouteHelper: "0xAE683548702be6d651e179e5F9313272bb18596A", @@ -73,6 +73,6 @@ module.exports = { NFTTokenFactory: "0x38c109aF4f3454172BA4eecf5676aA213b589e75", DODONFTRegistry: "0xF405372b7808363DCfbb5Eb81204889B7a69Aa3e", - DODONFTProxy: "0x622332C5e5B3E8B67Aee300184Ce024E428A89f8", + DODONFTProxy: "", } } \ No newline at end of file diff --git a/contracts/DODOFee/FeeDistributer.sol b/contracts/DODOFee/FeeDistributer.sol index 3e3cacc..ac6558a 100644 --- a/contracts/DODOFee/FeeDistributer.sol +++ b/contracts/DODOFee/FeeDistributer.sol @@ -29,15 +29,15 @@ contract FeeDistributor { uint256 public _STAKE_RESERVE_; uint256 public _BASE_REWARD_RATIO_; - mapping(address => uint256) internal _USER_BASE_REWARDS_; - mapping(address => uint256) internal _USER_BASE_PER_SHARE_; - - uint256 public _QUOTE_REWARD_RATIO_; - mapping(address => uint256) internal _USER_QUOTE_REWARDS_; - mapping(address => uint256) internal _USER_QUOTE_PER_SHARE_; + mapping(address => uint256) public _USER_BASE_REWARDS_; + mapping(address => uint256) public _USER_BASE_PER_SHARE_; + + uint256 public _QUOTE_REWARD_RATIO_; + mapping(address => uint256) public _USER_QUOTE_REWARDS_; + mapping(address => uint256) public _USER_QUOTE_PER_SHARE_; + + mapping(address => uint256) public _SHARES_; - mapping(address => uint256) internal _SHARES_; - bool internal _FEE_INITIALIZED_; // ============ Event ============ @@ -46,13 +46,13 @@ contract FeeDistributor { event Claim(address sender, uint256 baseAmount, uint256 quoteAmount); function init( - address baseToken, - address quoteToken, - address stakeToken + address baseToken, + address quoteToken, + address stakeToken ) external { require(!_FEE_INITIALIZED_, "ALREADY_INITIALIZED"); _FEE_INITIALIZED_ = true; - + _BASE_TOKEN_ = baseToken; _QUOTE_TOKEN_ = quoteToken; _STAKE_TOKEN_ = stakeToken; @@ -60,86 +60,126 @@ contract FeeDistributor { } function stake(address to) external { - _updateGlobalState(); - _updateUserReward(to); - uint256 stakeVault = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); - uint256 stakeInput = stakeVault.sub(_STAKE_RESERVE_); - _addShares(stakeInput, to); - emit Stake(to, stakeInput); + _updateGlobalState(); + _updateUserReward(to); + uint256 stakeVault = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); + uint256 stakeInput = stakeVault.sub(_STAKE_RESERVE_); + _addShares(stakeInput, to); + emit Stake(to, stakeInput); } function claim(address to) external { - _updateGlobalState(); - _updateUserReward(msg.sender); - _claim(msg.sender, to); + _updateGlobalState(); + _updateUserReward(msg.sender); + _claim(msg.sender, to); } - function unstake(uint256 amount, address to, bool withClaim) external { - require(_SHARES_[msg.sender]>=amount, "STAKE BALANCE ONT ENOUGH"); - _updateGlobalState(); - _updateUserReward(msg.sender); + function unstake( + uint256 amount, + address to, + bool withClaim + ) external { + require(_SHARES_[msg.sender] >= amount, "STAKE BALANCE ONT ENOUGH"); + _updateGlobalState(); + _updateUserReward(msg.sender); - if (withClaim) { - _claim(msg.sender, to); - } + if (withClaim) { + _claim(msg.sender, to); + } - _removeShares(amount, msg.sender); - StakeVault(_STAKE_VAULT_).transferOut(_STAKE_TOKEN_, amount, to); - emit UnStake(msg.sender, amount); + StakeVault(_STAKE_VAULT_).transferOut(_STAKE_TOKEN_, amount, to); + _removeShares(amount, msg.sender); + + emit UnStake(msg.sender, amount); + } + + // ============ View ================ + function getPendingReward(address user) + external + view + returns (uint256 baseReward, uint256 quoteReward) + { + uint256 baseInput = IERC20(_BASE_TOKEN_).balanceOf(address(this)).sub(_BASE_RESERVE_); + uint256 quoteInput = IERC20(_QUOTE_TOKEN_).balanceOf(address(this)).sub(_QUOTE_RESERVE_); + uint256 baseRwardRatio = _BASE_REWARD_RATIO_; + uint256 quoteRewardRatio = _QUOTE_REWARD_RATIO_; + if (_STAKE_RESERVE_ != 0) { + baseRwardRatio = _BASE_REWARD_RATIO_.add( + DecimalMath.divFloor(baseInput, _STAKE_RESERVE_) + ); + quoteRewardRatio = _QUOTE_REWARD_RATIO_.add( + DecimalMath.divFloor(quoteInput, _STAKE_RESERVE_) + ); + } + baseReward = DecimalMath + .mulFloor(_SHARES_[user], baseRwardRatio.sub(_USER_BASE_PER_SHARE_[user])) + .add(_USER_BASE_REWARDS_[user]); + quoteReward = DecimalMath + .mulFloor(_SHARES_[user], quoteRewardRatio.sub(_USER_QUOTE_PER_SHARE_[user])) + .add(_USER_QUOTE_REWARDS_[user]); } // ============ Internal ============ function _claim(address sender, address to) internal { - uint256 allBase = _USER_BASE_REWARDS_[sender]; - uint256 allQuote = _USER_QUOTE_REWARDS_[sender]; - IERC20(_BASE_TOKEN_).safeTransfer(to, allBase); - IERC20(_QUOTE_TOKEN_).safeTransfer(to, allQuote); - _USER_BASE_REWARDS_[sender] = 0; - _USER_BASE_REWARDS_[sender] = 0; - emit Claim(sender, allBase, allQuote); + uint256 allBase = _USER_BASE_REWARDS_[sender]; + uint256 allQuote = _USER_QUOTE_REWARDS_[sender]; + IERC20(_BASE_TOKEN_).safeTransfer(to, allBase); + IERC20(_QUOTE_TOKEN_).safeTransfer(to, allQuote); + + _BASE_RESERVE_ = _BASE_RESERVE_.sub(allBase); + _QUOTE_RESERVE_ = _QUOTE_RESERVE_.sub(allQuote); + _USER_BASE_REWARDS_[sender] = 0; + _USER_QUOTE_REWARDS_[sender] = 0; + + emit Claim(sender, allBase, allQuote); } function _updateGlobalState() internal { - uint256 baseInput = IERC20(_BASE_TOKEN_).balanceOf(address(this)).sub(_BASE_RESERVE_); - uint256 quoteInput = IERC20(_QUOTE_TOKEN_).balanceOf(address(this)).sub(_QUOTE_RESERVE_); - _BASE_REWARD_RATIO_ = _BASE_REWARD_RATIO_.add(DecimalMath.divFloor(baseInput, _STAKE_RESERVE_)); - _QUOTE_REWARD_RATIO_ = _QUOTE_REWARD_RATIO_.add(DecimalMath.divFloor(quoteInput, _STAKE_RESERVE_)); - _BASE_RESERVE_ = _BASE_RESERVE_.add(baseInput); - _QUOTE_RESERVE_ = _QUOTE_RESERVE_.add(quoteInput); + uint256 baseInput = IERC20(_BASE_TOKEN_).balanceOf(address(this)).sub(_BASE_RESERVE_); + uint256 quoteInput = IERC20(_QUOTE_TOKEN_).balanceOf(address(this)).sub(_QUOTE_RESERVE_); + + if (_STAKE_RESERVE_ != 0) { + _BASE_REWARD_RATIO_ = _BASE_REWARD_RATIO_.add( + DecimalMath.divFloor(baseInput, _STAKE_RESERVE_) + ); + _QUOTE_REWARD_RATIO_ = _QUOTE_REWARD_RATIO_.add( + DecimalMath.divFloor(quoteInput, _STAKE_RESERVE_) + ); + } + + _BASE_RESERVE_ = _BASE_RESERVE_.add(baseInput); + _QUOTE_RESERVE_ = _QUOTE_RESERVE_.add(quoteInput); } function _updateUserReward(address user) internal { - _USER_BASE_REWARDS_[user] = DecimalMath.mulFloor( - _SHARES_[user], - _BASE_REWARD_RATIO_.sub(_USER_BASE_PER_SHARE_[user]) - ).add(_USER_BASE_REWARDS_[user]); + _USER_BASE_REWARDS_[user] = DecimalMath + .mulFloor(_SHARES_[user], _BASE_REWARD_RATIO_.sub(_USER_BASE_PER_SHARE_[user])) + .add(_USER_BASE_REWARDS_[user]); _USER_BASE_PER_SHARE_[user] = _BASE_REWARD_RATIO_; - _USER_QUOTE_REWARDS_[user] = DecimalMath.mulFloor( - _SHARES_[user], - _QUOTE_REWARD_RATIO_.sub(_USER_QUOTE_PER_SHARE_[user]) - ).add(_USER_QUOTE_REWARDS_[user]); + _USER_QUOTE_REWARDS_[user] = DecimalMath + .mulFloor(_SHARES_[user], _QUOTE_REWARD_RATIO_.sub(_USER_QUOTE_PER_SHARE_[user])) + .add(_USER_QUOTE_REWARDS_[user]); _USER_QUOTE_PER_SHARE_[user] = _QUOTE_REWARD_RATIO_; } function _addShares(uint256 amount, address to) internal { - _SHARES_[to] = _SHARES_[to].add(amount); - _STAKE_RESERVE_ = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); + _SHARES_[to] = _SHARES_[to].add(amount); + _STAKE_RESERVE_ = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); } function _removeShares(uint256 amount, address from) internal { - _SHARES_[from] = _SHARES_[from].sub(amount); - _STAKE_RESERVE_ = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); + _SHARES_[from] = _SHARES_[from].sub(amount); + _STAKE_RESERVE_ = IERC20(_STAKE_TOKEN_).balanceOf(_STAKE_VAULT_); } } - contract StakeVault is Ownable { using SafeERC20 for IERC20; - + function transferOut( address token, uint256 amount, diff --git a/contracts/SmartRoute/proxies/DODONFTProxy.sol b/contracts/SmartRoute/proxies/DODONFTProxy.sol index c7afaa4..5775ed0 100644 --- a/contracts/SmartRoute/proxies/DODONFTProxy.sol +++ b/contracts/SmartRoute/proxies/DODONFTProxy.sol @@ -95,11 +95,10 @@ contract DODONFTProxy is ReentrancyGuard, InitializableOwnable { emit CreateNFTCollateralVault(msg.sender, newVault, name, baseURI); } - //Stake 碎片 function createFragment( address quoteToken, address vaultPreOwner, - address stakeToken, + address stakeToken, //address(0) using frag token uint256[] calldata dvmParams, //0 - lpFeeRate, 1 - mtFeeRate 2 - I, 3 - K uint256[] calldata fragParams, //0 - totalSupply, 1 - ownerRatio, 2 - buyoutTimestamp bool isOpenTwap @@ -108,11 +107,11 @@ contract DODONFTProxy is ReentrancyGuard, InitializableOwnable { address _quoteToken = quoteToken == _ETH_ADDRESS_ ? _WETH_ : quoteToken; if(stakeToken == address(0)) { - newFeeDistributor = address(0); - } else { - newFeeDistributor = ICloneFactory(_CLONE_FACTORY_).clone(_FEE_TEMPLATE_); - IFeeDistributor(newFeeDistributor).init(newFragment, _quoteToken, stakeToken); - } + stakeToken = newFragment; + } + + newFeeDistributor = ICloneFactory(_CLONE_FACTORY_).clone(_FEE_TEMPLATE_); + IFeeDistributor(newFeeDistributor).init(newFragment, _quoteToken, stakeToken); { uint256[] memory _dvmParams = dvmParams; @@ -161,7 +160,9 @@ contract DODONFTProxy is ReentrancyGuard, InitializableOwnable { uint256 stakeAmount, uint8 flag // 0 - ERC20, 1 - ETH ) external payable preventReentrant { - _deposit(msg.sender, feeDistributor, IFeeDistributor(feeDistributor)._STAKE_TOKEN_(), stakeAmount, flag == 1); + address stakeVault = IFeeDistributor(feeDistributor)._STAKE_VAULT_(); + require(stakeVault != address(0), "DODONFTProxy:STAKE_VAULT_EMPTY"); + _deposit(msg.sender, stakeVault, IFeeDistributor(feeDistributor)._STAKE_TOKEN_(), stakeAmount, flag == 1); IFeeDistributor(feeDistributor).stake(msg.sender); emit Stake(msg.sender, feeDistributor, stakeAmount); } diff --git a/contracts/intf/IFeeDistributor.sol b/contracts/intf/IFeeDistributor.sol index 891683f..9fc7829 100644 --- a/contracts/intf/IFeeDistributor.sol +++ b/contracts/intf/IFeeDistributor.sol @@ -18,4 +18,6 @@ interface IFeeDistributor { function _STAKE_TOKEN_() external view returns(address); + function _STAKE_VAULT_() external view returns(address); + } diff --git a/test/DODONFT/nftMainFlow.test.ts b/test/DODONFT/nftMainFlow.test.ts index ce1061b..7d8dd32 100644 --- a/test/DODONFT/nftMainFlow.test.ts +++ b/test/DODONFT/nftMainFlow.test.ts @@ -4,12 +4,11 @@ SPDX-License-Identifier: Apache-2.0 */ -import { decimalStr, mweiStr } from '../utils/Converter'; +import { decimalStr, mweiStr, fromWei } from '../utils/Converter'; import { logGas } from '../utils/Log'; import { NFTContext, getDODONftContext } from '../utils/NFTContext'; import { assert } from 'chai'; import * as contracts from '../utils/Contracts'; -import { Contract } from 'web3-eth-contract'; let author: string; let user1: string; @@ -23,26 +22,73 @@ async function init(ctx: NFTContext): Promise { await ctx.mintTestToken(user1, ctx.USDT, mweiStr("10000")); await ctx.mintTestToken(user2, ctx.USDT, mweiStr("10000")); - await ctx.approveProxy(user1); - await ctx.approveProxy(user2); + await ctx.approveProxy(ctx.USDT, user1); + await ctx.approveProxy(ctx.USDT, user2); } -async function createNFTVault(ctx: NFTContext) { - var tx = await ctx.NFTProxy.methods.createNFTCollateralVault( - "DODONFT", - "https://app.dodoex.io" - ).send(ctx.sendParam(author)); +async function getFeeGlobalState(ctx: NFTContext, feeAddress: string, baseToken, quoteToken, stakeToken) { + let feeInstance = contracts.getContractWithAddress(contracts.NFT_FEE, feeAddress); + let baseReserve = await feeInstance.methods._BASE_RESERVE_().call(); + let quoteReserve = await feeInstance.methods._QUOTE_RESERVE_().call(); + let baseBalance = await baseToken.methods.balanceOf(feeAddress).call(); + let quoteBalance = await quoteToken.methods.balanceOf(feeAddress).call(); + let stakeVault = await feeInstance.methods._STAKE_VAULT_().call(); + let stakeBalance = await stakeToken.methods.balanceOf(stakeVault).call(); + let stakeReserve = await feeInstance.methods._STAKE_RESERVE_().call(); + let baseRatio = await feeInstance.methods._BASE_REWARD_RATIO_().call(); + let quoteRatio = await feeInstance.methods._QUOTE_REWARD_RATIO_().call(); - return tx.events['CreateNFTCollateralVault']['returnValues']['vault']; + console.log("fee baseBalance:" + fromWei(baseBalance, 'ether') + " quoteBalance:" + fromWei(quoteBalance, 'mwei') + " vault stakeBalance:" + fromWei(stakeBalance, 'ether')); + console.log("fee baseReserve:" + fromWei(baseReserve, 'ether') + " quoteReserve:" + fromWei(quoteReserve, 'mwei') + " stakeReserve:" + fromWei(stakeReserve, 'ether')); + console.log("baseRatio:" + fromWei(baseRatio, 'ether') + " quoteRatio:" + fromWei(quoteRatio, 'mwei')); + + return { + "baseReserve": baseReserve, + "quoteReserve": quoteReserve, + "stakeReserve": stakeReserve, + "baseBalance": baseBalance, + "quoteBalance": quoteBalance, + "stakeBalance": stakeBalance, + "baseRatio": baseRatio, + "quoteRatio": quoteRatio + } } -async function createERC721(ctx:NFTContext) { - var tx = await ctx.NFTTokenFacotry.methods.createERC721( - "https://app.dodoex.io" - ).send(ctx.sendParam(author)); - return tx.events['NewERC721']['returnValues']['erc721']; +async function getFeeUserState(ctx: NFTContext, feeAddress: string, userAddress: string) { + let feeInstance = contracts.getContractWithAddress(contracts.NFT_FEE, feeAddress); + let userShares = await feeInstance.methods._SHARES_(userAddress).call(); + let [baseRewards, quoteRewards] = await feeInstance.methods.getPendingReward(userAddress).call(); + let userBasePerShares = await feeInstance.methods._USER_BASE_PER_SHARE_(userAddress).call(); + let userQuotePerShares = await feeInstance.methods._USER_QUOTE_PER_SHARE_(userAddress).call(); + + console.log("user shares:" + fromWei(userShares, 'ether')); + console.log("user baseRewards:" + fromWei(baseRewards, 'ether') + " userQuoteRewards:" + fromWei(quoteRewards, 'mwei')); + console.log("user basePerShares:" + fromWei(userBasePerShares, 'ether') + " userQuotePerShares:" + fromWei(userQuotePerShares, 'mwei')); + + return { + "userShares": userShares, + "userBaseRewards": baseRewards, + "userQuoteRewards": quoteRewards, + "userBasePerShares": userBasePerShares, + "userQuotePerShares": userQuotePerShares + } } +async function mockTrade(ctx: NFTContext, dvmAddress: string, dvmInstance, fragInstance) { + await ctx.transferQuoteToDVM(ctx.USDT, dvmAddress, user1, mweiStr("20")); + await dvmInstance.methods.sellQuote(user1).send(ctx.sendParam(user1)); + + await ctx.transferBaseToDVM(fragInstance, dvmAddress, user1, decimalStr("10")); + await dvmInstance.methods.sellBase(user1).send(ctx.sendParam(user1)); + + await ctx.transferQuoteToDVM(ctx.USDT, dvmAddress, user2, mweiStr("80")); + await dvmInstance.methods.sellQuote(user2).send(ctx.sendParam(user2)); + + await ctx.transferBaseToDVM(fragInstance, dvmAddress, user2, decimalStr("20")); + await dvmInstance.methods.sellBase(user2).send(ctx.sendParam(user2)); +} + + describe("DODONFT", () => { let snapshotId: string; @@ -74,41 +120,43 @@ describe("DODONFT", () => { }); it("createTokenAndTransferToVault", async () => { - var erc721Address = await createERC721(ctx); - var vaultAddress = await createNFTVault(ctx); + var erc721Address = await ctx.createERC721(ctx, author); + var vaultAddress = await ctx.createNFTVault(ctx, author); var nftVaultInstance = contracts.getContractWithAddress(contracts.NFT_VAULT, vaultAddress); var erc721Instance = contracts.getContractWithAddress(contracts.ERC721, erc721Address); await erc721Instance.methods.safeTransferFrom(author, vaultAddress, 0).send(ctx.sendParam(author)); - var nftIndex = await nftVaultInstance.methods.getIdByTokenIdAndAddr(erc721Address,0).call(); + var nftIndex = await nftVaultInstance.methods.getIdByTokenIdAndAddr(erc721Address, 0).call(); var nftInfo = await nftVaultInstance.methods.getNftInfoById(nftIndex).call(); assert(nftInfo.amount, '1') assert(nftInfo.tokenId, '0') }); - it.only("createFragment", async () => { - var erc721Address = await createERC721(ctx); - var vaultAddress = await createNFTVault(ctx); + it("createFragment", async () => { + var erc721Address = await ctx.createERC721(ctx, author); + var vaultAddress = await ctx.createNFTVault(ctx, author); var nftVaultInstance = contracts.getContractWithAddress(contracts.NFT_VAULT, vaultAddress); var erc721Instance = contracts.getContractWithAddress(contracts.ERC721, erc721Address); await erc721Instance.methods.safeTransferFrom(author, vaultAddress, 0).send(ctx.sendParam(author)); - var quoteToken = "0x156595bAF85D5C29E91d959889B022d952190A64"; - var vaultPreOwner = "0xaac153c1344cA14497A5dd22b1F70C28793625aa"; - var stakeToken = "0x854b0f89BAa9101e49Bfb357A38071C9db5d0DFa"; - // var quoteToken = ctx.USDT.options.address; - // var vaultPreOwner = author; - // var stakeToken = "0x0000000000000000000000000000000000000000"; + // var quoteToken = "0x156595bAF85D5C29E91d959889B022d952190A64"; + // var vaultPreOwner = "0xaac153c1344cA14497A5dd22b1F70C28793625aa"; + // var stakeToken = "0x854b0f89BAa9101e49Bfb357A38071C9db5d0DFa"; + var quoteToken = ctx.USDT.options.address; + var vaultPreOwner = author; + var stakeToken = "0x0000000000000000000000000000000000000000"; + var dvmParams = [ - "0", - "10000000000000000", - "1000000", - "1000000000000000000" + "0", //lpFeeRate + decimalStr("0.01"), //mtFeeRate + mweiStr("1"), // I + decimalStr("1") // K ]; var fragParams = [ - "100000000000000000000000000", - "200000000000000000", - "1617976800" + decimalStr("100000000"), //totalSupply + decimalStr("0.2"), //ownerRatio + Math.floor(new Date().getTime() / 1000 + 60 * 60) //buyoutTimeStamp 1h later ] + var isOpenTwap = false var callData = ctx.NFTProxy.methods.createFragment( quoteToken, @@ -118,35 +166,96 @@ describe("DODONFT", () => { fragParams, isOpenTwap ).encodeABI(); - console.log("data:",callData); + console.log("data:", callData); - // var tx = await logGas(await nftVaultInstance.methods.createFragment( - // ctx.NFTProxy.options.address, - // callData - // ), ctx.sendParam(author), "createFragment"); + await logGas(await nftVaultInstance.methods.createFragment( + ctx.NFTProxy.options.address, + callData + ), ctx.sendParam(author), "createFragment"); - // console.log("tx:",tx); + let [fragAddress, , dvmAddress] = await ctx.getRegistry(ctx, vaultAddress); + var dvmInstance = contracts.getContractWithAddress(contracts.DVM_NAME, dvmAddress); + var midPrice = await dvmInstance.methods.getMidPrice().call(); + assert(midPrice, mweiStr("1")); + let newVaultOwner = await nftVaultInstance.methods._OWNER_().call(); + assert(fragAddress, newVaultOwner); }); - it("stakeToFeeDistributor", async () => { + it.only("stakeToFeeDistributor", async () => { + let [vaultAddress, fragAddress, feeAddress, dvmAddress] = await ctx.createFragment(ctx, author, null, null, null); - }); + var nftFeeInstance = contracts.getContractWithAddress(contracts.NFT_FEE, feeAddress); + var dvmInstance = contracts.getContractWithAddress(contracts.DVM_NAME, dvmAddress); + var fragInstance = contracts.getContractWithAddress(contracts.NFT_FRAG, fragAddress); + await ctx.approveProxy(fragInstance, user1); + await ctx.approveProxy(fragInstance, user2); + //mock trading + //stake + await mockTrade(ctx, dvmAddress, dvmInstance, fragInstance); - it("dvm-trade", async () => { + await logGas(await ctx.NFTProxy.methods.stakeToFeeDistributor( + feeAddress, + decimalStr("5"), + 0 + ), ctx.sendParam(user1), "stakeToFeeDistributor"); - }); + await logGas(await ctx.NFTProxy.methods.stakeToFeeDistributor( + feeAddress, + decimalStr("10"), + 0 + ), ctx.sendParam(user2), "stakeToFeeDistributor"); - it("claim", async () => { + await mockTrade(ctx, dvmAddress, dvmInstance, fragInstance); - }); + await logGas(await ctx.NFTProxy.methods.stakeToFeeDistributor( + feeAddress, + decimalStr("10"), + 0 + ), ctx.sendParam(user1), "stakeToFeeDistributor"); - it("unstake", async () => { + await logGas(await ctx.NFTProxy.methods.stakeToFeeDistributor( + feeAddress, + decimalStr("20"), + 0 + ), ctx.sendParam(user2), "stakeToFeeDistributor"); + let globalObj = await getFeeGlobalState(ctx, feeAddress, fragInstance, ctx.USDT, fragInstance); + assert(globalObj['quoteBalance'], mweiStr("0.6")); + assert(globalObj['stakeReserve'], decimalStr("45")); + + let user1Obj = await getFeeUserState(ctx, feeAddress, user1); + assert(user1Obj['userQuoteRewards'], mweiStr("0.1")); + assert(user1Obj['userShares'], decimalStr("15")); + let user2Obj = await getFeeUserState(ctx, feeAddress, user2); + assert(user2Obj['userBaseRewards'], decimalStr("0.66666480000453957")); + assert(user2Obj['userShares'], decimalStr("30")); + + //claim + var user1BaseBalanceStart = await fragInstance.methods.balanceOf(user1).call() + await logGas(await nftFeeInstance.methods.claim(user1), ctx.sendParam(user1), "claim"); + var user1BaseBalanceEnd = await fragInstance.methods.balanceOf(user1).call() + user1Obj = await getFeeUserState(ctx, feeAddress, user1); + await getFeeGlobalState(ctx, feeAddress, fragInstance, ctx.USDT, fragInstance); + assert(user1Obj['userQuoteRewards'], "0"); + assert(globalObj['quoteBalance'], mweiStr("0.5")); + assert(user1BaseBalanceEnd - user1BaseBalanceStart, "333332400002269700"); + + //unstake + var user2BaseBalanceStart = await fragInstance.methods.balanceOf(user2).call() + await logGas(await nftFeeInstance.methods.unstake(decimalStr("30"),user2, true), ctx.sendParam(user2), "unstake"); + var user2BaseBalanceEnd = await fragInstance.methods.balanceOf(user2).call() + user2Obj = await getFeeUserState(ctx, feeAddress, user2); + await getFeeGlobalState(ctx, feeAddress, fragInstance, ctx.USDT, fragInstance); + assert(user2Obj['userQuoteRewards'], "0"); + assert(globalObj['quoteBalance'], mweiStr("0.3")); + assert(globalObj['stakeReserve'], mweiStr("15")); + assert(globalObj['stakeBalance'], mweiStr("15")); + assert(user2BaseBalanceEnd - user2BaseBalanceStart, "30666664800004540000"); }); it("buyout", async () => { - + }); it("redeem", async () => { diff --git a/test/utils/NFTContext.ts b/test/utils/NFTContext.ts index c7cdf2b..c264430 100644 --- a/test/utils/NFTContext.ts +++ b/test/utils/NFTContext.ts @@ -24,7 +24,7 @@ export class NFTContext { EVM: EVM; Web3: Web3; NFTTokenFacotry: Contract; - + NFTRegister: Contract; CollatteralVault: Contract; Fragment: Contract; @@ -33,10 +33,11 @@ export class NFTContext { NFTProxy: Contract; DODOApprove: Contract; DODOApproveProxy: Contract; - + //token USDT: Contract; WETH: Contract; + DODO: Contract; Deployer: string; Maintainer: string; @@ -59,6 +60,11 @@ export class NFTContext { ["USDT Token", "USDT", 6] ); + this.DODO = await contracts.newContract( + contracts.MINTABLE_ERC20_CONTRACT_NAME, + ["DODO Token", "DODO", 6] + ); + var cloneFactory = await contracts.newContract( contracts.CLONE_FACTORY_CONTRACT_NAME ); @@ -116,6 +122,8 @@ export class NFTContext { await this.NFTProxy.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); + await this.NFTRegister.methods.addAmindList(this.NFTProxy.options.address).send(this.sendParam(this.Deployer)); + await this.DODOApprove.methods.init(this.Deployer, this.DODOApproveProxy.options.address).send(this.sendParam(this.Deployer)); await this.DODOApproveProxy.methods.init(this.Deployer, [this.NFTProxy.options.address]).send(this.sendParam(this.Deployer)); @@ -136,14 +144,91 @@ export class NFTContext { await token.methods.mint(to, amount).send(this.sendParam(this.Deployer)); } - async approveProxy(account: string) { - await this.USDT.methods - .approve(this.DODOApprove.options.address, MAX_UINT256) - .send(this.sendParam(account)); - await this.WETH.methods + async approveProxy(token, account: string) { + await token.methods .approve(this.DODOApprove.options.address, MAX_UINT256) .send(this.sendParam(account)); } + + async getRegistry(ctx: NFTContext, vaultAddress: string) { + let fragAddress = await ctx.NFTRegister.methods._VAULT_FRAG_REGISTRY_(vaultAddress).call(); + let feeDistrubitor = await ctx.NFTRegister.methods._FRAG_FEE_REGISTRY_(fragAddress).call(); + let fragInstance = contracts.getContractWithAddress(contracts.NFT_FRAG, fragAddress); + let dvmAddress = await fragInstance.methods._DVM_().call(); + return [fragAddress, feeDistrubitor, dvmAddress]; + } + + async createNFTVault(ctx: NFTContext, author: string) { + var tx = await ctx.NFTProxy.methods.createNFTCollateralVault( + "DODONFT", + "https://app.dodoex.io" + ).send(ctx.sendParam(author)); + + return tx.events['CreateNFTCollateralVault']['returnValues']['vault']; + } + + async createERC721(ctx: NFTContext, author: string) { + var tx = await ctx.NFTTokenFacotry.methods.createERC721( + "https://app.dodoex.io" + ).send(ctx.sendParam(author)); + return tx.events['NewERC721']['returnValues']['erc721']; + } + + async createFragment(ctx: NFTContext, author: string, dvmParams, fragParams, addrs) { + var erc721Address = await this.createERC721(ctx, author); + var vaultAddress = await this.createNFTVault(ctx, author); + var nftVaultInstance = contracts.getContractWithAddress(contracts.NFT_VAULT, vaultAddress); + var erc721Instance = contracts.getContractWithAddress(contracts.ERC721, erc721Address); + await erc721Instance.methods.safeTransferFrom(author, vaultAddress,0).send(ctx.sendParam(author)); + if (dvmParams == null) { + dvmParams = [ + "0", //lpFeeRate + decimalStr("0.01"), //mtFeeRate + mweiStr("1"), // I + decimalStr("1") // K + ]; + } + if (fragParams == null) { + fragParams = [ + decimalStr("100000000"), //totalSupply + decimalStr("0.2"), //ownerRatio + Math.floor(new Date().getTime() / 1000 + 60 * 60) //buyoutTimeStamp 1h later + ] + } + if (addrs == null) { + addrs = [] + addrs.push(ctx.USDT.options.address);//quoteToken + addrs.push(author);//vaultPreOwner + addrs.push("0x0000000000000000000000000000000000000000");//stakeToken + } + + var callData = ctx.NFTProxy.methods.createFragment( + addrs[0], + addrs[1], + addrs[2], + dvmParams, + fragParams, + false + ).encodeABI(); + + await nftVaultInstance.methods.createFragment( + ctx.NFTProxy.options.address, + callData + ).send(ctx.sendParam(author)); + + let [fragAddress, feeAddress, dvmAddress] = await this.getRegistry(ctx, vaultAddress); + return [vaultAddress, fragAddress, feeAddress, dvmAddress, callData] + } + + + async transferBaseToDVM(baseToken, dvm: string, account: string, amount: string) { + await baseToken.methods.transfer(dvm, amount).send(this.sendParam(account)) + } + + async transferQuoteToDVM(quoteToken, dvm: string, account: string, amount: string) { + await quoteToken.methods.transfer(dvm, amount).send(this.sendParam(account)) + } + } export async function getDODONftContext(weth: string): Promise { diff --git a/truffle-test.sh b/truffle-test.sh index 1485a4d..bb25922 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