mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
* adding error handling for button onCLick * Updating butoon to show retry & updating toast message * Logging provider error * Removing unused imports * Using early return when provider fees are present to prevent unneccessary nesting * Adding retry logic for compute * Removing duplicate teast message * Adding tests for rendering Buy button * Additional tests for BuyButton * Refactoring BuyButton tests for download * Fix failing tests * Refactoring compute tests * Refactoring tests Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
295 lines
7.8 KiB
TypeScript
295 lines
7.8 KiB
TypeScript
import {
|
|
amountToUnits,
|
|
approve,
|
|
approveWei,
|
|
Datatoken,
|
|
FreOrderParams,
|
|
LoggerInstance,
|
|
OrderParams,
|
|
ProviderComputeInitialize,
|
|
ProviderFees,
|
|
ProviderInstance,
|
|
ProviderInitialize
|
|
} from '@oceanprotocol/lib'
|
|
import Web3 from 'web3'
|
|
import { getOceanConfig } from './ocean'
|
|
import { TransactionReceipt } from 'web3-eth'
|
|
import {
|
|
marketFeeAddress,
|
|
consumeMarketOrderFee,
|
|
consumeMarketFixedSwapFee
|
|
} from '../../app.config'
|
|
import { toast } from 'react-toastify'
|
|
|
|
async function initializeProvider(
|
|
asset: AssetExtended,
|
|
accountId: string,
|
|
providerFees?: ProviderFees
|
|
): Promise<ProviderInitialize> {
|
|
if (providerFees) return
|
|
try {
|
|
const provider = await ProviderInstance.initialize(
|
|
asset.id,
|
|
asset.services[0].id,
|
|
0,
|
|
accountId,
|
|
asset.services[0].serviceEndpoint
|
|
)
|
|
return provider
|
|
} catch (error) {
|
|
LoggerInstance.log('[Initialize Provider] Error:', error)
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param web3
|
|
* @param asset
|
|
* @param orderPriceAndFees
|
|
* @param accountId
|
|
* @param providerFees
|
|
* @param computeConsumerAddress
|
|
* @returns {TransactionReceipt} receipt of the order
|
|
*/
|
|
export async function order(
|
|
web3: Web3,
|
|
asset: AssetExtended,
|
|
orderPriceAndFees: OrderPriceAndFees,
|
|
accountId: string,
|
|
providerFees?: ProviderFees,
|
|
computeConsumerAddress?: string
|
|
): Promise<TransactionReceipt> {
|
|
const datatoken = new Datatoken(web3)
|
|
const config = getOceanConfig(asset.chainId)
|
|
|
|
const initializeData = await initializeProvider(
|
|
asset,
|
|
accountId,
|
|
providerFees
|
|
)
|
|
|
|
const orderParams = {
|
|
consumer: computeConsumerAddress || accountId,
|
|
serviceIndex: 0,
|
|
_providerFee: providerFees || initializeData.providerFee,
|
|
_consumeMarketFee: {
|
|
consumeMarketFeeAddress: marketFeeAddress,
|
|
consumeMarketFeeAmount: consumeMarketOrderFee,
|
|
consumeMarketFeeToken:
|
|
asset?.accessDetails?.baseToken?.address ||
|
|
'0x0000000000000000000000000000000000000000'
|
|
}
|
|
} as OrderParams
|
|
|
|
switch (asset.accessDetails?.type) {
|
|
case 'fixed': {
|
|
// this assumes all fees are in ocean
|
|
const txApprove = await approve(
|
|
web3,
|
|
config,
|
|
accountId,
|
|
asset.accessDetails.baseToken.address,
|
|
asset.accessDetails.datatoken.address,
|
|
await amountToUnits(
|
|
web3,
|
|
asset?.accessDetails?.baseToken?.address,
|
|
orderPriceAndFees.price
|
|
),
|
|
false
|
|
)
|
|
if (!txApprove) {
|
|
return
|
|
}
|
|
|
|
const freParams = {
|
|
exchangeContract: config.fixedRateExchangeAddress,
|
|
exchangeId: asset.accessDetails.addressOrId,
|
|
maxBaseTokenAmount: orderPriceAndFees.price,
|
|
baseTokenAddress: asset?.accessDetails?.baseToken?.address,
|
|
baseTokenDecimals: asset?.accessDetails?.baseToken?.decimals || 18,
|
|
swapMarketFee: consumeMarketFixedSwapFee,
|
|
marketFeeAddress
|
|
} as FreOrderParams
|
|
const tx = await datatoken.buyFromFreAndOrder(
|
|
asset.accessDetails.datatoken.address,
|
|
accountId,
|
|
orderParams,
|
|
freParams
|
|
)
|
|
|
|
return tx
|
|
}
|
|
case 'free': {
|
|
const tx = await datatoken.buyFromDispenserAndOrder(
|
|
asset.services[0].datatokenAddress,
|
|
accountId,
|
|
orderParams,
|
|
config.dispenserAddress
|
|
)
|
|
return tx
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* called when having a valid order, but with expired provider access, requires approval of the provider fee
|
|
* @param web3
|
|
* @param asset
|
|
* @param accountId
|
|
* @param validOrderTx
|
|
* @param providerFees
|
|
* @returns {TransactionReceipt} receipt of the order
|
|
*/
|
|
export async function reuseOrder(
|
|
web3: Web3,
|
|
asset: AssetExtended,
|
|
accountId: string,
|
|
validOrderTx: string,
|
|
providerFees?: ProviderFees
|
|
): Promise<TransactionReceipt> {
|
|
const datatoken = new Datatoken(web3)
|
|
const initializeData = await initializeProvider(
|
|
asset,
|
|
accountId,
|
|
providerFees
|
|
)
|
|
|
|
const tx = await datatoken.reuseOrder(
|
|
asset.accessDetails.datatoken.address,
|
|
accountId,
|
|
validOrderTx,
|
|
providerFees || initializeData.providerFee
|
|
)
|
|
|
|
return tx
|
|
}
|
|
|
|
async function approveProviderFee(
|
|
asset: AssetExtended,
|
|
accountId: string,
|
|
web3: Web3,
|
|
providerFeeAmount: string
|
|
): Promise<TransactionReceipt> {
|
|
const config = getOceanConfig(asset.chainId)
|
|
const baseToken =
|
|
asset?.accessDetails?.type === 'free'
|
|
? getOceanConfig(asset.chainId).oceanTokenAddress
|
|
: asset?.accessDetails?.baseToken?.address
|
|
const txApproveWei = await approveWei(
|
|
web3,
|
|
config,
|
|
accountId,
|
|
baseToken,
|
|
asset?.accessDetails?.datatoken?.address,
|
|
providerFeeAmount
|
|
)
|
|
return txApproveWei
|
|
}
|
|
|
|
async function startOrder(
|
|
web3: Web3,
|
|
asset: AssetExtended,
|
|
orderPriceAndFees: OrderPriceAndFees,
|
|
accountId: string,
|
|
hasDatatoken: boolean,
|
|
initializeData: ProviderComputeInitialize,
|
|
computeConsumerAddress?: string
|
|
): Promise<TransactionReceipt> {
|
|
const tx = await order(
|
|
web3,
|
|
asset,
|
|
orderPriceAndFees,
|
|
accountId,
|
|
initializeData.providerFee,
|
|
computeConsumerAddress
|
|
)
|
|
LoggerInstance.log('[compute] Asset ordered:', tx)
|
|
return tx
|
|
}
|
|
|
|
/**
|
|
* Handles order for compute assets for the following scenarios:
|
|
* - have validOrder and no providerFees -> then order is valid, providerFees are valid, it returns the valid order value
|
|
* - have validOrder and providerFees -> then order is valid but providerFees are not valid, we need to call reuseOrder and pay only providerFees
|
|
* - no validOrder -> we need to call order, to pay 1 DT & providerFees
|
|
* @param web3
|
|
* @param asset
|
|
* @param orderPriceAndFees
|
|
* @param accountId
|
|
* @param hasDatatoken
|
|
* @param initializeData
|
|
* @param computeConsumerAddress
|
|
* @returns {Promise<string>} tx id
|
|
*/
|
|
export async function handleComputeOrder(
|
|
web3: Web3,
|
|
asset: AssetExtended,
|
|
orderPriceAndFees: OrderPriceAndFees,
|
|
accountId: string,
|
|
hasDatatoken: boolean,
|
|
initializeData: ProviderComputeInitialize,
|
|
computeConsumerAddress?: string
|
|
): Promise<string> {
|
|
LoggerInstance.log(
|
|
'[compute] Handle compute order for asset type: ',
|
|
asset.metadata.type
|
|
)
|
|
LoggerInstance.log('[compute] Using initializeData: ', initializeData)
|
|
|
|
try {
|
|
// Return early when valid order is found, and no provider fees
|
|
// are to be paid
|
|
if (initializeData?.validOrder && !initializeData.providerFee) {
|
|
LoggerInstance.log(
|
|
'[compute] Has valid order: ',
|
|
initializeData.validOrder
|
|
)
|
|
return asset?.accessDetails?.validOrderTx
|
|
}
|
|
|
|
// Approve potential Provider fee amount first
|
|
if (initializeData?.providerFee?.providerFeeAmount !== '0') {
|
|
const txApproveProvider = await approveProviderFee(
|
|
asset,
|
|
accountId,
|
|
web3,
|
|
initializeData.providerFee.providerFeeAmount
|
|
)
|
|
|
|
if (!txApproveProvider)
|
|
throw new Error('Failed to approve provider fees!')
|
|
|
|
LoggerInstance.log('[compute] Approved provider fees:', txApproveProvider)
|
|
}
|
|
|
|
if (initializeData?.validOrder) {
|
|
LoggerInstance.log('[compute] Calling reuseOrder ...', initializeData)
|
|
const txReuseOrder = await reuseOrder(
|
|
web3,
|
|
asset,
|
|
accountId,
|
|
initializeData.validOrder,
|
|
initializeData.providerFee
|
|
)
|
|
if (!txReuseOrder) throw new Error('Failed to reuse order!')
|
|
LoggerInstance.log('[compute] Reused order:', txReuseOrder)
|
|
return txReuseOrder?.transactionHash
|
|
}
|
|
|
|
LoggerInstance.log('[compute] Calling order ...', initializeData)
|
|
const txStartOrder = await startOrder(
|
|
web3,
|
|
asset,
|
|
orderPriceAndFees,
|
|
accountId,
|
|
hasDatatoken,
|
|
initializeData,
|
|
computeConsumerAddress
|
|
)
|
|
LoggerInstance.log('[compute] Order succeeded', txStartOrder)
|
|
return txStartOrder?.transactionHash
|
|
} catch (error) {
|
|
toast.error(error.message)
|
|
LoggerInstance.error(`[compute] ${error.message}`)
|
|
}
|
|
}
|