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 userIdstring Si ID único del usuario en tu aplicación chainTypestring No Tipo de chain: ethereum (default) o tron emailstring No Email del usuario (para notificaciones) metadataobject 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 assetSímbolo del token (USDC, ETH, etc.) balanceBalance en unidades mínimas (wei/smallest unit) balanceFormattedBalance formateado con decimales decimalsNumero de decimales del token valueUsdValor 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 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48Arbitrum 0xaf88d065e77c8cC2239327C5EDb3A432268e5831Base 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
Consultar Balances Guía detallada sobre consulta de balances
Intents Aprende sobre operaciones basadas en intents
Crear Wallets API Referencia completa del endpoint
Seguridad Mejores prácticas de seguridad