diff --git a/src/balancer/OceanPool.ts b/src/balancer/OceanPool.ts index 930d92e6..2c5c2196 100644 --- a/src/balancer/OceanPool.ts +++ b/src/balancer/OceanPool.ts @@ -3,6 +3,7 @@ import { AbiItem } from 'web3-utils/types' import { TransactionReceipt } from 'web3-core' import { Pool } from './Pool' import { EventData, Filter } from 'web3-eth-contract' +import { parse } from 'path' declare type PoolTransactionType = 'swap' | 'join' | 'exit' @@ -143,6 +144,189 @@ export class OceanPool extends Pool { return super.getReserve(poolAddress, dtAddress) } + /** + * Returns max amount that you can buy. + * @param poolAddress + * @param tokenAddress + */ + public async getMaxBuyQuantity( + poolAddress: string, + tokenAddress: string + ): Promise { + const balance = await super.getReserve(poolAddress, tokenAddress) + return String(parseFloat(balance) / 3) + } + + /** + * Returns tokenInAmount required to get tokenOutAmount + * @param poolAddress + * @param tokenInAddress + * @param tokenOutAddress + * @param tokenOutAmount + */ + public async calcInGivenOut( + poolAddress: string, + tokenInAddress: string, + tokenOutAddress: string, + tokenOutAmount: string + ): Promise { + const result = await super.calcInGivenOut( + poolAddress, + await super.getReserve(poolAddress, tokenInAddress), + await super.getDenormalizedWeight(poolAddress, tokenInAddress), + await super.getReserve(poolAddress, tokenOutAddress), + await super.getDenormalizedWeight(poolAddress, tokenOutAddress), + tokenOutAmount, + await this.getSwapFee(poolAddress) + ) + + return result + } + + /** + * Returns tokenOutAmount given tokenInAmount + * @param poolAddress + * @param tokenInAddress + * @param tokenOutAddress + * @param tokenInAmount + */ + public async calcOutGivenIn( + poolAddress: string, + tokenInAddress: string, + tokenOutAddress: string, + tokenInAmount: string + ): Promise { + const result = await super.calcOutGivenIn( + poolAddress, + await super.getReserve(poolAddress, tokenInAddress), + await super.getDenormalizedWeight(poolAddress, tokenInAddress), + await super.getReserve(poolAddress, tokenOutAddress), + await super.getDenormalizedWeight(poolAddress, tokenOutAddress), + tokenInAmount, + await super.getSwapFee(poolAddress) + ) + + return result + } + + /** + * Returns no of shares receved for adding a token to the pool + * @param poolAddress + * @param tokenInAddress + * @param tokenInAmount + */ + public async calcPoolOutGivenSingleIn( + poolAddress: string, + tokenInAddress: string, + tokenInAmount: string + ): Promise { + const result = super.calcPoolOutGivenSingleIn( + poolAddress, + await super.getReserve(poolAddress, tokenInAddress), + await super.getDenormalizedWeight(poolAddress, tokenInAddress), + await super.getPoolSharesTotalSupply(poolAddress), + await super.getTotalDenormalizedWeight(poolAddress), + tokenInAmount, + await super.getSwapFee(poolAddress) + ) + return result + } + + /** + * Returns no of tokens required to get a specific no of poolShares + * @param poolAddress + * @param tokenInAddress + * @param poolShares + */ + public async calcSingleInGivenPoolOut( + poolAddress: string, + tokenInAddress: string, + poolShares: string + ): Promise { + const result = super.calcSingleInGivenPoolOut( + poolAddress, + await super.getReserve(poolAddress, tokenInAddress), + await super.getDenormalizedWeight(poolAddress, tokenInAddress), + await super.getPoolSharesTotalSupply(poolAddress), + await super.getTotalDenormalizedWeight(poolAddress), + poolShares, + await super.getSwapFee(poolAddress) + ) + return result + } + + /** + * Returns no of tokens received for spending a specific no of poolShares + * @param poolAddress + * @param tokenOutAddress + * @param poolShares + */ + public async calcSingleOutGivenPoolIn( + poolAddress: string, + tokenOutAddress: string, + poolShares: string + ): Promise { + const result = super.calcSingleOutGivenPoolIn( + poolAddress, + await super.getReserve(poolAddress, tokenOutAddress), + await super.getDenormalizedWeight(poolAddress, tokenOutAddress), + await super.getPoolSharesTotalSupply(poolAddress), + await super.getTotalDenormalizedWeight(poolAddress), + poolShares, + await super.getSwapFee(poolAddress) + ) + return result + } + + /** + * Returns no of pool shares required to receive a specified amount of tokens + * @param poolAddress + * @param tokenOutAddress + * @param tokenOutAmount + */ + public async calcPoolInGivenSingleOut( + poolAddress: string, + tokenOutAddress: string, + tokenOutAmount: string + ): Promise { + const result = super.calcPoolInGivenSingleOut( + poolAddress, + await super.getReserve(poolAddress, tokenOutAddress), + await super.getDenormalizedWeight(poolAddress, tokenOutAddress), + await super.getPoolSharesTotalSupply(poolAddress), + await super.getTotalDenormalizedWeight(poolAddress), + tokenOutAmount, + await super.getSwapFee(poolAddress) + ) + return result + } + + /** + * Returns max amount of tokens that you can add to the pool + * @param poolAddress + * @param tokenAddress + */ + public async getMaxAddLiquidity( + poolAddress: string, + tokenAddress: string + ): Promise { + const balance = await super.getReserve(poolAddress, tokenAddress) + return String(parseFloat(balance) / 2) + } + + /** + * Returns max amount of tokens that you can withdraw from the pool + * @param poolAddress + * @param tokenAddress + */ + public async getMaxRemoveLiquidity( + poolAddress: string, + tokenAddress: string + ): Promise { + const balance = await super.getReserve(poolAddress, tokenAddress) + return String(parseFloat(balance) / 3) + } + /** * Buy Data Token from a pool * @param {String} account @@ -155,31 +339,51 @@ export class OceanPool extends Pool { public async buyDT( account: string, poolAddress: string, - amount: string, - oceanAmount: string, - maxPrice: string + dtAmountWanted: string, + maxOceanAmount: string, + maxPrice?: string ): Promise { if (this.oceanAddress == null) { console.error('oceanAddress is not defined') return null } + if (!maxPrice) { + maxPrice = String(2 ** 256 - 1) + } const dtAddress = await this.getDTAddress(poolAddress) + if ( + parseFloat(dtAmountWanted) > + parseFloat(await this.getMaxBuyQuantity(poolAddress, dtAddress)) + ) { + console.error('Buy quantity exceeds quantity allowed') + return null + } + const calcInGivenOut = await this.calcInGivenOut( + poolAddress, + this.oceanAddress, + dtAddress, + dtAmountWanted + ) + if (parseFloat(calcInGivenOut) > parseFloat(maxOceanAmount)) { + console.error('Not enough Ocean Tokens') + return null + } // TODO - check balances first await super.approve( account, this.oceanAddress, poolAddress, - this.web3.utils.toWei(oceanAmount) + this.web3.utils.toWei(maxOceanAmount) ) return this.swapExactAmountOut( account, poolAddress, this.oceanAddress, - oceanAmount, + maxOceanAmount, dtAddress, - amount, + dtAmountWanted, maxPrice ) } @@ -188,7 +392,7 @@ export class OceanPool extends Pool { * Sell Data Token * @param {String} account * @param {String} poolAddress - * @param {String} amount Data Token amount + * @param {String} amount Data Token amount to be sold * @param {String} oceanAmount Ocean Token amount expected * @param {String} maxPrice Minimum price to sell * @return {TransactionReceipt} @@ -196,23 +400,43 @@ export class OceanPool extends Pool { public async sellDT( account: string, poolAddress: string, - amount: string, - oceanAmount: string, - minPrice: string + dtAmount: string, + oceanAmountWanted: string, + maxPrice?: string ): Promise { + if (!maxPrice) { + maxPrice = String(2 ** 256 - 1) + } if (this.oceanAddress == null) { console.error('oceanAddress is not defined') return null } const dtAddress = await this.getDTAddress(poolAddress) - return this.swapExactAmountOut( + if ( + parseFloat(oceanAmountWanted) > + parseFloat(await this.getMaxBuyQuantity(poolAddress, this.oceanAddress)) + ) { + console.error('Buy quantity exceeds quantity allowed') + return null + } + const calcOutGivenIn = await this.calcOutGivenIn( + poolAddress, + dtAddress, + this.oceanAddress, + dtAmount + ) + if (parseFloat(calcOutGivenIn) < parseFloat(oceanAmountWanted)) { + console.error('Not enough Data Tokens') + return null + } + return this.swapExactAmountIn( account, poolAddress, dtAddress, - amount, + dtAmount, this.oceanAddress, - oceanAmount, - minPrice + oceanAmountWanted, + maxPrice ) } @@ -229,6 +453,11 @@ export class OceanPool extends Pool { amount: string ): Promise { const dtAddress = await this.getDTAddress(poolAddress) + const maxAmount = await this.getMaxAddLiquidity(poolAddress, dtAddress) + if (parseFloat(amount) > parseFloat(maxAmount)) { + console.error('Too much reserve to add') + return null + } await super.approve(account, dtAddress, poolAddress, this.web3.utils.toWei(amount)) const result = await super.joinswapExternAmountIn( account, @@ -254,7 +483,23 @@ export class OceanPool extends Pool { maximumPoolShares: string ): Promise { const dtAddress = await this.getDTAddress(poolAddress) - // TODO Check balance of PoolShares before doing exit + const maxAmount = await this.getMaxRemoveLiquidity(poolAddress, dtAddress) + if (parseFloat(amount) > parseFloat(maxAmount)) { + console.error('Too much reserve to remove') + return null + } + const usershares = await this.sharesBalance(account, poolAddress) + if (parseFloat(usershares) < parseFloat(maximumPoolShares)) { + console.error('Not enough poolShares') + return null + } + if ( + parseFloat(maximumPoolShares) < + parseFloat(await this.calcPoolInGivenSingleOut(poolAddress, dtAddress, amount)) + ) { + console.error('Not enough poolShares') + return null + } return this.exitswapExternAmountOut( account, poolAddress, @@ -280,6 +525,11 @@ export class OceanPool extends Pool { console.error('oceanAddress is not defined') return null } + const maxAmount = await this.getMaxAddLiquidity(poolAddress, this.oceanAddress) + if (parseFloat(amount) > parseFloat(maxAmount)) { + console.error('Too much reserve to add') + return null + } await super.approve( account, this.oceanAddress, @@ -303,7 +553,7 @@ export class OceanPool extends Pool { * @param {String} amount Ocean Token amount in OCEAN * @return {TransactionReceipt} */ - public removeOceanLiquidity( + public async removeOceanLiquidity( account: string, poolAddress: string, amount: string, @@ -313,7 +563,25 @@ export class OceanPool extends Pool { console.error('oceanAddress is not defined') return null } - // TODO Check balance of PoolShares before doing exit + const maxAmount = await this.getMaxRemoveLiquidity(poolAddress, this.oceanAddress) + if (parseFloat(amount) > parseFloat(maxAmount)) { + console.error('Too much reserve to remove') + return null + } + const usershares = await this.sharesBalance(account, poolAddress) + if (parseFloat(usershares) < parseFloat(maximumPoolShares)) { + console.error('Not enough poolShares') + return null + } + if ( + parseFloat(maximumPoolShares) < + parseFloat( + await this.calcPoolInGivenSingleOut(poolAddress, this.oceanAddress, amount) + ) + ) { + console.error('Not enough poolShares') + return null + } return super.exitswapExternAmountOut( account, poolAddress, @@ -323,6 +591,27 @@ export class OceanPool extends Pool { ) } + /** + * Remove pool liquidity + * @param {String} account + * @param {String} poolAddress + * @param {String} poolShares + * @return {TransactionReceipt} + */ + public async removePoolLiquidity( + account: string, + poolAddress: string, + poolShares: string + ): Promise { + const usershares = await this.sharesBalance(account, poolAddress) + if (parseFloat(usershares) < parseFloat(poolShares)) { + console.error('Not enough poolShares') + return null + } + + return this.exitPool(account, poolAddress, poolShares, ['0', '0']) + } + /** * Get Data Token price from pool * @param {String} poolAddress @@ -358,19 +647,7 @@ export class OceanPool extends Pool { public async getOceanNeeded(poolAddress: string, dtRequired: string): Promise { const dtAddress = await this.getDTAddress(poolAddress) - const tokenBalanceIn = await this.getReserve(poolAddress, this.oceanAddress) - const tokenWeightIn = await this.getDenormalizedWeight(poolAddress, this.oceanAddress) - const tokenBalanceOut = await this.getReserve(poolAddress, dtAddress) - const tokenWeightOut = await this.getDenormalizedWeight(poolAddress, dtAddress) - const swapFee = await this.getSwapFee(poolAddress) - return super.calcInGivenOut( - tokenBalanceIn, - tokenWeightIn, - tokenBalanceOut, - tokenWeightOut, - dtRequired, - swapFee - ) + return this.calcInGivenOut(poolAddress, this.oceanAddress, dtAddress, dtRequired) } /** diff --git a/src/balancer/Pool.ts b/src/balancer/Pool.ts index c5b2799e..c5345a02 100644 --- a/src/balancer/Pool.ts +++ b/src/balancer/Pool.ts @@ -270,6 +270,23 @@ export class Pool extends PoolFactory { return result } + /** + * Get total supply of pool shares + * @param {String} poolAddress + * @return {String} + */ + async getPoolSharesTotalSupply(poolAddress: string): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let amount = null + try { + const result = await pool.methods.totalSupply().call() + amount = this.web3.utils.fromWei(result) + } catch (e) { + console.error(e) + } + return amount + } + /** * Get tokens composing this pool * @param {String} poolAddress @@ -594,7 +611,7 @@ export class Pool extends PoolFactory { account: string, poolAddress: string, poolAmountIn: string, - minAmountsOut: string + minAmountsOut: string[] ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress, { from: account @@ -799,6 +816,7 @@ export class Pool extends PoolFactory { } public async calcInGivenOut( + poolAddress: string, tokenBalanceIn: string, tokenWeightIn: string, tokenBalanceOut: string, @@ -806,14 +824,168 @@ export class Pool extends PoolFactory { tokenAmountOut: string, swapFee: string ): Promise { - const weightRatio = new Decimal(tokenWeightOut).div(new Decimal(tokenWeightIn)) - const diff = new Decimal(tokenBalanceOut).minus(tokenAmountOut) - const y = new Decimal(tokenBalanceOut).div(diff) - const foo = y.pow(weightRatio).minus(new Decimal(1)) - const tokenAmountIn = new Decimal(tokenBalanceIn) - .times(foo) - .div(new Decimal(1).minus(new Decimal(swapFee))) + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let amount = null + try { + const result = await pool.methods + .calcInGivenOut( + this.web3.utils.toWei(tokenBalanceIn), + this.web3.utils.toWei(tokenWeightIn), + this.web3.utils.toWei(tokenBalanceOut), + this.web3.utils.toWei(tokenWeightOut), + this.web3.utils.toWei(tokenAmountOut), + this.web3.utils.toWei(swapFee) + ) + .call() + amount = this.web3.utils.fromWei(result) + } catch (e) { + console.error(e) + } + return amount + } - return tokenAmountIn.toString() + public async calcOutGivenIn( + poolAddress: string, + tokenBalanceIn: string, + tokenWeightIn: string, + tokenBalanceOut: string, + tokenWeightOut: string, + tokenAmountIn: string, + swapFee: string + ): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let amount = null + try { + const result = await pool.methods + .calcOutGivenIn( + this.web3.utils.toWei(tokenBalanceIn), + this.web3.utils.toWei(tokenWeightIn), + this.web3.utils.toWei(tokenBalanceOut), + this.web3.utils.toWei(tokenWeightOut), + this.web3.utils.toWei(tokenAmountIn), + this.web3.utils.toWei(swapFee) + ) + .call() + amount = this.web3.utils.fromWei(result) + } catch (e) { + console.error(e) + } + return amount + } + + public async calcPoolOutGivenSingleIn( + poolAddress: string, + tokenBalanceIn: string, + tokenWeightIn: string, + poolSupply: string, + totalWeight: string, + tokenAmountIn: string, + swapFee: string + ): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let amount = null + try { + const result = await pool.methods + .calcPoolOutGivenSingleIn( + this.web3.utils.toWei(tokenBalanceIn), + this.web3.utils.toWei(tokenWeightIn), + this.web3.utils.toWei(poolSupply), + this.web3.utils.toWei(totalWeight), + this.web3.utils.toWei(tokenAmountIn), + this.web3.utils.toWei(swapFee) + ) + .call() + amount = this.web3.utils.fromWei(result) + } catch (e) { + console.error(e) + } + return amount + } + + public async calcSingleInGivenPoolOut( + poolAddress: string, + tokenBalanceIn: string, + tokenWeightIn: string, + poolSupply: string, + totalWeight: string, + poolAmountOut: string, + swapFee: string + ): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let amount = null + try { + const result = await pool.methods + .calcSingleInGivenPoolOut( + this.web3.utils.toWei(tokenBalanceIn), + this.web3.utils.toWei(tokenWeightIn), + this.web3.utils.toWei(poolSupply), + this.web3.utils.toWei(totalWeight), + this.web3.utils.toWei(poolAmountOut), + this.web3.utils.toWei(swapFee) + ) + .call() + amount = this.web3.utils.fromWei(result) + } catch (e) { + console.error(e) + } + return amount + } + + public async calcSingleOutGivenPoolIn( + poolAddress: string, + tokenBalanceOut: string, + tokenWeightOut: string, + poolSupply: string, + totalWeight: string, + poolAmountIn: string, + swapFee: string + ): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let amount = null + try { + const result = await pool.methods + .calcSingleOutGivenPoolIn( + this.web3.utils.toWei(tokenBalanceOut), + this.web3.utils.toWei(tokenWeightOut), + this.web3.utils.toWei(poolSupply), + this.web3.utils.toWei(totalWeight), + this.web3.utils.toWei(poolAmountIn), + this.web3.utils.toWei(swapFee) + ) + .call() + amount = this.web3.utils.fromWei(result) + } catch (e) { + console.error(e) + } + return amount + } + + public async calcPoolInGivenSingleOut( + poolAddress: string, + tokenBalanceOut: string, + tokenWeightOut: string, + poolSupply: string, + totalWeight: string, + tokenAmountOut: string, + swapFee: string + ): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let amount = null + try { + const result = await pool.methods + .calcPoolInGivenSingleOut( + this.web3.utils.toWei(tokenBalanceOut), + this.web3.utils.toWei(tokenWeightOut), + this.web3.utils.toWei(poolSupply), + this.web3.utils.toWei(totalWeight), + this.web3.utils.toWei(tokenAmountOut), + this.web3.utils.toWei(swapFee) + ) + .call() + amount = this.web3.utils.fromWei(result) + } catch (e) { + console.error(e) + } + return amount } } diff --git a/test/unit/balancer/Balancer.test.ts b/test/unit/balancer/Balancer.test.ts index e166088b..04a8e650 100644 --- a/test/unit/balancer/Balancer.test.ts +++ b/test/unit/balancer/Balancer.test.ts @@ -27,7 +27,7 @@ describe('Balancer flow', () => { let contracts: TestContractHandler let datatoken: DataTokens let tokenAddress: string - let consoleDebug: false + let consoleDebug: true let greatPool: string const tokenAmount = '1000' const transferAmount = '200' @@ -185,12 +185,29 @@ describe('Balancer flow', () => { assert(Number(bobDtBalance) > 0) assert(Number(bobOceanBalance) > 0) }) + it('Bob should get maximum DT liquidity that he can add to pool ', async () => { + const maxDT = await Pool.getMaxAddLiquidity(greatPool, tokenAddress) + if (consoleDebug) console.error('maxDT:' + maxDT) + assert(parseFloat(maxDT) > 0) + }) + + it('Bob should fail to add more than maximum DT liquidity that he can add to pool ', async () => { + const maxDT = await Pool.getMaxAddLiquidity(greatPool, tokenAddress) + const tx = await Pool.addDTLiquidity(bob, greatPool, String(parseFloat(maxDT) * 2)) + assert(tx === null) + }) it('Bob should add DT liquidity to pool ', async () => { + const maxDT = await Pool.getMaxAddLiquidity(greatPool, tokenAddress) + if (consoleDebug) console.error('maxDT:' + maxDT) const currentDtReserve = await Pool.getDTReserve(greatPool) if (consoleDebug) console.log('currentDtReserve:' + currentDtReserve) const bobDtBalance = await datatoken.balance(tokenAddress, bob) if (consoleDebug) console.log('BOB DT Balance:' + bobDtBalance) - await Pool.addDTLiquidity(bob, greatPool, bobDtBalance) + await Pool.addDTLiquidity( + bob, + greatPool, + String(Math.min(parseFloat(maxDT), parseFloat(bobDtBalance))) + ) const newbobDtBalance = await datatoken.balance(tokenAddress, bob) @@ -204,7 +221,23 @@ describe('Balancer flow', () => { assert(parseFloat(newDtReserve) > parseFloat(currentDtReserve)) assert(parseFloat(sharesBalance) > 0) }) - + it('Bob should get maximum DT liquidity that he can remove from pool ', async () => { + const maxDT = await Pool.getMaxRemoveLiquidity(greatPool, tokenAddress) + if (consoleDebug) console.log('maxDT:' + maxDT) + assert(parseFloat(maxDT) > 0) + }) + it('Bob should fail to remove more than maximum DT liquidity that he can remove from the pool ', async () => { + const maxDT = await Pool.getMaxRemoveLiquidity(greatPool, tokenAddress) + if (consoleDebug) console.log('maxDT:' + maxDT) + const poolShares = await Pool.sharesBalance(bob, greatPool) + const tx = await Pool.removeDTLiquidity( + bob, + greatPool, + String(parseFloat(maxDT) * 2), + poolShares + ) + assert(tx === null) + }) it('Bob should remove DT liquidity from pool ', async () => { const currentDtReserve = await Pool.getDTReserve(greatPool) if (consoleDebug) console.log('currentDtReserve:' + currentDtReserve) @@ -212,7 +245,14 @@ describe('Balancer flow', () => { if (consoleDebug) console.log('bobDtBalance:' + bobDtBalance) const poolShares = await Pool.sharesBalance(bob, greatPool) if (consoleDebug) console.log('poolShares:' + poolShares) - await Pool.removeDTLiquidity(bob, greatPool, '0.75', poolShares) + const maxDT = await Pool.getMaxRemoveLiquidity(greatPool, tokenAddress) + if (consoleDebug) console.log('maxDT:' + maxDT) + await Pool.removeDTLiquidity( + bob, + greatPool, + String(Math.min(parseFloat(maxDT), parseFloat('0.75'))), + poolShares + ) const newDtReserve = await Pool.getDTReserve(greatPool) if (consoleDebug) console.log('newDtReserve:' + newDtReserve) @@ -225,13 +265,28 @@ describe('Balancer flow', () => { assert(parseFloat(poolShares) > parseFloat(newpoolShares)) }) + it('Bob should get maximum Ocean liquidity that he can add to pool ', async () => { + const maxOcean = await Pool.getMaxAddLiquidity(greatPool, oceanTokenAddress) + assert(parseFloat(maxOcean) > 0) + }) + + it('Bob should fail to add more than maximum Ocean liquidity that he can add to pool ', async () => { + const maxOcean = await Pool.getMaxAddLiquidity(greatPool, oceanTokenAddress) + const tx = await Pool.addOceanLiquidity( + bob, + greatPool, + String(parseFloat(maxOcean) * 2) + ) + assert(tx === null) + }) + it('Bob should add Ocean liquidity to pool ', async () => { const currentDtReserve = await Pool.getOceanReserve(greatPool) const bobDtBalance = await datatoken.balance(oceanTokenAddress, bob) if (consoleDebug) console.log('currentDtReserve:' + currentDtReserve) if (consoleDebug) console.log('bobDtBalance:' + bobDtBalance) - - await Pool.addOceanLiquidity(bob, greatPool, '1') + const maxOcean = await Pool.getMaxAddLiquidity(greatPool, oceanTokenAddress) + await Pool.addOceanLiquidity(bob, greatPool, maxOcean) const newbobDtBalance = await datatoken.balance(oceanTokenAddress, bob) @@ -245,7 +300,21 @@ describe('Balancer flow', () => { assert(parseFloat(newDtReserve) > parseFloat(currentDtReserve)) assert(parseFloat(sharesBalance) > 0) }) - + it('Bob should get maximum Ocean liquidity that he can remove from pool ', async () => { + const maxOcean = await Pool.getMaxRemoveLiquidity(greatPool, oceanTokenAddress) + assert(parseFloat(maxOcean) > 0) + }) + it('Bob should fail to remove more than maximum Ocean liquidity that he can remove from the pool ', async () => { + const maxOcean = await Pool.getMaxRemoveLiquidity(greatPool, oceanTokenAddress) + const poolShares = await Pool.sharesBalance(bob, greatPool) + const tx = await Pool.removeOceanLiquidity( + bob, + greatPool, + String(parseFloat(maxOcean) * 2), + poolShares + ) + assert(tx === null) + }) it('Bob should remove Ocean liquidity from pool ', async () => { const currentDtReserve = await Pool.getOceanReserve(greatPool) const bobDtBalance = await datatoken.balance(oceanTokenAddress, bob)