diff --git a/contracts/CrowdPooling/impl/CPVesting.sol b/contracts/CrowdPooling/impl/CPVesting.sol index 4988a24..fb3bb57 100644 --- a/contracts/CrowdPooling/impl/CPVesting.sol +++ b/contracts/CrowdPooling/impl/CPVesting.sol @@ -14,6 +14,7 @@ import {Ownable} from "../../lib/Ownable.sol"; import {SafeERC20} from "../../lib/SafeERC20.sol"; import {IERC20} from "../../intf/IERC20.sol"; import {CPFunding} from "./CPFunding.sol"; +import {IDODOCallee} from "../../intf/IDODOCallee.sol"; /** * @title CPVesting @@ -38,12 +39,20 @@ contract CPVesting is CPFunding { // ============ Bidder Functions ============ - function bidderClaim() external afterSettlement { + function bidderClaim(address to,bytes calldata data) external afterSettlement { require(!_CLAIMED_[msg.sender], "ALREADY_CLAIMED"); _CLAIMED_[msg.sender] = true; - _transferBaseOut(msg.sender, _UNUSED_BASE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_)); - _transferQuoteOut(msg.sender, _UNUSED_QUOTE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_)); + uint256 baseAmount = _UNUSED_BASE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_); + uint256 quoteAmount = _UNUSED_QUOTE_.mul(_SHARES_[msg.sender]).div(_TOTAL_SHARES_); + + _transferBaseOut(to, baseAmount); + _transferQuoteOut(to, quoteAmount); + + if(data.length>0){ + IDODOCallee(to).CPClaimBidCall(msg.sender,baseAmount,quoteAmount,data); + } + } // ============ Owner Functions ============ diff --git a/contracts/SmartRoute/helper/DODOCalleeHelper.sol b/contracts/SmartRoute/helper/DODOCalleeHelper.sol index f1a7522..aa4827b 100644 --- a/contracts/SmartRoute/helper/DODOCalleeHelper.sol +++ b/contracts/SmartRoute/helper/DODOCalleeHelper.sol @@ -52,6 +52,18 @@ contract DODOCalleeHelper is ReentrancyGuard { _withdraw(assetTo, _quoteToken, amount, _quoteToken == _WETH_); } + function CPClaimBidCall( + address payable assetTo, + uint256 baseAmount, + uint256 quoteAmount, + bytes calldata + ) external preventReentrant { + address _baseToken = IDODOV2(msg.sender)._BASE_TOKEN_(); + address _quoteToken = IDODOV2(msg.sender)._QUOTE_TOKEN_(); + _withdraw(assetTo, _baseToken, baseAmount, _baseToken == _WETH_); + _withdraw(assetTo, _quoteToken, quoteAmount, _quoteToken == _WETH_); + } + function _withdraw( address payable to, address token, diff --git a/contracts/intf/IDODOCallee.sol b/contracts/intf/IDODOCallee.sol index 0dd00a4..7118809 100644 --- a/contracts/intf/IDODOCallee.sol +++ b/contracts/intf/IDODOCallee.sol @@ -36,4 +36,11 @@ interface IDODOCallee { uint256 amount, bytes calldata data ) external; + + function CPClaimBidCall( + address sender, + uint256 baseAmount, + uint256 quoteAmount, + bytes calldata data + ) external; } diff --git a/test/CrowdPooling/CPBid.test.ts b/test/CrowdPooling/CPBid.test.ts index 5743b47..cd88944 100644 --- a/test/CrowdPooling/CPBid.test.ts +++ b/test/CrowdPooling/CPBid.test.ts @@ -72,7 +72,7 @@ describe("Funding", () => { assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(), decimalStr("0.15")) await ctx.EVM.increaseTime(86400) - await logGas(ctx.CP.methods.cancel(bidder1, decimalStr("20")), ctx.sendParam(bidder1), "cancel") + await logGas(ctx.CP.methods.cancel(bidder1, decimalStr("20"),"0x"), ctx.sendParam(bidder1), "cancel") 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 index fe8bcc4..da07bd5 100644 --- a/test/CrowdPooling/CPCancelEthBid.test.ts +++ b/test/CrowdPooling/CPCancelEthBid.test.ts @@ -21,8 +21,8 @@ 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")) + await ctx.QUOTE.methods.deposit().send(ctx.sendParam(bidder1,"0.1")) + await ctx.QUOTE.methods.deposit().send(ctx.sendParam(bidder2,"0.5")) } describe("Funding", () => { @@ -31,8 +31,8 @@ describe("Funding", () => { before(async () => { config = { - totalBase: decimalStr("10000"), - poolQuoteCap: decimalStr("50000"), + totalBase: decimalStr("0.1"), + poolQuoteCap: decimalStr("0.5"), k: decimalStr("0"), i: decimalStr("10"), lpFeeRate: decimalStr("0.002"), @@ -58,31 +58,61 @@ describe("Funding", () => { describe("eth bid & cancel", () => { - it("bid and cancel", async () => { + it("cancel by callee contract", 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") + await logGas(ctx.CP.methods.bid(bidder1), ctx.sendParam(bidder1), "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")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), decimalStr("0")) - 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")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), decimalStr("0.05")) 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")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), decimalStr("0.05")) 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"))); }) + it("claim by callee contract", async () => { + await ctx.QUOTE.methods.deposit().send(ctx.sendParam(bidder1,"0.4")) + await ctx.QUOTE.methods.deposit().send(ctx.sendParam(bidder2,"0.5")) + + await ctx.QUOTE.methods.transfer(ctx.CP.options.address, decimalStr("0.5")).send(ctx.sendParam(bidder1)) + await logGas(ctx.CP.methods.bid(bidder1), ctx.sendParam(bidder1), "bidder1 bid") + await ctx.QUOTE.methods.transfer(ctx.CP.options.address, decimalStr("0.5")).send(ctx.sendParam(bidder2)) + await logGas(ctx.CP.methods.bid(bidder2), ctx.sendParam(bidder2), "bidder2 bid") + + assert.equal(await ctx.CP.methods.getShares(bidder1).call(), decimalStr("0.4995")) + assert.equal(await ctx.CP.methods.getShares(bidder2).call(), decimalStr("0.4995")) + assert.equal(await ctx.CP.methods._TOTAL_SHARES_().call(), decimalStr("0.999")) + assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), decimalStr("0.999")) + assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.Maintainer).call(), decimalStr("0.001")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), decimalStr("0")) + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder2).call(), decimalStr("0.5")) + + await ctx.EVM.increaseTime(86400 *2) + await logGas(ctx.CP.methods.settle(), ctx.sendParam(ctx.Deployer), "settle") + assert.equal(await ctx.QUOTE.methods.balanceOf(ctx.CP.options.address).call(), decimalStr("0.499")) + + await ctx.EVM.increaseTime(86400 * 2) + assert.equal(await ctx.BASE.methods.balanceOf(bidder1).call(), "0") + let receipt1 = await logGas(await ctx.CP.methods.bidderClaim(bidder1,"0x"),ctx.sendParam(bidder1),"claim"); + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder1).call(), decimalStr("0.2495")) + + let receipt2 = await logGas(await ctx.CP.methods.bidderClaim(ctx.DODOCallee.options.address,"0x00"),ctx.sendParam(bidder2),"claim"); + assert.equal(await ctx.QUOTE.methods.balanceOf(bidder2).call(), decimalStr("0.5")) + assert.equal(Number.parseInt(receipt2["events"]["3"]["raw"]["data"],16),Number(decimalStr("0.2495"))); + + }) + }) }) diff --git a/test/CrowdPooling/CPVesting.test.ts b/test/CrowdPooling/CPVesting.test.ts index 690bc8b..36bc08b 100644 --- a/test/CrowdPooling/CPVesting.test.ts +++ b/test/CrowdPooling/CPVesting.test.ts @@ -39,6 +39,7 @@ describe("Funding", () => { freezeDuration: new BigNumber(86400), vestingDuration: new BigNumber(86400), cliffRate: decimalStr("1"), + quoteTokenContract:"" } ctx = new CPContext(); await ctx.init(config); diff --git a/test/utils/Log.ts b/test/utils/Log.ts index 7e7c3b7..2ad8f6c 100644 --- a/test/utils/Log.ts +++ b/test/utils/Log.ts @@ -13,6 +13,7 @@ export const numberWithCommas = x => x.toString().replace(/\B(?=(\d{3})+(?!\d))/ export async function logGas(funcCall: any, params: any, desc: string) { const estimatedGas = await funcCall.estimateGas(params) + const receipt = await funcCall.send(params) const gasUsed = receipt.gasUsed; let colorFn; @@ -27,4 +28,4 @@ export async function logGas(funcCall: any, params: any, desc: string) { console.log(("Gas estimated:" + numberWithCommas(estimatedGas)).padEnd(60, '.'), blueText(desc) + " ", colorFn(numberWithCommas(gasUsed).padStart(5))); return receipt -} \ No newline at end of file +}