access
Download an asset: order the service if needed, then get a one-time URL.
Usage
const { } = await .({
: 'did:ope:1234abcd...',
// the most this download may pay the node, in the fee token's smallest unit
: { : '0x...', : ('0.5', 18) }
})
// fetch the actual data
const = await ()What happens in order:
- The service is checked before any other node call: it must exist on the asset and be an
accessservice, anduserdatamust fit its consumer parameters. Acomputeservice is refused with a pointer tocompute()andfreeCompute(). - If the service's node has a policy server and the asset or service has
credentials({}included), nautilus opens a policy-server session for this consumer (see credential-gated assets). An asset gated by addresses only needs no wallet; one whoseSSIpolicyasks for credentials needs a credential provider, and nautilus checks that the verifier accepted the presentation. This happens before any spend, so a refusal costs nothing. - The node is asked for provider fees and whether a previous order can be reused. Before
ordering, nautilus checks that the provider fee signature recovers to
providerFeeAddressthe way the datatoken contract verifies it, since an order with any other fee reverts. If it does not, nautilus callsinitializeagain, 1.1 s later: at most 3 calls in total (2 retries). It stops early when the node signs the same fee again, and does not ask again at all for a service withtimeout: 0, whose fee is the same on every request. A fee that is missing or lacks a field is refused at once. In every case it throws aProviderFeeSignatureErrorbefore any approval, purchase or order. - An existing order is reused where possible — extended if a new fee period is due, or
used as-is if not. The node's access
initializereports novalidOrder, so your newest order for the service is read from the datatoken's events, and each transaction is checked from its receipt the way the node checks a download: yours, for this service, inside the service's timeout with at least 10 minutes left (details). Otherwise a datatoken is bought and a new order placed. "No order found" places a new one. A lookup that fails (the RPC rate-limits it, does not answer, answers with an error, or answers without the data the check needs) or stops short (it cannot read back over the whole order window) is not "no order found": on a service that costs anything,access()stops before paying and throws anOrderCheckError, since buying without the check could pay twice. A free service is ordered anyway, with a warning (orderOnLookupFailuredoes the same for a paid one). - Only then is your consent asked, and only for the fee that will be paid: none when an
order is used as it stands, whatever the node quoted; the fee the node quotes now when
an order is extended or a new one placed. A non-zero fee must be allowed by
maxProviderFeeorconfirmProviderFees, or nautilus throws aProviderFeeNotAllowedErrorbefore any approval, purchase or order. The order is then allowed to pay exactly that fee. Everything it pulls from you (the purchase, the publish-market fee and the provider fee) is approved with one allowance per token and spender, after every check has passed. - The download URL is built, carrying the policy-server session if there is one.
Some limits of this release apply to access(): one download URL at a time per consumer and
node, parallel purchases of the same service, a second access() right after buying (it pays
again until 2 more blocks are mined), and orders older than about 55 hours. See
known limitations.
A fixed-price or free order prints nothing of its own at the default level. Earlier
releases let ocean.js print No config found for given network 'undefined' twice for each
fixed-price order, and once for a free order on a template-1 datatoken. The exchange and the
dispenser were built without the chain's config. Now every ocean.js contract nautilus builds
gets the config, so the line is gone. Their transactions also use the config's
gasFeeMultiplier, as the datatoken's own transactions already did. The exported
getPricingInfo and getDatatokenBalance take the config as an optional last argument;
without it they read the chain id from the signer, so they don't print the line either.
Returns
- Type:
AccessResult
Properties
url
- Type:
string
The one-time URL to download the raw data.
did / serviceId
- Type:
string
The asset and the service that were actually accessed — useful when you let nautilus pick the service.
transferTxId
- Type:
string
The order authorising this download.
reusedOrder
- Type:
boolean
true when an existing order was reused rather than a new one paid for. transferTxId is
then the previous order or reuse used as it stands, or the reuseOrder transaction that
extended it.
Parameters
config
- Type:
AccessConfig
Properties
assetDid
- Type:
string
The identifier of the asset to access.
serviceId (optional)
- Type:
string
Which service to download from. Defaults to the asset's first access service. It must
be an access service: a compute service runs jobs through
compute() or freeCompute().
fileIndex (optional)
- Type:
number
Which file of the service to download. Defaults to 0.
userdata (optional)
- Type:
UserCustomParameters
Values for the service's consumer parameters. They are checked against the service's
declaration before anything is sent: required parameters present, each value of its
declared type, a select value one of its options, and no undeclared keys. Keys set to
undefined or null are dropped, so they reach neither initialize nor the download URL.
See
validation, and the
ConsumerParameterBuilder for the publishing side.
maxProviderFee (optional)
- Type:
TokenAmount | TokenAmount[], whereTokenAmountis{ token: string; amount: bigint | string } - Default: the
Nautilus.createoption, else none
The most this call may pay in provider fees, per token, in the token's smallest unit (wei
for an 18-decimal token; parseUnits('0.5', 18) from ethers converts). A fee in a token not
listed is not covered. amount is a bigint or a decimal integer string; anything else
throws before the node is asked for a fee.
confirmProviderFees (optional)
- Type:
(fees: ProviderFeeQuote[]) => boolean | Promise<boolean> - Default: the
Nautilus.createoption, else none
Asked when maxProviderFee does not cover the fee, for example to show it to a user. It
receives each non-zero fee as { token, amount, collector, datatoken, did, serviceId }
(amount a bigint in the token's smallest unit, collector the providerFeeAddress that
receives it). Return true to pay, false to refuse. It is not called for a zero fee, a
fee within the ceiling, or a previous order used as it stands, which pays no fee. An error
it throws propagates before anything is spent.
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.
So a tight maxProviderFee passed here never falls back to a permissive
confirmProviderFees set in Nautilus.create, and { confirmProviderFees: undefined }
clears the default callback.
orderOnLookupFailure (optional)
- Type:
boolean - Default:
false
What to do when the previous order cannot be checked on chain and the service costs
anything (a price, a publish-market fee or a provider fee). By default access() throws an
OrderCheckError before paying: if your account already holds a valid
order, a new one would pay twice. true places a new order anyway, and still warns
(access.order.lookup_failed). Only true opts in: 'true', 1 or any other value does
not.
Could not check whether 0x4C84…4A52 already holds an order for service "Download (JSON)" of asset "Weather" (did:ope:59a18b63…): the RPC did not answer. A new order will be placed, as `orderOnLookupFailure` allows: if the account already holds one, this will pay twice (1.002 devEURAU).A free service is ordered either way, with a warning that it costs only gas. "No previous
order found" is not a failure and always orders, with no warning. When the price cannot be
read either (usually the same failing RPC), nothing can be bought, and access() throws an
OrderCheckError even with true.
Throws
-
PolicyDeniedError: the policy server refused this consumer (error.codeis the HTTP status,error.reasonits message, e.g.Access denied: Address not allowed at asset level.), or the verifier did not accept the presentation (error.codeisundefined,error.reasonnames the failed policies as the node reports them, e.g.holder-binding: java.lang.NullPointerException, anderror.policyResultslists them all;error.causeis the wallet's error when the wallet reported the failure). Also carriesdid,serviceIdandconsumerAddress. Don't sendpolicyResultsto a central log: a policy's error can quote a credential value. See what a rejection looks like. -
CredentialPresentationError(fromWaltIdCredentialProvider): the wallet could not present, e.g. it holds no credential of the requested type (error.reasonis'no-matching-credential'), or the verifier refused and its record names no failed policy ('verifier-rejected'; the others are listed onWaltIdCredentialProvider). Neither error carries the policy-server session id;NAUTILUS_DEBUG=sensitivewith thepolicyandwallettopics shows the raw answers (debugging). -
An
Errorwhen the service asks for a verifiable presentation and no credential provider is set. -
ConsumerParameterError:userdatadoes not fit the service's consumer parameters.error.issueslists each refused value as{ parameter, reason, message },reasonbeing'missing','wrong-type','not-an-option','invalid-declaration'(aselectthat declares no usable options),'unknown'or'not-object'. -
An
Errorwhen the asset has no service withserviceId, has noaccessservice, or the service is not anaccessservice. -
ProviderFeeSignatureError: the node's fee is missing, incomplete, or its signature would fail the datatoken's check. -
ProviderFeeNotAllowedError: the fee this call would pay is not zero and neithermaxProviderFeenorconfirmProviderFeesallows it.error.feeslists the fees,error.reasonis'no-limit','over-limit'or'declined'. -
OrderCheckError: the service costs anything and the previous order could not be checked on chain (the lookup failed or stopped short).
All of these are thrown before any approval, purchase or order; ConsumerParameterError and
the service errors before any node call other than resolving the asset.
OrderCheckError
access() stopped before paying, because it could not check whether your account already
holds a valid order for the service, and buying without that check could make you pay
twice. Nothing was ordered or paid, and it is safe to retry. The message says so, with
the price and the cause in plain words:
access() stopped before paying for service "Download (JSON)" of asset "Weather" (did:ope:59a18b63…): it could not check whether 0x4C84…4A52 already holds a valid order, and buying without that check could make you pay twice (1.002 devEURAU). Nothing was ordered or paid; it is safe to retry. Cause: the RPC rate-limited the check (HTTP 429). Retry in a minute or use another RPC. To buy anyway, pass `orderOnLookupFailure: true`.Fields, so a program need not parse the text:
| Field | Type | |
|---|---|---|
code | 'ORDER_CHECK_FAILED' | |
reason | 'rate-limited' | 'unreachable' | 'incomplete' | 'rpc-error' | Why the check failed (below). |
retryable | true | Nothing was sent: the same call can be made again. |
did, serviceId | string | The asset and service. |
consumer | string | The account the order is for. |
price | { amount: string; symbol: string } | undefined | The main cost of a new order, in whole tokens: the datatoken's price (exchange fees included), or for a free datatoken the first fee due. symbol is tokens of 0x… when the token's symbol cannot be read. undefined when the pricing could not be read either. |
costs | { amount: string; symbol: string }[] | Every part a new order would pay, each in its own token: the price, the publish-market fee and the provider fee, those that are not zero. The message names them all (1.002 devEURAU + 0.1 OCEAN). Empty when the pricing could not be read. |
checkedHours, windowHours | number | For incomplete only: how far back the check read (NaN when unknown), and the order window, the service's timeout (Infinity when its orders never expire). |
cause | Error | The RPC's error, redacted: first line only, credentials redacted also under JSON escaping or percent-encoding (so a key spelt "api\u004bey" is redacted too, and a value with an escaped quote or delimiter in it is redacted whole: the escape cannot cut it short), no URL, long hex shortened, at most 200 characters. |
reason | The cause in the message |
|---|---|
rate-limited | the RPC rate-limited the check (HTTP 429): an HTTP 429, a Retry-After header on an answer that is not a server error, JSON-RPC code -32005 that does not refuse the block range, or "rate limit" / "too many requests" in the answer |
unreachable | the RPC did not answer: a network error or a timeout (not an RPC that answered with an error that mentions a timeout) |
incomplete | the check covered only the last 16 of the 24 hours an order stays valid: the lookup stopped before it covered the order window (below) |
rpc-error | the RPC returned an error: … (the RPC's own words, redacted); or the RPC's answer lacked data the check needs (…): no newest block, no receipt for an order transaction its own logs list, or a receipt without one of the order events those logs list, as a load-balanced RPC whose backends lag each other can answer |
The lookup reads at most 100 000 blocks back, in as many rounds of reads as the blocks to
read need at 100 blocks per round (at least 100 rounds) and for at most 5 minutes, and checks
at most 50 candidate orders. When one of those bounds ends it before the service's
timeout with no order found, the result is incomplete, not "not found":
- Out of reads (the RPC serves fewer than 100 blocks per
eth_getLogscall, or answers so slowly that the reads take more than 5 minutes; a call the RPC does not answer within them ends the lookup at the 5 minutes): the next step is "Use another RPC, or retry." - Out of depth (a
timeoutlonger than 100 000 blocks, about 55 hours on OP Sepolia, or0, so orders never expire) or out of candidates: a retry ends the same way. The message says so, and when the account has not bought the service in that window (never, for orders that never expire), buying withorderOnLookupFailure: trueis safe. For orders that never expire the cause reads "the check covered only the last 55 hours, and orders of this service never expire".
When the price cannot be read either, the message says "the price could not be read either" instead of the price, and offers no opt-in: nothing can be bought until the RPC answers.
Don't catch the error and buy anyway on your own: that brings the second payment back. Show
the message, which names the cost, the cause and what to do, and let the user decide.
Retrying later, or with another RPC, is the safe first step. Offer orderOnLookupFailure: true only when the user knows the account holds no order in force, for example when an
incomplete lookup reports that a retry ends the same way.
const = 'did:ope:1234abcd...'
try {
await .({ })
} catch () {
if (!( instanceof )) throw
// Nothing was ordered or paid. With no costs, the price could not be read: nothing can
// be bought until the RPC answers.
const = .
.(() => `${.} ${.}`)
.(' + ')
const =
!== '' &&
(await (
`${.}\n\nBuy anyway? If this account already holds an order, this pays ${} a second time.`
))
if ()
await .({ , : true })
}The cause is redacted. To see the RPC's full answer when diagnosing, run with
NAUTILUS_DEBUG=sensitive (or setDebug): the raw error goes
to the debug channel under the order topic. See debugging before
sharing that output.