diff --git a/src/factories/NFTFactory.ts b/src/factories/NFTFactory.ts index 469fde0d..5c39e611 100644 --- a/src/factories/NFTFactory.ts +++ b/src/factories/NFTFactory.ts @@ -1,5 +1,6 @@ import { Contract } from 'web3-eth-contract' import Web3 from 'web3' +import BigNumber from 'bignumber.js' import { TransactionReceipt } from 'web3-core' import { AbiItem } from 'web3-utils' import defaultFactory721ABI from '@oceanprotocol/contracts/artifacts/contracts/ERC721Factory.sol/ERC721Factory.json' @@ -37,7 +38,7 @@ interface ErcCreateData { interface PoolData { addresses: string[] - ssParams: (string | number)[] + ssParams: (string | number | BigNumber)[] swapFees: number[] } diff --git a/src/pools/Router.ts b/src/pools/Router.ts index 4f00d4cb..9f363187 100644 --- a/src/pools/Router.ts +++ b/src/pools/Router.ts @@ -124,18 +124,17 @@ export class Router { /** * Estimate gas cost for addOceanToken - * @param {String} address + * @param {String} address * @param {String} tokenAddress token address we want to add * @param {Contract} routerContract optional contract instance * @return {Promise} */ - public async estGasAddOceanToken( + public async estGasAddOceanToken( address: string, tokenAddress: string, contractInstance?: Contract ) { - const routerContract = - contractInstance || this.router + const routerContract = contractInstance || this.router const gasLimitDefault = this.GASLIMIT_DEFAULT let estGas @@ -148,6 +147,7 @@ export class Router { } return estGas } + /** * Add a new token to oceanTokens list, pools with basetoken in this list have NO opf Fee * @param {String} address caller address @@ -162,9 +162,7 @@ export class Router { throw new Error(`Caller is not Router Owner`) } - - const estGas = await this.estGasAddOceanToken(address,tokenAddress) - + const estGas = await this.estGasAddOceanToken(address, tokenAddress) // Invoke createToken function of the contract const trxReceipt = await this.router.methods.addOceanToken(tokenAddress).send({ @@ -176,32 +174,31 @@ export class Router { return trxReceipt } - /** + /** * Estimate gas cost for removeOceanToken * @param {String} address caller address * @param {String} tokenAddress token address we want to add * @param {Contract} routerContract optional contract instance * @return {Promise} */ - public async estGasRemoveOceanToken( - address: string, - tokenAddress: string, - contractInstance?: Contract - ) { - const routerContract = - contractInstance || this.router - - const gasLimitDefault = this.GASLIMIT_DEFAULT - let estGas - try { - estGas = await routerContract.methods - .removeOceanToken(tokenAddress) - .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) - } catch (e) { - estGas = gasLimitDefault - } - return estGas + public async estGasRemoveOceanToken( + address: string, + tokenAddress: string, + contractInstance?: Contract + ) { + const routerContract = contractInstance || this.router + + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await routerContract.methods + .removeOceanToken(tokenAddress) + .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault } + return estGas + } /** * Remove a token from oceanTokens list, pools without basetoken in this list have a opf Fee @@ -217,8 +214,7 @@ export class Router { throw new Error(`Caller is not Router Owner`) } - const estGas = await this.estGasRemoveOceanToken(address,tokenAddress) - + const estGas = await this.estGasRemoveOceanToken(address, tokenAddress) // Invoke createToken function of the contract const trxReceipt = await this.router.methods.removeOceanToken(tokenAddress).send({ diff --git a/src/pools/balancer/Pool.ts b/src/pools/balancer/Pool.ts index 6042e20b..55d6df50 100644 --- a/src/pools/balancer/Pool.ts +++ b/src/pools/balancer/Pool.ts @@ -40,11 +40,9 @@ export class Pool { spender: string ): Promise { const tokenAbi = defaultERC20ABI.abi as AbiItem[] - const datatoken = new this.web3.eth.Contract(tokenAbi, tokenAddress, { - from: spender - }) + const datatoken = new this.web3.eth.Contract(tokenAbi, tokenAddress) const trxReceipt = await datatoken.methods.allowance(owner, spender).call() - return this.web3.utils.fromWei(trxReceipt) + return (await this.unitsToAmount(tokenAddress, trxReceipt)).toString() } /** @@ -103,18 +101,20 @@ export class Pool { let estGas try { estGas = await token.methods - .approve(spender, amount) + .approve(spender, await this.amountToUnits(tokenAddress, amount)) .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) } catch (e) { estGas = gasLimitDefault } try { - result = await token.methods.approve(spender, amount).send({ - from: account, - gas: estGas + 1, - gasPrice: await getFairGasPrice(this.web3) - }) + result = await token.methods + .approve(spender, await this.amountToUnits(tokenAddress, amount)) + .send({ + from: account, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) } catch (e) { this.logger.error(`ERRPR: Failed to approve spender to spend tokens : ${e.message}`) } @@ -247,62 +247,60 @@ export class Pool { return result } - /** + /** * Get basetoken address of this pool * @param {String} poolAddress * @return {String} */ - async getBasetoken(poolAddress: string): Promise { - const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) - let result = null - try { - result = await pool.methods.getBaseTokenAddress().call() - } catch (e) { - this.logger.error(`ERROR: Failed to get basetoken address: ${e.message}`) - } - return result + async getBasetoken(poolAddress: string): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let result = null + try { + result = await pool.methods.getBaseTokenAddress().call() + } catch (e) { + this.logger.error(`ERROR: Failed to get basetoken address: ${e.message}`) } + return result + } - /** + /** * Get datatoken address of this pool * @param {String} poolAddress * @return {String} */ - async getDatatoken(poolAddress: string): Promise { - const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) - let result = null - try { - result = await pool.methods.getDataTokenAddress().call() - } catch (e) { - this.logger.error(`ERROR: Failed to get datatoken address: ${e.message}`) - } - return result - } - + async getDatatoken(poolAddress: string): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let result = null + try { + result = await pool.methods.getDataTokenAddress().call() + } catch (e) { + this.logger.error(`ERROR: Failed to get datatoken address: ${e.message}`) + } + return result + } - /** + /** * Get marketFeeCollector of this pool * @param {String} poolAddress * @return {String} */ - async getMarketFeeCollector(poolAddress: string): Promise { - const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) - let result = null - try { - result = await pool.methods._marketCollector().call() - } catch (e) { - this.logger.error(`ERROR: Failed to get marketFeeCollector address: ${e.message}`) - } - return result + async getMarketFeeCollector(poolAddress: string): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let result = null + try { + result = await pool.methods._marketCollector().call() + } catch (e) { + this.logger.error(`ERROR: Failed to get marketFeeCollector address: ${e.message}`) } + return result + } - /** * Get OPF Collector of this pool * @param {String} poolAddress * @return {String} */ - async getOPFCollector(poolAddress: string): Promise { + async getOPFCollector(poolAddress: string): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let result = null try { @@ -312,6 +310,7 @@ export class Pool { } return result } + /** * Get if a token is bounded to a pool * @param {String} poolAddress @@ -341,7 +340,7 @@ export class Pool { try { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) const result = await pool.methods.getBalance(token).call() - amount = await this.unitsToAmount(token,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}`) @@ -435,103 +434,94 @@ export class Pool { return weight } - /** + /** * Get Market Fees available to be collected for a specific token * @param {String} poolAddress * @param {String} token token we want to check fees * @return {String} */ - async getMarketFees(poolAddress: string, token: string): Promise { - const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) - let weight = null - try { - const result = await pool.methods.marketFees(token).call() - weight = this.web3.utils.fromWei(result) - } catch (e) { - this.logger.error(`ERROR: Failed to get market fees for a token: ${e.message}`) - } - return weight + async getMarketFees(poolAddress: string, token: string): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let weight = null + try { + const result = await pool.methods.marketFees(token).call() + weight = this.web3.utils.fromWei(result) + } catch (e) { + this.logger.error(`ERROR: Failed to get market fees for a token: ${e.message}`) } + return weight + } - /** + /** * Get Community Fees available to be collected for a specific token * @param {String} poolAddress * @param {String} token token we want to check fees * @return {String} */ - async getCommunityFees(poolAddress: string, token: string): Promise { - const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) - let weight = null - try { - const result = await pool.methods.communityFees(token).call() - weight = this.web3.utils.fromWei(result) - } catch (e) { - this.logger.error(`ERROR: Failed to get community fees for a token: ${e.message}`) - } - return weight - } + async getCommunityFees(poolAddress: string, token: string): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let weight = null + try { + const result = await pool.methods.communityFees(token).call() + weight = this.web3.utils.fromWei(result) + } catch (e) { + this.logger.error(`ERROR: Failed to get community fees for a token: ${e.message}`) + } + return weight + } - /** + /** * Estimate gas cost for collectOPF * @param {String} address * @param {String} poolAddress * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estCollectOPF( - address: string, - poolAddress: string, - contractInstance?: Contract - ) :Promise{ - const poolContract = - contractInstance || - new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) - - const gasLimitDefault = this.GASLIMIT_DEFAULT - let estGas - try { - estGas = await poolContract.methods - .collectOPF() - .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) - } catch (e) { - estGas = gasLimitDefault - } - return estGas + public async estCollectOPF( + address: string, + poolAddress: string, + contractInstance?: Contract + ): Promise { + const poolContract = + contractInstance || + new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) + + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await poolContract.methods + .collectOPF() + .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault } + return estGas + } - - /** + /** * collectOPF - collect opf fee - can be called by anyone * @param {String} address * @param {String} poolAddress * @return {TransactionReceipt} */ - async collectOPF( - address: string, - poolAddress: string - ): Promise { - const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) - let result = null - const estGas = await this.estCollectOPF( - address, - poolAddress - ) - - try { - result = await pool.methods - .collectOPF() - .send({ - from: address, - gas: estGas + 1, - gasPrice: await getFairGasPrice(this.web3) - }) - } catch (e) { - this.logger.error(`ERROR: Failed to swap exact amount in : ${e.message}`) - } - return result - } + async collectOPF(address: string, poolAddress: string): Promise { + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let result = null + const estGas = await this.estCollectOPF(address, poolAddress) - /** + try { + result = await pool.methods.collectOPF().send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + } catch (e) { + this.logger.error(`ERROR: Failed to swap exact amount in : ${e.message}`) + } + return result + } + + /** * Estimate gas cost for collectMarketFee * @param {String} address * @param {String} poolAddress @@ -539,67 +529,60 @@ export class Pool { * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estCollectMarketFee( - address: string, - poolAddress: string, - to:string, - contractInstance?: Contract - ) :Promise{ - const poolContract = - contractInstance || - new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) - - const gasLimitDefault = this.GASLIMIT_DEFAULT - let estGas - try { - estGas = await poolContract.methods - .collectMarketFee(to) - .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) - } catch (e) { - estGas = gasLimitDefault - } - return estGas + public async estCollectMarketFee( + address: string, + poolAddress: string, + to: string, + contractInstance?: Contract + ): Promise { + const poolContract = + contractInstance || + new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) + + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await poolContract.methods + .collectMarketFee(to) + .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault } + return estGas + } - - /** + /** * collectOPF - collect market fees - can be called by the marketFeeCollector * @param {String} address * @param {String} poolAddress * @param {String} to address that will receive fees * @return {TransactionReceipt} */ - async collectMarketFee( - address: string, - poolAddress: string, - to: string - ): Promise { - if ((await this.getMarketFeeCollector(poolAddress)) !== address) { - throw new Error(`Caller is not MarketFeeCollector`) - } - const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) - let result = null - const estGas = await this.estCollectMarketFee( - address, - poolAddress, - to - ) - - try { - result = await pool.methods - .collectMarketFee(to) - .send({ - from: address, - gas: estGas + 1, - gasPrice: await getFairGasPrice(this.web3) - }) - } catch (e) { - this.logger.error(`ERROR: Failed to swap exact amount in : ${e.message}`) - } - return result + async collectMarketFee( + address: string, + poolAddress: string, + to: string + ): Promise { + if ((await this.getMarketFeeCollector(poolAddress)) !== address) { + throw new Error(`Caller is not MarketFeeCollector`) } + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let result = null + const estGas = await this.estCollectMarketFee(address, poolAddress, to) - /** + try { + result = await pool.methods.collectMarketFee(to).send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + } catch (e) { + this.logger.error(`ERROR: Failed to swap exact amount in : ${e.message}`) + } + return result + } + + /** * Estimate gas cost for collectMarketFee * @param {String} address * @param {String} poolAddress @@ -607,67 +590,63 @@ export class Pool { * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estUpdateMarketFeeCollector( - address: string, - poolAddress: string, - newCollector:string, - contractInstance?: Contract - ) :Promise{ - const poolContract = - contractInstance || - new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) - - const gasLimitDefault = this.GASLIMIT_DEFAULT - let estGas - try { - estGas = await poolContract.methods - .updateMarketFeeCollector(newCollector) - .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) - } catch (e) { - estGas = gasLimitDefault - } - return estGas - } - - - /** - * updateMarketFeeCollector - updates marketFeeCollector - can be called only by the marketFeeCollector - * @param {String} address - * @param {String} poolAddress - * @param {String} newCollector new market fee collector address - * @return {TransactionReceipt} - */ - async updateMarketFeeCollector( - address: string, - poolAddress: string, - newCollector: string - ): Promise { - if ((await this.getMarketFeeCollector(poolAddress)) !== address) { - throw new Error(`Caller is not MarketFeeCollector`) - } - const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) - let result = null - const estGas = await this.estUpdateMarketFeeCollector( - address, - poolAddress, - newCollector - ) - - try { - result = await pool.methods - .updateMarketFeeCollector(newCollector) - .send({ - from: address, - gas: estGas + 1, - gasPrice: await getFairGasPrice(this.web3) - }) - } catch (e) { - this.logger.error(`ERROR: Failed to swap exact amount in : ${e.message}`) - } - return result - } + public async estUpdateMarketFeeCollector( + address: string, + poolAddress: string, + newCollector: string, + contractInstance?: Contract + ): Promise { + const poolContract = + contractInstance || + new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) + + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await poolContract.methods + .updateMarketFeeCollector(newCollector) + .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + } + return estGas + } + + /** + * updateMarketFeeCollector - updates marketFeeCollector - can be called only by the marketFeeCollector + * @param {String} address + * @param {String} poolAddress + * @param {String} newCollector new market fee collector address + * @return {TransactionReceipt} + */ + async updateMarketFeeCollector( + address: string, + poolAddress: string, + newCollector: string + ): Promise { + if ((await this.getMarketFeeCollector(poolAddress)) !== address) { + throw new Error(`Caller is not MarketFeeCollector`) + } + const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let result = null + const estGas = await this.estUpdateMarketFeeCollector( + address, + poolAddress, + newCollector + ) + + try { + result = await pool.methods.updateMarketFeeCollector(newCollector).send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + } catch (e) { + this.logger.error(`ERROR: Failed to swap exact amount in : ${e.message}`) + } + return result + } - /** * Estimate gas cost for swapExactAmountIn * @param {String} address @@ -689,7 +668,7 @@ export class Pool { minAmountOut: string, maxPrice: string, contractInstance?: Contract - ) :Promise{ + ): Promise { const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -712,7 +691,7 @@ export class Pool { return estGas } - async amountToUnits(token:string,amount:string):Promise{ + async amountToUnits(token: string, amount: string): Promise { let decimals = 18 let amountFormatted const tokenContract = new this.web3.eth.Contract( @@ -724,15 +703,13 @@ export class Pool { } 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{ + async unitsToAmount(token: string, amount: string): Promise { let decimals = 18 let amountFormatted const tokenContract = new this.web3.eth.Contract( @@ -744,12 +721,10 @@ export class Pool { } catch (e) { this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') } - console.log(decimals,'decimals') - - amountFormatted = new BigNumber(parseInt(amount) /(10 ** decimals) ) - + + amountFormatted = new BigNumber(parseInt(amount) / 10 ** decimals) + return amountFormatted - } /** @@ -777,43 +752,16 @@ export class Pool { 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') + + amountInFormatted = await this.amountToUnits(tokenIn, tokenAmountIn) + + minAmountOutFormatted = await this.amountToUnits(tokenOut, minAmountOut) let result = null - // TODO: add multiple decimals support + const estGas = await this.estSwapExactAmountIn( address, poolAddress, @@ -823,7 +771,7 @@ export class Pool { minAmountOutFormatted, maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256 ) - console.log(minAmountOutFormatted,'minamoutnoutformatted') + console.log(minAmountOutFormatted, 'minamoutnoutformatted') try { result = await pool.methods .swapExactAmountIn( @@ -838,11 +786,10 @@ 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 } @@ -867,7 +814,7 @@ export class Pool { amountOut: string, maxPrice?: string, contractInstance?: Contract - ) :Promise{ + ): Promise { const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -912,10 +859,10 @@ export class Pool { ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let result = null - let maxAmountInFormatted + let maxAmountInFormatted let amountOutFormatted - maxAmountInFormatted = await this.amountToUnits(tokenIn,maxAmountIn) - amountOutFormatted = await this.amountToUnits(tokenOut,amountOut) + maxAmountInFormatted = await this.amountToUnits(tokenIn, maxAmountIn) + amountOutFormatted = await this.amountToUnits(tokenOut, amountOut) const estGas = await this.estSwapExactAmountOut( account, poolAddress, @@ -961,7 +908,7 @@ export class Pool { poolAmountOut: string, maxAmountsIn: string[], contractInstance?: Contract - ) :Promise{ + ): Promise { const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -995,14 +942,12 @@ export class Pool { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) const weiMaxAmountsIn = [] const tokens = await this.getFinalTokens(poolAddress) - - - for (let i=0;i<2;i++){ - const amount = await this.amountToUnits(tokens[i],maxAmountsIn[i]) + + for (let i = 0; i < 2; i++) { + const amount = await this.amountToUnits(tokens[i], maxAmountsIn[i]) weiMaxAmountsIn.push(amount) } - //console.log(weiMaxAmountsIn) - + // console.log(weiMaxAmountsIn) let result = null @@ -1042,7 +987,7 @@ export class Pool { poolAmountIn: string, minAmountsOut: string[], contractInstance?: Contract - ) :Promise{ + ): Promise { const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -1076,10 +1021,9 @@ export class Pool { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) const weiMinAmountsOut = [] const tokens = await this.getFinalTokens(poolAddress) - - - for (let i=0;i<2;i++){ - const amount = await this.amountToUnits(tokens[i],minAmountsOut[i]) + + for (let i = 0; i < 2; i++) { + const amount = await this.amountToUnits(tokens[i], minAmountsOut[i]) weiMinAmountsOut.push(amount) } let result = null @@ -1116,7 +1060,7 @@ export class Pool { tokenAmountIn: string, minPoolAmountOut: string, contractInstance?: Contract - ) :Promise{ + ): Promise { const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -1152,7 +1096,7 @@ export class Pool { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let result = null let amountInFormatted - amountInFormatted = await this.amountToUnits(tokenIn,tokenAmountIn) + amountInFormatted = await this.amountToUnits(tokenIn, tokenAmountIn) const estGas = await this.estJoinswapExternAmountIn( account, poolAddress, @@ -1197,7 +1141,7 @@ export class Pool { poolAmountOut: string, maxAmountIn: string, contractInstance?: Contract - ) :Promise{ + ): Promise { const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -1214,8 +1158,6 @@ export class Pool { 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 @@ -1235,7 +1177,7 @@ export class Pool { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let result = null let maxAmountInFormatted - maxAmountInFormatted = await this.amountToUnits(tokenIn,maxAmountIn) + maxAmountInFormatted = await this.amountToUnits(tokenIn, maxAmountIn) const estGas = await this.estJoinswapPoolAmountOut( account, poolAddress, @@ -1278,7 +1220,7 @@ export class Pool { poolAmountIn: string, minTokenAmountOut: string, contractInstance?: Contract - ) :Promise{ + ): Promise { const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -1313,8 +1255,8 @@ 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) + let minTokenOutFormatted + minTokenOutFormatted = await this.amountToUnits(tokenOut, minTokenAmountOut) const estGas = await this.estExitswapPoolAmountIn( account, poolAddress, @@ -1341,15 +1283,15 @@ export class Pool { } /** - * Estimate gas cost for joinswapExternAmountIn - * @param {String} address - * @param {String} poolAddress - * @param {String} tokenOut - * @param {String} tokenAmountOut will be converted to wei - * @param {String} maxPoolAmountIn will be converted to wei - * @param {Contract} contractInstance optional contract instance - * @return {Promise} - */ + * Estimate gas cost for joinswapExternAmountIn + * @param {String} address + * @param {String} poolAddress + * @param {String} tokenOut + * @param {String} tokenAmountOut will be converted to wei + * @param {String} maxPoolAmountIn will be converted to wei + * @param {Contract} contractInstance optional contract instance + * @return {Promise} + */ public async estExitswapExternAmountOut( address: string, poolAddress: string, @@ -1357,7 +1299,7 @@ export class Pool { tokenAmountOut: string, maxPoolAmountIn: string, contractInstance?: Contract - ) :Promise{ + ): Promise { const poolContract = contractInstance || new this.web3.eth.Contract(this.poolABI as AbiItem[], poolAddress) @@ -1431,42 +1373,52 @@ export class Pool { tokenOut: string ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) + let decimalsTokenIn = 18 + let decimalsTokenOut = 18 + + const tokenInContract = new this.web3.eth.Contract( + defaultERC20ABI.abi as AbiItem[], + tokenIn + ) + const tokenOutContract = new this.web3.eth.Contract( + defaultERC20ABI.abi as AbiItem[], + tokenOut + ) + try { + decimalsTokenIn = await tokenInContract.methods.decimals().call() + } catch (e) { + this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') + } + try { + decimalsTokenOut = await tokenOutContract.methods.decimals().call() + } catch (e) { + this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18') + } + let price = null try { - const result = await pool.methods.getSpotPrice(tokenIn, tokenOut).call() - price = this.web3.utils.fromWei(result) + price = await pool.methods.getSpotPrice(tokenIn, tokenOut).call() + price = new BigNumber(price.toString()) } catch (e) { this.logger.error('ERROR: Failed to get spot price of swapping tokenIn to tokenOut') } - return price - } - // public async calcSpotPrice( - // poolAddress: string, - // tokenBalanceIn: string, - // tokenWeightIn: string, - // tokenBalanceOut: string, - // tokenWeightOut: string, - // swapFee: string - // ): Promise { - // const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) - // let amount = '0' - // try { - // const result = await pool.methods - // .calcSpotPrice( - // this.web3.utils.toWei(tokenBalanceIn), - // this.web3.utils.toWei(tokenWeightIn), - // this.web3.utils.toWei(tokenBalanceOut), - // this.web3.utils.toWei(tokenWeightOut), - // this.web3.utils.toWei(swapFee) - // ) - // .call() - // amount = this.web3.utils.fromWei(result) - // } catch (e) { - // this.logger.error('ERROR: Failed to call calcSpotPrice') - // } - // return amount - // } + let decimalsDiff + if (decimalsTokenIn > decimalsTokenOut) { + decimalsDiff = decimalsTokenIn - decimalsTokenOut + price = new BigNumber(price / 10 ** decimalsDiff) + // console.log(price.toString()) + price = price / 10 ** decimalsTokenOut + // console.log('dtIn') + } else { + decimalsDiff = decimalsTokenOut - decimalsTokenIn + price = new BigNumber(price * 10 ** (2 * decimalsDiff)) + price = price / 10 ** decimalsTokenOut + // console.log('usdcIn') + } + + return price.toString() + } public async getAmountInExactOut( poolAddress: string, @@ -1476,14 +1428,15 @@ export class Pool { ): Promise { const pool = new this.web3.eth.Contract(this.poolABI, poolAddress) let amountOutFormatted - amountOutFormatted = await this.amountToUnits(tokenOut,tokenAmountOut) + 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,amountOutFormatted) + const result = await pool.methods + .getAmountInExactOut(tokenIn, tokenOut, amountOutFormatted) .call() - amount = await this.unitsToAmount(tokenIn,result) + amount = await this.unitsToAmount(tokenIn, result) } catch (e) { this.logger.error('ERROR: Failed to calcInGivenOut') } @@ -1496,34 +1449,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 - 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 - // } + amountInFormatted = await this.amountToUnits(tokenIn, tokenAmountIn) + let amount = null - console.log(amountInFormatted) + // console.log(amountInFormatted) try { const result = await pool.methods .getAmountOutExactIn(tokenIn, tokenOut, amountInFormatted) .call() - amount = await this.unitsToAmount(tokenOut,result) - + amount = await this.unitsToAmount(tokenOut, result) } catch (e) { this.logger.error('ERROR: Failed to calcOutGivenIn') } diff --git a/test/TestContractHandler.ts b/test/TestContractHandler.ts index 8a536279..4ea9403b 100644 --- a/test/TestContractHandler.ts +++ b/test/TestContractHandler.ts @@ -3,7 +3,6 @@ import { Contract } from 'web3-eth-contract' import { AbiItem } from 'web3-utils/types' import MockERC20 from '@oceanprotocol/contracts/artifacts/contracts/utils/mock/MockERC20Decimals.sol/MockERC20Decimals.json' - const oceanAddress = '0x967da4048cd07ab37855c090aaf366e4ce1b9f48' export class TestContractHandler { public accounts: string[] @@ -30,7 +29,6 @@ export class TestContractHandler { public OPFCollectorBytecode: string public MockERC20Bytecode: string public OPFBytecode: string - public factory721Address: string public template721Address: string @@ -56,7 +54,7 @@ export class TestContractHandler { SideStakingABI?: AbiItem | AbiItem[], FixedRateABI?: AbiItem | AbiItem[], DispenserABI?: AbiItem | AbiItem[], - OPFABI?: AbiItem | AbiItem[], + OPFABI?: AbiItem | AbiItem[], template721Bytecode?: string, template20Bytecode?: string, @@ -66,7 +64,7 @@ export class TestContractHandler { sideStakingBytecode?: string, fixedRateBytecode?: string, dispenserBytecode?: string, - opfBytecode?:string + opfBytecode?: string ) { this.web3 = web3 this.ERC721Template = new this.web3.eth.Contract(ERC721TemplateABI) @@ -104,7 +102,7 @@ export class TestContractHandler { // get est gascost estGas = await this.OPFCollector.deploy({ data: this.OPFBytecode, - arguments: [owner,owner] + arguments: [owner, owner] }).estimateGas(function (err, estGas) { if (err) console.log('DeployContracts: ' + err) return estGas @@ -112,7 +110,7 @@ export class TestContractHandler { // deploy the contract and get it's address this.opfCollectorAddress = await this.OPFCollector.deploy({ data: this.OPFBytecode, - arguments: [owner,owner] + arguments: [owner, owner] }) .send({ from: owner, @@ -123,7 +121,6 @@ export class TestContractHandler { return contract.options.address }) - // DEPLOY POOL TEMPLATE // get est gascost estGas = await this.PoolTemplate.deploy({ diff --git a/test/unit/pools/balancer/Pool.test.ts b/test/unit/pools/balancer/Pool.test.ts index 380ceda4..4f8aa07c 100644 --- a/test/unit/pools/balancer/Pool.test.ts +++ b/test/unit/pools/balancer/Pool.test.ts @@ -3,6 +3,7 @@ import { AbiItem } from 'web3-utils/types' import { TestContractHandler } from '../../../TestContractHandler' import { Contract } from 'web3-eth-contract' import Web3 from 'web3' +import BigNumber from 'bignumber.js' import ERC721Factory from '@oceanprotocol/contracts/artifacts/contracts/ERC721Factory.sol/ERC721Factory.json' import ERC721Template from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC721Template.sol/ERC721Template.json' import SideStaking from '@oceanprotocol/contracts/artifacts/contracts/pools/ssContracts/SideStaking.sol/SideStaking.json' @@ -17,6 +18,7 @@ import { LoggerInstance } from '../../../../src/utils' import { NFTFactory } from '../../../../src/factories/NFTFactory' import { Pool } from '../../../../src/pools/balancer/Pool' import { CONNREFUSED } from 'dns' +import exp from 'constants' const { keccak256 } = require('@ethersproject/keccak256') const web3 = new Web3('http://127.0.0.1:8545') const communityCollector = '0xeE9300b7961e0a01d9f0adb863C7A227A07AaD75' @@ -69,6 +71,10 @@ describe('Pool unit test', () => { await contracts.deployContracts(factoryOwner, FactoryRouter.abi as AbiItem[]) + // initialize Pool instance + pool = new Pool(web3, LoggerInstance, PoolTemplate.abi as AbiItem[]) + assert(pool != null) + daiContract = new web3.eth.Contract( contracts.MockERC20.options.jsonInterface, contracts.daiAddress @@ -78,893 +84,938 @@ describe('Pool unit test', () => { 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] }) + await pool.approve( + contracts.accounts[0], + contracts.daiAddress, + contracts.factory721Address, + '2000' + ) + await pool.approve( + contracts.accounts[0], + contracts.usdcAddress, + contracts.factory721Address, + '10000' + ) + expect( + await pool.allowance( + contracts.daiAddress, + contracts.accounts[0], + contracts.factory721Address + ) + ).to.equal('2000') + expect( + await pool.allowance( + contracts.usdcAddress, + contracts.accounts[0], + contracts.factory721Address + ) + ).to.equal('10000') 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()) + + console.log( + await usdcContract.methods.decimals().call(), + 'USDC DECIMALS IN THIS TEST' + ) }) - it('should initiate Pool instance', async () => { - pool = new Pool(web3, LoggerInstance, PoolTemplate.abi as AbiItem[]) - - }) + describe('Test a pool with DAI (18 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: [] + } -describe('Test a pool with DAI (18 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 basetokenInitialLiq = await pool.amountToUnits(contracts.daiAddress, '2000') - const poolData = { - addresses: [ - contracts.sideStakingAddress, + const poolData = { + addresses: [ + contracts.sideStakingAddress, + contracts.daiAddress, + contracts.factory721Address, + contracts.accounts[0], + contracts.accounts[0], + contracts.poolTemplateAddress + ], + ssParams: [ + web3.utils.toWei('1'), // rate + 18, // basetokenDecimals + web3.utils.toWei('10000'), + 2500000, // vested blocks + web3.utils.toWei('2000') // 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 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) + }) + + 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') // 0.1% + }) + + 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 denormalized weight', async () => { + expect( + await pool.getDenormalizedWeight(poolAddress, contracts.daiAddress) + ).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.daiAddress) + }) + + it('#getDatatoken - should return the datatoken address', async () => { + expect(await pool.getDatatoken(poolAddress)).to.equal(erc20Token) + }) + + it('#swapExactAmountIn - should swap', async () => { + 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, '10') + const tx = await pool.swapExactAmountIn( + user2, + poolAddress, contracts.daiAddress, - contracts.factory721Address, - contracts.accounts[0], - contracts.accounts[0], - contracts.poolTemplateAddress - ], - ssParams: [ - web3.utils.toWei('1'), // rate - 18, // basetokenDecimals - web3.utils.toWei('10000'), - 2500000, // vested blocks - web3.utils.toWei('2000') // baseToken initial pool liquidity - ], - swapFees: [ - 1e15, // - 1e15 + '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(user2, contracts.daiAddress, poolAddress, '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 ] - } - const nftFactory = new NFTFactory(contracts.factory721Address, web3, LoggerInstance) + await pool.approve(user2, erc20Token, poolAddress, '50') + await pool.approve(user2, contracts.daiAddress, poolAddress, '50') + 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') + }) + it('#joinswapExternAmountIn- user2 should add liquidity, receiving LP tokens', async () => { + const daiAmountIn = '100' + const minBPTOut = '0.1' + await pool.approve(user2, contracts.daiAddress, poolAddress, '100', true) + expect(await pool.allowance(contracts.daiAddress, user2, poolAddress)).to.equal( + '100' + ) + const tx = await pool.joinswapExternAmountIn( + user2, + poolAddress, + contracts.daiAddress, + daiAmountIn, + minBPTOut + ) - const txReceipt = await nftFactory.createNftErcWithPool( - contracts.accounts[0], - nftData, - ercData, - poolData - ) + assert(tx != null) - erc20Token = txReceipt.events.TokenCreated.returnValues.newTokenAddress - poolAddress = txReceipt.events.NewPool.returnValues.poolAddress + 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[1].returnValues.bptAmount + ) + }) - 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('#joinswapPoolAmountOut- user2 should add liquidity, receiving LP tokens', async () => { + const BPTAmountOut = '0.1' + const maxDAIIn = '100' + + await pool.approve(user2, contracts.daiAddress, poolAddress, '100') + + 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[1].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) + }) + + it('#getAmountInExactOut- should get the amount in for exact out', async () => { + const maxBTPIn = '0.5' + const exactDAIOut = '1' + + const amountIn = await pool.getAmountInExactOut( + poolAddress, + erc20Token, + contracts.daiAddress, + exactDAIOut + ) + + assert(amountIn != null) + + // console.log(tx) + + const spotPrice = await pool.getSpotPrice( + poolAddress, + erc20Token, + contracts.daiAddress + ) + + // amount of DAI 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.daiAddress, + exactDTIn + ) + + assert(amountOut != null) + + console.log(amountOut) + + const spotPrice = await pool.getSpotPrice( + poolAddress, + contracts.daiAddress, + erc20Token + ) + console.log(spotPrice) + // amount of DAI 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.daiAddress)) != null + ) + assert( + (await pool.getSpotPrice(poolAddress, contracts.daiAddress, erc20Token)) != null + ) + }) + + it('#getMarketFees- should get market fees for each token', async () => { + // we haven't performed any swap DT => DAI so there's no fee in erc20Token + // but there's a fee in DAI + assert((await pool.getMarketFees(poolAddress, erc20Token)) === '0') + assert((await pool.getMarketFees(poolAddress, contracts.daiAddress)) > '0') + }) + + it('#getCommunityFees- should get community fees for each token', async () => { + // we haven't performed any swap DT => DAI so there's no fee in erc20Token + // but there's a fee in DAI + + assert((await pool.getCommunityFees(poolAddress, erc20Token)) === '0') + assert((await pool.getCommunityFees(poolAddress, contracts.daiAddress)) > '0') + }) + + it('#collectMarketFee- should collect market fees for each token', async () => { + const spotPriceBefore = await pool.getSpotPrice( + poolAddress, + erc20Token, + contracts.daiAddress + ) + // contracts.accounts[0] is the marketFeeCollector + assert((await pool.getMarketFeeCollector(poolAddress)) === contracts.accounts[0]) + // user3 has no DAI (we are going to send DAI fee to him) + assert((await daiContract.methods.balanceOf(user3).call()) === '0') + // only marketFeeCollector can call this, set user3 as receiver + await pool.collectMarketFee(contracts.accounts[0], poolAddress, user3) + // DAI fees have been collected + assert((await pool.getMarketFees(poolAddress, contracts.daiAddress)) === '0') + // user3 got DAI + assert((await daiContract.methods.balanceOf(user3).call()) > '0') + // Spot price hasn't changed after fee collection + assert( + (await pool.getSpotPrice(poolAddress, erc20Token, contracts.daiAddress)) === + 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.daiAddress + ) + // some fee are available in DAI + assert((await pool.getCommunityFees(poolAddress, contracts.daiAddress)) > '0') + // opf collector has no DAI + assert( + (await daiContract.methods.balanceOf(contracts.opfCollectorAddress).call()) === + '0' + ) + // anyone can call callectOPF + await pool.collectOPF(contracts.accounts[0], poolAddress) + // DAI fees have been collected + assert((await pool.getCommunityFees(poolAddress, contracts.daiAddress)) === '0') + // OPF collector got DAI + assert( + (await daiContract.methods.balanceOf(contracts.opfCollectorAddress).call()) > '0' + ) + // Spot price hasn't changed after fee collection + assert( + (await pool.getSpotPrice(poolAddress, erc20Token, contracts.daiAddress)) === + 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) + }) }) - 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) - ) - }) + 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 basetokenInitialLiq = Number( + await pool.amountToUnits(contracts.usdcAddress, '2000') + ) + console.log(basetokenInitialLiq.toString()) + const poolData = { + addresses: [ + contracts.sideStakingAddress, + contracts.usdcAddress, + contracts.factory721Address, + contracts.accounts[0], + contracts.accounts[0], + contracts.poolTemplateAddress + ], + ssParams: [ + web3.utils.toWei('1'), // rate + await usdcContract.methods.decimals().call(), // basetokenDecimals + web3.utils.toWei('10000'), + 2500000, // vested blocks + basetokenInitialLiq // baseToken initial pool liquidity + ], + swapFees: [ + 1e15, // + 1e15 + ] + } - it('#getNumTokens - should return num of tokens in pool (2)', async () => { - expect(await pool.getNumTokens(poolAddress)).to.equal('2') - }) + const nftFactory = new NFTFactory(contracts.factory721Address, web3, LoggerInstance) - 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) - }) - - 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') //0.1% - }) - - 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 denormalized weight', async () => { - expect(await pool.getDenormalizedWeight(poolAddress, contracts.daiAddress)).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.daiAddress - ) - - }) - - it('#getDatatoken - should return the datatoken address', async () => { - expect(await pool.getDatatoken(poolAddress)).to.equal( - erc20Token - ) - }) - - it('#swapExactAmountIn - should swap', async () => { - 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 - ) - }) - - 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('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) - }) - - it('#getAmountInExactOut- should get the amount in for exact out', async () => { - const maxBTPIn = "0.5" - const exactDAIOut = "1" - - const amountIn = await pool.getAmountInExactOut( - poolAddress, - erc20Token, - contracts.daiAddress, - exactDAIOut - ) - - assert(amountIn != null) - - // console.log(tx) - - const spotPrice = await pool.getSpotPrice(poolAddress,erc20Token,contracts.daiAddress) - - // amount of DAI 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.daiAddress, - exactDTIn - ) - - assert(amountOut != null) - - console.log(amountOut) - - const spotPrice = await pool.getSpotPrice(poolAddress,contracts.daiAddress,erc20Token) - console.log(spotPrice) - // amount of DAI 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.daiAddress) != null) - assert(await pool.getSpotPrice(poolAddress,contracts.daiAddress,erc20Token) != null) - - }) - - it('#getMarketFees- should get market fees for each token', async () => { - - // we haven't performed any swap DT => DAI so there's no fee in erc20Token - // but there's a fee in DAI - assert(await pool.getMarketFees(poolAddress,erc20Token) == '0') - assert(await pool.getMarketFees(poolAddress,contracts.daiAddress) > '0') - - }) - - it('#getCommunityFees- should get community fees for each token', async () => { - // we haven't performed any swap DT => DAI so there's no fee in erc20Token - // but there's a fee in DAI - - assert(await pool.getCommunityFees(poolAddress,erc20Token) == '0') - assert(await pool.getCommunityFees(poolAddress,contracts.daiAddress) > '0') - - }) - - it('#collectMarketFee- should collect market fees for each token', async () => { - const spotPriceBefore = await pool.getSpotPrice(poolAddress,erc20Token,contracts.daiAddress) - // contracts.accounts[0] is the marketFeeCollector - assert(await pool.getMarketFeeCollector(poolAddress) == contracts.accounts[0]) - // user3 has no DAI (we are going to send DAI fee to him) - assert(await daiContract.methods.balanceOf(user3).call() == '0') - // only marketFeeCollector can call this, set user3 as receiver - await pool.collectMarketFee(contracts.accounts[0],poolAddress,user3) - // DAI fees have been collected - assert(await pool.getMarketFees(poolAddress,contracts.daiAddress) == '0') - // user3 got DAI - assert(await daiContract.methods.balanceOf(user3).call() > '0') - // Spot price hasn't changed after fee collection - assert(await pool.getSpotPrice(poolAddress,erc20Token,contracts.daiAddress)== 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.daiAddress) - // some fee are available in DAI - assert(await pool.getCommunityFees(poolAddress,contracts.daiAddress) > '0') - // opf collector has no DAI - assert(await daiContract.methods.balanceOf(contracts.opfCollectorAddress).call() == '0') - // anyone can call callectOPF - await pool.collectOPF(contracts.accounts[0],poolAddress) - // DAI fees have been collected - assert(await pool.getCommunityFees(poolAddress,contracts.daiAddress) == '0') - // OPF collector got DAI - assert(await daiContract.methods.balanceOf(contracts.opfCollectorAddress).call() > '0') - // Spot price hasn't changed after fee collection - assert(await pool.getSpotPrice(poolAddress,erc20Token,contracts.daiAddress)== 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) - - }) -}) - -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: [ + const txReceipt = await nftFactory.createNftErcWithPool( contracts.accounts[0], - user3, - contracts.accounts[0], - '0x0000000000000000000000000000000000000000' - ], - uints: [web3.utils.toWei('1000000'), 0], - bytess: [] - } + nftData, + ercData, + poolData + ) - const poolData = { - addresses: [ - contracts.sideStakingAddress, + 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 Reserve', 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 = await pool.amountToUnits(contracts.usdcAddress, '1000') // 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, '10') + const tx = await pool.swapExactAmountIn( + user2, + poolAddress, 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 + '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( + (await pool.amountToUnits(contracts.usdcAddress, '990')).toString() + ) + await pool.approve(user2, contracts.usdcAddress, poolAddress, '100') + 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 ] - } - - 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) - + + await pool.approve(user2, erc20Token, poolAddress, '50') + await pool.approve(user2, contracts.usdcAddress, poolAddress, '50') + 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, '100', true) + + 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[1].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, '100') + + 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[1].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(), 'spotPrice') + // 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, '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) + }) }) }) - -})