From 6b6ace5155b507e424bd1c8d71bcbc4dc088d076 Mon Sep 17 00:00:00 2001 From: owen05 Date: Tue, 15 Feb 2022 09:31:46 +0800 Subject: [PATCH] fix --- config/boba-config.js | 2 +- config/rinkeby-config.js | 4 +- contracts/DODOStarter/impl/FairFunding.sol | 108 ++++++++++++++++-- contracts/DODOStarter/impl/InstantFunding.sol | 73 +++++++++++- deploy-starter.txt | 15 +++ test/DODOStarter/FairFunding.test.ts | 90 +++++++++++++++ test/DODOStarter/InstantFunding.test.ts | 0 test/utils/Contracts.ts | 6 + test/utils/DODOStarterContext.ts | 108 ++++++++++++++++++ truffle-config.js | 2 +- 10 files changed, 391 insertions(+), 17 deletions(-) create mode 100644 test/DODOStarter/FairFunding.test.ts create mode 100644 test/DODOStarter/InstantFunding.test.ts create mode 100644 test/utils/DODOStarterContext.ts diff --git a/config/boba-config.js b/config/boba-config.js index 5d7f9b8..152a718 100644 --- a/config/boba-config.js +++ b/config/boba-config.js @@ -60,7 +60,7 @@ module.exports = { //Proxy DODOV2Proxy: "0x55793C2c8A796cCE00EF2D1a86CCA2E0399BF285", DSPProxy: "0x25B2f945Fec30F34b05d416C7c0b5c6c51A3ADdC", - CpProxy: "0x141b4497a298340E8B451ac4eeE947B00aDbe07A", + CpProxy: "0x7C062B9C584fA6eC2504270790D38240A2c5fE72", DPPProxy: "0xCDFD45f965De9932367833Ca7187e4c9C43A2380", RouteProxy: "0x0fe261aeE0d1C4DFdDee4102E82Dd425999065F4", DODOMineV3Proxy: "0xb0C7cB15b52b0093130487209e96CFeC0Ba4Ff66", diff --git a/config/rinkeby-config.js b/config/rinkeby-config.js index f5f1422..9f91ff6 100644 --- a/config/rinkeby-config.js +++ b/config/rinkeby-config.js @@ -120,8 +120,8 @@ module.exports = { //=================== Starter =================== DODOStarterProxy: "0x451E07405B79eDEEA87ccFa57e1BaF184Bea6773", DODOStarterFactory: "0xa28D60c3eCDc52521c8219bd6a4eba0AA8900F88", - FairFunding: "0x5D1f0d091B3Fe5F79908F232c50CfC3D9084c59d", - InstantFunding: "0x9297C04D4F8B47cCf7F2733b0F8f151aE1cb4615" + FairFunding: "0x9124B1191DDB6CB1CAF9aA899d5059ea52b5D09B", + InstantFunding: "0x80B21A1A832D3b0016A0d287967CD9Dce0Ade688" } } \ No newline at end of file diff --git a/contracts/DODOStarter/impl/FairFunding.sol b/contracts/DODOStarter/impl/FairFunding.sol index 4f50861..232bcdc 100644 --- a/contracts/DODOStarter/impl/FairFunding.sol +++ b/contracts/DODOStarter/impl/FairFunding.sol @@ -19,7 +19,7 @@ contract FairFunding is Vesting { using SafeMath for uint256; using SafeERC20 for IERC20; - uint256 internal constant _SETTEL_FUND_ = 200 finney; + uint256 internal constant _SETTEL_FUND_ = 2e17; // ============ Fair Mode ============ uint256 public _COOLING_DURATION_; @@ -147,12 +147,16 @@ contract FairFunding is Vesting { } function getPrice(uint256 fundAmount) public view returns (uint256 price) { - price = DecimalMath.divFloor(fundAmount, _TOTAL_TOKEN_AMOUNT_); - if (price < _LOWER_LIMIT_PRICE_) { - price = _LOWER_LIMIT_PRICE_; - } - if (price > _UPPER_LIMIT_PRICE_) { - price = _UPPER_LIMIT_PRICE_; + if(_FINAL_PRICE_ != 0) + price = _FINAL_PRICE_; + else { + price = DecimalMath.divFloor(fundAmount, _TOTAL_TOKEN_AMOUNT_); + if (price < _LOWER_LIMIT_PRICE_) { + price = _LOWER_LIMIT_PRICE_; + } + if (price > _UPPER_LIMIT_PRICE_) { + price = _UPPER_LIMIT_PRICE_; + } } } @@ -211,7 +215,6 @@ contract FairFunding is Vesting { // input fund check inputFund = currentFundBalance.sub(_FUNDS_RESERVE_); - _FUNDS_RESERVE_ = _FUNDS_RESERVE_.add(inputFund); if (_QUOTA_ != address(0)) { require( @@ -220,6 +223,7 @@ contract FairFunding is Vesting { ); } + _FUNDS_RESERVE_ = _FUNDS_RESERVE_.add(inputFund); _FUNDS_DEPOSITED_[to] = _FUNDS_DEPOSITED_[to].add(inputFund); _TOTAL_RAISED_FUNDS_ = _TOTAL_RAISED_FUNDS_.add(inputFund); @@ -246,7 +250,7 @@ contract FairFunding is Vesting { emit WithdrawFund(msg.sender, to, fundAmount, isSettled); } - function claimToken(address to) external { + function claimToken(address to) external preventReentrant { require(isSettled(), "NOT_SETTLED"); uint256 totalAllocation = getUserTokenAllocation(msg.sender); uint256 claimableTokenAmount = _claimToken(to, totalAllocation); @@ -308,8 +312,90 @@ contract FairFunding is Vesting { // ============ Version Control ============ - function version() virtual external pure returns (string memory) { + function version() virtual public pure returns (string memory) { return "FairFunding 1.0.0"; } -} + // ============ View Helper ============== + function getCurrentFundingInfo(address user) external view returns( + uint256 raiseFundAmount, + uint256 userFundAmount, + uint256 currentPrice, + uint256 soldTokenAmount, + uint256 claimableTokenAmount, + bool isHaveCap, + uint256 userQuota, + uint256 userCurrentQuota + ) { + raiseFundAmount =_TOTAL_RAISED_FUNDS_; + userFundAmount = _FUNDS_DEPOSITED_[user]; + currentPrice = getCurrentPrice(); + uint256 tmpSoldTokenAmount = DecimalMath.divCeil(_TOTAL_RAISED_FUNDS_, currentPrice); + soldTokenAmount = tmpSoldTokenAmount > _TOTAL_TOKEN_AMOUNT_ ? _TOTAL_TOKEN_AMOUNT_ : tmpSoldTokenAmount; + + if(block.timestamp > _TOKEN_VESTING_START_) { + uint256 totalAllocation = getUserTokenAllocation(user); + uint256 remainingToken = DecimalMath.mulFloor( + getRemainingRatio(block.timestamp,0), + totalAllocation + ); + claimableTokenAmount = totalAllocation.sub(remainingToken).sub(_CLAIMED_TOKEN_[user]); + }else { + claimableTokenAmount = 0; + } + + if(_QUOTA_ == address(0)) { + isHaveCap = false; + userQuota = uint256(-1); + userCurrentQuota = uint256(-1); + } else { + isHaveCap = true; + userQuota = uint256(IQuota(_QUOTA_).getUserQuota(user)); + if(userQuota > userFundAmount) { + userCurrentQuota = userQuota - userFundAmount; + } else { + userCurrentQuota = 0; + } + } + } + + function getBaseFundInfo() external view returns( + address tokenAddress, + address fundAddress, + uint256 totalTokenAmount, + uint256 price0, //_LOWER_LIMIT_PRICE_ + uint256 price1, //_UPPER_LIMIT_PRICE_ + string memory versionType, + uint256 startTime, + uint256 bidDuration, + uint256 tokenVestingStart, + uint256 tokenVestingDuration + ) { + tokenAddress = _TOKEN_ADDRESS_; + fundAddress = _FUNDS_ADDRESS_; + totalTokenAmount = _TOTAL_TOKEN_AMOUNT_; + price0 = _LOWER_LIMIT_PRICE_; + price1 = _UPPER_LIMIT_PRICE_; + + versionType = version(); + + startTime = _START_TIME_; + bidDuration = _BIDDING_DURATION_; + tokenVestingStart = _TOKEN_VESTING_START_; + tokenVestingDuration = _TOKEN_VESTING_DURATION_; + } + + + function getFairFundInfo(address user) external view returns( + bool isOverCapStop, + uint256 finalPrice, + uint256 userUnusedFundAmount, + uint256 coolDuration + ) { + isOverCapStop = _IS_OVERCAP_STOP; + finalPrice = _FINAL_PRICE_; + userUnusedFundAmount = getUserFundsUnused(user); + coolDuration = _COOLING_DURATION_; + } + +} \ No newline at end of file diff --git a/contracts/DODOStarter/impl/InstantFunding.sol b/contracts/DODOStarter/impl/InstantFunding.sol index 735d28d..0177904 100644 --- a/contracts/DODOStarter/impl/InstantFunding.sol +++ b/contracts/DODOStarter/impl/InstantFunding.sol @@ -189,7 +189,7 @@ contract InstantFunding is Vesting { emit DepositFund(to, depositFundAmount, newTokenAllocation); } - function claimToken(address to) external { + function claimToken(address to) external preventReentrant { uint256 totalAllocation = getUserTokenAllocation(msg.sender); uint256 claimableTokenAmount = _claimToken(to, totalAllocation); @@ -235,7 +235,76 @@ contract InstantFunding is Vesting { // ============ Version Control ============ - function version() virtual external pure returns (string memory) { + function version() virtual public pure returns (string memory) { return "InstantFunding 1.0.0"; } + + // ============ View Helper ============== + function getCurrentFundingInfo(address user) external view returns( + uint256 raiseFundAmount, + uint256 userFundAmount, + uint256 currentPrice, + uint256 soldTokenAmount, + uint256 claimableTokenAmount, + bool isHaveCap, + uint256 userQuota, + uint256 userCurrentQuota + ) { + raiseFundAmount =_TOTAL_RAISED_FUNDS_; + userFundAmount = _FUNDS_USED_[user]; + currentPrice = getCurrentPrice(); + soldTokenAmount = _TOTAL_ALLOCATED_TOKEN_; + + if(block.timestamp > _TOKEN_VESTING_START_) { + uint256 totalAllocation = getUserTokenAllocation(user); + uint256 remainingToken = DecimalMath.mulFloor( + getRemainingRatio(block.timestamp,0), + totalAllocation + ); + claimableTokenAmount = totalAllocation.sub(remainingToken).sub(_CLAIMED_TOKEN_[user]); + }else { + claimableTokenAmount = 0; + } + + if(_QUOTA_ == address(0)) { + isHaveCap = false; + userQuota = uint256(-1); + userCurrentQuota = uint256(-1); + } else { + isHaveCap = true; + userQuota = uint256(IQuota(_QUOTA_).getUserQuota(user)); + if(userQuota > userFundAmount) { + userCurrentQuota = userQuota - userFundAmount; + } else { + userCurrentQuota = 0; + } + } + } + + + function getBaseFundInfo() external view returns( + address tokenAddress, + address fundAddress, + uint256 totalTokenAmount, + uint256 price0, //_START_PRICE_ + uint256 price1, //_END_PRICE_ + string memory versionType, + uint256 startTime, + uint256 bidDuration, + uint256 tokenVestingStart, + uint256 tokenVestingDuration + ) { + tokenAddress = _TOKEN_ADDRESS_; + fundAddress = _FUNDS_ADDRESS_; + totalTokenAmount = _TOTAL_TOKEN_AMOUNT_; + price0 = _START_PRICE_; + price1 = _END_PRICE_; + + versionType = version(); + + startTime = _START_TIME_; + bidDuration = _BIDDING_DURATION_; + tokenVestingStart = _TOKEN_VESTING_START_; + tokenVestingDuration = _TOKEN_VESTING_DURATION_; + } } diff --git a/deploy-starter.txt b/deploy-starter.txt index dc51bca..3359741 100644 --- a/deploy-starter.txt +++ b/deploy-starter.txt @@ -37,3 +37,18 @@ Deploy time: 2022/1/29 上午10:00:18 Deploy type: STARTER FairFundingTemplate: 0x5D1f0d091B3Fe5F79908F232c50CfC3D9084c59d InstantFundingTemplate: 0x9297C04D4F8B47cCf7F2733b0F8f151aE1cb4615 +==================================================== +network type: rinkeby +Deploy time: 2022/2/11 下午9:43:38 +Deploy type: STARTER +FairFundingTemplate: 0x9124B1191DDB6CB1CAF9aA899d5059ea52b5D09B +==================================================== +network type: rinkeby +Deploy time: 2022/2/11 下午9:46:29 +Deploy type: STARTER +InstantFundingTemplate: 0x24eC6260e10EE3093A764Fa64b35b34fD75dD437 +==================================================== +network type: rinkeby +Deploy time: 2022/2/15 上午9:12:58 +Deploy type: STARTER +InstantFundingTemplate: 0x80B21A1A832D3b0016A0d287967CD9Dce0Ade688 diff --git a/test/DODOStarter/FairFunding.test.ts b/test/DODOStarter/FairFunding.test.ts new file mode 100644 index 0000000..c793b53 --- /dev/null +++ b/test/DODOStarter/FairFunding.test.ts @@ -0,0 +1,90 @@ +/* + + 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 { DODOStarterContext,getDODOStarterContext } from '../utils/DODOStarterContext'; +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; + +async function init(ctx: NFTPoolContext): Promise { + maker = ctx.SpareAccounts[0]; + user = ctx.SpareAccounts[1]; +} + +async function createFairFunding(ctx: DODOStarterContext) { + // 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"); + + // var newFilterAdmin = tx.events['CreateNFTPool'].returnValues['newFilterAdmin'] + // var filter = tx.events['CreateNFTPool'].returnValues['filter'] + + // return [newFilterAdmin, filter]; +} + +describe("FairFunding", () => { + let snapshotId: string; + let ctx: DODOStarterContext; + + before(async () => { + ctx = await getDODOStarterContext(); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("FairFunding", () => { + it("Create_FairFunding", 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"); + + // var newFilterAdmin = tx.events['CreateNFTPool'].returnValues['newFilterAdmin'] + // var filter = tx.events['CreateNFTPool'].returnValues['filter'] + + // console.log("newFilterAdmin:", newFilterAdmin) + // console.log("filterV1:", filter) + + // assert.equal( + // tx.events['CreateNFTPool'].returnValues['filterAdminOwner'], + // maker + // ) + }); + }); +}); + diff --git a/test/DODOStarter/InstantFunding.test.ts b/test/DODOStarter/InstantFunding.test.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index 0bbe568..d758d77 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -85,6 +85,12 @@ export const DODO_NFT_APPROVE = "DODONFTApprove" export const DODO_NFT_POOL_PROXY = "DODONFTPoolProxy" +export const DODO_STARTER_PROXY = "DODOStarterProxy" +export const DODO_STARTER_FACTORY = "DODOStarterFactory" +export const FAIR_FUNDING = "FairFunding" +export const INSTANT_FUNDING = "InstantFunding" + + interface ContractJson { abi: any; networks: { [network: number]: any }; diff --git a/test/utils/DODOStarterContext.ts b/test/utils/DODOStarterContext.ts new file mode 100644 index 0000000..bd9fa8a --- /dev/null +++ b/test/utils/DODOStarterContext.ts @@ -0,0 +1,108 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +import BigNumber from 'bignumber.js'; +import Web3 from 'web3'; +import { Contract } from 'web3-eth-contract'; + +import * as contracts from './Contracts'; +import { decimalStr, mweiStr, MAX_UINT256 } from './Converter'; +import { EVM, getDefaultWeb3 } from './EVM'; +import * as log from './Log'; + +BigNumber.config({ + EXPONENTIAL_AT: 1000, + DECIMAL_PLACES: 80, +}); + + +export class DODOStarterContext { + EVM: EVM; + Web3: Web3; + + //contract + DODOStarterFactory: Contract; + + //account + Deployer: string; + Maintainer: string; + SpareAccounts: string[]; + + //token + SellToken: Contract; + FundToken: Contract; + + + async init() { + this.EVM = new EVM(); + this.Web3 = getDefaultWeb3(); + + const allAccounts = await this.Web3.eth.getAccounts(); + this.Deployer = allAccounts[0]; + this.Maintainer = allAccounts[1]; + this.SpareAccounts = allAccounts.slice(2, 10); + + this.SellToken = await contracts.newContract( + contracts.MINTABLE_ERC20_CONTRACT_NAME, + ["SellToken", "DODO", 18] + ); + + this.FundToken = await contracts.newContract( + contracts.MINTABLE_ERC20_CONTRACT_NAME, + ["FundToken", "USDT", 18] + ); + + let cloneFactory = await contracts.newContract( + contracts.CLONE_FACTORY_CONTRACT_NAME + ); + + let fairFundingTemplate = await contracts.newContract( + contracts.FAIR_FUNDING + ) + + let instantFundingTemplate = await contracts.newContract( + contracts.INSTANT_FUNDING + ) + + this.DODOStarterFactory = await contracts.newContract(contracts.DODO_STARTER_FACTORY, + [ + cloneFactory.options.address, + fairFundingTemplate.options.address, + instantFundingTemplate.options.address + ] + ) + + await this.DODOStarterFactory.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer)); + + console.log(log.blueText("[Init DODOStarter context]")); + } + + sendParam(sender, value = "0") { + return { + from: sender, + gas: process.env["COVERAGE"] ? 10000000000 : 7000000, + gasPrice: mweiStr("1000"), + value: decimalStr(value), + }; + } + + async mintTestToken(to: string, token: Contract, amount: string) { + await token.methods.mint(to, amount).send(this.sendParam(this.Deployer)); + } + + async approveProxy(account: string, target: string, token: Contract) { + await token.methods + .approve(target, MAX_UINT256) + .send(this.sendParam(account)); + } +} + +export async function getDODOStarterContext(): Promise { + var context = new DODOStarterContext(); + await context.init(); + return context; +} \ No newline at end of file diff --git a/truffle-config.js b/truffle-config.js index 45634fc..1a52d32 100755 --- a/truffle-config.js +++ b/truffle-config.js @@ -252,7 +252,7 @@ module.exports = { enabled: true, runs: 200, }, - evmVersion: "istanbul" + // evmVersion: "istanbul" }, }, },