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:
- Coinbase Developer Platform account — Sign up at developer.coinbase.com
- CDP API credentials — Create an API key in the CDP dashboard
- PolicyLayer account — Sign up at policylayer.com
- Node.js 18+ — Required for both SDKs
Install the required packages:
npm install @coinbase/coinbase-sdk @policylayer/sdk
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
Security notes:
- Never commit
.envto 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()}`);
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
});
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}`);
}
}
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;
}
}
}
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');
}
}
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_NAMEmatches exactly (case-sensitive) - Ensure private key includes full PEM headers
- Check API key hasn't been revoked
"PolicyLayer connection refused"
- Verify
POLICYLAYER_API_URLis 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?
- Quick Start Guide - Get running in 5 minutes
- GitHub - Open source SDK
Top comments (0)