Migrating from v1
A call-by-call map from nautilus v1 to v2
nautilus v2 keeps the builder pattern but sits on a different stack: @oceanprotocol/lib
9.x instead of 3.4.6, ethers v6 instead of v5, ocean-node instead of Aquarius and
Provider, and DDO v5 instead of v4.1.0.
This is a call-by-call map. If something is not listed, it did not change.
1. Dependencies
"@oceanprotocol/lib": "3.4.6",
"ethers": "^5.7.2"
"@oceanprotocol/lib": "^9.2.1",
"ethers": "^6.17.0"@oceanprotocol/lib re-exports only the v4 DDO type. The DDO v5 types — AssetV5,
ServiceV5, MetadataV5 and the rest — are exported by @deltadao/nautilus itself, so you
do not need a direct @oceanprotocol/ddo-js dependency:
import type { AssetV5, MetadataV5, ServiceV5 } from '@deltadao/nautilus'ocean.js 9.2.1 still nests an older ddo-js under @oceanprotocol/lib for its own internals.
It is harmless and needs no override.
ddo-js 1.0.0 also makes credentials mandatory on the asset and on every service. The
builders always emit one ({} when no access rules are set), so this only affects DDOs you
assemble or patch by hand — see Validation.
2. Setting up
ethers v6 removed the providers namespace.
import { Wallet, providers } from 'ethers'
const provider = new providers.JsonRpcProvider('https://rpc.dev.pontus-x.eu')
import { JsonRpcProvider, Wallet } from 'ethers'
const provider = new JsonRpcProvider('https://rpc.dev.pontus-x.eu')
const signer = new Wallet('0x…', provider)Nautilus.create now takes an options object rather than a bare Partial<Config>:
const nautilus = await Nautilus.create(signer, { providerUri: '…' })
const nautilus = await Nautilus.create(signer, {
config: { oceanNodeUri: 'https://node.example.org' }
}) Config fields
| v1 | v2 |
|---|---|
metadataCacheUri | oceanNodeUri |
providerUri | oceanNodeUri |
subgraphUri | removed — pricing comes from chain reads |
| — | escrow, accessListFactory, sdk: 'evm' | 'oasis' |
ConfigHelper ships Pontus-X devnet (chain 32456). For any other deltaDAO network, pass
the addresses explicitly in config.
3. Reading assets
const asset = await nautilus.getAquariusAsset(did)
const assets = await nautilus.getAquariusAssets(dids)
const asset = await nautilus.getAsset(did)
const assets = await nautilus.getAssets(dids) DDO v5 nests everything under credentialSubject, so read through the
helpers rather than indexing into the document — that way a future DDO v6 does not break your code:
asset.metadata.name
asset.services[0].id
asset.nft.owner
asset.nft.state
import { getMetadata, getServices, getOwner, getLifecycleState } from '@deltadao/nautilus'
getMetadata(asset).name
getServices(asset)[0].id
getOwner(asset)
getLifecycleState(asset) New: nautilus.query(searchQuery) and nautilus.waitForIndexer(did, txid?).
4. Publishing
Publishing now signs the DDO as a verifiable credential and stores it off chain, writing
only a {remote} pointer. So it needs a remote store — see
remote stores and signing:
import { NodePersistentRemoteStore, Nautilus } from '@deltadao/nautilus'
const bootstrap = await Nautilus.create(signer, { config })
const nautilus = await Nautilus.create(signer, {
config,
// Uses the node's own storage — no external IPFS needed.
remoteStore: new NodePersistentRemoteStore(bootstrap.getNodeClient())
})Or with IPFS:
import { IpfsRemoteStore } from '@deltadao/nautilus'
remoteStore: new IpfsRemoteStore({ uploadUrl: 'http://127.0.0.1:5001/api/v0/add' })publish() gained options and returns a little more:
const { nftAddress, services, ddo, setMetadataTxReceipt } = await nautilus.publish(asset)
const { nftAddress, services, ddo, setMetadataTxReceipt, credential, indexed } =
await nautilus.publish(asset, { waitForIndexer: true }) It is also cheaper: the NFT, its first datatoken and that service's pricing are created in one transaction rather than three.
5. AssetBuilder
Every v1 method still exists. Three behave differently, and there are new ones.
new AssetBuilder(aquariusAsset)
new AssetBuilder(asset) // the asset from nautilus.getAsset()| v1 | v2 |
|---|---|
setDescription('text') | same call; wrapped into a language-tagged object |
setLicense('url') | same call; wrapped into { name }. Also accepts a License object |
addLinks(['url']) | same call; projected into v5's { label: url } map. Also accepts a map |
setContentLanguage('en') | repurposed: no longer a metadata field, it now sets the @language/@direction tag applied to every language-tagged value |
Now required: setProvidedBy(). DDO v5's schema requires providedBy, and build()
rejects a new asset without it (alongside name and type).
New:
builder
.setDisplayTitle('A nicer title')
.addAttachments([{ name: 'terms', fileType: 'pdf', sha256: '…', mirrors: [] }])
.setIssuer('did:web:example.org')
.addCredentialAccessList(CredentialListTypes.ALLOW, { chainId: 32456, accessList: '0x…' })
.addRequestCredentials(CredentialListTypes.ALLOW, [
{ type: 'VerifiableId', format: 'jwt_vc_json' }
])
.setVcPolicies(CredentialListTypes.ALLOW, ['signature', 'not-before'])
.setVpPolicies(CredentialListTypes.ALLOW, [{ policy: 'minimum-credentials', args: '1' }])
.setCredentialMatchRules({ match_allow: 'all', match_deny: 'any' })Also fixed: reset() now returns to the loaded asset in edit mode rather than emptying the
builder, and NFT defaults no longer leak between assets built in the same process.
6. ServiceBuilder
new ServiceBuilder({ aquariusAsset, serviceId })
new ServiceBuilder({ asset, serviceId }) File types changed, because ocean-node changed what it can read:
v1 FileTypes | v2 |
|---|---|
URL | URL — but the object is now UrlFileObject |
IPFS | IPFS — { type, hash } |
ARWEAVE | ARWEAVE — { type, transactionId } |
GRAPHQL | removed |
SMARTCONTRACT | removed |
| — | S3, FTP, NODE_PERSISTENT_STORAGE (new) |
setName() is effectively required now (v5 requires Service.name); build() defaults it
rather than failing.
addTrustedAlgorithms() finally honours serviceIds, because v5's
PublisherTrustedAlgorithms carries a required serviceId:
builder.addTrustedAlgorithms([{ did: 'did:ope:…', serviceIds: ['service-a', 'service-b'] }])New: setState(), setDisplayName(), setDataSchema(), setInputSchema(),
setOutputSchema(), and per-service gating via addCredentialAddresses() /
addRequestCredentials().
7. ConsumerParameterBuilder
Two v1 workarounds are gone, because v5 fixed what forced them:
builder.setType('select').addOption({ eu: 'Europe' })
// v4 emitted options as a JSON string
// v5 emits a structured array
builder.setDefault(false)
// v4 coerced this to the string "false"
// v5 keeps the boolean8. Access
const url: string = await nautilus.access({ assetDid })
const { url, transferTxId, reusedOrder } = await nautilus.access({ assetDid }) If the service is credential-gated, nautilus resolves the policy before placing any order — so a failed presentation costs nothing. See §10.
9. Compute
Compute changed the most, because C2D v2 is a different model.
const job = await nautilus.compute({ dataset, algorithm, additionalDatasets })
const { jobs, environment, orders } = await nautilus.compute({
dataset, algorithm, additionalDatasets,
computeEnv: env.id, // pick deliberately; v1 silently used [0]
resources: [{ id: 'cpu', amount: 2 }, { id: 'ram', amount: 2 }],
paymentToken: '0x…',
maxJobDuration: 3600
}) - Environments:
nautilus.getComputeEnvironments()lists them with their resources, limits and per-chain fees.resourcesandpaymentTokendefault from the environment. - Escrow: paid jobs lock funds in the
Escrowcontract. nautilus funds and authorises it from what the node quotes. - Free compute:
nautilus.freeCompute({ dataset, algorithm })— no order, no escrow, no payment token. Requires an environment exposingfree. - Output:
ComputeOutputis now{ remoteStorage?, encryption? }. The oldpublishAlgorithmLog/publishOutputflags no longer exist. - Renamed:
getComputeEnviroment(sic) →getComputeEnvironments. - New:
nautilus.streamComputeResult(),nautilus.getComputeLogs().
stopCompute and getComputeStatus no longer need a providerUri; they default to the
node the instance is configured with.
10. Credential-gated assets
New in v2. Configure a credential provider and nautilus handles the whole presentation exchange — initiate, fetch the presentation definition, authenticate to the wallet, match credentials, present, and carry the resulting session into the download or compute call.
import { Nautilus, WaltIdCredentialProvider } from '@deltadao/nautilus'
const bootstrap = await Nautilus.create(signer, { config })
const nautilus = await Nautilus.create(signer, {
config,
credentials: new WaltIdCredentialProvider(bootstrap.getNodeClient(), {
walletApi: 'https://wallet.example.org'
// Headless by default: all matching credentials, the wallet's first DID.
// Supply onSelectCredentials / onSelectDid to drive a UI instead.
})
})
// Nothing else changes at the call site.
const { url } = await nautilus.access({ assetDid: 'did:ope:…' })See credential-gated assets for the full picture.
11. Validation
New in v2, and worth using: DDO validation is entirely local, so it costs nothing and it names the failing field.
import { validate } from '@deltadao/nautilus'
const { valid, errors } = await validate(ddo)
// errors: { providedBy: ['Less than 1 values'] }publish() and edit() run this automatically before spending gas. See
validation.
12. Removed
The full removal table
| Removed | Replacement |
|---|---|
utils/aquarius.ts, utils/provider.ts | OceanNodeClient |
utils/subgraph/*, getAccessDetails | chain reads — getPricingInfo, getOrderPrice |
AccessDetails, AssetWithAccessDetails, OrderPriceAndFees | PricingInfo, OrderPrice |
local ComputeAsset / ComputeAlgorithm | ComputeAssetRef / ComputeAlgorithmRef |
transformAquariusAssetToDDO | unnecessary — v5 keeps indexed data separate |
getComputeEnviroment | getComputeEnvironments |