Skip to content
GitHubXDiscordRSS

Coinbase Provider

The Coinbase provider brings blockchain resources as first-class citizens to Infrastructure as Code. Just as you provision databases, servers, and CDNs, you can now declaratively manage blockchain accounts and smart contracts through Alchemy, powered by the Coinbase Developer Platform (CDP) SDK.

The Coinbase provider includes the following resources:

  • EvmAccount - Manage EVM EOA (Externally Owned Accounts)
  • EvmSmartAccount - Manage ERC-4337 smart accounts for gasless transactions
  1. CDP API Keys: Obtain API credentials from the Coinbase Developer Platform Portal

  2. Authentication: Set up your CDP credentials as environment variables:

Terminal window
export CDP_API_KEY_ID=your-api-key-id
export CDP_API_KEY_SECRET=your-api-key-secret
export CDP_WALLET_SECRET=your-wallet-secret

Here’s a complete example of using the Coinbase provider to create accounts with automatic testnet funding:

import { EvmAccount, EvmSmartAccount } from "alchemy/coinbase";
import alchemy from "alchemy";
// Create an EOA (Externally Owned Account)
const ownerAccount = await EvmAccount("owner", {
name: "my-wallet",
faucet: {
"base-sepolia": ["eth", "usdc"],
"ethereum-sepolia": ["eth"]
}
});
// Create a smart account controlled by the EOA
const smartAccount = await EvmSmartAccount("smart", {
owner: ownerAccount,
// Name is optional - inherits from owner if not specified
faucet: {
"base-sepolia": ["eth", "usdc"]
}
});
// Import an existing account with a private key
const treasuryAccount = await EvmAccount("treasury", {
name: "treasury-wallet",
privateKey: alchemy.secret(process.env.TREASURY_PRIVATE_KEY)
});
// Create a smart account with an external owner
const externalSmartAccount = await EvmSmartAccount("external-smart", {
name: "external-smart-wallet",
owner: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", // Owner address
adopt: true // Use existing smart account if it already exists
});
console.log("EOA Address:", ownerAccount.address);
console.log("Smart Account Address:", smartAccount.address);
console.log("Treasury Address:", treasuryAccount.address);

When you provide a faucet configuration, accounts automatically request testnet tokens on creation and updates. This eliminates the need to manually fund accounts for development:

const account = await EvmAccount("test", {
name: "test-account",
faucet: {
"base-sepolia": ["eth", "usdc"],
"ethereum-sepolia": ["eth"]
}
});
// Account is automatically funded with ETH and USDC on Base Sepolia, and ETH on Ethereum Sepolia

For bulk funding operations or re-funding existing accounts, use the included faucet script:

Add to your package.json:

{
"scripts": {
"faucet": "bun node_modules/alchemy/src/coinbase/faucet.ts"
}
}
Terminal window
# Fund all accounts with faucet configuration
bun run faucet
# Fund only accounts in 'dev' stage
bun run faucet dev
# Fund specific scope
bun run faucet backend/dev

The script:

  • Discovers all Coinbase accounts with faucet metadata
  • Requests tokens from CDP faucet for each configured network/token pair
  • Handles rate limiting automatically
  • Tracks funded combinations to avoid duplicates

Due to the immutable nature of blockchain, EvmAccount and EvmSmartAccount resources persist in CDP even after running alchemy destroy. When destroyed, accounts are removed from Alchemy’s state tracking but continue to exist in the Coinbase Developer Platform.

The provider validates that account names contain only letters, numbers, and hyphens. CDP will provide detailed validation errors for any additional requirements like length restrictions.

When creating a smart account without specifying a name, it inherits the owner account’s name. This creates matching names in CDP for both EOA and smart account pairs. This pattern follows the Base Account SDK’s payment charge interface, ensuring compatibility when using Coinbase’s payment infrastructure

Use the adopt: true flag to use existing accounts instead of creating new ones:

const account = await EvmAccount("my-account", {
name: "existing-account",
adopt: true // Uses existing account if it exists
});

Private keys must be encrypted using alchemy.secret() to ensure they are never exposed in state files:

// ✅ Correct - Private key is encrypted
const account = await EvmAccount("secure", {
name: "secure-wallet",
privateKey: alchemy.secret(process.env.PRIVATE_KEY)
});
// ❌ Wrong - Never pass plain text private keys
// This will cause a TypeScript error