compute
Start a paid Compute-to-Data job.
Usage
const { } = await .({
: { : 'did:ope:1234abcd...' },
: { : 'did:ope:5678wxyz...' },
// the most the job may lock in escrow, and pay in provider fees
: { : , : ('5', 18) },
: { : , : ('0.5', 18) }
})
const = [0].What happens in order:
- Every input is resolved and its compute service selected. Each input's
userdatais checked against its service's consumer parameters, and the algorithm'salgocustomdataagainst the parameters in its algorithm metadata, as foraccess: a mismatch throws aConsumerParameterErrorbefore the environment is read or anything is sent. Only the checked values are sent on, without theirundefinedornullkeys. - On a node with a policy server, a policy-server session is opened for every input
with
credentials, datasets and algorithm alike, each for its own asset and service (see credential-gated assets). A refusal aborts the whole job with nothing spent — the reason this comes first. The same sessions go toinitializeComputeandcomputeStart. initializeComputequotes the job: provider fees, reusable orders and the escrow amount. The provider fee of every input that will be ordered is checked locally, as foraccess: a fee that is missing, lacks a field, or that the datatoken would reject throws aProviderFeeSignatureErrorbefore escrow or any order. A compute fee is the same on every request, soinitializeComputeis called once and a bad fee is refused at once, not requested again; contact the node operator if this happens.- Everything the quote would spend is checked against what you allowed, before the first
transaction:
- the inputs' non-zero provider fees, summed per token, against
maxProviderFee, orconfirmProviderFeesasked once with all of them (ProviderFeeNotAllowedError); - the escrow quote: a quote of zero funds nothing and is not checked further, so a free
environment may leave its escrow address out. Otherwise its contract must be the
pinned escrow contract (your
config.escrow, else the chain'sEnterpriseEscrow, else itsEscrow), and its chain, token and payee the job's own (EscrowPaymentNotAllowedError, which no callback overrides); its amount must be withinmaxEscrowPayment, orconfirmEscrowPaymentmust returntrue(EscrowPaymentNotAllowedErroragain); - every input's order is planned: its pricing is read from chain, and pricing that
cannot be ordered (no active exchange or dispenser, or a chain config without the
contract it needs) throws, as does a
payerother than the signer; - the wallet must hold what has to be deposited.
- the inputs' non-zero provider fees, summed per token, against
- Escrow is funded in exact amounts: nautilus approves the escrow contract for the shortfall only (what the job needs less what is already available in escrow), deposits it, and authorises the environment's account to lock the job's amount on top of its current locks. A standing allowance or authorisation that already covers the job is left as it is. Nothing is approved for more than is deposited.
- Each input is ordered as planned in step 4 — reusing an existing order where the node
says one is valid — allowed to pay exactly the provider fee approved there.
compute()does not look previous orders up on chain the wayaccess()does, so it has noorderOnLookupFailureoption and does not throw anOrderCheckError. In practice nothing is reused: ocean-node reports a valid order only for atransferTxIdit is sent, andcompute()sends none, so every input that costs anything is bought again on every job (see the pricing guide and known limitations). - The job starts. The node locks the job's payment in escrow when it accepts the job.
Steps 5 to 7, with the escrow reads they depend on, run one job at a time per chain, payer,
payment token and environment account within one Nautilus instance (see
Concurrent jobs).
Choosing an environment
v1 silently used the node's first environment. Prefer choosing deliberately, since resources, duration limits and fees differ:
const = await .()
const = [0]
const { } = await .({
: { : 'did:ope:1234abcd...' },
: { : 'did:ope:5678wxyz...' },
: .,
: [
{ : 'cpu', : 2 },
{ : 'ram', : 2 }
]
})Returns
- Type:
ComputeResult
Properties
jobs
- Type:
NodeComputeJob[]
The started jobs, with the node's environment. Poll them with getComputeStatus.
Each jobId is <environmentHash>-<jobId>, as the node names a started job; every job
method takes it in that form.
environment
- Type:
ComputeEnvironment
The environment the job actually runs in, including the one nautilus picked for you.
initializeResults
- Type:
ProviderComputeInitializeResults
What the node quoted: per-input provider fees and reusable orders, plus the escrow payment.
orders
- Type:
Record<string, string>
The order transaction backing each input, keyed by did#serviceId so two inputs on the same asset keep separate orders.
Parameters
config
- Type:
ComputeConfig
Properties
dataset
- Type:
ComputeAssetRef
{ did, serviceId?, userdata? }. Defaults to the asset's first compute service.
userdata must fit the service's
consumer parameters.
algorithm
- Type:
ComputeAlgorithmRef
ComputeAssetRef plus algocustomdata? and envs? (environment variables for the
container). userdata must fit the algorithm service's consumer parameters, and
algocustomdata those of its algorithm metadata.
additionalDatasets (optional)
- Type:
ComputeAssetRef[]
Extra inputs. C2D v2 passes every dataset in one array; nautilus assembles that for you.
computeEnv (optional)
- Type:
string
Environment id. Defaults to the node's first, which is logged when it happens.
resources (optional)
- Type:
ComputeResourceRequest[]
[{ id: 'cpu', amount: 2 }]. Ids come from ComputeEnvironment.resources. A non-empty list is sent
exactly as given: ocean-node gives each resource missing from it its minimum, usually 0,
and 0 CPU or RAM means the job runs without that limit, so list every resource you want
bounded. Without resources, or with an empty list, every resource the environment lists is requested at its
minimum, raised to 1 for cpu, ram and disk within the resource's maximum; other
resources, such as GPUs, at their minimum.
paymentToken (optional)
- Type:
string
Must be a token the environment prices for this chain — fees is keyed by chain id.
Defaults to the first accepted token; an unaccepted one fails with the list of accepted ones.
maxJobDuration (optional)
- Type:
number
Seconds. Capped to the environment's maxJobDuration, with a warning when it is
(compute.duration.capped, once per node, environment, requested duration and limit).
output (optional)
- Type:
ComputeOutput
{ remoteStorage?, encryption? }. v1's hardcoded publishAlgorithmLog and publishOutput
flags no longer exist.
outputBucketId (optional)
- Type:
string
Store results in an ocean-node bucket instead. Mutually exclusive with output.
additionalViewers (optional)
- Type:
string[]
Extra addresses allowed to read the results.
metadata (optional)
- Type:
Record<string, string | number | boolean>
queueMaxWaitTime (optional)
- Type:
number
Seconds to wait in the queue when resources are unavailable.
maxProviderFee (optional)
- Type:
TokenAmount | TokenAmount[], whereTokenAmountis{ token: string; amount: bigint | string } - Default: the
Nautilus.createoption, else none
The most the job may pay in provider fees, per token, in the token's smallest unit: the sum
of every input's fee in that token. See access.
confirmProviderFees (optional)
- Type:
(fees: ProviderFeeQuote[]) => boolean | Promise<boolean> - Default: the
Nautilus.createoption, else none
Asked once, with every input's non-zero fee, when maxProviderFee does not cover them.
Return true to pay them all.
maxProviderFee and confirmProviderFees replace the Nautilus.create defaults as a
pair: set either one here, even to undefined, and neither default applies to this call.
A tight maxProviderFee therefore never falls back to a permissive default callback.
maxEscrowPayment (optional)
- Type:
TokenAmount | TokenAmount[] - Default: the
Nautilus.createoption, else none
The most the job may lock in escrow, per payment token, in the token's smallest unit (the
node quotes payment.amount in those units). Without it or confirmEscrowPayment, a paid
job is refused with an EscrowPaymentNotAllowedError before anything is spent; a quote of
zero needs neither.
confirmEscrowPayment (optional)
- Type:
(payment: EscrowPaymentQuote) => boolean | Promise<boolean> - Default: the
Nautilus.createoption, else none
Asked when maxEscrowPayment does not cover the quote, with
{ escrowAddress, token, amount, payee, minLockSeconds, chainId } (amount a bigint).
Return true to fund it. It is asked only after the escrow contract, chain, token and payee
passed their checks.
maxEscrowPayment and confirmEscrowPayment replace the Nautilus.create defaults as a
pair, like the provider-fee options.
Escrow contract
nautilus funds exactly one escrow contract per chain, chosen without the node:
config.escrow, when you pass it toNautilus.create: that contract, and no other.- Otherwise the chain's
EnterpriseEscrowin Ocean's address data (the addresses ocean.js ships, or the fileADDRESS_FILEnames). This is the contract ocean-node 4.2.0 uses wherever the address data has one: OP Sepolia, Optimism, Sepolia and Ethereum mainnet among the bundled chains, and the local stack. - Otherwise the chain's
Escrowin the address data, on a chain that lists noEnterpriseEscrow.
A quote naming any other contract is refused with EscrowPaymentNotAllowedError
('escrow-address'), whose message names the expected contract and the rule that chose it.
No callback overrides this.
On a chain whose address data lists neither (Pontus-X devnet, 32456), paid compute is
refused ('unknown-escrow') until you set the chain's EnterpriseEscrow contract as
config.escrow. nautilus ships no Pontus-X escrow address; ask the network operator for it.
compute is also exported as a standalone function, compute(config, context). Its
context.escrow plays the part of config.escrow: set it to pin a contract, or leave it
out for the default above. context.chainConfig.escrow is not read.
Concurrent jobs
Funding escrow sets absolute values read from the chain: the authorisation becomes the amount already locked plus this job's, and the lock count one more than the current one. Two jobs planned from the same reading would each authorise only their own amount, and the node would then refuse the second job's lock after its orders were paid.
So each Nautilus instance runs paid jobs that share a chain, payer, payment token and
environment account one at a time, from the escrow reads to computeStart. A job waits for
the previous one's lock before it reads escrow, so it plans from what that job left. The
checks and the order planning in step 4 run before the job waits, and jobs for another
environment, token or payer do not wait at all.
This covers one instance. Jobs started by another Nautilus instance, another process or
another machine with the same wallet are not serialised: run them one after the other, or
from one instance. The standalone compute(config, context) serialises only when given a
lock as context.escrowLock.
Throws
ConsumerParameterError, as foraccess: an input'suserdataor the algorithm'salgocustomdatadoes not fit the declared consumer parameters, or aselectamong them declares no usable options ('invalid-declaration').error.did,error.serviceIdanderror.fieldname the input.PolicyDeniedError,CredentialPresentationError,ProviderFeeSignatureErrorandProviderFeeNotAllowedError, as foraccess, and anErrorwhen an input asks for a verifiable presentation and no credential provider is set. A refused presentation names the failed policies of the input whose session it was (error.did,error.serviceId).EscrowPaymentNotAllowedError, witherror.reason:'unknown-escrow': no escrow contract is pinned for the chain: noconfig.escrow, and noEnterpriseEscroworEscrowin Ocean's address data. Set the chain'sEnterpriseEscrowasconfig: { escrow: '0x…' }inNautilus.create;'escrow-address': the node named another contract than the pinned one;'mismatch': the quote is for another chain, token or payee, or its amount is not an exact non-negative integer (a malformed amount is refused even without an escrow address);'no-limit','over-limit'or'declined': the amount was not allowed.
- An
Errorwhen an input's pricing cannot be ordered, or thepayeris not the signer. - An
Errorwhen the wallet cannot cover the deposit.
Not an OrderCheckError: that one is thrown by access
when its on-chain lookup of a previous order fails. compute() makes no such lookup.
All of these are thrown before any approval, deposit or order.