Skip to main content
Esta guia te muestra como ejecutar diferentes tipos de intents, monitorear su progreso, y manejar los resultados.

Ejecutar un Intent de Lending

El caso mas comun: depositar fondos para ganar intereses.
import { Pan } from '@pan/sdk';

const pan = new Pan({ apiKey: process.env.PAN_API_KEY });

// Crear intent de lending
const intent = await pan.lend({
  walletId: 'pan_wallet_abc123',
  amount: 1000,
  asset: 'USDC'
  // No especificamos chain - Pan elige la mejor
});

console.log('Intent creado:', intent.id);
console.log('Estado:', intent.status);
console.log('Estrategia:', intent.executionPlan?.strategy);

Flujo Completo con Monitoreo

async function ejecutarLending(walletId, amount) {
  console.log(`Iniciando lending de ${amount} USDC...`);

  // 1. Verificar balances primero
  const balances = await pan.wallet.getBalances(walletId);
  console.log(`Balance total: $${balances.totalValueUsd}`);

  // 2. Verificar yields disponibles
  const yields = await pan.yields.getAll();
  console.log(`Mejor APY: ${yields.best.apy}% en ${yields.best.chain}`);

  // 3. Crear intent
  const intent = await pan.lend({
    walletId,
    amount,
    asset: 'USDC'
  });

  console.log(`Intent creado: ${intent.id}`);
  console.log(`Plan: ${intent.executionPlan?.strategy}`);

  // 4. Monitorear ejecucion
  const resultado = await monitorearIntent(intent.id);

  // 5. Mostrar resultado
  console.log('\n=== Resultado ===');
  console.log(`Estado: ${resultado.status}`);
  console.log(`Gas total: $${resultado.results.totalGasCostUsd}`);
  console.log(`Monto final: ${resultado.results.finalAmount} USDC`);
  console.log(`APY: ${resultado.results.apy}%`);

  return resultado;
}

async function monitorearIntent(intentId) {
  const maxIntentos = 120; // 10 minutos
  let intentos = 0;

  while (intentos < maxIntentos) {
    const intent = await pan.getIntent(intentId);

    // Mostrar progreso
    if (intent.executionPlan) {
      const total = intent.executionPlan.steps.length;
      const completados = intent.results?.completedSteps || 0;
      console.log(`  [${completados}/${total}] ${intent.status}`);
    }

    // Verificar estado final
    if (intent.status === 'completed') {
      return intent;
    }

    if (intent.status === 'failed') {
      throw new Error(`Intent fallido: ${intent.error.message}`);
    }

    // Esperar 5 segundos
    await new Promise(r => setTimeout(r, 5000));
    intentos++;
  }

  throw new Error('Timeout esperando intent');
}

// Ejecutar
await ejecutarLending('pan_wallet_abc123', 1000);

Ejecutar Withdraw

Retirar fondos de un protocolo de lending:
const intent = await pan.withdraw({
  walletId: 'pan_wallet_abc123',
  amount: 500,
  asset: 'USDC',
  chain: 'base' // Requerido: de donde retirar
});

const resultado = await monitorearIntent(intent.id);
console.log(`Retirado: ${resultado.results.finalAmount} USDC`);

Ejecutar Bridge

Mover fondos entre chains:
const intent = await pan.bridge({
  walletId: 'pan_wallet_abc123',
  amount: 500,
  asset: 'USDC',
  fromChain: 'arbitrum',
  toChain: 'base'
});

const resultado = await monitorearIntent(intent.id);
console.log(`Transferido a Base: ${resultado.results.finalAmount} USDC`);

Verificar Antes de Ejecutar

Siempre verifica que hay fondos suficientes:
async function verificarYEjecutar(walletId, amount, asset = 'USDC') {
  // 1. Obtener balances
  const response = await pan.wallet.getBalances(walletId);

  // 2. Calcular total del asset
  let totalAsset = 0;
  for (const chainData of response.chains) {
    const token = chainData.tokens.find(t => t.asset === asset);
    if (token) {
      totalAsset += parseFloat(token.balanceFormatted);
    }
  }

  console.log(`${asset} disponible: ${totalAsset}`);
  console.log(`${asset} requerido: ${amount}`);

  // 3. Verificar suficiencia
  if (totalAsset < amount) {
    throw new Error(
      `Fondos insuficientes. Tienes ${totalAsset} ${asset}, necesitas ${amount}`
    );
  }

  // 4. Obtener mejor yield
  const yields = await pan.yields.getAll();
  console.log(`Mejor APY: ${yields.best.apy}% en ${yields.best.chain}`);

  // 5. Confirmar con usuario (opcional)
  const confirmar = await preguntarUsuario(
    `Depositar ${amount} ${asset} a ${yields.best.apy}% APY?`
  );

  if (!confirmar) {
    console.log('Operacion cancelada');
    return null;
  }

  // 6. Ejecutar
  const intent = await pan.lend({ walletId, amount, asset });
  return await monitorearIntent(intent.id);
}

Mostrar Progreso al Usuario

class IntentTracker {
  constructor(onUpdate) {
    this.onUpdate = onUpdate;
  }

  async track(intentId) {
    while (true) {
      const intent = await pan.getIntent(intentId);

      // Calcular progreso
      const progress = this.calcularProgreso(intent);

      // Notificar UI
      this.onUpdate({
        status: intent.status,
        progress,
        step: this.getCurrentStep(intent),
        transactions: intent.results?.transactions || [],
        error: intent.error
      });

      // Verificar fin
      if (intent.status === 'completed' || intent.status === 'failed') {
        return intent;
      }

      await new Promise(r => setTimeout(r, 5000));
    }
  }

  calcularProgreso(intent) {
    if (!intent.executionPlan) return 0;
    const total = intent.executionPlan.steps.length;
    const completados = intent.results?.completedSteps || 0;

    // Ajustar por estado actual
    if (intent.status === 'planning') return 5;
    if (intent.status === 'completed') return 100;

    return Math.round(10 + (completados / total) * 85);
  }

  getCurrentStep(intent) {
    if (intent.status === 'planning') {
      return { type: 'planning', message: 'Calculando mejor estrategia...' };
    }

    if (!intent.executionPlan) return null;

    const completados = intent.results?.completedSteps || 0;
    const pasoActual = intent.executionPlan.steps[completados];

    if (!pasoActual) return null;

    const mensajes = {
      bridge: `Transfiriendo de ${pasoActual.from} a ${pasoActual.to}...`,
      deposit: `Depositando en ${pasoActual.protocol}...`,
      withdraw: `Retirando de ${pasoActual.protocol}...`,
      swap: `Intercambiando tokens...`
    };

    return {
      type: pasoActual.type,
      message: mensajes[pasoActual.type] || 'Procesando...'
    };
  }
}

// Uso
const tracker = new IntentTracker((update) => {
  console.log(`[${update.progress}%] ${update.step?.message || update.status}`);

  // Actualizar UI
  setProgress(update.progress);
  setMessage(update.step?.message);

  // Mostrar transacciones completadas
  update.transactions.forEach(tx => {
    console.log(`  ✓ ${tx.type}: ${tx.txHash}`);
  });
});

const resultado = await tracker.track(intent.id);

Manejo de Errores

async function ejecutarConManejo(params) {
  try {
    const intent = await pan.lend(params);
    return await monitorearIntent(intent.id);
  } catch (error) {
    switch (error.code) {
      case 'INSUFFICIENT_FUNDS':
        console.error('No hay fondos suficientes');
        console.error(`Necesitas: ${error.details.required}`);
        console.error(`Tienes: ${error.details.available}`);
        // Mostrar UI para depositar
        break;

      case 'WALLET_NOT_FOUND':
        console.error('Wallet no encontrada');
        // Crear wallet primero
        break;

      case 'BRIDGE_FAILED':
        console.error('Bridge fallo - los fondos estan seguros en origen');
        // Reintentar o notificar usuario
        break;

      case 'DEPOSIT_FAILED':
        console.error('Deposito fallo - fondos en wallet de destino');
        // Reintentar deposito
        break;

      case 'RATE_LIMITED':
        console.error('Demasiadas solicitudes - espera un momento');
        await new Promise(r => setTimeout(r, 60000));
        return ejecutarConManejo(params); // Reintentar
        break;

      default:
        console.error('Error inesperado:', error.message);
        throw error;
    }
  }
}

Reintentar Intents Fallidos

async function ejecutarConReintentos(params, maxReintentos = 3) {
  const erroresReintentables = [
    'BRIDGE_TIMEOUT',
    'NETWORK_ERROR',
    'RPC_ERROR'
  ];

  for (let intento = 1; intento <= maxReintentos; intento++) {
    try {
      console.log(`Intento ${intento}/${maxReintentos}...`);

      const intent = await pan.lend(params);
      return await monitorearIntent(intent.id);

    } catch (error) {
      console.error(`Intento ${intento} fallido:`, error.message);

      // Solo reintentar errores transitorios
      if (!erroresReintentables.includes(error.code)) {
        throw error;
      }

      // Backoff exponencial
      const espera = Math.pow(2, intento) * 5000; // 10s, 20s, 40s
      console.log(`Esperando ${espera/1000}s antes de reintentar...`);
      await new Promise(r => setTimeout(r, espera));
    }
  }

  throw new Error(`Fallido despues de ${maxReintentos} intentos`);
}

Ejemplo con React

import { useState, useCallback } from 'react';
import { usePan } from './hooks/usePan';

function LendingForm({ walletId }) {
  const pan = usePan();
  const [amount, setAmount] = useState('');
  const [status, setStatus] = useState('idle');
  const [progress, setProgress] = useState(0);
  const [error, setError] = useState(null);
  const [result, setResult] = useState(null);

  const ejecutar = useCallback(async () => {
    try {
      setStatus('loading');
      setError(null);
      setProgress(0);

      // Crear intent
      const intent = await pan.lend({
        walletId,
        amount: parseFloat(amount),
        asset: 'USDC'
      });

      setStatus('executing');

      // Monitorear
      while (true) {
        const updated = await pan.getIntent(intent.id);

        // Actualizar progreso
        if (updated.executionPlan) {
          const total = updated.executionPlan.steps.length;
          const done = updated.results?.completedSteps || 0;
          setProgress(Math.round((done / total) * 100));
        }

        // Verificar fin
        if (updated.status === 'completed') {
          setStatus('completed');
          setResult(updated.results);
          break;
        }

        if (updated.status === 'failed') {
          throw new Error(updated.error.message);
        }

        await new Promise(r => setTimeout(r, 5000));
      }
    } catch (err) {
      setStatus('error');
      setError(err.message);
    }
  }, [pan, walletId, amount]);

  return (
    <div className="lending-form">
      {status === 'idle' && (
        <>
          <input
            type="number"
            value={amount}
            onChange={(e) => setAmount(e.target.value)}
            placeholder="Cantidad USDC"
          />
          <button onClick={ejecutar} disabled={!amount}>
            Depositar
          </button>
        </>
      )}

      {status === 'loading' && (
        <div>Creando operacion...</div>
      )}

      {status === 'executing' && (
        <div>
          <div>Ejecutando... {progress}%</div>
          <progress value={progress} max="100" />
        </div>
      )}

      {status === 'completed' && (
        <div className="success">
          <h3>Completado!</h3>
          <p>Monto depositado: {result.finalAmount} USDC</p>
          <p>APY: {result.apy}%</p>
          <p>Gas: ${result.totalGasCostUsd}</p>
          <button onClick={() => setStatus('idle')}>
            Nueva operacion
          </button>
        </div>
      )}

      {status === 'error' && (
        <div className="error">
          <p>Error: {error}</p>
          <button onClick={() => setStatus('idle')}>
            Reintentar
          </button>
        </div>
      )}
    </div>
  );
}

Proximos Pasos