Draft
Conversation
* feat: add MPP support for 402-gated RPC endpoints Adds transparent HTTP 402 payment handling via the Machine Payments Protocol (mpp-rs). When --mpp-key is set (or MPP_KEY env var), the transport automatically intercepts 402 responses, pays the challenge, and retries with the credential. - Add MppHttpTransport: custom HTTP transport using mpp's PaymentExt - Wire --mpp-key through RpcOpts -> Config -> ProviderBuilder -> RuntimeTransport - Gate all MPP code behind cfg(feature = "mpp"), enabled by default Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0530-3e86-726f-9d92-f4c35e965e4f * fix: disable mpp feature by default, fix fmt and clippy The mpp feature is disabled by default since mpp-rs needs to be updated from alloy v1 to alloy v2 to match foundry's current dependencies. All MPP code is behind cfg(feature = "mpp") so it compiles cleanly. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0530-3e86-726f-9d92-f4c35e965e4f * fix: enable mpp feature, use alloy patches, fix deny sources - Enable mpp feature by default in workspace - Use only mpp client feature (no evm/tempo) to avoid pulling openssl - Implement EvmSigningProvider inline using foundry's own alloy deps - Handle 402 protocol directly instead of via mpp's Fetch trait (avoids reqwest v0.12 vs v0.13 conflict) - Add mpp-rs to deny.toml allow-git list - Fix clippy (io::Error::other) and fmt Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0530-3e86-726f-9d92-f4c35e965e4f * fix: add mpp_key to config test fixture Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0530-3e86-726f-9d92-f4c35e965e4f * fix: drop mpp client feature to avoid reqwest/openssl conflict Use mpp with no features (protocol types only). Implement 402 challenge-response and signing directly in MppHttpTransport/MppSigner instead of depending on mpp's client module which pulls reqwest 0.12 with native-tls/openssl. Also fixes missing mpp_key field in forge config test. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0530-3e86-726f-9d92-f4c35e965e4f * refactor: remove mpp feature flag, make unconditional MPP support is now always compiled in. Removes all cfg(feature = "mpp") gates, the [features] section from foundry-common, and makes mpp, alloy-signer, and alloy-signer-local non-optional dependencies. Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0530-3e86-726f-9d92-f4c35e965e4f * test: add MPP transport tests - test_mpp_signer_produces_valid_credential: verifies MppSigner echoes challenge fields and produces valid payload with DID source - test_mpp_transport_non_402_passthrough: non-402 responses pass through without payment flow - test_mpp_transport_402_then_200: full 402 → pay → retry → 200 flow, verifies exactly 2 HTTP calls - test_mpp_transport_402_credential_is_valid: server-side validation that the credential parses correctly with matching challenge fields - test_mpp_transport_402_missing_www_authenticate: 402 without WWW-Authenticate header returns clear error - test_mpp_transport_via_runtime_transport: end-to-end through RuntimeTransport with --mpp-key wiring Co-Authored-By: zerosnacks <95942363+zerosnacks@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d0530-3e86-726f-9d92-f4c35e965e4f --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>
…ssion support - Make MppHttpTransport generic over PaymentProvider trait (default: TempoProvider) - Use MultiProvider in connect_http() for both charge and session intents - Add keychain signing mode support via discover_mpp_config() - Add MockPaymentProvider for unit tests (no real RPC calls) - Add live integration test (test_mpp_live_pay) against rpc.mpp.tempo.xyz - Remove unused alloy-signer/alloy-signer-local deps from foundry-common - Enable mpp crate features: tempo, client Amp-Thread-ID: https://ampcode.com/threads/T-019d06b8-fcca-7402-8e45-09082c690a8c Co-authored-by: Amp <amp@ampcode.com>
When a plain HTTP transport receives a 402 Payment Required response (no Tempo wallet configured), show a clear error with setup instructions instead of a generic HTTP error. Also: - Add discover_mpp_config() for richer key discovery (wallet/key addresses) - Use MultiProvider in connect_http() for charge + session support - Add keychain signing mode for smart wallet keys - Remove test_mpp_transport_via_runtime_transport (untestable without funds) - Move all test imports to module level Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d06b8-fcca-7402-8e45-09082c690a8c
…ndpoint Pass self.url (the user's --rpc-url) to TempoProvider/TempoSessionProvider instead of hardcoding https://rpc.tempo.xyz. This makes MPP work with any 402-enabled RPC endpoint (e.g. https://mpp.alchemy.com). Also updates mpp-rs to latest alloy-2.0 branch. Amp-Thread-ID: https://ampcode.com/threads/T-019d06ed-d3eb-719e-bb41-3cff0dcb2443 Co-authored-by: Amp <amp@ampcode.com>
…trings Replace hardcoded 'https://rpc.tempo.xyz' with mpp-rs's own DEFAULT_RPC_URL constant. This way the RPC URL is owned by mpp-rs and updates automatically when the upstream library changes it. Amp-Thread-ID: https://ampcode.com/threads/T-019d06ed-d3eb-719e-bb41-3cff0dcb2443 Co-authored-by: Amp <amp@ampcode.com>
…oded RPC Replace mpp-rs TempoSessionProvider with a custom SessionProvider that uses expiring nonces (nonce=0, nonceKey=MAX) for channel open transactions — matching how tempoxyz/wallet works. This eliminates: - The need for a separate chain RPC URL (no eth_getTransactionCount) - The chicken-and-egg problem when the RPC is itself 402-gated - Any hardcoded RPC URLs in the codebase Users just need 'tempo wallet login' and any 402-enabled --rpc-url works automatically. Amp-Thread-ID: https://ampcode.com/threads/T-019d06ed-d3eb-719e-bb41-3cff0dcb2443 Co-authored-by: Amp <amp@ampcode.com>
…tion - MppHttpTransport<P> is now generic over any PaymentProvider - LazyMppHttpTransport (type alias) lazily discovers Tempo keys on first 402 - Tests use MppHttpTransport<MockPaymentProvider> directly - ResolveProvider trait bridges lazy init and direct providers - InnerTransport::Http uses LazyMppHttpTransport via ::lazy() constructor Amp-Thread-ID: https://ampcode.com/threads/T-019d074c-484c-742a-82bd-385576eaec98 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019d074c-484c-742a-82bd-385576eaec98 Co-authored-by: Amp <amp@ampcode.com>
…Debug) Amp-Thread-ID: https://ampcode.com/threads/T-019d074c-484c-742a-82bd-385576eaec98 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019d1abe-2d4a-735a-b45c-68798c10dff1 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019d1abe-2d4a-735a-b45c-68798c10dff1 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019d1abe-2d4a-735a-b45c-68798c10dff1 Co-authored-by: Amp <amp@ampcode.com>
Amp-Thread-ID: https://ampcode.com/threads/T-019d1f7f-764a-7288-a2e5-ba7f7f1447c3 Co-authored-by: Amp <amp@ampcode.com>
Remove MppKeyEntry/MppKeysFile duplicates from mpp/keys.rs and use the shared types from crate::tempo (KeyEntry with wallet_type, KeysFile, read_tempo_keys_file, constants) directly. Only MppKeyConfig and the primary key selection logic remain MPP-specific. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d1f7f-764a-7288-a2e5-ba7f7f1447c3
…13920) * fix(mpp): pass key_authorization from keys.toml, handle KeyAlreadyExists Two bugs prevented MPP from working with passkey/keychain wallets: 1. key_authorization was never read from keys.toml or passed to TempoSigningMode::Keychain — hardcoded to None. Without it, the on-chain keychain validation fails with 'access key does not exist'. 2. SESSION_OPEN_GAS_LIMIT (2M) was too low for key authorization provisioning with WebAuthn signature verification. Bumped to 10M. Additionally, key_authorization should only be included on the first channel open (to provision the key on-chain). Subsequent opens would fail with KeyAlreadyExists. Fix: optimistically skip key_authorization (assume key exists), and retry with it only when the gateway reports 'access key does not exist'. Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d23b1-d969-7797-862b-d20db85e7c47 * feat(mpp): persist payment channels to disk for cross-process reuse Store open channel state in ~/.tempo/foundry/channels.json so that subsequent cast/forge invocations reuse existing channels instead of opening new ones on-chain. - Add persist.rs: JSON channel store with load/save/upsert - SessionProvider loads persisted channels on init, saves on updates - Channels keyed by payee:currency:escrow, tracked with deposit/amount - Spent/inactive channels evicted on load - No secrets stored — only public on-chain data and bookkeeping Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d23b1-d969-7797-862b-d20db85e7c47 * feat(mpp): topUp, 410 recovery, e2e tests for all tools, CI workflow - TopUp existing channel when deposit is exhausted (approve + topUp tx) - Handle 204 No Content after topUp, retry with voucher - Handle 410 Gone (stale channel), clear state and open new channel - Add anvil fork and chisel fork steps to mpp-test.sh - Show voucher count after forge/anvil/chisel steps - Add mpp-e2e.yml CI workflow (TEMPO_KEYS_TOML_B64 secret) Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d23b1-d969-7797-862b-d20db85e7c47 * Fix CI Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d23b1-d969-7797-862b-d20db85e7c47 * feat(config): add built-in RPC endpoint aliases as fallbacks (#13929) Amp-Thread-ID: https://ampcode.com/threads/T-019d2524-f05b-74e8-b880-deb3d6f0d227 Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> --------- Co-authored-by: Amp <amp@ampcode.com> Co-authored-by: onbjerg <onbjerg@users.noreply.github.com> Co-authored-by: Derek Cofausper <256792747+decofe@users.noreply.github.com> Co-authored-by: onbjerg <8862627+onbjerg@users.noreply.github.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d25af-8b2b-7709-b8cf-d5226c13ccf8
- Restore common/tempo.rs as canonical source for shared key types - Add generic decode_key_authorization<T: Decodable> to common/tempo - Remove duplicate WalletType, keys_path(), decode_key_authorization() from wallets/tempo.rs — now imports from foundry_common::tempo - Replace inline hex+RLP decode in mpp/transport.rs with shared helper - Replace manual toml::Value parsing in transport test with discover_mpp_config() Amp-Thread-ID: https://ampcode.com/threads/T-019d25af-8b2b-7709-b8cf-d5226c13ccf8 Co-authored-by: Amp <amp@ampcode.com>
- Move KeyEntry, KeysFile, KeyType, StoredTokenLimit to common/tempo.rs as the single source of truth, using Address types - wallets/tempo.rs now imports from foundry_common::tempo, removing all duplicate type definitions - lookup_signer() uses read_tempo_keys_file() instead of manual file I/O - MppKeyConfig uses Address instead of String, simplifying transport.rs (no more string-to-Address parsing) - Update mpp/keys.rs tests to use valid Address values - Remove unused toml dependency from foundry-wallets Amp-Thread-ID: https://ampcode.com/threads/T-019d25af-8b2b-7709-b8cf-d5226c13ccf8 Co-authored-by: Amp <amp@ampcode.com>
a85fbd4 to
46db5e4
Compare
Co-authored-by: Amp <amp@ampcode.com> Amp-Thread-ID: https://ampcode.com/threads/T-019d25af-8b2b-7709-b8cf-d5226c13ccf8
46db5e4 to
c4d1d8a
Compare
Closed
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.
Transparent HTTP 402 payment handling via the Machine Payments Protocol (mpp-rs). When an RPC endpoint returns 402, the transport automatically pays the challenge and retries.
Supersedes #13851.
Changes
MppHttpTransportwraps the HTTP transport with automatic 402 handling, 410 channel recovery, and key_authorization retry logic~/.tempo/foundry/channels.jsonfor cross-process reuseTEMPO_PRIVATE_KEYenv var or~/.tempo/wallet/keys.tomlKeyEntry,KeysFile,decode_key_authorizationincommon/tempo.rs, shared by bothwalletsandmppscripts/mpp-test.sh+ CI workflowDependencies
Depends on
alloy-2.0branches (not yet merged to main):mpp-rs:https://github.com/tempoxyz/mpp-rsbranchalloy-2.0tempo:https://github.com/tempoxyz/tempobranchalloy-2.0These branches exist because foundry uses alloy
2.0.0-rc.0while tempo/mpp-rs main branches use alloy v1. The alloy rev is patched from100a332→4031dadto match.Testing
Unit tests:
cargo test -p foundry-common -- mpp