Crear una Wallet Basica
El caso mas simple: crear una wallet para un usuario nuevo.Copy
import { Pan } from '@pan/sdk';
const pan = new Pan({ apiKey: process.env.PAN_API_KEY });
// Crear wallet
const wallet = await pan.wallet.create({
userId: 'usuario_123'
});
console.log('Wallet ID:', wallet.id);
console.log('Address:', wallet.address);
Crear Wallet con Metadata
Almacena informacion adicional sobre el usuario:Copy
const wallet = await pan.wallet.create({
userId: 'usuario_maria_456',
email: '[email protected]',
metadata: {
nombre: 'Maria Garcia',
plan: 'premium',
kyc_status: 'verified',
referral_code: 'ABC123',
preferences: {
currency: 'USD',
language: 'es'
}
}
});
El
email se usa para notificaciones opcionales. El metadata puede contener cualquier JSON serializable.Patron: Crear Wallet al Registrarse
Integra la creacion de wallet en tu flujo de registro:Copy
// En tu handler de registro
async function onUserSignup(userData) {
// 1. Crear usuario en tu base de datos
const user = await db.users.create({
email: userData.email,
name: userData.name
});
// 2. Crear wallet de Pan
const wallet = await pan.wallet.create({
userId: user.id,
email: user.email,
metadata: {
name: user.name,
signupDate: new Date().toISOString(),
source: userData.referralSource
}
});
// 3. Guardar referencia a wallet
await db.users.update(user.id, {
panWalletId: wallet.id,
panWalletAddress: wallet.address
});
// 4. Retornar usuario con wallet
return {
...user,
wallet: {
id: wallet.id,
address: wallet.address
}
};
}
Patron: Obtener o Crear Wallet
Para usuarios que pueden o no tener wallet:Copy
async function getOrCreateWallet(userId, email) {
try {
// Intentar obtener wallet existente
const wallet = await pan.wallet.get(userId);
console.log('Wallet existente encontrada');
return wallet;
} catch (error) {
if (error.code === 'WALLET_NOT_FOUND') {
// Crear nueva wallet
console.log('Creando nueva wallet...');
return await pan.wallet.create({ userId, email });
}
throw error;
}
}
// Uso
const wallet = await getOrCreateWallet('user_123', '[email protected]');
Obtener Wallet Existente
Recuperar una wallet por userId:Copy
// Por userId (recomendado)
const wallet = await pan.wallet.get('usuario_123');
console.log('ID:', wallet.id);
console.log('Address:', wallet.address);
console.log('Metadata:', wallet.metadata);
El endpoint usa
userId como parametro, no walletId. Esto facilita el mapeo entre tus usuarios y sus wallets.Manejo de Errores Comunes
Usuario ya tiene wallet
Copy
try {
const wallet = await pan.wallet.create({ userId: 'usuario_123' });
} catch (error) {
if (error.code === 'WALLET_ALREADY_EXISTS') {
// Obtener wallet existente
const existingWallet = await pan.wallet.get('usuario_123');
console.log('Usuario ya tiene wallet:', existingWallet.id);
}
}
Limite de wallets excedido
Copy
try {
const wallet = await pan.wallet.create({ userId: 'nuevo_usuario' });
} catch (error) {
if (error.code === 'WALLET_LIMIT_EXCEEDED') {
console.log('Has alcanzado el limite de wallets de tu plan');
console.log('Actualiza a Pro o Enterprise para mas wallets');
// Mostrar UI de upgrade
}
}
Wallet no encontrada
Copy
try {
const wallet = await pan.wallet.get('usuario_inexistente');
} catch (error) {
if (error.code === 'WALLET_NOT_FOUND') {
console.log('Usuario no tiene wallet registrada');
// Crear wallet o mostrar UI
}
}
Ejemplo Completo: Servicio de Wallets
Copy
// services/wallet-service.js
class WalletService {
constructor(panClient, db) {
this.pan = panClient;
this.db = db;
}
/**
* Crea una wallet para un usuario nuevo
*/
async createForUser(user) {
// Verificar que no tenga wallet
const existing = await this.db.users.findOne({
where: { id: user.id, panWalletId: { not: null } }
});
if (existing?.panWalletId) {
throw new Error('Usuario ya tiene wallet');
}
// Crear wallet en Pan
const wallet = await this.pan.wallet.create({
userId: user.id,
email: user.email,
metadata: {
name: user.name,
createdAt: new Date().toISOString()
}
});
// Guardar en base de datos
await this.db.users.update({
where: { id: user.id },
data: {
panWalletId: wallet.id,
panWalletAddress: wallet.address
}
});
return wallet;
}
/**
* Obtiene la wallet de un usuario
*/
async getForUser(userId) {
const user = await this.db.users.findUnique({
where: { id: userId },
select: { panWalletId: true }
});
if (!user?.panWalletId) {
return null;
}
return await this.pan.wallet.get(userId);
}
/**
* Obtiene balances formateados para mostrar en UI
*/
async getBalancesForDisplay(userId) {
const user = await this.db.users.findUnique({
where: { id: userId }
});
if (!user?.panWalletId) {
return { hasWallet: false, balances: [] };
}
const response = await this.pan.wallet.getBalances(user.panWalletId);
// Formatear para UI
const formatted = [];
for (const chainData of response.chains) {
for (const token of chainData.tokens) {
if (parseFloat(token.balanceFormatted) > 0) {
formatted.push({
chain: this.formatChainName(chainData.chain),
chainId: chainData.chain,
token: token.asset,
amount: token.balanceFormatted,
usd: token.valueUsd.toFixed(2)
});
}
}
}
return {
hasWallet: true,
address: user.panWalletAddress,
balances: formatted,
totalUsd: response.totalValueUsd.toFixed(2)
};
}
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;
}
}
export default WalletService;
Uso con React
Copy
// hooks/useWallet.js
import { useState, useEffect } from 'react';
import { usePan } from './usePan';
export function useWallet(userId) {
const pan = usePan();
const [wallet, setWallet] = useState(null);
const [balances, setBalances] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
async function loadWallet() {
try {
setLoading(true);
// Obtener wallet
const w = await pan.wallet.get(userId);
setWallet(w);
// Obtener balances
const b = await pan.wallet.getBalances(w.id);
setBalances(b);
} catch (err) {
if (err.code !== 'WALLET_NOT_FOUND') {
setError(err);
}
} finally {
setLoading(false);
}
}
if (userId) {
loadWallet();
}
}, [userId, pan]);
const createWallet = async () => {
try {
setLoading(true);
const w = await pan.wallet.create({ userId });
setWallet(w);
return w;
} catch (err) {
setError(err);
throw err;
} finally {
setLoading(false);
}
};
const refreshBalances = async () => {
if (!wallet) return;
const b = await pan.wallet.getBalances(wallet.id);
setBalances(b);
};
return {
wallet,
balances,
loading,
error,
createWallet,
refreshBalances,
hasWallet: !!wallet
};
}
Copy
// components/WalletCard.jsx
function WalletCard({ userId }) {
const { wallet, balances, loading, createWallet, hasWallet } = useWallet(userId);
if (loading) {
return <div>Cargando wallet...</div>;
}
if (!hasWallet) {
return (
<div className="wallet-card empty">
<h3>No tienes una wallet</h3>
<button onClick={createWallet}>
Crear Wallet
</button>
</div>
);
}
return (
<div className="wallet-card">
<h3>Tu Wallet</h3>
<p className="address">{wallet.address}</p>
<h4>Balances</h4>
{balances?.totalValueUsd > 0 ? (
<>
{balances.chains.map(chainData =>
chainData.tokens.map(token => (
<div key={`${chainData.chain}-${token.asset}`} className="balance-row">
<span>{chainData.chain}</span>
<span>{token.balanceFormatted} {token.asset}</span>
<span>${token.valueUsd.toFixed(2)}</span>
</div>
))
)}
<div className="total">
Total: ${balances.totalValueUsd.toFixed(2)}
</div>
</>
) : (
<p>Sin balances. Deposita fondos a tu direccion.</p>
)}
</div>
);
}
