Skip to content

Conversation

@wei3erHase
Copy link
Member

@wei3erHase wei3erHase commented Jun 25, 2025

🤖 Linear

Closes AZT-225

Description

Summary by CodeRabbit

  • Refactor

    • Updated NFT ownership logic to use a special private owner address instead of a separate existence flag, simplifying how private ownership and token existence are tracked.
    • Adjusted related methods and internal logic to consistently use the new private owner address convention.
  • Tests

    • Updated test assertions to check for the new private owner address instead of the zero address.
    • Introduced a constant for the private owner address in both contract and test code.
  • New Features

    • Added a globally accessible constant representing the private owner address for NFTs.

@linear
Copy link

linear bot commented Jun 25, 2025

@github-actions
Copy link

Benchmark Comparison

Contract: token

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 345,492 345,492 13,517 13,517 53,578 53,578
burn_public 214,216 214,216 2,253 2,253 67,694 67,694
mint_to_private 339,185 339,185 12,954 12,954 47,594 47,594
mint_to_public 214,216 214,216 2,253 2,253 68,539 68,539
transfer_private_to_private 356,647 356,647 24,218 24,218 20,909 20,909
transfer_private_to_public 345,572 345,572 13,517 13,517 66,686 66,686
transfer_private_to_public_with_commitment 371,620 371,620 24,781 24,781 72,564 72,564
transfer_public_to_private 335,644 335,644 12,954 12,954 60,860 60,860
transfer_public_to_public 214,216 214,216 2,253 2,253 81,043 81,043

@github-actions
Copy link

Benchmark Comparison

Contract: token

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 345,492 345,492 13,517 13,517 53,578 53,578
burn_public 214,216 214,216 2,253 2,253 67,694 67,694
mint_to_private 339,185 339,185 12,954 12,954 47,594 47,594
mint_to_public 214,216 214,216 2,253 2,253 68,539 68,539
transfer_private_to_private 356,647 356,647 24,218 24,218 20,909 20,909
transfer_private_to_public 345,572 345,572 13,517 13,517 66,686 66,686
transfer_private_to_public_with_commitment 371,620 371,620 24,781 24,781 72,564 72,564
transfer_public_to_private 335,644 335,644 12,954 12,954 60,860 60,860
transfer_public_to_public 214,216 214,216 2,253 2,253 81,043 81,043

@wei3erHase
Copy link
Member Author

waiting for nft benchmark

@github-actions
Copy link

Benchmark Comparison

Contract: token

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 345,492 345,492 13,517 13,517 53,578 53,578
burn_public 214,216 214,216 2,253 2,253 67,694 67,694
mint_to_private 339,185 339,185 12,954 12,954 47,594 47,594
mint_to_public 214,216 214,216 2,253 2,253 68,539 68,539
transfer_private_to_private 356,647 356,647 24,218 24,218 20,909 20,909
transfer_private_to_public 345,572 345,572 13,517 13,517 66,686 66,686
transfer_private_to_public_with_commitment 371,620 371,620 24,781 24,781 72,564 72,564
transfer_public_to_private 335,644 335,644 12,954 12,954 60,860 60,860
transfer_public_to_public 214,216 214,216 2,253 2,253 81,043 81,043

@wei3erHase wei3erHase changed the title feat: deprecating nft_exists mapping (opus PoC) feat: deprecating nft_exists mapping Jun 26, 2025
@github-actions
Copy link

Benchmark Comparison

Contract: token

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 345,492 345,492 13,517 13,517 53,578 53,578
burn_public 214,216 214,216 2,253 2,253 67,694 67,694
mint_to_private 339,185 339,185 12,954 12,954 47,594 47,594
mint_to_public 214,216 214,216 2,253 2,253 68,539 68,539
transfer_private_to_private 356,647 356,647 24,218 24,218 20,909 20,909
transfer_private_to_public 345,572 345,572 13,517 13,517 66,686 66,686
transfer_private_to_public_with_commitment 371,620 371,620 24,781 24,781 72,564 72,564
transfer_public_to_private 335,644 335,644 12,954 12,954 60,860 60,860
transfer_public_to_public 214,216 214,216 2,253 2,253 81,043 81,043

@github-actions
Copy link

Benchmark Comparison

Contract: nft

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 348,886 348,809 -77 (-0.0%) 2,816 2,816 44,502 43,525 -977 (-2.2%)
🟢 burn_public 237,823 237,823 3,380 2,253 -1,127 (-33.3%) 51,581 43,739 -7,842 (-15.2%)
mint_to_private 369,272 369,194 -78 (-0.0%) 13,517 13,517 44,218 43,218 -1,000 (-2.3%)
🟢 mint_to_public 237,823 237,823 3,380 2,253 -1,127 (-33.3%) 57,811 49,910 -7,901 (-13.7%)
transfer_private_to_private 338,154 338,154 12,954 12,954 5,296 5,296
🔴 transfer_private_to_public 348,889 348,814 -75 (-0.0%) 2,816 2,816 37,886 44,862 +6,976 (+18.4%)
transfer_public_to_private 365,760 365,761 +1 (+0.0%) 13,517 13,517 44,618 44,611 -7 (-0.0%)
transfer_public_to_public 237,823 237,823 2,253 2,253 43,782 43,775 -7 (-0.0%)

Contract: token

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 376,154 376,154 14,081 14,081 34,484 34,484
burn_public 237,823 237,823 3,380 3,380 47,307 47,307
mint_to_private 369,997 369,997 13,517 13,517 32,711 32,711
mint_to_public 237,823 237,823 3,380 3,380 53,313 53,313
transfer_private_to_private 370,828 370,828 24,218 24,218 6,706 6,706
transfer_private_to_public 376,234 376,234 14,081 14,081 46,843 46,843
transfer_private_to_public_with_commitment 402,416 402,416 25,345 25,345 48,504 48,504
transfer_public_to_private 366,485 366,485 13,517 13,517 45,238 45,238
transfer_public_to_public 237,823 237,823 3,380 3,380 59,296 59,296

1 similar comment
@github-actions
Copy link

Benchmark Comparison

Contract: nft

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 348,886 348,809 -77 (-0.0%) 2,816 2,816 44,502 43,525 -977 (-2.2%)
🟢 burn_public 237,823 237,823 3,380 2,253 -1,127 (-33.3%) 51,581 43,739 -7,842 (-15.2%)
mint_to_private 369,272 369,194 -78 (-0.0%) 13,517 13,517 44,218 43,218 -1,000 (-2.3%)
🟢 mint_to_public 237,823 237,823 3,380 2,253 -1,127 (-33.3%) 57,811 49,910 -7,901 (-13.7%)
transfer_private_to_private 338,154 338,154 12,954 12,954 5,296 5,296
🔴 transfer_private_to_public 348,889 348,814 -75 (-0.0%) 2,816 2,816 37,886 44,862 +6,976 (+18.4%)
transfer_public_to_private 365,760 365,761 +1 (+0.0%) 13,517 13,517 44,618 44,611 -7 (-0.0%)
transfer_public_to_public 237,823 237,823 2,253 2,253 43,782 43,775 -7 (-0.0%)

Contract: token

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 376,154 376,154 14,081 14,081 34,484 34,484
burn_public 237,823 237,823 3,380 3,380 47,307 47,307
mint_to_private 369,997 369,997 13,517 13,517 32,711 32,711
mint_to_public 237,823 237,823 3,380 3,380 53,313 53,313
transfer_private_to_private 370,828 370,828 24,218 24,218 6,706 6,706
transfer_private_to_public 376,234 376,234 14,081 14,081 46,843 46,843
transfer_private_to_public_with_commitment 402,416 402,416 25,345 25,345 48,504 48,504
transfer_public_to_private 366,485 366,485 13,517 13,517 45,238 45,238
transfer_public_to_public 237,823 237,823 3,380 3,380 59,296 59,296

@coderabbitai
Copy link

coderabbitai bot commented Jul 21, 2025

📝 Walkthrough

Walkthrough

This change removes the nft_exists boolean map from the NFT contract's storage and replaces its functionality with a convention: the presence of a special constant, PRIVATE_OWNER_ADDRESS, as the public owner indicates both token existence and private ownership. All logic for minting, burning, and transferring NFTs is updated to use this sentinel value instead of checking or setting nft_exists. Supporting changes include the introduction of PRIVATE_OWNER_ADDRESS in both contract and test code, updates to test assertions and utility functions to use this new convention, and the addition of a new constants module. Method signatures and internal logic are refactored to align with this approach.

Estimated code review effort

4 (~90 minutes)

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

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 details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1051d73 and b9c1cdb.

📒 Files selected for processing (8)
  • src/nft_contract/src/main.nr (12 hunks)
  • src/nft_contract/src/test/mint_to_private.nr (2 hunks)
  • src/nft_contract/src/test/transfer_private_to_public.nr (3 hunks)
  • src/nft_contract/src/test/transfer_public_to_private.nr (3 hunks)
  • src/nft_contract/src/test/utils.nr (2 hunks)
  • src/nft_contract/src/types.nr (1 hunks)
  • src/nft_contract/src/types/constants.nr (1 hunks)
  • src/ts/test/nft.test.ts (3 hunks)
🔇 Additional comments (20)
src/nft_contract/src/types/constants.nr (1)

1-4: LGTM! Well-designed sentinel constant.

The PRIVATE_OWNER_ADDRESS constant is properly implemented using deterministic hashing of a descriptive string. This approach ensures a collision-resistant sentinel value that's distinct from legitimate addresses while being consistently reproducible across all usage contexts.

src/nft_contract/src/types.nr (1)

2-2: LGTM! Proper module declaration.

The addition of the constants module declaration correctly exposes the new constant definitions for use throughout the contract codebase.

src/nft_contract/src/test/mint_to_private.nr (2)

3-3: LGTM! Proper import of the new constant.

The import correctly brings in the PRIVATE_OWNER_ADDRESS constant needed for the updated test logic.


16-16: LGTM! Test updated to reflect new private ownership semantics.

The assertion correctly verifies that after minting to private, the public owner is set to PRIVATE_OWNER_ADDRESS instead of the zero address, aligning with the new sentinel-based approach for indicating private ownership.

src/nft_contract/src/test/transfer_public_to_private.nr (2)

3-3: LGTM! Proper import of the sentinel constant.

The import correctly brings in PRIVATE_OWNER_ADDRESS to replace the previous zero address usage in ownership assertions.


24-24: LGTM! Assertions updated for new private ownership semantics.

Both test functions correctly verify that after transferring NFTs to private state, the public owner is set to PRIVATE_OWNER_ADDRESS instead of the zero address, properly reflecting the new sentinel-based approach.

Also applies to: 53-53

src/nft_contract/src/test/transfer_private_to_public.nr (2)

3-3: LGTM! Proper import of the sentinel constant.

The import correctly brings in PRIVATE_OWNER_ADDRESS for use in the updated test assertions.


15-15: LGTM! Initial state verifications updated correctly.

Both assertions properly verify that NFTs in private state have PRIVATE_OWNER_ADDRESS as their public owner before transferring to public state, correctly reflecting the new sentinel-based private ownership semantics.

Also applies to: 40-40

src/ts/test/nft.test.ts (2)

19-22: LGTM! Constant definition aligns with contract implementation.

The PRIVATE_OWNER_ADDRESS constant is correctly defined using the Poseidon hash of "PRIVATE_OWNER", matching the contract's implementation.


560-560: Correct usage of PRIVATE_OWNER_ADDRESS sentinel value.

The assertions correctly expect PRIVATE_OWNER_ADDRESS as the public owner when NFTs are transferred to private ownership, aligning with the new ownership model.

Also applies to: 608-608

src/nft_contract/src/test/utils.nr (1)

87-101: Correct implementation of token existence check.

The function properly determines token existence by checking if the public owner is non-zero, which aligns with the new ownership model after removing the nft_exists mapping.

src/nft_contract/src/main.nr (9)

36-37: Correct import of PRIVATE_OWNER_ADDRESS constant.

The constant is properly imported to support the new ownership model.


93-94: Correct usage of PRIVATE_OWNER_ADDRESS in private-to-public transfers.

The functions correctly update the public owner from PRIVATE_OWNER_ADDRESS to the new owner, properly reflecting the transition from private to public ownership.

Also applies to: 115-116


180-180: Correct implementation of public-to-private transfer.

The function properly sets the public owner to PRIVATE_OWNER_ADDRESS when transferring to private ownership, maintaining consistency with the new ownership model.

Also applies to: 195-196


309-317: Well-implemented mint_private_internal function.

The function correctly ensures the token doesn't exist (zero owner) before setting the owner to PRIVATE_OWNER_ADDRESS, maintaining the ownership invariants.


319-326: Proper implementation of burn_from_private_internal.

The function correctly validates that the token is privately owned (PRIVATE_OWNER_ADDRESS) before burning it by setting the owner to zero.


396-397: Correct implementation of private minting.

The function properly uses mint_private_internal to set the public owner to PRIVATE_OWNER_ADDRESS, indicating private ownership.


414-416: Proper token existence validation.

The function correctly checks that the token doesn't exist (zero owner) before minting, preventing duplicate token IDs.


434-435: Correct implementation of private burning.

The function properly uses burn_from_private_internal to set the public owner to zero, indicating the token no longer exists.


36-37: Confirm PRIVATE_OWNER_ADDRESS is unreachable by any valid key derivation

I see that PRIVATE_OWNER_ADDRESS is computed as:

pub global PRIVATE_OWNER_ADDRESS: AztecAddress =
    AztecAddress::from_field(poseidon2_hash_bytes("PRIVATE_OWNER".as_bytes()));

However, security depends on this field value never being producible by a legitimate account’s address derivation. Please verify the following:

• AztecAddress derivation model
– Ensure normal user addresses are computed via a one-way hash of a public key (e.g., Blake2 or similar), not directly from arbitrary field inputs.
– Confirm that AztecAddress::from_field is not used when deriving real user addresses.

• Collision impossibility
– Validate that no public key → address function can ever output the Poseidon2 hash of "PRIVATE_OWNER".

If both hold, the sentinel is cryptographically unreachable and safe to use. Otherwise, consider switching to an address derived from a one-time random private key (so its public address cannot be reproduced).

_validate_public_token_owner(storage.public_owners, from, token_id);

_update_public_token_owner(storage.public_owners, AztecAddress::zero(), token_id);
_update_public_token_owner(storage.public_owners, PRIVATE_OWNER_ADDRESS, token_id);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

⚠️ Potential issue

Potential state confusion with commitment transfers.

Setting the public owner to PRIVATE_OWNER_ADDRESS during commitment transfers (line 266) could create ambiguity. This address is also used to indicate regular private ownership, making it impossible to distinguish between tokens in commitment state vs. regular private ownership from the public state alone.

Consider using a separate sentinel address for commitment states to maintain clear state separation.

🤖 Prompt for AI Agents
In src/nft_contract/src/main.nr at line 266, the code sets the public owner to
PRIVATE_OWNER_ADDRESS during commitment transfers, which causes ambiguity
between commitment state and regular private ownership. To fix this, define and
use a distinct sentinel address specifically for commitment states instead of
reusing PRIVATE_OWNER_ADDRESS, ensuring clear differentiation in the public
ownership state.

@github-actions
Copy link

Benchmark Comparison

Contract: nft

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 331,755 331,678 -77 (-0.0%) 2,816 2,816 44,502 43,525 -977 (-2.2%)
🟢 burn_public 227,539 227,539 3,380 2,253 -1,127 (-33.3%) 51,581 43,739 -7,842 (-15.2%)
mint_to_private 356,923 356,845 -78 (-0.0%) 13,517 13,517 44,218 43,218 -1,000 (-2.3%)
🟢 mint_to_public 227,539 227,539 3,380 2,253 -1,127 (-33.3%) 57,811 49,910 -7,901 (-13.7%)
transfer_private_to_private 325,803 325,803 12,954 12,954 5,296 5,296
🔴 transfer_private_to_public 331,758 331,683 -75 (-0.0%) 2,816 2,816 37,886 44,862 +6,976 (+18.4%)
transfer_public_to_private 353,411 353,412 +1 (+0.0%) 13,517 13,517 44,618 44,611 -7 (-0.0%)
transfer_public_to_public 227,539 227,539 2,253 2,253 43,782 43,775 -7 (-0.0%)

Contract: token

Function Gates DA Gas L2 Gas
Status Base PR Diff Base PR Diff Base PR Diff
burn_private 363,830 363,830 14,081 14,081 34,484 34,484
burn_public 227,539 227,539 3,380 3,380 47,307 47,307
mint_to_private 357,673 357,673 13,517 13,517 32,711 32,711
mint_to_public 227,539 227,539 3,380 3,380 53,313 53,313
transfer_private_to_private 363,311 363,311 24,218 24,218 6,706 6,706
transfer_private_to_public 363,910 363,910 14,081 14,081 46,843 46,843
transfer_private_to_public_with_commitment 394,872 394,872 25,345 25,345 48,504 48,504
transfer_public_to_private 354,161 354,161 13,517 13,517 45,238 45,238
transfer_public_to_public 227,539 227,539 3,380 3,380 59,296 59,296

@0xShaito 0xShaito closed this Oct 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

3 participants