DEV Community

L_X_1
L_X_1

Posted on • Originally published at policylayer.com

Coinbase SDK + PolicyLayer: The Ultimate Stack for Safe AI Agents

The Coinbase Developer Platform (CDP) SDK is revolutionising how developers create wallets for AI agents. Their MPC (Multi-Party Computation) solution removes the headache of seed phrase management.

But MPC manages custody, not policy. You still need a way to control how much the agent spends.

Here's how to combine Coinbase SDK with PolicyLayer for the ultimate secure agent stack.

The Missing Link in MPC

Coinbase's MPC wallets are incredibly secure against key theft. The private key is split across multiple parties, making extraction practically impossible.

However, if your AI agent (which controls the MPC wallet) decides to "empty wallet to address X" due to a prompt injection, the MPC system will faithfully execute that valid signature request. From the MPC's perspective, the request came from an authorised caller—it doesn't know the caller was compromised.

MPC secures the key. PolicyLayer secures the intent.

Layer What It Protects What It Doesn't Protect
MPC (Coinbase) Key extraction, server compromise Agent logic bugs, prompt injection
PolicyLayer Spending amounts, recipients, velocity Key theft (not applicable—keys are MPC)
Combined Complete protection

The combination creates defence in depth: even if an attacker compromises your agent's logic, they cannot exceed policy limits.

Prerequisites

Before starting, you'll need:

  1. Coinbase Developer Platform account — Sign up at developer.coinbase.com
  2. CDP API credentials — Create an API key in the CDP dashboard
  3. PolicyLayer account — Sign up at policylayer.com
  4. Node.js 18+ — Required for both SDKs

Install the required packages:

npm install @coinbase/coinbase-sdk @policylayer/sdk
Enter fullscreen mode Exit fullscreen mode

Environment Configuration

Create a .env file with your credentials:

# Coinbase CDP credentials
CDP_KEY_NAME=your-key-name
CDP_PRIVATE_KEY="-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----"

# PolicyLayer credentials
POLICYLAYER_API_KEY=pl_live_xxxxxxxxxxxx
POLICYLAYER_API_URL=https://api.policylayer.com

# Network configuration
NETWORK=base-mainnet  # or base-sepolia for testnet
Enter fullscreen mode Exit fullscreen mode

Security notes:

  • Never commit .env to version control
  • Use separate API keys for development and production
  • Rotate credentials regularly

Integration Guide

PolicyLayer wraps any wallet SDK, including Coinbase's CDP. The adapter pattern means you can switch wallet providers without changing your policy logic.

1. Initialise Coinbase SDK

import { Coinbase } from "@coinbase/coinbase-sdk";
import { PolicyWallet } from "@policylayer/sdk";

// Initialise Coinbase SDK
const coinbase = new Coinbase({
  apiKeyName: process.env.CDP_KEY_NAME,
  privateKey: process.env.CDP_PRIVATE_KEY,
});

// Create the Agent's Wallet
const cdpWallet = await coinbase.wallet.create();
console.log(`Wallet created: ${await cdpWallet.getDefaultAddress()}`);
Enter fullscreen mode Exit fullscreen mode

2. Create Policy-Wrapped Wallet

// Create a custom adapter for Coinbase SDK
const coinbaseAdapter = {
  getAddress: async () => cdpWallet.getDefaultAddress(),
  signAndSendTransaction: async (tx) => {
    const transfer = await cdpWallet.createTransfer({
      amount: tx.amount,
      assetId: tx.asset,
      destination: tx.to
    });
    await transfer.wait(); // Wait for confirmation
    return { hash: transfer.getTransactionHash() };
  }
};

// Wrap with PolicyLayer controls
const secureWallet = new PolicyWallet(coinbaseAdapter, {
  apiUrl: process.env.POLICYLAYER_API_URL,
  apiKey: process.env.POLICYLAYER_API_KEY
});
Enter fullscreen mode Exit fullscreen mode

3. Usage

Now transactions are validated against your spending policies:

// If this exceeds daily limit, it fails before signing.
// No gas fees wasted. No funds lost.
try {
  const result = await secureWallet.send({
    chain: 'base',
    asset: 'usdc',
    to: '0xRecipient...',
    amount: '500000000' // 500 USDC (6 decimals)
  });
  console.log(`Transaction sent: ${result.hash}`);
} catch (error) {
  if (error.code === 'POLICY_DECISION_DENY') {
    // Specific reason is in error.message (e.g., "Transaction blocked: DAILY_LIMIT")
    console.log(`Policy denied: ${error.message}`);
  }
}
Enter fullscreen mode Exit fullscreen mode

Error Handling

Robust error handling is essential for production agents:

async function sendWithRetry(wallet, transaction, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await wallet.send(transaction);
    } catch (error) {
      // Policy denials - don't retry (they won't change)
      if (error.code === 'POLICY_DECISION_DENY') {
        throw error; // Policy rejection is final
      }

      // Network errors - retry with backoff
      if (attempt < maxRetries) {
        await sleep(1000 * attempt);
        continue;
      }
      throw error;
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Testing the Integration

Always test on testnet before deploying to production:

// Use Base Sepolia for testing
const testWallet = new PolicyWallet(coinbaseAdapter, {
  apiUrl: 'https://api.policylayer.com',
  apiKey: process.env.POLICYLAYER_API_KEY,
  testMode: true // Enables additional logging
});

// Test policy enforcement
async function runTests() {
  // Test 1: Valid transaction (should succeed)
  const result = await testWallet.send({
    chain: 'base-sepolia',
    asset: 'usdc',
    to: '0xWhitelistedAddress...',
    amount: '1000000' // 1 USDC
  });
  console.log('✓ Valid transaction passed');

  // Test 2: Exceed per-transaction limit (should fail)
  try {
    await testWallet.send({
      chain: 'base-sepolia',
      asset: 'usdc',
      to: '0xWhitelistedAddress...',
      amount: '999999000000' // 999,999 USDC
    });
    console.log('✗ Should have failed');
  } catch (error) {
    console.log('✓ Per-transaction limit enforced');
  }
}
Enter fullscreen mode Exit fullscreen mode

Production vs Testnet

Aspect Testnet Production
Network base-sepolia base-mainnet
Funds Faucet tokens (free) Real assets
API URL Same Same
Policies Relaxed for testing Strict limits

Deployment checklist:

  • [ ] All tests passing on testnet
  • [ ] Production policies configured and reviewed
  • [ ] Monitoring and alerts enabled
  • [ ] Kill switch tested and accessible
  • [ ] Incident response plan documented

Troubleshooting

"CDP API authentication failed"

  • Verify CDP_KEY_NAME matches exactly (case-sensitive)
  • Ensure private key includes full PEM headers
  • Check API key hasn't been revoked

"PolicyLayer connection refused"

  • Verify POLICYLAYER_API_URL is correct
  • Check API key is valid and has correct permissions
  • Ensure firewall allows outbound HTTPS

"Transaction rejected but should have passed"

  • Check daily counter hasn't been consumed by earlier transactions
  • Verify recipient is on whitelist (if whitelist enabled)
  • Review policy configuration in dashboard

"MPC signing timeout"

  • CDP MPC signing can take 2-5 seconds
  • Increase timeout settings if needed
  • Check CDP status page for service issues

Why This Stack Wins

Component Responsibility
Coinbase SDK Key management, on-ramps, MPC security
PolicyLayer Spending limits, whitelists, kill switch
Combined Enterprise-grade agent security

This is the standard reference architecture for enterprise-grade Agentic Finance applications. MPC handles the cryptographic security; PolicyLayer handles the business logic security.


Related reading:

Ready to secure your AI agents?

Top comments (0)