feat(agent-world): x402 payment bridge (section-agnostic)#11
Merged
Conversation
Adds src/openhuman/tinyplace/payment.rs — the reusable x402 fulfillment
bridge shared across register / buy / bid / offer. Turns a 402
PaymentChallenge into a signed X402PaymentMap by paying on-chain through
the OpenHuman wallet, then signing the authorization via the tiny.place SDK.
- validate_challenge: require asset/amount/to/network; route SOL->native,
USDC->SPL; reject unsupported assets and expired challenges (lenient on
unparseable expiry).
- to_transfer_params: map the challenge to the wallet's PrepareTransferParams.
- build_payment_map: delegate canonical-message + Ed25519 signing + flatten
to the SDK's build_x402_payment_map (no hand-rolled crypto). The on-chain
tx is carried as onChainTx/tx/transaction references, NEVER the signature
field (which is the off-chain authorization signature).
- fulfill_payment: validate -> prepare_transfer -> execute_prepared(confirmed)
-> build_payment_map -> { payment_map, on_chain_tx, quote_id }.
Generic via PaymentContext { purpose, nonce_prefix, extra_metadata }. No RPC
controller / UI in this PR (#![allow(dead_code)] until the register handler);
network/cluster/mint selection deferred to the live-test follow-up.
13 offline unit tests (mock challenge + deterministic LocalSigner seed; no
network, no funds), including signature-verifies-against-pubkey and
on-chain-tx-in-references-not-signature.
truncate() sliced on byte offsets (&s[..6] / &s[s.len()-4..]), which would panic on a multi-byte UTF-8 boundary. Switch to char-based take/skip so the log helper is safe on any input. Add a regression test.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Adds
src/openhuman/tinyplace/payment.rs— the reusable x402 fulfillment bridge shared across register / buy / bid / offer. It turns a402 Payment RequiredPaymentChallengeinto a signedX402PaymentMapby paying on-chain through the OpenHuman wallet, then signing the authorization via the tiny.place SDK.This is the foundation PR; the section write-handlers (register, marketplace) consume this bridge in follow-up PRs.
How it works
validate_challenge— requireasset/amount/to/network; routeSOL→native,USDC→SPL; reject unsupported assets and expired challenges (lenient on unparseable expiry — backend is source of truth).to_transfer_params— map the challenge to the wallet'sPrepareTransferParams.build_payment_map— delegate canonical-message + Ed25519 signing + flattening to the SDK'sbuild_x402_payment_map(no hand-rolled crypto). The on-chain tx is carried asonChainTx/tx/transactionreferences, never thesignaturefield (which is the off-chain authorization signature).fulfill_payment—validate → prepare_transfer → execute_prepared(confirmed:true) → build_payment_map → { payment_map, on_chain_tx, quote_id }.Generic via
PaymentContext { purpose, nonce_prefix, extra_metadata }.Scope (deliberately minimal)
payment.rs, +1 linemod payment;inmod.rs.all.rs/ UI / i18n —#![allow(dead_code)]until the register handler lands.fulfill_paymentis unreachable (no controller); the user-confirmation gate lives in the future consumer'sconfirmedRPC param.challenge.networkthrough verbatim and never picks a cluster.Tests
13 offline unit tests (mock
PaymentChallenge+ deterministicLocalSignerseed; no network, no funds), including:signature_verifies_against_pubkey— reconstructs the canonical message from the flattened map and verifies the Ed25519 authorization signature against the signer's public key (exactly what the backend does).on_chain_tx_in_references_not_signature— proves the on-chain tx lands in references andsignatureis a distinct 64-byte Ed25519 sig.Verification
GGML_NATIVE=OFF cargo check --locked --lib✅cargo test --lib -- tinyplace::payment✅ 13 passed / 0 failedcargo fmt --check✅