From d983485948a55d0ee846951e02cf911633b08d96 Mon Sep 17 00:00:00 2001 From: mingda Date: Sun, 4 Oct 2020 10:47:47 +0800 Subject: [PATCH] DODO Mine reward vault & reader --- contracts/intf/IDODO.sol | 7 +++-- contracts/token/DODOMine.sol | 9 +++--- contracts/token/DODOMineReader.sol | 44 +++++++++++++++++++++++++++++ contracts/token/DODORewardVault.sol | 33 ++++++++++++++++++++++ contracts/token/DODOToken.sol | 2 +- test/Mining.test.ts | 18 ++++++++---- test/utils/Contracts.ts | 3 ++ 7 files changed, 104 insertions(+), 12 deletions(-) create mode 100644 contracts/token/DODOMineReader.sol create mode 100644 contracts/token/DODORewardVault.sol diff --git a/contracts/intf/IDODO.sol b/contracts/intf/IDODO.sol index fe273da..4b1f6cc 100644 --- a/contracts/intf/IDODO.sol +++ b/contracts/intf/IDODO.sol @@ -8,6 +8,7 @@ pragma solidity 0.6.9; pragma experimental ABIEncoderV2; + interface IDODO { function init( address owner, @@ -42,6 +43,8 @@ interface IDODO { function queryBuyBaseToken(uint256 amount) external view returns (uint256 payQuote); + function getExpectedTarget() external view returns (uint256 baseTarget, uint256 quoteTarget); + function depositBaseTo(address to, uint256 amount) external returns (uint256); function withdrawBase(uint256 amount) external returns (uint256); @@ -54,9 +57,9 @@ interface IDODO { function withdrawAllQuote() external returns (uint256); - function _BASE_CAPITAL_TOKEN_() external returns (address); + function _BASE_CAPITAL_TOKEN_() external view returns (address); - function _QUOTE_CAPITAL_TOKEN_() external returns (address); + function _QUOTE_CAPITAL_TOKEN_() external view returns (address); function _BASE_TOKEN_() external returns (address); diff --git a/contracts/token/DODOMine.sol b/contracts/token/DODOMine.sol index 8734c9f..699c0fb 100644 --- a/contracts/token/DODOMine.sol +++ b/contracts/token/DODOMine.sol @@ -13,6 +13,7 @@ import {DecimalMath} from "../lib/DecimalMath.sol"; import {SafeERC20} from "../lib/SafeERC20.sol"; import {SafeMath} from "../lib/SafeMath.sol"; import {IERC20} from "../intf/IERC20.sol"; +import {IDODORewardVault, DODORewardVault} from "./DODORewardVault.sol"; contract DODOMine is Ownable { @@ -44,7 +45,7 @@ contract DODOMine is Ownable { uint256 accDODOPerShare; // Accumulated DODOs per share, times 1e12. See below. } - address public dodoToken; + address public dodoRewardVault; uint256 public dodoPerBlock; // Info of each pool. @@ -65,7 +66,7 @@ contract DODOMine is Ownable { event Claim(address indexed user, uint256 amount); constructor(address _dodoToken, uint256 _startBlock) public { - dodoToken = _dodoToken; + dodoRewardVault = address(new DODORewardVault(_dodoToken)); startBlock = _startBlock; } @@ -307,9 +308,9 @@ contract DODOMine is Ownable { safeDODOTransfer(msg.sender, pending); } - // Safe DODO transfer function, just in case if rounding error causes pool to not have enough DODOs. + // Safe DODO transfer function function safeDODOTransfer(address _to, uint256 _amount) internal { - IERC20(dodoToken).safeTransfer(_to, _amount); + IDODORewardVault(dodoRewardVault).reward(_to, _amount); realizedReward[_to] = realizedReward[_to].add(_amount); emit Claim(_to, _amount); } diff --git a/contracts/token/DODOMineReader.sol b/contracts/token/DODOMineReader.sol new file mode 100644 index 0000000..f5ee9a4 --- /dev/null +++ b/contracts/token/DODOMineReader.sol @@ -0,0 +1,44 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {IDODO} from "../intf/IDODO.sol"; +import {IERC20} from "../intf/IERC20.sol"; +import {SafeMath} from "../lib/SafeMath.sol"; + + +interface IDODOMine { + function getUserLpBalance(address _lpToken, address _user) external view returns (uint256); +} + + +contract DODOMineReader { + using SafeMath for uint256; + + function getUserStakedBalance( + address _dodoMine, + address _dodo, + address _user + ) external view returns (uint256 baseBalance, uint256 quoteBalance) { + address baseLpToken = IDODO(_dodo)._BASE_CAPITAL_TOKEN_(); + address quoteLpToken = IDODO(_dodo)._QUOTE_CAPITAL_TOKEN_(); + + uint256 baseLpBalance = IDODOMine(_dodoMine).getUserLpBalance(baseLpToken, _user); + uint256 quoteLpBalance = IDODOMine(_dodoMine).getUserLpBalance(quoteLpToken, _user); + + uint256 baseLpTotalSupply = IERC20(baseLpToken).totalSupply(); + uint256 quoteLpTotalSupply = IERC20(quoteLpToken).totalSupply(); + + (uint256 baseTarget, uint256 quoteTarget) = IDODO(_dodo).getExpectedTarget(); + baseBalance = baseTarget.mul(baseLpBalance).div(baseLpTotalSupply); + quoteBalance = quoteTarget.mul(quoteLpBalance).div(quoteLpTotalSupply); + + return (baseBalance, quoteBalance); + } +} diff --git a/contracts/token/DODORewardVault.sol b/contracts/token/DODORewardVault.sol new file mode 100644 index 0000000..1942c46 --- /dev/null +++ b/contracts/token/DODORewardVault.sol @@ -0,0 +1,33 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +pragma solidity 0.6.9; +pragma experimental ABIEncoderV2; + +import {Ownable} from "../lib/Ownable.sol"; +import {SafeERC20} from "../lib/SafeERC20.sol"; +import {IERC20} from "../intf/IERC20.sol"; + + +interface IDODORewardVault { + function reward(address to, uint256 amount) external; +} + + +contract DODORewardVault is Ownable { + using SafeERC20 for IERC20; + + address public dodoToken; + + constructor(address _dodoToken) public { + dodoToken = _dodoToken; + } + + function reward(address to, uint256 amount) external onlyOwner { + IERC20(dodoToken).safeTransfer(to, amount); + } +} diff --git a/contracts/token/DODOToken.sol b/contracts/token/DODOToken.sol index 672611f..da51b1f 100644 --- a/contracts/token/DODOToken.sol +++ b/contracts/token/DODOToken.sol @@ -19,7 +19,7 @@ contract DODOToken { using SafeMath for uint256; string public symbol = "DODO"; - string public name = "DODO bird food"; + string public name = "DODO bird"; uint256 public decimals = 18; uint256 public totalSupply = 1000000000 * 10**18; // 1 Billion diff --git a/test/Mining.test.ts b/test/Mining.test.ts index 917e7c0..9b04679 100644 --- a/test/Mining.test.ts +++ b/test/Mining.test.ts @@ -8,7 +8,7 @@ import { DODOContext, getDODOContext } from './utils/Context'; import { decimalStr, MAX_UINT256 } from './utils/Converter'; // import * as assert from "assert" -import { newContract, DODO_TOKEN_CONTRACT_NAME, DODO_MINE_NAME, TEST_ERC20_CONTRACT_NAME, getContractWithAddress } from './utils/Contracts'; +import { newContract, DODO_TOKEN_CONTRACT_NAME, DODO_MINE_NAME, TEST_ERC20_CONTRACT_NAME, getContractWithAddress, DODO_MINE_READER_NAME } from './utils/Contracts'; import { Contract } from 'web3-eth-contract'; import { assert } from 'chai'; import { logGas } from './utils/Log'; @@ -17,6 +17,7 @@ let BaseDLP: Contract let QuoteDLP: Contract let DODOToken: Contract let DODOMine: Contract +let DODOMineReader: Contract let lp1: string; let lp2: string; @@ -38,6 +39,7 @@ async function init(ctx: DODOContext): Promise { DODOToken = await newContract(DODO_TOKEN_CONTRACT_NAME) DODOMine = await newContract(DODO_MINE_NAME, [DODOToken.options.address, (await ctx.Web3.eth.getBlockNumber()).toString()]) + DODOMineReader = await newContract(DODO_MINE_READER_NAME) BaseDLP = await getContractWithAddress(TEST_ERC20_CONTRACT_NAME, await ctx.DODO.methods._BASE_CAPITAL_TOKEN_().call()) QuoteDLP = await getContractWithAddress(TEST_ERC20_CONTRACT_NAME, await ctx.DODO.methods._QUOTE_CAPITAL_TOKEN_().call()) @@ -48,10 +50,12 @@ async function init(ctx: DODOContext): Promise { await BaseDLP.methods.approve(DODOMine.options.address, MAX_UINT256).send(ctx.sendParam(lp2)) await QuoteDLP.methods.approve(DODOMine.options.address, MAX_UINT256).send(ctx.sendParam(lp2)) - await DODOMine.methods.setReward(decimalStr("100")).send(ctx.sendParam(ctx.Deployer)) + await DODOMine.methods.setReward(decimalStr("100"), true).send(ctx.sendParam(ctx.Deployer)) await DODOMine.methods.addLpToken(BaseDLP.options.address, "1", true).send(ctx.sendParam(ctx.Deployer)) await DODOMine.methods.addLpToken(QuoteDLP.options.address, "2", true).send(ctx.sendParam(ctx.Deployer)) - await DODOToken.methods.transfer(DODOMine.options.address, decimalStr("100000000")).send(ctx.sendParam(ctx.Deployer)) + + const rewardVault = await DODOMine.methods.dodoRewardVault().call() + await DODOToken.methods.transfer(rewardVault, decimalStr("100000000")).send(ctx.sendParam(ctx.Deployer)) } describe("Lock DODO Token", () => { @@ -73,7 +77,7 @@ describe("Lock DODO Token", () => { }); describe("Lp Deposit", () => { - it.only("single lp deposit", async () => { + it("single lp deposit", async () => { await logGas(DODOMine.methods.deposit(BaseDLP.options.address, decimalStr("100")), ctx.sendParam(lp1), "deposit") await ctx.EVM.fastMove(100) assert.equal(await DODOMine.methods.getPendingReward(BaseDLP.options.address, lp1).call(), "3333333333333333333300") @@ -99,7 +103,7 @@ describe("Lock DODO Token", () => { assert.equal(await DODOMine.methods.getAllPendingReward(lp2).call(), "8366666666666666666600") }) - it("lp multi deposit and withdraw", async () => { + it.only("lp multi deposit and withdraw", async () => { await DODOMine.methods.deposit(BaseDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp2)) await DODOMine.methods.deposit(BaseDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp1)) await ctx.EVM.fastMove(100) @@ -112,6 +116,10 @@ describe("Lock DODO Token", () => { assert.equal(await DODOMine.methods.getAllPendingReward(lp1).call(), "0") assert.equal(await DODOToken.methods.balanceOf(lp1).call(), "2805555555555555555500") assert.equal(await DODOMine.methods.getRealizedReward(lp1).call(), "2805555555555555555500") + + var balance = await DODOMineReader.methods.getUserStakedBalance(DODOMine.options.address, ctx.DODO.options.address, lp1).call() + assert.equal(balance.baseBalance, decimalStr("100")) + assert.equal(balance.quoteBalance, decimalStr("0")) }) it("lp claim", async () => { diff --git a/test/utils/Contracts.ts b/test/utils/Contracts.ts index 7a3a01b..d89d28c 100644 --- a/test/utils/Contracts.ts +++ b/test/utils/Contracts.ts @@ -22,6 +22,7 @@ const Uniswap = require(`${jsonPath}UniswapV2Pair.json`) const UniswapArbitrageur = require(`${jsonPath}UniswapArbitrageur.json`) const DODOToken = require(`${jsonPath}DODOToken.json`) const DODOMine = require(`${jsonPath}DODOMine.json`) +const DODOMineReader = require(`${jsonPath}DODOMineReader.json`) const LockedTokenVault = require(`${jsonPath}LockedTokenVault.json`) import { getDefaultWeb3 } from './EVM'; @@ -41,6 +42,7 @@ export const UNISWAP_ARBITRAGEUR_CONTRACT_NAME = "UniswapArbitrageur" export const DODO_TOKEN_CONTRACT_NAME = "DODOToken" export const LOCKED_TOKEN_VAULT_CONTRACT_NAME = "LockedTokenVault" export const DODO_MINE_NAME = "DODOMine" +export const DODO_MINE_READER_NAME = "DODOMineReader" var contractMap: { [name: string]: any } = {} contractMap[CLONE_FACTORY_CONTRACT_NAME] = CloneFactory @@ -56,6 +58,7 @@ contractMap[UNISWAP_ARBITRAGEUR_CONTRACT_NAME] = UniswapArbitrageur contractMap[DODO_TOKEN_CONTRACT_NAME] = DODOToken contractMap[LOCKED_TOKEN_VAULT_CONTRACT_NAME] = LockedTokenVault contractMap[DODO_MINE_NAME] = DODOMine +contractMap[DODO_MINE_READER_NAME] = DODOMineReader interface ContractJson { abi: any;