feat(swift): add x402 exact client#129
Closed
EfeDurmaz16 wants to merge 12 commits into
Closed
Conversation
7eb8c02 to
3219a6f
Compare
EfeDurmaz16
added a commit
to EfeDurmaz16/mpp-sdk
that referenced
this pull request
May 25, 2026
Three fixes so the x402-exact matrix actually executes once the language adapter PRs (solana-foundation#124, solana-foundation#126, solana-foundation#127, solana-foundation#128, solana-foundation#129, solana-foundation#130) rebase on top of this branch: 1. Pair filter is data-driven. Previously only ts-x402 self-pair and rust-x402 self-pair were accepted. Now the filter walks the registered adapters and accepts any pair where: both sides are the TS reference (stub-payload), both sides are the Rust spine, the two sides share a base language id (same-language self-pair, e.g. go-x402-client <-> go-x402-server), or one side is the Rust spine (cross-spine pair in either direction). TS reference is locked to its self-pair only because its stub payload would fail real signature verification on any other server. 2. rust-x402 cargo --manifest-path corrected from ../../rust/Cargo.toml to ../rust/Cargo.toml. The path was stale after the tests/interop -> harness rename; the existing rust (charge) entries already used the correct relative path, the x402 entries did not. 3. Pair selector docstring rewritten to spell out the data-driven matrix policy so future language ports don't need to touch the test.
This was referenced May 25, 2026
EfeDurmaz16
added a commit
to EfeDurmaz16/mpp-sdk
that referenced
this pull request
May 26, 2026
Three fixes so the x402-exact matrix actually executes once the language adapter PRs (solana-foundation#124, solana-foundation#126, solana-foundation#127, solana-foundation#128, solana-foundation#129, solana-foundation#130) rebase on top of this branch: 1. Pair filter is data-driven. Previously only ts-x402 self-pair and rust-x402 self-pair were accepted. Now the filter walks the registered adapters and accepts any pair where: both sides are the TS reference (stub-payload), both sides are the Rust spine, the two sides share a base language id (same-language self-pair, e.g. go-x402-client <-> go-x402-server), or one side is the Rust spine (cross-spine pair in either direction). TS reference is locked to its self-pair only because its stub payload would fail real signature verification on any other server. 2. rust-x402 cargo --manifest-path corrected from ../../rust/Cargo.toml to ../rust/Cargo.toml. The path was stale after the tests/interop -> harness rename; the existing rust (charge) entries already used the correct relative path, the x402 entries did not. 3. Pair selector docstring rewritten to spell out the data-driven matrix policy so future language ports don't need to touch the test.
b29f8f8 to
8293b0e
Compare
EfeDurmaz16
added a commit
to EfeDurmaz16/mpp-sdk
that referenced
this pull request
May 26, 2026
Three fixes so the x402-exact matrix actually executes once the language adapter PRs (solana-foundation#124, solana-foundation#126, solana-foundation#127, solana-foundation#128, solana-foundation#129, solana-foundation#130) rebase on top of this branch: 1. Pair filter is data-driven. Previously only ts-x402 self-pair and rust-x402 self-pair were accepted. Now the filter walks the registered adapters and accepts any pair where: both sides are the TS reference (stub-payload), both sides are the Rust spine, the two sides share a base language id (same-language self-pair, e.g. go-x402-client <-> go-x402-server), or one side is the Rust spine (cross-spine pair in either direction). TS reference is locked to its self-pair only because its stub payload would fail real signature verification on any other server. 2. rust-x402 cargo --manifest-path corrected from ../../rust/Cargo.toml to ../rust/Cargo.toml. The path was stale after the tests/interop -> harness rename; the existing rust (charge) entries already used the correct relative path, the x402 entries did not. 3. Pair selector docstring rewritten to spell out the data-driven matrix policy so future language ports don't need to touch the test.
EfeDurmaz16
added a commit
to EfeDurmaz16/mpp-sdk
that referenced
this pull request
May 26, 2026
Three fixes so the x402-exact matrix actually executes once the language adapter PRs (solana-foundation#124, solana-foundation#126, solana-foundation#127, solana-foundation#128, solana-foundation#129, solana-foundation#130) rebase on top of this branch: 1. Pair filter is data-driven. Previously only ts-x402 self-pair and rust-x402 self-pair were accepted. Now the filter walks the registered adapters and accepts any pair where: both sides are the TS reference (stub-payload), both sides are the Rust spine, the two sides share a base language id (same-language self-pair, e.g. go-x402-client <-> go-x402-server), or one side is the Rust spine (cross-spine pair in either direction). TS reference is locked to its self-pair only because its stub payload would fail real signature verification on any other server. 2. rust-x402 cargo --manifest-path corrected from ../../rust/Cargo.toml to ../rust/Cargo.toml. The path was stale after the tests/interop -> harness rename; the existing rust (charge) entries already used the correct relative path, the x402 entries did not. 3. Pair selector docstring rewritten to spell out the data-driven matrix policy so future language ports don't need to touch the test.
Adds the canonical x402 `exact` intent to the cross-language interop harness, plus TypeScript reference client and server fixtures and matrix wiring that registers the Rust spine adapters already shipped under `rust/crates/x402/src/bin/`. Language adapters can now target the harness contract (X402_INTEROP_* env vars, ready/result JSON shapes) to validate against the Rust spine cell. The TS reference fixture carries a stub credential payload (challenge id + resource) so the harness wiring, negative-code classification, cross-server portability, and idempotent-resubmit flows can run without a full Solana signer. Pair restriction in the matrix gates TS↔TS and Rust↔Rust by default; full TS↔Rust on-chain settlement parity lands with a follow-up SDK port. The legacy MPP charge runner hard-skips the new intent so default `pnpm test` behaviour is unchanged.
Adds X402 library + x402-interop-client executable as sibling targets to the existing SolanaMpp library in swift/Package.swift. Client-only, darwin-only (CryptoKit + Apple Swift toolchain required). Source: solana-foundation/x402-sdk PR solana-foundation#26 (tip 36c5e9c). - swift/Package.swift — add X402 library, X402InteropClient executable, and X402Tests test target alongside SolanaMpp - swift/Sources/X402/ — implementation (renamed X402SwiftExact -> X402) - swift/Sources/X402InteropClient/ — interop harness adapter - swift/Tests/X402Tests/ — 20 XCTest cases (all passing on darwin) - notes/codex-review-swift-x402-r4.md — Codex r4 evidence carry-over - tests/interop/src/implementations.ts — swift-x402-client entry, gated on process.platform === "darwin" Behavioral parity verified against rust/crates/x402/ reference spine: CAIP-2 network identifiers, SPL/Token-2022 program IDs, stablecoin mint addresses, EXACT_SCHEME, MAX_MEMO_BYTES, per-network stablecoin resolution with cross-network fallback, fail-closed network selection, extra.tokenProgram SPL allowlist enforcement, decimals validation (0..255 integer), x402Version validation, and the canonical X402_INTEROP_* env-var contract including X402_INTEROP_PREFER_CURRENCIES.
PaymentRequirement Codable previously read only `amount`, silently dropping every spine-shaped challenge that uses the canonical `maxAmountRequired` wire field (TS fixture, Rust spine output, Go/Kotlin/PHP ports). Mirror the Rust spine fallback in rust/crates/x402/src/protocol/schemes/exact/types.rs by accepting either field, preferring `amount` when both are present for back-compat. Adds two regression tests covering the new wire field and the both-present precedence.
Compute budget values (limit=20_000, price=1 microLamport) already match the Rust spine canonical at rust/crates/x402/src/client/exact/payment.rs:55-57. Document the cross-reference at the call site and pin the serialized SetComputeUnitLimit and SetComputeUnitPrice byte sequences in a regression test so any future drift surfaces in CI rather than at the facilitator.
The TypeScript adapter at `typescript/` is a fixture stub that only exercises challenge framing; it does not settle on-chain. The Rust spine at `rust/crates/x402/` is the canonical reference. Update the Swift X402 in-source docs that previously labelled the TS adapter as "reference" so future contributors and review tooling cannot mistake the fixture stub for the authoritative implementation.
Replace CryptoKit with the swift-crypto `Crypto` module so the X402 client builds and runs on Linux as well as Apple platforms. The Curve25519.Signing and SHA256 APIs are API-compatible across CryptoKit and SwiftCrypto, so no logic changes are required. Drop the Darwin-only runtime gate from the `swift-x402-client` interop adapter registration so the same harness entry works on ubuntu CI runners that have a Swift toolchain installed. CI runners without Swift must still leave the adapter out of MPP_INTEROP_CLIENTS.
Replace the per-line TS constants.ts:NN cross-references with the canonical rust/crates/x402/src/protocol/schemes/exact/types.rs line ranges so the source of truth is unambiguous. TS is a fixture stub and is non-authoritative; the mint table values themselves are unchanged. Also refresh notes/codex-review-swift-x402-r4.md to reflect the SwiftCrypto migration (no longer Darwin-only/CryptoKit/XCTest).
0a962ce to
1970c81
Compare
EfeDurmaz16
added a commit
to EfeDurmaz16/mpp-sdk
that referenced
this pull request
May 26, 2026
Three fixes so the x402-exact matrix actually executes once the language adapter PRs (solana-foundation#124, solana-foundation#126, solana-foundation#127, solana-foundation#128, solana-foundation#129, solana-foundation#130) rebase on top of this branch: 1. Pair filter is data-driven. Previously only ts-x402 self-pair and rust-x402 self-pair were accepted. Now the filter walks the registered adapters and accepts any pair where: both sides are the TS reference (stub-payload), both sides are the Rust spine, the two sides share a base language id (same-language self-pair, e.g. go-x402-client <-> go-x402-server), or one side is the Rust spine (cross-spine pair in either direction). TS reference is locked to its self-pair only because its stub payload would fail real signature verification on any other server. 2. rust-x402 cargo --manifest-path corrected from ../../rust/Cargo.toml to ../rust/Cargo.toml. The path was stale after the tests/interop -> harness rename; the existing rust (charge) entries already used the correct relative path, the x402 entries did not. 3. Pair selector docstring rewritten to spell out the data-driven matrix policy so future language ports don't need to touch the test.
Collaborator
Author
|
Closing per maintainer's focus-on-Ruby guidance. The branch stays available on the fork so the work can be cherry-picked once the Ruby pattern is set and we replay the same shape across languages. |
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.
Summary
Ports the x402
exactclient to Swift by adding anX402library andx402-interop-clientexecutable as sibling targets in the existingswift/Package.swift(alongsideSolanaMppfrom #104). Client-only, darwin-only.Scope
swift/Sources/X402/:Base58.swift,Challenge.swift,Constants.swift,Ed25519CompressedPoint.swift,JSONValue.swift,Rpc.swift,Signer.swift,SolanaTransaction.swiftswift/Sources/X402InteropClient/main.swiftextra.tokenProgramSPL allowlist viavalidatedTokenProgram()rejecting any program outside{SPL Token, Token-2022}extra.decimalsvalidated as 0..255 integer (rejects NaN/Inf/negative/fractional)SetComputeUnitLimitinstruction; price-only cap respectedNSNullhandling for the absent fixture settlement headerFiles changed
swift/Package.swift,swift/Sources/X402/**,swift/Sources/X402InteropClient/main.swiftswift/Tests/X402Tests/**(20 XCTest cases)harness/src/implementations.tsregistersswift-x402-clientdarwin-gated (process.platform === "darwin") withintents: ["x402-exact"]Security highlights
MemorySolanaSignerpubkey verify uses CryptoKit derivation and throws on mismatchy = p,y = p + 1pluscount == 32assert) prevents accepting malformed compressed pointsx402Versionvalidation rejects unknown protocol revisionsTest evidence
swift test --package-path swifton darwin (Swift 6.2.3): 76/76 pass (56 SolanaMpp + 20 X402)pnpm typecheck(harness/): clean for changed filesnotes/codex-review/pr-129-r8.md)swift-x402-clientno-op cleanly via the darwin gateCloses / supersedes
None.
Reviewer notes
SYSTEM_PROGRAMandLIGHTHOUSE_PROGRAMconstants intentionally omitted (server-only)resourceurl|uripermissive deser not applicable: SwiftPaymentRequirementdoes not decoderesource; routing happens via headers and CAIP-2 network