diff --git a/src/contracts/Datatoken.ts b/src/contracts/Datatoken.ts index 92113bd78..b95f8a07b 100644 --- a/src/contracts/Datatoken.ts +++ b/src/contracts/Datatoken.ts @@ -737,6 +737,17 @@ export class Datatoken extends SmartContract { return decimals } + /** + * It returns the token template index. + * @param {String} dtAddress Datatoken adress + * @return {Promise} + */ + public async getId(dtAddress: string): Promise { + const dtContract = this.getContract(dtAddress) + const id = await dtContract.getId() + return id + } + /** * It returns the token symbol * @param {String} dtAddress Datatoken adress @@ -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} + */ + public async getFixedRates(dtAddress: string): Promise { + 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} + */ + public async getDispensers(dtAddress: string): Promise { + const dtContract = this.getContract(dtAddress) + const dispensers = await dtContract.getDispensers() + return dispensers + } + /** * Returns true if address has deployERC20 role * @param {String} dtAddress Datatoken adress diff --git a/src/utils/ContractUtils.ts b/src/utils/ContractUtils.ts index e9312486f..6d7be84c7 100644 --- a/src/utils/ContractUtils.ts +++ b/src/utils/ContractUtils.ts @@ -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 diff --git a/src/utils/Logger.ts b/src/utils/Logger.ts index a2e480b4d..10acd80ed 100644 --- a/src/utils/Logger.ts +++ b/src/utils/Logger.ts @@ -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 diff --git a/src/utils/OrderUtils.ts b/src/utils/OrderUtils.ts new file mode 100644 index 000000000..a148b3df1 --- /dev/null +++ b/src/utils/OrderUtils.ts @@ -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} - 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 !') + } +} diff --git a/src/utils/ProviderErrors.ts b/src/utils/ProviderErrors.ts index 75cad5c64..19acd1e97 100644 --- a/src/utils/ProviderErrors.ts +++ b/src/utils/ProviderErrors.ts @@ -1,5 +1,3 @@ -import { LoggerInstance } from './Logger' - const PREDEFINED_ERRORS = { datasets: { invalid: 'Datasets is not a list, as expected' diff --git a/src/utils/TokenUtils.ts b/src/utils/TokenUtils.ts index 4d7621bf5..d1c37bea3 100644 --- a/src/utils/TokenUtils.ts +++ b/src/utils/TokenUtils.ts @@ -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' diff --git a/src/utils/index.ts b/src/utils/index.ts index 9a4f76543..5e99d8110 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -9,3 +9,4 @@ export * from './minAbi' export * from './SignatureUtils' export * from './TokenUtils' export * from './ProviderErrors' +export * from './OrderUtils' diff --git a/test/integration/CodeExamples.test.ts b/test/integration/CodeExamples.test.ts index 15979f4df..20bb231d0 100644 --- a/test/integration/CodeExamples.test.ts +++ b/test/integration/CodeExamples.test.ts @@ -94,7 +94,6 @@ import { Files, FixedRateExchange, FreCreationParams, - getHash, Nft, NftCreateData, NftFactory, diff --git a/test/integration/ComputeFlow.test.ts b/test/integration/ComputeFlow.test.ts index f70b9132e..31558c986 100644 --- a/test/integration/ComputeFlow.test.ts +++ b/test/integration/ComputeFlow.test.ts @@ -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 () => { diff --git a/test/integration/PublishFlows.test.ts b/test/integration/PublishFlows.test.ts index 648502019..84afb72aa 100644 --- a/test/integration/PublishFlows.test.ts +++ b/test/integration/PublishFlows.test.ts @@ -8,7 +8,6 @@ import { Aquarius, NftFactory, NftCreateData, - getHash, ZERO_ADDRESS, Nft, approve, diff --git a/test/unit/Datatoken.test.ts b/test/unit/Datatoken.test.ts index 43a47684c..0c5a8a277 100644 --- a/test/unit/Datatoken.test.ts +++ b/test/unit/Datatoken.test.ts @@ -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, @@ -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 = { @@ -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 = { @@ -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 () => { diff --git a/test/unit/Dispenser.test.ts b/test/unit/Dispenser.test.ts index c21de8546..12bd8608b 100644 --- a/test/unit/Dispenser.test.ts +++ b/test/unit/Dispenser.test.ts @@ -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, @@ -9,7 +9,6 @@ import { DispenserParams, Dispenser, ZERO_ADDRESS, - Config, getEventFromTx } from '../../src/' import { DatatokenCreateParams } from '../../src/@types' @@ -23,7 +22,6 @@ describe('Dispenser flow', () => { let datatoken: Datatoken let dtAddress: string let addresses - let config: Config const nftData: NftCreateData = { name: '72120Bundle', @@ -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() diff --git a/test/unit/FixedRateExchange.test.ts b/test/unit/FixedRateExchange.test.ts index 649877bbd..b615f6590 100644 --- a/test/unit/FixedRateExchange.test.ts +++ b/test/unit/FixedRateExchange.test.ts @@ -93,8 +93,8 @@ describe('Fixed Rate unit test', () => { const freCreatedEvent = getEventFromTx(trxReceipt, 'NewFixedRate') const tokenCreatedEvent = getEventFromTx(trxReceipt, 'TokenCreated') - dtAddress = tokenCreatedEvent.args.newTokenAddress - exchangeId = freCreatedEvent.args.exchangeId + dtAddress = tokenCreatedEvent?.args?.newTokenAddress + exchangeId = freCreatedEvent?.args?.exchangeId // user1 has no dt1 expect(await balance(user1, dtAddress, await user1.getAddress())).to.equal('0.0') @@ -487,8 +487,8 @@ describe('Fixed Rate unit test', () => { const freCreatedEvent = getEventFromTx(trxReceipt, 'NewFixedRate') const tokenCreatedEvent = getEventFromTx(trxReceipt, 'TokenCreated') - dtAddress = tokenCreatedEvent.args.newTokenAddress - exchangeId = freCreatedEvent.args.exchangeId + dtAddress = tokenCreatedEvent?.args?.newTokenAddress + exchangeId = freCreatedEvent?.args?.exchangeId // user1 has no dt1 expect(await balance(user1, dtAddress, await user1.getAddress())).to.equal('0.0') diff --git a/test/unit/Nft.test.ts b/test/unit/Nft.test.ts index f902cd794..55aa96761 100644 --- a/test/unit/Nft.test.ts +++ b/test/unit/Nft.test.ts @@ -1,7 +1,7 @@ import { assert } from 'chai' import sha256 from 'crypto-js/sha256' import { ethers, Signer } from 'ethers' -import { getAddresses, getTestConfig, provider } from '../config' +import { getAddresses, provider } from '../config' import { NftFactory, NftCreateData, Nft, ZERO_ADDRESS, getEventFromTx } from '../../src' import { MetadataAndTokenURI } from '../../src/@types' diff --git a/test/unit/NftFactory.test.ts b/test/unit/NftFactory.test.ts index 2ec032da9..4906b32a4 100644 --- a/test/unit/NftFactory.test.ts +++ b/test/unit/NftFactory.test.ts @@ -1,6 +1,6 @@ import { assert, expect } from 'chai' import { getTestConfig, provider, getAddresses } from '../config' -import { ethers, Signer, providers } from 'ethers' +import { ethers, Signer } from 'ethers' import { NftFactory, NftCreateData, diff --git a/test/unit/Router.test.ts b/test/unit/Router.test.ts index 3b027dd30..911415122 100644 --- a/test/unit/Router.test.ts +++ b/test/unit/Router.test.ts @@ -1,6 +1,5 @@ import { assert, expect } from 'chai' import { getTestConfig, provider, getAddresses } from '../config' -import MockERC20 from '@oceanprotocol/contracts/artifacts/contracts/utils/mock/MockERC20Decimals.sol/MockERC20Decimals.json' import { ethers, Signer } from 'ethers' import { NftFactory, @@ -176,9 +175,8 @@ describe('Router unit test', () => { ERC_PARAMS, freParams ) - const trxReceipt2 = await tx.wait() + const trxReceipt2 = await tx2.wait() // events have been emitted - const nftCreatedEvent2 = getEventFromTx(trxReceipt2, 'NFTCreated') const TokenCreatedEvent2 = getEventFromTx(trxReceipt2, 'TokenCreated') const NewFixedRateEvent2 = getEventFromTx(trxReceipt2, 'NewFixedRate') diff --git a/test/unit/veOcean.test.ts b/test/unit/veOcean.test.ts index ab4752613..e1d9539b9 100644 --- a/test/unit/veOcean.test.ts +++ b/test/unit/veOcean.test.ts @@ -4,11 +4,9 @@ import { Config, approve, VeOcean, - VeFeeDistributor, sendTx, NftFactory, VeAllocate, - VeFeeEstimate, getEventFromTx, amountToUnits } from '../../src' @@ -18,8 +16,6 @@ describe('veOcean tests', () => { let addresses: any let nftFactory let veOcean: VeOcean - let veFeeDistributor: VeFeeDistributor - let veFeeEstimate: VeFeeEstimate let veAllocate: VeAllocate let ownerAccount let Alice @@ -36,7 +32,7 @@ describe('veOcean tests', () => { }) it('initialize accounts', async () => { - chainId = (await Alice.provider.getNetwork()).chainId + chainId = (await Alice.provider.getNetwork())?.chainId const minAbi = [ { constant: false, @@ -73,9 +69,7 @@ describe('veOcean tests', () => { amountToUnits(null, null, '1000', 18) ) veOcean = new VeOcean(addresses.veOCEAN, Alice) - veFeeDistributor = new VeFeeDistributor(addresses.veFeeDistributor, Alice) veAllocate = new VeAllocate(addresses.veAllocate, Alice) - veFeeEstimate = new VeFeeEstimate(addresses.veFeeEstimate, Alice) nftFactory = new NftFactory(addresses.ERC721Factory, Alice) })