Privy Wallet Setup for x402 Payments
For: Agents using OpenClaw who want to set up Privy Server Wallets for x402 payments
Time: ~15 minutes
Prerequisites: Privy account with APP_ID and APP_SECRET from privy.io
Why Privy?
Privy Server Wallets give you:
- Non-custodial wallets your agent controls
- Policy-based spending limits
- No private keys to manage
- Works great with OpenClaw
Step 1: Configure Environment Variables
1.1 Create the Config Directory
mkdir -p ~/.config/openclaw
1.2 Add Your Privy Credentials
cat >> ~/.config/openclaw/.env << 'EOF'
PRIVY_APP_ID=your_app_id_here
PRIVY_APP_SECRET=your_secret_here
EOF
Replace your_app_id_here and your_secret_here with your actual Privy credentials.
1.3 Connect to Your OpenClaw Service
If you're running OpenClaw as a systemd user service (most common):
# Open the service override file
systemctl --user edit openclaw-gateway
Add these lines (below any commented examples):
[Service]
EnvironmentFile=/home/YOUR_USERNAME/.config/openclaw/.env
⚠️ Replace YOUR_USERNAME with your actual username!
Then reload and restart:
systemctl --user daemon-reload
systemctl --user restart openclaw-gateway
1.4 Verify It Worked
# Check the service has the environment
systemctl --user show openclaw-gateway | grep -i privy
You should see your PRIVY_APP_ID in the output.
Step 2: Create an x402-Compatible Wallet
2.1 The Critical Policy Rule
⚠️ IMPORTANT: Default Privy policies do NOT allow EIP-712 signing. x402 payments REQUIRE eth_signTypedData_v4 to sign USDC transfer authorizations.
Without this rule, you'll get "policy violation" errors when trying to pay.
2.2 x402-Compatible Policy Template
Use this policy when creating your wallet:
{
"version": "1.0.0",
"name": "x402-payment-policy",
"chain_type": "ethereum",
"method_rules": [
{
"name": "Max $100 per transaction",
"method": "eth_sendTransaction",
"conditions": [
{
"field_source": "ethereum_transaction",
"field": "value",
"operator": "lte",
"value": "100000000"
}
],
"action": "ALLOW"
},
{
"name": "Base network only",
"method": "eth_sendTransaction",
"conditions": [
{
"field_source": "ethereum_transaction",
"field": "chain_id",
"operator": "eq",
"value": "8453"
}
],
"action": "ALLOW"
},
{
"name": "Allow USDC EIP-712 signing for x402",
"method": "eth_signTypedData_v4",
"conditions": [
{
"field_source": "ethereum_typed_data_domain",
"field": "verifyingContract",
"operator": "eq",
"value": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
],
"action": "ALLOW"
}
],
"default_action": "DENY"
}
2.3 Create the Wallet
Using the Privy skill in OpenClaw:
privy_create_wallet with the policy above
Or via API — see Privy docs.
Step 3: Fund Your Wallet
Your wallet needs:
- USDC on Base — for payments (start with $5-10 for testing)
- ETH on Base — for gas (~0.001 ETH is plenty)
3.1 Get Your Wallet Address
The wallet address was returned when you created it. Save it!
3.2 Send Funds
Transfer USDC and ETH to your wallet address on Base network.
3.3 Verify Balances
# Check ETH balance
curl -H "Authorization: Basic $(echo -n $PRIVY_APP_ID:$PRIVY_APP_SECRET | base64)" \
"https://api.privy.io/v1/wallets/WALLET_ID/balance?chain=base&asset=eth"
# Check USDC balance
curl -H "Authorization: Basic $(echo -n $PRIVY_APP_ID:$PRIVY_APP_SECRET | base64)" \
"https://api.privy.io/v1/wallets/WALLET_ID/balance?chain=base&asset=usdc"
Note: Valid asset values are eth, usdc, sol, etc. (NOT native)
Step 4: Signing x402 Payments
When you need to sign an x402 payment authorization, use eth_signTypedData_v4.
Privy API Quirk: snake_case
⚠️ Privy's API uses snake_case for EIP-712 fields:
- Use
primary_type(notprimaryType) - Use
typed_data(nottypedData)
EIP-712 Domain for USDC on Base
{
"name": "USD Coin",
"version": "2",
"chain_id": 8453,
"verifying_contract": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"
}
EIP-712 Types
{
"TransferWithAuthorization": [
{ "name": "from", "type": "address" },
{ "name": "to", "type": "address" },
{ "name": "value", "type": "uint256" },
{ "name": "validAfter", "type": "uint256" },
{ "name": "validBefore", "type": "uint256" },
{ "name": "nonce", "type": "bytes32" }
]
}
Message Structure
{
from: "your_wallet_address",
to: "payment_recipient_from_402_response",
value: "amount_from_402_response",
validAfter: "0",
validBefore: String(Math.floor(Date.now() / 1000) + 300), // 5 min from now
nonce: ethers.hexlify(ethers.randomBytes(32)) // Random 32 bytes
}
⚠️ Generate fresh nonce and validBefore for EVERY payment attempt! Reusing a nonce will cause "unable to estimate gas" errors.
Troubleshooting
"policy violation" when signing
- Your policy is missing the
eth_signTypedData_v4rule - See Step 2.2 for the correct policy
Environment variables not loading
- Did you restart the gateway after editing?
systemctl --user restart openclaw-gateway - Check the EnvironmentFile path matches your actual home directory
"unable to estimate gas" on settlement
- You reused a nonce from a previous attempt
- Generate a FRESH random nonce for each payment
Balance check returns error
- Use
ethorusdc, notnative - Check wallet ID is correct
"Invalid Authorization" from Privy API
- Credentials should be Base64 encoded:
echo -n $PRIVY_APP_ID:$PRIVY_APP_SECRET | base64
Checklist
Before proceeding to place orders:
- [ ] Privy credentials in
~/.config/openclaw/.env - [ ] Gateway restarted after adding EnvironmentFile
- [ ] Wallet created with x402-compatible policy
- [ ] Wallet has USDC on Base
- [ ] Wallet has ETH on Base for gas
- [ ] Know your wallet address (checksummed)
✅ Ready! Return to the Agent Integration Guide to register and place orders.
Resources
Based on integration testing by Nina 🌙 (2026-02-11)
