diff --git a/contracts/CrowdPooling/impl/CPFunding.sol b/contracts/CrowdPooling/impl/CPFunding.sol index a09a59b..b15b4a2 100644 --- a/contracts/CrowdPooling/impl/CPFunding.sol +++ b/contracts/CrowdPooling/impl/CPFunding.sol @@ -16,6 +16,7 @@ import {IDVM} from "../../DODOVendingMachine/intf/IDVM.sol"; import {IDVMFactory} from "../../Factory/DVMFactory.sol"; import {CPStorage} from "./CPStorage.sol"; import {PMMPricing} from "../../lib/PMMPricing.sol"; +import {IDODOCallee} from "../../intf/IDODOCallee.sol"; contract CPFunding is CPStorage { using SafeERC20 for IERC20; @@ -39,12 +40,17 @@ contract CPFunding is CPStorage { emit Bid(to, input, mtFee); } - function cancel(address assetTo, uint256 amount) external phaseBidOrCalm preventReentrant { + function cancel(address to, uint256 amount, bytes calldata data) external phaseBidOrCalm preventReentrant { require(_SHARES_[msg.sender] >= amount, "SHARES_NOT_ENOUGH"); _burnShares(msg.sender, amount); - _transferQuoteOut(assetTo, amount); + _transferQuoteOut(to, amount); _sync(); - emit Cancel(assetTo,amount); + + if(data.length > 0){ + IDODOCallee(to).CPCancelCall(msg.sender,amount,data); + } + + emit Cancel(msg.sender,amount); } function _mintShares(address to, uint256 amount) internal { diff --git a/contracts/SmartRoute/helper/DODOCalleeHelper.sol b/contracts/SmartRoute/helper/DODOCalleeHelper.sol index caac55c..f1a7522 100644 --- a/contracts/SmartRoute/helper/DODOCalleeHelper.sol +++ b/contracts/SmartRoute/helper/DODOCalleeHelper.sol @@ -1,5 +1,5 @@ /* - + Copyright 2020 DODO ZOO. SPDX-License-Identifier: Apache-2.0 @@ -43,6 +43,15 @@ contract DODOCalleeHelper is ReentrancyGuard { _withdraw(assetTo, _quoteToken, quoteAmount, _quoteToken == _WETH_); } + function CPCancelCall( + address payable assetTo, + uint256 amount, + bytes calldata + )external preventReentrant{ + address _quoteToken = IDODOV2(msg.sender)._QUOTE_TOKEN_(); + _withdraw(assetTo, _quoteToken, amount, _quoteToken == _WETH_); + } + function _withdraw( address payable to, address token, diff --git a/contracts/intf/IDODOCallee.sol b/contracts/intf/IDODOCallee.sol index d506c0c..0dd00a4 100644 --- a/contracts/intf/IDODOCallee.sol +++ b/contracts/intf/IDODOCallee.sol @@ -31,7 +31,7 @@ interface IDODOCallee { bytes calldata data ) external; - function CACancelCall( + function CPCancelCall( address sender, uint256 amount, bytes calldata data diff --git a/test/CrowdPooling/CPBid.test.ts b/test/CrowdPooling/CPBid.test.ts index 252c837..5743b47 100644 --- a/test/CrowdPooling/CPBid.test.ts +++ b/test/CrowdPooling/CPBid.test.ts @@ -41,6 +41,7 @@ describe("Funding", () => { freezeDuration: new BigNumber(86400), vestingDuration: new BigNumber(86400), cliffRate: decimalStr("1"), + quoteTokenContract:"" } ctx = new CPContext(); await ctx.init(config); @@ -57,7 +58,7 @@ describe("Funding", () => { describe("bid & cancel", () => { - it("bid", async () => { + it("bid and cancel", async () => { await ctx.QUOTE.methods.transfer(ctx.CP.options.address, decimalStr("100")).send(ctx.sendParam(bidder1)) await logGas(ctx.CP.methods.bid(bidder1), ctx.sendParam(bidder1), "bid") assert.equal(await ctx.CP.methods.getShares(bidder1).call(), decimalStr("99.9")) @@ -75,6 +76,7 @@ describe("Funding", () => { assert.equal(await ctx.CP.methods.getShares(bidder1).call(), decimalStr("79.9")) assert.equal(await ctx.CP.methods._TOTAL_SHARES_().call(), decimalStr("129.85")) assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), decimalStr("920")) + }) }) diff --git a/test/CrowdPooling/CPCancelEthBid.test.ts b/test/CrowdPooling/CPCancelEthBid.test.ts new file mode 100644 index 0000000..fe8bcc4 --- /dev/null +++ b/test/CrowdPooling/CPCancelEthBid.test.ts @@ -0,0 +1,88 @@ +/* + + Copyright 2020 DODO ZOO. + SPDX-License-Identifier: Apache-2.0 + +*/ + +// import * as assert from 'assert'; + +import { decimalStr, mweiStr } from "../utils/Converter"; +import { logGas } from '../utils/Log'; +import { CPContext, CPContextInitConfig } from '../utils/CrowdPoolingContext'; +import BigNumber from 'bignumber.js'; +import { assert } from 'chai'; +const truffleAssert = require('truffle-assertions'); + +let bidder1: string; +let bidder2: string; +let config: CPContextInitConfig + +async function init(ctx: CPContext): Promise { + bidder1 = ctx.SpareAccounts[1] + bidder2 = ctx.SpareAccounts[2] + await ctx.QUOTE.methods.deposit().send(ctx.sendParam(bidder1,"0.2")) + await ctx.QUOTE.methods.deposit().send(ctx.sendParam(bidder2,"0.3")) +} + +describe("Funding", () => { + let snapshotId: string; + let ctx: CPContext; + + before(async () => { + config = { + totalBase: decimalStr("10000"), + poolQuoteCap: decimalStr("50000"), + k: decimalStr("0"), + i: decimalStr("10"), + lpFeeRate: decimalStr("0.002"), + bidDuration: new BigNumber(86400), + calmDuration: new BigNumber(86400), + freezeDuration: new BigNumber(86400), + vestingDuration: new BigNumber(86400), + cliffRate: decimalStr("1"), + quoteTokenContract:"WETH9" + } + ctx = new CPContext(); + await ctx.init(config); + await init(ctx); + }); + + beforeEach(async () => { + snapshotId = await ctx.EVM.snapshot(); + }); + + afterEach(async () => { + await ctx.EVM.reset(snapshotId); + }); + + describe("eth bid & cancel", () => { + + it("bid and cancel", async () => { + await ctx.QUOTE.methods.transfer(ctx.CP.options.address, decimalStr("0.1")).send(ctx.sendParam(bidder1)) + await logGas(ctx.CP.methods.bid(bidder1), ctx.sendParam(bidder1), "bid") + assert.equal(await ctx.CP.methods.getShares(bidder1).call(), decimalStr("0.0999")) + assert.equal(await ctx.CP.methods._TOTAL_SHARES_().call(), decimalStr("0.0999")) + assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(), decimalStr("0.0001")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), decimalStr("0.1")) + + await ctx.EVM.increaseTime(86400) + await logGas(ctx.CP.methods.cancel(bidder1, decimalStr("0.05"),"0x"), ctx.sendParam(bidder1), "cancel and get 0.05 weth") + assert.equal(await ctx.CP.methods.getShares(bidder1).call(), decimalStr("0.0499")) + assert.equal(await ctx.CP.methods._TOTAL_SHARES_().call(), decimalStr("0.0499")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), decimalStr("0.15")) + + let beforeEthBalance = await ctx.Web3.eth.getBalance(bidder1); + let receipt = await logGas(ctx.CP.methods.cancel(ctx.DODOCallee.options.address, decimalStr("0.02"),"0x00"), ctx.sendParam(bidder1), "cancel and get 0.02 eth") + assert.equal(await ctx.CP.methods.getShares(bidder1).call(), decimalStr("0.0299")) + assert.equal(await ctx.CP.methods._TOTAL_SHARES_().call(), decimalStr("0.0299")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), decimalStr("0.15")) + let afterEthBalance = await ctx.Web3.eth.getBalance(bidder1); + assert.equal(Number.parseInt(receipt["events"]["1"]["raw"]["data"],16),Number(decimalStr("0.02"))); + + // assert.equal(Number(afterEthBalance) - Number(beforeEthBalance) + Number(receipt.gasUsed)*Number(mweiStr("1000")),Number(decimalStr("0.02"))); + + }) + + }) +}) diff --git a/test/utils/CrowdPoolingContext.ts b/test/utils/CrowdPoolingContext.ts index c8710a9..4be9af2 100644 --- a/test/utils/CrowdPoolingContext.ts +++ b/test/utils/CrowdPoolingContext.ts @@ -30,6 +30,7 @@ export interface CPContextInitConfig { freezeDuration: BigNumber; vestingDuration: BigNumber; cliffRate: string; + quoteTokenContract: string; } @@ -43,6 +44,7 @@ export class CPContext { Deployer: string; Maintainer: string; SpareAccounts: string[]; + DODOCallee: Contract; constructor() { } @@ -67,10 +69,18 @@ export class CPContext { contracts.MINTABLE_ERC20_CONTRACT_NAME, ["TestBase", "BASE", 18] ); - this.QUOTE = await contracts.newContract( - contracts.MINTABLE_ERC20_CONTRACT_NAME, - ["TestQuote", "QUOTE", 18] - ); + if(config.quoteTokenContract){ + this.QUOTE = await contracts.newContract( + config.quoteTokenContract, + ["TestQuote", "QUOTE", 18] + ); + }else{ + this.QUOTE = await contracts.newContract( + contracts.MINTABLE_ERC20_CONTRACT_NAME, + ["TestQuote", "QUOTE", 18] + ); + } + this.DODOCallee = await contracts.newContract(contracts.DODO_CALLEE_HELPER_NAME,[this.QUOTE.options.address]); this.DVMFactory = await contracts.newContract(contracts.DVM_FACTORY_NAME, [