cardano-foundation / cardano-foundation/cardano-rosetta-java

Asset data addition via /call endpoint

Open
#775 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
26
Forks
15
Avg merge
5d 3h
Merged PRs (30d)
2

Description

## Why

Integrators migrating from `cardano-graphql` need standalone asset lookups. Today, asset metadata in Rosetta is only ever returned *attached to an `Amount`* — inside `/block`, `/block/transaction`, `/account/balance`, `/account/coins` or `/search/transactions` responses. There is no way to ask "what is asset X?" without first finding a transaction or address that happens to contain it.

Concretely, these `cardano-graphql` queries have no clean Rosetta equivalent:

```graphql
query assets($assetId: Hex) {
assets(where: { assetId: { _eq: $assetId } }) {
assetId assetName name decimals fingerprint policyId
}
}

query assets($fingerprint: String) {
assets(where: { fingerprint: { _eq: $fingerprint } }) { ... }
}

query assets($assetId: Hex) {
assets(where: { assetId: { _eq: $assetId } }) {
tokenMints_aggregate { aggregate { sum { quantity } } }
}
}
```

Gaps today:

| Field | Status |
|---|---|
| `policyId`, `assetName` (hex), `decimals`, `name`, `ticker`, `description`, `url`, `logo` | available, but only bundled into `Amount.currency` |
| `assetId` / subject | available as `currency.metadata.subject` |
| `fingerprint` (CIP-14 `asset1…`) | **not exposed anywhere** |
| token supply (`tokenMints_aggregate`) | **not available — mints are not indexed** |
| lookup by assetId / by fingerprint | **not possible** |

## What

Add a read-only asset lookup method to the existing `/call` endpoint, in the same style as the already-implemented `get_parse_error_blocks` / `mark_parse_error_block_checked` (`CallServiceImpl`). This keeps us inside the Mesh spec (`/call` is the sanctioned escape hatch for network-specific procedures) without inventing non-standard endpoints.

New method:

- **`get_asset_data`** — look up a single asset by `asset_id` **or** by `fingerprint`, returning identifiers, Token Registry metadata, and current supply.

It must be advertised in `NetworkOptionsResponse.allow.call_methods` (already wired via `CallService.getSupportedMethods()`), and documented in the `CallRequest` / `CallResponse` descriptions in `api.yaml`.

*(Sample values below are illustrative — the shape is what matters, not the exact hex/fingerprints.)*

### 1. `get_asset_data` — by asset id

`asset_id` = base16 `policyId` + base16 `assetName` (same value as today's `currency.metadata.subject`).

**Request**

```json
{
"network_identifier": { "blockchain": "cardano", "network": "mainnet" },
"method": "get_asset_data",
"parameters": {
"asset_id": "1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209484f534b59"
}
}
```

**Response**

```json
{
"result": {
"asset": {
"asset_id": "1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209484f534b59",
"policy_id": "1e349c9bdea19fd6c147626a5260bc44b71635f398b67c59881df209",
"asset_name": "484f534b59",
"fingerprint": "asset17q7r59zlc3dgw0venc80pdv566q6yguw03f0d9",
"decimals": 0,
"name": "Hosky Token",
"ticker": "HOSKY",
"description": "Hosky Token",
"url": "https://hosky.io",
"logo": { "format": "BASE64", "value": "iVBORw0KGgo..." },
"version": 1,
"supply": {
"quantity": "1000000000000000",
"total_minted": "1000000000000000",
"total_burned": "0",
"as_of_block": 12345678
}
}
},
"idempotent": false
}
```

Notes on the response shape:
- `policy_id`, `asset_name`, `asset_id` and `fingerprint` are derived on-chain/locally — always present. `asset_name` stays base16; decoding it to a display string is left to the caller, since on-chain asset names are arbitrary bytes and not guaranteed to be valid UTF-8 (e.g. CIP-68 label prefixes).
- `decimals`, `name`, `ticker`, `description`, `url`, `logo`, `version` come from the Cardano Token Registry (reuse `TokenRegistryService`) — omitted when the asset isn't registered. Fallback for `decimals` stays `0`, consistent with current behaviour.
- `supply.quantity` is the net of mints minus burns, i.e. the direct equivalent of `tokenMints_aggregate { aggregate { sum { quantity } } }`. `total_minted` / `total_burned` are broken out because they're free once mint events are indexed and save callers a second query. `as_of_block` pins the figure to a block height so callers can reason about staleness.
- `idempotent: false` — both Token Registry entries and supply change over time.

### 2. `get_asset_data` — by fingerprint

Requires implementing CIP-14 fingerprint derivation, which we don't have today. Note the existing `AssetFingerprint` class is a misnomer — it holds `policyId` + hex `assetName`, not a CIP-14 bech32 fingerprint.

**Request**

```json
{
"network_identifier": { "blockchain": "cardano", "network": "mainnet" },
"method": "get_asset_data",
"parameters": {
"fingerprint": "asset17q7r59zlc3dgw0venc80pdv566q6yguw03f0d9"
}
}
```

**Response** — same shape as above.

Exactly one of `asset_id` / `fingerprint` must be supplied; supplying both or neither is a parameter error.

### 3. Unknown / unregistered assets

An asset that exists on chain but has no Token Registry entry still returns the derived fields and `supply`, with the registry fields omitted — matching the existing fallback behaviour in `TokenRegistryServiceImpl.createFallbackMetadata`:

```json
{
"result": {
"asset": {
"asset_id": "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c64d494e",
"policy_id": "29d222ce763455e3d7a09a665ce554f00ac89d2e99a1a83d267170c6",
"asset_name": "4d494e",
"fingerprint": "asset1jsa0nakldvzq0aqf0mkstd0j4rt3nc5f0uf75t",
"decimals": 0,
"supply": {
"quantity": "5000000000",
"total_minted": "5000000000",
"total_burned": "0",
"as_of_block": 12345678
}
}
},
"idempotent": false
}
```

An asset that has never been minted on this network is a not-found error rather than an empty object.

### Errors

Reuse the existing `/call` error factories:
- unknown method → `callMethodNotSupported`
- missing / malformed / mutually exclusive parameters → `callParameterMissing`
- `asset_id` not valid hex, or shorter than a 56-char policy id → parameter error
- offline mode → `notSupportedInOfflineMode` (already handled in `CallApiImplementation`)

### Prerequisite: indexing mint/burn events

Supply is the one part of this that isn't just a re-shaping of data we already hold. Today there is **no mint data in the database at all**:

- `OperationType` has no mint or burn type, so mints never become operations.
- There is no mint or asset table in the schema — the jOOQ tables are `Address`, `AddressUtxo`, `Block`, `Transaction`, `TxInput`, `Withdrawal`, staking/governance and friends.
- Supply cannot be back-computed from UTXOs either, since `store.utxo.pruning-enabled` defaults to `true` and spent UTXOs are pruned.

So this needs indexer work first. The enabled yaci-store starters in `yaci-indexer/pom.xml` are currently blocks, transaction, utxo, staking, governance, epoch and admin — no assets/mint store. Worth checking whether an upstream yaci-store assets module can be enabled to capture mint/burn events before we consider writing our own aggregation, and what the storage cost of that looks like.

Suggested split if that turns out to be sizeable:
1. identifiers + Token Registry metadata + CIP-14 fingerprint (self-contained, deliverable now)
2. `supply` block (depends on mint indexing)

### Reference

- Mesh specifications — `/call`, `CallRequest`, `CallResponse`, `Allow.call_methods`: https://github.com/coinbase/mesh-specifications
- Existing implementation to mirror: `api/src/main/java/org/cardanofoundation/rosetta/api/call/service/CallServiceImpl.java`
- Registry lookup to reuse: `api/src/main/java/org/cardanofoundation/rosetta/api/common/service/TokenRegistryServiceImpl.java`

Contributor guide

No contributing guide indexed for this repository

Research direction

Start with api/src/main/java/org/cardanofoundation/rosetta/api/call/service/CallServiceImpl.java and TokenRegistryServiceImpl.java, then inspect yaci-indexer/pom.xml for an upstream assets module that can capture mint and burn events. The first deliverable is asset identifiers, metadata, and CIP-14 fingerprint support; supply fields require mint indexing and should be assessed separately.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
api, backend, blockchain, database
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.