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 (not primaryType)
  • Use typed_data (not typedData)

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_v4 rule
  • 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 eth or usdc, not native
  • 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)