Commit 45038fb
feat(v2): implement x402 v2 protocol with multi-chain support (#25)
* implement
* feat(v2): add EVM signer with EIP-3009 support
Implement Phase 4 of v2 protocol - EVM signer with EIP-3009 authorization:
- v2/internal/eip3009: EIP-712 typed data and signature creation utilities
- v2/signers/evm: Signer implementation for Ethereum-compatible chains
- NewSigner/NewSignerFromKey constructors
- CanSign/Sign methods for payment payload generation
- Support for max amount limits via WithMaxAmount option
- CAIP-2 network identifier support
Tests use Foundry/Anvil default test key for consistency and clarity.
* feat(v2): add SVM signer with Solana transaction building
Implement the v2 SVM (Solana) signer for the x402 protocol v2.
New files:
- v2/internal/solana/solana.go - Utility functions for Solana
- BuildTransferCheckedInstruction: SPL Token TransferChecked builder
- BuildSetComputeUnitLimitInstruction: Compute budget instruction
- BuildSetComputeUnitPriceInstruction: Priority fee instruction
- DeriveAssociatedTokenAddress: ATA derivation
- GetRPCURL: CAIP-2 network to RPC URL mapping
- v2/signers/svm/signer.go - SVM Signer implementation
- NewSigner: Create from base58 private key
- NewSignerFromKey: Create from existing key
- NewSignerFromKeygenFile: Create from Solana keygen JSON file
- Full v2.Signer interface implementation
- CAIP-2 network validation (rejects EVM networks)
- Fee payer extraction from requirements.Extra
Key differences from v1:
- Uses CAIP-2 network identifiers (e.g., solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp)
- Returns v2.SVMPayload type directly
- Uses v2.PaymentRequirements with Amount field
- Sets X402Version to 2
This completes Phase 5 of the v2 implementation plan.
* feat(v2): add HTTP transport layer with middleware, client, and transport
Implements Phase 6 of the v2 protocol implementation plan.
New files:
- v2/http/internal/helpers/helpers.go - HTTP helper functions
- ParsePaymentHeader, SendPaymentRequired, AddPaymentResponseHeader
- ParsePaymentRequirements, ParseSettlement, BuildPaymentHeader
- BuildResourceURL for constructing resource URLs from requests
- v2/http/middleware.go - HTTP payment middleware
- Config struct with facilitator options, authorization, and hooks
- NewX402Middleware factory with automatic requirement enrichment
- settlementInterceptor for deferred settlement on success
- Support for verify-only mode and fallback facilitators
- GetPaymentFromContext for handler access to payment info
- v2/http/transport.go - HTTP transport with 402 handling
- X402Transport implementing http.RoundTripper
- Automatic 402 detection and payment retry
- Payment callbacks for monitoring (attempt, success, failure)
- v2/http/client.go - HTTP client wrapper
- NewClient with functional options pattern
- WithSigner, WithSelector, WithHTTPClient options
- WithPaymentCallback, WithPaymentCallbacks options
- GetSettlement helper for extracting settlement from response
All tests pass with race detection enabled.
* feat(v2): add Gin framework middleware adapter
Implements Phase 7 of the v2 protocol plan.
- Add v2/http/gin/middleware.go with Gin-compatible middleware
- Support CAIP-2 network identifiers (e.g., eip155:84532)
- Add PaymentContextKey for storing verified payment in Gin context
- Add GetPaymentFromContext helper function
- Support verify-only mode
- Support primary and fallback facilitators
- Add comprehensive test coverage
The middleware follows the same pattern as v1 Gin middleware but uses
v2 types with x402Version: 2 and restructured PaymentRequired/PaymentPayload.
* feat(v2): add MCP integration with server and client transport
Implements Phase 8 of the v2 protocol plan - MCP (Model Context Protocol)
integration for payment-gated AI tools.
Server-side components:
- v2/mcp/types.go: MCP-specific payment requirements type (v2 format)
- v2/mcp/errors.go: MCP-specific error types and payment error wrapper
- v2/mcp/server/config.go: Server configuration with tool payment mapping
- v2/mcp/server/server.go: X402Server wrapping MCP server with payment support
- v2/mcp/server/handler.go: HTTP handler for payment verification/settlement
- v2/mcp/server/facilitator.go: HTTP facilitator client for MCP server
- v2/mcp/server/requirements.go: Requirement validation for MCP tools
Client-side components:
- v2/mcp/client/config.go: Client configuration with signers and callbacks
- v2/mcp/client/transport.go: Transport wrapper for automatic 402 handling
Key v2 protocol features:
- CAIP-2 network identifiers (e.g., eip155:84532)
- Resource info object in payment requirements
- Extensions passthrough support
- Payment in _meta['x402/payment'] for tool calls
- Settlement response in _meta['x402/payment-response'] in results
Tests cover:
- Free and paid tool handling
- Payment verification and settlement flow
- VerifyOnly mode
- Tool execution error handling (no settlement)
- Payment requirements extraction (v2 format)
- Configuration options
* feat(v2): add example applications for HTTP, Gin, and MCP
Add Phase 9 examples demonstrating v2 protocol usage:
- examples/v2/http/main.go: HTTP server/client with payment middleware
- examples/v2/gin/main.go: Gin framework integration example
- examples/v2/mcp/main.go: MCP server with paid tools and client
All examples demonstrate:
- CAIP-2 network identifiers (e.g., eip155:84532)
- v2 PaymentRequirements configuration
- EVM and SVM signer creation
- Automatic payment handling
- Payment event callbacks
This completes Phase 9 of the v2 implementation plan.
* test(v2): add comprehensive tests for EIP-3009 authorization
Add unit tests for the internal eip3009 package covering:
- GenerateNonce: randomness, uniqueness, non-zero values
- CreateAuthorization: field population, time bounds, nonce uniqueness
- SignAuthorization: signature format, determinism, chain separation
These tests ensure the cryptographic foundations of EVM payment
signing work correctly across different chains, tokens, and amounts.
* chore: remove beads issue tracking references from AGENTS.md
* fix: resolve all golangci-lint issues
- Fix errcheck: add explicit error handling for json.Encode and w.Write calls
- Fix staticcheck SA5011: use t.Fatal instead of t.Error for nil checks
- Fix staticcheck SA4006: properly use nonce variable in eip3009_test.go
- Remove unused mockSigner2 type and math/big import from client_test.go
* fix(v2/mcp): handle nil error data in 402 responses properly
Return ErrNoPaymentRequirements when resp.Error.Data is nil or empty
instead of attempting to unmarshal and causing 'unexpected end of JSON
input' errors. This provides clearer error messages for clients when
payment requirements are missing from 402 responses.
* fix(svm): fix critical address matching and amount validation bugs
- Fix case-insensitive base58 address comparison (Solana base58 is case-sensitive)
- Add uint64 overflow check before converting big.Int amounts
- Add nil requirements validation in CanSign
- Add negative/zero amount validation
- Add token decimals range validation (0-255)
- Add explicit token not found error when asset doesn't match
- Update test to expect case-sensitive behavior
This fixes a critical security issue where incorrect token addresses could
match due to case-insensitive comparison, and prevents silent truncation
of large amounts during uint64 conversion.
* fix(solana): correct error wrapping format for proper unwrapping
Fix error wrapping in GetRPCURL to place sentinel error at end with %w
verb, allowing errors.Is/As to properly unwrap v2.ErrInvalidNetwork.
Previous format broke error chain unwrapping.
* fix(svm): add ATA creation instruction to transfers
Add idempotent associated token account creation instruction before
SPL token transfers to ensure the destination ATA exists. The facilitator
sponsors the rent-exempt balance as fee payer.
Without this instruction, transfers fail when the recipient's ATA does
not exist, which is a functional requirement of the exact_svm specification.
Fixes critical issue from code review: v2/signers/svm/signer.go lines 283-314
* fix(http): ensure settlement before connection hijack
Fixes issue where Hijack() could bypass payment settlement for WebSocket
upgrades and other connection takeover scenarios. Now settles payment first
before allowing hijack, preventing potential payment bypass.
* fix(mcp): return value instead of pointer to copy in GetPaymentConfig
The GetPaymentConfig function was returning a pointer to a local copy
of the map value, which is misleading for callers who might expect to
modify the stored configuration. Changed the signature to return
(ToolPaymentConfig, bool) for clearer read-only semantics that match
Go idioms (similar to map lookups).
* fix(mcp): return errors instead of panicking in handler constructors
Replace panic() with error returns in NewX402Handler and initializeFacilitators.
Panicking in library code crashes consuming applications; returning errors
allows callers to handle configuration failures gracefully.
Changes:
- NewX402Handler now returns (*X402Handler, error) instead of *X402Handler
- initializeFacilitators now returns (Facilitator, Facilitator, error)
- Handler() methods in X402Server now return (http.Handler, error)
- Updated Start() methods to handle handler creation errors
- All tests pass with race detection
Fixes issue from scratch/comments.md (v2/mcp/server/handler.go:72-79)
* fix(mcp): validate X402Version in payment extraction
Reject payment payloads with unsupported x402Version to prevent
processing incompatible payments. This ensures verify/settle logic
only operates on version 2 payloads as expected by the protocol.
* fix(http): add context parameter to EnrichRequirements
- Change EnrichRequirements signature to accept context.Context as first parameter
- Allows callers to control timeouts and cancellation for facilitator requests
- Update all call sites to use context.WithTimeout(context.Background(), RequestTimeout)
- Add context import to http/pocketbase/middleware.go
- Update both v1 and v2 implementations for consistency
- All tests pass with race detection
* fix(mcp): guard against nil signers in WithSigner option
* fix(http): add timeouts to HTTP clients in middleware
- Set HTTP client timeout to DefaultTimeouts.RequestTimeout in all middleware
- Prevents indefinite hangs when facilitator is unresponsive
- Affects v1 and v2 standard middleware, Gin middleware, and PocketBase middleware
- All 6 facilitator client instantiations now have explicit timeout
- Tests pass with race detection
Resolves issue from scratch/comments.md: HTTP clients without timeout configuration
* fix(mcp): set Success=true in verify-only mode when verification succeeds
* fix(svm): add timeout to Solana RPC blockhash fetch
The GetLatestBlockhash call was using context.Background() which could hang indefinitely if the RPC node is unresponsive. Now uses a timeout context with DefaultTimeouts.VerifyTimeout to prevent hangs.
* fix(config): add RequestTimeout validation to TimeoutConfig
* fix(test): use time.Duration for HTTP client timeout instead of int
* cleanup
* fix(svm): use idempotent ATA creation instruction for token transfers
Replace non-idempotent NewCreateInstruction with CreateIdempotent
(instruction index 1) to prevent transaction failures when the
destination Associated Token Account already exists.
* Update v2/http/facilitator.go
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fix(svm): inject mock RPC client in tests to prevent real network calls
Add RPCClient interface to enable dependency injection for Solana RPC
operations. Update TestSign_ValidPayment and TestTransactionStructure to
use a mock client with deterministic blockhash, eliminating flaky tests
caused by real mainnet RPC calls.
* refactor(svm): generate test wallets at runtime instead of hardcoded key
Remove the hardcoded testPrivateKeyBase58 constant and replace with
newTestWallet() helper that generates fresh Solana wallets for each test.
This eliminates static secrets from the repository.
* refactor(v2): align types with reference implementation
- Add InvalidMessage field to VerifyResponse for human-readable error messages
- Add ErrorMessage field to SettleResponse for human-readable error messages
- Change PaymentRequired.Resource from value to pointer type for optional semantics
- Update validation to handle nil Resource (now optional)
- Update all middleware and helpers to use pointer for Resource
- Update all test files for pointer type compatibility
These changes align with the Coinbase x402 Go reference implementation.
* Update v2/signers/svm/signer_test.go
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* Update v2/types.go
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* refactor(v2): add nil guards and error context to helpers
- Add ErrCodeUnsupportedVersion error code for version mismatches
- Update ParsePaymentHeader to use ErrCodeUnsupportedVersion instead of
ErrCodeUnsupportedScheme for protocol version errors
- Add nil input guards to AddPaymentResponseHeader and BuildPaymentHeader
- Wrap errors with function context for better debugging
- Add sentinel errors ErrNilSettlement and ErrNilPayment
- Add tests for nil guard behavior and error code verification
* fix(svm): remove duplicate import block in signer_test.go
Merge artifact from remote caused duplicate import declarations.
Consolidated imports and added missing 'errors' package to original block.
* refactor(v2): improve error handling and input validation
- Fix WithDetails nil map panic by lazily initializing Details map
- Update SendPaymentRequired to return error and wrap encoding failures
- Add nil guards to ParsePaymentRequirements for resp and resp.Body
- Update middleware to handle SendPaymentRequired errors (logged only)
- Add negative input validation to AmountToBigInt (decimals < 0, amount < 0)
- Add comprehensive tests for all new behavior
* iteratr
* Update .iteratr.hooks.yml
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
---------
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>1 parent 5d563bb commit 45038fb
File tree
57 files changed
+13916
-119
lines changed- examples/v2
- gin
- http
- mcp
- http
- gin
- pocketbase
- mcp/server
- specs
- v2
- encoding
- facilitator
- http
- gin
- internal/helpers
- internal
- eip3009
- solana
- mcp
- client
- server
- signers
- evm
- svm
- validation
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
57 files changed
+13916
-119
lines changed| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
4 | | - | |
5 | | - | |
6 | | - | |
7 | | - | |
8 | | - | |
9 | | - | |
10 | | - | |
11 | | - | |
12 | | - | |
13 | | - | |
14 | | - | |
15 | | - | |
16 | | - | |
17 | | - | |
18 | | - | |
19 | | - | |
20 | | - | |
21 | | - | |
22 | | - | |
23 | | - | |
24 | | - | |
25 | | - | |
26 | | - | |
27 | | - | |
28 | | - | |
29 | | - | |
30 | | - | |
31 | | - | |
32 | | - | |
33 | | - | |
34 | | - | |
35 | | - | |
36 | | - | |
37 | | - | |
38 | | - | |
39 | | - | |
40 | | - | |
41 | | - | |
42 | | - | |
43 | | - | |
44 | | - | |
45 | | - | |
46 | | - | |
47 | | - | |
48 | | - | |
49 | | - | |
50 | | - | |
51 | | - | |
52 | | - | |
53 | | - | |
54 | | - | |
55 | | - | |
56 | | - | |
57 | | - | |
58 | | - | |
59 | | - | |
60 | | - | |
61 | | - | |
62 | | - | |
63 | | - | |
64 | | - | |
65 | | - | |
66 | | - | |
67 | | - | |
68 | | - | |
69 | | - | |
70 | | - | |
71 | | - | |
72 | | - | |
73 | | - | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | | - | |
78 | | - | |
79 | | - | |
80 | | - | |
81 | | - | |
82 | | - | |
83 | | - | |
84 | | - | |
85 | | - | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
94 | | - | |
95 | | - | |
96 | | - | |
97 | | - | |
98 | | - | |
99 | | - | |
100 | | - | |
101 | | - | |
102 | 3 | | |
103 | 4 | | |
104 | 5 | | |
| |||
0 commit comments