Las wallets son el componente fundamental de Pan. Representan cuentas de usuario que funcionan en multiples blockchains con una sola direccion.
¿Qué es una Wallet en Pan?
Una wallet de Pan es una cuenta no-custodial que:
- Una direccion, multiples chains: La misma direccion funciona en Ethereum, Arbitrum, Base, etc.
- No-custodial: Tu no tienes acceso a las llaves privadas (gestionadas por Privy)
- Ligada a un usuario: Cada wallet pertenece a un
userId de tu aplicacion
- Multi-token: Soporta multiples tokens por chain
| Chain | Balances |
|---|
| Ethereum | 200 USDC, 0.5 ETH |
| Arbitrum | 500 USDC, 0.1 ETH |
| Base | 300 USDC |
Todos accesibles con la misma direccion 0x742d35Cc...
Una direccion, multiples chains: La misma direccion 0x742d35Cc... funciona en todas las redes EVM.
Crear una Wallet
curl -X POST https://api.pan.dev/v1/wallets \
-H "Authorization: Bearer $PAN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"userId": "usuario_maria_123",
"chainType": "ethereum",
"metadata": {
"nombre": "Maria Garcia",
"email": "[email protected]",
"plan": "premium"
}
}'
Parametros
| Parametro | Tipo | Requerido | Descripcion |
|---|
userId | string | Si | ID unico del usuario en tu aplicacion |
chainType | string | No | Tipo de chain: ethereum (default) o tron |
email | string | No | Email del usuario (para notificaciones) |
metadata | object | No | Datos adicionales que quieras almacenar |
Respuesta
{
"id": "pan_wallet_a1b2c3d4e5f6",
"userId": "usuario_maria_123",
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"chainType": "ethereum",
"chains": [
"ethereum",
"ethereum-sepolia",
"arbitrum",
"arbitrum-sepolia",
"base",
"base-sepolia"
],
"metadata": {
"nombre": "Maria Garcia",
"email": "[email protected]",
"plan": "premium"
},
"createdAt": "2024-01-15T10:30:00Z"
}
userId debe ser unico. Si intentas crear una wallet con un userId que ya existe, recibiras un error. Usa el endpoint GET para recuperar wallets existentes.
Obtener una Wallet
curl -X GET "https://api.pan.dev/v1/wallets/usuario_maria_123" \
-H "Authorization: Bearer $PAN_API_KEY"
El endpoint usa userId, no walletId. Esto facilita mapear usuarios de tu aplicacion a wallets de Pan.
Consultar Balances
Obtiene balances de todas las chains y tokens con una sola llamada:
curl -X GET "https://api.pan.dev/v1/balances/pan_wallet_a1b2c3d4e5f6" \
-H "Authorization: Bearer $PAN_API_KEY"
{
"walletId": "pan_wallet_a1b2c3d4e5f6",
"address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"chains": [
{
"chain": "ethereum",
"tokens": [
{
"asset": "USDC",
"balance": "200000000",
"balanceFormatted": "200.00",
"decimals": 6,
"valueUsd": 200.00
},
{
"asset": "ETH",
"balance": "500000000000000000",
"balanceFormatted": "0.50",
"decimals": 18,
"valueUsd": 1250.00
}
]
},
{
"chain": "arbitrum",
"tokens": [
{
"asset": "USDC",
"balance": "500000000",
"balanceFormatted": "500.00",
"decimals": 6,
"valueUsd": 500.00
}
]
},
{
"chain": "base",
"tokens": [
{
"asset": "USDC",
"balance": "300000000",
"balanceFormatted": "300.00",
"decimals": 6,
"valueUsd": 300.00
}
]
}
],
"totalValueUsd": 2250.00
}
Estructura de Balance
Cada token en el balance incluye:
| Campo | Descripcion |
|---|
asset | Simbolo del token (USDC, ETH, etc.) |
balance | Balance en unidades minimas (wei/smallest unit) |
balanceFormatted | Balance formateado con decimales |
decimals | Numero de decimales del token |
valueUsd | Valor en USD del balance |
Chains Soportadas
Mainnet
| Chain | Chain ID | Estado |
|---|
| Ethereum | 1 | Proximo |
| Arbitrum One | 42161 | Proximo |
| Base | 8453 | Proximo |
Testnet (Disponible ahora)
| Chain | Chain ID | Estado |
|---|
| Ethereum Sepolia | 11155111 | Activo |
| Arbitrum Sepolia | 421614 | Activo |
| Base Sepolia | 84532 | Activo |
Actualmente Pan opera en testnets. Mainnet estara disponible pronto.
Tokens Soportados
USDC (Principal)
| Chain | Direccion |
|---|
| Ethereum | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 |
| Arbitrum | 0xaf88d065e77c8cC2239327C5EDb3A432268e5831 |
| Base | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
Otros Tokens
| Token | Ethereum | Arbitrum | Base |
|---|
| USDT | Si | Si | No |
| DAI | Si | Si | No |
| WETH | Si | Si | Si |
Como Funcionan las Wallets Multi-chain
Pan utiliza Privy para generar wallets. La magia de “una direccion, multiples chains” funciona porque:
- Derivacion Deterministica: La misma llave privada genera la misma direccion en todas las chains EVM
- Privy como Custodio Tecnico: Privy maneja las llaves de forma segura
- Tu Aplicacion No Tiene Acceso: Las llaves nunca salen de la infraestructura de Privy
Tu app llama a Pan
wallet.create({ userId: '123' })
Pan solicita wallet a Privy
Privy genera llave privada, deriva direccion, y la almacena encriptada
Pan guarda el mapeo
Se registra la relacion userId → walletId en la base de datos
Pan retorna la wallet
Tu app recibe { id, address, chains[] }
La llave privada nunca sale de Privy. Tu aplicacion solo recibe la direccion publica.
Arquitectura de Seguridad
| Capa | Componentes | Responsabilidad |
|---|
| Tu Aplicacion | Pan SDK | Envia requests con API Key |
| Pan API | Wallet Service, Intent Service | Orquesta operaciones |
| Privy (Custodio) | Key Generation, Encrypted Storage, Signer | Genera, almacena y firma |
| Blockchains | Ethereum, Arbitrum, Base | Reciben transacciones firmadas |
Limites de Wallets
Los limites de wallets dependen de tu plan:
| Plan | Wallets Maximas |
|---|
| Free | 100 |
| Pro | 10,000 |
| Enterprise | Ilimitadas |
Verificar Uso
// El limite se verifica automaticamente al crear
try {
const wallet = await pan.wallet.create({ userId: 'nuevo_user' });
} catch (error) {
if (error.code === 'WALLET_LIMIT_EXCEEDED') {
console.log('Necesitas actualizar tu plan');
}
}
El campo metadata te permite almacenar datos adicionales con cada wallet:
const wallet = await pan.wallet.create({
userId: 'usuario_123',
metadata: {
// Datos de tu aplicacion
nombre: 'Juan Perez',
email: '[email protected]',
plan: 'premium',
kyc_status: 'verified',
referral_code: 'ABC123',
// Cualquier dato serializable a JSON
preferences: {
currency: 'USD',
language: 'es'
}
}
});
No almacenes datos sensibles (contrasenas, tokens de sesion, etc.) en metadata. Aunque estan almacenados de forma segura, es mejor practica mantenerlos separados.
Patrones Comunes
Crear Wallet al Registrarse
async function onUserSignup(user) {
// Crear wallet de Pan junto con el registro
const wallet = await pan.wallet.create({
userId: user.id,
metadata: {
email: user.email,
name: user.name,
signupDate: new Date().toISOString()
}
});
// Guardar walletId en tu base de datos
await db.users.update(user.id, {
panWalletId: wallet.id,
panWalletAddress: wallet.address
});
return wallet;
}
Obtener o Crear Wallet
async function getOrCreateWallet(userId) {
try {
// Intentar obtener wallet existente
return await pan.wallet.get(userId);
} catch (error) {
if (error.code === 'WALLET_NOT_FOUND') {
// Crear si no existe
return await pan.wallet.create({ userId });
}
throw error;
}
}
Mostrar Balances al Usuario
async function getFormattedBalances(walletId) {
const response = await pan.wallet.getBalances(walletId);
const formatted = [];
for (const chainData of response.chains) {
for (const token of chainData.tokens) {
if (parseFloat(token.balanceFormatted) > 0) {
formatted.push({
chain: formatChainName(chainData.chain),
token: token.asset,
amount: token.balanceFormatted,
usd: `$${token.valueUsd.toFixed(2)}`
});
}
}
}
return {
items: formatted,
total: `$${response.totalValueUsd.toFixed(2)}`
};
}
function formatChainName(chain) {
const names = {
'ethereum': 'Ethereum',
'ethereum-sepolia': 'Ethereum (Testnet)',
'arbitrum': 'Arbitrum',
'arbitrum-sepolia': 'Arbitrum (Testnet)',
'base': 'Base',
'base-sepolia': 'Base (Testnet)'
};
return names[chain] || chain;
}
Fondear Wallets
En Testnet (Demo)
Pan ofrece un endpoint para fondear wallets de prueba:
await fetch('https://api.pan.dev/v1/demo/fund', {
method: 'POST',
headers: {
'Authorization': `Bearer ${API_KEY}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
walletId: 'pan_wallet_abc123',
chain: 'arbitrum-sepolia',
amount: 1000 // USDC
})
});
En Produccion
En produccion, los usuarios depositan fondos directamente a su direccion de wallet:
// Mostrar direccion para deposito
const wallet = await pan.wallet.get(userId);
return {
message: 'Deposita fondos a esta direccion:',
address: wallet.address,
supportedChains: ['Ethereum', 'Arbitrum', 'Base'],
supportedTokens: ['USDC', 'USDT', 'ETH']
};
Proximos Pasos