From 6e135231d3f575e726b7cceeb118f8cd31ef79f2 Mon Sep 17 00:00:00 2001 From: lacoop6tu Date: Wed, 27 Oct 2021 16:29:13 -0500 Subject: [PATCH] add more Pool tests --- src/pools/balancer/Pool.ts | 52 +++--- src/pools/balancer/index.ts | 3 +- test/unit/pools/balancer/Pool.test.ts | 247 +++++++++++++++++++++++--- 3 files changed, 252 insertions(+), 50 deletions(-) diff --git a/src/pools/balancer/Pool.ts b/src/pools/balancer/Pool.ts index a6180ee8..4c7b02c7 100644 --- a/src/pools/balancer/Pool.ts +++ b/src/pools/balancer/Pool.ts @@ -14,7 +14,7 @@ const MaxUint256 = /** * Provides an interface to Ocean friendly fork from Balancer BPool */ - +// TODO: Add decimals handling export class Pool { public poolABI: AbiItem | AbiItem[] public web3: Web3 @@ -35,12 +35,12 @@ export class Pool { * @param {String} spender */ public async allowance( - tokenAdress: string, + tokenAddress: string, owner: string, spender: string ): Promise { const tokenAbi = defaultERC20ABI.abi as AbiItem[] - const datatoken = new this.web3.eth.Contract(tokenAbi, tokenAdress, { + const datatoken = new this.web3.eth.Contract(tokenAbi, tokenAddress, { from: spender }) const trxReceipt = await datatoken.methods.allowance(owner, spender).call() @@ -391,7 +391,7 @@ export class Pool { minAmountOut: string, maxPrice: string, contractInstance?: Contract - ) { + ) :Promise{ const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -488,7 +488,7 @@ export class Pool { amountOut: string, maxPrice?: string, contractInstance?: Contract - ) { + ) :Promise{ const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -579,7 +579,7 @@ export class Pool { poolAmountOut: string, maxAmountsIn: string[], contractInstance?: Contract - ) { + ) :Promise{ const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -657,7 +657,7 @@ export class Pool { poolAmountIn: string, minAmountsOut: string[], contractInstance?: Contract - ) { + ) :Promise{ const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -696,13 +696,12 @@ export class Pool { weiMinAmountsOut.push(this.web3.utils.toWei(amount)) } let result = null - const estGas = this.estExitPool( + const estGas = await this.estExitPool( account, poolAddress, this.web3.utils.toWei(poolAmountIn), weiMinAmountsOut ) - try { result = await pool.methods .exitPool(this.web3.utils.toWei(poolAmountIn), weiMinAmountsOut) @@ -730,7 +729,7 @@ export class Pool { tokenAmountIn: string, minPoolAmountOut: string, contractInstance?: Contract - ) { + ) :Promise{ const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -794,7 +793,7 @@ export class Pool { } /** - * Estimate gas cost for joinswapExternAmountOut + * Estimate gas cost for joinswapPoolAmountOut * @param {String} address * @param {String} poolAddress * @param {String} tokenIn @@ -803,14 +802,14 @@ export class Pool { * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estJoinswapExternAmountOut( + public async estJoinswapPoolAmountOut( address: string, poolAddress: string, tokenIn: string, poolAmountOut: string, maxAmountIn: string, contractInstance?: Contract - ) { + ) :Promise{ const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -819,13 +818,16 @@ export class Pool { let estGas try { estGas = await poolContract.methods - .joinswapExternAmountOut(tokenIn, poolAmountOut, maxAmountIn) + .joinswapPoolAmountOut(tokenIn, poolAmountOut, maxAmountIn) .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) } catch (e) { estGas = gasLimitDefault } return estGas } + + + /** * Specify poolAmountOut pool shares that you want to get, and a token tokenIn to pay with. This costs tokenAmountIn tokens (these went into the pool). * @param {String} account @@ -845,7 +847,7 @@ export class Pool { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let result = null - const estGas = await this.estJoinswapExternAmountOut( + const estGas = await this.estJoinswapPoolAmountOut( account, poolAddress, tokenIn, @@ -887,7 +889,7 @@ export class Pool { poolAmountIn: string, minTokenAmountOut: string, contractInstance?: Contract - ) { + ) :Promise{ const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -965,7 +967,7 @@ export class Pool { tokenAmountOut: string, maxPoolAmountIn: string, contractInstance?: Contract - ) { + ) :Promise{ const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -1086,10 +1088,10 @@ export class Pool { let amount = null // if (new Decimal(tokenAmountOut).gte(tokenBalanceOut)) return null try { - const result = await pool.methods + const result = await pool.methods .getAmountInExactOut(tokenIn, tokenOut, tokenAmountOut) .call() - // amount = this.web3.utils.fromWei(result) + amount = this.web3.utils.fromWei(result) } catch (e) { this.logger.error('ERROR: Failed to calcInGivenOut') } @@ -1102,17 +1104,18 @@ export class Pool { tokenOut: string, tokenAmountIn: string ): Promise { + // TODO: solve decimals issue FIRST 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 tokenInDecimals + let tokenInDecimals = 18 try { tokenInDecimals = await tokenInContract.methods.decimals().call() } catch (e) { - this.logger.error('ERROR: FAILED TO CALL DECIMALS()') + this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') } //const tokenInDecimals = await tokenInContract.methods.decimals().call() if (tokenInDecimals == 18) { @@ -1125,7 +1128,12 @@ export class Pool { const result = await pool.methods .getAmountOutExactIn(tokenIn, tokenOut, amountInFormatted) .call() - amount = this.web3.utils.fromWei(result) + if (tokenInDecimals == 18){ + amount = this.web3.utils.fromWei(result) + } + else{ + amount = parseInt(result) / (10**tokenInDecimals) + } } catch (e) { this.logger.error('ERROR: Failed to calcOutGivenIn') } diff --git a/src/pools/balancer/index.ts b/src/pools/balancer/index.ts index 9c700234..0d56f727 100644 --- a/src/pools/balancer/index.ts +++ b/src/pools/balancer/index.ts @@ -1,2 +1 @@ -export * from './PoolFactory' -export * from './OceanPool' +export * from './Pool' diff --git a/test/unit/pools/balancer/Pool.test.ts b/test/unit/pools/balancer/Pool.test.ts index 8183b747..e867eb4a 100644 --- a/test/unit/pools/balancer/Pool.test.ts +++ b/test/unit/pools/balancer/Pool.test.ts @@ -78,11 +78,9 @@ describe('Pool unit test', () => { }) it('should initiate Pool instance', async () => { - pool = new Pool(web3, LoggerInstance,PoolTemplate.abi as AbiItem[]) - + pool = new Pool(web3, LoggerInstance, PoolTemplate.abi as AbiItem[]) }) - it('#create a pool', async () => { // CREATE A POOL // we prepare transaction parameters objects @@ -138,42 +136,239 @@ describe('Pool unit test', () => { erc20Token = txReceipt.events.TokenCreated.returnValues.newTokenAddress poolAddress = txReceipt.events.NewPool.returnValues.poolAddress - - - erc20Contract = new web3.eth.Contract( - ERC20Template.abi as AbiItem[], - erc20Token - ) + 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 daiContract.methods.balanceOf(user2).call()).to.equal( + await pool.sharesBalance(user2, contracts.daiAddress) + ) + }) + + 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.daiAddress) - + const currentTokens = await pool.getCurrentTokens(poolAddress) + expect(currentTokens[0]).to.equal(erc20Token) + expect(currentTokens[1]).to.equal(contracts.daiAddress) + }) + + 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.daiAddress) + }) + + 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.daiAddress)).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.daiAddress)).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') + }) + + it('#getNormalizedWeight - should return the normalized weight', async () => { + expect(await pool.getNormalizedWeight(poolAddress, contracts.daiAddress)).to.equal( + '0.5' + ) + expect(await pool.getNormalizedWeight(poolAddress, erc20Token)).to.equal('0.5') + }) + + it('#getDenormalizedWeight - should return the swap fee', async () => { + expect(await pool.getDenormalizedWeight(poolAddress, contracts.daiAddress)).to.equal( + '5' + ) + expect(await pool.getDenormalizedWeight(poolAddress, erc20Token)).to.equal('5') }) it('#swapExactAmountIn - should swap', async () => { - await daiContract.methods.transfer(user2,web3.utils.toWei('100')).send({from:contracts.accounts[0]}) - expect(await daiContract.methods.balanceOf(user2).call()).to.equal(web3.utils.toWei('100')) + await daiContract.methods + .transfer(user2, web3.utils.toWei('1000')) + .send({ from: contracts.accounts[0] }) + expect(await daiContract.methods.balanceOf(user2).call()).to.equal( + web3.utils.toWei('1000') + ) expect(await erc20Contract.methods.balanceOf(user2).call()).to.equal('0') - await pool.approve(user2,contracts.daiAddress,poolAddress,web3.utils.toWei('100')) - const tx = await pool.swapExactAmountIn(user2,poolAddress,contracts.daiAddress,'10',erc20Token,'1') - expect(await erc20Contract.methods.balanceOf(user2).call()).to.equal(tx.events.LOG_SWAP.returnValues.tokenAmountOut) + await pool.approve(user2, contracts.daiAddress, poolAddress, web3.utils.toWei('100')) + const tx = await pool.swapExactAmountIn( + user2, + poolAddress, + contracts.daiAddress, + '10', + erc20Token, + '1' + ) + expect(await erc20Contract.methods.balanceOf(user2).call()).to.equal( + tx.events.LOG_SWAP.returnValues.tokenAmountOut + ) }) it('#swapExactAmountOut - should swap', async () => { - // await pool.approve(contracts.accounts[0],contracts.daiAddress,poolAddress,web3.utils.toWei('100')) - expect(await daiContract.methods.balanceOf(user2).call()).to.equal(web3.utils.toWei('90')) - await pool.swapExactAmountOut(user2,poolAddress,contracts.daiAddress,'10',erc20Token,'1') - -}) + // await pool.approve(contracts.accounts[0],contracts.daiAddress,poolAddress,web3.utils.toWei('100')) + expect(await daiContract.methods.balanceOf(user2).call()).to.equal( + web3.utils.toWei('990') + ) + const tx = await pool.swapExactAmountOut( + user2, + poolAddress, + contracts.daiAddress, + '100', + erc20Token, + '50' + ) + assert(tx != null) + }) + 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.daiAddress, 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 daiAmountIn = '100' + const minBPTOut = '0.1' + await pool.approve(user2, contracts.daiAddress, poolAddress, web3.utils.toWei('1000')) + + const tx = await pool.joinswapExternAmountIn( + user2, + poolAddress, + contracts.daiAddress, + daiAmountIn, + 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 maxDAIIn = '100' + + await pool.approve(user2, contracts.daiAddress, poolAddress, web3.utils.toWei('1000')) + + const tx = await pool.joinswapPoolAmountOut( + user2, + poolAddress, + contracts.daiAddress, + BPTAmountOut, + maxDAIIn + ) + + 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 OCEAN 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.daiAddress) + }) + + it('#exitswapPoolAmountIn- user2 exit the pool receiving only DAI', async () => { + const BPTAmountIn = '0.5' + const minDAIOut = '0.5' + + const tx = await pool.exitswapPoolAmountIn( + user2, + poolAddress, + contracts.daiAddress, + BPTAmountIn, + minDAIOut + ) + + assert(tx != null) + + expect(tx.events.LOG_EXIT[0].returnValues.tokenOut).to.equal(contracts.daiAddress) + + // 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 DAI', async () => { + const maxBTPIn = "0.5" + const exactDAIOut = "1" + + const tx = await pool.exitswapPoolAmountIn( + user2, + poolAddress, + contracts.daiAddress, + maxBTPIn, + exactDAIOut + ) + + assert(tx != null) + + expect(tx.events.LOG_EXIT[0].returnValues.tokenOut).to.equal(contracts.daiAddress) + + // DTs were also unstaked in the same transaction (went to the staking contract) + expect(tx.events.LOG_EXIT[1].returnValues.tokenOut).to.equal(erc20Token) + }) })