This commit is contained in:
owen05
2021-01-08 21:03:41 +08:00
parent 1677eda8ac
commit ff4a51982f
16 changed files with 5 additions and 3974 deletions

View File

@@ -70,11 +70,11 @@ describe("Funding", () => {
assert.equal(await ctx.BASE.methods.balanceOf(ctx.CP.options.address).call(), "2997000000000000000000")
assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), "0")
await ctx.CP.methods.bidderClaim().send(ctx.sendParam(bidder1))
await ctx.CP.methods.bidderClaim(bidder1, "0x").send(ctx.sendParam(bidder1))
assert.equal(await ctx.BASE.methods.balanceOf(bidder1).call(), "999000000000000000000")
assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), "0")
await ctx.CP.methods.bidderClaim().send(ctx.sendParam(bidder2))
await ctx.CP.methods.bidderClaim(bidder2, "0x").send(ctx.sendParam(bidder2))
assert.equal(await ctx.BASE.methods.balanceOf(bidder2).call(), "1998000000000000000000")
assert.equal(await ctx.QUOTE.methods.balanceOf(bidder2).call(), "0")
@@ -93,11 +93,11 @@ describe("Funding", () => {
assert.equal(await ctx.BASE.methods.balanceOf(ctx.CP.options.address).call(), "5000000000000000000000")
assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), decimalStr("39910"))
await ctx.CP.methods.bidderClaim().send(ctx.sendParam(bidder1))
await ctx.CP.methods.bidderClaim(bidder1, "0x").send(ctx.sendParam(bidder1))
assert.equal(await ctx.BASE.methods.balanceOf(bidder1).call(), "1666666666666666666666")
assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), "13303333333333333333333")
await ctx.CP.methods.bidderClaim().send(ctx.sendParam(bidder2))
await ctx.CP.methods.bidderClaim(bidder2, "0x").send(ctx.sendParam(bidder2))
assert.equal(await ctx.BASE.methods.balanceOf(bidder2).call(), "3333333333333333333333")
assert.equal(await ctx.QUOTE.methods.balanceOf(bidder2).call(), "26606666666666666666666")
})

View File

@@ -1,633 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import * as assert from 'assert';
import { DODOContext, getDODOContext } from './utils/DVMContext';
import { decimalStr } from './utils/Converter';
let lp1: string;
let lp2: string;
let trader: string;
let tempAccount: string;
async function init(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(decimalStr("100"));
tempAccount = ctx.spareAccounts[5];
lp1 = ctx.spareAccounts[0];
lp2 = ctx.spareAccounts[1];
trader = ctx.spareAccounts[2];
await ctx.mintTestToken(lp1, decimalStr("100"), decimalStr("10000"));
await ctx.mintTestToken(lp2, decimalStr("100"), decimalStr("10000"));
await ctx.mintTestToken(trader, decimalStr("100"), decimalStr("10000"));
await ctx.approveDODO(lp1);
await ctx.approveDODO(lp2);
await ctx.approveDODO(trader);
}
describe("Admin", () => {
let snapshotId: string;
let ctx: DODOContext;
before(async () => {
ctx = await getDODOContext();
await init(ctx);
});
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId);
});
describe("Settings", () => {
it("set oracle", async () => {
await ctx.DODO.methods
.setOracle(tempAccount)
.send(ctx.sendParam(ctx.Deployer));
assert.equal(await ctx.DODO.methods._ORACLE_().call(), tempAccount);
});
it("set suprevisor", async () => {
await ctx.DODO.methods
.setSupervisor(tempAccount)
.send(ctx.sendParam(ctx.Deployer));
assert.equal(await ctx.DODO.methods._SUPERVISOR_().call(), tempAccount);
});
it("set maintainer", async () => {
await ctx.DODO.methods
.setMaintainer(tempAccount)
.send(ctx.sendParam(ctx.Deployer));
assert.equal(await ctx.DODO.methods._MAINTAINER_().call(), tempAccount);
});
it("set liquidity provider fee rate", async () => {
await ctx.DODO.methods
.setLiquidityProviderFeeRate(decimalStr("0.01"))
.send(ctx.sendParam(ctx.Deployer));
assert.equal(
await ctx.DODO.methods._LP_FEE_RATE_().call(),
decimalStr("0.01")
);
});
it("set maintainer fee rate", async () => {
await ctx.DODO.methods
.setMaintainerFeeRate(decimalStr("0.01"))
.send(ctx.sendParam(ctx.Deployer));
assert.equal(
await ctx.DODO.methods._MT_FEE_RATE_().call(),
decimalStr("0.01")
);
});
it("set k", async () => {
await ctx.DODO.methods
.setK(decimalStr("0.2"))
.send(ctx.sendParam(ctx.Deployer));
assert.equal(await ctx.DODO.methods._K_().call(), decimalStr("0.2"));
});
it("set gas price limit", async () => {
await ctx.DODO.methods
.setGasPriceLimit(decimalStr("100"))
.send(ctx.sendParam(ctx.Deployer));
assert.equal(
await ctx.DODO.methods._GAS_PRICE_LIMIT_().call(),
decimalStr("100")
);
});
});
describe("Controls", () => {
it("control flow", async () => {
await ctx.DODO.methods
.disableBaseDeposit()
.send(ctx.sendParam(ctx.Supervisor));
await assert.rejects(
ctx.DODO.methods.depositBase(decimalStr("10")).send(ctx.sendParam(lp1)),
/DEPOSIT_BASE_NOT_ALLOWED/
);
await ctx.DODO.methods
.enableBaseDeposit()
.send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
decimalStr("10")
);
await ctx.DODO.methods
.disableQuoteDeposit()
.send(ctx.sendParam(ctx.Supervisor));
await assert.rejects(
ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1)),
/DEPOSIT_QUOTE_NOT_ALLOWED/
);
await ctx.DODO.methods
.enableQuoteDeposit()
.send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods
.depositQuote(decimalStr("10"))
.send(ctx.sendParam(lp1));
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
decimalStr("10")
);
await ctx.DODO.methods
.disableTrading()
.send(ctx.sendParam(ctx.Supervisor));
await assert.rejects(
ctx.DODO.methods
.buyBaseToken(decimalStr("1"), decimalStr("200"), "0x")
.send(ctx.sendParam(trader)),
/TRADE_NOT_ALLOWED/
);
await ctx.DODO.methods.enableTrading().send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods
.buyBaseToken(decimalStr("1"), decimalStr("200"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("101")
);
});
it("control flow premission", async () => {
await assert.rejects(
ctx.DODO.methods.setGasPriceLimit("1").send(ctx.sendParam(trader)),
/NOT_SUPERVISOR_OR_OWNER/
);
await assert.rejects(
ctx.DODO.methods.disableTrading().send(ctx.sendParam(trader)),
/NOT_SUPERVISOR_OR_OWNER/
);
await assert.rejects(
ctx.DODO.methods.disableQuoteDeposit().send(ctx.sendParam(trader)),
/NOT_SUPERVISOR_OR_OWNER/
);
await assert.rejects(
ctx.DODO.methods.disableBaseDeposit().send(ctx.sendParam(trader)),
/NOT_SUPERVISOR_OR_OWNER/
);
await assert.rejects(
ctx.DODO.methods.disableBuying().send(ctx.sendParam(trader)),
/NOT_SUPERVISOR_OR_OWNER/
);
await assert.rejects(
ctx.DODO.methods.disableSelling().send(ctx.sendParam(trader)),
/NOT_SUPERVISOR_OR_OWNER/
);
await assert.rejects(
ctx.DODO.methods.setOracle(trader).send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods.setSupervisor(trader).send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods.setMaintainer(trader).send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods
.setLiquidityProviderFeeRate(decimalStr("0.1"))
.send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods
.setMaintainerFeeRate(decimalStr("0.1"))
.send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods.setK(decimalStr("0.1")).send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods.enableTrading().send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods.enableQuoteDeposit().send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods.enableBaseDeposit().send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods.enableBuying().send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods.enableSelling().send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods
.setBaseBalanceLimit(decimalStr("0"))
.send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods
.setQuoteBalanceLimit(decimalStr("0"))
.send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods.enableTrading().send(ctx.sendParam(trader)),
/NOT_OWNER/
);
});
it("advanced controls", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.disableBuying()
.send(ctx.sendParam(ctx.Supervisor));
await assert.rejects(
ctx.DODO.methods
.buyBaseToken(decimalStr("1"), decimalStr("200"), "0x")
.send(ctx.sendParam(trader)),
/BUYING_NOT_ALLOWED/
);
await ctx.DODO.methods.enableBuying().send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods
.disableSelling()
.send(ctx.sendParam(ctx.Supervisor));
await assert.rejects(
ctx.DODO.methods
.sellBaseToken(decimalStr("1"), decimalStr("200"), "0x")
.send(ctx.sendParam(trader)),
/SELLING_NOT_ALLOWED/
);
await ctx.DODO.methods.enableSelling().send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods
.setBaseBalanceLimit(decimalStr("0"))
.send(ctx.sendParam(ctx.Deployer));
await assert.rejects(
ctx.DODO.methods
.depositBase(decimalStr("1000"))
.send(ctx.sendParam(lp1)),
/BASE_BALANCE_LIMIT_EXCEEDED/
);
await ctx.DODO.methods
.setQuoteBalanceLimit(decimalStr("0"))
.send(ctx.sendParam(ctx.Deployer));
await assert.rejects(
ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1)),
/QUOTE_BALANCE_LIMIT_EXCEEDED/
);
});
});
describe("Final settlement", () => {
it("final settlement when R is ONE", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.finalSettlement()
.send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods.claimAssets().send(ctx.sendParam(lp1));
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
decimalStr("100")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
decimalStr("10000")
);
});
it("final settlement when R is ABOVE ONE", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.finalSettlement()
.send(ctx.sendParam(ctx.Deployer));
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "0");
await ctx.DODO.methods.claimAssets().send(ctx.sendParam(lp1));
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
decimalStr("94.995")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
"10551951805416248746110"
);
});
it("final settlement when R is BELOW ONE", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.sellBaseToken(decimalStr("5"), decimalStr("100"), "0x")
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.finalSettlement()
.send(ctx.sendParam(ctx.Deployer));
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "0");
await ctx.DODO.methods.claimAssets().send(ctx.sendParam(lp1));
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
decimalStr("105")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
"9540265973590798352835"
);
});
it("final settlement when only deposit base", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.finalSettlement()
.send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods.claimAssets().send(ctx.sendParam(lp1));
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
decimalStr("100")
);
});
it("final settlement when only deposit quote", async () => {
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.finalSettlement()
.send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods.claimAssets().send(ctx.sendParam(lp1));
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
decimalStr("10000")
);
});
it("final settlement revert cases", async () => {
await assert.rejects(
ctx.DODO.methods.claimAssets().send(ctx.sendParam(lp1)),
/DODO_NOT_CLOSED/
);
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("500"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.finalSettlement()
.send(ctx.sendParam(ctx.Deployer));
await assert.rejects(
ctx.DODO.methods.finalSettlement().send(ctx.sendParam(ctx.Deployer)),
/DODO_CLOSED/
);
await assert.rejects(
ctx.DODO.methods.withdrawAllBase().send(ctx.sendParam(lp1)),
/DODO_CLOSED/
)
await assert.rejects(
ctx.DODO.methods.withdrawAllQuote().send(ctx.sendParam(lp1)),
/DODO_CLOSED/
)
await ctx.DODO.methods.claimAssets().send(ctx.sendParam(lp2));
await assert.rejects(
ctx.DODO.methods.claimAssets().send(ctx.sendParam(lp2)),
/ALREADY_CLAIMED/
);
await assert.rejects(
ctx.DODO.methods.enableQuoteDeposit().send(ctx.sendParam(ctx.Deployer)),
/DODO_CLOSED/
);
await assert.rejects(
ctx.DODO.methods.enableBaseDeposit().send(ctx.sendParam(ctx.Deployer)),
/DODO_CLOSED/
);
await assert.rejects(
ctx.DODO.methods.enableTrading().send(ctx.sendParam(ctx.Deployer)),
/DODO_CLOSED/
);
await assert.rejects(
ctx.DODO.methods.enableBuying().send(ctx.sendParam(ctx.Deployer)),
/DODO_CLOSED/
);
await assert.rejects(
ctx.DODO.methods.enableSelling().send(ctx.sendParam(ctx.Deployer)),
/DODO_CLOSED/
);
});
});
describe("donate", () => {
it("donate quote & base token", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositBase(decimalStr("20"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("2000"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.donateBaseToken(decimalStr("2"))
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.donateQuoteToken(decimalStr("500"))
.send(ctx.sendParam(trader));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"10666666666666666666"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
"1166666666666666666666"
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp2).call(),
"21333333333333333333"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp2).call(),
"2333333333333333333333"
);
await ctx.DODO.methods.withdrawAllBase().send(ctx.sendParam(lp1));
await ctx.DODO.methods.withdrawAllBase().send(ctx.sendParam(lp2));
await ctx.DODO.methods.withdrawAllQuote().send(ctx.sendParam(lp1));
await ctx.DODO.methods.withdrawAllQuote().send(ctx.sendParam(lp2));
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
"100666666666666666666"
);
assert.equal(
await ctx.BASE.methods.balanceOf(lp2).call(),
"101333333333333333334"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
"10166666666666666666666"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp2).call(),
"10333333333333333333334"
);
});
});
describe("retrieve", () => {
it("retrieve base token", async () => {
await ctx.BASE.methods
.transfer(ctx.DODO.options.address, decimalStr("1"))
.send(ctx.sendParam(trader));
await assert.rejects(
ctx.DODO.methods
.retrieve(ctx.BASE.options.address, decimalStr("1"))
.send(ctx.sendParam(trader)),
/NOT_OWNER/
);
await assert.rejects(
ctx.DODO.methods
.retrieve(ctx.BASE.options.address, decimalStr("2"))
.send(ctx.sendParam(ctx.Deployer)),
/DODO_BASE_BALANCE_NOT_ENOUGH/
);
await ctx.DODO.methods
.retrieve(ctx.BASE.options.address, decimalStr("1"))
.send(ctx.sendParam(ctx.Deployer));
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Deployer).call(),
decimalStr("1")
);
});
it("retrieve quote token", async () => {
await ctx.QUOTE.methods
.transfer(ctx.DODO.options.address, decimalStr("1"))
.send(ctx.sendParam(trader));
await assert.rejects(
ctx.DODO.methods
.retrieve(ctx.QUOTE.options.address, decimalStr("2"))
.send(ctx.sendParam(ctx.Deployer)),
/DODO_QUOTE_BALANCE_NOT_ENOUGH/
);
await ctx.DODO.methods
.retrieve(ctx.QUOTE.options.address, decimalStr("1"))
.send(ctx.sendParam(ctx.Deployer));
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Deployer).call(),
decimalStr("1")
);
});
});
describe("revert cases", () => {
it("k revert cases", async () => {
await assert.rejects(
ctx.DODO.methods
.setK(decimalStr("1"))
.send(ctx.sendParam(ctx.Deployer)),
/K>=1/
);
await assert.rejects(
ctx.DODO.methods
.setK(decimalStr("0"))
.send(ctx.sendParam(ctx.Deployer)),
/K=0/
);
});
it("fee revert cases", async () => {
await assert.rejects(
ctx.DODO.methods
.setLiquidityProviderFeeRate(decimalStr("0.999"))
.send(ctx.sendParam(ctx.Deployer)),
/FEE_RATE>=1/
);
await assert.rejects(
ctx.DODO.methods
.setMaintainerFeeRate(decimalStr("0.998"))
.send(ctx.sendParam(ctx.Deployer)),
/FEE_RATE>=1/
);
});
});
});

View File

@@ -1,157 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import { DODOContext, getDODOContext } from './utils/DVMContext';
import { decimalStr, gweiStr } from './utils/Converter';
import BigNumber from "bignumber.js";
import * as assert from "assert"
let lp1: string
let lp2: string
let trader: string
let hacker: string
async function init(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(decimalStr("100"))
lp1 = ctx.spareAccounts[0]
lp2 = ctx.spareAccounts[1]
trader = ctx.spareAccounts[2]
hacker = ctx.spareAccounts[3]
await ctx.mintTestToken(lp1, decimalStr("100"), decimalStr("10000"))
await ctx.mintTestToken(lp2, decimalStr("100"), decimalStr("10000"))
await ctx.mintTestToken(trader, decimalStr("100"), decimalStr("10000"))
await ctx.mintTestToken(hacker, decimalStr("10000"), decimalStr("1000000"))
await ctx.approveDODO(lp1)
await ctx.approveDODO(lp2)
await ctx.approveDODO(trader)
await ctx.approveDODO(hacker)
}
describe("Attacks", () => {
let snapshotId: string
let ctx: DODOContext
before(async () => {
ctx = await getDODOContext()
await init(ctx);
})
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId)
});
describe("Price offset attack", () => {
/*
attack describe:
1. hacker deposit a great number of base token
2. hacker buy base token
3. hacker withdraw a great number of base token
4. hacker sell or buy base token to finish the arbitrage loop
expected:
1. hacker won't earn any quote token or sell base token with price better than what dodo provides
2. quote token lp and base token lp have no loss
Same in quote direction
*/
it("attack on base token", async () => {
await ctx.DODO.methods.depositBase(decimalStr("10")).send(ctx.sendParam(lp1))
await ctx.DODO.methods.depositQuote(decimalStr("1000")).send(ctx.sendParam(lp1))
let hackerInitBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(hacker).call())
let hackerInitQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(hacker).call())
// attack step 1
await ctx.DODO.methods.depositBase(decimalStr("5000")).send(ctx.sendParam(hacker))
// attack step 2
await ctx.DODO.methods.buyBaseToken(decimalStr("9.5"), decimalStr("2000"), "0x").send(ctx.sendParam(hacker))
// attack step 3
await ctx.DODO.methods.withdrawBase(decimalStr("5000")).send(ctx.sendParam(hacker))
// attack step 4
let hackerTempBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(hacker).call())
if (hackerTempBaseBalance.isGreaterThan(hackerInitBaseBalance)) {
await ctx.DODO.methods.sellBaseToken(hackerTempBaseBalance.minus(hackerInitBaseBalance).toString(), "0", "0x").send(ctx.sendParam(hacker))
} else {
await ctx.DODO.methods.buyBaseToken(hackerInitBaseBalance.minus(hackerTempBaseBalance).toString(), decimalStr("5000"), "0x").send(ctx.sendParam(hacker))
}
// expected hacker no profit
let hackerBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(hacker).call())
let hackerQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(hacker).call())
assert.ok(hackerBaseBalance.isLessThanOrEqualTo(hackerInitBaseBalance))
assert.ok(hackerQuoteBalance.isLessThanOrEqualTo(hackerInitQuoteBalance))
// expected lp no loss
let lpBaseBalance = new BigNumber(await ctx.DODO.methods.getLpBaseBalance(lp1).call())
let lpQuoteBalance = new BigNumber(await ctx.DODO.methods.getLpQuoteBalance(lp1).call())
assert.ok(lpBaseBalance.isGreaterThanOrEqualTo(decimalStr("10")))
assert.ok(lpQuoteBalance.isGreaterThanOrEqualTo(decimalStr("1000")))
})
it("attack on quote token", async () => {
await ctx.DODO.methods.depositBase(decimalStr("10")).send(ctx.sendParam(lp1))
await ctx.DODO.methods.depositQuote(decimalStr("1000")).send(ctx.sendParam(lp1))
let hackerInitBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(hacker).call())
let hackerInitQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(hacker).call())
// attack step 1
await ctx.DODO.methods.depositQuote(decimalStr("100000")).send(ctx.sendParam(hacker))
// attack step 2
await ctx.DODO.methods.sellBaseToken(decimalStr("9"), decimalStr("500"), "0x").send(ctx.sendParam(hacker))
// attack step 3
await ctx.DODO.methods.withdrawQuote(decimalStr("100000")).send(ctx.sendParam(hacker))
// attack step 4
let hackerTempBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(hacker).call())
if (hackerTempBaseBalance.isGreaterThan(hackerInitBaseBalance)) {
await ctx.DODO.methods.sellBaseToken(hackerTempBaseBalance.minus(hackerInitBaseBalance).toString(), "0", "0x").send(ctx.sendParam(hacker))
} else {
await ctx.DODO.methods.buyBaseToken(hackerInitBaseBalance.minus(hackerTempBaseBalance).toString(), decimalStr("5000"), "0x").send(ctx.sendParam(hacker))
}
// expected hacker no profit
let hackerBaseBalance = new BigNumber(await ctx.BASE.methods.balanceOf(hacker).call())
let hackerQuoteBalance = new BigNumber(await ctx.QUOTE.methods.balanceOf(hacker).call())
assert.ok(hackerBaseBalance.isLessThanOrEqualTo(hackerInitBaseBalance))
assert.ok(hackerQuoteBalance.isLessThanOrEqualTo(hackerInitQuoteBalance))
// expected lp no loss
let lpBaseBalance = new BigNumber(await ctx.DODO.methods.getLpBaseBalance(lp1).call())
let lpQuoteBalance = new BigNumber(await ctx.DODO.methods.getLpQuoteBalance(lp1).call())
assert.ok(lpBaseBalance.isGreaterThanOrEqualTo(decimalStr("10")))
assert.ok(lpQuoteBalance.isGreaterThanOrEqualTo(decimalStr("1000")))
})
})
describe("Front run attack", () => {
/*
attack describe:
hacker tries to front run oracle updating by sending tx with higher gas price
expected:
revert tx
*/
it("front run", async () => {
await ctx.DODO.methods.depositBase(decimalStr("10")).send(ctx.sendParam(lp1))
await ctx.DODO.methods.depositQuote(decimalStr("1000")).send(ctx.sendParam(lp1))
await assert.rejects(
ctx.DODO.methods.buyBaseToken(decimalStr("1"), decimalStr("200"), "0x").send({ from: trader, gas: 300000, gasPrice: gweiStr("200") }), /GAS_PRICE_EXCEED/
)
await assert.rejects(
ctx.DODO.methods.sellBaseToken(decimalStr("1"), decimalStr("200"), "0x").send({ from: trader, gas: 300000, gasPrice: gweiStr("200") }), /GAS_PRICE_EXCEED/
)
})
})
})

View File

@@ -1,233 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import * as assert from 'assert';
import { BigNumber } from 'bignumber.js';
import { TransactionReceipt } from 'web3-core';
import { Contract } from 'web3-eth-contract';
import {
DefaultDODOContextInitConfig,
DODOContext,
getDODOContext,
} from './utils/DVMContext';
import * as contracts from './utils/Contracts';
import { decimalStr, MAX_UINT256 } from './utils/Converter';
import { logGas } from './utils/Log';
let lp: string;
let trader: string;
let DODOEthProxy: Contract;
async function init(ctx: DODOContext): Promise<void> {
// switch ctx to eth proxy mode
const WETH = await contracts.newContract(contracts.WETH_CONTRACT_NAME);
await ctx.DODOZoo.methods
.breedDODO(
ctx.Maintainer,
WETH.options.address,
ctx.QUOTE.options.address,
ctx.ORACLE.options.address,
DefaultDODOContextInitConfig.lpFeeRate,
DefaultDODOContextInitConfig.mtFeeRate,
DefaultDODOContextInitConfig.k,
DefaultDODOContextInitConfig.gasPriceLimit
)
.send(ctx.sendParam(ctx.Deployer));
ctx.DODO = contracts.getContractWithAddress(
contracts.DODO_CONTRACT_NAME,
await ctx.DODOZoo.methods
.getDODO(WETH.options.address, ctx.QUOTE.options.address)
.call()
);
await ctx.DODO.methods.enableBaseDeposit().send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods.enableQuoteDeposit().send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods.enableTrading().send(ctx.sendParam(ctx.Deployer));
ctx.BASE = WETH;
DODOEthProxy = await contracts.newContract(
contracts.DODO_ETH_PROXY_CONTRACT_NAME,
[ctx.DODOZoo.options.address, WETH.options.address]
);
// env
lp = ctx.spareAccounts[0];
trader = ctx.spareAccounts[1];
await ctx.setOraclePrice(decimalStr("100"));
await ctx.approveDODO(lp);
await ctx.approveDODO(trader);
await ctx.QUOTE.methods
.mint(lp, decimalStr("1000"))
.send(ctx.sendParam(ctx.Deployer));
await ctx.QUOTE.methods
.mint(trader, decimalStr("1000"))
.send(ctx.sendParam(ctx.Deployer));
await ctx.QUOTE.methods
.approve(DODOEthProxy.options.address, MAX_UINT256)
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp));
}
describe("DODO ETH PROXY", () => {
let snapshotId: string;
let ctx: DODOContext;
before(async () => {
ctx = await getDODOContext();
await init(ctx);
await ctx.QUOTE.methods
.approve(DODOEthProxy.options.address, MAX_UINT256)
.send(ctx.sendParam(trader));
});
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
const depositAmount = "10";
await DODOEthProxy.methods
.depositEthAsBase(decimalStr(depositAmount), ctx.QUOTE.options.address)
.send(ctx.sendParam(lp, depositAmount));
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId);
});
describe("buy&sell eth directly", () => {
it("buy", async () => {
const buyAmount = "1";
await logGas(
DODOEthProxy.methods.buyEthWithToken(
ctx.QUOTE.options.address,
decimalStr(buyAmount),
decimalStr("200")
),
ctx.sendParam(trader),
"buy ETH with token directly"
);
assert.strictEqual(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("8.999")
);
assert.strictEqual(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"898581839502056240973"
);
});
it("sell", async () => {
const sellAmount = "1";
await logGas(
DODOEthProxy.methods.sellEthToToken(
ctx.QUOTE.options.address,
decimalStr(sellAmount),
decimalStr("50")
),
ctx.sendParam(trader, sellAmount),
"sell ETH to token directly"
);
assert.strictEqual(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("11")
);
assert.strictEqual(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"1098617454226610630663"
);
});
});
describe("withdraw eth directly", () => {
it("withdraw", async () => {
const withdrawAmount = decimalStr("5");
const baseLpTokenAddress = await ctx.DODO.methods
._BASE_CAPITAL_TOKEN_()
.call();
const baseLpToken = contracts.getContractWithAddress(
contracts.TEST_ERC20_CONTRACT_NAME,
baseLpTokenAddress
);
await baseLpToken.methods
.approve(DODOEthProxy.options.address, MAX_UINT256)
.send(ctx.sendParam(lp));
const lpEthBalanceBefore = await ctx.Web3.eth.getBalance(lp);
const txReceipt: TransactionReceipt = await DODOEthProxy.methods
.withdrawEthAsBase(withdrawAmount, ctx.QUOTE.options.address)
.send(ctx.sendParam(lp));
assert.strictEqual(
await ctx.DODO.methods.getLpBaseBalance(lp).call(),
withdrawAmount
);
const tx = await ctx.Web3.eth.getTransaction(txReceipt.transactionHash);
const ethSpentOnGas = new BigNumber(tx.gasPrice).times(txReceipt.gasUsed);
const lpEthBalanceAfter = await ctx.Web3.eth.getBalance(lp);
assert.ok(
new BigNumber(lpEthBalanceBefore)
.plus(withdrawAmount)
.minus(ethSpentOnGas)
.eq(lpEthBalanceAfter)
);
});
it("withdraw all", async () => {
const withdrawAmount = decimalStr("10");
const baseLpTokenAddress = await ctx.DODO.methods
._BASE_CAPITAL_TOKEN_()
.call();
const baseLpToken = contracts.getContractWithAddress(
contracts.TEST_ERC20_CONTRACT_NAME,
baseLpTokenAddress
);
await baseLpToken.methods
.approve(DODOEthProxy.options.address, MAX_UINT256)
.send(ctx.sendParam(lp));
const lpEthBalanceBefore = await ctx.Web3.eth.getBalance(lp);
const txReceipt: TransactionReceipt = await DODOEthProxy.methods
.withdrawAllEthAsBase(ctx.QUOTE.options.address)
.send(ctx.sendParam(lp));
assert.strictEqual(
await ctx.DODO.methods.getLpBaseBalance(lp).call(),
"0"
);
const tx = await ctx.Web3.eth.getTransaction(txReceipt.transactionHash);
const ethSpentOnGas = new BigNumber(tx.gasPrice).times(txReceipt.gasUsed);
const lpEthBalanceAfter = await ctx.Web3.eth.getBalance(lp);
assert.ok(
new BigNumber(lpEthBalanceBefore)
.plus(withdrawAmount)
.minus(ethSpentOnGas)
.eq(lpEthBalanceAfter)
);
});
});
describe("revert cases", () => {
it("value not match", async () => {
await assert.rejects(
DODOEthProxy.methods
.sellEthToToken(
ctx.QUOTE.options.address,
decimalStr("1"),
decimalStr("50")
)
.send(ctx.sendParam(trader, "2")),
/ETH_AMOUNT_NOT_MATCH/
);
await assert.rejects(
DODOEthProxy.methods
.depositEthAsBase(decimalStr("1"), ctx.QUOTE.options.address)
.send(ctx.sendParam(lp, "2")),
/ETH_AMOUNT_NOT_MATCH/
);
});
});
});

View File

@@ -1,247 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import * as assert from 'assert';
import BigNumber from 'bignumber.js';
import { TransactionReceipt } from 'web3-core';
import { Contract } from 'web3-eth-contract';
import {
DefaultDODOContextInitConfig,
DODOContext,
getDODOContext,
} from './utils/DVMContext';
import * as contracts from './utils/Contracts';
import { decimalStr, MAX_UINT256 } from './utils/Converter';
import { logGas } from './utils/Log';
let lp: string;
let trader: string;
let DODOEthProxy: Contract;
async function init(ctx: DODOContext): Promise<void> {
// switch ctx to eth proxy mode
const WETH = await contracts.newContract(contracts.WETH_CONTRACT_NAME);
await ctx.DODOZoo.methods
.breedDODO(
ctx.Maintainer,
ctx.BASE.options.address,
WETH.options.address,
ctx.ORACLE.options.address,
DefaultDODOContextInitConfig.lpFeeRate,
DefaultDODOContextInitConfig.mtFeeRate,
DefaultDODOContextInitConfig.k,
DefaultDODOContextInitConfig.gasPriceLimit
)
.send(ctx.sendParam(ctx.Deployer));
ctx.DODO = contracts.getContractWithAddress(
contracts.DODO_CONTRACT_NAME,
await ctx.DODOZoo.methods
.getDODO(ctx.BASE.options.address, WETH.options.address)
.call()
);
await ctx.DODO.methods.enableBaseDeposit().send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods.enableQuoteDeposit().send(ctx.sendParam(ctx.Deployer));
await ctx.DODO.methods.enableTrading().send(ctx.sendParam(ctx.Deployer));
ctx.QUOTE = WETH;
DODOEthProxy = await contracts.newContract(
contracts.DODO_ETH_PROXY_CONTRACT_NAME,
[ctx.DODOZoo.options.address, WETH.options.address]
);
// env
lp = ctx.spareAccounts[0];
trader = ctx.spareAccounts[1];
await ctx.setOraclePrice(decimalStr("0.01"));
await ctx.approveDODO(lp);
await ctx.approveDODO(trader);
await ctx.BASE.methods
.mint(lp, decimalStr("1000"))
.send(ctx.sendParam(ctx.Deployer));
await ctx.BASE.methods
.mint(trader, decimalStr("1000"))
.send(ctx.sendParam(ctx.Deployer));
await ctx.BASE.methods
.approve(DODOEthProxy.options.address, MAX_UINT256)
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.depositBase(decimalStr("1000"))
.send(ctx.sendParam(lp));
}
describe("DODO ETH PROXY", () => {
let snapshotId: string;
let ctx: DODOContext;
before(async () => {
ctx = await getDODOContext();
await init(ctx);
await ctx.BASE.methods
.approve(DODOEthProxy.options.address, MAX_UINT256)
.send(ctx.sendParam(trader));
});
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
let depositAmount = "10";
await DODOEthProxy.methods
.depositEthAsQuote(decimalStr(depositAmount), ctx.BASE.options.address)
.send(ctx.sendParam(lp, depositAmount));
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId);
});
describe("buy&sell eth directly", () => {
it("buy", async () => {
const maxPayEthAmount = "2.1";
const ethInPoolBefore = decimalStr("10");
const traderEthBalanceBefore = await ctx.Web3.eth.getBalance(trader);
const txReceipt: TransactionReceipt = await logGas(
DODOEthProxy.methods.buyTokenWithEth(
ctx.BASE.options.address,
decimalStr("200"),
decimalStr(maxPayEthAmount)
),
ctx.sendParam(trader, maxPayEthAmount),
"buy token with ETH directly"
);
const ethInPoolAfter = "12056338203652739553";
assert.strictEqual(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
ethInPoolAfter
);
assert.strictEqual(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("1200")
);
const tx = await ctx.Web3.eth.getTransaction(txReceipt.transactionHash);
const ethSpentOnGas = new BigNumber(tx.gasPrice).times(txReceipt.gasUsed);
const traderEthBalanceAfter = await ctx.Web3.eth.getBalance(trader);
const totalEthBefore = new BigNumber(traderEthBalanceBefore).plus(
ethInPoolBefore
);
const totalEthAfter = new BigNumber(traderEthBalanceAfter)
.plus(ethSpentOnGas)
.plus(ethInPoolAfter);
assert.ok(totalEthBefore.eq(totalEthAfter));
});
it("sell", async () => {
const minReceiveEthAmount = "0.45";
await logGas(
DODOEthProxy.methods.sellTokenToEth(
ctx.BASE.options.address,
decimalStr("50"),
decimalStr(minReceiveEthAmount)
),
ctx.sendParam(trader),
"sell token to ETH directly"
);
assert.strictEqual(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"9503598324131652490"
);
assert.strictEqual(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("950")
);
});
});
describe("withdraw eth directly", () => {
it("withdraw", async () => {
const withdrawAmount = decimalStr("5");
const quoteLpTokenAddress = await ctx.DODO.methods
._QUOTE_CAPITAL_TOKEN_()
.call();
const quoteLpToken = contracts.getContractWithAddress(
contracts.TEST_ERC20_CONTRACT_NAME,
quoteLpTokenAddress
);
await quoteLpToken.methods
.approve(DODOEthProxy.options.address, MAX_UINT256)
.send(ctx.sendParam(lp));
const lpEthBalanceBefore = await ctx.Web3.eth.getBalance(lp);
const txReceipt: TransactionReceipt = await DODOEthProxy.methods
.withdrawEthAsQuote(withdrawAmount, ctx.BASE.options.address)
.send(ctx.sendParam(lp));
assert.strictEqual(
await ctx.DODO.methods.getLpQuoteBalance(lp).call(),
withdrawAmount
);
const tx = await ctx.Web3.eth.getTransaction(txReceipt.transactionHash);
const ethSpentOnGas = new BigNumber(tx.gasPrice).times(txReceipt.gasUsed);
const lpEthBalanceAfter = await ctx.Web3.eth.getBalance(lp);
assert.ok(
new BigNumber(lpEthBalanceBefore)
.plus(withdrawAmount)
.minus(ethSpentOnGas)
.eq(lpEthBalanceAfter)
);
});
it("withdraw all", async () => {
const withdrawAmount = decimalStr("10");
const quoteLpTokenAddress = await ctx.DODO.methods
._QUOTE_CAPITAL_TOKEN_()
.call();
const quoteLpToken = contracts.getContractWithAddress(
contracts.TEST_ERC20_CONTRACT_NAME,
quoteLpTokenAddress
);
await quoteLpToken.methods
.approve(DODOEthProxy.options.address, MAX_UINT256)
.send(ctx.sendParam(lp));
const lpEthBalanceBefore = await ctx.Web3.eth.getBalance(lp);
const txReceipt: TransactionReceipt = await DODOEthProxy.methods
.withdrawAllEthAsQuote(ctx.BASE.options.address)
.send(ctx.sendParam(lp));
assert.strictEqual(
await ctx.DODO.methods.getLpQuoteBalance(lp).call(),
"0"
);
const tx = await ctx.Web3.eth.getTransaction(txReceipt.transactionHash);
const ethSpentOnGas = new BigNumber(tx.gasPrice).times(txReceipt.gasUsed);
const lpEthBalanceAfter = await ctx.Web3.eth.getBalance(lp);
assert.ok(
new BigNumber(lpEthBalanceBefore)
.plus(withdrawAmount)
.minus(ethSpentOnGas)
.eq(lpEthBalanceAfter)
);
});
});
describe("revert cases", () => {
it("value not match", async () => {
await assert.rejects(
DODOEthProxy.methods
.buyTokenWithEth(
ctx.BASE.options.address,
decimalStr("50"),
decimalStr("1")
)
.send(ctx.sendParam(trader, "2")),
/ETH_AMOUNT_NOT_MATCH/
);
await assert.rejects(
DODOEthProxy.methods
.depositEthAsQuote(decimalStr("1"), ctx.BASE.options.address)
.send(ctx.sendParam(lp, "2")),
/ETH_AMOUNT_NOT_MATCH/
);
});
});
});

View File

@@ -1,91 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import { DODOContext, getDODOContext } from './utils/DVMContext';
import * as assert from "assert"
import { newContract, TEST_ERC20_CONTRACT_NAME, getContractWithAddress, DODO_CONTRACT_NAME } from './utils/Contracts';
async function init(ctx: DODOContext): Promise<void> { }
describe("DODO ZOO", () => {
let snapshotId: string
let ctx: DODOContext
before(async () => {
ctx = await getDODOContext()
await init(ctx);
})
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId)
});
describe("Breed new dodo", () => {
it("could not deploy the same dodo", async () => {
await assert.rejects(
ctx.DODOZoo.methods.breedDODO(ctx.Maintainer, ctx.BASE.options.address, ctx.QUOTE.options.address, ctx.ORACLE.options.address, "0", "0", "1", "0").send(ctx.sendParam(ctx.Deployer)),
/DODO_REGISTERED/
)
await assert.rejects(
ctx.DODOZoo.methods.breedDODO(ctx.Maintainer, ctx.QUOTE.options.address, ctx.BASE.options.address, ctx.ORACLE.options.address, "0", "0", "1", "0").send(ctx.sendParam(ctx.Deployer)),
/DODO_REGISTERED/
)
})
it("breed new dodo", async () => {
let newBase = await newContract(TEST_ERC20_CONTRACT_NAME, ["AnotherBase", 18])
let newQuote = await newContract(TEST_ERC20_CONTRACT_NAME, ["AnotherQuote", 18])
await assert.rejects(
ctx.DODOZoo.methods.breedDODO(ctx.Maintainer, newBase.options.address, newQuote.options.address, ctx.ORACLE.options.address, "0", "0", "1", "0").send(ctx.sendParam(ctx.Maintainer)),
/NOT_OWNER/
)
await ctx.DODOZoo.methods.breedDODO(ctx.Maintainer, newBase.options.address, newQuote.options.address, ctx.ORACLE.options.address, "0", "0", "1", "0").send(ctx.sendParam(ctx.Deployer))
let newDODO = getContractWithAddress(DODO_CONTRACT_NAME, await ctx.DODOZoo.methods.getDODO(newBase.options.address, newQuote.options.address).call())
assert.equal(await newDODO.methods._BASE_TOKEN_().call(), newBase.options.address)
assert.equal(await newDODO.methods._QUOTE_TOKEN_().call(), newQuote.options.address)
// could not init twice
await assert.rejects(
newDODO.methods.init(ctx.Deployer, ctx.Supervisor, ctx.Maintainer, ctx.QUOTE.options.address, ctx.BASE.options.address, ctx.ORACLE.options.address, "0", "0", "1", "0").send(ctx.sendParam(ctx.Deployer)),
/DODO_INITIALIZED/
)
// console.log(await ctx.DODOZoo.methods.getDODOs().call())
})
// it.only("remove dodo", async () => {
// console.log(await ctx.DODOZoo.methods.getDODOs().call())
// await ctx.DODOZoo.methods.removeDODO(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer))
// console.log(await ctx.DODOZoo.methods.getDODO(ctx.BASE.options.address, ctx.QUOTE.options.address).call())
// console.log(await ctx.DODOZoo.methods.getDODOs().call())
// })
it("dodo register control flow", async () => {
await ctx.DODOZoo.methods.removeDODO(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer))
assert.equal(await ctx.DODOZoo.methods.getDODO(ctx.BASE.options.address, ctx.QUOTE.options.address).call(), "0x0000000000000000000000000000000000000000")
await assert.rejects(
ctx.DODOZoo.methods.removeDODO(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer)),
/DODO_NOT_REGISTERED/
)
await ctx.DODOZoo.methods.addDODO(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer))
assert.equal(await ctx.DODOZoo.methods.getDODO(ctx.BASE.options.address, ctx.QUOTE.options.address).call(), ctx.DODO.options.address)
await assert.rejects(
ctx.DODOZoo.methods.addDODO(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer)),
/DODO_REGISTERED/
)
})
})
})

View File

@@ -1,925 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import * as assert from 'assert';
import { DODOContext, getDODOContext } from './utils/DVMContext';
import { decimalStr } from './utils/Converter';
import { logGas } from './utils/Log';
let lp1: string;
let lp2: string;
let trader: string;
async function init(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(decimalStr("100"));
lp1 = ctx.spareAccounts[0];
lp2 = ctx.spareAccounts[1];
trader = ctx.spareAccounts[2];
await ctx.mintTestToken(lp1, decimalStr("100"), decimalStr("10000"));
await ctx.mintTestToken(lp2, decimalStr("100"), decimalStr("10000"));
await ctx.mintTestToken(trader, decimalStr("100"), decimalStr("10000"));
await ctx.approveDODO(lp1);
await ctx.approveDODO(lp2);
await ctx.approveDODO(trader);
}
describe("LiquidityProvider", () => {
let snapshotId: string;
let ctx: DODOContext;
before(async () => {
ctx = await getDODOContext();
await init(ctx);
});
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId);
});
describe("R equals to ONE", () => {
it("multi lp deposit & withdraw", async () => {
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
decimalStr("0")
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
decimalStr("0")
);
await logGas(
ctx.DODO.methods.depositBase(decimalStr("10")),
ctx.sendParam(lp1),
"deposit base"
);
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
decimalStr("90")
);
await logGas(
ctx.DODO.methods.depositQuote(decimalStr("1000")),
ctx.sendParam(lp1),
"deposit quote"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
decimalStr("9000")
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
decimalStr("10")
);
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("10")
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
decimalStr("1000")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
decimalStr("1000")
);
await ctx.DODO.methods
.depositBase(decimalStr("3"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.depositQuote(decimalStr("70"))
.send(ctx.sendParam(lp2));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
decimalStr("10")
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
decimalStr("1000")
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp2).call(),
decimalStr("3")
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp2).call(),
decimalStr("70")
);
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("13")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
decimalStr("1070")
);
await ctx.DODO.methods
.withdrawBase(decimalStr("5"))
.send(ctx.sendParam(lp1));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
decimalStr("5")
);
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
decimalStr("95")
);
await ctx.DODO.methods
.withdrawQuote(decimalStr("100"))
.send(ctx.sendParam(lp1));
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
decimalStr("900")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
decimalStr("9100")
);
await ctx.DODO.methods.withdrawAllBase().send(ctx.sendParam(lp1));
assert.equal(await ctx.DODO.methods.getLpBaseBalance(lp1).call(), "0");
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
decimalStr("100")
);
await ctx.DODO.methods.withdrawAllQuote().send(ctx.sendParam(lp1));
assert.equal(await ctx.DODO.methods.getLpQuoteBalance(lp1).call(), "0");
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
decimalStr("10000")
);
});
});
describe("R is ABOVE ONE", () => {
it("deposit", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"10010841132009222923"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
decimalStr("1000")
);
await ctx.DODO.methods
.depositBase(decimalStr("5"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.depositQuote(decimalStr("100"))
.send(ctx.sendParam(lp2));
// lp1 & lp2 would both have profit because the curve becomes flatter
// but the withdraw penalty is greater than this free profit
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"10163234422929069723"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
decimalStr("1000")
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp2).call(),
"5076114129127759292"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp2).call(),
decimalStr("100")
);
assert.equal(
await ctx.DODO.methods.getWithdrawBasePenalty(decimalStr("5")).call(),
"228507420047606093"
);
assert.equal(
await ctx.DODO.methods
.getWithdrawQuotePenalty(decimalStr("100"))
.call(),
"0"
);
});
it("withdraw", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.DODO.methods.getWithdrawBasePenalty(decimalStr("4")).call(),
"1065045389392391665"
);
assert.equal(
await ctx.DODO.methods
.getWithdrawQuotePenalty(decimalStr("100"))
.call(),
"0"
);
await ctx.DODO.methods
.withdrawBase(decimalStr("4"))
.send(ctx.sendParam(lp1));
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
"92934954610607608335"
);
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
"2060045389392391665"
);
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
"7075045389392391665"
);
await ctx.DODO.methods
.withdrawQuote(decimalStr("100"))
.send(ctx.sendParam(lp1));
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
decimalStr("9100")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"1451951805416248746119"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
decimalStr("900")
);
});
});
describe("R is BELOW ONE", () => {
it("deposit", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.sellBaseToken(decimalStr("5"), decimalStr("200"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
decimalStr("10")
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
"1000978629616255276996"
);
await ctx.DODO.methods
.depositQuote(decimalStr("500"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.depositBase(decimalStr("5"))
.send(ctx.sendParam(lp2));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
decimalStr("10")
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
"1012529270910521756641"
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp2).call(),
decimalStr("5")
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp2).call(),
"505769674273013522654"
);
assert.equal(
await ctx.DODO.methods.getWithdrawBasePenalty(decimalStr("5")).call(),
"0"
);
assert.equal(
await ctx.DODO.methods
.getWithdrawQuotePenalty(decimalStr("500"))
.call(),
"17320315567280002300"
);
});
it("withdraw", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.sellBaseToken(decimalStr("5"), decimalStr("200"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.DODO.methods.getWithdrawBasePenalty(decimalStr("4")).call(),
"0"
);
assert.equal(
await ctx.DODO.methods
.getWithdrawQuotePenalty(decimalStr("100"))
.call(),
"7389428846238900753"
);
await ctx.DODO.methods
.withdrawQuote(decimalStr("100"))
.send(ctx.sendParam(lp1));
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
"9092610571153761099247"
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"447655402437037253588"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
"908310739520405637520"
);
await ctx.DODO.methods
.withdrawBase(decimalStr("4"))
.send(ctx.sendParam(lp1));
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
decimalStr("94")
);
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("11")
);
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
decimalStr("6")
);
});
});
describe("Oracle changes", () => {
it("base side lp don't has pnl when R is BELOW ONE", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.sellBaseToken(decimalStr("5"), decimalStr("200"), "0x")
.send(ctx.sendParam(trader));
await ctx.setOraclePrice(decimalStr("80"));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
decimalStr("10")
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
"914362409397559037208"
);
await ctx.setOraclePrice(decimalStr("120"));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
decimalStr("10")
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
"1085284653936129406317"
);
});
it("quote side lp don't has pnl when R is ABOVE ONE", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("600"), "0x")
.send(ctx.sendParam(trader));
await ctx.setOraclePrice(decimalStr("80"));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"11138732839027528597"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
decimalStr("1000")
);
await ctx.setOraclePrice(decimalStr("120"));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"9234731968726215588"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
decimalStr("1000")
);
});
});
describe("Transfer lp token", () => {
it("transfer", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.BaseCapital.methods
.transfer(lp2, decimalStr("5"))
.send(ctx.sendParam(lp1));
await ctx.QuoteCapital.methods
.transfer(lp2, decimalStr("5"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods.withdrawAllBase().send(ctx.sendParam(lp2));
await ctx.DODO.methods.withdrawAllQuote().send(ctx.sendParam(lp2));
assert.equal(
await ctx.BASE.methods.balanceOf(lp2).call(),
decimalStr("105")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp2).call(),
decimalStr("10005")
);
});
});
describe("Deposit & transfer to other account", () => {
it("base token", async () => {
await ctx.DODO.methods
.depositBaseTo(lp2, decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.withdrawBaseTo(trader, decimalStr("5"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.withdrawAllBaseTo(ctx.Supervisor)
.send(ctx.sendParam(lp2));
assert.equal(
await ctx.BASE.methods.balanceOf(lp1).call(),
decimalStr("90")
);
assert.equal(
await ctx.BASE.methods.balanceOf(lp2).call(),
decimalStr("100")
);
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("105")
);
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Supervisor).call(),
decimalStr("5")
);
});
it("quote token", async () => {
await ctx.DODO.methods
.depositQuoteTo(lp2, decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.withdrawQuoteTo(trader, decimalStr("500"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.withdrawAllQuoteTo(ctx.Supervisor)
.send(ctx.sendParam(lp2));
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp1).call(),
decimalStr("9000")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(lp2).call(),
decimalStr("10000")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
decimalStr("10500")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Supervisor).call(),
decimalStr("500")
);
});
});
describe("Corner cases", () => {
it("single side deposit", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.sellBaseToken("5015841132009222923", decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "0");
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
"10010841132009222923"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
"1103903610832497492"
);
await ctx.DODO.methods.depositQuote("1").send(ctx.sendParam(lp2));
assert.equal(
await ctx.DODO.methods.getQuoteCapitalBalanceOf(lp2).call(),
"1103903610832497493"
);
});
it("single side deposit & lp deposit when R isn't equal to ONE", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
await ctx.DODO.methods.depositQuote("1").send(ctx.sendParam(lp2));
assert.equal(
await ctx.DODO.methods.getQuoteCapitalBalanceOf(lp2).call(),
"1"
);
assert.equal(await ctx.DODO.methods.getLpQuoteBalance(lp2).call(), "1");
});
it("single side deposit (base) & oracle change introduces loss", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
await ctx.setOraclePrice(decimalStr("120"));
await ctx.DODO.methods
.sellBaseToken(decimalStr("4"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.sellBaseToken(decimalStr("1"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "2");
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
"9234731968726215603"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
"1105993618321025490"
);
await ctx.DODO.methods.depositQuote("1").send(ctx.sendParam(lp2));
assert.equal(
await ctx.DODO.methods.getQuoteCapitalBalanceOf(lp2).call(),
"7221653398290521828"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp2).call(),
"7221653398290521884"
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"9234731968726215603"
);
});
it("single side deposit (base) & oracle change introduces profit", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
await ctx.setOraclePrice(decimalStr("80"));
await ctx.DODO.methods
.sellBaseToken(decimalStr("4"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.sellBaseToken(decimalStr("4"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "2");
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
"11138732839027528584"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
"1105408308382702868"
);
await ctx.DODO.methods.depositQuote("1").send(ctx.sendParam(lp2));
assert.equal(
await ctx.DODO.methods.getQuoteCapitalBalanceOf(lp2).call(),
"21553269260529319669"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp2).call(),
"21553269260529319697"
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"11138732839027528584"
);
});
it("single side deposit (quote) & oracle change introduces loss", async () => {
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.sellBaseToken(decimalStr("5"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
await ctx.setOraclePrice(decimalStr("80"));
await ctx.DODO.methods
.buyBaseToken(decimalStr("4"), decimalStr("600"), "0x")
.send(ctx.sendParam(trader));
await ctx.DODO.methods
.buyBaseToken(decimalStr("0.99"), decimalStr("500"), "0x")
.send(ctx.sendParam(trader));
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "1");
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
"9980000000000000"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
"914362409397559035414"
);
await ctx.DODO.methods.depositBase("1").send(ctx.sendParam(lp2));
assert.equal(
await ctx.DODO.methods.getBaseCapitalBalanceOf(lp2).call(),
"10247647352975730"
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp2).call(),
"10247647352975730"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp1).call(),
"914362409397559035414"
);
});
it("deposit and withdraw immediately", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"10010841132009222923"
);
await ctx.DODO.methods
.depositBase(decimalStr("5"))
.send(ctx.sendParam(lp2));
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"10163234422929069723"
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp2).call(),
"5076114129127759292"
);
await ctx.DODO.methods.withdrawAllBase().send(ctx.sendParam(lp2));
assert.equal(
await ctx.BASE.methods.balanceOf(lp2).call(),
"99841132414635941792"
);
assert.equal(
await ctx.DODO.methods.getLpBaseBalance(lp1).call(),
"10182702153814588648"
);
});
});
describe("Revert cases", () => {
it("withdraw base amount exceeds DODO balance", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("5"), decimalStr("1000"), "0x")
.send(ctx.sendParam(trader));
await assert.rejects(
ctx.DODO.methods.withdrawBase(decimalStr("6")).send(ctx.sendParam(lp1)),
/DODO_BASE_BALANCE_NOT_ENOUGH/
);
await assert.rejects(
ctx.DODO.methods.withdrawAllBase().send(ctx.sendParam(lp1)),
/DODO_BASE_BALANCE_NOT_ENOUGH/
);
});
it("withdraw quote amount exceeds DODO balance", async () => {
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.sellBaseToken(decimalStr("5"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
await assert.rejects(
ctx.DODO.methods
.withdrawQuote(decimalStr("600"))
.send(ctx.sendParam(lp1)),
/DODO_QUOTE_BALANCE_NOT_ENOUGH/
);
await assert.rejects(
ctx.DODO.methods.withdrawAllQuote().send(ctx.sendParam(lp1)),
/DODO_QUOTE_BALANCE_NOT_ENOUGH/
);
});
it("withdraw base could not afford penalty", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.buyBaseToken(decimalStr("9"), decimalStr("10000"), "0x")
.send(ctx.sendParam(trader));
await assert.rejects(
ctx.DODO.methods
.withdrawBase(decimalStr("0.5"))
.send(ctx.sendParam(lp1)),
/PENALTY_EXCEED/
);
await assert.rejects(
ctx.DODO.methods.getWithdrawBasePenalty(decimalStr("10")).call(),
/DODO_BASE_BALANCE_NOT_ENOUGH/
);
});
it("withdraw quote could not afford penalty", async () => {
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.sellBaseToken(decimalStr("10"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
await assert.rejects(
ctx.DODO.methods
.withdrawQuote(decimalStr("200"))
.send(ctx.sendParam(lp1)),
/PENALTY_EXCEED/
);
await assert.rejects(
ctx.DODO.methods.getWithdrawQuotePenalty(decimalStr("1000")).call(),
/DODO_QUOTE_BALANCE_NOT_ENOUGH/
);
});
it("withdraw all base could not afford penalty", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("9.5"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositBase(decimalStr("0.5"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.buyBaseToken(decimalStr("9"), decimalStr("10000"), "0x")
.send(ctx.sendParam(trader));
await assert.rejects(
ctx.DODO.methods
.withdrawBase(decimalStr("0.5"))
.send(ctx.sendParam(lp2)),
/PENALTY_EXCEED/
);
});
it("withdraw all quote could not afford penalty", async () => {
await ctx.DODO.methods
.depositQuote(decimalStr("800"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("200"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.sellBaseToken(decimalStr("10"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
await assert.rejects(
ctx.DODO.methods
.withdrawQuote(decimalStr("200"))
.send(ctx.sendParam(lp2)),
/PENALTY_EXCEED/
);
});
it("withdraw amount exceeds lp balance", async () => {
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositBase(decimalStr("10"))
.send(ctx.sendParam(lp2));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp1));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp2));
await assert.rejects(
ctx.DODO.methods
.withdrawBase(decimalStr("11"))
.send(ctx.sendParam(lp1)),
/LP_BASE_CAPITAL_BALANCE_NOT_ENOUGH/
);
await assert.rejects(
ctx.DODO.methods
.withdrawQuote(decimalStr("1100"))
.send(ctx.sendParam(lp1)),
/LP_QUOTE_CAPITAL_BALANCE_NOT_ENOUGH/
);
});
it("withdraw when there is no lp", async () => {
await assert.rejects(
ctx.DODO.methods.withdrawBase(decimalStr("1")).send(ctx.sendParam(lp1)),
/NO_BASE_LP/
);
await assert.rejects(
ctx.DODO.methods
.withdrawQuote(decimalStr("1"))
.send(ctx.sendParam(lp1)),
/NO_QUOTE_LP/
);
});
});
});

View File

@@ -1,94 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import { DODOContext, getDODOContext } from './utils/DVMContext';
import { decimalStr, gweiStr } from './utils/Converter';
import * as assert from "assert"
let lp: string
let trader: string
async function init(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(decimalStr("10"))
lp = ctx.spareAccounts[0]
trader = ctx.spareAccounts[1]
await ctx.approveDODO(lp)
await ctx.approveDODO(trader)
await ctx.mintTestToken(lp, decimalStr("10000"), decimalStr("10000000"))
await ctx.mintTestToken(trader, decimalStr("0"), decimalStr("10000000"))
await ctx.DODO.methods.depositBase(decimalStr("10000")).send(ctx.sendParam(lp))
}
describe("Trader", () => {
let snapshotId: string
let ctx: DODOContext
before(async () => {
let dodoContextInitConfig = {
lpFeeRate: decimalStr("0"),
mtFeeRate: decimalStr("0"),
k: decimalStr("0.99"), // nearly one
gasPriceLimit: gweiStr("100"),
}
ctx = await getDODOContext(dodoContextInitConfig)
await init(ctx);
})
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId)
});
// price change quickly
describe("Trade long tail coin", () => {
it("price discover", async () => {
// 10% depth
// avg price = 11.137
await ctx.DODO.methods.buyBaseToken(decimalStr("1000"), decimalStr("100000"), "0x").send(ctx.sendParam(trader))
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), decimalStr("1000"))
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "9988900000000000000000000")
// 20% depth
// avg price = 12.475
await ctx.DODO.methods.buyBaseToken(decimalStr("1000"), decimalStr("100000"), "0x").send(ctx.sendParam(trader))
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), decimalStr("2000"))
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "9975049999999999999970000")
// 50% depth
// avg price = 19.9
await ctx.DODO.methods.buyBaseToken(decimalStr("3000"), decimalStr("300000"), "0x").send(ctx.sendParam(trader))
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), decimalStr("5000"))
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "9900499999999999999970000")
// 80% depth
// avg price = 49.6
await ctx.DODO.methods.buyBaseToken(decimalStr("3000"), decimalStr("300000"), "0x").send(ctx.sendParam(trader))
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), decimalStr("8000"))
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "9603199999999999999970000")
})
it("user has no pnl if buy and sell immediately", async () => {
// lp buy
await ctx.DODO.methods.buyBaseToken(decimalStr("1000"), decimalStr("100000"), "0x").send(ctx.sendParam(lp))
// trader buy and sell
await ctx.DODO.methods.buyBaseToken(decimalStr("1000"), decimalStr("100000"), "0x").send(ctx.sendParam(trader))
await ctx.DODO.methods.sellBaseToken(decimalStr("1000"), decimalStr("0"), "0x").send(ctx.sendParam(trader))
// no profit or loss (may have precision problems)
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), "0")
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "9999999999999999999970000")
})
})
})

View File

@@ -1,172 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import { DODOContext, getDODOContext } from './utils/DVMContext';
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, DODO_MINE_READER_NAME } from './utils/Contracts';
import { Contract } from 'web3-eth-contract';
import { assert } from 'chai';
import { logGas } from './utils/Log';
let BaseDLP: Contract
let QuoteDLP: Contract
let DODOToken: Contract
let DODOMine: Contract
let DODOMineReader: Contract
let lp1: string;
let lp2: string;
async function init(ctx: DODOContext): Promise<void> {
lp1 = ctx.spareAccounts[0];
lp2 = ctx.spareAccounts[1];
await ctx.mintTestToken(lp1, decimalStr("100"), decimalStr("10000"));
await ctx.mintTestToken(lp2, decimalStr("100"), decimalStr("10000"));
await ctx.approveDODO(lp1);
await ctx.approveDODO(lp2);
await ctx.DODO.methods.depositBase(decimalStr("100")).send(ctx.sendParam(lp1))
await ctx.DODO.methods.depositQuote(decimalStr("10000")).send(ctx.sendParam(lp1))
await ctx.DODO.methods.depositBase(decimalStr("100")).send(ctx.sendParam(lp2))
await ctx.DODO.methods.depositQuote(decimalStr("10000")).send(ctx.sendParam(lp2))
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())
await BaseDLP.methods.approve(DODOMine.options.address, MAX_UINT256).send(ctx.sendParam(lp1))
await QuoteDLP.methods.approve(DODOMine.options.address, MAX_UINT256).send(ctx.sendParam(lp1))
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"), 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))
const rewardVault = await DODOMine.methods.dodoRewardVault().call()
await DODOToken.methods.transfer(rewardVault, decimalStr("100000000")).send(ctx.sendParam(ctx.Deployer))
}
describe("Lock DODO Token", () => {
let snapshotId: string
let ctx: DODOContext
before(async () => {
ctx = await getDODOContext()
await init(ctx);
})
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId)
});
describe("Lp Deposit", () => {
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")
assert.equal(await DODOMine.methods.getDlpMiningSpeed(BaseDLP.options.address).call(), "33333333333333333333")
})
it("multi lp deposit", async () => {
await DODOMine.methods.deposit(BaseDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp1))
await ctx.EVM.fastMove(100)
await DODOMine.methods.deposit(BaseDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp2))
await ctx.EVM.fastMove(100)
assert.equal(await DODOMine.methods.getPendingReward(BaseDLP.options.address, lp1).call(), "5033333333333333333200")
assert.equal(await DODOMine.methods.getPendingReward(BaseDLP.options.address, lp2).call(), "1666666666666666666600")
await DODOMine.methods.deposit(QuoteDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp1))
await ctx.EVM.fastMove(100)
await DODOMine.methods.deposit(QuoteDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp2))
await ctx.EVM.fastMove(100)
assert.equal(await DODOMine.methods.getPendingReward(QuoteDLP.options.address, lp1).call(), "10066666666666666666600")
assert.equal(await DODOMine.methods.getPendingReward(QuoteDLP.options.address, lp2).call(), "3333333333333333333300")
assert.equal(await DODOMine.methods.getAllPendingReward(lp1).call(), "18466666666666666666500")
assert.equal(await DODOMine.methods.getAllPendingReward(lp2).call(), "8366666666666666666600")
})
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)
await logGas(DODOMine.methods.withdraw(BaseDLP.options.address, decimalStr("50")), ctx.sendParam(lp1), "withdraw")
assert.equal(await DODOMine.methods.getAllPendingReward(lp1).call(), "0")
assert.equal(await DODOToken.methods.balanceOf(lp1).call(), "1683333333333333333300")
assert.equal(await DODOMine.methods.getRealizedReward(lp1).call(), "1683333333333333333300")
await ctx.EVM.fastMove(100)
await DODOMine.methods.deposit(BaseDLP.options.address, decimalStr("50")).send(ctx.sendParam(lp1))
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 () => {
await DODOMine.methods.deposit(BaseDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp1))
await DODOMine.methods.deposit(BaseDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp2))
await DODOMine.methods.deposit(QuoteDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp1))
await DODOMine.methods.deposit(QuoteDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp2))
await ctx.EVM.fastMove(100)
await logGas(DODOMine.methods.claim(BaseDLP.options.address), ctx.sendParam(lp1), "claim")
assert.equal(await DODOMine.methods.getPendingReward(BaseDLP.options.address, lp1).call(), "0")
assert.equal(await DODOMine.methods.getAllPendingReward(lp1).call(), "3433333333333333333200")
assert.equal(await DODOMine.methods.getRealizedReward(lp1).call(), "1749999999999999999900")
assert.equal(await DODOToken.methods.balanceOf(lp1).call(), "1749999999999999999900")
await logGas(DODOMine.methods.claimAll(), ctx.sendParam(lp2), "claim 2 pool")
assert.equal(await DODOMine.methods.getPendingReward(BaseDLP.options.address, lp2).call(), "0")
assert.equal(await DODOMine.methods.getAllPendingReward(lp2).call(), "0")
assert.equal(await DODOMine.methods.getRealizedReward(lp2).call(), "5133333333333333333200")
assert.equal(await DODOToken.methods.balanceOf(lp2).call(), "5133333333333333333200")
})
it("lp emergency withdraw", async () => {
await DODOMine.methods.deposit(QuoteDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp1))
await ctx.EVM.fastMove(100)
await DODOMine.methods.emergencyWithdraw(QuoteDLP.options.address).send(ctx.sendParam(lp1))
assert.equal(await QuoteDLP.methods.balanceOf(lp1).call(), decimalStr("10000"))
assert.equal(await DODOMine.methods.getPendingReward(QuoteDLP.options.address, lp1).call(), "0")
assert.equal(await DODOMine.methods.getAllPendingReward(lp1).call(), "0")
assert.equal(await DODOMine.methods.getRealizedReward(lp1).call(), "0")
assert.equal(await DODOToken.methods.balanceOf(lp1).call(), "0")
})
it("setLpToken", async () => {
await DODOMine.methods.deposit(BaseDLP.options.address, decimalStr("100")).send(ctx.sendParam(lp1))
await ctx.EVM.fastMove(100)
await DODOMine.methods.setLpToken(BaseDLP.options.address, "2", true).send(ctx.sendParam(ctx.Deployer))
await ctx.EVM.fastMove(100)
assert.equal(await DODOMine.methods.getAllPendingReward(lp1).call(), "8366666666666666666600")
})
})
})

View File

@@ -1,101 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import * as assert from 'assert';
import { Contract } from 'web3-eth-contract';
import { DODOContext, getDODOContext } from './utils/DVMContext';
import { DODO_REBALANCER_NAME, newContract } from './utils/Contracts';
import { decimalStr } from './utils/Converter';
let lp: string;
let trader: string;
let rebalancer: Contract;
async function init(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(decimalStr("100"));
lp = ctx.spareAccounts[0];
trader = ctx.spareAccounts[1];
await ctx.approveDODO(lp);
await ctx.approveDODO(trader);
await ctx.mintTestToken(lp, decimalStr("10"), decimalStr("1000"));
await ctx.mintTestToken(trader, decimalStr("10"), decimalStr("1000"));
await ctx.DODO.methods
.depositBaseTo(lp, decimalStr("10"))
.send(ctx.sendParam(lp));
await ctx.DODO.methods
.depositQuoteTo(lp, decimalStr("1000"))
.send(ctx.sendParam(lp));
rebalancer = await newContract(DODO_REBALANCER_NAME)
}
describe("Trader", () => {
let snapshotId: string;
let ctx: DODOContext;
before(async () => {
ctx = await getDODOContext();
await init(ctx);
});
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId);
});
describe("rebalance", () => {
it("R above ONE rebalance", async () => {
await ctx.DODO.methods.buyBaseToken(decimalStr("1"), decimalStr("110"), "0x").send(ctx.sendParam(trader))
await ctx.DODO.methods.disableTrading().send(ctx.sendParam(ctx.Deployer))
await ctx.DODO.methods.transferOwnership(rebalancer.options.address).send(ctx.sendParam(ctx.Deployer))
await rebalancer.methods.claimOwnership(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer))
await ctx.BASE.methods.transfer(rebalancer.options.address, decimalStr("2")).send(ctx.sendParam(trader))
await rebalancer.methods.rebalance(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer));
assert.equal(await ctx.DODO.methods.getMidPrice().call(), await ctx.DODO.methods.getOraclePrice().call())
await rebalancer.methods.transferOwnership(ctx.DODO.options.address, ctx.Deployer).send(ctx.sendParam(ctx.Deployer))
await ctx.DODO.methods.claimOwnership().send(ctx.sendParam(ctx.Deployer))
await rebalancer.methods.retrieve(ctx.BASE.options.address).send(ctx.sendParam(ctx.Deployer))
await rebalancer.methods.retrieve(ctx.QUOTE.options.address).send(ctx.sendParam(ctx.Deployer))
assert.equal(await ctx.BASE.methods.balanceOf(ctx.Deployer).call(), "996997569110682237")
assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Deployer).call(), "101113906016449927750")
});
it("R below ONE rebalance", async () => {
await ctx.DODO.methods.sellBaseToken(decimalStr("1"), decimalStr("90"), "0x").send(ctx.sendParam(trader))
await ctx.DODO.methods.disableTrading().send(ctx.sendParam(ctx.Deployer))
await ctx.DODO.methods.transferOwnership(rebalancer.options.address).send(ctx.sendParam(ctx.Deployer))
await rebalancer.methods.claimOwnership(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer))
await ctx.QUOTE.methods.transfer(rebalancer.options.address, decimalStr("200")).send(ctx.sendParam(trader))
await rebalancer.methods.rebalance(ctx.DODO.options.address).send(ctx.sendParam(ctx.Deployer));
assert.equal(await ctx.DODO.methods.getMidPrice().call(), await ctx.DODO.methods.getOraclePrice().call())
await rebalancer.methods.transferOwnership(ctx.DODO.options.address, ctx.Deployer).send(ctx.sendParam(ctx.Deployer))
await ctx.DODO.methods.claimOwnership().send(ctx.sendParam(ctx.Deployer))
await rebalancer.methods.retrieve(ctx.BASE.options.address).send(ctx.sendParam(ctx.Deployer))
await rebalancer.methods.retrieve(ctx.QUOTE.options.address).send(ctx.sendParam(ctx.Deployer))
assert.equal(await ctx.BASE.methods.balanceOf(ctx.Deployer).call(), "997008973080757726")
assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Deployer).call(), "101085569972088780856")
});
});
});

View File

@@ -1,103 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import { DODOContext, getDODOContext } from './utils/DVMContext';
import { decimalStr, gweiStr } from './utils/Converter';
import * as assert from "assert"
let lp: string
let trader: string
async function init(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(decimalStr("1"))
lp = ctx.spareAccounts[0]
trader = ctx.spareAccounts[1]
await ctx.approveDODO(lp)
await ctx.approveDODO(trader)
await ctx.mintTestToken(lp, decimalStr("10000"), decimalStr("10000"))
await ctx.mintTestToken(trader, decimalStr("10000"), decimalStr("10000"))
await ctx.DODO.methods.depositBase(decimalStr("10000")).send(ctx.sendParam(lp))
await ctx.DODO.methods.depositQuote(decimalStr("10000")).send(ctx.sendParam(lp))
}
describe("Trader", () => {
let snapshotId: string
let ctx: DODOContext
before(async () => {
let dodoContextInitConfig = {
lpFeeRate: decimalStr("0.0001"),
mtFeeRate: decimalStr("0"),
k: gweiStr("1"), // nearly zero
gasPriceLimit: gweiStr("100"),
}
ctx = await getDODOContext(dodoContextInitConfig)
await init(ctx);
})
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId)
});
describe("Trade stable coin", () => {
it("trade with tiny slippage", async () => {
// 10% depth avg price 1.000100000111135
await ctx.DODO.methods.buyBaseToken(decimalStr("1000"), decimalStr("1001"), "0x").send(ctx.sendParam(trader))
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), decimalStr("11000"))
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "8999899999888865431655")
// 99.9% depth avg price 1.00010109
await ctx.DODO.methods.buyBaseToken(decimalStr("8990"), decimalStr("10000"), "0x").send(ctx.sendParam(trader))
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), decimalStr("19990"))
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "8990031967806921648")
// sell to 99.9% depth avg price 0.9999
await ctx.DODO.methods.sellBaseToken(decimalStr("19980"), decimalStr("19970"), "0x").send(ctx.sendParam(trader))
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), decimalStr("10"))
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "19986992950440794518402")
})
it("huge sell trading amount", async () => {
// trader could sell any number of base token
// but the price will drop quickly
await ctx.mintTestToken(trader, decimalStr("10000"), decimalStr("0"))
await ctx.DODO.methods.sellBaseToken(decimalStr("20000"), decimalStr("0"), "0x").send(ctx.sendParam(trader))
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), decimalStr("0"))
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "19998999990001000029997")
})
it("huge buy trading amount", async () => {
// could not buy all base balance
await assert.rejects(
ctx.DODO.methods.buyBaseToken(decimalStr("10000"), decimalStr("10010"), "0x").send(ctx.sendParam(trader)),
/DODO_BASE_BALANCE_NOT_ENOUGH/
)
// when buy amount close to base balance, price will increase quickly
await ctx.mintTestToken(trader, decimalStr("0"), decimalStr("10000"))
await ctx.DODO.methods.buyBaseToken(decimalStr("9999"), decimalStr("20000"), "0x").send(ctx.sendParam(trader))
assert.equal(await ctx.BASE.methods.balanceOf(trader).call(), decimalStr("19999"))
assert.equal(await ctx.QUOTE.methods.balanceOf(trader).call(), "9000000119999999900000")
})
it("tiny withdraw penalty", async () => {
await ctx.DODO.methods.buyBaseToken(decimalStr("9990"), decimalStr("10000"), "0x").send(ctx.sendParam(trader))
// penalty only 0.2% even if withdraw make pool utilization rate raise to 99.5%
assert.equal(await ctx.DODO.methods.getWithdrawBasePenalty(decimalStr("5")).call(), "9981967500000000")
})
})
})

View File

@@ -1,184 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import { DODOContext, getDODOContext } from './utils/DVMContext';
import { decimalStr, MAX_UINT256 } from './utils/Converter';
// import * as assert from "assert"
import { newContract, DODO_TOKEN_CONTRACT_NAME, LOCKED_TOKEN_VAULT_CONTRACT_NAME } from './utils/Contracts';
import { Contract } from 'web3-eth-contract';
import * as assert from 'assert';
import BigNumber from 'bignumber.js';
import { logGas } from './utils/Log';
let DODOToken: Contract
let LockedTokenVault: Contract
let initTime: any
let u1: string
let u2: string
let u3: string
async function init(ctx: DODOContext): Promise<void> {
u1 = ctx.spareAccounts[0];
u2 = ctx.spareAccounts[1];
u3 = ctx.spareAccounts[2];
initTime = (await ctx.Web3.eth.getBlock(await ctx.Web3.eth.getBlockNumber())).timestamp;
DODOToken = await newContract(DODO_TOKEN_CONTRACT_NAME)
// release after 1 day, cliff 10% and vest in 1 day
LockedTokenVault = await newContract(LOCKED_TOKEN_VAULT_CONTRACT_NAME, [DODOToken.options.address, initTime + 86400, 86400, decimalStr("0.1")])
DODOToken.methods.approve(LockedTokenVault.options.address, MAX_UINT256).send(ctx.sendParam(ctx.Deployer))
LockedTokenVault.methods.deposit(decimalStr("10000")).send(ctx.sendParam(ctx.Deployer))
}
describe("Lock DODO Token", () => {
let snapshotId: string
let ctx: DODOContext
before(async () => {
ctx = await getDODOContext()
await init(ctx);
})
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId)
});
describe("Lock operations", () => {
it("init states", async () => {
assert.equal(await LockedTokenVault.methods._UNDISTRIBUTED_AMOUNT_().call(), decimalStr("10000"))
await logGas(LockedTokenVault.methods.grant(
[u1],
[decimalStr("100")]
), ctx.sendParam(ctx.Deployer), "grant 1 address")
})
it("grant", async () => {
await logGas(LockedTokenVault.methods.grant(
[u1, u2, u3],
[decimalStr("100"), decimalStr("200"), decimalStr("300")]
), ctx.sendParam(ctx.Deployer), "grant 3 address")
assert.equal(await LockedTokenVault.methods._UNDISTRIBUTED_AMOUNT_().call(), decimalStr("9400"))
assert.equal(await LockedTokenVault.methods.getOriginBalance(u1).call(), decimalStr("100"))
assert.equal(await LockedTokenVault.methods.getOriginBalance(u2).call(), decimalStr("200"))
assert.equal(await LockedTokenVault.methods.getClaimableBalance(u1).call(), "0")
await ctx.EVM.increaseTime(86400)
assert.ok(approxEqual(await LockedTokenVault.methods.getClaimableBalance(u1).call(), decimalStr("10")))
await ctx.EVM.increaseTime(30000)
assert.ok(approxEqual(await LockedTokenVault.methods.getClaimableBalance(u1).call(), decimalStr("41.25")))
})
it("claim", async () => {
await LockedTokenVault.methods.grant(
[u1, u2, u3],
[decimalStr("100"), decimalStr("200"), decimalStr("300")]
).send(ctx.sendParam(ctx.Deployer))
await ctx.EVM.increaseTime(86400)
await LockedTokenVault.methods.claim().send(ctx.sendParam(u1))
assert.equal(await LockedTokenVault.methods.getOriginBalance(u1).call(), decimalStr("100"))
assert.equal(await LockedTokenVault.methods.getClaimableBalance(u1).call(), "0")
assert.ok(approxEqual(await DODOToken.methods.balanceOf(u1).call(), decimalStr("10")))
await ctx.EVM.increaseTime(30000)
await LockedTokenVault.methods.claim().send(ctx.sendParam(u1))
assert.equal(await LockedTokenVault.methods.getClaimableBalance(u1).call(), "0")
assert.ok(approxEqual(await LockedTokenVault.methods.getRemainingBalance(u1).call(), decimalStr("58.75")))
assert.ok(approxEqual(await DODOToken.methods.balanceOf(u1).call(), decimalStr("41.25")))
await LockedTokenVault.methods.claim().send(ctx.sendParam(u2))
assert.equal(await LockedTokenVault.methods.getClaimableBalance(u2).call(), "0")
assert.ok(approxEqual(await LockedTokenVault.methods.getRemainingBalance(u2).call(), decimalStr("117.5")))
assert.ok(approxEqual(await DODOToken.methods.balanceOf(u2).call(), decimalStr("82.5")))
})
it("recall & transfer", async () => {
await LockedTokenVault.methods.grant(
[u1, u2, u3],
[decimalStr("100"), decimalStr("200"), decimalStr("300")]
).send(ctx.sendParam(ctx.Deployer))
// recall u2
await LockedTokenVault.methods.recall(u2).send(ctx.sendParam(ctx.Deployer))
assert.equal(await LockedTokenVault.methods.getOriginBalance(u2).call(), "0")
// transfer from u3 to u2
await ctx.EVM.increaseTime(86400 + 30000)
await LockedTokenVault.methods.transferLockedToken(u2).send(ctx.sendParam(u3))
await LockedTokenVault.methods.claim().send(ctx.sendParam(u2))
assert.equal(await LockedTokenVault.methods.getClaimableBalance(u2).call(), "0")
assert.ok(approxEqual(await LockedTokenVault.methods.getRemainingBalance(u2).call(), decimalStr("176.25")))
assert.ok(approxEqual(await DODOToken.methods.balanceOf(u2).call(), decimalStr("123.75")))
// transfer from u2 to u3
await ctx.EVM.increaseTime(30000)
await LockedTokenVault.methods.transferLockedToken(u3).send(ctx.sendParam(u2))
await LockedTokenVault.methods.claim().send(ctx.sendParam(u3))
assert.equal(await LockedTokenVault.methods.getClaimableBalance(u3).call(), "0")
assert.ok(approxEqual(await LockedTokenVault.methods.getRemainingBalance(u3).call(), decimalStr("82.5")))
assert.ok(approxEqual(await DODOToken.methods.balanceOf(u3).call(), decimalStr("93.75")))
// transfer from u3 to u1
await LockedTokenVault.methods.transferLockedToken(u1).send(ctx.sendParam(u3))
})
it("withdraw", async () => {
await LockedTokenVault.methods.grant(
[u1, u2, u3],
[decimalStr("100"), decimalStr("200"), decimalStr("300")]
).send(ctx.sendParam(ctx.Deployer))
await LockedTokenVault.methods.withdraw(decimalStr("1000")).send(ctx.sendParam(ctx.Deployer))
assert.equal(await LockedTokenVault.methods._UNDISTRIBUTED_AMOUNT_().call(), decimalStr("8400"))
await assert.rejects(
LockedTokenVault.methods.withdraw(decimalStr("8500")).send(ctx.sendParam(ctx.Deployer)),
/SUB_ERROR/
)
})
it("finish distributed", async () => {
await LockedTokenVault.methods.grant(
[u1, u2, u3],
[decimalStr("100"), decimalStr("200"), decimalStr("300")]
).send(ctx.sendParam(ctx.Deployer))
await LockedTokenVault.methods.finishDistribute().send(ctx.sendParam(ctx.Deployer))
// can not recall
await assert.rejects(
LockedTokenVault.methods.recall(u2).send(ctx.sendParam(ctx.Deployer)),
/DISTRIBUTE FINISHED/
)
})
})
})
function approxEqual(numStr1: string, numStr2: string) {
let num1 = new BigNumber(numStr1)
let num2 = new BigNumber(numStr2)
let ratio = num1.div(num2).minus(1).abs()
if (ratio.isLessThan(0.0002)) {
return true
} else {
return false
}
}

View File

@@ -1,553 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import * as assert from 'assert';
import { DODOContext, getDODOContext } from './utils/DVMContext';
import { decimalStr } from './utils/Converter';
import { logGas } from './utils/Log';
let lp: string;
let trader: string;
async function init(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(decimalStr("100"));
lp = ctx.spareAccounts[0];
trader = ctx.spareAccounts[1];
await ctx.approveDODO(lp);
await ctx.approveDODO(trader);
await ctx.mintTestToken(lp, decimalStr("10"), decimalStr("1000"));
await ctx.mintTestToken(trader, decimalStr("10"), decimalStr("1000"));
await ctx.DODO.methods
.depositBaseTo(lp, decimalStr("10"))
.send(ctx.sendParam(lp));
await ctx.DODO.methods
.depositQuoteTo(lp, decimalStr("1000"))
.send(ctx.sendParam(lp));
}
describe("Trader", () => {
let snapshotId: string;
let ctx: DODOContext;
before(async () => {
ctx = await getDODOContext();
await init(ctx);
});
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId);
});
describe("R goes above ONE", () => {
it("buy when R equals ONE", async () => {
await logGas(ctx.DODO.methods.buyBaseToken(decimalStr("1"), decimalStr("110"), "0x"), ctx.sendParam(trader), "buy base token when balanced")
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("11")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"898581839502056240973"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
decimalStr("0.001")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
decimalStr("0")
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("8.999")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"1101418160497943759027"
);
// price update
assert.equal(
await ctx.DODO.methods.getMidPrice().call(),
"102353368821735563400"
);
});
it("buy when R is ABOVE ONE", async () => {
await ctx.DODO.methods.buyBaseToken(decimalStr("1"), decimalStr("110"), "0x").send(ctx.sendParam(trader))
await logGas(ctx.DODO.methods.buyBaseToken(decimalStr("1"), decimalStr("130"), "0x"), ctx.sendParam(trader), "buy when R is ABOVE ONE")
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("12")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"794367183433412077653"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
decimalStr("0.002")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
decimalStr("0")
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("7.998")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"1205632816566587922347"
);
});
it("sell when R is ABOVE ONE", async () => {
await ctx.DODO.methods.buyBaseToken(decimalStr("1"), decimalStr("110"), "0x").send(ctx.sendParam(trader))
await logGas(ctx.DODO.methods.sellBaseToken(decimalStr("0.5"), decimalStr("40"), "0x"), ctx.sendParam(trader), "sell when R is ABOVE ONE")
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("10.5")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"949280846351657143136"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
decimalStr("0.001")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
"50851561534203512"
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("9.499")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"1050668302086808653352"
);
});
it("sell when R is ABOVE ONE and RStatus back to ONE", async () => {
await ctx.DODO.methods.buyBaseToken(decimalStr("1"), decimalStr("110"), "0x").send(ctx.sendParam(trader))
await logGas(ctx.DODO.methods.sellBaseToken("1003002430889317763", decimalStr("90"), "0x"), ctx.sendParam(trader), "sell when R is ABOVE ONE and RStatus back to ONE")
// R status
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "0");
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
"9996997569110682237"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"999695745518506168723"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
decimalStr("0.001")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
"101418160497943759"
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
"10002002430889317763"
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"1000202836320995887518"
);
// target status
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
"10002002430889317763"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
"1000202836320995887518"
);
});
it("sell when R is ABOVE ONE and RStatus becomes BELOW ONE", async () => {
await ctx.DODO.methods.buyBaseToken(decimalStr("1"), decimalStr("110"), "0x").send(ctx.sendParam(trader))
await logGas(ctx.DODO.methods.sellBaseToken(decimalStr("2"), decimalStr("90"), "0x"), ctx.sendParam(trader), "sell when R is ABOVE ONE and RStatus becomes BELOW ONE [gas cost worst case]")
// R status
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "2");
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("9")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"1098020621600061709144"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
decimalStr("0.001")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
"200038898794388634"
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("10.999")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"901779339501143902222"
);
// target status
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
"10002002430889317763"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
"1000400077797588777268"
);
});
});
describe("R goes below ONE", () => {
it("sell when R equals ONE", async () => {
await logGas(ctx.DODO.methods.sellBaseToken(decimalStr("1"), decimalStr("90"), "0x"), ctx.sendParam(trader), "sell base token when balanced")
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("9")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"1098617454226610630663"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
"0"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
"98914196817061816"
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("11")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"901283631576572307521"
);
// price update
assert.equal(
await ctx.DODO.methods.getMidPrice().call(),
"97736983274307939149"
);
});
it("sell when R is BELOW ONE", async () => {
await ctx.DODO.methods.sellBaseToken(decimalStr("3"), decimalStr("90"), "0x").send(ctx.sendParam(trader))
await logGas(ctx.DODO.methods.sellBaseToken(decimalStr("3"), decimalStr("90"), "0x"), ctx.sendParam(trader), "sell when R is BELOW ONE")
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("4")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"1535961012052716726151"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
"0"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
"537573733252474148"
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("16")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"463501414214030799701"
);
});
it("buy when R is BELOW ONE", async () => {
await ctx.DODO.methods.sellBaseToken(decimalStr("1"), decimalStr("90"), "0x").send(ctx.sendParam(trader))
await logGas(ctx.DODO.methods.buyBaseToken(decimalStr("0.5"), decimalStr("60"), "0x"), ctx.sendParam(trader), "buy when R is BELOW ONE")
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("9.5")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"1049294316148665165453"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
decimalStr("0.0005")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
"98914196817061816"
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("10.4995")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"950606769654517772731"
);
});
it("buy when R is BELOW ONE and RStatus back to ONE", async () => {
await ctx.DODO.methods.sellBaseToken(decimalStr("1"), decimalStr("90"), "0x").send(ctx.sendParam(trader))
await logGas(ctx.DODO.methods.buyBaseToken("997008973080757728", decimalStr("110"), "0x"), ctx.sendParam(trader), "buy when R is BELOW ONE and RStatus back to ONE")
// R status
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "0");
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
"9997008973080757728"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"999703024198699411500"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
"997008973080757"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
"98914196817061816"
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
"10001994017946161515"
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"1000198061604483526684"
);
// target status
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
"10001994017946161515"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
"1000198061604483526684"
);
});
it("buy when R is BELOW ONE and RStatus becomes ABOVE ONE", async () => {
await ctx.DODO.methods.sellBaseToken(decimalStr("1"), decimalStr("90"), "0x").send(ctx.sendParam(trader))
await logGas(ctx.DODO.methods.buyBaseToken(decimalStr("2"), decimalStr("220"), "0x"), ctx.sendParam(trader), "buy when R is BELOW ONE and RStatus becomes ABOVE ONE [gas cost worst case]")
// R status
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "1");
// trader balances
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("11")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"897977789597854403796"
);
// maintainer balances
assert.equal(
await ctx.BASE.methods.balanceOf(ctx.Maintainer).call(),
decimalStr("0.002")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(),
"98914196817061816"
);
// dodo balances
assert.equal(
await ctx.DODO.methods._BASE_BALANCE_().call(),
decimalStr("8.998")
);
assert.equal(
await ctx.DODO.methods._QUOTE_BALANCE_().call(),
"1101923296205328534388"
);
// target status
assert.equal(
await ctx.DODO.methods._TARGET_BASE_TOKEN_AMOUNT_().call(),
"10004000000000000000"
);
assert.equal(
await ctx.DODO.methods._TARGET_QUOTE_TOKEN_AMOUNT_().call(),
"1000198061604483526684"
);
});
});
describe("Corner cases", () => {
it("buy or sell 0", async () => {
await ctx.DODO.methods
.sellBaseToken(decimalStr("0"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("10")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
decimalStr("1000")
);
await ctx.DODO.methods
.buyBaseToken(decimalStr("0"), decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
decimalStr("10")
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
decimalStr("1000")
);
});
it("buy or sell a tiny amount", async () => {
// no precision problem
await ctx.DODO.methods
.sellBaseToken("1", decimalStr("0"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
"9999999999999999999"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"1000000000000000000100"
);
// have precision problem, charge 0
await ctx.DODO.methods
.buyBaseToken("1", decimalStr("1"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
"10000000000000000000"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"1000000000000000000100"
);
assert.equal(await ctx.DODO.methods._R_STATUS_().call(), "0");
// no precision problem if trading amount is extremely small
await ctx.DODO.methods
.buyBaseToken("10", decimalStr("1"), "0x")
.send(ctx.sendParam(trader));
assert.equal(
await ctx.BASE.methods.balanceOf(trader).call(),
"10000000000000000010"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"999999999999999999100"
);
});
it("sell a huge amount of base token", async () => {
await ctx.mintTestToken(trader, decimalStr("10000"), "0");
await ctx.DODO.methods
.sellBaseToken(decimalStr("10000"), "0", "0x")
.send(ctx.sendParam(trader));
// nearly drain out quote pool
// because the fee donated is greater than remaining quote pool
// quote lp earn a considerable profit
assert.equal(
await ctx.QUOTE.methods.balanceOf(trader).call(),
"1996900220185135480813"
);
assert.equal(
await ctx.DODO.methods.getLpQuoteBalance(lp).call(),
"4574057156329524019750"
);
});
});
describe("Revert cases", () => {
it("price limit", async () => {
await assert.rejects(
ctx.DODO.methods
.buyBaseToken(decimalStr("1"), decimalStr("100"), "0x")
.send(ctx.sendParam(trader)),
/BUY_BASE_COST_TOO_MUCH/
);
await assert.rejects(
ctx.DODO.methods
.sellBaseToken(decimalStr("1"), decimalStr("100"), "0x")
.send(ctx.sendParam(trader)),
/SELL_BASE_RECEIVE_NOT_ENOUGH/
);
});
it("base balance limit", async () => {
await assert.rejects(
ctx.DODO.methods
.buyBaseToken(decimalStr("11"), decimalStr("10000"), "0x")
.send(ctx.sendParam(trader)),
/DODO_BASE_BALANCE_NOT_ENOUGH/
);
await ctx.DODO.methods
.buyBaseToken(decimalStr("1"), decimalStr("200"), "0x")
.send(ctx.sendParam(trader));
await assert.rejects(
ctx.DODO.methods
.buyBaseToken(decimalStr("11"), decimalStr("10000"), "0x")
.send(ctx.sendParam(trader)),
/DODO_BASE_BALANCE_NOT_ENOUGH/
);
});
});
});

View File

@@ -1,202 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import * as assert from 'assert';
import { Contract } from 'web3-eth-contract';
import { DODOContext, getDODOContext } from './utils/DVMContext';
import {
newContract,
UNISWAP_ARBITRAGEUR_CONTRACT_NAME,
UNISWAP_CONTRACT_NAME,
} from './utils/Contracts';
import { decimalStr } from './utils/Converter';
import { logGas } from './utils/Log';
let lp: string;
let keeper: string;
let Uniswap: Contract;
let UniswapArbitrageur: Contract;
let UniswapReverse: Contract;
let UniswapArbitrageurReverse: Contract;
async function init(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(decimalStr("100"));
lp = ctx.spareAccounts[0];
keeper = ctx.spareAccounts[1];
await ctx.approveDODO(lp);
await ctx.mintTestToken(lp, decimalStr("100"), decimalStr("10000"));
await ctx.DODO.methods.depositBase(decimalStr("10")).send(ctx.sendParam(lp));
await ctx.DODO.methods
.depositQuote(decimalStr("1000"))
.send(ctx.sendParam(lp));
Uniswap = await newContract(UNISWAP_CONTRACT_NAME);
Uniswap.methods
.initialize(ctx.BASE.options.address, ctx.QUOTE.options.address)
.send(ctx.sendParam(ctx.Deployer));
ctx.BASE.methods
.transfer(Uniswap.options.address, decimalStr("10"))
.send(ctx.sendParam(lp));
ctx.QUOTE.methods
.transfer(Uniswap.options.address, decimalStr("2000"))
.send(ctx.sendParam(lp));
Uniswap.methods.sync().send(ctx.sendParam(lp));
UniswapArbitrageur = await newContract(UNISWAP_ARBITRAGEUR_CONTRACT_NAME, [
Uniswap.options.address,
ctx.DODO.options.address,
]);
UniswapReverse = await newContract(UNISWAP_CONTRACT_NAME);
UniswapReverse.methods
.initialize(ctx.BASE.options.address, ctx.QUOTE.options.address)
.send(ctx.sendParam(ctx.Deployer));
ctx.BASE.methods
.transfer(UniswapReverse.options.address, decimalStr("10"))
.send(ctx.sendParam(lp));
ctx.QUOTE.methods
.transfer(UniswapReverse.options.address, decimalStr("2000"))
.send(ctx.sendParam(lp));
UniswapReverse.methods.sync().send(ctx.sendParam(lp));
UniswapArbitrageurReverse = await newContract(
UNISWAP_ARBITRAGEUR_CONTRACT_NAME,
[UniswapReverse.options.address, ctx.DODO.options.address]
);
}
describe("Uniswap Arbitrageur", () => {
let snapshotId: string;
let ctx: DODOContext;
before(async () => {
ctx = await getDODOContext();
await init(ctx);
});
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId);
});
describe("arbitrage with not reverse pair", () => {
it("buy at dodo", async () => {
await ctx.setOraclePrice(decimalStr("100"));
// dodo price 100 uniswap price 200
// buy at dodo
<<<<<<< Updated upstream
await logGas(
UniswapArbitrageur.methods.executeBuyArbitrage(decimalStr("1")),
ctx.sendParam(keeper),
"arbitrage buy at dodo not reverse"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(keeper).call(),
"79836384956601695518"
);
});
=======
logGas(await UniswapArbitrageur.methods.executeBuyArbitrage(decimalStr("1")), ctx.sendParam(keeper), "arbitrage buy at dodo not reverse")
assert.equal(await ctx.QUOTE.methods.balanceOf(keeper).call(), "79836384956601695518")
})
>>>>>>> Stashed changes
it("sell at dodo", async () => {
await ctx.setOraclePrice(decimalStr("300"));
// dodo price 300 uniswap price 200
// sell at dodo
<<<<<<< Updated upstream
await logGas(
UniswapArbitrageur.methods.executeSellArbitrage(decimalStr("1")),
ctx.sendParam(keeper),
"arbitrage sell at dodo not reverse"
);
assert.equal(
await ctx.BASE.methods.balanceOf(keeper).call(),
"252761069524143743"
);
});
});
=======
logGas(await UniswapArbitrageur.methods.executeSellArbitrage(decimalStr("1")), ctx.sendParam(keeper), "arbitrage sell at dodo not reverse")
assert.equal(await ctx.BASE.methods.balanceOf(keeper).call(), "252761069524143743")
})
})
>>>>>>> Stashed changes
describe("arbitrage with reverse pair", () => {
it("buy at dodo", async () => {
await ctx.setOraclePrice(decimalStr("100"));
// dodo price 100 uniswap price 200
// buy at dodo
<<<<<<< Updated upstream
await logGas(
UniswapArbitrageurReverse.methods.executeBuyArbitrage(decimalStr("1")),
ctx.sendParam(keeper),
"arbitrage buy at dodo reverse"
);
assert.equal(
await ctx.QUOTE.methods.balanceOf(keeper).call(),
"79836384956601695518"
);
});
=======
logGas(await UniswapArbitrageurReverse.methods.executeBuyArbitrage(decimalStr("1")), ctx.sendParam(keeper), "arbitrage buy at dodo reverse")
assert.equal(await ctx.QUOTE.methods.balanceOf(keeper).call(), "79836384956601695518")
})
>>>>>>> Stashed changes
it("sell at dodo", async () => {
await ctx.setOraclePrice(decimalStr("300"));
// dodo price 300 uniswap price 200
// sell at dodo
<<<<<<< Updated upstream
await logGas(
UniswapArbitrageurReverse.methods.executeSellArbitrage(decimalStr("1")),
ctx.sendParam(keeper),
"arbitrage sell at dodo reverse"
);
assert.equal(
await ctx.BASE.methods.balanceOf(keeper).call(),
"252761069524143743"
);
});
});
=======
logGas(await UniswapArbitrageurReverse.methods.executeSellArbitrage(decimalStr("1")), ctx.sendParam(keeper), "arbitrage sell at dodo reverse")
assert.equal(await ctx.BASE.methods.balanceOf(keeper).call(), "252761069524143743")
})
})
>>>>>>> Stashed changes
describe("revert cases", () => {
it("price not match", async () => {
await ctx.setOraclePrice(decimalStr("200"));
await assert.rejects(
UniswapArbitrageurReverse.methods
.executeBuyArbitrage(decimalStr("1"))
.send(ctx.sendParam(keeper)),
/NOT_PROFITABLE/
);
await assert.rejects(
UniswapArbitrageurReverse.methods
.executeSellArbitrage(decimalStr("1"))
.send(ctx.sendParam(keeper)),
/NOT_PROFITABLE/
);
});
});
});

View File

@@ -1,274 +0,0 @@
/*
Copyright 2020 DODO ZOO.
SPDX-License-Identifier: Apache-2.0
*/
import BigNumber from 'bignumber.js';
import { DODOContext, getDODOContext } from '../utils-v1/ProxyContextV1';
import { decimalStr, MAX_UINT256, fromWei, mweiStr } from '../utils-v1/Converter';
import { logGas } from '../utils-v1/Log';
import * as contracts from '../utils-v1/Contracts';
import { assert } from 'chai';
let lp: string;
let trader: string;
async function initDODO_USDT(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(ctx.DODO_USDT_ORACLE, mweiStr("0.1"));
lp = ctx.spareAccounts[0];
trader = ctx.spareAccounts[1];
let DODO = ctx.DODO;
let USDT = ctx.USDT;
let DODO_USDT = ctx.DODO_USDT;
await ctx.approvePair(DODO, USDT, DODO_USDT.options.address, lp);
await ctx.approvePair(DODO, USDT, DODO_USDT.options.address, trader);
await ctx.mintToken(DODO, USDT, lp, decimalStr("10000000"), mweiStr("1000000"));
await DODO_USDT.methods
.depositBaseTo(lp, decimalStr("10000000"))
.send(ctx.sendParam(lp));
await DODO_USDT.methods
.depositQuoteTo(lp, mweiStr("1000000"))
.send(ctx.sendParam(lp));
}
async function initUSDT_USDC(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(ctx.USDT_USDC_ORACLE, decimalStr("1"));
lp = ctx.spareAccounts[0];
trader = ctx.spareAccounts[1];
let USDT = ctx.USDT;
let USDC = ctx.USDC;
let USDT_USDC = ctx.USDT_USDC;
await ctx.approvePair(USDT, USDC, USDT_USDC.options.address, lp);
await ctx.mintToken(USDT, USDC, lp, mweiStr("1000000"), mweiStr("1000000"));
await USDT_USDC.methods
.depositBaseTo(lp, mweiStr("1000000"))
.send(ctx.sendParam(lp));
await USDT_USDC.methods
.depositQuoteTo(lp, mweiStr("1000000"))
.send(ctx.sendParam(lp));
}
async function initWETH_USDC(ctx: DODOContext): Promise<void> {
await ctx.setOraclePrice(ctx.WETH_USDC_ORACLE, mweiStr("450"));
lp = ctx.spareAccounts[0];
trader = ctx.spareAccounts[1];
let WETH = ctx.WETH;
let USDC = ctx.USDC;
let WETH_USDC = ctx.WETH_USDC;
await ctx.approvePair(WETH, USDC, WETH_USDC.options.address, lp);
await ctx.mintToken(null, USDC, lp, decimalStr("0"), mweiStr("3600"));
await WETH.methods.deposit().send(ctx.sendParam(lp, '8'));
await WETH_USDC.methods
.depositBaseTo(lp, decimalStr("8"))
.send(ctx.sendParam(lp));
await WETH_USDC.methods
.depositQuoteTo(lp, mweiStr("3600"))
.send(ctx.sendParam(lp));
}
async function initIncentive(ctx: DODOContext): Promise<void> {
var blockNum = await ctx.Web3.eth.getBlockNumber();
await ctx.DODOIncentive.methods.switchIncentive(blockNum + 1).send(ctx.sendParam(ctx.Deployer));
await ctx.mintToken(ctx.DODO, null, ctx.DODOIncentive.options.address, decimalStr("10000"), mweiStr("0"));
}
//mock sdk logic
async function calcRoute(ctx: DODOContext, fromTokenAmount: string, slippage: number, routes: any[], pairs: any[], isIncentive: boolean) {
let tmpDirections: number[] = []
let strDirections: string = ''
let dodoPairs: string[] = []
for (let i = 0; i < pairs.length; i++) {
let curPair = pairs[i]
dodoPairs.push(curPair.pair)
if (routes[i].address == '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') {
tmpDirections[i] = 0;
} else if (curPair.base === routes[i].address) {
tmpDirections[i] = 0;
} else {
tmpDirections[i] = 1;
}
}
var [returmAmount,] = await ctx.DODOSwapCalcHelper.methods.calcReturnAmountV1(
fromTokenAmount,
dodoPairs,
tmpDirections,
).call();
// console.log("returnAmount:", returmAmount)
// console.log("localAmount:", swapAmount)
// console.log("midPrices:", midPrices)
let toAmount = new BigNumber(returmAmount).multipliedBy(1 - slippage).toFixed(0, BigNumber.ROUND_DOWN)
let deadline = Math.floor(new Date().getTime() / 1000 + 60 * 10);
for (let i = tmpDirections.length - 1; i >= 0; i--) {
strDirections += tmpDirections[i].toString()
}
return ctx.DODOV1Proxy02.methods.dodoSwapV1(
routes[0].address,
routes[routes.length - 1].address,
fromTokenAmount,
toAmount,
dodoPairs,
parseInt(strDirections, 2),
isIncentive,
deadline
)
}
describe("Trader", () => {
let snapshotId: string;
let ctx: DODOContext;
before(async () => {
let ETH = await contracts.newContract(
contracts.WETH_CONTRACT_NAME
);
ctx = await getDODOContext(ETH.options.address);
await initDODO_USDT(ctx);
await initUSDT_USDC(ctx);
await initWETH_USDC(ctx);
await initIncentive(ctx);
});
beforeEach(async () => {
snapshotId = await ctx.EVM.snapshot();
});
afterEach(async () => {
await ctx.EVM.reset(snapshotId);
});
describe("route calc with incentive test", () => {
it("incentive-switch with trade", async () => {
await ctx.DODOIncentive.methods.changePerReward(decimalStr("10")).send(ctx.sendParam(ctx.Deployer));
var totalReward = await ctx.DODOIncentive.methods.totalReward().call();
var totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call();
var blockNum = await ctx.Web3.eth.getBlockNumber();
console.log("Init - Total Reward:" + fromWei(totalReward, 'ether') + "; Total distribution:" + fromWei(totalDistribution, 'ether') + "; BlockNumber:" + blockNum);
//Aim to increase block
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
blockNum = await ctx.Web3.eth.getBlockNumber();
console.log("Close BlockNumber:", blockNum + 1)
await ctx.DODOIncentive.methods.switchIncentive(0).send(ctx.sendParam(ctx.Deployer));
totalReward = await ctx.DODOIncentive.methods.totalReward().call();
totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call();
blockNum = await ctx.Web3.eth.getBlockNumber();
console.log("Close incentive - Total Reward:" + fromWei(totalReward, 'ether') + "; Total distribution:" + fromWei(totalDistribution, 'ether') + "; BlockNumber:" + blockNum)
//Aim to increase block
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
blockNum = await ctx.Web3.eth.getBlockNumber();
await ctx.DODOIncentive.methods.switchIncentive(blockNum + 1).send(ctx.sendParam(ctx.Deployer));
console.log("Open BlockNumber:", blockNum + 1)
//Aim to increase block
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.DODOIncentive.methods.changePerReward(decimalStr("10")).send(ctx.sendParam(ctx.Deployer));
totalReward = await ctx.DODOIncentive.methods.totalReward().call();
totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call();
blockNum = await ctx.Web3.eth.getBlockNumber();
console.log("End incentive - Total Reward:" + fromWei(totalReward, 'ether') + "; Total distribution:" + fromWei(totalDistribution, 'ether') + "; BlockNumber:" + blockNum)
assert(totalReward, decimalStr("100"));
});
it("incentive-changeBoost with trade", async () => {
await ctx.DODOIncentive.methods.changePerReward(decimalStr("10")).send(ctx.sendParam(ctx.Deployer));
var totalReward = await ctx.DODOIncentive.methods.totalReward().call();
var totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call();
var blockNum = await ctx.Web3.eth.getBlockNumber();
console.log("Init - Total Reward:" + fromWei(totalReward, 'ether') + "; Total distribution:" + fromWei(totalDistribution, 'ether') + "; BlockNumber:" + blockNum);
//Aim to increase block
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
blockNum = await ctx.Web3.eth.getBlockNumber();
console.log("Change BlockNumber:", blockNum + 1)
await ctx.DODOIncentive.methods.changePerReward(decimalStr("20")).send(ctx.sendParam(ctx.Deployer));
totalReward = await ctx.DODOIncentive.methods.totalReward().call();
totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call();
blockNum = await ctx.Web3.eth.getBlockNumber();
console.log("change incentive - Total Reward:" + fromWei(totalReward, 'ether') + "; Total distribution:" + fromWei(totalDistribution, 'ether') + "; BlockNumber:" + blockNum)
//Aim to increase block
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.mintToken(ctx.DODO, null, lp, decimalStr("100"), mweiStr("0"));
await ctx.DODOIncentive.methods.changePerReward(decimalStr("10")).send(ctx.sendParam(ctx.Deployer));
totalReward = await ctx.DODOIncentive.methods.totalReward().call();
totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call();
blockNum = await ctx.Web3.eth.getBlockNumber();
console.log("End incentive - Total Reward:" + fromWei(totalReward, 'ether') + "; Total distribution:" + fromWei(totalDistribution, 'ether') + "; BlockNumber:" + blockNum)
assert(totalReward, decimalStr("140"));
});
it("directly swap with incentive", async () => {
await ctx.mintToken(ctx.DODO, ctx.USDT, trader, decimalStr("1000"), mweiStr("0"));
var b_DODO = await ctx.DODO.methods.balanceOf(trader).call()
var b_USDT = await ctx.USDT.methods.balanceOf(trader).call()
console.log("Before DODO:" + fromWei(b_DODO, 'ether') + "; USDT:" + fromWei(b_USDT, 'mwei'));
var b_totalReward = await ctx.DODOIncentive.methods.totalReward().call();
var b_totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call();
console.log("Before Total Reward:" + fromWei(b_totalReward, 'ether') + "; Total distribution:" + fromWei(b_totalDistribution, 'ether'))
//approve DODO entry
await ctx.DODO.methods.approve(ctx.DODOApprove.options.address, MAX_UINT256).send(ctx.sendParam(trader))
//set route path
var routes = [{
address: ctx.DODO.options.address,
decimals: 18
},
{
address: ctx.USDT.options.address,
decimals: 6
}];
var pairs = [{
pair: ctx.DODO_USDT.options.address,
base: ctx.DODO.options.address,
pairContract: ctx.DODO_USDT
}];
await logGas(await calcRoute(ctx, decimalStr('10'), 0.1, routes, pairs, false), ctx.sendParam(trader), "directly swap without incentive first")
await logGas(await calcRoute(ctx, decimalStr('10'), 0.1, routes, pairs, false), ctx.sendParam(trader), "directly swap without incentive second")
var a_DODO = await ctx.DODO.methods.balanceOf(trader).call()
var a_USDT = await ctx.USDT.methods.balanceOf(trader).call()
console.log("After No Incentive DODO:" + fromWei(a_DODO, 'ether') + "; USDT:" + fromWei(a_USDT, 'mwei'));
await logGas(await calcRoute(ctx, decimalStr('10'), 0.1, routes, pairs, true), ctx.sendParam(trader), "directly swap with incentive first")
await logGas(await calcRoute(ctx, decimalStr('10'), 0.1, routes, pairs, true), ctx.sendParam(trader), "directly swap with incentive second")
var a_totalReward = await ctx.DODOIncentive.methods.totalReward().call();
var a_totalDistribution = await ctx.DODOIncentive.methods.totalDistribution().call();
console.log("After Total Reward:" + fromWei(a_totalReward, 'ether') + "; Total distribution:" + fromWei(a_totalDistribution, 'ether'))
a_DODO = await ctx.DODO.methods.balanceOf(trader).call()
a_USDT = await ctx.USDT.methods.balanceOf(trader).call()
console.log("After Incentive DODO:" + fromWei(a_DODO, 'ether') + "; USDT:" + fromWei(a_USDT, 'mwei'));
assert(a_DODO, decimalStr("961.493"));
});
});
});

View File

@@ -256,7 +256,7 @@ export class DODOContext {
await this.DODOV1Proxy02.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer));
await this.DODOIncentive.methods.initOwner(this.Deployer).send(this.sendParam(this.Deployer));
await this.DODOApprove.methods.init(this.Deployer,this.DODOV1Proxy02.options.address).send(this.sendParam(this.Deployer));
await this.DODOApprove.methods.init(this.Deployer, this.DODOV1Proxy01.options.address).send(this.sendParam(this.Deployer));
await this.DODOIncentive.methods.changeDODOProxy(this.DODOV1Proxy02.options.address).send(this.sendParam(this.Deployer));
this.DODOSwapCalcHelper = await contracts.newContract(