Machine Payments Protocol
on Avalanche
Streaming payment channels for AI agents and services. Deposit once, sign unlimited off-chain vouchers at zero gas, settle when ready. Adapted from Tempo's MPP for any EVM chain.
0xF1EB69d85897ba945B5E2EbcBAD831bf3671F137
0x6e2DD66C1bfb66a2b579D291CdF6EA559E93619b
How It Works
Payment channels let agents pay per-request without a transaction each time. Only the open and settle/close touch the chain.
Open Channel
Payer deposits USDC into the StreamChannel contract, naming the payee.
On-chain ~150k gasSign Vouchers
Payer signs EIP-712 cumulative vouchers off-chain. Each voucher says "I owe you X total". Send via HTTP headers.
Zero gasSettle or Close
Payee submits the latest voucher on-chain to collect. Unused deposit is refunded to payer.
On-chain ~90k gas⚡ Cumulative Vouchers
Each voucher supersedes the last. Only the final one matters — settle once, not per request.
🔒 Escrowed Deposits
Funds are locked in the contract. Payee can't take more than signed. Payer can reclaim after grace period.
🌐 Any ERC-20 Token
Works with USDC, USDT, WAVAX, or any standard token on Avalanche.
🔗 Chain-Aware IDs
Channel IDs include block.chainid — no cross-chain replay attacks.
Deployed Contracts
| Network | Contract | Chain ID | USDC |
|---|---|---|---|
| Mainnet | 0xF1EB69d85897ba945B5E2EbcBAD831bf3671F137 |
43114 | 0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E |
| Fuji | 0x6e2DD66C1bfb66a2b579D291CdF6EA559E93619b |
43113 | 0x5425890298aed601595a70AB815c96711a31Bc65 |
"Tempo Stream Channel" version "1" — matching Tempo's original spec for cross-compatibility.
Try the MPP API
This is a real API gated by MPP payments. No voucher = 402 rejected. Valid voucher = data returned. Try it.
Controls
localhost:3402
1. Call without payment
Hit the API with no voucher — get 402.
2. Discover pricing
Learn the service's payment terms.
3. Open a payment channel
Connect wallet, approve USDC, deposit into channel.
4. Make paid API calls
Signs a voucher (zero gas), sends via HTTP headers, gets data.
Live Request / Response
Integration Guide
For Services (Accept Payments)
Add MPP payment gating to any HTTP API. Agents discover your pricing via /.well-known/mpp, then send signed vouchers with each request.
service.ts
import express from "express";
// MPP discovery endpoint
app.get("/.well-known/mpp", (req, res) => {
res.json({
version: "draft-tempo-stream-00",
payee: SERVICE_ADDRESS,
channel_contract: "0xF1EB69d85897ba945B5E2EbcBAD831bf3671F137",
chain_id: 43114,
price_per_request: "100000", // 0.10 USDC
accepted_tokens: [USDC_ADDRESS],
});
});
// Paid API endpoint
app.post("/api/data", async (req, res) => {
const channelId = req.headers["x-mpp-channel-id"];
const amount = req.headers["x-mpp-cumulative-amount"];
const voucher = req.headers["x-mpp-voucher"];
if (!channelId || !amount || !voucher) {
return res.status(402).json({
error: "Payment Required",
mpp_info: "GET /.well-known/mpp"
});
}
// Store voucher, serve data, settle later
storeVoucher(channelId, amount, voucher);
res.json({ data: "your response here" });
});
agent.ts
import { createWalletClient, http, parseUnits } from "viem";
// 1. Open channel (one-time, on-chain)
const channelId = await walletClient.writeContract({
address: STREAM_CHANNEL,
abi: streamChannelAbi,
functionName: "open",
args: [payeeAddress, USDC, parseUnits("1", 6), salt, zeroAddress],
});
// 2. Sign vouchers off-chain (zero gas, repeat per request)
const signature = await walletClient.signTypedData({
domain: {
name: "Tempo Stream Channel",
version: "1",
chainId: 43114,
verifyingContract: STREAM_CHANNEL,
},
types: {
Voucher: [
{ name: "channelId", type: "bytes32" },
{ name: "cumulativeAmount", type: "uint128" },
],
},
primaryType: "Voucher",
message: { channelId, cumulativeAmount: parseUnits("0.10", 6) },
});
// 3. Send to service via HTTP
await fetch("https://api.example.com/data", {
headers: {
"X-MPP-Channel-Id": channelId,
"X-MPP-Cumulative-Amount": "100000",
"X-MPP-Voucher": signature,
},
});
HTTP 402 Flow
# 1. Agent makes request without payment
POST /api/data
→ 402 Payment Required
{
"error": "Payment Required",
"mpp_info": "GET /.well-known/mpp"
}
# 2. Agent discovers pricing
GET /.well-known/mpp
→ 200 OK
{
"version": "draft-tempo-stream-00",
"payee": "0x3d7A...D787",
"channel_contract": "0xF1EB...F137",
"chain_id": 43114,
"price_per_request": "100000",
"accepted_tokens": ["0xB97E...a6E"]
}
# 3. Agent opens channel on-chain, then sends with voucher
POST /api/data
X-MPP-Channel-Id: 0x9a0b6130...
X-MPP-Cumulative-Amount: 100000
X-MPP-Voucher: 0x1daffe21...
→ 200 OK
{ "data": "..." }
IStreamChannel.sol
interface IStreamChannel {
struct Channel {
bool finalized;
uint64 closeRequestedAt;
address payer;
address payee;
address token;
address authorizedSigner;
uint128 deposit;
uint128 settled;
}
function open(
address payee, address token,
uint128 deposit, bytes32 salt,
address authorizedSigner
) external returns (bytes32 channelId);
function settle(
bytes32 channelId, uint128 cumulativeAmount,
bytes calldata signature
) external;
function close(...) external;
function topUp(bytes32, uint256) external;
function requestClose(bytes32) external;
function withdraw(bytes32) external;
function getChannel(bytes32)
external view returns (Channel memory);
}
Contract Functions
| Function | Caller | Description |
|---|---|---|
open() | Payer | Deposit tokens and create a new payment channel |
settle() | Payee | Submit a signed voucher to claim accumulated payment |
topUp() | Payer | Add more tokens to an existing channel (cancels pending close) |
close() | Payee | Final settlement + refund remaining deposit to payer |
requestClose() | Payer | Start 15-minute grace period for unilateral withdrawal |
withdraw() | Payer | Reclaim unsettled deposit after grace period expires |
getChannel() | Anyone | Read channel state (view function, no gas) |
computeChannelId() | Anyone | Compute deterministic channel ID from parameters |
getVoucherDigest() | Anyone | Get the EIP-712 digest for a voucher (for verification) |
getChannelsBatch() | Anyone | Read multiple channels in one call |
Events
| Event | Emitted When |
|---|---|
ChannelOpened | A new channel is created with a deposit |
Settled | Payee claims payment via a signed voucher |
TopUp | Payer adds more funds to a channel |
CloseRequested | Payer initiates the 15-minute grace period |
CloseRequestCancelled | Payer tops up, cancelling a pending close |
ChannelClosed | Channel is finalized (cooperative or after grace) |
ChannelExpired | Payer withdraws after grace period |