Use MPP with Foundry
Foundry includes native MPP support on Tempo. When an RPC endpoint returns 402 Payment Required, Foundry automatically handles the payment challenge with no wrapper scripts, middleware, or code changes.
Every Foundry tool works transparently with MPP-gated endpoints:
cast— queries and transactionsforge— scripts and forked testsanvil— local forks of paid endpointschisel— interactive REPL sessions
How MPP works with Foundry
When you point any Foundry tool at an MPP-gated RPC URL, the built-in transport intercepts 402 responses and resolves them using MPP's session flow:
- First request — Foundry sends a normal JSON-RPC request to the endpoint.
- 402 challenge — The server responds with
402 Payment Requiredand aWWW-Authenticate: Paymentheader describing the price. - Key discovery — Foundry reads your signing key from
$TEMPO_HOME/wallet/keys.toml(default~/.tempo/wallet/keys.toml) or theTEMPO_PRIVATE_KEYenv var. If the server offers multiple payment challenges (e.g. different chains or currencies), Foundry automatically picks the one matching your key's chain ID and spending allowance. - Channel open — If no payment channel exists, Foundry opens one on-chain with a deposit (default:
100,000base units). This is a one-time on-chain lockup — unused balance remains in the channel. - Voucher payment — Foundry signs an off-chain voucher against the open channel and retries the request with an
Authorization: Paymentheader. - Auto top-up — When a channel's deposit is exhausted, Foundry sends a top-up transaction. The server accepts it with
204 No Content, then Foundry signs a fresh voucher and retries automatically. - Channel reuse — Subsequent requests reuse the same channel. Channel state is persisted to
$TEMPO_HOME/foundry/channels.json(default~/.tempo/foundry/channels.json) across process invocations.
Foundry MPP setup
Install Foundry
Tempo support now ships in the latest Foundry releases:
foundryupAll standard Foundry commands work as before — MPP activates only when an endpoint returns 402.
Foundry MPP examples
MPP requests with cast
Query chain state through a paid endpoint:
# Get latest block number
cast block-number --rpc-url https://rpc.mpp.tempo.xyz
# Read a contract
cast call 0x20c0000000000000000000000000000000000000 \
"balanceOf(address)(uint256)" 0xYourAddress \
--rpc-url https://rpc.mpp.tempo.xyzMPP requests in forge script
Run deployment or read scripts against a paid endpoint:
// script/ReadBlock.s.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Script.sol";
contract ReadBlock is Script {
function run() public view {
console.log("block", block.number);
console.log("chain", block.chainid);
}
}forge script script/ReadBlock.s.sol --rpc-url https://rpc.mpp.tempo.xyzMPP requests in forked forge test
Fork a paid endpoint in tests using vm.createSelectFork:
// test/MppFork.t.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "forge-std/Test.sol";
contract MppForkTest is Test {
function test_fork_via_mpp() public {
vm.createSelectFork("https://rpc.mpp.tempo.xyz");
assertGt(block.number, 0);
assertEq(block.chainid, 4217);
}
}forge test --match-test test_fork_via_mpp -vvvMPP requests with Anvil
Fork a paid endpoint locally. Local RPC calls stay local, but any upstream fetches Anvil makes to the fork URL go through MPP:
anvil --fork-url https://rpc.mpp.tempo.xyzMPP requests with Chisel
Interactive REPL against a paid endpoint:
chisel --fork-url https://rpc.mpp.tempo.xyz➜ block.number
Type: uint256
├ Hex: 0x...
└ Decimal: 1234567
Foundry MPP configuration
MPP deposit amount
Set the fallback deposit amount used when the server does not suggest one:
export MPP_DEPOSIT=500000
cast block-number --rpc-url https://rpc.mpp.tempo.xyzThe deposit determines how many RPC calls you can make before the channel needs a top-up. When a channel is exhausted, Foundry automatically tops it up.
MPP key discovery
Foundry discovers MPP signing keys in this order:
TEMPO_PRIVATE_KEYenv var — highest priority, no keychain metadata$TEMPO_HOME/wallet/keys.toml— created bytempo wallet login, includes keychain signing mode and authorized signer metadata
Within keys.toml, the key selection priority is:
- Passkey entries first
- Entries with an inline private key second
- First entry as fallback
Foundry needs a usable inline private key — entries without one are skipped.
When the server offers multiple chains or currencies, Foundry picks the first key that matches both the chain ID and currency from the challenge.
MPP channel persistence
Open channels are saved to $TEMPO_HOME/foundry/channels.json (default ~/.tempo/foundry/channels.json). This allows channel reuse across process invocations — you won't re-open a channel every time you run cast or forge.
Channels are automatically evicted when fully spent or closed. If the server restarts and returns 410 Gone, Foundry clears stale local state and opens a fresh channel on the next request.
Tempo MPP testnet workflow
Use the Moderato testnet MPP endpoint for development:
cast block-number --rpc-url https://rpc.mpp.moderato.tempo.xyz
# Mainnet
cast block-number --rpc-url https://rpc.mpp.tempo.xyzFund your testnet wallet with tempo wallet fund before making requests.
MPP gas sponsorship
Some MPP endpoints sponsor gas fees on behalf of the caller. When the server's challenge includes a feePayer flag, Foundry delegates gas payment to the server, so no native balance is needed for gas.
Troubleshoot MPP with Foundry
| Error | Cause | Fix |
|---|---|---|
tempo: command not found | Tempo CLI not installed | Run curl -fsSL https://tempo.xyz/install | bash |
no supported MPP challenge | Missing wallet key or wrong chain/currency | Run tempo wallet login or check keys.toml |
410 Gone | Stale local channel state | Re-run the command — Foundry clears stale state and opens a fresh channel |
access key does not exist | Signing key not yet provisioned on-chain | Foundry retries automatically with a key provisioning bundle — no action needed |
Next steps for MPP with Foundry
Handle payment-gated resources with the TypeScript SDK
Make paid requests from a terminal or AI agent
Session-based billing with off-chain vouchers
Was this helpful?