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

add initial FixedRateExchange class and test

This commit is contained in:
lacoop6tu 2021-11-04 14:59:07 -05:00
parent 817dc31c35
commit 48e573636b
4 changed files with 497 additions and 3 deletions

2
package-lock.json generated
View File

@ -3493,7 +3493,7 @@
}
},
"@oceanprotocol/contracts": {
"version": "git+https://github.com/oceanprotocol/contracts.git#bb1382e700d6f2f994b8e3abfd6a7831e69b1804",
"version": "git+https://github.com/oceanprotocol/contracts.git#c1bea5033dfc9071105a11b63ce86d8e8f612b7b",
"from": "git+https://github.com/oceanprotocol/contracts.git#v4main",
"requires": {
"@balancer-labs/v2-pool-utils": "^1.0.0",

View File

@ -25,6 +25,7 @@
"changelog": "auto-changelog -p",
"prepublishOnly": "npm run build",
"test:ss": "mocha --config=test/unit/.mocharc.json --node-env=test --exit 'test/unit/pools/ssContracts/SideStaking.test.ts'",
"test:fixed": "mocha --config=test/unit/.mocharc.json --node-env=test --exit 'test/unit/pools/fixedRate/FixedRateExchange.test.ts'",
"test:pool": "mocha --config=test/unit/.mocharc.json --node-env=test --exit 'test/unit/pools/balancer/Pool.test.ts'",
"test:router": "mocha --config=test/unit/.mocharc.json --node-env=test --exit 'test/unit/pools/Router.test.ts'",
"test:unit": "mocha --config=test/unit/.mocharc.json --node-env=test --exit 'test/unit/**/*.test.ts'",

View File

@ -1,6 +1,338 @@
import defaultFixedRateExchangeABI from '@oceanprotocol/contracts/artifacts/contracts/pools/fixedRate/FixedRateExchange.sol/FixedRateExchange.json'
import defaultERC20ABI from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC20Template.sol/ERC20Template.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 { Logger, getFairGasPrice } from '../../utils'
const MAX_AWAIT_PROMISES = 10
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
}
export class FixedRateExchange {
public GASLIMIT_DEFAULT = 1000000
public web3: Web3 = null
}
/** Ocean related functions */
public oceanAddress: string = null
public fixedRateAddress:string
public fixedRateExchangeABI: AbiItem | AbiItem[]
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
* @param {any} fixedRateExchangeABI
*/
constructor(
web3: Web3,
logger: Logger,
fixedRateAddress: string,
fixedRateExchangeABI: AbiItem | AbiItem[] = null,
oceanAddress: string = null,
startBlock?: number
) {
this.web3 = web3
if (startBlock) this.startBlock = startBlock
else this.startBlock = 0
this.fixedRateExchangeABI =
fixedRateExchangeABI || (defaultFixedRateExchangeABI.abi as AbiItem[])
this.oceanAddress = oceanAddress
this.fixedRateAddress = fixedRateAddress
this.contract = new this.web3.eth.Contract(
this.fixedRateExchangeABI,
this.fixedRateAddress
)
this.logger = logger
}
async amountToUnits(token: string, amount: string): Promise<string> {
let decimals = 18
const tokenContract = new this.web3.eth.Contract(
defaultERC20ABI.abi as AbiItem[],
token
)
try {
decimals = await tokenContract.methods.decimals().call()
} catch (e) {
this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18')
}
const amountFormatted = new BigNumber(parseInt(amount) * 10 ** decimals)
return amountFormatted.toString()
}
async unitsToAmount(token: string, amount: string): Promise<string> {
let decimals = 18
const tokenContract = new this.web3.eth.Contract(
defaultERC20ABI.abi as AbiItem[],
token
)
try {
decimals = await tokenContract.methods.decimals().call()
} catch (e) {
this.logger.error('ERROR: FAILED TO CALL DECIMALS(), USING 18')
}
const amountFormatted = new BigNumber(parseInt(amount) / 10 ** decimals)
return amountFormatted.toString()
}
/**
* 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> {
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.contract.methods
.swap(exchangeId, this.web3.utils.toWei(String(dataTokenAmount)))
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
try {
const trxReceipt = await this.contract.methods
.swap(exchangeId, this.web3.utils.toWei(String(dataTokenAmount)))
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
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> {
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.contract.methods
.setRate(exchangeId, this.web3.utils.toWei(String(newRate)))
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
const trxReceipt = await this.contract.methods
.setRate(exchangeId, this.web3.utils.toWei(String(newRate)))
.send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
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
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.contract.methods
.toggleExchangeState(exchangeId)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
const trxReceipt = await this.contract.methods.toggleExchangeState(exchangeId).send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
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
const gasLimitDefault = this.GASLIMIT_DEFAULT
let estGas
try {
estGas = await this.contract.methods
.toggleExchangeState(exchangeId)
.estimateGas({ from: address }, (err, estGas) => (err ? gasLimitDefault : estGas))
} catch (e) {
estGas = gasLimitDefault
}
const trxReceipt = await this.contract.methods.toggleExchangeState(exchangeId).send({
from: address,
gas: estGas + 1,
gasPrice: await getFairGasPrice(this.web3)
})
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)
result.exchangeID = exchangeId
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
}
}

View File

@ -0,0 +1,161 @@
import { assert, expect } from 'chai'
import { AbiItem } from 'web3-utils/types'
import { TestContractHandler } from '../../../TestContractHandler'
import { Contract } from 'web3-eth-contract'
import Web3 from 'web3'
import BigNumber from 'bignumber.js'
import ERC721Factory from '@oceanprotocol/contracts/artifacts/contracts/ERC721Factory.sol/ERC721Factory.json'
import ERC721Template from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC721Template.sol/ERC721Template.json'
import SSContract from '@oceanprotocol/contracts/artifacts/contracts/pools/ssContracts/SideStaking.sol/SideStaking.json'
import FactoryRouter from '@oceanprotocol/contracts/artifacts/contracts/pools/FactoryRouter.sol/FactoryRouter.json'
import ERC20Template from '@oceanprotocol/contracts/artifacts/contracts/templates/ERC20Template.sol/ERC20Template.json'
import Dispenser from '@oceanprotocol/contracts/artifacts/contracts/pools/dispenser/Dispenser.sol/Dispenser.json'
import FixedRate from '@oceanprotocol/contracts/artifacts/contracts/pools/fixedRate/FixedRateExchange.sol/FixedRateExchange.json'
import MockERC20 from '@oceanprotocol/contracts/artifacts/contracts/utils/mock/MockERC20Decimals.sol/MockERC20Decimals.json'
import PoolTemplate from '@oceanprotocol/contracts/artifacts/contracts/pools/balancer/BPool.sol/BPool.json'
import OPFCollector from '@oceanprotocol/contracts/artifacts/contracts/communityFee/OPFCommunityFeeCollector.sol/OPFCommunityFeeCollector.json'
import { LoggerInstance } from '../../../../src/utils'
import { NFTFactory } from '../../../../src/factories/NFTFactory'
import { Pool } from '../../../../src/pools/balancer/Pool'
import { FixedRateExchange } from '../../../../src/pools/fixedRate/FixedRateExchange'
const { keccak256 } = require('@ethersproject/keccak256')
const web3 = new Web3('http://127.0.0.1:8545')
const communityCollector = '0xeE9300b7961e0a01d9f0adb863C7A227A07AaD75'
describe('Fixed Rate unit test', () => {
let factoryOwner: string
let nftOwner: string
let exchangeOwner: string
let user1: string
let user2: string
let user3: string
let initialBlock: number
let fixedRateAddress: string
let exchangeId: string
let contracts: TestContractHandler
let fixedRate: FixedRateExchange
let dtAddress: string
let dtAddress2: string
let dtContract: Contract
let daiContract: Contract
let usdcContract: Contract
const vestedBlocks = 2500000
it('should deploy contracts', async () => {
contracts = new TestContractHandler(
web3,
ERC721Template.abi as AbiItem[],
ERC20Template.abi as AbiItem[],
PoolTemplate.abi as AbiItem[],
ERC721Factory.abi as AbiItem[],
FactoryRouter.abi as AbiItem[],
SSContract.abi as AbiItem[],
FixedRate.abi as AbiItem[],
Dispenser.abi as AbiItem[],
OPFCollector.abi as AbiItem[],
ERC721Template.bytecode,
ERC20Template.bytecode,
PoolTemplate.bytecode,
ERC721Factory.bytecode,
FactoryRouter.bytecode,
SSContract.bytecode,
FixedRate.bytecode,
Dispenser.bytecode,
OPFCollector.bytecode
)
await contracts.getAccounts()
factoryOwner = contracts.accounts[0]
nftOwner = contracts.accounts[1]
user1 = contracts.accounts[2]
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,
contracts.daiAddress
)
usdcContract = new web3.eth.Contract(
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)', () => {
it('#create an exchange', async () => {
// CREATE AN Exchange
// we prepare transaction parameters objects
const nftData = {
name: '72120Bundle',
symbol: '72Bundle',
templateIndex: 1,
baseURI: 'https://oceanprotocol.com/nft/'
}
const ercData = {
templateIndex: 1,
strings: ['ERC20B1', 'ERC20DT1Symbol'],
addresses: [
contracts.accounts[0],
user3,
contracts.accounts[0],
'0x0000000000000000000000000000000000000000'
],
uints: [web3.utils.toWei('1000000'), 0],
bytess: []
}
//[baseToken,owner,marketFeeCollector,allowedSwapper]
const fixedRateData = {
fixedPriceAddress:contracts.fixedRateAddress,
addresses:[contracts.daiAddress,exchangeOwner,user3, '0x0000000000000000000000000000000000000000'],
uints:[18,18,web3.utils.toWei('1'),1e15,0]
}
const nftFactory = new NFTFactory(contracts.factory721Address, web3, LoggerInstance)
const txReceipt = await nftFactory.createNftErcWithFixedRate(
exchangeOwner,
nftData,
ercData,
fixedRateData
)
initialBlock = await web3.eth.getBlockNumber()
dtAddress = txReceipt.events.TokenCreated.returnValues.newTokenAddress
exchangeId = txReceipt.events.NewFixedRate.returnValues.exchangeId
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)
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
)
})
})
})