Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/components/Asset/AssetActions/Compute/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,6 @@ export default function Compute({
const funds = await escrow.getUserFunds(owner, oceanTokenAddress)
// funds[0] is returned as string, convert to bigint
const depositedWei = BigInt(funds[0] ?? '0')

if (depositedWei < amountWei) {
const depositTx = await escrow.deposit(oceanTokenAddress, amountHuman)
await depositTx.wait()
Expand Down
11 changes: 9 additions & 2 deletions src/components/ComputeWizard/ComputeWizardController.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,6 @@ export default function ComputeWizardController({
datasetParams[param.name] = param.value ?? param.default ?? ''
})
}

const initResult = await initializePricingAndProvider({
datasetsForProvider,
algorithmAsset: actualAlgorithmAsset,
Expand Down Expand Up @@ -556,6 +555,10 @@ export default function ComputeWizardController({
): Promise<void> {
setIsSubmittingJob(true)
try {
const formValuesForEscrow = formikValues || initialFormValues
const shouldDepositEscrow = new Decimal(
formValuesForEscrow?.actualPaymentAmount || 0
).gt(0)
const {
datasetResponses,
actualAlgorithmAsset,
Expand All @@ -564,7 +567,11 @@ export default function ComputeWizardController({
initializedProvider,
selectedComputeEnv,
selectedResources
} = await initPriceAndFees(datasetServices, formikValues)
} = await initPriceAndFees(
datasetServices,
formikValues,
shouldDepositEscrow
)

if (!datasetResponses || !selectedComputeEnv || !selectedResources) {
throw new Error('Missing compute initialization data.')
Expand Down
43 changes: 31 additions & 12 deletions src/components/ComputeWizard/ConfigureEnvironment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import { useProfile } from '@context/Profile'
import styles from './index.module.css'
import { useEthersSigner } from '@hooks/useEthersSigner'
import Input from '@components/@shared/FormInput'
import Decimal from 'decimal.js'
import { MAX_DECIMALS } from '@utils/constants'

interface ResourceValues {
cpu: number
Expand Down Expand Up @@ -241,7 +243,20 @@ export default function ConfigureEnvironment({
const escrowAvailableFunds = useMemo(() => {
if (!displaySymbol) return 0
const funds = escrowFundsByToken[displaySymbol]
return funds ? parseFloat(funds.available ?? '0') : 0
if (!funds?.available) return 0
// Avoid floating rounding up (e.g. 2.9999999999 -> 3)
return new Decimal(funds.available)
.toDecimalPlaces(MAX_DECIMALS, Decimal.ROUND_DOWN)
.toNumber()
}, [escrowFundsByToken, displaySymbol])

const escrowAvailableFundsDisplay = useMemo(() => {
if (!displaySymbol) return '0'
const funds = escrowFundsByToken[displaySymbol]
if (!funds?.available) return '0'
return new Decimal(funds.available)
.toDecimalPlaces(MAX_DECIMALS, Decimal.ROUND_DOWN)
.toFixed(3)
}, [escrowFundsByToken, displaySymbol])

const [mode, setMode] = useState<'free' | 'paid'>(
Expand Down Expand Up @@ -312,6 +327,8 @@ export default function ConfigureEnvironment({
getEnvResourceValues(false)
)
const round3 = (v: number) => Math.round((v + Number.EPSILON) * 1000) / 1000
const roundUp3 = (v: number) =>
new Decimal(v).toDecimalPlaces(3, Decimal.ROUND_UP).toNumber()

const getLimits = (id: string, isFree: boolean) => {
const env = values.computeEnv
Expand Down Expand Up @@ -442,7 +459,7 @@ export default function ConfigureEnvironment({
}
currentPrice = round3(totalPrice * currentValues.jobDuration)
const availableEscrow = escrowAvailableFunds
actualPaymentAmount = round3(
actualPaymentAmount = roundUp3(
Math.max(0, currentPrice - availableEscrow)
)
escrowCoveredAmount = round3(Math.min(availableEscrow, currentPrice))
Expand Down Expand Up @@ -790,24 +807,26 @@ export default function ConfigureEnvironment({
{mode === 'paid' && (
<div className={styles.escrowValidation}>
{(() => {
const jobPrice = calculatePrice()
const availableEscrow = escrowAvailableFunds

if (jobPrice > availableEscrow) {
const deltaAmount = jobPrice - availableEscrow
const jobPrice = new Decimal(calculatePrice())
const availableEscrow = new Decimal(escrowAvailableFunds)
if (jobPrice.gt(availableEscrow)) {
const deltaAmount = jobPrice.minus(availableEscrow)
const deltaDisplay = deltaAmount.lt(0.001)
? '<0.001'
: deltaAmount.toDecimalPlaces(3, Decimal.ROUND_UP).toFixed(3)
return (
<div className={styles.insufficientEscrow}>
<p>
Insufficient escrow balance. An additional{' '}
<strong>
{deltaAmount.toFixed(3)} {displaySymbol}
{deltaDisplay} {displaySymbol}
</strong>{' '}
will be added to your escrow account to cover this job.
</p>
<p className={styles.escrowBreakdown}>
Job cost: {jobPrice.toFixed(3)} {displaySymbol} | Available
escrow: {availableEscrow.toFixed(3)} {displaySymbol} |
Additional needed: {deltaAmount.toFixed(3)} {displaySymbol}
escrow: {escrowAvailableFundsDisplay} {displaySymbol} |
Additional needed: {deltaDisplay} {displaySymbol}
</p>
</div>
)
Expand All @@ -817,8 +836,8 @@ export default function ConfigureEnvironment({
<div className={styles.sufficientEscrow}>
<p>Sufficient escrow balance available for this job.</p>
<p className={styles.escrowBreakdown}>
Job cost: {jobPrice.toFixed(2)} {displaySymbol} | Available
escrow: {availableEscrow.toFixed(2)} {displaySymbol}
Job cost: {jobPrice.toFixed(3)} {displaySymbol} | Available
escrow: {escrowAvailableFundsDisplay} {displaySymbol}
</p>
</div>
)
Expand Down
34 changes: 31 additions & 3 deletions src/components/ComputeWizard/Review/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,11 @@ export default function Review({
const c2dPriceRaw =
currentMode === 'paid' ? paidResources?.price : freeResources?.price
const c2dPrice =
c2dPriceRaw != null ? Math.round(Number(c2dPriceRaw) * 100) / 100 : 0
c2dPriceRaw != null
? new Decimal(c2dPriceRaw || 0)
.toDecimalPlaces(MAX_DECIMALS, Decimal.ROUND_DOWN)
.toNumber()
: 0
const c2dFeeTokenAddress = useMemo(() => {
if (!values.computeEnv || typeof values.computeEnv === 'string') return
const currentChainId =
Expand Down Expand Up @@ -1480,17 +1484,41 @@ export default function Review({
const escrowFunds = [
{
name: 'AMOUNT AVAILABLE IN THE ESCROW ACCOUNT',
value: Number(values.escrowFunds).toFixed(3) || '0'
value: new Decimal(values.escrowFunds || 0)
.toDecimalPlaces(MAX_DECIMALS, Decimal.ROUND_DOWN)
.toFixed(3)
}
]

const amountDeposit = [
{
name: 'AMOUNT TO DEPOSIT IN THE ESCROW ACCOUNT',
value: c2dPrice ? c2dPrice.toString() : '0'
value: (() => {
const jobPrice = new Decimal(values.jobPrice || 0)
const escrow = new Decimal(values.escrowFunds || 0)
const needed = Decimal.max(0, jobPrice.minus(escrow))
if (needed.eq(0)) return '0'
return needed.lt(0.001)
? '0.001'
: needed.toDecimalPlaces(3, Decimal.ROUND_UP).toFixed(3)
})()
}
]

useEffect(() => {
const jobPrice = new Decimal(values.jobPrice || 0)
const escrow = new Decimal(values.escrowFunds || 0)
const needed = Decimal.max(0, jobPrice.minus(escrow))
console.log('[Review] C2D deposit calc', {
jobPrice: jobPrice.toString(),
escrow: escrow.toString(),
needed: needed.toString(),
neededDisplay: needed.lt(0.001)
? '<0.001'
: needed.toDecimalPlaces(3, Decimal.ROUND_UP).toFixed(3)
})
}, [values.jobPrice, values.escrowFunds])

const nonZeroTotals = totalPriceBreakdown.filter(
(price) => price.symbol && new Decimal(price.value || 0).gt(0)
)
Expand Down
2 changes: 2 additions & 0 deletions src/components/ComputeWizard/_types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ export interface FormComputeData {
}
totalPrice?: string
escrowFunds: string
escrowCoveredAmount?: string
actualPaymentAmount?: string
jobPrice: string
baseToken?: string | null
}
Expand Down
109 changes: 59 additions & 50 deletions src/components/ComputeWizard/hooks/useComputeInitialization.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useCallback, useState } from 'react'
import { useCallback, useRef, useState } from 'react'
import {
ComputeEnvironment,
ProviderComputeInitializeResults,
Expand Down Expand Up @@ -127,6 +127,7 @@ export function useComputeInitialization({
const [extraFeesLoaded, setExtraFeesLoaded] = useState(false)
const [isInitLoading, setIsInitLoading] = useState(false)
const [initError, setInitError] = useState<string>()
const lastEscrowDepositKey = useRef<string | null>(null)

const initializePricingAndProvider = useCallback(
async ({
Expand Down Expand Up @@ -186,65 +187,73 @@ export function useComputeInitialization({
}
)
)

if (shouldDepositEscrow && selectedResources.mode === 'paid') {
const depositRequired =
selectedResources.mode === 'paid' &&
Number(selectedResources.price || 0) > 0
if (Boolean(shouldDepositEscrow) && depositRequired) {
if (!oceanTokenAddress || !web3Provider) {
throw new Error('Missing token or provider for escrow payment')
}

const escrow = new EscrowContract(
ethers.getAddress(initializedProvider.payment.escrowAddress),
signer,
algorithmAsset.credentialSubject.chainId
const escrowAddress = ethers.getAddress(
initializedProvider.payment.escrowAddress
)

const amountHuman = String(selectedResources.price || 0)
const tokenDetails = await getTokenInfo(
oceanTokenAddress,
web3Provider
)
const amountWei = ethers.parseUnits(
amountHuman,
tokenDetails.decimals
)

const erc20 = new ethers.Contract(
oceanTokenAddress,
[
'function approve(address spender, uint256 amount) returns (bool)',
'function allowance(address owner, address spender) view returns (uint256)'
],
signer
)

const owner = await signer.getAddress()
const escrowAddress = (
escrow.contract.target ?? escrow.contract.address
).toString()

if (amountWei !== BigInt(0)) {
const approveTx = await erc20.approve(escrowAddress, amountWei)
await approveTx.wait()
while (true) {
const allowanceNow = await erc20.allowance(owner, escrowAddress)
if (allowanceNow >= amountWei) {
break
}
await new Promise((resolve) => setTimeout(resolve, 2000))
}

const depositTx = await escrow.deposit(
const depositKey = `${escrowAddress}:${oceanTokenAddress}:${amountHuman}`
if (lastEscrowDepositKey.current === depositKey) {
console.log('escrow deposit skipped (already done)', depositKey)
} else {
lastEscrowDepositKey.current = depositKey
const tokenDetails = await getTokenInfo(
oceanTokenAddress,
amountHuman
web3Provider
)
const amountWei = ethers.parseUnits(
amountHuman,
tokenDetails.decimals
)
const escrow = new EscrowContract(
escrowAddress,
signer,
algorithmAsset.credentialSubject.chainId
)
await depositTx.wait()
await escrow.authorize(

const erc20 = new ethers.Contract(
oceanTokenAddress,
selectedComputeEnv.consumerAddress,
initializedProvider.payment.amount.toString(),
selectedResources.jobDuration.toString(),
'10'
[
'function approve(address spender, uint256 amount) returns (bool)',
'function allowance(address owner, address spender) view returns (uint256)'
],
signer
)

const owner = await signer.getAddress()
const escrowSpender =
(escrow.contract.target ?? escrow.contract.address).toString() ||
escrowAddress
if (amountWei !== BigInt(0)) {
const approveTx = await erc20.approve(escrowSpender, amountWei)
await approveTx.wait()
while (true) {
const allowanceNow = await erc20.allowance(owner, escrowSpender)
if (allowanceNow >= amountWei) {
break
}
await new Promise((resolve) => setTimeout(resolve, 2000))
}
const depositTx = await escrow.deposit(
oceanTokenAddress,
amountHuman
)
await depositTx.wait()
await escrow.authorize(
oceanTokenAddress,
selectedComputeEnv.consumerAddress,
initializedProvider.payment.amount.toString(),
selectedResources.jobDuration.toString(),
'10'
)
}
}
}

Expand Down
7 changes: 4 additions & 3 deletions src/components/Profile/Header/Stats.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ReactElement, useState } from 'react'
import Decimal from 'decimal.js'
import NumberUnit from './NumberUnit'
import styles from './Stats.module.css'
import { useProfile } from '@context/Profile'
Expand Down Expand Up @@ -60,9 +61,9 @@ export default function Stats({
? 'Escrow Available Funds 👉 Click to Withdraw 👈'
: 'Escrow Available Funds'
}
value={`${Number(selectedEscrowAvailable).toFixed(
2
)} ${activeToken}`}
value={`${new Decimal(selectedEscrowAvailable || 0)
.toDecimalPlaces(2, Decimal.ROUND_DOWN)
.toFixed(2)} ${activeToken}`}
/>
</div>
</>
Expand Down
Loading