Los Intents son el corazón de pan API. Representan objetivos financieros de alto nivel en lugar de transacciones especificas. En lugar de decirle a pan exactamente que transacciones ejecutar, expresas lo que quieres lograr y pan determina la mejor manera de hacerlo.
¿Qué es un Intent?
Un intent es una declaracion de un objetivo financiero. Cuando creas un intent, le dices a pan que quieres lograr, no como lograrlo.
Enfoque Tradicional // Especificas COMO hacerlo
await bridge ( 500 , 'arbitrum' , 'base' );
await approve ( usdc , aavePool );
await aavePool . supply ( usdc , 500 );
Enfoque Intent // Especificas QUE quieres
await pan . lend ({
walletId: 'wallet_123' ,
amount: 500 ,
asset: 'USDC'
});
El sistema de pan automaticamente:
Analiza el estado actual del wallet
Evalua protocolos DeFi y oportunidades disponibles
Genera un plan de ejecución optimizado
Ejecuta el plan de forma segura
Tu App envia request
POST /intents con action: lend, amount: 1000 USDC
Planificación
pan consulta balances (500 ARB, 500 Base) y APYs (Base 8.5%, ARB 7.2%)
Generación de plan
Plan optimo: bridge ARB a Base + deposit en Aave
Ejecución
pan ejecuta bridge y deposit, retornando txHashes
Resultado
Intent completado con resultados y costos de gas
Tipos de Intent
Lend (Prestar)
Deposita fondos en un protocolo de lending para ganar intereses:
const intent = await pan . lend ({
walletId: 'pan_wallet_abc123' ,
amount: 1000 ,
asset: 'USDC'
// chain es opcional - pan encuentra el mejor APY
});
Que hace pan:
Consulta APYs en todas las chains soportadas
Identifica donde están los fondos del wallet
Si es necesario, hace bridge a la chain con mejor APY
Deposita en el protocolo (Aave) automaticamente
Withdraw (Retirar)
Retira fondos de un protocolo de lending:
const intent = await pan . withdraw ({
walletId: 'pan_wallet_abc123' ,
amount: 500 ,
asset: 'USDC' ,
chain: 'base' // requerido para withdraw
});
Bridge
Mueve fondos entre chains:
const intent = await pan . bridge ({
walletId: 'pan_wallet_abc123' ,
amount: 500 ,
asset: 'USDC' ,
fromChain: 'ethereum' ,
toChain: 'arbitrum'
});
Parámetros de Intent
Comunes a todos los intents
Parámetro Tipo Requerido Descripción walletIdstring Si ID de la wallet pan actionstring Si lend, withdraw, o bridgeamountnumber Si Cantidad en unidades del token assetstring No Token a usar (default: USDC)
Específicos por accion
Lend:
Parámetro Tipo Requerido Descripción chainstring No Chain destino. Si no se especifica, pan elige la mejor
Withdraw:
Parámetro Tipo Requerido Descripción chainstring Si Chain de donde retirar
Bridge:
Parámetro Tipo Requerido Descripción fromChainstring Si Chain origen toChainstring Si Chain destino
Ciclo de Vida de un Intent
Los intents pasan por estados bien definidos:
Estado Descripción Siguiente pendingIntent creado y validado planningplanningAnalizando balances, consultando yields, generando plan executing o failedexecutingEjecutando pasos: bridges, deposits completed o failedcompletedTodas las transacciones confirmadas Final failedError registrado, fondos seguros Final
Pending
El intent ha sido creado y registrado en el sistema. Este estado es muy breve.
Planning
pan está analizando el wallet, evaluando protocolos, y generando el plan de ejecución optimo. {
"status" : "planning" ,
"executionPlan" : null // Aun no disponible
}
Que ocurre internamente:
Consulta balances del wallet en todas las chains
Obtiene APYs actuales de protocolos
Calcula rutas de bridge optimas
Estima costos de gas
Genera plan de ejecución
Executing
pan está ejecutando las transacciones en blockchain. {
"status" : "executing" ,
"executionPlan" : {
"strategy" : "single-bridge" ,
"steps" : [
{ "type" : "bridge" , "status" : "completed" },
{ "type" : "deposit" , "status" : "executing" }
]
},
"results" : {
"completedSteps" : 1 ,
"transactions" : [
{ "type" : "bridge" , "txHash" : "0x123..." }
]
}
}
Completed
Todas las operaciones completaron exitosamente. {
"status" : "completed" ,
"results" : {
"completedSteps" : 2 ,
"totalGasUsed" : "350000" ,
"totalGasCostUsd" : 2.30 ,
"finalAmount" : "998.50" ,
"apy" : 8.52
},
"completedAt" : "2024-01-15T10:45:30Z"
}
Failed
Algo salio mal durante la ejecución. {
"status" : "failed" ,
"error" : {
"code" : "INSUFFICIENT_FUNDS" ,
"message" : "Wallet does not have sufficient USDC" ,
"details" : {
"required" : "1000 USDC" ,
"available" : "750 USDC"
}
},
"results" : {
"completedSteps" : 1 ,
"failedStep" : 2
}
}
Monitorear Intents
La ejecución de intents es asincrona. Debes hacer polling para seguir el progreso:
async function esperarIntent ( intentId ) {
const maxIntentos = 60 ; // 5 minutos
let intentos = 0 ;
while ( intentos < maxIntentos ) {
const intent = await pan . getIntent ( intentId );
console . log ( `Estado: ${ intent . status } ` );
// Mostrar progreso si está ejecutando
if ( intent . status === 'executing' && intent . executionPlan ) {
const pasos = intent . executionPlan . steps ;
const completados = intent . results ?. completedSteps || 0 ;
console . log ( ` Progreso: ${ completados } / ${ pasos . length } ` );
// Mostrar transacciones completadas
intent . results ?. transactions ?. forEach ( tx => {
console . log ( ` ${ tx . type } : ${ tx . txHash } ` );
});
}
// Verificar estados finales
if ( intent . status === 'completed' ) {
console . log ( 'Intent completado!' );
console . log ( `Gas total: $ ${ intent . results . totalGasCostUsd } ` );
return intent ;
}
if ( intent . status === 'failed' ) {
console . error ( 'Intent fallido:' , intent . error . message );
throw new Error ( intent . error . message );
}
// Esperar 5 segundos
await new Promise ( r => setTimeout ( r , 5000 ));
intentos ++ ;
}
throw new Error ( 'Timeout esperando intent' );
}
Intervalo de polling recomendado : 5-10 segundos. Mas frecuente desperdicia recursos, menos frecuente reduce responsividad.
Estrategias de Ejecución
pan determina automáticamente la mejor estrategia basandose en el estado del wallet:
Situación Estrategia Pasos Fondos ya en mejor chain no-bridgeDeposito directo Fondos en una chain diferente single-bridge1 bridge + deposit Fondos en múltiples chains multi-bridgeN bridges + deposit Sin fondos suficientes insufficientError: fondos insuficientes
no-bridge
Los fondos ya están en la chain destino. Deposito directo.
{
"strategy" : "no-bridge" ,
"steps" : [
{ "type" : "deposit" , "chain" : "base" , "amount" : "1000" }
]
}
single-bridge
Los fondos están en una sola chain diferente. Un bridge + deposito.
{
"strategy" : "single-bridge" ,
"steps" : [
{ "type" : "bridge" , "from" : "arbitrum" , "to" : "base" , "amount" : "1000" },
{ "type" : "deposit" , "chain" : "base" , "amount" : "1000" }
]
}
multi-bridge
Los fondos están distribuidos en múltiples chains. Varios bridges + deposito.
{
"strategy" : "multi-bridge" ,
"steps" : [
{ "type" : "bridge" , "from" : "ethereum" , "to" : "base" , "amount" : "200" },
{ "type" : "bridge" , "from" : "arbitrum" , "to" : "base" , "amount" : "500" },
{ "type" : "deposit" , "chain" : "base" , "amount" : "1000" }
]
}
insufficient
No hay fondos suficientes. El intent falla en planning.
{
"status" : "failed" ,
"error" : {
"code" : "INSUFFICIENT_FUNDS" ,
"message" : "Not enough USDC across all chains" ,
"details" : {
"required" : "1000" ,
"available" : "750"
}
}
}
Manejo de Errores
Errores Comunes
Código Descripción Solución INSUFFICIENT_FUNDSNo hay fondos suficientes Depositar más fondos WALLET_NOT_FOUNDWallet no existe Verificar walletId INVALID_CHAINChain no soportada Usar chain válida BRIDGE_FAILEDFallo en bridge Reintentar intent DEPOSIT_FAILEDFallo en depósito Verificar protocolo
Estructura de Error
{
"error" : {
"code" : "ERROR_CODE" ,
"message" : "Mensaje legible para humanos" ,
"details" : {
"campo1" : "valor1" ,
"campo2" : "valor2"
}
}
}
Reintentar Intents Fallidos
async function ejecutarConReintento ( params , maxReintentos = 3 ) {
let ultimoError ;
for ( let i = 0 ; i < maxReintentos ; i ++ ) {
try {
const intent = await pan . lend ( params );
return await esperarIntent ( intent . id );
} catch ( error ) {
ultimoError = error ;
// Solo reintentar errores transitorios
const reintentables = [ 'BRIDGE_FAILED' , 'NETWORK_ERROR' , 'TIMEOUT' ];
if ( ! reintentables . includes ( error . code )) {
throw error ;
}
console . log ( `Reintento ${ i + 1 } / ${ maxReintentos } ...` );
await new Promise ( r => setTimeout ( r , 5000 * ( i + 1 ))); // Backoff
}
}
throw ultimoError ;
}
Ejemplos Avanzados
Lending con Verificacion de APY
async function lendSiApy ( walletId , amount , minApy ) {
// Obtener yields actuales
const { rates , best } = await pan . yields . getAll ();
// Verificar APY minimo
if ( best . apy < minApy ) {
console . log ( `APY actual ( ${ best . apy } %) menor a mínimo ( ${ minApy } %)` );
return null ;
}
// Crear intent
const intent = await pan . lend ({
walletId ,
amount ,
asset: 'USDC'
});
console . log ( `Lending a ${ best . chain } con ${ best . apy } % APY` );
return intent ;
}
// Uso: solo prestar si APY > 7%
await lendSiApy ( 'wallet_123' , 1000 , 7.0 );
Rebalanceo Automatico
async function rebalancearAMejorYield ( walletId ) {
// 1. Obtener balances actuales
const balancesResponse = await pan . wallet . getBalances ( walletId );
// 2. Calcular total disponible
let totalUsdc = 0 ;
for ( const chainData of balancesResponse . chains ) {
const usdc = chainData . tokens . find ( t => t . asset === 'USDC' );
if ( usdc ) totalUsdc += parseFloat ( usdc . balanceFormatted );
}
if ( totalUsdc === 0 ) {
console . log ( 'No hay USDC disponible' );
return ;
}
// 3. Obtener mejor yield
const { best } = await pan . yields . getAll ();
console . log ( `Mejor yield: ${ best . apy } % en ${ best . chain } ` );
// 4. Crear intent de lending
// pan automáticamente consolidara fondos de todas las chains
const intent = await pan . lend ({
walletId ,
amount: totalUsdc ,
asset: 'USDC'
});
return intent ;
}
Notificaciones de Progreso
async function ejecutarConNotificaciones ( walletId , amount , onProgress ) {
const intent = await pan . lend ({ walletId , amount , asset: 'USDC' });
let ultimoEstado = null ;
let ultimosPasos = 0 ;
while ( true ) {
const actual = await pan . getIntent ( intent . id );
// Notificar cambio de estado
if ( actual . status !== ultimoEstado ) {
onProgress ({
type: 'status' ,
status: actual . status ,
message: getStatusMessage ( actual . status )
});
ultimoEstado = actual . status ;
}
// Notificar pasos completados
const pasosCompletados = actual . results ?. completedSteps || 0 ;
if ( pasosCompletados > ultimosPasos ) {
const paso = actual . executionPlan . steps [ pasosCompletados - 1 ];
onProgress ({
type: 'step' ,
step: paso ,
completed: pasosCompletados ,
total: actual . executionPlan . steps . length
});
ultimosPasos = pasosCompletados ;
}
// Estado final
if ( actual . status === 'completed' || actual . status === 'failed' ) {
return actual ;
}
await new Promise ( r => setTimeout ( r , 5000 ));
}
}
function getStatusMessage ( status ) {
return {
pending: 'Iniciando...' ,
planning: 'Calculando mejor estrategia...' ,
executing: 'Ejecutando transacciones...' ,
completed: 'Completado!' ,
failed: 'Error en ejecución'
}[ status ];
}
// Uso
await ejecutarConNotificaciones ( 'wallet_123' , 1000 , ( progress ) => {
console . log ( progress );
// Enviar a UI, webhook, etc.
});
Próximos Pasos