1
0
mirror of https://github.com/oceanprotocol/ocean.js.git synced 2024-11-26 20:39:05 +01:00
ocean.js/src/exchange/FixedRateExchange.ts
2020-10-30 04:43:26 -07:00

483 lines
14 KiB
TypeScript

import defaultFixedRateExchangeABI from '@oceanprotocol/contracts/artifacts/FixedRateExchange.json'
import BigNumber from 'bignumber.js'
import { TransactionReceipt } from 'web3-core'
import { Contract, EventData } from 'web3-eth-contract'
import { AbiItem } from 'web3-utils/types'
import Web3 from 'web3'
import { SubscribablePromise, Logger } from '../utils'
import { DataTokens } from '../datatokens/Datatokens'
export interface FixedPriceExchange {
exchangeID?: string
exchangeOwner: string
dataToken: string
baseToken: string
fixedRate: string
active: boolean
supply: string
}
export interface FixedPriceSwap {
exchangeID: string
caller: string
baseTokenAmount: string
dataTokenAmount: string
}
export enum FixedRateCreateProgressStep {
CreatingExchange,
ApprovingDatatoken
}
const DEFAULT_GAS_LIMIT = 1000000
export class OceanFixedRateExchange {
/** Ocean related functions */
public oceanAddress: string = null
public fixedRateExchangeAddress: string
public fixedRateExchangeABI: AbiItem | AbiItem[]
public web3: Web3
public contract: Contract = null
private logger: Logger
public datatokens: DataTokens
/**
* Instantiate FixedRateExchange
* @param {any} web3
* @param {String} fixedRateExchangeAddress
* @param {any} fixedRateExchangeABI
* @param {String} oceanAddress
*/
constructor(
web3: Web3,
logger: Logger,
fixedRateExchangeAddress: string = null,
fixedRateExchangeABI: AbiItem | AbiItem[] = null,
oceanAddress: string = null,
datatokens: DataTokens
) {
this.web3 = web3
this.fixedRateExchangeAddress = fixedRateExchangeAddress
this.fixedRateExchangeABI =
fixedRateExchangeABI || (defaultFixedRateExchangeABI.abi as AbiItem[])
this.oceanAddress = oceanAddress
this.datatokens = datatokens
if (web3)
this.contract = new this.web3.eth.Contract(
this.fixedRateExchangeABI,
this.fixedRateExchangeAddress
)
this.logger = logger
}
/**
* Creates new exchange pair between Ocean Token and data token.
* @param {String} dataToken Data Token Contract Address
* @param {Number} rate exchange rate
* @param {String} address User address
* @param {String} amount Optional, amount of datatokens to be approved for the exchange
* @return {Promise<TransactionReceipt>} TransactionReceipt
*/
public create(
dataToken: string,
rate: string,
address: string,
amount?: string
): SubscribablePromise<FixedRateCreateProgressStep, TransactionReceipt> {
return new SubscribablePromise(async (observer) => {
observer.next(FixedRateCreateProgressStep.CreatingExchange)
let estGas
try {
/* estGas = await this.contract.methods
.create(this.oceanAddress, dataToken, this.web3.utils.toWei(rate))
.estimateGas(function (err, g) {
if (err) {
return DEFAULT_GAS_LIMIT
} else {
return g
}
})
*/
estGas = DEFAULT_GAS_LIMIT
} catch (e) {
estGas = DEFAULT_GAS_LIMIT
}
let exchangeId = null
let trxReceipt = null
try {
trxReceipt = await this.contract.methods
.create(this.oceanAddress, dataToken, this.web3.utils.toWei(rate))
.send({
from: address,
gas: estGas + 1
})
exchangeId = trxReceipt.events.ExchangeCreated.returnValues[0]
} catch (e) {
this.logger.error(`ERROR: Failed to create new exchange: ${e.message}`)
}
if (amount && exchangeId) {
observer.next(FixedRateCreateProgressStep.ApprovingDatatoken)
this.datatokens.approve(dataToken, this.fixedRateExchangeAddress, amount, address)
}
return trxReceipt
})
}
/**
* Creates unique exchange identifier.
* @param {String} dataToken Data Token Contract Address
* @param {String} owner Owner of the exchange
* @return {Promise<string>} exchangeId
*/
public async generateExchangeId(dataToken: string, owner: string): Promise<string> {
const exchangeId = await this.contract.methods
.generateExchangeId(this.oceanAddress, dataToken, owner)
.call()
return exchangeId
}
/**
* Atomic swap
* @param {String} exchangeId ExchangeId
* @param {Number} dataTokenAmount Amount of Data Tokens
* @param {String} address User address
* @return {Promise<TransactionReceipt>} transaction receipt
*/
public async buyDT(
exchangeId: string,
dataTokenAmount: string,
address: string
): Promise<TransactionReceipt> {
let estGas
try {
estGas = await this.contract.methods
.swap(exchangeId, this.web3.utils.toWei(String(dataTokenAmount)))
.estimateGas(function (err, g) {
if (err) {
return DEFAULT_GAS_LIMIT
} else {
return g
}
})
} catch (e) {
estGas = DEFAULT_GAS_LIMIT
}
try {
const trxReceipt = await this.contract.methods
.swap(exchangeId, this.web3.utils.toWei(String(dataTokenAmount)))
.send({
from: address,
gas: estGas + 1
})
return trxReceipt
} catch (e) {
this.logger.error(`ERROR: Failed to buy datatokens: ${e.message}`)
return null
}
}
/**
* Gets total number of exchanges
* @param {String} exchangeId ExchangeId
* @param {Number} dataTokenAmount Amount of Data Tokens
* @return {Promise<Number>} no of available exchanges
*/
public async getNumberOfExchanges(): Promise<number> {
const numExchanges = await this.contract.methods.getNumberOfExchanges().call()
return numExchanges
}
/**
* Set new rate
* @param {String} exchangeId ExchangeId
* @param {Number} newRate New rate
* @param {String} address User account
* @return {Promise<TransactionReceipt>} transaction receipt
*/
public async setRate(
exchangeId: string,
newRate: number,
address: string
): Promise<TransactionReceipt> {
let estGas
try {
estGas = await this.contract.methods
.setRate(exchangeId, this.web3.utils.toWei(String(newRate)))
.estimateGas(function (err, estGas) {
if (err) {
console.error(`ERROR: FixedPriceExchange: ${err.message}`)
return DEFAULT_GAS_LIMIT
}
return estGas
})
} catch (e) {
estGas = DEFAULT_GAS_LIMIT
}
const trxReceipt = await this.contract.methods
.setRate(exchangeId, this.web3.utils.toWei(String(newRate)))
.send({
from: address,
gas: estGas + 1
})
return trxReceipt
}
/**
* Activate an exchange
* @param {String} exchangeId ExchangeId
* @param {String} address User address
* @return {Promise<TransactionReceipt>} transaction receipt
*/
public async activate(
exchangeId: string,
address: string
): Promise<TransactionReceipt> {
const exchange = await this.getExchange(exchangeId)
if (!exchange) return null
if (exchange.active === true) return null
let estGas
try {
estGas = await this.contract.methods
.toggleExchangeState(exchangeId)
.estimateGas(function (err, estGas) {
if (err) {
console.error(`ERROR: FixedPriceExchange: ${err.message}`)
estGas = DEFAULT_GAS_LIMIT
}
return estGas
})
} catch (e) {
this.logger.error(`ERROR: FixedPriceExchange: ${e.message}`)
estGas = DEFAULT_GAS_LIMIT
}
const trxReceipt = await this.contract.methods.toggleExchangeState(exchangeId).send({
from: address,
gas: estGas + 1
})
return trxReceipt
}
/**
* Deactivate an exchange
* @param {String} exchangeId ExchangeId
* @param {String} address User address
* @return {Promise<TransactionReceipt>} transaction receipt
*/
public async deactivate(
exchangeId: string,
address: string
): Promise<TransactionReceipt> {
const exchange = await this.getExchange(exchangeId)
if (!exchange) return null
if (exchange.active === false) return null
let estGas
try {
estGas = await this.contract.methods
.toggleExchangeState(exchangeId)
.estimateGas(function (err, estGas) {
if (err) {
console.error(`ERROR: FixedPriceExchange: ${err.message}`)
estGas = DEFAULT_GAS_LIMIT
}
return estGas
})
} catch (e) {
this.logger.error(`ERROR: FixedPriceExchange: ${e.message}`)
estGas = DEFAULT_GAS_LIMIT
}
const trxReceipt = await this.contract.methods.toggleExchangeState(exchangeId).send({
from: address,
gas: estGas + 1
})
return trxReceipt
}
/**
* Get Rate
* @param {String} exchangeId ExchangeId
* @return {Promise<string>} Rate (converted from wei)
*/
public async getRate(exchangeId: string): Promise<string> {
const weiRate = await this.contract.methods.getRate(exchangeId).call()
return this.web3.utils.fromWei(weiRate)
}
/**
* Get Supply
* @param {String} exchangeId ExchangeId
* @return {Promise<string>} Rate (converted from wei)
*/
public async getSupply(exchangeId: string): Promise<string> {
const weiRate = await this.contract.methods.getSupply(exchangeId).call()
return this.web3.utils.fromWei(weiRate)
}
/**
* getOceanNeeded
* @param {String} exchangeId ExchangeId
* @param {Number} dataTokenAmount Amount of Data Tokens
* @return {Promise<string>} Ocean amount needed
*/
public async getOceanNeeded(
exchangeId: string,
dataTokenAmount: string
): Promise<string> {
const weiRate = await this.contract.methods
.CalcInGivenOut(exchangeId, this.web3.utils.toWei(dataTokenAmount))
.call()
return this.web3.utils.fromWei(weiRate)
}
/**
* Get exchange details
* @param {String} exchangeId ExchangeId
* @return {Promise<FixedPricedExchange>} Exchange details
*/
public async getExchange(exchangeId: string): Promise<FixedPriceExchange> {
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)
return result
}
/**
* Get all exchanges
* @param {String} exchangeId ExchangeId
* @return {Promise<String[]>} Exchanges list
*/
public async getExchanges(): Promise<string[]> {
return await this.contract.methods.getExchanges().call()
}
/**
* Check if an exchange is active
* @param {String} exchangeId ExchangeId
* @return {Promise<Boolean>} Result
*/
public async isActive(exchangeId: string): Promise<boolean> {
const result = await this.contract.methods.isActive(exchangeId).call()
return result
}
/**
* Calculates how many basetokens are needed to get specifyed amount of datatokens
* @param {String} exchangeId ExchangeId
* @param {String} dataTokenAmount dataTokenAmount
* @return {Promise<String>} Result
*/
public async CalcInGivenOut(
exchangeId: string,
dataTokenAmount: string
): Promise<string> {
const result = await this.contract.methods
.CalcInGivenOut(exchangeId, this.web3.utils.toWei(dataTokenAmount))
.call()
return this.web3.utils.fromWei(result)
}
public async searchforDT(
dataTokenAddress: string,
minSupply: string
): Promise<FixedPriceExchange[]> {
const result: FixedPriceExchange[] = []
const events = await this.contract.getPastEvents('ExchangeCreated', {
filter: { datatoken: dataTokenAddress.toLowerCase() },
fromBlock: 0,
toBlock: 'latest'
})
for (let i = 0; i < events.length; i++) {
const constituents = await this.getExchange(events[i].returnValues[0])
constituents.exchangeID = events[i].returnValues[0]
if (
constituents.active === true &&
constituents.dataToken.toLowerCase() === dataTokenAddress.toLowerCase()
) {
const supply = new BigNumber(constituents.supply)
const required = new BigNumber(minSupply)
if (supply.gte(required)) {
result.push(constituents)
}
}
}
return result
}
/**
* Get all exchanges, filtered by creator(if any)
* @param {String} account
* @return {Promise<FixedPricedExchange[]>}
*/
public async getExchangesbyCreator(account?: string): Promise<FixedPriceExchange[]> {
const result: FixedPriceExchange[] = []
const events = await this.contract.getPastEvents('ExchangeCreated', {
filter: {},
fromBlock: 0,
toBlock: 'latest'
})
for (let i = 0; i < events.length; i++) {
if (!account || events[i].returnValues[3].toLowerCase() === account.toLowerCase())
result.push(await this.getExchange(events[i].returnValues[0]))
}
return result
}
/**
* Get all swaps for an exchange, filtered by account(if any)
* @param {String} exchangeId
* @param {String} account
* @return {Promise<FixedPricedSwap[]>}
*/
public async getExchangeSwaps(
exchangeId: string,
account?: string
): Promise<FixedPriceSwap[]> {
const result: FixedPriceSwap[] = []
const events = await this.contract.getPastEvents('Swapped', {
filter: { exchangeId: exchangeId },
fromBlock: 0,
toBlock: 'latest'
})
for (let i = 0; i < events.length; i++) {
if (!account || events[i].returnValues[1].toLowerCase() === account.toLowerCase())
result.push(this.getEventData(events[i]))
}
return result
}
/**
* Get all swaps for an account
* @param {String} account
* @return {Promise<FixedPricedSwap[]>}
*/
public async getAllExchangesSwaps(account: string): Promise<FixedPriceSwap[]> {
const result: FixedPriceSwap[] = []
const events = await this.contract.getPastEvents('ExchangeCreated', {
filter: {},
fromBlock: 0,
toBlock: 'latest'
})
for (let i = 0; i < events.length; i++) {
const swaps: FixedPriceSwap[] = await this.getExchangeSwaps(
events[i].returnValues[0],
account
)
swaps.forEach((swap) => {
result.push(swap)
})
}
return result
}
private getEventData(data: EventData): FixedPriceSwap {
const result: FixedPriceSwap = {
exchangeID: data.returnValues[0],
caller: data.returnValues[1],
baseTokenAmount: data.returnValues[2],
dataTokenAmount: data.returnValues[3]
}
return result
}
}