From 6df2123f910fb48b1bb5aefeb16d717824ab11b9 Mon Sep 17 00:00:00 2001 From: lacoop6tu Date: Thu, 28 Oct 2021 17:10:26 -0500 Subject: [PATCH] add initial multi decimals support, add USDC test --- src/pools/balancer/Pool.ts | 196 ++++++++--- test/unit/pools/balancer/Pool.test.ts | 452 +++++++++++++++++++++++++- 2 files changed, 594 insertions(+), 54 deletions(-) diff --git a/src/pools/balancer/Pool.ts b/src/pools/balancer/Pool.ts index b571abca..6042e20b 100644 --- a/src/pools/balancer/Pool.ts +++ b/src/pools/balancer/Pool.ts @@ -341,12 +341,12 @@ export class Pool { try { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) const result = await pool.methods.getBalance(token).call() - amount = this.web3.utils.fromWei(result) + amount = await this.unitsToAmount(token,result) } catch (e) { this.logger.error(`ERROR: Failed to get how many tokens \ are in the pool: ${e.message}`) } - return amount + return amount.toString() } /** @@ -700,9 +700,9 @@ export class Pool { estGas = await poolContract.methods .swapExactAmountIn( tokenIn, - this.web3.utils.toWei(tokenAmountIn), + tokenAmountIn, tokenOut, - this.web3.utils.toWei(minAmountOut), + minAmountOut, maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256 ) .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) @@ -712,6 +712,46 @@ export class Pool { return estGas } + async amountToUnits(token:string,amount:string):Promise{ + let decimals = 18 + let amountFormatted + const tokenContract = new this.web3.eth.Contract( + defaultERC20ABI.abi as AbiItem[], + token + ) + try { + decimals = await tokenContract.methods.decimals().call() + } catch (e) { + this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') + } + console.log(decimals,'decimals') + + amountFormatted = new BigNumber(parseInt(amount) * 10 ** decimals) + + return amountFormatted + + } + + async unitsToAmount(token:string,amount:string):Promise{ + let decimals = 18 + let amountFormatted + const tokenContract = new this.web3.eth.Contract( + defaultERC20ABI.abi as AbiItem[], + token + ) + try { + decimals = await tokenContract.methods.decimals().call() + } catch (e) { + this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') + } + console.log(decimals,'decimals') + + amountFormatted = new BigNumber(parseInt(amount) /(10 ** decimals) ) + + return amountFormatted + + } + /** * swapExactAmountIn - Trades an exact tokenAmountIn of tokenIn taken from the caller by the pool, in exchange for at least minAmountOut of tokenOut given to the caller from the pool, with a maximum marginal price of maxPrice. Returns (tokenAmountOut, spotPriceAfter), where tokenAmountOut is the amount of token that came out of the pool, and spotPriceAfter is the new marginal spot price, ie, the result of getSpotPrice after the call. (These values are what are limited by the arguments; you are guaranteed tokenAmountOut >= minAmountOut and spotPriceAfter <= maxPrice). * @param {String} address @@ -733,25 +773,64 @@ export class Pool { maxPrice?: string ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + const tokenInContract = new this.web3.eth.Contract( + defaultERC20ABI.abi as AbiItem[], + tokenIn + ) + + let amountInFormatted + let minAmountOutFormatted + + // let tokenInDecimals = 18 + // let tokenOutDecimals = 18 + // try { + // tokenInDecimals = await tokenInContract.methods.decimals().call() + // } catch (e) { + // this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') + // } + // try { + // tokenOutDecimals = await tokenOutContract.methods.decimals().call() + // } catch (e) { + // this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') + // } + amountInFormatted = await this.amountToUnits(tokenIn,tokenAmountIn) + console.log(amountInFormatted,'amountinformatted') + //const tokenInDecimals = await tokenInContract.methods.decimals().call() + // if (tokenInDecimals == 18) { + // amountInFormatted = this.web3.utils.toWei(tokenAmountIn) + // } else { + // amountInFormatted = parseInt(tokenAmountIn) * 10 ** tokenInDecimals + // } + + minAmountOutFormatted = await this.amountToUnits(tokenOut,minAmountOut) + // console.log(test) + // if (tokenOutDecimals == 18) { + // minAmountOutFormatted = this.web3.utils.toWei(minAmountOut) + // } else { + // minAmountOutFormatted = parseInt(minAmountOut) * 10 ** tokenOutDecimals + // } + // console.log(tokenInDecimals,'tokenin decimals') + // console.log(tokenOutDecimals,'token out decimals') + let result = null // TODO: add multiple decimals support const estGas = await this.estSwapExactAmountIn( address, poolAddress, tokenIn, - this.web3.utils.toWei(tokenAmountIn), + amountInFormatted, tokenOut, - this.web3.utils.toWei(minAmountOut), + minAmountOutFormatted, maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256 ) - + console.log(minAmountOutFormatted,'minamoutnoutformatted') try { result = await pool.methods .swapExactAmountIn( tokenIn, - this.web3.utils.toWei(tokenAmountIn), + amountInFormatted, tokenOut, - this.web3.utils.toWei(minAmountOut), + minAmountOutFormatted, maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256 ) .send({ @@ -759,9 +838,11 @@ export class Pool { gas: estGas + 1, gasPrice: await getFairGasPrice(this.web3) }) + } catch (e) { this.logger.error(`ERROR: Failed to swap exact amount in : ${e.message}`) } + return result } @@ -831,14 +912,17 @@ export class Pool { ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let result = null - + let maxAmountInFormatted + let amountOutFormatted + maxAmountInFormatted = await this.amountToUnits(tokenIn,maxAmountIn) + amountOutFormatted = await this.amountToUnits(tokenOut,amountOut) const estGas = await this.estSwapExactAmountOut( account, poolAddress, tokenIn, - this.web3.utils.toWei(maxAmountIn), + maxAmountInFormatted, tokenOut, - this.web3.utils.toWei(amountOut), + amountOutFormatted, maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256 ) @@ -846,9 +930,9 @@ export class Pool { result = await pool.methods .swapExactAmountOut( tokenIn, - this.web3.utils.toWei(maxAmountIn), + maxAmountInFormatted, tokenOut, - this.web3.utils.toWei(amountOut), + amountOutFormatted, maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256 ) .send({ @@ -910,12 +994,15 @@ export class Pool { ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) const weiMaxAmountsIn = [] - - let amount: string - - for (amount of maxAmountsIn) { - weiMaxAmountsIn.push(this.web3.utils.toWei(amount)) + const tokens = await this.getFinalTokens(poolAddress) + + + for (let i=0;i<2;i++){ + const amount = await this.amountToUnits(tokens[i],maxAmountsIn[i]) + weiMaxAmountsIn.push(amount) } + //console.log(weiMaxAmountsIn) + let result = null @@ -988,10 +1075,12 @@ export class Pool { ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) const weiMinAmountsOut = [] - let amount: string - - for (amount of minAmountsOut) { - weiMinAmountsOut.push(this.web3.utils.toWei(amount)) + const tokens = await this.getFinalTokens(poolAddress) + + + for (let i=0;i<2;i++){ + const amount = await this.amountToUnits(tokens[i],minAmountsOut[i]) + weiMinAmountsOut.push(amount) } let result = null const estGas = await this.estExitPool( @@ -1062,12 +1151,13 @@ export class Pool { ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let result = null - + let amountInFormatted + amountInFormatted = await this.amountToUnits(tokenIn,tokenAmountIn) const estGas = await this.estJoinswapExternAmountIn( account, poolAddress, tokenIn, - this.web3.utils.toWei(tokenAmountIn), + amountInFormatted, this.web3.utils.toWei(minPoolAmountOut) ) @@ -1075,7 +1165,7 @@ export class Pool { result = await pool.methods .joinswapExternAmountIn( tokenIn, - this.web3.utils.toWei(tokenAmountIn), + amountInFormatted, this.web3.utils.toWei(minPoolAmountOut) ) .send({ @@ -1144,20 +1234,21 @@ export class Pool { ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let result = null - + let maxAmountInFormatted + maxAmountInFormatted = await this.amountToUnits(tokenIn,maxAmountIn) const estGas = await this.estJoinswapPoolAmountOut( account, poolAddress, tokenIn, this.web3.utils.toWei(poolAmountOut), - this.web3.utils.toWei(maxAmountIn) + maxAmountInFormatted ) try { result = await pool.methods .joinswapPoolAmountOut( tokenIn, this.web3.utils.toWei(poolAmountOut), - this.web3.utils.toWei(maxAmountIn) + maxAmountInFormatted ) .send({ from: account, @@ -1222,20 +1313,21 @@ export class Pool { ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let result = null - + let minTokenOutFormatted + minTokenOutFormatted = await this.amountToUnits(tokenOut,minTokenAmountOut) const estGas = await this.estExitswapPoolAmountIn( account, poolAddress, tokenOut, this.web3.utils.toWei(poolAmountIn), - this.web3.utils.toWei(minTokenAmountOut) + minTokenOutFormatted ) try { result = await pool.methods .exitswapPoolAmountIn( tokenOut, this.web3.utils.toWei(poolAmountIn), - this.web3.utils.toWei(minTokenAmountOut) + minTokenOutFormatted ) .send({ from: account, @@ -1383,13 +1475,15 @@ export class Pool { tokenAmountOut: string ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let amountOutFormatted + amountOutFormatted = await this.amountToUnits(tokenOut,tokenAmountOut) let amount = null // if (new Decimal(tokenAmountOut).gte(tokenBalanceOut)) return null try { const result = await pool.methods - .getAmountInExactOut(tokenIn, tokenOut,this.web3.utils.toWei(tokenAmountOut)) + .getAmountInExactOut(tokenIn, tokenOut,amountOutFormatted) .call() - amount = this.web3.utils.fromWei(result) + amount = await this.unitsToAmount(tokenIn,result) } catch (e) { this.logger.error('ERROR: Failed to calcInGivenOut') } @@ -1409,31 +1503,27 @@ export class Pool { tokenIn ) let amountInFormatted - let tokenInDecimals = 18 - try { - tokenInDecimals = await tokenInContract.methods.decimals().call() - } catch (e) { - this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') - } - //const tokenInDecimals = await tokenInContract.methods.decimals().call() - if (tokenInDecimals == 18) { - amountInFormatted = this.web3.utils.toWei(tokenAmountIn) - } else { - amountInFormatted = parseInt(tokenAmountIn) * 10 ** tokenInDecimals - } + amountInFormatted = await this.amountToUnits(tokenIn,tokenAmountIn) + // let tokenInDecimals = 18 + // try { + // tokenInDecimals = await tokenInContract.methods.decimals().call() + // } catch (e) { + // this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') + // } + // //const tokenInDecimals = await tokenInContract.methods.decimals().call() + // if (tokenInDecimals == 18) { + // amountInFormatted = this.web3.utils.toWei(tokenAmountIn) + // } else { + // amountInFormatted = parseInt(tokenAmountIn) * 10 ** tokenInDecimals + // } let amount = null console.log(amountInFormatted) try { const result = await pool.methods .getAmountOutExactIn(tokenIn, tokenOut, amountInFormatted) .call() - console.log(result) - if (tokenInDecimals == 18){ - amount = this.web3.utils.fromWei(result) - } - else{ - amount = parseInt(result) / (10**tokenInDecimals) - } + amount = await this.unitsToAmount(tokenOut,result) + } catch (e) { this.logger.error('ERROR: Failed to calcOutGivenIn') } diff --git a/test/unit/pools/balancer/Pool.test.ts b/test/unit/pools/balancer/Pool.test.ts index 3cbe194c..380ceda4 100644 --- a/test/unit/pools/balancer/Pool.test.ts +++ b/test/unit/pools/balancer/Pool.test.ts @@ -35,6 +35,7 @@ describe('Pool unit test', () => { let erc20Token: string let erc20Contract: Contract let daiContract: Contract + let usdcContract: Contract it('should deploy contracts', async () => { contracts = new TestContractHandler( @@ -72,13 +73,23 @@ describe('Pool unit test', () => { contracts.MockERC20.options.jsonInterface, contracts.daiAddress ) + + usdcContract = new web3.eth.Contract( + contracts.MockERC20.options.jsonInterface, + contracts.usdcAddress + ) await daiContract.methods .approve(contracts.factory721Address, web3.utils.toWei('10000')) .send({ from: contracts.accounts[0] }) + await usdcContract.methods + .approve(contracts.factory721Address, web3.utils.toWei('10000')) + .send({ from: contracts.accounts[0] }) expect(await daiContract.methods.balanceOf(contracts.accounts[0]).call()).to.equal( web3.utils.toWei('100000') ) + console.log(await usdcContract.methods.balanceOf(contracts.accounts[0]).call()) + console.log(await usdcContract.methods.decimals().call()) }) it('should initiate Pool instance', async () => { @@ -86,6 +97,7 @@ describe('Pool unit test', () => { }) +describe('Test a pool with DAI (18 Decimals)',()=>{ it('#create a pool', async () => { // CREATE A POOL // we prepare transaction parameters objects @@ -400,7 +412,7 @@ describe('Pool unit test', () => { contracts.daiAddress, exactDAIOut ) - + assert(amountIn != null) // console.log(tx) @@ -518,3 +530,441 @@ describe('Pool unit test', () => { }) }) + +describe('Test a pool with USDC (6 Decimals)',()=>{ + it('#create a pool', async () => { + // CREATE A POOL + // we prepare transaction parameters objects + const nftData = { + name: '72120Bundle', + symbol: '72Bundle', + templateIndex: 1, + baseURI: 'https://oceanprotocol.com/nft/' + } + const ercData = { + templateIndex: 1, + strings: ['ERC20B1', 'ERC20DT1Symbol'], + addresses: [ + contracts.accounts[0], + user3, + contracts.accounts[0], + '0x0000000000000000000000000000000000000000' + ], + uints: [web3.utils.toWei('1000000'), 0], + bytess: [] + } + + const poolData = { + addresses: [ + contracts.sideStakingAddress, + contracts.usdcAddress, + contracts.factory721Address, + contracts.accounts[0], + contracts.accounts[0], + contracts.poolTemplateAddress + ], + ssParams: [ + web3.utils.toWei('1'), // rate + 6, // basetokenDecimals + web3.utils.toWei('10000'), + 2500000, // vested blocks + 2000 * 1e6 // baseToken initial pool liquidity + ], + swapFees: [ + 1e15, // + 1e15 + ] + } + + const nftFactory = new NFTFactory(contracts.factory721Address, web3, LoggerInstance) + + const txReceipt = await nftFactory.createNftErcWithPool( + contracts.accounts[0], + nftData, + ercData, + poolData + ) + + erc20Token = txReceipt.events.TokenCreated.returnValues.newTokenAddress + poolAddress = txReceipt.events.NewPool.returnValues.poolAddress + + erc20Contract = new web3.eth.Contract(ERC20Template.abi as AbiItem[], erc20Token) + // user2 has no dt1 + expect(await erc20Contract.methods.balanceOf(user2).call()).to.equal('0') + }) + + it('#sharesBalance - should return user shares balance (datatoken balance, LPT balance, etc) ', async () => { + expect(await usdcContract.methods.balanceOf(user2).call()).to.equal( + await pool.sharesBalance(user2, contracts.usdcAddress) + ) + }) + + it('#getNumTokens - should return num of tokens in pool (2)', async () => { + expect(await pool.getNumTokens(poolAddress)).to.equal('2') + }) + + it('#getPoolSharesTotalSupply - should return totalSupply of LPT', async () => { + // dt owner which added liquidity has half of pool shares (the rest is in the sidestaking contracta) + const dtOwnerLPTBalance = await pool.sharesBalance(contracts.accounts[0], poolAddress) + expect(await pool.sharesBalance(contracts.accounts[0], poolAddress)).to.equal( + await pool.sharesBalance(contracts.sideStakingAddress, poolAddress) + ) + // total supply is twice the dtOwner balance + expect(await pool.getPoolSharesTotalSupply(poolAddress)).to.equal( + (2 * Number(dtOwnerLPTBalance)).toString() + ) + }) + it('#getCurrentTokens - should return current pool tokens', async () => { + const currentTokens = await pool.getCurrentTokens(poolAddress) + expect(currentTokens[0]).to.equal(erc20Token) + expect(currentTokens[1]).to.equal(contracts.usdcAddress) + }) + + it('#getFinalTokens - should return final pool tokens', async () => { + const finalTokens = await pool.getFinalTokens(poolAddress) + expect(finalTokens[0]).to.equal(erc20Token) + expect(finalTokens[1]).to.equal(contracts.usdcAddress) + }) + + it('#getController - should return the pool controller (sideStaking address)', async () => { + expect(await pool.getController(poolAddress)).to.equal(contracts.sideStakingAddress) + }) + + it('#isBound - should return true if token is bound into the pool', async () => { + expect(await pool.isBound(poolAddress, contracts.usdcAddress)).to.equal(true) + expect(await pool.isBound(poolAddress, contracts.oceanAddress)).to.equal(false) + }) + + it('#getReserve - should return final pool tokens', async () => { + expect(await pool.getReserve(poolAddress, contracts.usdcAddress)).to.equal('2000') // base token initial liquidity + // rate is 1 so we have the same amount of DTs + expect(await pool.getReserve(poolAddress, erc20Token)).to.equal('2000') + }) + + it('#isFinalized - should return true if pool is finalized', async () => { + expect(await pool.isFinalized(poolAddress)).to.equal(true) + expect(await pool.isFinalized(contracts.oceanAddress)).to.equal(null) + }) + + it('#getSwapFee - should return the swap fee', async () => { + expect(await pool.getSwapFee(poolAddress)).to.equal('0.001') //0.1% + }) + + it('#getNormalizedWeight - should return the normalized weight', async () => { + expect(await pool.getNormalizedWeight(poolAddress, contracts.usdcAddress)).to.equal( + '0.5' + ) + expect(await pool.getNormalizedWeight(poolAddress, erc20Token)).to.equal('0.5') + }) + + it('#getDenormalizedWeight - should return the denormalized weight', async () => { + expect(await pool.getDenormalizedWeight(poolAddress, contracts.usdcAddress)).to.equal( + '5' + ) + expect(await pool.getDenormalizedWeight(poolAddress, erc20Token)).to.equal('5') + }) + + it('#getBasetoken - should return the basetoken address', async () => { + expect(await pool.getBasetoken(poolAddress)).to.equal( + contracts.usdcAddress + ) + + }) + + it('#getDatatoken - should return the datatoken address', async () => { + expect(await pool.getDatatoken(poolAddress)).to.equal( + erc20Token + ) + }) + + it('#swapExactAmountIn - should swap', async () => { + const transferAmount = 1e9 // 1000 USDC + await usdcContract.methods + .transfer(user2, transferAmount) + .send({ from: contracts.accounts[0] }) + expect(await usdcContract.methods.balanceOf(user2).call()).to.equal( + transferAmount.toString()) + + expect(await erc20Contract.methods.balanceOf(user2).call()).to.equal('0') + await pool.approve(user2, contracts.usdcAddress, poolAddress, web3.utils.toWei('100')) + const tx = await pool.swapExactAmountIn( + user2, + poolAddress, + contracts.usdcAddress, + '10', + erc20Token, + '1' + ) + expect(await erc20Contract.methods.balanceOf(user2).call()).to.equal( + tx.events.LOG_SWAP.returnValues.tokenAmountOut + ) + }) + + it('#swapExactAmountOut - should swap', async () => { + + expect(await usdcContract.methods.balanceOf(user2).call()).to.equal( + (990*1e6).toString() + ) + const tx = await pool.swapExactAmountOut( + user2, + poolAddress, + contracts.usdcAddress, + '100', + erc20Token, + '50' + ) + assert(tx != null) + //console.log(tx.events) + }) + + it('#joinPool- user2 should add liquidity, receiving LP tokens', async () => { + const BPTAmountOut = '0.01' + const maxAmountsIn = [ + '50', // Amounts IN + '50' // Amounts IN + ] + + await pool.approve(user2, erc20Token, poolAddress, web3.utils.toWei('1000')) + await pool.approve(user2, contracts.usdcAddress, poolAddress, '1000') + const tx = await pool.joinPool(user2, poolAddress, BPTAmountOut, maxAmountsIn) + assert(tx != null) + expect(await pool.sharesBalance(user2, poolAddress)).to.equal(BPTAmountOut) + expect(tx.events.LOG_JOIN.event === 'LOG_JOIN') + expect(tx.events.LOG_BPT.event === 'LOG_BPT') + + //console.log(tx) + // console.log(tx.events.LOG_JOIN) + // console.log(tx.events.LOG_BPT) + }) + it('#joinswapExternAmountIn- user2 should add liquidity, receiving LP tokens', async () => { + const usdcAmountIn = '100' + const minBPTOut = '0.1' + await pool.approve(user2, contracts.usdcAddress, poolAddress, web3.utils.toWei('1000')) + + const tx = await pool.joinswapExternAmountIn( + user2, + poolAddress, + contracts.usdcAddress, + usdcAmountIn, + minBPTOut + ) + + assert(tx != null) + + expect(tx.events.LOG_JOIN[0].event === 'LOG_JOIN') + expect(tx.events.LOG_BPT.event === 'LOG_BPT') + // 2 JOIN EVENTS BECAUSE SIDE STAKING ALSO STAKED DTs, TODO: we should add to whom has been sent in the LOG_BPT event + expect(tx.events.LOG_JOIN[0].returnValues.bptAmount).to.equal( + tx.events.LOG_JOIN[0].returnValues.bptAmount + ) + }) + + it('#joinswapPoolAmountOut- user2 should add liquidity, receiving LP tokens', async () => { + const BPTAmountOut = '0.1' + const maxUSDCIn = '100' + + await pool.approve(user2, contracts.usdcAddress, poolAddress, web3.utils.toWei('1000')) + + const tx = await pool.joinswapPoolAmountOut( + user2, + poolAddress, + contracts.usdcAddress, + BPTAmountOut, + maxUSDCIn + ) + + assert(tx != null) + + expect(tx.events.LOG_JOIN[0].event === 'LOG_JOIN') + expect(tx.events.LOG_BPT.event === 'LOG_BPT') + // 2 JOIN EVENTS BECAUSE SIDE STAKING ALSO STAKED DTs, TODO: we should add to whom has been sent in the LOG_BPT event + expect(tx.events.LOG_JOIN[0].returnValues.bptAmount).to.equal( + tx.events.LOG_JOIN[0].returnValues.bptAmount + ) + }) + + it('#exitPool- user2 exit the pool receiving both tokens, burning LP', async () => { + const BPTAmountIn = '0.5' + const minAmountOut = [ + '1', // min amount out for USDC AND DT + '1' + ] + + const tx = await pool.exitPool(user2, poolAddress, BPTAmountIn, minAmountOut) + + assert(tx != null) + + expect(tx.events.LOG_EXIT[0].returnValues.tokenOut).to.equal(erc20Token) + expect(tx.events.LOG_EXIT[1].returnValues.tokenOut).to.equal(contracts.usdcAddress) + }) + + it('#exitswapPoolAmountIn- user2 exit the pool receiving only USDC', async () => { + const BPTAmountIn = '0.5' + const minUSDCOut = '0.5' + + const tx = await pool.exitswapPoolAmountIn( + user2, + poolAddress, + contracts.usdcAddress, + BPTAmountIn, + minUSDCOut + ) + + assert(tx != null) + + expect(tx.events.LOG_EXIT[0].returnValues.tokenOut).to.equal(contracts.usdcAddress) + + // DTs were also unstaked in the same transaction (went to the staking contract) + expect(tx.events.LOG_EXIT[1].returnValues.tokenOut).to.equal(erc20Token) + }) + + it('#exitswapExternAmountOut- user2 exit the pool receiving only USDC', async () => { + const maxBTPIn = "0.5" + const exactUSDCOut = "1" + + const tx = await pool.exitswapPoolAmountIn( + user2, + poolAddress, + contracts.usdcAddress, + maxBTPIn, + exactUSDCOut + ) + + assert(tx != null) + + expect(tx.events.LOG_EXIT[0].returnValues.tokenOut).to.equal(contracts.usdcAddress) + + // DTs were also unstaked in the same transaction (went to the staking contract) + expect(tx.events.LOG_EXIT[1].returnValues.tokenOut).to.equal(erc20Token) + }) + + it('#getAmountInExactOut- should get the amount in for exact out', async () => { + const maxBTPIn = "0.5" + const exactUSDCOut = "1" + + const amountIn = await pool.getAmountInExactOut( + poolAddress, + erc20Token, + contracts.usdcAddress, + exactUSDCOut + ) + + assert(amountIn != null) + + console.log(amountIn.toString()) + + const spotPrice = await pool.getSpotPrice(poolAddress,erc20Token,contracts.usdcAddress) + console.log(spotPrice.toString()) + // amount of USDC In will be slightly bigger than spotPrice + // assert(amountIn >spotPrice) + }) + + it('#getAmountOutExactIn- should get the amount out for exact In', async () => { + const exactDTIn= "1" + + const amountOut = await pool.getAmountOutExactIn( + poolAddress, + erc20Token, + contracts.usdcAddress, + exactDTIn + ) + + assert(amountOut != null) + + console.log(amountOut) + + const spotPrice = await pool.getSpotPrice(poolAddress,contracts.usdcAddress,erc20Token) + console.log(spotPrice) + // amount of USDC received will be slightly less than spotPrice + // assert(amountOut< spotPrice) + }) + + it('#getSpotPrice- should get the spot price', async () => { + + + assert(await pool.getSpotPrice(poolAddress,erc20Token,contracts.usdcAddress) != null) + assert(await pool.getSpotPrice(poolAddress,contracts.usdcAddress,erc20Token) != null) + + }) + + it('#getMarketFees- should get market fees for each token', async () => { + + // we haven't performed any swap DT => USDC so there's no fee in erc20Token + // but there's a fee in USDC + assert(await pool.getMarketFees(poolAddress,erc20Token) == '0') + assert(await pool.getMarketFees(poolAddress,contracts.usdcAddress) > '0') + + }) + + it('#getCommunityFees- should get community fees for each token', async () => { + // we haven't performed any swap DT => USDC so there's no fee in erc20Token + // but there's a fee in USDC + + assert(await pool.getCommunityFees(poolAddress,erc20Token) == '0') + assert(await pool.getCommunityFees(poolAddress,contracts.usdcAddress) > '0') + + }) + + it('#collectMarketFee- should collect market fees for each token', async () => { + const spotPriceBefore = await pool.getSpotPrice(poolAddress,erc20Token,contracts.usdcAddress) + // contracts.accounts[0] is the marketFeeCollector + assert(await pool.getMarketFeeCollector(poolAddress) == contracts.accounts[0]) + // user3 has no USDC (we are going to send USDC fee to him) + assert(await usdcContract.methods.balanceOf(user3).call() == '0') + // only marketFeeCollector can call this, set user3 as receiver + await pool.collectMarketFee(contracts.accounts[0],poolAddress,user3) + // USDC fees have been collected + assert(await pool.getMarketFees(poolAddress,contracts.usdcAddress) == '0') + // user3 got USDC + assert(await usdcContract.methods.balanceOf(user3).call() > '0') + // Spot price hasn't changed after fee collection + assert(await pool.getSpotPrice(poolAddress,erc20Token,contracts.usdcAddress)== spotPriceBefore) + }) + + + it('#getMarketFeeCollector- should get market fees for each token', async () => { + + // contracts.accounts[0] is the marketFeeCollector + assert(await pool.getMarketFeeCollector(poolAddress) == contracts.accounts[0]) + + + }) + + + it('#getOPFCollector- should get market fees for each token', async () => { + + + assert(await pool.getOPFCollector(poolAddress) == contracts.opfCollectorAddress) + + + }) + + it('#collectCommunityFee- should get community fees for each token', async () => { + const spotPriceBefore = await pool.getSpotPrice(poolAddress,erc20Token,contracts.usdcAddress) + // some fee are available in USDC + assert(await pool.getCommunityFees(poolAddress,contracts.usdcAddress) > '0') + // opf collector has no USDC + assert(await usdcContract.methods.balanceOf(contracts.opfCollectorAddress).call() == '0') + // anyone can call callectOPF + await pool.collectOPF(contracts.accounts[0],poolAddress) + // USDC fees have been collected + assert(await pool.getCommunityFees(poolAddress,contracts.usdcAddress) == '0') + // OPF collector got USDC + assert(await usdcContract.methods.balanceOf(contracts.opfCollectorAddress).call() > '0') + // Spot price hasn't changed after fee collection + assert(await pool.getSpotPrice(poolAddress,erc20Token,contracts.usdcAddress)== spotPriceBefore) + }) + + it('#updateMarketFeeCollector- should update market fee collector', async () => { + + // contracts.accounts[0] is the marketFeeCollector + assert(await pool.getMarketFeeCollector(poolAddress) == contracts.accounts[0]) + + await pool.updateMarketFeeCollector(contracts.accounts[0],poolAddress,user3) + + assert(await pool.getMarketFeeCollector(poolAddress) == user3) + + }) +}) + +})