Skip to content

feat: add first-class ARC-403 interface packages - #16

Merged
IlyasRidhuan merged 3 commits into
stack/housekeepingfrom
stack/arc403-interface
Aug 12, 2026
Merged

feat: add first-class ARC-403 interface packages#16
IlyasRidhuan merged 3 commits into
stack/housekeepingfrom
stack/arc403-interface

Conversation

@alejoamiras

@alejoamiras alejoamiras commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

What

Adds first-class ARC-403 interface packages and switches production contracts to them:

  • arc403_interface → contract Arc403Authorizer: the fungible-token hook (authorize_private / authorize_public with (from, amount, selector)).
  • arc403_interface/multitoken → contract Arc403MultiTokenAuthorizer: the id-bearing variant (Noir allows one contract per package, hence the sub-package).
  • Token and MultiToken now import the interface packages instead of their test spy contracts; the spies remain, test-only.
  • Token README points implementers at the interface package; the previous pointer was a 404 (defi-wonderland/aztec-arc403-extensions).

Why

ARC-403 is the standard with active ecosystem demand, and the only in-repo definition of its hook interface was a test mock that production code imported directly. Implementers now get a canonical, importable artifact. The interface is loudly labeled draft — it tracks the ARC-403 forum discussion, which is still evolving.

Notes

  • Function names/signatures are identical to the spies, and selectors hash only name(param_types) — call behavior is unchanged. Token bytecode is bit-identical to main; MultiToken's private note-spending functions show benign bytecode renumbering from the added dependency (class ID changes, as it does on every aztec version bump).
  • Stub bodies are empty because barretenberg cannot derive VKs for assert(false) circuits. The root README (which ships in the npm package) carries a CAUTION: never deploy the stubs — an empty hook approves everything.
  • Codex adversarial review: no functional findings; its "ship the warning in the npm package" flag is addressed in the second commit.

Validation

aztec compile (13 artifacts) ✓ · full token_contract (79) and multitoken_contract (77) Noir suites ✓ · 28 authorization tests exercise the hook end-to-end against the spies.

🤖 Generated with Claude Code

@alejoamiras alejoamiras changed the title stack/arc403 interface feat: add first-class ARC-403 interface packages Aug 11, 2026
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

alejoamiras and others added 3 commits August 12, 2026 17:40
The only in-repo definition of the ARC-403 hook interface was the test
spy contract, which production Token/MultiToken imported directly, and
the README pointed implementers at a reference repo that no longer
exists (404). Add canonical interface packages:

- arc403_interface (Arc403Authorizer): the fungible-token hook
- arc403_interface/multitoken (Arc403MultiTokenAuthorizer): the
  id-bearing variant (Noir allows one contract per package)

Token and MultiToken now import the interface packages; the test spies
stay test-only. Bodies are empty stubs (bb cannot derive VKs for
assert(false) circuits); READMEs and doc comments warn loudly that
deploying a stub yields an allow-all authorizer.

Function names and signatures are unchanged, so selectors and behavior
are identical — verified by the 28 existing authorization tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Codex review flagged that the interface packages' warning only lived in
src/arc403_interface/README.md, which is not part of the published npm
package — consumers would see deployable stub artifacts with no notice.
The root README ships; put the warning there.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Reviewers reasonably ask whether an empty-bodied contract-as-interface
is idiomatic Aztec Noir or Solidity thinking. It is precedented:
aztec-packages ships the same pattern in
noir-contracts/contracts/protocol_interface (FeeJuice,
ContractInstanceRegistry). Cite that lineage in the crate docs, note
that the macro derives call interfaces from signatures only (bodies are
never read), and record why the raw-FunctionSelector alternative was
rejected.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@IlyasRidhuan
IlyasRidhuan force-pushed the stack/arc403-interface branch from f153881 to 5278d33 Compare August 12, 2026 16:40
@IlyasRidhuan
IlyasRidhuan merged commit 9dd82d5 into main Aug 12, 2026
9 of 13 checks passed
@alejoamiras
alejoamiras deleted the stack/arc403-interface branch August 12, 2026 17:00
@github-actions

Copy link
Copy Markdown

Benchmark Comparison

CPU Cores RAM Arch
AMD EPYC 9V74 80-Core Processor 4 16 GiB x64

Contract: escrow

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
(partial) withdraw 486,936 486,936 3,744 3,744 587,800 587,800 15,014 11,780 -3,234 (-21.5%)
withdraw 331,210 331,210 832 832 499,700 499,700 12,195 9,505 -2,690 (-22.1%)
withdraw_nft 378,517 378,517 1,440 1,440 527,400 527,400 13,379 10,465 -2,914 (-21.8%)

Contract: logic

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
get_escrow 390,359 390,359 192 192 456,000 456,000 13,589 10,565 -3,024 (-22.3%)
secret_key_to_public_keys 387,718 387,718 192 192 456,000 456,000 13,514 10,568 -2,946 (-21.8%)
share_escrow 303,989 303,989 1,952 1,952 520,700 520,700 11,991 9,270 -2,721 (-22.7%)

Contract: multitoken

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
burn_private 260,810 260,810 832 832 499,700 499,700 10,934 8,482 -2,452 (-22.4%)
burn_public 223,823 223,823 416 416 638,292 638,292 10,500 8,166 -2,334 (-22.2%)
initialize_transfer_commitment 225,077 225,077 768 768 474,500 474,500 10,443 8,142 -2,301 (-22.0%)
mint_to_private 297,577 297,577 1,408 1,408 511,400 511,400 11,823 9,173 -2,650 (-22.4%)
mint_to_public 223,823 223,823 416 416 637,800 637,800 10,504 8,153 -2,351 (-22.4%)
transfer_private_to_commitment 264,328 264,328 1,024 1,024 511,400 511,400 10,987 8,525 -2,462 (-22.4%)
transfer_private_to_private 337,261 337,261 2,048 2,048 555,100 555,100 12,329 9,590 -2,739 (-22.2%)
transfer_private_to_public 307,363 307,363 1,056 1,056 714,677 714,677 11,824 9,186 -2,638 (-22.3%)
transfer_public_to_commitment 223,823 223,823 640 640 663,516 663,516 10,596 8,170 -2,426 (-22.9%)
transfer_public_to_private 298,128 298,128 1,024 1,024 683,958 683,958 11,745 9,096 -2,649 (-22.6%)
transfer_public_to_public 223,823 223,823 480 480 673,505 673,505 10,573 8,259 -2,314 (-21.9%)

Contract: nft

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
burn_private 275,257 275,257 416 416 661,052 661,052 11,444 8,844 -2,600 (-22.7%)
burn_public 223,823 223,823 448 448 670,034 670,034 10,505 8,138 -2,367 (-22.5%)
mint_to_private 341,050 341,050 1,600 1,600 735,342 735,342 12,494 9,629 -2,865 (-22.9%)
mint_to_public 223,823 223,823 448 448 670,718 670,718 10,559 8,109 -2,450 (-23.2%)
transfer_private_to_private 249,719 249,719 832 832 499,700 499,700 10,793 8,363 -2,430 (-22.5%)
transfer_private_to_public 275,286 275,286 416 416 659,258 659,258 11,425 8,873 -2,552 (-22.3%)
transfer_public_to_private 292,460 292,460 992 992 683,268 683,268 11,661 9,031 -2,630 (-22.6%)
transfer_public_to_public 223,823 223,823 384 384 633,285 633,285 10,629 8,149 -2,480 (-23.3%)

Contract: token

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
burn_private 303,652 303,652 1,024 1,024 713,843 713,843 11,829 9,189 -2,640 (-22.3%)
burn_public 223,823 223,823 448 448 672,518 672,518 10,532 8,165 -2,367 (-22.5%)
initialize_transfer_commitment 225,077 225,077 768 768 474,500 474,500 10,473 8,102 -2,371 (-22.6%)
mint_to_private 355,289 355,289 2,144 2,144 737,956 737,956 12,668 9,857 -2,811 (-22.2%)
mint_to_public 223,823 223,823 448 448 672,113 672,113 10,473 8,251 -2,222 (-21.2%)
transfer_private_to_commitment 260,673 260,673 992 992 511,400 511,400 10,995 8,519 -2,476 (-22.5%)
transfer_private_to_private 344,855 344,855 2,592 2,592 557,600 557,600 12,586 9,705 -2,881 (-22.9%)
transfer_private_to_public 303,708 303,708 1,024 1,024 713,990 713,990 11,849 9,193 -2,656 (-22.4%)
transfer_private_to_public_with_commitment 308,509 308,509 1,600 1,600 747,290 747,290 11,905 9,217 -2,688 (-22.6%)
transfer_public_to_commitment 223,823 223,823 576 576 662,496 662,496 10,594 8,175 -2,419 (-22.8%)
transfer_public_to_private 296,092 296,092 992 992 683,271 683,271 11,782 9,121 -2,661 (-22.6%)
transfer_public_to_public 223,823 223,823 448 448 672,515 672,515 10,539 8,160 -2,379 (-22.6%)

Contract: vault

🚦 Function Gates DA Gas L2 Gas Proving Time (ms)
Base PR Diff Base PR Diff Base PR Diff Base PR Diff
deposit_private_to_private 508,340 508,340 1,312 1,312 878,035 878,035 15,356 11,935 -3,421 (-22.3%)
deposit_private_to_private_exact 612,065 612,065 1,888 1,888 915,103 915,103 17,507 13,524 -3,983 (-22.8%)
deposit_private_to_public 436,085 436,085 768 768 862,060 862,060 14,198 11,027 -3,171 (-22.3%)
deposit_public_to_private 424,713 424,713 1,984 1,984 964,832 964,832 14,083 10,911 -3,172 (-22.5%)
deposit_public_to_private_exact 424,724 424,724 1,952 1,952 949,256 949,256 14,124 10,909 -3,215 (-22.8%)
deposit_public_to_public 223,823 223,823 832 832 897,350 897,350 10,544 8,169 -2,375 (-22.5%)
issue_private_to_private_exact 612,065 612,065 1,888 1,888 915,763 915,763 17,541 13,507 -4,034 (-23.0%)
issue_private_to_public_exact 484,820 484,820 1,344 1,344 899,785 899,785 15,067 11,683 -3,384 (-22.5%)
issue_public_to_private 346,165 346,165 1,376 1,376 921,448 921,448 12,542 9,724 -2,818 (-22.5%)
issue_public_to_public 223,823 223,823 832 832 898,040 898,040 10,495 8,129 -2,366 (-22.5%)
redeem_private_to_private_exact 614,859 614,859 1,888 1,888 915,610 915,610 17,472 13,587 -3,885 (-22.2%)
redeem_private_to_public 436,028 436,028 768 768 862,504 862,504 14,249 11,016 -3,233 (-22.7%)
redeem_public_to_private_exact 427,575 427,575 1,952 1,952 949,577 949,577 14,156 11,027 -3,129 (-22.1%)
redeem_public_to_public 223,823 223,823 832 832 897,872 897,872 10,552 8,161 -2,391 (-22.7%)
withdraw_private_to_private 511,134 511,134 1,312 1,312 878,245 878,245 15,392 11,960 -3,432 (-22.3%)
withdraw_private_to_private_exact 614,859 614,859 1,888 1,888 915,556 915,556 17,545 13,546 -3,999 (-22.8%)
withdraw_private_to_public_exact 484,763 484,763 1,344 1,344 900,019 900,019 15,040 11,701 -3,339 (-22.2%)
withdraw_public_to_private 440,832 440,832 2,528 2,528 967,143 967,143 14,354 11,113 -3,241 (-22.6%)
withdraw_public_to_public 223,823 223,823 832 832 898,175 898,175 10,502 8,132 -2,370 (-22.6%)

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.

2 participants