1
0
mirror of https://github.com/oceanprotocol/ocean.js.git synced 2024-11-26 20:39:05 +01:00

Merge pull request #348 from oceanprotocol/feature/pool_validations

add pool validation and helpers
This commit is contained in:
Alex Coseru 2020-10-13 15:11:32 +03:00 committed by GitHub
commit 5c2cef618e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 863 additions and 114 deletions

View File

@ -21,9 +21,14 @@ before_script:
- cd barge
- git checkout v3
- export ADDRESS_FILE="${HOME}/.ocean/ocean-contracts/artifacts/address.json"
- mkdir "${HOME}/.ocean/"
- mkdir "${HOME}/.ocean/ocean-contracts/"
- mkdir "${HOME}/.ocean/ocean-contracts/artifacts"
- touch $ADDRESS_FILE
- echo "{}" >> $ADDRESS_FILE
- export AQUARIUS_URI="http://172.15.0.5:5000"
- export DEPLOY_CONTRACTS=true
- export CONTRACTS_VERSION=v0.5.3
- export DEPLOY_CONTRACTS="true"
- export CONTRACTS_VERSION=v0.5.5
- bash -x start_ocean.sh --no-dashboard 2>&1 > start_ocean.log &
- cd ..
- ./scripts/waitforcontracts.sh

6
package-lock.json generated
View File

@ -939,9 +939,9 @@
}
},
"@oceanprotocol/contracts": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-0.5.3.tgz",
"integrity": "sha512-gJ8qQACJgxOPIrPE0OFQ09iYXBAisOGg56EmelQlsMUgp0yY0DKgBntDP83S/Ho1yBjGygqfxCjQrPH63hh/PA=="
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/@oceanprotocol/contracts/-/contracts-0.5.5.tgz",
"integrity": "sha512-Omwlh3KxPm2JOuLd6DW4teAQhGaIv0fRTopCvctey0XGsf3DcbJpwS0A0YfgLQnvCyyVMKsiq90YCqpJ3SO/cw=="
},
"@octokit/auth-token": {
"version": "2.4.2",

View File

@ -42,7 +42,7 @@
},
"dependencies": {
"@ethereum-navigator/navigator": "^0.5.0",
"@oceanprotocol/contracts": "^0.5.3",
"@oceanprotocol/contracts": "^0.5.5",
"decimal.js": "^10.2.0",
"fs": "0.0.1-security",
"lzma": "^2.3.2",

View File

@ -3,3 +3,7 @@ if [ "${DEPLOY_CONTRACTS}" = "true" ]; then
sleep 2
done
fi
cat "barge/start_ocean.log"
ls -lh "${HOME}/.ocean/ocean-contracts/"
ls -lh "${HOME}/.ocean/ocean-contracts/artifacts/"
cat "${HOME}/.ocean/ocean-contracts/artifacts/address.json"

View File

@ -3,6 +3,8 @@ import { AbiItem } from 'web3-utils/types'
import { TransactionReceipt } from 'web3-core'
import { Pool } from './Pool'
import { EventData, Filter } from 'web3-eth-contract'
import BigNumber from 'bignumber.js'
import Decimal from 'decimal.js'
declare type PoolTransactionType = 'swap' | 'join' | 'exit'
@ -63,31 +65,43 @@ export class OceanPool extends Pool {
fee: string
): Promise<string> {
if (this.oceanAddress == null) {
console.error('oceanAddress is not defined')
console.error('ERROR: oceanAddress is not defined')
return null
}
if (parseFloat(fee) > 0.1) {
console.error('Swap fee too high. The maximum allowed swapFee is 0.1 (10%).')
console.error('ERROR: Swap fee too high. The maximum allowed swapFee is 0.1 (10%).')
return null
}
if (parseFloat(weight) > 9 || parseFloat(weight) < 1) {
console.error('Weight out of bounds (min 1, max9)')
console.error('ERROR: Weight out of bounds (min 1, max9)')
return null
}
const address = await super.createPool(account)
const oceanWeight = 10 - parseFloat(weight)
const oceanAmount = (parseFloat(amount) * oceanWeight) / parseFloat(weight)
this.dtAddress = token
await this.approve(account, token, address, this.web3.utils.toWei(String(amount)))
await this.approve(
let txid
txid = await this.approve(
account,
token,
address,
this.web3.utils.toWei(String(amount))
)
if (!txid) {
console.error('ERROR: Failed to call approve DT token')
return null
}
txid = await this.approve(
account,
this.oceanAddress,
address,
this.web3.utils.toWei(String(oceanAmount))
)
await super.setup(
if (!txid) {
console.error('ERROR: Failed to call approve OCEAN token')
return null
}
txid = await super.setup(
account,
address,
token,
@ -98,7 +112,10 @@ export class OceanPool extends Pool {
this.web3.utils.toWei(String(oceanWeight)),
this.web3.utils.toWei(fee)
)
if (!txid) {
console.error('ERROR: Failed to create a new pool')
return null
}
return address
}
@ -127,14 +144,14 @@ export class OceanPool extends Pool {
*/
public async getOceanReserve(poolAddress: string): Promise<string> {
if (this.oceanAddress == null) {
console.error('oceanAddress is not defined')
console.error('ERROR: oceanAddress is not defined')
return null
}
return super.getReserve(poolAddress, this.oceanAddress)
}
/**
* Get Data Token balance of a pool
* Get datatoken balance of a pool
* @param {String} poolAddress
* @return {String}
*/
@ -144,10 +161,309 @@ export class OceanPool extends Pool {
}
/**
* Buy Data Token from a pool
* Returns max amount that you can buy.
* @param poolAddress
* @param tokenAddress
*/
public async getMaxBuyQuantity(
poolAddress: string,
tokenAddress: string
): Promise<string> {
const balance = await super.getReserve(poolAddress, tokenAddress)
return String(parseFloat(balance) / 3)
}
/**
* Returns max amount of OCEAN that you can buy.
* @param poolAddress
* @param tokenAddress
*/
public async getOceanMaxBuyQuantity(poolAddress: string): Promise<string> {
return this.getMaxBuyQuantity(poolAddress, this.oceanAddress)
}
/**
* Returns max amount of DT that you can buy.
* @param poolAddress
* @param tokenAddress
*/
public async getDTMaxBuyQuantity(poolAddress: string): Promise<string> {
return this.getMaxBuyQuantity(poolAddress, await this.getDTAddress(poolAddress))
}
/**
* Returns tokenInAmount required to get tokenOutAmount
* @param poolAddress
* @param tokenInAddress
* @param tokenOutAddress
* @param tokenOutAmount
*/
public async calcInGivenOut(
poolAddress: string,
tokenInAddress: string,
tokenOutAddress: string,
tokenOutAmount: string
): Promise<string> {
const result = await super.calcInGivenOut(
poolAddress,
await super.getReserve(poolAddress, tokenInAddress),
await super.getDenormalizedWeight(poolAddress, tokenInAddress),
await super.getReserve(poolAddress, tokenOutAddress),
await super.getDenormalizedWeight(poolAddress, tokenOutAddress),
tokenOutAmount,
await this.getSwapFee(poolAddress)
)
return result
}
/**
* Returns tokenOutAmount given tokenInAmount
* @param poolAddress
* @param tokenInAddress
* @param tokenOutAddress
* @param tokenInAmount
*/
public async calcOutGivenIn(
poolAddress: string,
tokenInAddress: string,
tokenOutAddress: string,
tokenInAmount: string
): Promise<string> {
const result = await super.calcOutGivenIn(
poolAddress,
await super.getReserve(poolAddress, tokenInAddress),
await super.getDenormalizedWeight(poolAddress, tokenInAddress),
await super.getReserve(poolAddress, tokenOutAddress),
await super.getDenormalizedWeight(poolAddress, tokenOutAddress),
tokenInAmount,
await super.getSwapFee(poolAddress)
)
return result
}
/**
* Returns no of shares receved for adding a token to the pool
* @param poolAddress
* @param tokenInAddress
* @param tokenInAmount
*/
public async calcPoolOutGivenSingleIn(
poolAddress: string,
tokenInAddress: string,
tokenInAmount: string
): Promise<string> {
const result = super.calcPoolOutGivenSingleIn(
poolAddress,
await super.getReserve(poolAddress, tokenInAddress),
await super.getDenormalizedWeight(poolAddress, tokenInAddress),
await super.getPoolSharesTotalSupply(poolAddress),
await super.getTotalDenormalizedWeight(poolAddress),
tokenInAmount,
await super.getSwapFee(poolAddress)
)
return result
}
/**
* Returns no of tokens required to get a specific no of poolShares
* @param poolAddress
* @param tokenInAddress
* @param poolShares
*/
public async calcSingleInGivenPoolOut(
poolAddress: string,
tokenInAddress: string,
poolShares: string
): Promise<string> {
const result = super.calcSingleInGivenPoolOut(
poolAddress,
await super.getReserve(poolAddress, tokenInAddress),
await super.getDenormalizedWeight(poolAddress, tokenInAddress),
await super.getPoolSharesTotalSupply(poolAddress),
await super.getTotalDenormalizedWeight(poolAddress),
poolShares,
await super.getSwapFee(poolAddress)
)
return result
}
/**
* Returns no of tokens received for spending a specific no of poolShares
* @param poolAddress
* @param tokenOutAddress
* @param poolShares
*/
public async calcSingleOutGivenPoolIn(
poolAddress: string,
tokenOutAddress: string,
poolShares: string
): Promise<string> {
const result = super.calcSingleOutGivenPoolIn(
poolAddress,
await super.getReserve(poolAddress, tokenOutAddress),
await super.getDenormalizedWeight(poolAddress, tokenOutAddress),
await super.getPoolSharesTotalSupply(poolAddress),
await super.getTotalDenormalizedWeight(poolAddress),
poolShares,
await super.getSwapFee(poolAddress)
)
return result
}
/**
* Returns no of pool shares required to receive a specified amount of tokens
* @param poolAddress
* @param tokenOutAddress
* @param tokenOutAmount
*/
public async calcPoolInGivenSingleOut(
poolAddress: string,
tokenOutAddress: string,
tokenOutAmount: string
): Promise<string> {
const result = super.calcPoolInGivenSingleOut(
poolAddress,
await super.getReserve(poolAddress, tokenOutAddress),
await super.getDenormalizedWeight(poolAddress, tokenOutAddress),
await super.getPoolSharesTotalSupply(poolAddress),
await super.getTotalDenormalizedWeight(poolAddress),
tokenOutAmount,
await super.getSwapFee(poolAddress)
)
return result
}
/**
* Returns no of pool shares required to receive specified amount of DT
* @param poolAddress
* @param dtAmount
*/
public async getPoolSharesRequiredToRemoveDT(
poolAddress: string,
dtAmount: string
): Promise<string> {
const dtAddress = await this.getDTAddress(poolAddress)
return this.calcPoolInGivenSingleOut(poolAddress, dtAddress, dtAmount)
}
/**
* Returns DT amnount received after spending poolShares
* @param poolAddress
* @param poolShares
*/
public async getDTRemovedforPoolShares(
poolAddress: string,
poolShares: string
): Promise<string> {
const dtAddress = await this.getDTAddress(poolAddress)
return this.calcSingleOutGivenPoolIn(poolAddress, dtAddress, poolShares)
}
/**
* Returns no of pool shares required to receive specified amount of DT
* @param poolAddress
* @param dtAmount
*/
public async getPoolSharesRequiredToRemoveOcean(
poolAddress: string,
oceanAmount: string
): Promise<string> {
return this.calcPoolInGivenSingleOut(poolAddress, this.oceanAddress, oceanAmount)
}
/**
* Returns Ocean amnount received after spending poolShares
* @param poolAddress
* @param poolShares
*/
public async getOceanRemovedforPoolShares(
poolAddress: string,
poolShares: string
): Promise<string> {
return this.calcSingleOutGivenPoolIn(poolAddress, this.oceanAddress, poolShares)
}
/**
* Returns max DT amount that you can add to the pool
* @param poolAddress
*/
public async getDTMaxAddLiquidity(poolAddress: string): Promise<string> {
const dtAddress = await this.getDTAddress(poolAddress)
return this.getMaxAddLiquidity(poolAddress, dtAddress)
}
/**
* Returns max Ocean amount that you can add to the pool
* @param poolAddress
*/
public async getOceanMaxAddLiquidity(poolAddress: string): Promise<string> {
return this.getMaxAddLiquidity(poolAddress, this.oceanAddress)
}
/**
* Returns max amount of tokens that you can add to the pool
* @param poolAddress
* @param tokenAddress
*/
public async getMaxAddLiquidity(
poolAddress: string,
tokenAddress: string
): Promise<string> {
const balance = await super.getReserve(poolAddress, tokenAddress)
if (parseFloat(balance) > 0) {
const result = new BigNumber(this.web3.utils.toWei(balance))
.dividedBy(3)
.integerValue(BigNumber.ROUND_DOWN)
.minus(1)
return this.web3.utils.fromWei(result.toString())
} else return '0'
}
/**
* Returns max amount of tokens that you can withdraw from the pool
* @param poolAddress
* @param tokenAddress
*/
public async getMaxRemoveLiquidity(
poolAddress: string,
tokenAddress: string
): Promise<string> {
const balance = await super.getReserve(poolAddress, tokenAddress)
if (parseFloat(balance) > 0) {
const result = new BigNumber(this.web3.utils.toWei(balance))
.dividedBy(4)
.integerValue(BigNumber.ROUND_DOWN)
.minus(1)
return this.web3.utils.fromWei(result.toString())
} else return '0'
}
/**
* Returns max amount of DT that you can withdraw from the pool
* @param poolAddress
* @param tokenAddress
*/
public async getDTMaxRemoveLiquidity(poolAddress: string): Promise<string> {
const dtAddress = await this.getDTAddress(poolAddress)
return this.getMaxRemoveLiquidity(poolAddress, dtAddress)
}
/**
* Returns max amount of Ocean that you can withdraw from the pool
* @param poolAddress
* @param tokenAddress
*/
public async getOceanMaxRemoveLiquidity(poolAddress: string): Promise<string> {
return this.getMaxRemoveLiquidity(poolAddress, this.oceanAddress)
}
/**
* Buy datatoken from a pool
* @param {String} account
* @param {String} poolAddress
* @param {String} amount Data Token amount
* @param {String} amount datatoken amount
* @param {String} oceanAmount Ocean Token amount payed
* @param {String} maxPrice Maximum price to pay
* @return {TransactionReceipt}
@ -155,40 +471,54 @@ export class OceanPool extends Pool {
public async buyDT(
account: string,
poolAddress: string,
amount: string,
oceanAmount: string,
maxPrice: string
dtAmountWanted: string,
maxOceanAmount: string,
maxPrice?: string
): Promise<TransactionReceipt> {
if (this.oceanAddress == null) {
console.error('oceanAddress is not defined')
console.error('ERROR: undefined ocean token contract address')
return null
}
const dtAddress = await this.getDTAddress(poolAddress)
if (
parseFloat(dtAmountWanted) > parseFloat(await this.getDTMaxBuyQuantity(poolAddress))
) {
console.error('ERROR: Buy quantity exceeds quantity allowed')
return null
}
const calcInGivenOut = await this.getOceanNeeded(poolAddress, dtAmountWanted)
if (parseFloat(calcInGivenOut) > parseFloat(maxOceanAmount)) {
console.error('ERROR: Not enough Ocean Tokens')
return null
}
// TODO - check balances first
await super.approve(
const txid = await super.approve(
account,
this.oceanAddress,
poolAddress,
this.web3.utils.toWei(oceanAmount)
this.web3.utils.toWei(maxOceanAmount)
)
if (!txid) {
console.error('ERROR: OCEAN approve failed')
return null
}
return this.swapExactAmountOut(
account,
poolAddress,
this.oceanAddress,
oceanAmount,
maxOceanAmount,
dtAddress,
amount,
dtAmountWanted,
maxPrice
)
}
/**
* Sell Data Token
* Sell datatoken
* @param {String} account
* @param {String} poolAddress
* @param {String} amount Data Token amount
* @param {String} amount datatoken amount to be sold
* @param {String} oceanAmount Ocean Token amount expected
* @param {String} maxPrice Minimum price to sell
* @return {TransactionReceipt}
@ -196,31 +526,54 @@ export class OceanPool extends Pool {
public async sellDT(
account: string,
poolAddress: string,
amount: string,
oceanAmount: string,
minPrice: string
dtAmount: string,
oceanAmountWanted: string,
maxPrice?: string
): Promise<TransactionReceipt> {
if (this.oceanAddress == null) {
console.error('oceanAddress is not defined')
console.error('ERROR: oceanAddress is not defined')
return null
}
const dtAddress = await this.getDTAddress(poolAddress)
return this.swapExactAmountOut(
if (
parseFloat(oceanAmountWanted) >
parseFloat(await this.getOceanMaxBuyQuantity(poolAddress))
) {
console.error('ERROR: Buy quantity exceeds quantity allowed')
return null
}
const calcOutGivenIn = await this.getOceanReceived(poolAddress, dtAmount)
if (parseFloat(calcOutGivenIn) < parseFloat(oceanAmountWanted)) {
console.error('ERROR: Not enough datatokens')
return null
}
const txid = await super.approve(
account,
dtAddress,
poolAddress,
this.web3.utils.toWei(dtAmount)
)
if (!txid) {
console.error('ERROR: DT approve failed')
return null
}
return this.swapExactAmountIn(
account,
poolAddress,
dtAddress,
amount,
dtAmount,
this.oceanAddress,
oceanAmount,
minPrice
oceanAmountWanted,
maxPrice
)
}
/**
* Add Data Token amount to pool liquidity
* Add datatoken amount to pool liquidity
* @param {String} account
* @param {String} poolAddress
* @param {String} amount Data Token amount
* @param {String} amount datatoken amount
* @return {TransactionReceipt}
*/
public async addDTLiquidity(
@ -229,7 +582,21 @@ export class OceanPool extends Pool {
amount: string
): Promise<TransactionReceipt> {
const dtAddress = await this.getDTAddress(poolAddress)
await super.approve(account, dtAddress, poolAddress, this.web3.utils.toWei(amount))
const maxAmount = await this.getMaxAddLiquidity(poolAddress, dtAddress)
if (parseFloat(amount) > parseFloat(maxAmount)) {
console.error('ERROR: Too much reserve to add')
return null
}
const txid = await super.approve(
account,
dtAddress,
poolAddress,
this.web3.utils.toWei(amount)
)
if (!txid) {
console.error('ERROR: DT approve failed')
return null
}
const result = await super.joinswapExternAmountIn(
account,
poolAddress,
@ -241,10 +608,10 @@ export class OceanPool extends Pool {
}
/**
* Remove Data Token amount from pool liquidity
* Remove datatoken amount from pool liquidity
* @param {String} account
* @param {String} poolAddress
* @param {String} amount Data Token amount
* @param {String} amount datatoken amount
* @return {TransactionReceipt}
*/
public async removeDTLiquidity(
@ -254,7 +621,23 @@ export class OceanPool extends Pool {
maximumPoolShares: string
): Promise<TransactionReceipt> {
const dtAddress = await this.getDTAddress(poolAddress)
// TODO Check balance of PoolShares before doing exit
const maxAmount = await this.getDTMaxRemoveLiquidity(poolAddress)
if (parseFloat(amount) > parseFloat(maxAmount)) {
console.error('ERROR: Too much reserve to remove')
return null
}
const usershares = await this.sharesBalance(account, poolAddress)
if (parseFloat(usershares) < parseFloat(maximumPoolShares)) {
console.error('ERROR: Not enough poolShares')
return null
}
if (
parseFloat(maximumPoolShares) <
parseFloat(await this.getPoolSharesRequiredToRemoveDT(poolAddress, amount))
) {
console.error('ERROR: Not enough poolShares')
return null
}
return this.exitswapExternAmountOut(
account,
poolAddress,
@ -277,15 +660,24 @@ export class OceanPool extends Pool {
amount: string
): Promise<TransactionReceipt> {
if (this.oceanAddress == null) {
console.error('oceanAddress is not defined')
console.error('ERROR: oceanAddress is not defined')
return null
}
await super.approve(
const maxAmount = await this.getOceanMaxAddLiquidity(poolAddress)
if (parseFloat(amount) > parseFloat(maxAmount)) {
console.error('ERROR: Too much reserve to add')
return null
}
const txid = await super.approve(
account,
this.oceanAddress,
poolAddress,
this.web3.utils.toWei(amount)
)
if (!txid) {
console.error('ERROR: OCEAN approve failed')
return null
}
const result = await super.joinswapExternAmountIn(
account,
poolAddress,
@ -303,17 +695,33 @@ export class OceanPool extends Pool {
* @param {String} amount Ocean Token amount in OCEAN
* @return {TransactionReceipt}
*/
public removeOceanLiquidity(
public async removeOceanLiquidity(
account: string,
poolAddress: string,
amount: string,
maximumPoolShares: string
): Promise<TransactionReceipt> {
if (this.oceanAddress == null) {
console.error('oceanAddress is not defined')
console.error('ERROR: oceanAddress is not defined')
return null
}
const maxAmount = await this.getOceanMaxRemoveLiquidity(poolAddress)
if (parseFloat(amount) > parseFloat(maxAmount)) {
console.error('ERROR: Too much reserve to remove')
return null
}
const usershares = await this.sharesBalance(account, poolAddress)
if (parseFloat(usershares) < parseFloat(maximumPoolShares)) {
console.error('ERROR: Not enough poolShares')
return null
}
if (
parseFloat(maximumPoolShares) <
parseFloat(await this.getPoolSharesRequiredToRemoveOcean(poolAddress, amount))
) {
console.error('ERROR: Not enough poolShares')
return null
}
// TODO Check balance of PoolShares before doing exit
return super.exitswapExternAmountOut(
account,
poolAddress,
@ -324,20 +732,45 @@ export class OceanPool extends Pool {
}
/**
* Get Data Token price from pool
* Remove pool liquidity
* @param {String} account
* @param {String} poolAddress
* @param {String} poolShares
* @param {String} minDT Minimum DT expected (defaults 0)
* @param {String} poolShares Minim Ocean expected (defaults 0)
* @return {TransactionReceipt}
*/
public async removePoolLiquidity(
account: string,
poolAddress: string,
poolShares: string,
minDT = '0',
minOcean = '0'
): Promise<TransactionReceipt> {
const usershares = await this.sharesBalance(account, poolAddress)
if (parseFloat(usershares) < parseFloat(poolShares)) {
console.error('ERROR: Not enough poolShares')
return null
}
return this.exitPool(account, poolAddress, poolShares, [minDT, minOcean])
}
/**
* Get datatoken price from pool
* @param {String} poolAddress
* @return {String}
*/
public async getDTPrice(poolAddress: string): Promise<string> {
if (this.oceanAddress == null) {
console.error('oceanAddress is not defined')
console.error('ERROR: oceanAddress is not defined')
return null
}
return this.getOceanNeeded(poolAddress, '1')
}
/**
* Search all pools that have Data Token in their composition
* Search all pools that have datatoken in their composition
* @param {String} dtAddress
* @return {String[]}
*/
@ -358,19 +791,17 @@ export class OceanPool extends Pool {
public async getOceanNeeded(poolAddress: string, dtRequired: string): Promise<string> {
const dtAddress = await this.getDTAddress(poolAddress)
const tokenBalanceIn = await this.getReserve(poolAddress, this.oceanAddress)
const tokenWeightIn = await this.getDenormalizedWeight(poolAddress, this.oceanAddress)
const tokenBalanceOut = await this.getReserve(poolAddress, dtAddress)
const tokenWeightOut = await this.getDenormalizedWeight(poolAddress, dtAddress)
const swapFee = await this.getSwapFee(poolAddress)
return super.calcInGivenOut(
tokenBalanceIn,
tokenWeightIn,
tokenBalanceOut,
tokenWeightOut,
dtRequired,
swapFee
)
return this.calcInGivenOut(poolAddress, this.oceanAddress, dtAddress, dtRequired)
}
public async getOceanReceived(poolAddress: string, dtSold: string): Promise<string> {
const dtAddress = await this.getDTAddress(poolAddress)
return this.calcOutGivenIn(poolAddress, dtAddress, this.oceanAddress, dtSold)
}
public async getDTNeeded(poolAddress: string, OceanRequired: string): Promise<string> {
const dtAddress = await this.getDTAddress(poolAddress)
return this.calcInGivenOut(poolAddress, dtAddress, this.oceanAddress, OceanRequired)
}
/**

View File

@ -2,9 +2,13 @@ import Web3 from 'web3'
import { AbiItem } from 'web3-utils/types'
import { TransactionReceipt } from 'web3-core'
import Decimal from 'decimal.js'
import BigNumber from 'bignumber.js'
import jsonpoolABI from '@oceanprotocol/contracts/artifacts/BPool.json'
import { PoolFactory } from './PoolFactory'
const MaxUint256: BigNumber = new BigNumber(
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
)
/**
* Provides an interface to Balancer BPool & BFactory
*/
@ -270,6 +274,23 @@ export class Pool extends PoolFactory {
return result
}
/**
* Get total supply of pool shares
* @param {String} poolAddress
* @return {String}
*/
async getPoolSharesTotalSupply(poolAddress: string): Promise<string> {
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress)
let amount = null
try {
const result = await pool.methods.totalSupply().call()
amount = this.web3.utils.fromWei(result)
} catch (e) {
console.error(e)
}
return amount
}
/**
* Get tokens composing this pool
* @param {String} poolAddress
@ -483,7 +504,7 @@ export class Pool extends PoolFactory {
tokenAmountIn: string,
tokenOut: string,
minAmountOut: string,
maxPrice: string
maxPrice?: string
): Promise<TransactionReceipt> {
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress, {
from: account
@ -496,7 +517,7 @@ export class Pool extends PoolFactory {
this.web3.utils.toWei(tokenAmountIn),
tokenOut,
this.web3.utils.toWei(minAmountOut),
this.web3.utils.toWei(maxPrice)
maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256
)
.send({ from: account, gas: this.GASLIMIT_DEFAULT })
} catch (e) {
@ -523,7 +544,7 @@ export class Pool extends PoolFactory {
maxAmountIn: string,
tokenOut: string,
minAmountOut: string,
maxPrice: string
maxPrice?: string
): Promise<TransactionReceipt> {
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress, {
from: account
@ -536,7 +557,7 @@ export class Pool extends PoolFactory {
this.web3.utils.toWei(maxAmountIn),
tokenOut,
this.web3.utils.toWei(minAmountOut),
this.web3.utils.toWei(maxPrice)
maxPrice ? this.web3.utils.toWei(maxPrice) : MaxUint256
)
.send({ from: account, gas: this.GASLIMIT_DEFAULT })
} catch (e) {
@ -594,7 +615,7 @@ export class Pool extends PoolFactory {
account: string,
poolAddress: string,
poolAmountIn: string,
minAmountsOut: string
minAmountsOut: string[]
): Promise<TransactionReceipt> {
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress, {
from: account
@ -799,6 +820,7 @@ export class Pool extends PoolFactory {
}
public async calcInGivenOut(
poolAddress: string,
tokenBalanceIn: string,
tokenWeightIn: string,
tokenBalanceOut: string,
@ -806,14 +828,168 @@ export class Pool extends PoolFactory {
tokenAmountOut: string,
swapFee: string
): Promise<string> {
const weightRatio = new Decimal(tokenWeightOut).div(new Decimal(tokenWeightIn))
const diff = new Decimal(tokenBalanceOut).minus(tokenAmountOut)
const y = new Decimal(tokenBalanceOut).div(diff)
const foo = y.pow(weightRatio).minus(new Decimal(1))
const tokenAmountIn = new Decimal(tokenBalanceIn)
.times(foo)
.div(new Decimal(1).minus(new Decimal(swapFee)))
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress)
let amount = null
try {
const result = await pool.methods
.calcInGivenOut(
this.web3.utils.toWei(tokenBalanceIn),
this.web3.utils.toWei(tokenWeightIn),
this.web3.utils.toWei(tokenBalanceOut),
this.web3.utils.toWei(tokenWeightOut),
this.web3.utils.toWei(tokenAmountOut),
this.web3.utils.toWei(swapFee)
)
.call()
amount = this.web3.utils.fromWei(result)
} catch (e) {
console.error(e)
}
return amount
}
return tokenAmountIn.toString()
public async calcOutGivenIn(
poolAddress: string,
tokenBalanceIn: string,
tokenWeightIn: string,
tokenBalanceOut: string,
tokenWeightOut: string,
tokenAmountIn: string,
swapFee: string
): Promise<string> {
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress)
let amount = null
try {
const result = await pool.methods
.calcOutGivenIn(
this.web3.utils.toWei(tokenBalanceIn),
this.web3.utils.toWei(tokenWeightIn),
this.web3.utils.toWei(tokenBalanceOut),
this.web3.utils.toWei(tokenWeightOut),
this.web3.utils.toWei(tokenAmountIn),
this.web3.utils.toWei(swapFee)
)
.call()
amount = this.web3.utils.fromWei(result)
} catch (e) {
console.error(e)
}
return amount
}
public async calcPoolOutGivenSingleIn(
poolAddress: string,
tokenBalanceIn: string,
tokenWeightIn: string,
poolSupply: string,
totalWeight: string,
tokenAmountIn: string,
swapFee: string
): Promise<string> {
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress)
let amount = null
try {
const result = await pool.methods
.calcPoolOutGivenSingleIn(
this.web3.utils.toWei(tokenBalanceIn),
this.web3.utils.toWei(tokenWeightIn),
this.web3.utils.toWei(poolSupply),
this.web3.utils.toWei(totalWeight),
this.web3.utils.toWei(tokenAmountIn),
this.web3.utils.toWei(swapFee)
)
.call()
amount = this.web3.utils.fromWei(result)
} catch (e) {
console.error(e)
}
return amount
}
public async calcSingleInGivenPoolOut(
poolAddress: string,
tokenBalanceIn: string,
tokenWeightIn: string,
poolSupply: string,
totalWeight: string,
poolAmountOut: string,
swapFee: string
): Promise<string> {
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress)
let amount = null
try {
const result = await pool.methods
.calcSingleInGivenPoolOut(
this.web3.utils.toWei(tokenBalanceIn),
this.web3.utils.toWei(tokenWeightIn),
this.web3.utils.toWei(poolSupply),
this.web3.utils.toWei(totalWeight),
this.web3.utils.toWei(poolAmountOut),
this.web3.utils.toWei(swapFee)
)
.call()
amount = this.web3.utils.fromWei(result)
} catch (e) {
console.error(e)
}
return amount
}
public async calcSingleOutGivenPoolIn(
poolAddress: string,
tokenBalanceOut: string,
tokenWeightOut: string,
poolSupply: string,
totalWeight: string,
poolAmountIn: string,
swapFee: string
): Promise<string> {
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress)
let amount = null
try {
const result = await pool.methods
.calcSingleOutGivenPoolIn(
this.web3.utils.toWei(tokenBalanceOut),
this.web3.utils.toWei(tokenWeightOut),
this.web3.utils.toWei(poolSupply),
this.web3.utils.toWei(totalWeight),
this.web3.utils.toWei(poolAmountIn),
this.web3.utils.toWei(swapFee)
)
.call()
amount = this.web3.utils.fromWei(result)
} catch (e) {
console.error(e)
}
return amount
}
public async calcPoolInGivenSingleOut(
poolAddress: string,
tokenBalanceOut: string,
tokenWeightOut: string,
poolSupply: string,
totalWeight: string,
tokenAmountOut: string,
swapFee: string
): Promise<string> {
const pool = new this.web3.eth.Contract(this.poolABI, poolAddress)
let amount = null
try {
const result = await pool.methods
.calcPoolInGivenSingleOut(
this.web3.utils.toWei(tokenBalanceOut),
this.web3.utils.toWei(tokenWeightOut),
this.web3.utils.toWei(poolSupply),
this.web3.utils.toWei(totalWeight),
this.web3.utils.toWei(tokenAmountOut),
this.web3.utils.toWei(swapFee)
)
.call()
amount = this.web3.utils.fromWei(result)
} catch (e) {
console.error(e)
}
return amount
}
}

View File

@ -417,4 +417,13 @@ export class DataTokens {
}
return null
}
public getStartOrderEventSignature(): string {
const abi = this.datatokensABI as AbiItem[]
const eventdata = abi.find(function (o) {
if (o.name === 'OrderStarted' && o.type === 'event') return o
})
const topic = this.web3.eth.abi.encodeEventSignature(eventdata as any)
return topic
}
}

View File

@ -612,15 +612,12 @@ export class Assets extends Instantiable {
): Promise<Order[]> {
const results: Order[] = []
const address = account.getId().toLowerCase()
const { datatokens } = this.ocean
const topic1 = '0x000000000000000000000000' + address.substring(2)
const events = await this.web3.eth.getPastLogs({
topics: [
[
'0xe1c4fa794edfa8f619b8257a077398950357b9c6398528f94480307352f9afcc',
null,
'0x000000000000000000000000' + address.substring(address.length - 40)
]
],
fromBlock: fromBlock || 0
topics: [[datatokens.getStartOrderEventSignature(), null, topic1]],
fromBlock: fromBlock || 0,
toBlock: 'latest'
})
for (let i = 0; i < events.length; i++) {
const order: Order = {
@ -631,18 +628,20 @@ export class Assets extends Instantiable {
consumer: '0x' + events[i].topics[1].substring(events[i].topics[1].length - 40),
payer: '0x' + events[i].topics[2].substring(events[i].topics[2].length - 40)
}
const params = this.web3.eth.abi.decodeParameters(
['uint256', 'uint256', 'uint256', 'uint256'],
events[i].data
)
order.serviceId = parseInt(params[1])
order.timestamp = parseInt(params[2])
order.amount = this.web3.utils.fromWei(params[0])
order.did = didPrefixed(didNoZeroX(order.dtAddress))
const service = await this.getServiceByIndex(order.did, order.serviceId)
order.serviceType = service.type
if (!serviceType || (serviceType && serviceType === service.type))
results.push(order)
try {
const params = this.web3.eth.abi.decodeParameters(
['uint256', 'uint256', 'uint256', 'uint256'],
events[i].data
)
order.serviceId = parseInt(params[1])
order.timestamp = parseInt(params[2])
order.amount = this.web3.utils.fromWei(params[0])
order.did = didPrefixed(didNoZeroX(order.dtAddress))
const service = await this.getServiceByIndex(order.did, order.serviceId)
order.serviceType = service.type
if (!serviceType || (serviceType && serviceType === service.type))
results.push(order)
} catch (e) {}
}
return results
}

View File

@ -68,7 +68,7 @@ const configs: ConfigHelperConfig[] = [
export class ConfigHelper {
/* Load contract addresses from env ADDRESS_FILE (generated by ocean-contracts) */
public getAddressesFromEnv(): Partial<ConfigHelperConfig> {
public getAddressesFromEnv(network: string): Partial<ConfigHelperConfig> {
try {
const data = JSON.parse(
fs.readFileSync(
@ -78,13 +78,14 @@ export class ConfigHelper {
)
)
const { DTFactory, BFactory, FixedRateExchange, Metadata } = data?.ganache
const { DTFactory, BFactory, FixedRateExchange, Metadata, Ocean } = data[network]
const configAddresses: Partial<ConfigHelperConfig> = {
factoryAddress: DTFactory,
poolFactoryAddress: BFactory,
fixedRateExchangeAddress: FixedRateExchange,
metadataContractAddress: Metadata,
oceanTokenAddress: Ocean,
...(process.env.AQUARIUS_URI && { metadataStoreUri: process.env.AQUARIUS_URI })
}
@ -107,10 +108,8 @@ export class ConfigHelper {
return null
}
if (network === 'development') {
const contractAddressesConfig = this.getAddressesFromEnv()
config = { ...config, ...contractAddressesConfig }
}
const contractAddressesConfig = this.getAddressesFromEnv(config.network)
config = { ...config, ...contractAddressesConfig }
const nodeUri = infuraProjectId
? `${config.nodeUri}/${infuraProjectId}`

View File

@ -283,7 +283,7 @@ describe('Compute flow', () => {
tokenAddressAlgorithm
)
assert(algorithmAsset.dataToken === tokenAddressAlgorithm)
await sleep(6000)
await sleep(60000)
})
it('Alice mints 100 DTs and tranfers them to the compute marketplace', async () => {
@ -445,7 +445,7 @@ describe('Compute flow', () => {
alice
)
assert(newDdo !== null)
await sleep(6000)
await sleep(60000)
const metaData = await ocean.assets.getServiceByType(ddo.id, 'compute')
assert.equal(
metaData.attributes.main.privacy.allowRawAlgorithm,

View File

@ -112,7 +112,7 @@ describe('Marketplace flow', () => {
)
ddo = await ocean.assets.create(asset, alice, [service1], tokenAddress)
assert(ddo.dataToken === tokenAddress)
await sleep(6000)
await sleep(60000)
})
it('Alice mints 100 tokens', async () => {
@ -212,7 +212,7 @@ describe('Marketplace flow', () => {
}
const newDdo = await ocean.assets.editMetadata(ddo.id, newMetaData, alice)
assert(newDdo !== null)
await sleep(6000)
await sleep(60000)
const metaData = await ocean.assets.getServiceByType(ddo.id, 'metadata')
assert.equal(metaData.attributes.main.name, newMetaData.title)
assert.equal(

View File

@ -27,7 +27,7 @@ describe('Balancer flow', () => {
let contracts: TestContractHandler
let datatoken: DataTokens
let tokenAddress: string
let consoleDebug: false
let consoleDebug: true
let greatPool: string
const tokenAmount = '1000'
const transferAmount = '200'
@ -171,26 +171,55 @@ describe('Balancer flow', () => {
const requiredOcean = await Pool.getOceanNeeded(alicePoolAddress, '1')
assert(Number(requiredOcean) > 0)
})
it('Get amount of DT needed to buy 1 Ocean', async () => {
const requiredOcean = await Pool.getDTNeeded(alicePoolAddress, '1')
assert(Number(requiredOcean) > 0)
})
it('Bob should search for pools with this DT', async () => {
const pools = await Pool.searchPoolforDT(tokenAddress)
assert(pools.length > 0)
greatPool = pools[0]
})
it('Bob should buy a DT ', async () => {
it('Bob should buy 2 DT ', async () => {
const maxPrice = parseFloat(currentDtPrice) * 2
await Pool.buyDT(bob, greatPool, '1', '2', String(maxPrice))
await Pool.buyDT(bob, greatPool, '2', '4')
const bobDtBalance = await datatoken.balance(tokenAddress, bob)
const bobOceanBalance = await datatoken.balance(oceanTokenAddress, bob)
assert(Number(bobDtBalance) > 0)
assert(Number(bobOceanBalance) > 0)
})
it('Bob should sell 1 DT ', async () => {
const maxPrice = parseFloat(currentDtPrice) * 2
await Pool.sellDT(bob, greatPool, '1', '1')
const bobDtBalance = await datatoken.balance(tokenAddress, bob)
const bobOceanBalance = await datatoken.balance(oceanTokenAddress, bob)
assert(Number(bobDtBalance) === 1)
assert(Number(bobOceanBalance) > 0)
})
it('Bob should get maximum DT liquidity that he can add to pool ', async () => {
const maxDT = await Pool.getDTMaxAddLiquidity(greatPool)
if (consoleDebug) console.error('maxDT:' + maxDT)
assert(parseFloat(maxDT) > 0)
})
it('Bob should fail to add more than maximum DT liquidity that he can add to pool ', async () => {
const maxDT = await Pool.getDTMaxAddLiquidity(greatPool)
const tx = await Pool.addDTLiquidity(bob, greatPool, String(parseFloat(maxDT) * 2))
assert(tx === null)
})
it('Bob should add DT liquidity to pool ', async () => {
const maxDT = await Pool.getDTMaxAddLiquidity(greatPool)
if (consoleDebug) console.error('maxDT:' + maxDT)
const currentDtReserve = await Pool.getDTReserve(greatPool)
if (consoleDebug) console.log('currentDtReserve:' + currentDtReserve)
const bobDtBalance = await datatoken.balance(tokenAddress, bob)
if (consoleDebug) console.log('BOB DT Balance:' + bobDtBalance)
await Pool.addDTLiquidity(bob, greatPool, bobDtBalance)
await Pool.addDTLiquidity(
bob,
greatPool,
String(Math.min(parseFloat(maxDT), parseFloat(bobDtBalance)))
)
const newbobDtBalance = await datatoken.balance(tokenAddress, bob)
@ -204,7 +233,37 @@ describe('Balancer flow', () => {
assert(parseFloat(newDtReserve) > parseFloat(currentDtReserve))
assert(parseFloat(sharesBalance) > 0)
})
it('Bob should get maximum DT liquidity that he can remove from pool ', async () => {
const maxDT = await Pool.getDTMaxRemoveLiquidity(greatPool)
if (consoleDebug) console.log('maxDT:' + maxDT)
assert(parseFloat(maxDT) > 0)
})
it('Bob should know how many Pool Shares he needs to remove 1 DT ', async () => {
const poolShares = await Pool.getPoolSharesRequiredToRemoveDT(greatPool, '1')
if (consoleDebug) console.log('poolShares:' + poolShares)
assert(parseFloat(poolShares) > 0)
})
it('Bob should know how many DT gets in exchange of his Pool Shares', async () => {
const poolShares = await Pool.getDTRemovedforPoolShares(
greatPool,
await Pool.sharesBalance(bob, greatPool)
)
if (consoleDebug) console.log('poolShares:' + poolShares)
assert(parseFloat(poolShares) > 0)
})
it('Bob should fail to remove more than maximum DT liquidity that he can remove from the pool ', async () => {
const maxDT = await Pool.getDTMaxRemoveLiquidity(greatPool)
if (consoleDebug) console.log('maxDT:' + maxDT)
const poolShares = await Pool.sharesBalance(bob, greatPool)
const tx = await Pool.removeDTLiquidity(
bob,
greatPool,
String(parseFloat(maxDT) * 2),
poolShares
)
assert(tx === null)
})
it('Bob should remove DT liquidity from pool ', async () => {
const currentDtReserve = await Pool.getDTReserve(greatPool)
if (consoleDebug) console.log('currentDtReserve:' + currentDtReserve)
@ -212,7 +271,14 @@ describe('Balancer flow', () => {
if (consoleDebug) console.log('bobDtBalance:' + bobDtBalance)
const poolShares = await Pool.sharesBalance(bob, greatPool)
if (consoleDebug) console.log('poolShares:' + poolShares)
await Pool.removeDTLiquidity(bob, greatPool, '0.75', poolShares)
const maxDT = await Pool.getMaxRemoveLiquidity(greatPool, tokenAddress)
if (consoleDebug) console.log('maxDT:' + maxDT)
await Pool.removeDTLiquidity(
bob,
greatPool,
String(Math.min(parseFloat(maxDT), parseFloat('0.75'))),
poolShares
)
const newDtReserve = await Pool.getDTReserve(greatPool)
if (consoleDebug) console.log('newDtReserve:' + newDtReserve)
@ -225,13 +291,33 @@ describe('Balancer flow', () => {
assert(parseFloat(poolShares) > parseFloat(newpoolShares))
})
it('Bob should get maximum Ocean liquidity that he can add to pool ', async () => {
const maxOcean = await Pool.getOceanMaxAddLiquidity(greatPool)
assert(parseFloat(maxOcean) > 0)
})
it('Bob should fail to add more than maximum Ocean liquidity that he can add to pool ', async () => {
const maxOcean = await Pool.getOceanMaxAddLiquidity(greatPool)
const tx = await Pool.addOceanLiquidity(
bob,
greatPool,
String(parseFloat(maxOcean) * 2)
)
assert(tx === null)
})
it('Bob should add Ocean liquidity to pool ', async () => {
const currentDtReserve = await Pool.getOceanReserve(greatPool)
const bobDtBalance = await datatoken.balance(oceanTokenAddress, bob)
if (consoleDebug) console.log('currentDtReserve:' + currentDtReserve)
if (consoleDebug) console.log('bobDtBalance:' + bobDtBalance)
const maxOcean = await Pool.getOceanMaxAddLiquidity(greatPool)
await Pool.addOceanLiquidity(bob, greatPool, '1')
await Pool.addOceanLiquidity(
bob,
greatPool,
String(Math.min(parseFloat(maxOcean), parseFloat(bobDtBalance)))
)
const newbobDtBalance = await datatoken.balance(oceanTokenAddress, bob)
@ -245,6 +331,34 @@ describe('Balancer flow', () => {
assert(parseFloat(newDtReserve) > parseFloat(currentDtReserve))
assert(parseFloat(sharesBalance) > 0)
})
it('Bob should get maximum Ocean liquidity that he can remove from pool ', async () => {
const maxOcean = await Pool.getMaxRemoveLiquidity(greatPool, oceanTokenAddress)
assert(parseFloat(maxOcean) > 0)
})
it('Bob should fail to remove more than maximum Ocean liquidity that he can remove from the pool ', async () => {
const maxOcean = await Pool.getOceanMaxRemoveLiquidity(greatPool)
const poolShares = await Pool.sharesBalance(bob, greatPool)
const tx = await Pool.removeOceanLiquidity(
bob,
greatPool,
String(parseFloat(maxOcean) * 2),
poolShares
)
assert(tx === null)
})
it('Bob should know how many Pool Shares he needs to remove 1 OCEAN ', async () => {
const poolShares = await Pool.getPoolSharesRequiredToRemoveOcean(greatPool, '1')
if (consoleDebug) console.log('poolShares:' + poolShares)
assert(parseFloat(poolShares) > 0)
})
it('Bob should know how many OCEAN gets in exchange of his Pool Shares', async () => {
const poolShares = await Pool.getOceanRemovedforPoolShares(
greatPool,
await Pool.sharesBalance(bob, greatPool)
)
if (consoleDebug) console.log('poolShares:' + poolShares)
assert(parseFloat(poolShares) > 0)
})
it('Bob should remove Ocean liquidity from pool ', async () => {
const currentDtReserve = await Pool.getOceanReserve(greatPool)
@ -269,6 +383,18 @@ describe('Balancer flow', () => {
assert(parseFloat(poolShares) > parseFloat(newpoolShares))
})
it('ALice should remove all liquidity', async () => {
const aliceShares = await Pool.sharesBalance(alice, greatPool)
const aliceDtBalance = await datatoken.balance(tokenAddress, alice)
const aliceOceanBalance = await datatoken.balance(oceanTokenAddress, alice)
await Pool.removePoolLiquidity(alice, greatPool, aliceShares)
const newAliceDtBalance = await datatoken.balance(tokenAddress, alice)
const newAliceOceanBalance = await datatoken.balance(oceanTokenAddress, alice)
const newAliceShares = await Pool.sharesBalance(alice, greatPool)
assert(parseFloat(aliceDtBalance) < parseFloat(newAliceDtBalance))
assert(parseFloat(aliceOceanBalance) < parseFloat(newAliceOceanBalance))
assert(parseFloat(aliceShares) > parseFloat(newAliceShares))
})
it('ALice should get all the pools that she created', async () => {
const alicePools = await Pool.getPoolsbyCreator(alice)
assert(alicePools.length > 0)