Home / Cardano Foundation / cardano-rosetta-java
Apr 03, 8-9 AM (0)
Apr 03, 9-10 AM (0)
Apr 03, 10-11 AM (0)
Apr 03, 11-12 PM (0)
Apr 03, 12-1 PM (0)
Apr 03, 1-2 PM (0)
Apr 03, 2-3 PM (0)
Apr 03, 3-4 PM (0)
Apr 03, 4-5 PM (0)
Apr 03, 5-6 PM (0)
Apr 03, 6-7 PM (0)
Apr 03, 7-8 PM (0)
Apr 03, 8-9 PM (0)
Apr 03, 9-10 PM (0)
Apr 03, 10-11 PM (0)
Apr 03, 11-12 AM (0)
Apr 04, 12-1 AM (0)
Apr 04, 1-2 AM (0)
Apr 04, 2-3 AM (0)
Apr 04, 3-4 AM (0)
Apr 04, 4-5 AM (0)
Apr 04, 5-6 AM (0)
Apr 04, 6-7 AM (0)
Apr 04, 7-8 AM (0)
Apr 04, 8-9 AM (0)
Apr 04, 9-10 AM (0)
Apr 04, 10-11 AM (0)
Apr 04, 11-12 PM (0)
Apr 04, 12-1 PM (0)
Apr 04, 1-2 PM (0)
Apr 04, 2-3 PM (0)
Apr 04, 3-4 PM (0)
Apr 04, 4-5 PM (0)
Apr 04, 5-6 PM (0)
Apr 04, 6-7 PM (0)
Apr 04, 7-8 PM (0)
Apr 04, 8-9 PM (0)
Apr 04, 9-10 PM (0)
Apr 04, 10-11 PM (0)
Apr 04, 11-12 AM (0)
Apr 05, 12-1 AM (0)
Apr 05, 1-2 AM (0)
Apr 05, 2-3 AM (0)
Apr 05, 3-4 AM (0)
Apr 05, 4-5 AM (0)
Apr 05, 5-6 AM (0)
Apr 05, 6-7 AM (0)
Apr 05, 7-8 AM (0)
Apr 05, 8-9 AM (0)
Apr 05, 9-10 AM (0)
Apr 05, 10-11 AM (0)
Apr 05, 11-12 PM (0)
Apr 05, 12-1 PM (0)
Apr 05, 1-2 PM (0)
Apr 05, 2-3 PM (0)
Apr 05, 3-4 PM (0)
Apr 05, 4-5 PM (0)
Apr 05, 5-6 PM (0)
Apr 05, 6-7 PM (0)
Apr 05, 7-8 PM (0)
Apr 05, 8-9 PM (0)
Apr 05, 9-10 PM (0)
Apr 05, 10-11 PM (0)
Apr 05, 11-12 AM (0)
Apr 06, 12-1 AM (0)
Apr 06, 1-2 AM (0)
Apr 06, 2-3 AM (0)
Apr 06, 3-4 AM (0)
Apr 06, 4-5 AM (0)
Apr 06, 5-6 AM (0)
Apr 06, 6-7 AM (0)
Apr 06, 7-8 AM (0)
Apr 06, 8-9 AM (0)
Apr 06, 9-10 AM (0)
Apr 06, 10-11 AM (0)
Apr 06, 11-12 PM (0)
Apr 06, 12-1 PM (0)
Apr 06, 1-2 PM (0)
Apr 06, 2-3 PM (0)
Apr 06, 3-4 PM (0)
Apr 06, 4-5 PM (25)
Apr 06, 5-6 PM (3)
Apr 06, 6-7 PM (0)
Apr 06, 7-8 PM (0)
Apr 06, 8-9 PM (0)
Apr 06, 9-10 PM (0)
Apr 06, 10-11 PM (0)
Apr 06, 11-12 AM (0)
Apr 07, 12-1 AM (0)
Apr 07, 1-2 AM (0)
Apr 07, 2-3 AM (0)
Apr 07, 3-4 AM (0)
Apr 07, 4-5 AM (0)
Apr 07, 5-6 AM (0)
Apr 07, 6-7 AM (0)
Apr 07, 7-8 AM (0)
Apr 07, 8-9 AM (1)
Apr 07, 9-10 AM (3)
Apr 07, 10-11 AM (0)
Apr 07, 11-12 PM (0)
Apr 07, 12-1 PM (0)
Apr 07, 1-2 PM (0)
Apr 07, 2-3 PM (0)
Apr 07, 3-4 PM (0)
Apr 07, 4-5 PM (0)
Apr 07, 5-6 PM (0)
Apr 07, 6-7 PM (0)
Apr 07, 7-8 PM (0)
Apr 07, 8-9 PM (0)
Apr 07, 9-10 PM (0)
Apr 07, 10-11 PM (0)
Apr 07, 11-12 AM (0)
Apr 08, 12-1 AM (0)
Apr 08, 1-2 AM (0)
Apr 08, 2-3 AM (0)
Apr 08, 3-4 AM (0)
Apr 08, 4-5 AM (0)
Apr 08, 5-6 AM (0)
Apr 08, 6-7 AM (0)
Apr 08, 7-8 AM (0)
Apr 08, 8-9 AM (0)
Apr 08, 9-10 AM (0)
Apr 08, 10-11 AM (0)
Apr 08, 11-12 PM (0)
Apr 08, 12-1 PM (0)
Apr 08, 1-2 PM (1)
Apr 08, 2-3 PM (4)
Apr 08, 3-4 PM (0)
Apr 08, 4-5 PM (0)
Apr 08, 5-6 PM (0)
Apr 08, 6-7 PM (0)
Apr 08, 7-8 PM (0)
Apr 08, 8-9 PM (0)
Apr 08, 9-10 PM (0)
Apr 08, 10-11 PM (0)
Apr 08, 11-12 AM (0)
Apr 09, 12-1 AM (0)
Apr 09, 1-2 AM (0)
Apr 09, 2-3 AM (0)
Apr 09, 3-4 AM (0)
Apr 09, 4-5 AM (0)
Apr 09, 5-6 AM (0)
Apr 09, 6-7 AM (0)
Apr 09, 7-8 AM (0)
Apr 09, 8-9 AM (0)
Apr 09, 9-10 AM (0)
Apr 09, 10-11 AM (0)
Apr 09, 11-12 PM (3)
Apr 09, 12-1 PM (2)
Apr 09, 1-2 PM (0)
Apr 09, 2-3 PM (1)
Apr 09, 3-4 PM (0)
Apr 09, 4-5 PM (0)
Apr 09, 5-6 PM (1)
Apr 09, 6-7 PM (5)
Apr 09, 7-8 PM (0)
Apr 09, 8-9 PM (0)
Apr 09, 9-10 PM (0)
Apr 09, 10-11 PM (0)
Apr 09, 11-12 AM (0)
Apr 10, 12-1 AM (0)
Apr 10, 1-2 AM (0)
Apr 10, 2-3 AM (0)
Apr 10, 3-4 AM (4)
Apr 10, 4-5 AM (0)
Apr 10, 5-6 AM (2)
Apr 10, 6-7 AM (0)
Apr 10, 7-8 AM (0)
Apr 10, 8-9 AM (0)
55 commits this week Apr 03, 2026 - Apr 10, 2026
fix: separate InterruptedException from IOException in YaciHttpGatewayImpl (#732)
Thread.currentThread().interrupt() was called for both IOException and
InterruptedException. On IOException (e.g. yaci-indexer not ready), this
left the request thread in interrupted state. With pruning enabled, the
subsequent oldest_block_identifier DB query hit the interrupted flag,
causing JDBC to close the socket and fail with "Unable to rollback
against JDBC Connection".

Closes #732

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
fix: separate InterruptedException from IOException in YaciHttpGatewayImpl (#732)
Thread.currentThread().interrupt() was called for both IOException and
InterruptedException. On IOException (e.g. yaci-indexer not ready), this
left the request thread in interrupted state. With pruning enabled, the
subsequent oldest_block_identifier DB query hit the interrupted flag,
causing JDBC to close the socket and fail with "Unable to rollback
against JDBC Connection".

Closes #732

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
build: bump yaci-store-assets-ext to 2.1.0-pre4-d358232-SNAPSHOT
Picks up the upstream CIP-68 batch-query optimization
(bloxbean/yaci-store commit d358232b8) which replaces the per-subject
N+1 pattern in TokenQueryService.prefetchBatch with a single
window-function query.

Only affects the yaci-indexer classpath — the api module uses its
own JPA entities and its own MetadataReferenceNftRepository batch
query, so no behavioral change on the read path.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
docs: explain the @Transactional / StructuredTaskScope trade-off
The fork/join block in queryMetadataBatch is deliberate: parallelising
the two CIP-26 queries (metadata + logos) saves a round-trip at the
cost of losing the outer readOnly transaction context inside the
forked virtual threads. Since CIP-26 rows are only written during
the periodic offchain token-registry sync, the read-skew risk is
negligible for our use case.

Document the caveat, the reasoning, and the revert recipe so a
future reader is not surprised when they notice that each fork
opens its own transaction.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
perf: batch CIP-68 reference NFT lookups with window-function query
Replace the N+1 per-subject CIP-68 query pattern in TokenQueryService
with a single native SQL call using ROW_NUMBER() OVER (PARTITION BY
policy_id, asset_name ORDER BY slot DESC) + row-value tuple IN.

Before: 1 (or 2 with logos) CIP-26 queries + K individual CIP-68
queries, where K = number of CIP-68 fungible tokens in the batch.
A wallet with 50 CIP-68 tokens triggered 51-52 round-trips.

After: 2 or 3 queries total (CIP-26 metadata, CIP-26 logos when
enabled, CIP-68 latest-by-slot) regardless of batch size. The CIP-68
query is skipped entirely when no fungible token candidates are
present.

Implementation uses Spring Data JPA's custom repository fragment
pattern (MetadataReferenceNftRepositoryCustom + Impl suffix,
auto-detected). Dynamic SQL builds the correct number of positional
placeholders at call time; values are bound via Query.setParameter
so there is no SQL injection surface. The native query is portable
across H2 2.2.x and PostgreSQL 12+.

The derived method findFirstByPolicyIdAndAssetNameAndLabelOrderBySlotDesc
had only one production caller and is removed. Test stubs are updated
to mock the new batch method; ArgumentCaptor verifies that the
fungible-to-reference-NFT prefix conversion still produces the
correct PolicyAssetPair input.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
refactor: cleanup issues 4-7 from review (types, logo format, decimals)
#4/#7 Pass AssetFingerprint through instead of re-parsing subject strings.
TokenQueryService.queryMetadataBatch now accepts Collection<AssetFingerprint>
directly and returns Map<AssetFingerprint, TokenRegistryCurrencyData>. The
CIP-68 prefix check uses fingerprint.getSymbol() instead of substring
gymnastics on the concatenated subject. TokenRegistryServiceImpl's delegation
drops the dual subjects-list + subjectToPolicyId-map plumbing and no longer
calls toSubject() twice per asset.

#5 Detect CIP-68 logo format from the value instead of hardcoding URL.
Known URL schemes (http://, https://, ipfs://, ar://) map to URL; everything
else (raw base64, data: URIs) maps to BASE64. Adds 5 new tests for the
detection logic covering each case.

#6 Consistent decimals policy in TokenQueryService: never default null to 0
in the query layer — let the downstream mapper
(AccountMapperUtil.getDecimalsWithFallback) provide the fallback. CIP-26 and
CIP-68 paths now behave the same for null decimals.

TokenRegistryServiceImplTest simplified: dropped the fallback-when-missing
test since that behavior is owned by TokenQueryService and already covered
by its tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
refactor: remove dead queryMetadata single-subject path
Drop the unused TokenQueryService.queryMetadata() method and its
private applyCip26(builder, cip26, subject) helper. The method was
never called from production code — all paths go through
queryMetadataBatch, including single-subject use cases.

Rewrites the 18 TokenQueryServiceTest call sites to exercise the
batch path with single-element lists, preserving full coverage of
CIP merge semantics, prefix conversion, and logo handling.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
test: add tests for TokenRegistryMapper and DataMapper
TokenRegistryMapper (14 tests):
- Full field mapping, null handling, all-null fields
- Logo format mapping: BASE64, URL, null format, null value
- End-to-end: CIP-26/CIP-68 logos through full mapping pipeline

DataMapper (7 tests):
- ADA amount creation with null symbol
- Native token amount with metadata
- Logo passthrough for both BASE64 and URL formats
- Null metadata graceful handling
- Version passthrough
- Value negation for spent/unspent

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
test: add extensive unit tests for TokenQueryService
23 tests covering:
- Single subject query: empty results, CIP-26 only, CIP-68 only,
  full override, partial override, null decimals default
- CIP-68 prefix conversion: non-fungible prefix skipped, plain asset
  name skipped, short subject skipped, correct 0014df10->000643b0
  conversion, bare prefix edge case
- Logo handling: disabled flag, CIP-26 BASE64 logo, CIP-68 URL logo,
  CIP-68 overrides CIP-26 logo, disabled suppresses CIP-68 logo,
  null logo graceful handling
- Batch queries: multiple subjects, missing subjects fallback,
  batch logo fetch, batch CIP-68 override, null logo filtering

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
refactor: use own JPA entities with TokenQueryService abstraction
Remove yaci-store-assets-ext dependency from API module. Instead, create
own JPA entities (TokenMetadataEntity, TokenLogoEntity, MetadataReferenceNftEntity)
mapping to the same tables, following the existing project pattern.

Introduce TokenQueryService with @Transactional(readOnly=true) that abstracts
CIP-26/CIP-68 query details and priority-based merge. TokenRegistryServiceImpl
is now a thin delegate that only handles asset extraction from Rosetta domain
objects and forwards metadata lookup to TokenQueryService.

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
refactor: replace REST token registry with DB-based assets-ext StorageReaders
Replace the HTTP-based token metadata fetching (tokens.cardano.org REST API)
with direct database reads via yaci-store assets-ext CIP-26/CIP-68 StorageReaders.
The yaci-indexer now syncs token metadata into local DB tables, and the API module
reads from them directly — eliminating external REST dependency, caching layer,
and improving reliability.

Key changes:
- Add yaci-store-assets-ext extension to both api and yaci-indexer modules
- Create AssetsExtReadOnlyConfiguration for API (read-only, no processors/cron jobs)
- Rewrite TokenRegistryServiceImpl to use Cip26/Cip68 StorageReaders
- Delete HTTP gateway, domain models, Guava token cache and related tests
- Include pre-release jars in libs/ as file-based Maven repo (temporary)
- Keep TOKEN_REGISTRY_LOGO_FETCH toggle for logo enable/disable

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>