Skip to content

Add RegisterX402V1 instruction for x402 endpoint registration - #9

Open
blockiosaurus wants to merge 2 commits into
mainfrom
claude/add-x402-registration-X1H9H
Open

Add RegisterX402V1 instruction for x402 endpoint registration#9
blockiosaurus wants to merge 2 commits into
mainfrom
claude/add-x402-registration-X1H9H

Conversation

@blockiosaurus

@blockiosaurus blockiosaurus commented Apr 6, 2026

Copy link
Copy Markdown
Contributor

Adds a new instruction to mpl-agent-tools that allows agent asset owners
to register x402 payment endpoints for easy discovery. The endpoint URL
(up to 128 bytes) is stored in an X402EndpointV1 PDA account derived
from the agent asset address.

Includes:

  • X402EndpointV1 state account (200 bytes, zero-copy Pod)
  • RegisterX402V1 processor with asset ownership validation
  • Generated JS and Rust clients via Shank + Kinobi pipeline
  • Rust SBF tests and JS AVA tests

https://claude.ai/code/session_017W5vUmT8MHsKj5CCj3d6XJ

Summary by CodeRabbit

  • New Features

    • Add X402 endpoint registration for assets with custom URLs
    • Enforce owner authorization, URL length validation, and prevention of duplicate registrations
    • Introduce new X402-related error codes
  • Tests

    • Added JS and Rust integration tests covering success and failure (owner checks, duplicate registration, invalid URL)

Adds a new instruction to mpl-agent-tools that allows agent asset owners
to register x402 payment endpoints for easy discovery. The endpoint URL
(up to 128 bytes) is stored in an X402EndpointV1 PDA account derived
from the agent asset address.

Includes:
- X402EndpointV1 state account (200 bytes, zero-copy Pod)
- RegisterX402V1 processor with asset ownership validation
- Generated JS and Rust clients via Shank + Kinobi pipeline
- Rust SBF tests and JS AVA tests

https://claude.ai/code/session_017W5vUmT8MHsKj5CCj3d6XJ
@coderabbitai

coderabbitai Bot commented Apr 6, 2026

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 3aaad7ec-a9c1-4128-b498-5e2ff90aa3b9

📥 Commits

Reviewing files that changed from the base of the PR and between 8766a94 and 6f967d0.

⛔ Files ignored due to path filters (4)
  • clients/js/src/generated/tools/accounts/x402EndpointV1.ts is excluded by !**/generated/**
  • clients/js/src/generated/tools/instructions/registerX402V1.ts is excluded by !**/generated/**
  • clients/rust-tools/src/generated/accounts/x402_endpoint_v1.rs is excluded by !**/generated/**
  • clients/rust-tools/src/generated/instructions/register_x402_v1.rs is excluded by !**/generated/**
📒 Files selected for processing (6)
  • clients/js/test/tools/registerX402.test.ts
  • clients/rust-tools/tests/register_x402.rs
  • idls/mpl_agent_tools.json
  • programs/mpl-agent-tools/src/processor/mod.rs
  • programs/mpl-agent-tools/src/processor/register_x402_v1.rs
  • programs/mpl-agent-tools/src/state/x402_endpoint_v1.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • clients/js/test/tools/registerX402.test.ts
  • programs/mpl-agent-tools/src/processor/mod.rs
  • programs/mpl-agent-tools/src/processor/register_x402_v1.rs

Walkthrough

Adds X402 endpoint registration: new instruction, on-chain X402EndpointV1 state, PDA derivation/creation logic, error variants, kinobi/IDL entries, and test suites in both JS and Rust validating success and failure cases.

Changes

Cohort / File(s) Summary
Program State & Layout
programs/mpl-agent-tools/src/state/x402_endpoint_v1.rs, programs/mpl-agent-tools/src/state/mod.rs
Introduces X402EndpointV1 state struct, PDA derivation check, account creation and initialization helpers, and registers Key::X402EndpointV1 discriminator.
Processor & Instruction
programs/mpl-agent-tools/src/processor/register_x402_v1.rs, programs/mpl-agent-tools/src/processor/mod.rs, programs/mpl-agent-tools/src/instruction.rs
Adds RegisterX402V1 instruction discriminant/enum, processor routing, and new handler that parses args, validates guards (ownership, PDA, uninitialized account, URL length), creates PDA account, and writes URL bytes.
Errors
programs/mpl-agent-tools/src/error.rs
Adds new error variants: InvalidX402EndpointDerivation, X402EndpointMustBeUninitialized, AssetOwnerMustRegisterX402, InvalidUrlLength (discriminants 14–17).
IDL & Kinobi Config
idls/mpl_agent_tools.json, configs/kinobi-tools.cjs
Adds RegisterX402V1 instruction, RegisterX402V1Args, X402EndpointV1 account type, extends Key enum, new error codes, and Kinobi PDA/account declaration x402EndpointV1 with seeds ["x402_endpoint", asset].
Client Tests
clients/js/test/tools/registerX402.test.ts
Adds AVA tests: successful registration, non-owner registration failure, duplicate registration failure; validates PDA derivation and on-chain fields.
Rust Integration Tests
clients/rust-tools/tests/register_x402.rs
Adds tokio ProgramTest integration tests covering success, custom-authority success, non-owner failure (AssetOwnerMustRegisterX402), and duplicate registration failure (X402EndpointMustBeUninitialized), plus helpers for setup and error assertion.

Sequence Diagram

sequenceDiagram
    participant Client as Client
    participant Proc as Processor
    participant Guard as Validator
    participant Sys as SystemProgram
    participant State as X402EndpointV1

    Client->>Proc: send RegisterX402V1 + accounts + url
    Proc->>Guard: parse args, split header, extract url
    Guard->>Guard: verify x402_endpoint is system-owned & uninitialized
    Guard->>Guard: verify agent_asset is AssetV1 and non-empty
    Guard->>Guard: verify signer == asset.owner (or use payer)
    Guard->>Guard: check PDA derivation (asset seed + bump)
    Guard->>Guard: validate url length > 0
    alt any check fails
        Guard-->>Proc: return MplAgentToolsError::*
        Proc-->>Client: tx fails
    else all checks pass
        Proc->>Sys: create_account on x402_endpoint PDA (space = base + 4 + url_len)
        Sys->>State: allocate & assign owner
        Proc->>State: initialize struct (key, bump, asset, authority)
        Proc->>State: write trailing url length + bytes
        State-->>Proc: account initialized
        Proc-->>Client: tx succeeds
    end
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested Reviewers

  • nhanphan
  • danenbm
  • brandontulsi

Poem

🐰 A tiny rabbit hops with glee,
Registers endpoints beneath a tree,
PDAs snug, URLs tucked in,
Owners sign and let it begin,
Hop hops — the registry sings for me! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add RegisterX402V1 instruction for x402 endpoint registration' clearly and concisely describes the primary change: introduction of a new RegisterX402V1 instruction for x402 endpoint registration.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/add-x402-registration-X1H9H

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

Replaces the fixed-size [u8; 128] URL buffer with the repo's standard
variable-length string pattern: a zero-sized [u8; 0] marker with
#[idl_type("String")] in the args and account structs, with the actual
URL stored as a trailing Borsh string (u32 length + bytes).

This matches the pattern used in RegisterIdentityV1 for
agent_registration_uri and produces cleaner client APIs that accept
native String/string types instead of fixed byte arrays.

https://claude.ai/code/session_017W5vUmT8MHsKj5CCj3d6XJ

@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.

🧹 Nitpick comments (4)
clients/js/test/tools/registerX402.test.ts (2)

49-66: Consider asserting specific error codes in negative tests.

The tests correctly verify that errors are thrown, but don't assert the specific error code. This could help catch regressions where the wrong error is returned.

💡 Optional: Assert specific error messages
// Example for non-owner test
await t.throwsAsync(result, {
  message: /AssetOwnerMustRegisterX402/,
});

Also applies to: 68-90

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/js/test/tools/registerX402.test.ts` around lines 49 - 66, Update the
negative tests to assert the specific error code/message instead of only
checking that an error is thrown: in the test "it cannot register an x402
endpoint if not the asset owner" wrap the call to
registerX402V1(...).sendAndConfirm(umi) with t.throwsAsync expecting the
AssetOwnerMustRegisterX402 error (or its exact message/regex), and apply the
same change to the other failing tests referenced (the block covering lines
68-90) so they assert the precise error codes/messages returned by
registerX402V1.

11-17: Minor: urlToBytes assumes ASCII-only URLs.

The helper uses charCodeAt which returns UTF-16 code units. For URLs with non-ASCII characters (e.g., internationalized domain names), this would produce incorrect byte sequences. Since x402 endpoint URLs are typically ASCII HTTP(S) URLs, this is likely fine, but consider using TextEncoder for robustness if non-ASCII support is needed in the future.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/js/test/tools/registerX402.test.ts` around lines 11 - 17, The
urlToBytes helper assumes ASCII by using charCodeAt; replace it with a UTF-8
encoder (TextEncoder) to produce correct byte sequences for non-ASCII URLs, then
write those bytes into the fixed-length 128 array (truncate if encoded bytes
>128, pad with zeros if shorter) in the urlToBytes function so it robustly
handles internationalized URLs while preserving the existing fixed-size output.
programs/mpl-agent-tools/src/processor/mod.rs (1)

59-66: LGTM on the dispatch pattern, but pre-existing length validation gap exists.

The new RegisterX402V1 dispatch arm follows the established zero-copy pattern via bytemuck::from_bytes. However, there's a pre-existing issue across all instruction handlers: the code only validates that instruction_data is non-empty (line 29), but bytemuck::from_bytes will panic if the slice is shorter than size_of::<RegisterX402V1Args>() (137 bytes).

This affects all existing instructions too, so it's not introduced by this PR, but consider adding length validation in a follow-up:

💡 Suggested improvement (for all instructions)
 Ok(MplAgentToolsInstructionDiscriminant::RegisterX402V1) => {
     msg!("Instruction: RegisterX402V1");
+    if instruction_data.len() < core::mem::size_of::<RegisterX402V1Args>() {
+        return Err(MplAgentToolsError::InvalidInstructionData.into());
+    }
     // Zero-copy: cast instruction data to args struct.
     let args: &RegisterX402V1Args = bytemuck::from_bytes(
         &instruction_data[..core::mem::size_of::<RegisterX402V1Args>()],
     );
     register_x402_v1(accounts, args)
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@programs/mpl-agent-tools/src/processor/mod.rs` around lines 59 - 66, Add a
bounds check before zero-copy casting so bytemuck::from_bytes cannot panic: in
the dispatch arm handling MplAgentToolsInstructionDiscriminant::RegisterX402V1
(and similarly in other arms), verify instruction_data.len() >=
core::mem::size_of::<RegisterX402V1Args>() and return a proper error (or
Err/ProgramError) if too short, then safely call bytemuck::from_bytes to obtain
&RegisterX402V1Args and invoke register_x402_v1(accounts, args); ensure you use
the same size calculation (core::mem::size_of::<RegisterX402V1Args>()) to keep
the check accurate.
clients/rust-tools/tests/register_x402.rs (1)

75-80: Consider adding bounds check in test helper.

make_url_bytes will panic if the input string exceeds 128 bytes. While acceptable for tests with known inputs, adding an assertion could improve debuggability.

♻️ Optional: Add debug assertion
 fn make_url_bytes(url: &str) -> [u8; 128] {
+    assert!(url.len() <= 128, "URL exceeds 128 byte limit");
     let mut buf = [0u8; 128];
     let bytes = url.as_bytes();
     buf[..bytes.len()].copy_from_slice(bytes);
     buf
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@clients/rust-tools/tests/register_x402.rs` around lines 75 - 80, The helper
function make_url_bytes can panic if the input exceeds 128 bytes; add an
explicit length check (e.g., debug_assert!(bytes.len() <= buf.len()) or assert!
with a descriptive message) at the start of make_url_bytes to fail fast with a
clear error instead of an implicit slice-copy panic, then proceed to copy bytes
into buf as before.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@clients/js/test/tools/registerX402.test.ts`:
- Around line 49-66: Update the negative tests to assert the specific error
code/message instead of only checking that an error is thrown: in the test "it
cannot register an x402 endpoint if not the asset owner" wrap the call to
registerX402V1(...).sendAndConfirm(umi) with t.throwsAsync expecting the
AssetOwnerMustRegisterX402 error (or its exact message/regex), and apply the
same change to the other failing tests referenced (the block covering lines
68-90) so they assert the precise error codes/messages returned by
registerX402V1.
- Around line 11-17: The urlToBytes helper assumes ASCII by using charCodeAt;
replace it with a UTF-8 encoder (TextEncoder) to produce correct byte sequences
for non-ASCII URLs, then write those bytes into the fixed-length 128 array
(truncate if encoded bytes >128, pad with zeros if shorter) in the urlToBytes
function so it robustly handles internationalized URLs while preserving the
existing fixed-size output.

In `@clients/rust-tools/tests/register_x402.rs`:
- Around line 75-80: The helper function make_url_bytes can panic if the input
exceeds 128 bytes; add an explicit length check (e.g., debug_assert!(bytes.len()
<= buf.len()) or assert! with a descriptive message) at the start of
make_url_bytes to fail fast with a clear error instead of an implicit slice-copy
panic, then proceed to copy bytes into buf as before.

In `@programs/mpl-agent-tools/src/processor/mod.rs`:
- Around line 59-66: Add a bounds check before zero-copy casting so
bytemuck::from_bytes cannot panic: in the dispatch arm handling
MplAgentToolsInstructionDiscriminant::RegisterX402V1 (and similarly in other
arms), verify instruction_data.len() >=
core::mem::size_of::<RegisterX402V1Args>() and return a proper error (or
Err/ProgramError) if too short, then safely call bytemuck::from_bytes to obtain
&RegisterX402V1Args and invoke register_x402_v1(accounts, args); ensure you use
the same size calculation (core::mem::size_of::<RegisterX402V1Args>()) to keep
the check accurate.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 2d440bfe-2b5e-4672-bd12-c1f930e5617f

📥 Commits

Reviewing files that changed from the base of the PR and between eae1203 and 8766a94.

⛔ Files ignored due to path filters (12)
  • clients/js/src/generated/tools/accounts/index.ts is excluded by !**/generated/**
  • clients/js/src/generated/tools/accounts/x402EndpointV1.ts is excluded by !**/generated/**
  • clients/js/src/generated/tools/errors/mplAgentTools.ts is excluded by !**/generated/**
  • clients/js/src/generated/tools/instructions/index.ts is excluded by !**/generated/**
  • clients/js/src/generated/tools/instructions/registerX402V1.ts is excluded by !**/generated/**
  • clients/js/src/generated/tools/types/key.ts is excluded by !**/generated/**
  • clients/rust-tools/src/generated/accounts/mod.rs is excluded by !**/generated/**
  • clients/rust-tools/src/generated/accounts/x402_endpoint_v1.rs is excluded by !**/generated/**
  • clients/rust-tools/src/generated/errors/mpl_agent_tools.rs is excluded by !**/generated/**
  • clients/rust-tools/src/generated/instructions/mod.rs is excluded by !**/generated/**
  • clients/rust-tools/src/generated/instructions/register_x402_v1.rs is excluded by !**/generated/**
  • clients/rust-tools/src/generated/types/key.rs is excluded by !**/generated/**
📒 Files selected for processing (10)
  • clients/js/test/tools/registerX402.test.ts
  • clients/rust-tools/tests/register_x402.rs
  • configs/kinobi-tools.cjs
  • idls/mpl_agent_tools.json
  • programs/mpl-agent-tools/src/error.rs
  • programs/mpl-agent-tools/src/instruction.rs
  • programs/mpl-agent-tools/src/processor/mod.rs
  • programs/mpl-agent-tools/src/processor/register_x402_v1.rs
  • programs/mpl-agent-tools/src/state/mod.rs
  • programs/mpl-agent-tools/src/state/x402_endpoint_v1.rs

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