Las wallets son el componente fundamental de pan. Representan cuentas de usuario que funcionan en múltiples blockchains con una sola dirección.
¿Qué es una Wallet en pan?
Una wallet de pan es una cuenta no-custodial que:
- Una dirección, múltiples chains: La misma dirección 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 aplicación
- Multi-token: Soporta múltiples 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 dirección 0x742d35Cc...
Una dirección, múltiples chains: La misma dirección 0x742d35Cc... funciona en todas las redes EVM.
Crear una Wallet
curl -X POST https://api.pan.tech/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": "maria@ejemplo.com",
"plan": "premium"
}
}'
Parámetros
| Parámetro | Tipo | Requerido | Descripción |
|---|
userId | string | Si | ID único del usuario en tu aplicación |
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": "maria@ejemplo.com",
"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.tech/v1/wallets/usuario_maria_123" \
-H "Authorization: Bearer $PAN_API_KEY"
El endpoint usa userId, no walletId. Esto facilita mapear usuarios de tu aplicación a wallets de pan.
Consultar Balances
Obtiene balances de todas las chains y tokens con una sola llamada:
curl -X GET "https://api.pan.tech/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 | Descripción |
|---|
asset | Símbolo del token (USDC, ETH, etc.) |
balance | Balance en unidades mínimas (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 | Próximo |
| Arbitrum One | 42161 | Próximo |
| Base | 8453 | Próximo |
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 estará disponible pronto.
Tokens Soportados
USDC (Principal)
| Chain | Dirección |
|---|
| 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 Funciónan las Wallets Multi-chain
pan utiliza Privy para generar wallets. La magia de “una dirección, múltiples chains” funciona porque:
- Derivación Determinística: La misma llave privada genera la misma dirección 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 dirección, 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 aplicación solo recibe la dirección 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 límites de wallets dependen de tu plan:
| Plan | Wallets Maximas |
|---|
| Free | 100 |
| Pro | 10,000 |
| Enterprise | Ilimitadas |
Verificar Uso
// El límite se verifica automáticamente 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 aplicación
nombre: 'Juan Perez',
email: 'juan@ejemplo.com',
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 están almacenados de forma segura, es mejor práctica 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.tech/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 dirección de wallet:
// Mostrar dirección para deposito
const wallet = await pan.wallet.get(userId);
return {
message: 'Deposita fondos a está dirección:',
address: wallet.address,
supportedChains: ['Ethereum', 'Arbitrum', 'Base'],
supportedTokens: ['USDC', 'USDT', 'ETH']
};
Próximos Pasos