Consulta Basica
Copy
const yields = await pan.yields.getAll();
console.log('Mejor APY:', yields.best);
console.log('Todos los yields:', yields.rates);
Estructura de Respuesta
Copy
{
"rates": [
{
"chain": "base",
"protocol": "aave",
"asset": "USDC",
"apy": 8.52
},
{
"chain": "arbitrum",
"protocol": "aave",
"asset": "USDC",
"apy": 7.23
},
{
"chain": "ethereum",
"protocol": "aave",
"asset": "USDC",
"apy": 5.15
}
],
"best": {
"chain": "base",
"asset": "USDC",
"apy": 8.52,
"reasoning": "Highest APY available"
}
}
Campos de Rate
| Campo | Tipo | Descripcion |
|---|---|---|
chain | string | Nombre de la chain |
protocol | string | Protocolo DeFi |
asset | string | Token soportado |
apy | number | APY actual (porcentaje) |
Mostrar Yields al Usuario
Copy
async function mostrarOportunidades() {
const { rates, best } = await pan.yields.getAll();
console.log('\n=== Mejores Oportunidades de Rendimiento ===\n');
// Ordenar por APY descendente
const ordenados = [...rates].sort((a, b) => b.apy - a.apy);
ordenados.forEach((y, i) => {
const mejor = y.chain === best.chain ? ' (MEJOR)' : '';
console.log(`${i + 1}. ${y.chain} - ${y.protocol}`);
console.log(` APY: ${y.apy.toFixed(2)}%${mejor}`);
console.log('');
});
}
Decidir Donde Depositar
Copy
async function analizarMejorOpcion(walletId, amount) {
// 1. Obtener balances y yields
const [balancesResponse, yieldsResponse] = await Promise.all([
pan.wallet.getBalances(walletId),
pan.yields.getAll()
]);
const { rates, best } = yieldsResponse;
// 2. Encontrar donde estan los fondos
const fuentes = [];
for (const chainData of balancesResponse.chains) {
const usdc = chainData.tokens.find(t => t.asset === 'USDC');
if (usdc && parseFloat(usdc.balanceFormatted) > 0) {
fuentes.push({
chain: chainData.chain,
balance: parseFloat(usdc.balanceFormatted)
});
}
}
// 3. Calcular escenarios
console.log('\n=== Analisis de Opciones ===\n');
// Opcion A: Depositar donde estan los fondos (sin bridge)
for (const fuente of fuentes) {
const yieldLocal = rates.find(y => y.chain === fuente.chain);
if (yieldLocal) {
const gananciaAnual = fuente.balance * (yieldLocal.apy / 100);
console.log(`Sin bridge (${fuente.chain}):`);
console.log(` APY: ${yieldLocal.apy}%`);
console.log(` Ganancia anual estimada: $${gananciaAnual.toFixed(2)}`);
console.log('');
}
}
// Opcion B: Bridge al mejor APY
const gananciaOptima = amount * (best.apy / 100);
console.log(`Con bridge a ${best.chain}:`);
console.log(` APY: ${best.apy}%`);
console.log(` Ganancia anual estimada: $${gananciaOptima.toFixed(2)}`);
// 4. Calcular si vale la pena el bridge
const apyLocal = rates.find(y => fuentes.some(f => f.chain === y.chain))?.apy || 0;
const diferenciaApy = best.apy - apyLocal;
const gananciaExtraAnual = amount * (diferenciaApy / 100);
// Asumir costo de bridge ~$3
const costoBridge = 3;
const diasParaRecuperar = (costoBridge / gananciaExtraAnual) * 365;
console.log(`\nAnalisis:`);
console.log(` Diferencia APY: ${diferenciaApy.toFixed(2)}%`);
console.log(` Ganancia extra anual: $${gananciaExtraAnual.toFixed(2)}`);
console.log(` Dias para recuperar costo de bridge: ${diasParaRecuperar.toFixed(0)}`);
if (diasParaRecuperar < 30) {
console.log(` Recomendacion: Bridge a ${best.chain}`);
return { action: 'bridge', target: best.chain };
} else {
console.log(` Recomendacion: Depositar localmente`);
return { action: 'local', target: fuentes[0]?.chain };
}
}
Componente React de Yields
Copy
import { useState, useEffect } from 'react';
function YieldsTable() {
const [yields, setYields] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function cargar() {
const data = await pan.yields.getAll();
setYields(data);
setLoading(false);
}
cargar();
}, []);
if (loading) return <div>Cargando yields...</div>;
return (
<div className="yields-table">
<h3>Oportunidades de Rendimiento</h3>
<div className="best-yield">
<span>Mejor APY:</span>
<span className="apy">{yields.best.apy.toFixed(2)}%</span>
<span className="chain">en {yields.best.chain}</span>
</div>
<table>
<thead>
<tr>
<th>Chain</th>
<th>Protocolo</th>
<th>APY</th>
</tr>
</thead>
<tbody>
{yields.rates
.sort((a, b) => b.apy - a.apy)
.map((y) => (
<tr
key={`${y.chain}-${y.protocol}`}
className={y.chain === yields.best.chain ? 'best' : ''}
>
<td>
<ChainIcon chain={y.chain} />
{y.chain}
</td>
<td>
<ProtocolIcon protocol={y.protocol} />
{y.protocol}
</td>
<td className="apy">
{y.apy.toFixed(2)}%
</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
function RiskBadge({ level }) {
const colors = {
low: 'green',
medium: 'yellow',
high: 'red'
};
const labels = {
low: 'Bajo',
medium: 'Medio',
high: 'Alto'
};
return (
<span className={`risk-badge ${colors[level]}`}>
{labels[level]}
</span>
);
}
Calcular Ganancias Estimadas
Copy
function calcularGanancias(monto, apy, periodos = [30, 90, 365]) {
const tasaDiaria = apy / 365 / 100;
return periodos.map(dias => {
// Interes compuesto diario
const montoFinal = monto * Math.pow(1 + tasaDiaria, dias);
const ganancia = montoFinal - monto;
return {
dias,
periodo: dias === 30 ? '1 mes' : dias === 90 ? '3 meses' : '1 año',
montoFinal: montoFinal.toFixed(2),
ganancia: ganancia.toFixed(2),
porcentaje: ((ganancia / monto) * 100).toFixed(2)
};
});
}
// Uso
const ganancias = calcularGanancias(1000, 8.52);
console.log(ganancias);
// [
// { dias: 30, periodo: '1 mes', montoFinal: '1006.97', ganancia: '6.97', porcentaje: '0.70' },
// { dias: 90, periodo: '3 meses', montoFinal: '1021.12', ganancia: '21.12', porcentaje: '2.11' },
// { dias: 365, periodo: '1 año', montoFinal: '1088.97', ganancia: '88.97', porcentaje: '8.90' }
// ]
Alertas de APY
Copy
class YieldMonitor {
constructor(minApy) {
this.minApy = minApy;
this.lastBestApy = null;
}
async check() {
const { best } = await pan.yields.getAll();
const alerts = [];
// Alerta si APY supera umbral
if (best.apy >= this.minApy) {
alerts.push({
type: 'high_apy',
message: `APY de ${best.apy}% disponible en ${best.chain}`,
yield: best
});
}
// Alerta si APY cambio significativamente
if (this.lastBestApy) {
const cambio = best.apy - this.lastBestApy;
if (Math.abs(cambio) > 1) {
alerts.push({
type: 'apy_change',
message: `APY ${cambio > 0 ? 'subio' : 'bajo'} ${Math.abs(cambio).toFixed(2)}%`,
from: this.lastBestApy,
to: best.apy
});
}
}
this.lastBestApy = best.apy;
return alerts;
}
async startMonitoring(intervalMs = 300000, onAlert) {
// Check inicial
const alerts = await this.check();
if (alerts.length) onAlert(alerts);
// Monitoreo periodico
setInterval(async () => {
const alerts = await this.check();
if (alerts.length) onAlert(alerts);
}, intervalMs);
}
}
// Uso
const monitor = new YieldMonitor(8.0);
monitor.startMonitoring(300000, (alerts) => {
alerts.forEach(alert => {
console.log(`[ALERTA] ${alert.message}`);
// Enviar notificacion, email, etc.
});
});
