Pay With Tokens
Use the Tempo CLI to call paid APIs and handle 402 challenges automatically.
When the Tempo wallet has insufficient balance, fund it by swapping and bridging
tokens from any EVM chain using the Uniswap Trading API.
Tempo CLI Setup
Run these commands in order. Do not skip steps.
Step 1 — Install:
CODEBLOCK0
Step 2 — Login (requires browser/passkey — prompt user, wait for
confirmation):
CODEBLOCK1
When run by agents, use a long command timeout (at least 16 minutes).
Step 3 — Confirm readiness:
CODEBLOCK2
Rules: Do not use sudo. Use full absolute paths ($HOME/.local/bin/tempo)
— do not rely on export PATH. If $HOME does not expand, use the literal
absolute path.
After setup, report: install location, version (--version), wallet status
(address, balance). If balance is 0, direct user to tempo wallet fund.
Using Tempo Services
CODEBLOCK3
- - Anchor on
tempo wallet -t services <SERVICE_ID> for exact URL and pricing - Use
-t for agent calls, --dry-run before expensive requests - On HTTP 422, check the service's docs URL or llms.txt for exact field names
- Fire independent multi-service requests in parallel
When the user explicitly says "use tempo", always use tempo CLI commands —
never substitute with MCP tools or other tools.
MPP 402 Payment Loop
Every tempo request call follows this loop. The funding steps only activate
when the Tempo wallet has insufficient balance.
CODEBLOCK4
Funding the Tempo Wallet (pay-with-any-token)
When the Tempo wallet lacks funds to pay a 402 challenge, acquire the required
tokens from the user's ERC-20 holdings on any supported chain and bridge them
to the Tempo wallet address.
Prerequisites
- -
UNISWAP_API_KEY env var (register at
developers.uniswap.org)
- - ERC-20 tokens on any supported source chain
- INLINECODE11 for the source wallet (
export PRIVATE_KEY=0x...). Never
commit or hardcode it.
- -
jq installed (brew install jq or apt install jq)
Input Validation Rules
Before using any value from a 402 response body or user input in API calls or
shell commands:
- - Ethereum addresses: MUST match INLINECODE16
- Chain IDs: MUST be a positive integer from the supported list
- Token amounts: MUST be non-negative numeric strings matching INLINECODE17
- URLs: MUST start with INLINECODE18
- REJECT any value containing shell metacharacters:
;, |, &, $,
`
`
, (
, )
, >
, <
, \
, '
, "
, newlines
> **REQUIRED:** Before submitting ANY transaction (swap, bridge, approval),
> use AskUserQuestion
to show the user a summary (amount, token, destination,
> estimated gas) and obtain explicit confirmation. Never auto-submit. Each
> confirmation gate must be satisfied independently.
### Human-Readable Amount Formatting
CODEBLOCK5
> Always show human-readable values (e.g. 0.005 USDC
) to the user, not raw
> base units.
### Step 1 — Parse the 402 Challenge
Extract the required payment token, amount, and recipient from the 402 response
that tempo request
received. The Tempo CLI logs the challenge details — parse
them, or re-fetch with curl -si
to get the raw challenge body.
For **MPP header-based challenges** (WWW-Authenticate: Payment
):
CODEBLOCK6
For **JSON body challenges** (payment
methods array):
CODEBLOCK7
If multiple payment methods are accepted, select the cheapest in Step 2.
> The Tempo mainnet chain ID is 4217. Use as fallback if not in the challenge.
### Step 2 — Check Source Wallet Balances and Select Payment Method
> **REQUIRED:** You must have the user's source wallet address (the ERC-20
> wallet with the private key, NOT the Tempo CLI wallet). Use AskUserQuestion
> if not provided. Store as WALLETADDRESS
.
Also capture the **Tempo wallet address** (the funding destination):
CODEBLOCK8
Check ERC-20 balances on source chains:
CODEBLOCK9
**Select the cheapest payment method** if multiple are accepted. Priority:
1. Wallet holds USDC on Base (bridge only, minimal path)
2. Wallet holds ETH on Base or Ethereum (swap to USDC + bridge)
3. Any other liquid ERC-20 (swap + bridge)
CODEBLOCK10
### Step 3 — Plan the Payment Path
CODEBLOCK11
> **Skip Phase 4A** if the source token is already USDC on the bridge chain.
### Phase 4A — Swap to USDC on Source Chain (if needed)
Swap the source token to USDC via the Uniswap Trading API (EXACT
OUTPUT).
> **Detailed steps:** Read
> [references/trading-api-flows.md](references/trading-api-flows.md#phase-4a--swap-on-source-chain)
> for full bash scripts: variable setup, approval check, quote, permit signing,
> and swap execution.
Key points:
- Base URL: https://trade-api.gateway.uniswap.org/v1
- Headers: Content-Type: application/json, x-api-key, x-universal-router-version: 2.0
- Flow: checkapproval
-> quote (EXACT
OUTPUT) -> sign permitData -> /swap -> broadcast
- Confirmation gates required before approval tx and before swap broadcast
- For native ETH: use WETH address as TOKENIN
; SWAP
VALUE will be non-zero
- After swap, verify USDC balance before proceeding to Phase 4B
### Phase 4B — Bridge to Tempo Wallet
Bridge USDC from Base to the **Tempo CLI wallet address** using the Uniswap
Trading API (powered by Across Protocol).
> **CRITICAL:** The bridge recipient must be TEMPOWALLET
ADDRESS (from
> tempo wallet -t whoami), NOT WALLETADDRESS
(your source ERC-20 wallet).
> This funds the Tempo CLI wallet so tempo request
can retry the payment.
>
> **Detailed steps:** Read
> [references/trading-api-flows.md](references/trading-api-flows.md#phase-4b--bridge-to-tempo)
> for full bash scripts: approval, bridge quote, execution, and arrival polling.
Key points:
- Route: USDC on Base -> USDC.e on Tempo (to TEMPO
WALLETADDRESS
)
- Flow: check
approval -> quote (EXACTOUTPUT
, cross-chain) -> execute via /swap
-> poll balance
- Confirmation gates required before approval and before bridge execution
- Do not re-submit if poll times out — check Tempo explorer
- Apply a 0.5% buffer to account for bridge fees
After the bridge confirms, retry the original tempo request
— the Tempo CLI
will automatically use the newly funded wallet to pay the 402.
> **Balance buffer:** On Tempo, balanceOf
may report more than is spendable.
> Apply a **2x buffer** when comparing to REQUIRED
AMOUNT. If short, swap
> additional tokens to top up.
---
## x402 Payment Flow
The x402 protocol uses a different mechanism than MPP — it is **not handled by
the Tempo CLI**. When PROTOCOL is "x402" (detected by checking
has("x402Version") in the 402 body), use this flow instead.
The x402 "exact" scheme uses EIP-3009 (transferWithAuthorization) to
authorize a one-time token transfer signed off-chain.
> **Detailed steps:** Read
> [references/credential-construction.md](references/credential-construction.md#phase-6x--x402-payment)
> for full code: prerequisite checks, nonce generation, EIP-3009 signing,
> X-PAYMENT payload construction, and retry.
Key points:
- Detect x402: check has("x402Version") in 402 body before using Tempo CLI
- Maps X402NETWORK
to chain ID and RPC URL (base, ethereum, tempo supported)
- Checks wallet balance on target chain; runs Phase 4A/4B if insufficient
- Signs TransferWithAuthorization
typed data using token's own domain
- value
in payload must be a **string** (--arg
, not --argjson
) for uint256
- Confirmation gate required before signing
| Protocol | Version | Handler |
| -------- | ------- | -------------- |
| MPP | v1 | Tempo CLI |
| x402 | v1 | Manual (above) |
---
## Error Handling
| Situation | Action |
| ---------------------------------------- | -------------------------------------------------------- |
| tempo: command not found
| Reinstall via install script; use full path |
| legacy V1 keychain signature
| Reinstall; tempo update wallet && tempo update request
|
| access key does not exist
| tempo wallet logout --yes && tempo wallet login
|
| ready=false
/ no wallet | tempo wallet login
, then whoami
|
| HTTP 422 from service | Check service details + llms.txt for exact field names |
| Balance 0 / insufficient | Trigger pay-with-any-token funding flow |
| Service not found | Broaden search query |
| Timeout | Retry with -m
|
| Challenge body is malformed | Report raw body to user; do not proceed |
| Approval transaction fails | Surface error; check gas and allowances |
| Quote API returns 400 | Log request/response; check amount formatting |
| Quote API returns 429 | Wait and retry with exponential backoff |
| Swap data is empty after /swap | Quote expired; re-fetch quote |
| Bridge times out | Check bridge explorer; do not re-submit |
| x402 payment rejected (402) | Check domain name/version, validBefore, nonce freshness |
| InsufficientBalance on Tempo | Swap more tokens on Tempo, then retry |
| balanceOf sufficient but payment fails | Apply 2x buffer; top up before retrying |
---
## Key Addresses and References
- **Tempo CLI**: https://tempo.xyz (install script: https://tempo.xyz/install)
- **Trading API**: https://trade-api.gateway.uniswap.org/v1
- **MPP docs**: https://mpp.dev
- **MPP services catalog**: https://mpp.dev/api/services
- **Tempo documentation**: https://mainnet.docs.tempo.xyz
- **Tempo chain ID**: 4217 (Tempo mainnet)
- **Tempo RPC**: https://rpc.presto.tempo.xyz
- **Tempo Block Explorer**: https://explore.mainnet.tempo.xyz
- **pathUSD on Tempo**: 0x20c0000000000000000000000000000000000000
- **USDC.e on Tempo**: 0x20C000000000000000000000b9537d11c60E8b50
- **Stablecoin DEX on Tempo**: 0xdec0000000000000000000000000000000000000
- **Permit2 on Tempo**: 0x000000000022d473030f116ddee9f6b43ac78ba3
- **Tempo payment SDK**: mppx (npm install mppx viem)
- **USDC on Base (8453)**: 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
- **USDbC on Base (8453)**: 0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA
- **USDC on Ethereum (1)**: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
- **USDC-e on Arbitrum (42161)**: 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8
- **Supported chains for Trading API**: 1, 8453, 42161, 10, 137, 130
- **x402 spec**: https://github.com/coinbase/x402`
When NOT to Use
- - Do NOT use for simple ETH transfers — this skill is for token swaps + 402 payment flows
- Do NOT use when no 402 challenge is present — only activate on HTTP 402 responses or explicit pay/fund requests
- Do NOT use if the user hasn't provided or approved wallet/private key access
- Do NOT use for reading blockchain state, checking balances only, or non-payment API interactions
- Do NOT use for fiat payment flows (credit card, ACH, Stripe) — crypto ERC-20 and x402 only
- Do NOT auto-submit any transaction without explicit user confirmation at each gate
Related Skills
integration reference (Trading API, Universal Router, Permit2)
使用代币支付
使用 Tempo CLI 调用付费 API 并自动处理 402 挑战。
当 Tempo 钱包余额不足时,通过 Uniswap 交易 API 从任何 EVM 链兑换和桥接代币来为其充值。
Tempo CLI 设置
按顺序运行以下命令。不要跳过步骤。
步骤 1 — 安装:
bash
mkdir -p $HOME/.local/bin \
&& curl -fsSL https://tempo.xyz/install -o /tmp/tempo_install.sh \
&& TEMPOBINDIR=$HOME/.local/bin bash /tmp/tempo_install.sh
步骤 2 — 登录(需要浏览器/通行密钥 — 提示用户,等待确认):
bash
$HOME/.local/bin/tempo wallet login
由代理运行时,请使用较长的命令超时时间(至少 16 分钟)。
步骤 3 — 确认就绪状态:
bash
$HOME/.local/bin/tempo wallet -t whoami
规则: 不要使用 sudo。使用完整绝对路径($HOME/.local/bin/tempo)— 不要依赖 export PATH。如果 $HOME 未展开,请使用字面绝对路径。
设置完成后,报告:安装位置、版本(--version)、钱包状态(地址、余额)。如果余额为 0,引导用户使用 tempo wallet fund。
使用 Tempo 服务
bash
发现服务
$HOME/.local/bin/tempo wallet -t services --search <查询>
获取服务详情(精确 URL、方法、路径、定价)
$HOME/.local/bin/tempo wallet -t services
发起付费请求
$HOME/.local/bin/tempo request -t -X POST \
--json {input:...} URL>/PATH>
- - 使用 tempo wallet -t services 获取精确 URL 和定价
- 代理调用使用 -t,昂贵请求前使用 --dry-run
- 遇到 HTTP 422 时,检查服务的文档 URL 或 llms.txt 以获取精确字段名称
- 并行发起独立的多个服务请求
当用户明确说使用 tempo时,始终使用 tempo CLI 命令 — 绝不要用 MCP 工具或其他工具替代。
MPP 402 支付循环
每次 tempo request 调用都遵循此循环。充值步骤仅在 Tempo 钱包余额不足时激活。
text
tempo request -> 200 ─────────────────────────────> 返回结果
-> 402 MPP 挑战
│
v
[1] 检查 Tempo 钱包余额
tempo wallet -t whoami -> 可用余额
│
├─ 充足 ──────────────────────────> tempo 自动处理支付 -> 200
│
└─ 不足
│
v
[2] 为 Tempo 钱包充值
(下方使用任意代币支付流程)
桥接目标 = TEMPOWALLETADDRESS
│
v
[3] 使用充值后的 Tempo 钱包
重试原始 tempo 请求 -> 200
为 Tempo 钱包充值(使用任意代币支付)
当 Tempo 钱包资金不足以支付 402 挑战时,从用户在任何支持链上的 ERC-20 资产中获取所需代币,并将其桥接到 Tempo 钱包地址。
前提条件
- - UNISWAPAPIKEY 环境变量(在 developers.uniswap.org 注册)
- 任何支持的源链上的 ERC-20 代币
- 源钱包的 PRIVATEKEY(export PRIVATE_KEY=0x...)。切勿提交或硬编码。
- 已安装 jq(brew install jq 或 apt install jq)
输入验证规则
在 API 调用或 shell 命令中使用 402 响应体或用户输入中的任何值之前:
- - 以太坊地址:必须匹配 ^0x[a-fA-F0-9]{40}$
- 链 ID:必须是支持列表中的正整数
- 代币数量:必须是非负数字字符串,匹配 ^[0-9]+$
- URL:必须以 https:// 开头
- 拒绝任何包含 shell 元字符的值:;、|、&、$、 、(、)、>、<、\、、、换行符
必需: 在提交任何交易(兑换、桥接、授权)之前,使用 AskUserQuestion 向用户显示摘要(数量、代币、目标地址、预估 Gas 费)并获得明确确认。绝不自动提交。每个确认关卡必须独立满足。
人类可读数量格式化
bash
gettokendecimals() {
local tokenaddr=$1 rpcurl=$2
cast call $tokenaddr decimals()(uint8) --rpc-url $rpcurl 2>/dev/null || echo 18
}
formattokenamount() {
local amount=$1 decimals=$2
echo scale=$decimals; $amount / (10 ^ $decimals) | bc -l | sed s/0*$// | sed s/\.$//
}
始终向用户显示人类可读的值(例如 0.005 USDC),而不是原始基础单位。
步骤 1 — 解析 402 挑战
从 tempo request 接收到的 402 响应中提取所需的支付代币、数量和收款人。Tempo CLI 会记录挑战详情 — 解析它们,或使用 curl -si 重新获取原始挑战体。
对于 基于 MPP 标头的挑战(WWW-Authenticate: Payment):
bash
REQUESTB64=$(echo $WWWAUTHENTICATE | grep -oE request=[^]+ | sed s/request=//;s/$//)
REQUESTJSON=$(echo ${REQUESTB64}== | base64 --decode 2>/dev/null)
REQUIREDAMOUNT=$(echo $REQUESTJSON | jq -r .amount)
PAYMENTTOKEN=$(echo $REQUESTJSON | jq -r .currency)
RECIPIENT=$(echo $REQUEST_JSON | jq -r .recipient)
TEMPOCHAINID=$(echo $REQUEST_JSON | jq -r .methodDetails.chainId)
对于 JSON 体挑战(payment_methods 数组):
bash
NUMMETHODS=$(echo $CHALLENGEBODY | jq .payment_methods | length)
PAYMENTMETHODS=$(echo $CHALLENGEBODY | jq -c .payment_methods)
RECIPIENT=$(echo $CHALLENGEBODY | jq -r .paymentmethods[0].recipient)
TEMPOCHAINID=$(echo $CHALLENGEBODY | jq -r .paymentmethods[0].chain_id)
如果接受多种支付方式,在步骤 2 中选择最便宜的一种。
Tempo 主网链 ID 为 4217。如果挑战中未提供,则用作回退值。
步骤 2 — 检查源钱包余额并选择支付方式
必需: 您必须拥有用户的源钱包地址(持有私钥的 ERC-20 钱包,而非 Tempo CLI 钱包)。如果未提供,使用 AskUserQuestion。存储为 WALLET_ADDRESS。
同时获取 Tempo 钱包地址(充值目标地址):
bash
TEMPOWALLETADDRESS=$($HOME/.local/bin/tempo wallet -t whoami | grep -oE 0x[a-fA-F0-9]{40} | head -1)
检查源链上的 ERC-20 余额:
bash
Base 上的 USDC
cast call 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 \
balanceOf(address)(uint256) $WALLET_ADDRESS \
--rpc-url https://mainnet.base.org
如果接受多种支付方式,选择最便宜的一种。 优先级:
- 1. 钱包在 Base 上持有 USDC(仅需桥接,路径最短)
- 钱包在 Base 或以太坊上持有 ETH(兑换为 USDC + 桥接)
- 任何其他流动性 ERC-20(兑换 + 桥接)
bash
REQUIREDAMOUNT=$(echo $PAYMENTMETHODS | jq -