Skip to content

fix: allow nullable owner in Nft record and handle null account_id for burned NFTs#241

Open
Twiineenock wants to merge 3 commits into
hiero-ledger:mainfrom
Twiineenock:feat/accIdCanBeNull
Open

fix: allow nullable owner in Nft record and handle null account_id for burned NFTs#241
Twiineenock wants to merge 3 commits into
hiero-ledger:mainfrom
Twiineenock:feat/accIdCanBeNull

Conversation

@Twiineenock

@Twiineenock Twiineenock commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

The Hedera mirror node returns "account_id": null for burned NFTs — this is correct,
documented behaviour. A burned NFT still exists as an on-chain record but has no owner.

Previously, MirrorNodeJsonConverterImpl.toNft() called
AccountId.fromString(node.get("account_id").asText()) without a null check, causing
AccountId.fromString("null") to throw, which propagated as a JsonParseException (500)
whenever a token's NFT list contained burned serial numbers.

Changes:

  • Allow @Nullable AccountId owner in Nft.java — accurately models the Hedera data model
    where burned NFTs have no owner
  • Add null check in Spring MirrorNodeJsonConverterImpl.toNft() before parsing account_id
  • Add null check in Microprofile MirrorNodeJsonConverterImpl.toNft() for consistency

Related issue(s):

Fixes #237

Notes for reviewer:

The fix is consistent with how every other nullable account field is already handled in the
same files (e.g. receiver_account_id, collector_account_id all have null guards).
account_id on the NFT object was the only field missing this guard.

Screenshot

image

Checklist

  • Documented (code comments explaining why account_id can be null)
  • Tested (unit, integration, etc.)

…r burned NFTs

Signed-off-by: Twiineenock <twiineenockfox@gmail.com>
@Twiineenock Twiineenock requested review from a team as code owners July 9, 2026 20:51
@Twiineenock Twiineenock marked this pull request as draft July 9, 2026 20:52
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown

Review Change Stack

Note

.coderabbit.yml has unrecognized properties

CodeRabbit is using all valid settings from your configuration. Unrecognized properties (listed below) have been ignored and may indicate typos or deprecated fields that can be removed.

⚠️ Parsing warnings (1)
Validation error: Unrecognized key: "path_instructions"
⚙️ Configuration instructions
  • Please see the configuration documentation for more information.
  • You can also validate your configuration using the online YAML validator.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Walkthrough

The Nft record now permits null owners for burned NFTs. Both mirror-node JSON converters preserve null account_id values, and tests cover nullable construction, validation, and NFT JSON parsing.

Changes

Nullable NFT Owner Support

Layer / File(s) Summary
Nft record contract and validation tests
hiero-enterprise-base/src/main/java/org/hiero/base/data/Nft.java, hiero-enterprise-base/src/test/java/org/hiero/base/test/NftTest.java
owner is annotated as nullable, burned NFTs are documented, owner null validation is removed, and constructor/accessor validation is tested.
Mirror-node null owner parsing and coverage
hiero-enterprise-microprofile/.../MirrorNodeJsonConverterImpl.java, hiero-enterprise-spring/.../MirrorNodeJsonConverterImpl.java, hiero-enterprise-spring/.../MirrorNodeJsonConverterImplTest.java
Both converters handle JSON-null account_id; tests cover live and burned NFTs, empty inputs, missing arrays, and null-node handling.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant MirrorNode
  participant MirrorNodeJsonConverterImpl
  participant Nft
  participant NftTest

  MirrorNode->>MirrorNodeJsonConverterImpl: NFT JSON with account_id null
  MirrorNodeJsonConverterImpl->>MirrorNodeJsonConverterImpl: Detect null account_id
  MirrorNodeJsonConverterImpl->>Nft: Construct Nft(owner=null)
  Nft-->>MirrorNodeJsonConverterImpl: Nullable Nft instance
  NftTest->>Nft: Validate nullable owner and constructor rules
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 13.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main fix: supporting nullable NFT owners and null account_id parsing for burned NFTs.
Description check ✅ Passed The description is directly related to the changes and accurately describes the burned NFT null-handling fix.
Linked Issues check ✅ Passed The changes address #237 by preventing null account_id parsing failures and adding coverage for burned NFTs.
Out of Scope Changes check ✅ Passed The added nullable model change, parser updates, and tests all support the burned NFT fix and do not appear out of scope.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

…ull account_id handling

Signed-off-by: Twiineenock <twiineenockfox@gmail.com>
…ConverterImplTest

Signed-off-by: Twiineenock <twiineenockfox@gmail.com>
@Twiineenock Twiineenock marked this pull request as ready for review July 10, 2026 11:49

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Pro

Run ID: c367ee47-a04b-4b1a-93f1-d39faea3eb81

📥 Commits

Reviewing files that changed from the base of the PR and between ffa5603 and 5450510.

📒 Files selected for processing (2)
  • hiero-enterprise-base/src/test/java/org/hiero/base/test/NftTest.java
  • hiero-enterprise-spring/src/test/java/org/hiero/spring/test/MirrorNodeJsonConverterImplTest.java

Comment on lines +42 to +60
@Test
void toNft_withValidAccountId_returnsNftWithOwner() throws Exception {
// given — a live NFT with a non-null account_id
final ObjectNode node = objectMapper.createObjectNode();
node.put("token_id", TOKEN_ID);
node.put("account_id", ACCOUNT_ID);
node.put("serial_number", SERIAL);
node.put("metadata", METADATA_BASE64);

// when
final Optional<Nft> result = converter.toNft(node);

// then
Assertions.assertTrue(result.isPresent());
final Nft nft = result.get();
Assertions.assertEquals(TokenId.fromString(TOKEN_ID), nft.tokenId());
Assertions.assertEquals(SERIAL, nft.serial());
Assertions.assertEquals(AccountId.fromString(ACCOUNT_ID), nft.owner());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider asserting metadata round-trip in the toNft tests.

Both toNft_withValidAccountId_returnsNftWithOwner and toNft_withNullAccountId_returnsNftWithNullOwner set METADATA_BASE64 in the JSON node but don't verify that nft.metadata() decodes back to the original METADATA bytes. The converter uses node.get("metadata").binaryValue() (base64 decode), so a regression in that decoding path would go undetected.

🧪 Suggested addition
     Assertions.assertEquals(SERIAL, nft.serial());
     Assertions.assertEquals(AccountId.fromString(ACCOUNT_ID), nft.owner());
+    Assertions.assertArrayEquals(METADATA, nft.metadata());
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Test
void toNft_withValidAccountId_returnsNftWithOwner() throws Exception {
// given — a live NFT with a non-null account_id
final ObjectNode node = objectMapper.createObjectNode();
node.put("token_id", TOKEN_ID);
node.put("account_id", ACCOUNT_ID);
node.put("serial_number", SERIAL);
node.put("metadata", METADATA_BASE64);
// when
final Optional<Nft> result = converter.toNft(node);
// then
Assertions.assertTrue(result.isPresent());
final Nft nft = result.get();
Assertions.assertEquals(TokenId.fromString(TOKEN_ID), nft.tokenId());
Assertions.assertEquals(SERIAL, nft.serial());
Assertions.assertEquals(AccountId.fromString(ACCOUNT_ID), nft.owner());
}
`@Test`
void toNft_withValidAccountId_returnsNftWithOwner() throws Exception {
// given — a live NFT with a non-null account_id
final ObjectNode node = objectMapper.createObjectNode();
node.put("token_id", TOKEN_ID);
node.put("account_id", ACCOUNT_ID);
node.put("serial_number", SERIAL);
node.put("metadata", METADATA_BASE64);
// when
final Optional<Nft> result = converter.toNft(node);
// then
Assertions.assertTrue(result.isPresent());
final Nft nft = result.get();
Assertions.assertEquals(TokenId.fromString(TOKEN_ID), nft.tokenId());
Assertions.assertEquals(SERIAL, nft.serial());
Assertions.assertEquals(AccountId.fromString(ACCOUNT_ID), nft.owner());
Assertions.assertArrayEquals(METADATA, nft.metadata());
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

MirrorNodeJsonConverterImpl.toNft() throws when account_id is null for burned/deleted NFTs

1 participant