Add RegisterX402V1 instruction for x402 endpoint registration - #9
Add RegisterX402V1 instruction for x402 endpoint registration#9blockiosaurus wants to merge 2 commits into
Conversation
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
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: ⛔ Files ignored due to path filters (4)
📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (3)
WalkthroughAdds X402 endpoint registration: new instruction, on-chain Changes
Sequence DiagramsequenceDiagram
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
Estimated Code Review Effort🎯 3 (Moderate) | ⏱️ ~25 minutes Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
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
There was a problem hiding this comment.
🧹 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:urlToBytesassumes ASCII-only URLs.The helper uses
charCodeAtwhich 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 usingTextEncoderfor 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
RegisterX402V1dispatch arm follows the established zero-copy pattern viabytemuck::from_bytes. However, there's a pre-existing issue across all instruction handlers: the code only validates thatinstruction_datais non-empty (line 29), butbytemuck::from_byteswill panic if the slice is shorter thansize_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_byteswill 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
⛔ Files ignored due to path filters (12)
clients/js/src/generated/tools/accounts/index.tsis excluded by!**/generated/**clients/js/src/generated/tools/accounts/x402EndpointV1.tsis excluded by!**/generated/**clients/js/src/generated/tools/errors/mplAgentTools.tsis excluded by!**/generated/**clients/js/src/generated/tools/instructions/index.tsis excluded by!**/generated/**clients/js/src/generated/tools/instructions/registerX402V1.tsis excluded by!**/generated/**clients/js/src/generated/tools/types/key.tsis excluded by!**/generated/**clients/rust-tools/src/generated/accounts/mod.rsis excluded by!**/generated/**clients/rust-tools/src/generated/accounts/x402_endpoint_v1.rsis excluded by!**/generated/**clients/rust-tools/src/generated/errors/mpl_agent_tools.rsis excluded by!**/generated/**clients/rust-tools/src/generated/instructions/mod.rsis excluded by!**/generated/**clients/rust-tools/src/generated/instructions/register_x402_v1.rsis excluded by!**/generated/**clients/rust-tools/src/generated/types/key.rsis excluded by!**/generated/**
📒 Files selected for processing (10)
clients/js/test/tools/registerX402.test.tsclients/rust-tools/tests/register_x402.rsconfigs/kinobi-tools.cjsidls/mpl_agent_tools.jsonprograms/mpl-agent-tools/src/error.rsprograms/mpl-agent-tools/src/instruction.rsprograms/mpl-agent-tools/src/processor/mod.rsprograms/mpl-agent-tools/src/processor/register_x402_v1.rsprograms/mpl-agent-tools/src/state/mod.rsprograms/mpl-agent-tools/src/state/x402_endpoint_v1.rs
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:
https://claude.ai/code/session_017W5vUmT8MHsKj5CCj3d6XJ
Summary by CodeRabbit
New Features
Tests