From 8f43ae058f0a69839833e7be6050dc7a23618b98 Mon Sep 17 00:00:00 2001 From: lacoop6tu Date: Fri, 5 Nov 2021 12:36:07 -0500 Subject: [PATCH] add more functions and tests --- src/pools/balancer/Pool.ts | 85 ++- src/pools/fixedRate/FixedRateExchange.ts | 522 ++++++++++++++---- src/pools/ssContracts/SideStaking.ts | 16 +- .../pools/fixedRate/FixedRateExchange.test.ts | 187 +++++-- .../pools/ssContracts/SideStaking.test.ts | 10 +- 5 files changed, 620 insertions(+), 200 deletions(-) diff --git a/src/pools/balancer/Pool.ts b/src/pools/balancer/Pool.ts index b6df3fa2..4a695a22 100644 --- a/src/pools/balancer/Pool.ts +++ b/src/pools/balancer/Pool.ts @@ -4,11 +4,11 @@ import { TransactionReceipt } from 'web3-core' import { Contract } from 'web3-eth-contract' import { Logger, getFairGasPrice } from '../../utils' import BigNumber from 'bignumber.js' -const BN = require('bn.js'); import PoolTemplate from '@oceanprotocol/contracts/artifacts/contracts/pools/balancer/BPool.sol/BPool.json' import defaultPool from '@oceanprotocol/contracts/artifacts/contracts/pools/FactoryRouter.sol/FactoryRouter.json' import defaultERC20ABI from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC20Template.sol/ERC20Template.json' import Decimal from 'decimal.js' +const BN = require('bn.js') const MaxUint256 = '115792089237316195423570985008687907853269984665640564039457584007913129639934' @@ -131,11 +131,10 @@ export class Pool { let result = null const amountFormatted = await this.amountToUnits(tokenAddress, amount) const estGas = await this.estApprove(account, tokenAddress, spender, amountFormatted) - - + try { result = await token.methods - .approve(spender,new BigNumber(await this.amountToUnits(tokenAddress, amount))) + .approve(spender, new BigNumber(await this.amountToUnits(tokenAddress, amount))) .send({ from: account, gas: estGas + 1, @@ -165,8 +164,7 @@ export class Pool { return result } - - /** + /** * Estimate gas cost for setSwapFee * @param {String} account * @param {String} tokenAddress @@ -176,30 +174,27 @@ export class Pool { * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estSetSwapFee( - account: string, - poolAddress: string, - fee: string, - contractInstance?: Contract - ): Promise { - const poolContract = - contractInstance || - new this.web3.eth.Contract(defaultERC20ABI.abi as AbiItem[], poolAddress) - - const gasLimitDefault = this.GASLIMIT_DEFAULT - let estGas - try { - estGas = await poolContract.methods - .setSwapFee(fee) - .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) - } catch (e) { - estGas = gasLimitDefault - - } - return estGas + public async estSetSwapFee( + account: string, + poolAddress: string, + fee: string, + contractInstance?: Contract + ): Promise { + const poolContract = + contractInstance || + new this.web3.eth.Contract(defaultERC20ABI.abi as AbiItem[], poolAddress) + + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await poolContract.methods + .setSwapFee(fee) + .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault } - - + return estGas + } /** * Set pool fee @@ -216,8 +211,8 @@ export class Pool { from: account }) let result = null - const estGas = await this.estSetSwapFee(account,poolAddress,fee) - + const estGas = await this.estSetSwapFee(account, poolAddress, fee) + try { result = await pool.methods.setSwapFee(this.web3.utils.toWei(fee)).send({ from: account, @@ -572,7 +567,7 @@ export class Pool { 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, @@ -633,7 +628,7 @@ export class Pool { 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, @@ -698,7 +693,7 @@ export class Pool { poolAddress, newCollector ) - + try { result = await pool.methods.updateMarketFeeCollector(newCollector).send({ from: address, @@ -746,7 +741,7 @@ export class Pool { tokenAmountIn, tokenOut, minAmountOut, - maxPrice ? maxPrice : MaxUint256 + maxPrice || MaxUint256 ) .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) } catch (e) { @@ -826,8 +821,8 @@ export class Pool { minAmountOutFormatted.toString(), maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256 ) - - //console.log(minAmountOutFormatted, 'minamoutnoutformatted') + + // console.log(minAmountOutFormatted, 'minamoutnoutformatted') try { result = await pool.methods .swapExactAmountIn( @@ -884,7 +879,7 @@ export class Pool { maxAmountIn, tokenOut, amountOut, - maxPrice ? maxPrice : MaxUint256 + maxPrice || MaxUint256 ) .estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas)) } catch (e) { @@ -927,7 +922,7 @@ export class Pool { amountOutFormatted, maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256 ) - + try { result = await pool.methods .swapExactAmountOut( @@ -1012,7 +1007,7 @@ export class Pool { this.web3.utils.toWei(poolAmountOut), weiMaxAmountsIn ) - + try { result = await pool.methods .joinPool(this.web3.utils.toWei(poolAmountOut), weiMaxAmountsIn) @@ -1088,7 +1083,7 @@ export class Pool { this.web3.utils.toWei(poolAmountIn), weiMinAmountsOut ) - + try { result = await pool.methods .exitPool(this.web3.utils.toWei(poolAmountIn), weiMinAmountsOut) @@ -1160,7 +1155,7 @@ export class Pool { amountInFormatted, this.web3.utils.toWei(minPoolAmountOut) ) - + try { result = await pool.methods .joinswapExternAmountIn( @@ -1241,7 +1236,7 @@ export class Pool { this.web3.utils.toWei(poolAmountOut), maxAmountInFormatted ) - + try { result = await pool.methods .joinswapPoolAmountOut( @@ -1321,7 +1316,7 @@ export class Pool { this.web3.utils.toWei(poolAmountIn), minTokenOutFormatted ) - + try { result = await pool.methods .exitswapPoolAmountIn( @@ -1400,7 +1395,7 @@ export class Pool { this.web3.utils.toWei(tokenAmountOut), this.web3.utils.toWei(maxPoolAmountIn) ) - + try { result = await pool.methods .exitswapExternAmountOut( diff --git a/src/pools/fixedRate/FixedRateExchange.ts b/src/pools/fixedRate/FixedRateExchange.ts index d9ac70ac..2414c71c 100644 --- a/src/pools/fixedRate/FixedRateExchange.ts +++ b/src/pools/fixedRate/FixedRateExchange.ts @@ -10,15 +10,30 @@ import { Logger, getFairGasPrice } from '../../utils' const MAX_AWAIT_PROMISES = 10 export interface FixedPriceExchange { - exchangeID?: string + active: boolean exchangeOwner: string dataToken: string baseToken: string fixedRate: string - active: boolean - supply: string + dtDecimals: string + btDecimals: string + dtBalance: string + btBalance: string + dtSupply: string + btSupply: string + withMint: boolean + allowedSwapper: string + exchangeID?: string } +export interface FeesInfo { + opfFee: string + marketFee: string + marketFeeCollector: string + marketFeeAvailable: string + oceanFeeAvailable: string + exchangeID: string +} export interface FixedPriceSwap { exchangeID: string caller: string @@ -35,18 +50,16 @@ export class FixedRateExchange { public GASLIMIT_DEFAULT = 1000000 /** Ocean related functions */ public oceanAddress: string = null - public fixedRateAddress:string + public fixedRateAddress: string public fixedRateExchangeABI: AbiItem | AbiItem[] - public fixedRateContract:Contract + public fixedRateContract: Contract public web3: Web3 public contract: Contract = null private logger: Logger - + public startBlock: number public ssABI: AbiItem | AbiItem[] - - /** * Instantiate FixedRateExchange * @param {any} web3 @@ -61,7 +74,7 @@ export class FixedRateExchange { startBlock?: number ) { this.web3 = web3 - + if (startBlock) this.startBlock = startBlock else this.startBlock = 0 this.fixedRateExchangeABI = @@ -72,16 +85,17 @@ export class FixedRateExchange { this.fixedRateExchangeABI, this.fixedRateAddress ) - + this.logger = logger } + async amountToUnits(token: string, amount: string): Promise { let decimals = 18 const tokenContract = new this.web3.eth.Contract( defaultERC20ABI.abi as AbiItem[], token ) - console.log('1') + try { decimals = await tokenContract.methods.decimals().call() } catch (e) { @@ -110,20 +124,23 @@ export class FixedRateExchange { return amountFormatted.toString() } - - /** * Creates unique exchange identifier. * @param {String} dataToken Data Token Contract Address * @param {String} owner Owner of the exchange * @return {Promise} exchangeId */ - public async generateExchangeId(dataToken: string, owner: string): Promise { + public async generateExchangeId( + basetoken: string, + dataToken: string, + owner: string + ): Promise { const exchangeId = await this.contract.methods - .generateExchangeId(this.oceanAddress, dataToken, owner) + .generateExchangeId(basetoken, dataToken, owner) .call() return exchangeId } + /** * Estimate gas cost for buyDT * @param {String} account @@ -132,26 +149,26 @@ export class FixedRateExchange { * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estBuyDT( + public async estBuyDT( account: string, datatokenAddress: string, - dtAmount:string, + dtAmount: string, maxBasetokenAmount: string, contractInstance?: Contract ): Promise { - const fixedRate = - contractInstance || this.fixedRateContract + const fixedRate = contractInstance || this.fixedRateContract const gasLimitDefault = this.GASLIMIT_DEFAULT let estGas try { estGas = await fixedRate.methods - .buyDT(datatokenAddress,dtAmount.toString(),maxBasetokenAmount.toString()) + .buyDT(datatokenAddress, dtAmount.toString(), maxBasetokenAmount.toString()) .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) } catch (e) { estGas = gasLimitDefault } return estGas } + /** * Atomic swap * @param {String} exchangeId ExchangeId @@ -164,15 +181,30 @@ export class FixedRateExchange { address: string, exchangeId: string, datatokenAmount: string, - maxBasetokenAmount:string + maxBasetokenAmount: string ): Promise { - const dtAmountFormatted = await this.amountToUnits(((await this.getExchange(exchangeId)).dataToken),datatokenAmount) - const maxBtFormatted = await this.amountToUnits(((await this.getExchange(exchangeId)).baseToken),maxBasetokenAmount) - - const estGas = await this.estBuyDT(address,exchangeId,dtAmountFormatted,maxBtFormatted) + const dtAmountFormatted = await this.amountToUnits( + ( + await this.getExchange(exchangeId) + ).dataToken, + datatokenAmount + ) + const maxBtFormatted = await this.amountToUnits( + ( + await this.getExchange(exchangeId) + ).baseToken, + maxBasetokenAmount + ) + + const estGas = await this.estBuyDT( + address, + exchangeId, + dtAmountFormatted, + maxBtFormatted + ) try { const trxReceipt = await this.contract.methods - .buyDT(exchangeId, dtAmountFormatted,maxBtFormatted) + .buyDT(exchangeId, dtAmountFormatted, maxBtFormatted) .send({ from: address, gas: estGas + 1, @@ -193,26 +225,26 @@ export class FixedRateExchange { * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estSellDT( + public async estSellDT( account: string, datatokenAddress: string, - dtAmount:string, + dtAmount: string, maxBasetokenAmount: string, contractInstance?: Contract ): Promise { - const fixedRate = - contractInstance || this.fixedRateContract + const fixedRate = contractInstance || this.fixedRateContract const gasLimitDefault = this.GASLIMIT_DEFAULT let estGas try { estGas = await fixedRate.methods - .sellDT(datatokenAddress,dtAmount,maxBasetokenAmount) + .sellDT(datatokenAddress, dtAmount, maxBasetokenAmount) .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) } catch (e) { estGas = gasLimitDefault } return estGas } + /** * Atomic swap * @param {String} exchangeId ExchangeId @@ -225,15 +257,29 @@ export class FixedRateExchange { address: string, exchangeId: string, datatokenAmount: string, - minBasetokenAmount:string + minBasetokenAmount: string ): Promise { - - const dtAmountFormatted = await this.amountToUnits((await this.getExchange(exchangeId)).dataToken,datatokenAmount) - const minBtFormatted = await this.amountToUnits((await this.getExchange(exchangeId)).baseToken,minBasetokenAmount) - const estGas = await this.estBuyDT(address,exchangeId,dtAmountFormatted,minBtFormatted) + const dtAmountFormatted = await this.amountToUnits( + ( + await this.getExchange(exchangeId) + ).dataToken, + datatokenAmount + ) + const minBtFormatted = await this.amountToUnits( + ( + await this.getExchange(exchangeId) + ).baseToken, + minBasetokenAmount + ) + const estGas = await this.estBuyDT( + address, + exchangeId, + dtAmountFormatted, + minBtFormatted + ) try { const trxReceipt = await this.contract.methods - .sellDT(exchangeId, dtAmountFormatted,minBtFormatted) + .sellDT(exchangeId, dtAmountFormatted, minBtFormatted) .send({ from: address, gas: estGas + 1, @@ -265,19 +311,18 @@ export class FixedRateExchange { * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estSetRate( + public async estSetRate( account: string, exchangeId: string, - newRate:string, + newRate: string, contractInstance?: Contract ): Promise { - const fixedRate = - contractInstance || this.fixedRateContract + const fixedRate = contractInstance || this.fixedRateContract const gasLimitDefault = this.GASLIMIT_DEFAULT let estGas try { estGas = await fixedRate.methods - .setRate(exchangeId,newRate) + .setRate(exchangeId, newRate) .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) } catch (e) { estGas = gasLimitDefault @@ -295,13 +340,15 @@ export class FixedRateExchange { public async setRate( address: string, exchangeId: string, - newRate: number, - + newRate: string ): Promise { - - const estGas = await this.estSetRate(address,exchangeId,this.web3.utils.toWei(String(newRate))) + const estGas = await this.estSetRate( + address, + exchangeId, + this.web3.utils.toWei(String(newRate)) + ) const trxReceipt = await this.contract.methods - .setRate(exchangeId, this.web3.utils.toWei(String(newRate))) + .setRate(exchangeId, this.web3.utils.toWei(newRate)) .send({ from: address, gas: estGas + 1, @@ -310,31 +357,80 @@ export class FixedRateExchange { return trxReceipt } - /** + /** + * Estimate gas cost for setRate + * @param {String} account + * @param {String} exchangeId ExchangeId + * @param {String} newAllowedSwapper new allowed swapper address + * @param {Contract} contractInstance optional contract instance + * @return {Promise} + */ + public async estSetAllowedSwapper( + account: string, + exchangeId: string, + newAllowedSwapper: string, + contractInstance?: Contract + ): Promise { + const fixedRate = contractInstance || this.fixedRateContract + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await fixedRate.methods + .setRate(exchangeId, newAllowedSwapper) + .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + } + return estGas + } + + /** + * Set new rate + * @param {String} exchangeId ExchangeId + * @param {String} newAllowedSwapper newAllowedSwapper (set address zero if we want to remove allowed swapper) + * @param {String} address User account + * @return {Promise} transaction receipt + */ + public async setAllowedSwapper( + address: string, + exchangeId: string, + newAllowedSwapper: string + ): Promise { + const estGas = await this.estSetAllowedSwapper(address, exchangeId, newAllowedSwapper) + const trxReceipt = await this.contract.methods + .setAllowedSwapper(exchangeId, newAllowedSwapper) + .send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + return trxReceipt + } + + /** * Estimate gas cost for activate * @param {String} account * @param {String} exchangeId ExchangeId * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estActivate( - account: string, - exchangeId: string, - contractInstance?: Contract - ): Promise { - const fixedRate = - contractInstance || this.fixedRateContract - const gasLimitDefault = this.GASLIMIT_DEFAULT - let estGas - try { - estGas = await fixedRate.methods + public async estActivate( + account: string, + exchangeId: string, + contractInstance?: Contract + ): Promise { + const fixedRate = contractInstance || this.fixedRateContract + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await fixedRate.methods .toggleExchangeState(exchangeId) - .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) - } catch (e) { - estGas = gasLimitDefault - } - return estGas + .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault } + return estGas + } /** * Activate an exchange @@ -350,8 +446,8 @@ export class FixedRateExchange { if (!exchange) return null if (exchange.active === true) return null const gasLimitDefault = this.GASLIMIT_DEFAULT - - const estGas = await this.estActivate(address,exchangeId) + + const estGas = await this.estActivate(address, exchangeId) const trxReceipt = await this.contract.methods.toggleExchangeState(exchangeId).send({ from: address, gas: estGas + 1, @@ -360,33 +456,30 @@ export class FixedRateExchange { return trxReceipt } - /** + /** * Estimate gas cost for deactivate * @param {String} account * @param {String} exchangeId ExchangeId * @param {Contract} contractInstance optional contract instance * @return {Promise} */ - public async estDeactivate( - account: string, - exchangeId: string, - contractInstance?: Contract - ): Promise { - const fixedRate = - contractInstance || this.fixedRateContract - const gasLimitDefault = this.GASLIMIT_DEFAULT - let estGas - try { - estGas = await fixedRate.methods - .toggleExchangeState(exchangeId) - .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) - } catch (e) { - estGas = gasLimitDefault - } - return estGas - } - - + public async estDeactivate( + account: string, + exchangeId: string, + contractInstance?: Contract + ): Promise { + const fixedRate = contractInstance || this.fixedRateContract + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await fixedRate.methods + .toggleExchangeState(exchangeId) + .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + } + return estGas + } /** * Deactivate an exchange @@ -397,18 +490,19 @@ export class FixedRateExchange { public async deactivate( address: string, exchangeId: string - ): Promise { const exchange = await this.getExchange(exchangeId) if (!exchange) return null if (exchange.active === false) return null - - const estGas = await this.estDeactivate(address,exchangeId) + + const estGas = await this.estDeactivate(address, exchangeId) + const trxReceipt = await this.contract.methods.toggleExchangeState(exchangeId).send({ from: address, gas: estGas + 1, gasPrice: await getFairGasPrice(this.web3) }) + return trxReceipt } @@ -423,29 +517,101 @@ export class FixedRateExchange { } /** - * Get Supply + * Get Datatoken Supply in the exchange * @param {String} exchangeId ExchangeId - * @return {Promise} Rate (converted from wei) + * @return {Promise} dt supply formatted */ - public async getSupply(exchangeId: string): Promise { - const weiRate = await this.contract.methods.getSupply(exchangeId).call() - return this.web3.utils.fromWei(weiRate) + public async getDTSupply(exchangeId: string): Promise { + const dtSupply = await this.contract.methods.getDTSupply(exchangeId).call() + return await this.unitsToAmount( + ( + await this.getExchange(exchangeId) + ).dataToken, + dtSupply + ) } /** - * getOceanNeeded + * Get Basetoken Supply in the exchange * @param {String} exchangeId ExchangeId - * @param {Number} dataTokenAmount Amount of Data Tokens - * @return {Promise} Ocean amount needed + * @return {Promise} dt supply formatted */ - public async getOceanNeeded( + public async getBTSupply(exchangeId: string): Promise { + const btSupply = await this.contract.methods.getBTSupply(exchangeId).call() + return await this.unitsToAmount( + ( + await this.getExchange(exchangeId) + ).baseToken, + btSupply + ) + } + + /** + * Get Allower Swapper (if set this is the only account which can use this exchange, else is set at address(0)) + * @param {String} exchangeId ExchangeId + * @return {Promise} address of allowedSwapper + */ + public async getAllowedSwapper(exchangeId: string): Promise { + return await this.contract.methods.getAllowedSwapper(exchangeId).call() + } + + /** + * getBTNeeded - returns amount in basetoken that user will pay for dataTokenAmount + * @param {String} exchangeId ExchangeId + * @param {Number} dataTokenAmount Amount of Data Tokens user wants to buy + * @return {Promise} Amount of basetoken needed for buying + */ + public async getAmountBTIn( exchangeId: string, dataTokenAmount: string ): Promise { - const weiRate = await this.contract.methods - .calcBaseInGivenOutDT(exchangeId, this.web3.utils.toWei(dataTokenAmount)) + const result = await this.contract.methods + .calcBaseInGivenOutDT( + exchangeId, + await this.amountToUnits( + ( + await this.getExchange(exchangeId) + ).dataToken, + dataTokenAmount + ) + ) .call() - return this.web3.utils.fromWei(weiRate) + return await this.unitsToAmount( + ( + await this.getExchange(exchangeId) + ).baseToken, + result.baseTokenAmount + ) + } + + /** + * getBTOut - returns amount in basetoken that user will receive for dataTokenAmount sold + * @param {String} exchangeId ExchangeId + * @param {Number} dataTokenAmount Amount of Data Tokens + * @return {Promise} Amount of basetokens user will receive + */ + public async getAmountBTOut( + exchangeId: string, + dataTokenAmount: string + ): Promise { + const result = await this.contract.methods + .calcBaseOutGivenInDT( + exchangeId, + await this.amountToUnits( + ( + await this.getExchange(exchangeId) + ).dataToken, + dataTokenAmount + ) + ) + .call() + + return await this.unitsToAmount( + ( + await this.getExchange(exchangeId) + ).baseToken, + result.baseTokenAmount + ) } /** @@ -457,9 +623,50 @@ export class FixedRateExchange { const result: FixedPriceExchange = await this.contract.methods .getExchange(exchangeId) .call() - // result.fixedRate = this.web3.utils.fromWei(result.fixedRate) - // result.supply = this.web3.utils.fromWei(result.supply) - // result.exchangeID = exchangeId + result.dtDecimals = result.dtDecimals.toString() + result.btDecimals = result.btDecimals.toString() + result.dtBalance = await this.unitsToAmount(result.dataToken, result.dtBalance) + result.btBalance = await this.unitsToAmount(result.baseToken, result.btBalance) + result.dtSupply = await this.unitsToAmount(result.dataToken, result.dtSupply) + result.btSupply = await this.unitsToAmount(result.baseToken, result.btSupply) + result.fixedRate = this.web3.utils.fromWei(result.fixedRate) + result.exchangeID = exchangeId + return result + } + + /** + * Get fee details for an exchange + * @param {String} exchangeId ExchangeId + * @return {Promise} Exchange details + */ + public async getFeesInfo(exchangeId: string): Promise { + const result: FeesInfo = await this.contract.methods.getFeesInfo(exchangeId).call() + result.opfFee = await this.unitsToAmount( + ( + await this.getExchange(exchangeId) + ).baseToken, + result.opfFee + ) + result.marketFee = await this.unitsToAmount( + ( + await this.getExchange(exchangeId) + ).baseToken, + result.marketFee + ) + result.marketFeeAvailable = await this.unitsToAmount( + ( + await this.getExchange(exchangeId) + ).baseToken, + result.marketFeeAvailable + ) + result.oceanFeeAvailable = await this.unitsToAmount( + ( + await this.getExchange(exchangeId) + ).baseToken, + result.oceanFeeAvailable + ) + + result.exchangeID = exchangeId return result } @@ -482,7 +689,106 @@ export class FixedRateExchange { return result } - + /** + * Estimate gas cost for activate + * @param {String} account + * @param {String} exchangeId ExchangeId + * @param {Contract} contractInstance optional contract instance + * @return {Promise} + */ + public async estActivateMint( + account: string, + exchangeId: string, + contractInstance?: Contract + ): Promise { + const fixedRate = contractInstance || this.fixedRateContract + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await fixedRate.methods + .toggleMintState(exchangeId, true) + .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + } + return estGas + } - -} \ No newline at end of file + /** + * Activate minting option for fixed rate contract + * @param {String} exchangeId ExchangeId + * @param {String} address User address + * @return {Promise} transaction receipt + */ + public async activateMint( + address: string, + exchangeId: string + ): Promise { + const exchange = await this.getExchange(exchangeId) + if (!exchange) return null + if (exchange.withMint === true) return null + const gasLimitDefault = this.GASLIMIT_DEFAULT + + const estGas = await this.estActivateMint(address, exchangeId) + const trxReceipt = await this.contract.methods + .toggleMintState(exchangeId, true) + .send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + return trxReceipt + } + + /** + * Estimate gas cost for deactivate + * @param {String} account + * @param {String} exchangeId ExchangeId + * @param {Contract} contractInstance optional contract instance + * @return {Promise} + */ + public async estDeactivateMint( + account: string, + exchangeId: string, + contractInstance?: Contract + ): Promise { + const fixedRate = contractInstance || this.fixedRateContract + const gasLimitDefault = this.GASLIMIT_DEFAULT + let estGas + try { + estGas = await fixedRate.methods + .toggleMintState(exchangeId) + .estimateGas({ from: account }, (err, estGas) => (err ? gasLimitDefault : estGas)) + } catch (e) { + estGas = gasLimitDefault + } + return estGas + } + + /** + * Deactivate minting for fixed rate + * @param {String} exchangeId ExchangeId + * @param {String} address User address + * @return {Promise} transaction receipt + */ + public async deactivateMint( + address: string, + exchangeId: string + ): Promise { + const exchange = await this.getExchange(exchangeId) + if (!exchange) return null + if (exchange.withMint === false) return null + + const estGas = await this.estDeactivate(address, exchangeId) + + const trxReceipt = await this.contract.methods + .toggleMintState(exchangeId, false) + .send({ + from: address, + gas: estGas + 1, + gasPrice: await getFairGasPrice(this.web3) + }) + + return trxReceipt + } +} diff --git a/src/pools/ssContracts/SideStaking.ts b/src/pools/ssContracts/SideStaking.ts index afb493fa..1b47306c 100644 --- a/src/pools/ssContracts/SideStaking.ts +++ b/src/pools/ssContracts/SideStaking.ts @@ -65,7 +65,7 @@ export class SideStaking { /** * Get DTs in circulation (amount vested not accounted) * @param {String} ssAddress side staking contract address - * @param {String} datatokenAddress datatoken address + * @param {String} datatokenAddress datatoken address * @return {String} */ async getDataTokenCirculatingSupply( @@ -87,7 +87,7 @@ export class SideStaking { /** * Get Publisher address * @param {String} ssAddress side staking contract address - * @param {String} datatokenAddress datatoken address + * @param {String} datatokenAddress datatoken address * @return {String} */ async getPublisherAddress( @@ -159,7 +159,7 @@ export class SideStaking { } /** - * Get dt balance in the staking contract available for being added as liquidity + * Get dt balance in the staking contract available for being added as liquidity * @param {String} ssAddress side staking contract address * @param {String} datatokenAddress datatokenAddress * @return {String} @@ -175,7 +175,7 @@ export class SideStaking { } catch (e) { this.logger.error(`ERROR: Failed to get: ${e.message}`) } - result = await this.unitsToAmount(datatokenAddress,result) + result = await this.unitsToAmount(datatokenAddress, result) return result } @@ -200,7 +200,7 @@ export class SideStaking { * Get total amount vesting * @param {String} ssAddress side staking contract address * @param {String} datatokenAddress datatokenAddress - * @return {String} + * @return {String} */ async getvestingAmount(ssAddress: string, datatokenAddress: string): Promise { const sideStaking = new this.web3.eth.Contract(this.ssABI, ssAddress) @@ -210,7 +210,7 @@ export class SideStaking { } catch (e) { this.logger.error(`ERROR: Failed to get: ${e.message}`) } - result = await this.unitsToAmount(datatokenAddress,result) + result = await this.unitsToAmount(datatokenAddress, result) return result } @@ -251,7 +251,7 @@ export class SideStaking { } catch (e) { this.logger.error(`ERROR: Failed to get: ${e.message}`) } - result = await this.unitsToAmount(datatokenAddress,result) + result = await this.unitsToAmount(datatokenAddress, result) return result } @@ -284,7 +284,7 @@ export class SideStaking { return estGas } - /**Send vested tokens available to the publisher address, can be called by anyone + /** Send vested tokens available to the publisher address, can be called by anyone * * @param {String} account * @param {String} ssAddress side staking contract address diff --git a/test/unit/pools/fixedRate/FixedRateExchange.test.ts b/test/unit/pools/fixedRate/FixedRateExchange.test.ts index 5fc03f20..d38597b4 100644 --- a/test/unit/pools/fixedRate/FixedRateExchange.test.ts +++ b/test/unit/pools/fixedRate/FixedRateExchange.test.ts @@ -4,6 +4,7 @@ import { TestContractHandler } from '../../../TestContractHandler' import { Contract } from 'web3-eth-contract' import Web3 from 'web3' import BigNumber from 'bignumber.js' +import BN from 'bn.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 SSContract from '@oceanprotocol/contracts/artifacts/contracts/pools/ssContracts/SideStaking.sol/SideStaking.json' @@ -32,6 +33,8 @@ describe('Fixed Rate unit test', () => { let user3: string let initialBlock: number let fixedRateAddress: string + let daiAddress: string + let usdcAddress: string let exchangeId: string let contracts: TestContractHandler let fixedRate: FixedRateExchange @@ -41,7 +44,7 @@ describe('Fixed Rate unit test', () => { let daiContract: Contract let usdcContract: Contract const vestedBlocks = 2500000 - + const ADDRESS_ZERO = '0x0000000000000000000000000000000000000000' it('should deploy contracts', async () => { contracts = new TestContractHandler( web3, @@ -72,11 +75,11 @@ describe('Fixed Rate unit test', () => { user2 = contracts.accounts[3] user3 = contracts.accounts[4] exchangeOwner = contracts.accounts[0] + await contracts.deployContracts(factoryOwner, FactoryRouter.abi as AbiItem[]) // initialize fixed rate // - daiContract = new web3.eth.Contract( contracts.MockERC20.options.jsonInterface, @@ -87,14 +90,11 @@ describe('Fixed Rate unit test', () => { contracts.MockERC20.options.jsonInterface, contracts.usdcAddress ) - console.log( await usdcContract.methods.decimals().call(), 'USDC DECIMALS IN THIS TEST' ) - - }) describe('Test a Fixed Rate Exchange with DAI (18 Decimals)', () => { @@ -110,21 +110,16 @@ describe('Fixed Rate unit test', () => { const ercData = { templateIndex: 1, strings: ['ERC20B1', 'ERC20DT1Symbol'], - addresses: [ - contracts.accounts[0], - user3, - contracts.accounts[0], - '0x0000000000000000000000000000000000000000' - ], + addresses: [contracts.accounts[0], user3, contracts.accounts[0], ADDRESS_ZERO], uints: [web3.utils.toWei('1000000'), 0], bytess: [] } - //[baseToken,owner,marketFeeCollector,allowedSwapper] + // [baseToken,owner,marketFeeCollector,allowedSwapper] const fixedRateData = { - fixedPriceAddress:contracts.fixedRateAddress, - addresses:[contracts.daiAddress,exchangeOwner,user3, '0x0000000000000000000000000000000000000000'], - uints:[18,18,web3.utils.toWei('1'),1e15,0] + fixedPriceAddress: contracts.fixedRateAddress, + addresses: [contracts.daiAddress, exchangeOwner, user3, ADDRESS_ZERO], + uints: [18, 18, web3.utils.toWei('1'), 1e15, 0] } const nftFactory = new NFTFactory(contracts.factory721Address, web3, LoggerInstance) @@ -135,7 +130,7 @@ describe('Fixed Rate unit test', () => { ercData, fixedRateData ) - + initialBlock = await web3.eth.getBlockNumber() dtAddress = txReceipt.events.TokenCreated.returnValues.newTokenAddress exchangeId = txReceipt.events.NewFixedRate.returnValues.exchangeId @@ -143,49 +138,171 @@ describe('Fixed Rate unit test', () => { dtContract = new web3.eth.Contract(ERC20Template.abi as AbiItem[], dtAddress) // user2 has no dt1 expect(await dtContract.methods.balanceOf(user2).call()).to.equal('0') - + fixedRateAddress = contracts.fixedRateAddress - fixedRate = new FixedRateExchange(web3, LoggerInstance, fixedRateAddress, FixedRate.abi as AbiItem[],contracts.oceanAddress) + fixedRate = new FixedRateExchange( + web3, + LoggerInstance, + fixedRateAddress, + FixedRate.abi as AbiItem[], + contracts.oceanAddress + ) assert(fixedRate != null) }) it('#isActive - should return true if exchange is active', async () => { - expect(await fixedRate.isActive(exchangeId)).to.equal( - true - ) - expect(await fixedRate.isActive('0x00')).to.equal( - false - ) + expect(await fixedRate.isActive(exchangeId)).to.equal(true) + expect(await fixedRate.isActive('0x00')).to.equal(false) + }) + + it('#deactivate - should deactivate an exchange if exchangeOwner', async () => { + expect(await fixedRate.isActive(exchangeId)).to.equal(true) + await fixedRate.deactivate(exchangeOwner, exchangeId) + + expect(await fixedRate.isActive(exchangeId)).to.equal(false) + }) + + it('#activate - should activate an exchange if exchangeOwner', async () => { + expect(await fixedRate.isActive(exchangeId)).to.equal(false) + await fixedRate.activate(exchangeOwner, exchangeId) + expect(await fixedRate.isActive(exchangeId)).to.equal(true) + }) + + it('#activateMint - should activate Mint(allows fixed rate contract to mint dts if required), if exchangeOwner', async () => { + expect((await fixedRate.getExchange(exchangeId)).withMint).to.equal(false) + await fixedRate.activateMint(exchangeOwner, exchangeId) + expect((await fixedRate.getExchange(exchangeId)).withMint).to.equal(true) + }) + + it('#dectivateMint - should deactivate Mint if exchangeOwner', async () => { + expect((await fixedRate.getExchange(exchangeId)).withMint).to.equal(true) + await fixedRate.deactivateMint(exchangeOwner, exchangeId) + expect((await fixedRate.getExchange(exchangeId)).withMint).to.equal(false) + }) + + it('#generate exchangeId - should generate a specific exchangeId', async () => { + expect( + await fixedRate.generateExchangeId(contracts.daiAddress, dtAddress, exchangeOwner) + ).to.equal(exchangeId) + }) + + it('#getNumberOfExchanges - should return total number of exchanges', async () => { + expect(await fixedRate.getNumberOfExchanges()).to.equal('1') + }) + + it('#getExchanges - should return all exchanges ids', async () => { + const exchangeIds = await fixedRate.getExchanges() + expect(exchangeIds[0]).to.equal(exchangeId) + }) + + it('#getRate - should return rate', async () => { + expect(await fixedRate.getRate(exchangeId)).to.equal('1') + }) + + it('#setRate - set new rate if exchangeOwner', async () => { + await fixedRate.setRate(exchangeOwner, exchangeId, '2') + expect(await fixedRate.getRate(exchangeId)).to.equal('2') + await fixedRate.setRate(exchangeOwner, exchangeId, '1') + expect(await fixedRate.getRate(exchangeId)).to.equal('1') + }) + + it('#getDTSupply - should get the dt supply in the exchange', async () => { + expect(await fixedRate.getDTSupply(exchangeId)).to.equal('0') + }) + it('#getBTSupply - should get the bt supply in the exchange', async () => { + expect(await fixedRate.getBTSupply(exchangeId)).to.equal('0') + }) + it('#getBTNeeded - should get bt amount for a specific dt amount', async () => { + console.log(await fixedRate.getAmountBTIn(exchangeId, '100')) + }) + it('#getBTNeeded - should get bt amount for a specific dt amount', async () => { + console.log(await fixedRate.getAmountBTOut(exchangeId, '100')) }) it('#buyDT - user2 should buy some dt', async () => { - await dtContract.methods.mint(exchangeOwner,web3.utils.toWei('1000')).send({from:exchangeOwner}) - await dtContract.methods.approve(fixedRateAddress,web3.utils.toWei('1000')).send({from:exchangeOwner}) - await daiContract.methods.transfer(user2,web3.utils.toWei('100')).send({from:exchangeOwner}) - await daiContract.methods.approve(fixedRateAddress,web3.utils.toWei('100')).send({from:user2}) - const tx = await fixedRate.buyDT(user2,exchangeId,'10','11') - // console.log(tx.events.Swapped.returnValues) + await dtContract.methods + .mint(exchangeOwner, web3.utils.toWei('1000')) + .send({ from: exchangeOwner }) + await dtContract.methods + .approve(fixedRateAddress, web3.utils.toWei('1000')) + .send({ from: exchangeOwner }) + await daiContract.methods + .transfer(user2, web3.utils.toWei('100')) + .send({ from: exchangeOwner }) + await daiContract.methods + .approve(fixedRateAddress, web3.utils.toWei('100')) + .send({ from: user2 }) + expect(await dtContract.methods.balanceOf(user2).call()).to.equal('0') + const daiBalanceBefore = new BN(await daiContract.methods.balanceOf(user2).call()) + expect(daiBalanceBefore.toString()).to.equal(web3.utils.toWei('100')) + const tx = await fixedRate.buyDT(user2, exchangeId, '10', '11') + // console.log(tx.events.Swapped.returnValues) assert(tx.events.Swapped != null) const args = tx.events.Swapped.returnValues expect(args.exchangeId).to.equal(exchangeId) expect(args.by).to.equal(user2) expect(args.dataTokenSwappedAmount).to.equal(web3.utils.toWei('10')) expect(args.tokenOutAddress).to.equal(dtAddress) - + expect(await dtContract.methods.balanceOf(user2).call()).to.equal( + args.dataTokenSwappedAmount + ) + expect( + daiBalanceBefore.sub(new BN(args.baseTokenSwappedAmount)).toString() + ).to.equal(await daiContract.methods.balanceOf(user2).call()) }) it('#sellDT - user2 should sell some dt', async () => { - - await dtContract.methods.approve(fixedRateAddress,web3.utils.toWei('10')).send({from:user2}) - const tx = await fixedRate.sellDT(user2,exchangeId,'10','9') - // console.log(tx.events.Swapped.returnValues) + await dtContract.methods + .approve(fixedRateAddress, web3.utils.toWei('10')) + .send({ from: user2 }) + const daiBalanceBefore = new BN(await daiContract.methods.balanceOf(user2).call()) + const tx = await fixedRate.sellDT(user2, exchangeId, '10', '9') + // console.log(tx.events.Swapped.returnValues) assert(tx.events.Swapped != null) const args = tx.events.Swapped.returnValues expect(args.exchangeId).to.equal(exchangeId) expect(args.by).to.equal(user2) expect(args.dataTokenSwappedAmount).to.equal(web3.utils.toWei('10')) expect(args.tokenOutAddress).to.equal(contracts.daiAddress) + expect(await dtContract.methods.balanceOf(user2).call()).to.equal('0') + expect( + daiBalanceBefore.add(new BN(args.baseTokenSwappedAmount)).toString() + ).to.equal(await daiContract.methods.balanceOf(user2).call()) }) + it('#getExchange - should return exchange details', async () => { + const result = await fixedRate.getExchange(exchangeId) + expect(result.active).to.equal(true) + expect(result.btDecimals).to.equal('18') + expect(result.dtDecimals).to.equal('18') + expect(result.baseToken).to.equal(contracts.daiAddress) + expect(result.dataToken).to.equal(dtAddress) + expect(result.exchangeOwner).to.equal(exchangeOwner) + expect(result.withMint).to.equal(false) + expect(result.dtBalance).to.equal('10') + expect(result.btBalance).to.equal('0') + expect(result.dtSupply).to.equal('1000') + expect(result.btSupply).to.equal('0') + expect(result.fixedRate).to.equal('1') + }) + + it('#getFeesInfo - should return exchange fee details', async () => { + const result = await fixedRate.getFeesInfo(exchangeId) + expect(result.marketFee).to.equal('0.001') + // we made 2 swaps for 10 DT at rate 1, the fee is 0.1% for market and always in basetoken so it's 0.01 DAI + // we made 2 swaps for 10 DT at rate 1, the fee is 0.1% for ocean community and always in basetoken so it's 0.01 DAI + expect(result.marketFeeAvailable).to.equal('0.02') // formatted for basetoken decimals + expect(result.oceanFeeAvailable).to.equal('0.02') // formatted for basetoken decimals + expect(result.marketFeeCollector).to.equal(user3) + expect(result.opfFee).to.equal('0.001') + }) + + it('#getAllowedSwapper- should return address(0) if not set, if exchangeOwner', async () => { + expect(await fixedRate.getAllowedSwapper(exchangeId)).to.equal(ADDRESS_ZERO) + }) + it('#setAllowedSwapper- should return address(0) if not set, if exchangeOwner', async () => { + await fixedRate.setAllowedSwapper(exchangeOwner, exchangeId, user2) + expect(await fixedRate.getAllowedSwapper(exchangeId)).to.equal(user2) + }) }) }) diff --git a/test/unit/pools/ssContracts/SideStaking.test.ts b/test/unit/pools/ssContracts/SideStaking.test.ts index 16db55a2..5f27e8dc 100644 --- a/test/unit/pools/ssContracts/SideStaking.test.ts +++ b/test/unit/pools/ssContracts/SideStaking.test.ts @@ -260,15 +260,17 @@ describe('SideStaking unit test', () => { sideStakingAddress, erc20Token ) - + expect( - await sideStaking.unitsToAmount(erc20Token,await erc20Contract.methods.balanceOf(contracts.accounts[0]).call() ) + await sideStaking.unitsToAmount( + erc20Token, + await erc20Contract.methods.balanceOf(contracts.accounts[0]).call() + ) ).to.equal(await sideStaking.getvestingAmountSoFar(sideStakingAddress, erc20Token)) - + expect( await sideStaking.getvestingLastBlock(sideStakingAddress, erc20Token) ).to.equal((await web3.eth.getBlockNumber()).toString()) - }) it('#swapExactAmountIn - should swap', async () => {