Skip to content

Commit

Permalink
Feature/Add order util method (#1759)
Browse files Browse the repository at this point in the history
* added order  helper method using contracts calls

* fixes consume fees  & lint

* cleanup

* update order helper method to be more generic

* fix all warnings
  • Loading branch information
bogdanfazakas authored Aug 25, 2023
1 parent 2724e63 commit faa7de3
Show file tree
Hide file tree
Showing 17 changed files with 257 additions and 35 deletions.
33 changes: 33 additions & 0 deletions src/contracts/Datatoken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -737,6 +737,17 @@ export class Datatoken extends SmartContract {
return decimals
}

/**
* It returns the token template index.
* @param {String} dtAddress Datatoken adress
* @return {Promise<number>}
*/
public async getId(dtAddress: string): Promise<number> {
const dtContract = this.getContract(dtAddress)
const id = await dtContract.getId()
return id
}

/**
* It returns the token symbol
* @param {String} dtAddress Datatoken adress
Expand Down Expand Up @@ -770,6 +781,28 @@ export class Datatoken extends SmartContract {
return nftAddress
}

/**
* It returns the list of fixedRateExchanges created for this datatoken.
* @param {String} dtAddress Datatoken adress
* @return {Promise<number>}
*/
public async getFixedRates(dtAddress: string): Promise<any[]> {
const dtContract = this.getContract(dtAddress)
const fixedRates = await dtContract.getFixedRates()
return fixedRates
}

/**
* It returns the list of dispensers created for this datatoken.
* @param {String} dtAddress Datatoken adress
* @return {Promise<number>}
*/
public async getDispensers(dtAddress: string): Promise<any[]> {
const dtContract = this.getContract(dtAddress)
const dispensers = await dtContract.getDispensers()
return dispensers
}

/**
* Returns true if address has deployERC20 role
* @param {String} dtAddress Datatoken adress
Expand Down
2 changes: 1 addition & 1 deletion src/utils/ContractUtils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ethers, Signer, providers, Contract, ContractFunction, BigNumber } from 'ethers'

import { Config } from '../config'
import { minAbi, GASLIMIT_DEFAULT, LoggerInstance, FEE_HISTORY_NOT_SUPPORTED } from '.'
import { minAbi } from '.'

const MIN_GAS_FEE_POLYGON = 30000000000 // minimum recommended 30 gwei polygon main and mumbai fees
const POLYGON_NETWORK_ID = 137
Expand Down
4 changes: 3 additions & 1 deletion src/utils/Logger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ export enum LogLevel {
/* eslint-enable no-unused-vars */

export class Logger {
constructor(private logLevel: LogLevel = LogLevel.Error) {}
constructor(private logLevel?: LogLevel) {
this.logLevel = logLevel || LogLevel.Error
}

public setLevel(logLevel: LogLevel): void {
this.logLevel = logLevel
Expand Down
204 changes: 204 additions & 0 deletions src/utils/OrderUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import { Signer } from 'ethers'
import {
ProviderInstance,
Datatoken,
Dispenser,
Config,
OrderParams,
Asset,
FreOrderParams,
approve,
FixedRateExchange,
ConsumeMarketFee
} from '../index'
import Decimal from 'decimal.js'

/**
* Orders an asset based on the specified pricing schema and configuration.
* @param {Asset} asset - The asset to be ordered.
* @param {Signer} consumerAccount - The signer account of the consumer.
* @param {Config} config - The configuration settings.
* @param {Datatoken} datatoken - The Datatoken instance.
* @param {ConsumeMarketFee} [consumeMarketOrderFee] - Optional consume market fee.
* @param {string} [consumeMarketFixedSwapFee='0'] - Fixed swap fee for consuming the market.
* @param {number} [datatokenIndex=0] - Index of the datatoken within the asset.
* @param {number} [serviceIndex=0] - Index of the service within the asset.
* @param {number} [fixedRateIndex=0] - Index of the fixed rate within the pricing schema.
* @returns {Promise<void>} - A promise that resolves when the asset order process is completed.
* @throws {Error} If the pricing schema is not supported or if required indexes are invalid.
*/
export async function orderAsset(
asset: Asset,
consumerAccount: Signer,
config: Config,
datatoken: Datatoken,
consumeMarketOrderFee?: ConsumeMarketFee,
consumeMarketFixedSwapFee: string = '0',
datatokenIndex: number = 0,
serviceIndex: number = 0,
fixedRateIndex: number = 0
) {
if (!consumeMarketOrderFee)
consumeMarketOrderFee = {
consumeMarketFeeAddress: '0x0000000000000000000000000000000000000000',
consumeMarketFeeAmount: '0',
consumeMarketFeeToken:
asset.stats.price.tokenAddress || '0x0000000000000000000000000000000000000000'
}

if (!asset.datatokens[datatokenIndex].address)
throw new Error(
`The datatoken with index: ${datatokenIndex} does not exist for the asset with did: ${asset.id}`
)

if (!asset.services[serviceIndex].id)
throw new Error(
`There is no service with index: ${serviceIndex} defined for the asset with did: ${asset.id}`
)

const templateIndex = await datatoken.getId(asset.datatokens[datatokenIndex].address)
const fixedRates = await datatoken.getFixedRates(
asset.datatokens[datatokenIndex].address
)
const dispensers = await datatoken.getDispensers(
asset.datatokens[datatokenIndex].address
)
const publishMarketFees = await datatoken.getPublishingMarketFee(
asset.datatokens[datatokenIndex].address
)
const pricingType =
fixedRates.length > 0 ? 'fixed' : dispensers.length > 0 ? 'free' : 'NOT_ALLOWED'
const initializeData = await ProviderInstance.initialize(
asset.id,
asset.services[serviceIndex].id,
0,
await consumerAccount.getAddress(),
config.providerUri
)

const orderParams = {
consumer: await consumerAccount.getAddress(),
serviceIndex,
_providerFee: initializeData.providerFee,
_consumeMarketFee: consumeMarketOrderFee
} as OrderParams

switch (pricingType) {
case 'free': {
if (templateIndex === 1) {
const dispenser = new Dispenser(config.dispenserAddress, consumerAccount)
const dispenserTx = await dispenser.dispense(
asset.datatokens[datatokenIndex].address,
'1',
await consumerAccount.getAddress()
)
if (!dispenserTx) {
return
}
return await datatoken.startOrder(
asset.datatokens[datatokenIndex].address,
orderParams.consumer,
orderParams.serviceIndex,
orderParams._providerFee,
orderParams._consumeMarketFee
)
}
if (templateIndex === 2) {
return await datatoken.buyFromDispenserAndOrder(
asset.services[serviceIndex].datatokenAddress,
orderParams,
config.dispenserAddress
)
}
break
}
case 'fixed': {
const fre = new FixedRateExchange(config.fixedRateExchangeAddress, consumerAccount)

if (!fixedRates[fixedRateIndex].id)
throw new Error(
`There is no fixed rate exchange with index: ${serviceIndex} for the asset with did: ${asset.id}`
)
const fees = await fre.getFeesInfo(fixedRates[fixedRateIndex].id)
const exchange = await fre.getExchange(fixedRates[fixedRateIndex].id)
const { baseTokenAmount } = await fre.calcBaseInGivenDatatokensOut(
fees.exchangeId,
'1',
consumeMarketOrderFee.consumeMarketFeeAmount
)

const price = new Decimal(+baseTokenAmount || 0)
.add(new Decimal(consumeMarketOrderFee.consumeMarketFeeAmount || 0))
.add(new Decimal(+publishMarketFees.publishMarketFeeAmount || 0))
.toString()

const freParams = {
exchangeContract: config.fixedRateExchangeAddress,
exchangeId: fees.exchangeId,
maxBaseTokenAmount: price,
baseTokenAddress: exchange.baseToken,
baseTokenDecimals: parseInt(exchange.btDecimals) || 18,
swapMarketFee: consumeMarketFixedSwapFee,
marketFeeAddress: publishMarketFees.publishMarketFeeAddress
} as FreOrderParams

if (templateIndex === 1) {
const tx: any = await approve(
consumerAccount,
config,
await consumerAccount.getAddress(),
exchange.baseToken,
config.fixedRateExchangeAddress,
price,
false
)
const txApprove = typeof tx !== 'number' ? await tx.wait() : tx
if (!txApprove) {
return
}
const freTx = await fre.buyDatatokens(
exchange.exchangeId,
'1',
price,
publishMarketFees.publishMarketFeeAddress,
consumeMarketFixedSwapFee
)
const buyDtTx = await freTx.wait()
if (!buyDtTx) {
return
}
return await datatoken.startOrder(
asset.datatokens[datatokenIndex].address,
orderParams.consumer,
orderParams.serviceIndex,
orderParams._providerFee,
orderParams._consumeMarketFee
)
}
if (templateIndex === 2) {
const tx: any = await approve(
consumerAccount,
config,
await consumerAccount.getAddress(),
exchange.baseToken,
asset.datatokens[datatokenIndex].address,
price,
false
)

const txApprove = typeof tx !== 'number' ? await tx.wait() : tx
if (!txApprove) {
return
}
return await datatoken.buyFromFreAndOrder(
asset.datatokens[datatokenIndex].address,
orderParams,
freParams
)
}
break
}
default:
throw new Error('Pricing schema not supported !')
}
}
2 changes: 0 additions & 2 deletions src/utils/ProviderErrors.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { LoggerInstance } from './Logger'

const PREDEFINED_ERRORS = {
datasets: {
invalid: 'Datasets is not a list, as expected'
Expand Down
2 changes: 1 addition & 1 deletion src/utils/TokenUtils.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import Decimal from 'decimal.js'
import { ethers, Signer, BigNumber } from 'ethers'
import { ethers, Signer } from 'ethers'
import { amountToUnits, unitsToAmount, minAbi, sendTx, LoggerInstance } from '.'
import { Config } from '../config'
import { ReceiptOrEstimate, ReceiptOrDecimal } from '../@types'
Expand Down
1 change: 1 addition & 0 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ export * from './minAbi'
export * from './SignatureUtils'
export * from './TokenUtils'
export * from './ProviderErrors'
export * from './OrderUtils'
1 change: 0 additions & 1 deletion test/integration/CodeExamples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ import {
Files,
FixedRateExchange,
FreCreationParams,
getHash,
Nft,
NftCreateData,
NftFactory,
Expand Down
1 change: 1 addition & 0 deletions test/integration/ComputeFlow.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,7 @@ describe('Compute flow tests', async () => {
delay(100000)

const jobFinished = await waitTillJobEnds()
console.log('Job finished: ', jobFinished)

// move to start orders with initial txid's and provider fees
it('should restart a computeJob without paying anything, because order is valid and providerFees are still valid', async () => {
Expand Down
1 change: 0 additions & 1 deletion test/integration/PublishFlows.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
Aquarius,
NftFactory,
NftCreateData,
getHash,
ZERO_ADDRESS,
Nft,
approve,
Expand Down
10 changes: 3 additions & 7 deletions test/unit/Datatoken.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { assert, expect } from 'chai'
import { getTestConfig, provider, getAddresses } from '../config'
import { ethers, Signer, providers } from 'ethers'
import { provider, getAddresses } from '../config'
import { ethers, Signer } from 'ethers'
import {
Config,
NftFactory,
NftCreateData,
Datatoken,
Expand All @@ -27,10 +26,8 @@ describe('Datatoken', () => {
let nftFactory: NftFactory
let nftAddress: string
let datatokenAddress: string
let fixedRateAddress: string
let exchangeId: string
let freParams: FreCreationParams
let config: Config
let addresses: any

const nftData: NftCreateData = {
Expand All @@ -49,7 +46,6 @@ describe('Datatoken', () => {
user3 = (await provider.getSigner(3)) as Signer
datatokenDeployer = (await provider.getSigner(4)) as Signer

config = await getTestConfig(nftOwner as Signer)
addresses = await getAddresses()

freParams = {
Expand Down Expand Up @@ -208,7 +204,7 @@ describe('Datatoken', () => {
assert(freCreatedEvent !== null)

// fixedRateAddress = freCreatedEvent.args.ad
exchangeId = freCreatedEvent.args.exchangeId
exchangeId = freCreatedEvent?.args?.exchangeId
})

it('#createFixedRate - should FAIL create FRE if NOT DatatokenDeployer', async () => {
Expand Down
7 changes: 2 additions & 5 deletions test/unit/Dispenser.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { assert, expect } from 'chai'
import { getTestConfig, provider, getAddresses } from '../config'
import { ethers, Signer, providers } from 'ethers'
import { provider, getAddresses } from '../config'
import { Signer } from 'ethers'

import {
NftFactory,
Expand All @@ -9,7 +9,6 @@ import {
DispenserParams,
Dispenser,
ZERO_ADDRESS,
Config,
getEventFromTx
} from '../../src/'
import { DatatokenCreateParams } from '../../src/@types'
Expand All @@ -23,7 +22,6 @@ describe('Dispenser flow', () => {
let datatoken: Datatoken
let dtAddress: string
let addresses
let config: Config

const nftData: NftCreateData = {
name: '72120Bundle',
Expand Down Expand Up @@ -51,7 +49,6 @@ describe('Dispenser flow', () => {
user1 = (await provider.getSigner(3)) as Signer
user2 = (await provider.getSigner(4)) as Signer

config = await getTestConfig(factoryOwner as Signer)
addresses = await getAddresses()

nftData.owner = await factoryOwner.getAddress()
Expand Down
Loading

0 comments on commit faa7de3

Please sign in to comment.