lending-instructions

StefanIliev545's avatarfrom StefanIliev545

Load when working with KapanRouter instructions, UTXO tracking, flash loan flows, or debugging transaction failures

⭐1stars🔀1forks📁View on GitHub🕐Updated Jan 4, 2026

When & Why to Use This Skill

This Claude skill provides comprehensive technical guidance for the Kapan Lending Instructions System, a UTXO-based architecture designed for complex DeFi operations. It assists developers in constructing multi-step transaction flows, accurately tracking output indices (UTXOs), managing flash loan repayment logic, and troubleshooting execution failures within the KapanRouter ecosystem.

Use Cases

  • Constructing DeFi Transaction Flows: Building precise sequences of instructions for lending operations such as deposits, collateral withdrawals, and swaps while ensuring correct UTXO index referencing.
  • Debugging Transaction Failures: Analyzing failed blockchain transactions by manually tracking UTXO indices and identifying synchronization errors, particularly the index jumps caused by 'Approve' instructions.
  • Flash Loan Implementation: Designing complex flash loan patterns across multiple providers (Balancer, Aave, Morpho) and correctly calculating repayment amounts and fee buffers using 'Split' instructions.
  • Protocol Context Encoding: Generating the specific context bytes required for different lending protocols, such as market addresses for Compound or MarketParams tuples for Morpho.
namelending-instructions
descriptionLoad when working with KapanRouter instructions, UTXO tracking, flash loan flows, or debugging transaction failures

Kapan Lending Instructions System

The KapanRouter uses a UTXO-based instruction system where each operation can consume inputs from previous outputs and produce new outputs. Understanding this is critical for building correct flows.

Core Concepts

UTXO (Unspent Transaction Output)

Each instruction can create outputs that subsequent instructions reference by index. The router maintains an outputs[] array during execution.

Two Instruction Types

  1. Router Instructions (protocolName: "router") - Handled by KapanRouter directly
  2. Protocol Instructions (protocolName: "aave", "compound", etc.) - Delegated to gateways

Router Instructions

Defined in utils/v2/instructionHelpers.ts:4-13:

Type Creates UTXO? Description
FlashLoan Yes Borrows from flash loan provider, creates output for repayment amount
PullToken No Pulls tokens from user to router (requires user approval)
PushToken No Sends tokens from UTXO to user
ToOutput Yes Creates a UTXO from hardcoded amount/token (no actual transfer)
Approve Yes (empty) Approves gateway to spend UTXO. Creates empty output for index sync!
Split Yes (2) Splits UTXO into fee portion + remainder (for Aave flash loan fees)
Add Yes Combines two UTXOs of same token
Subtract Yes Subtracts one UTXO from another

Critical: Approve Creates Empty Output

The Approve instruction creates an empty output to maintain index synchronization. This is why output indices jump after approvals:

// After Approve(2), the next output is index 3, not 2!
addRouter(encodeApprove(2, "aave"), true); // createsUtxo=true

Lending Operations

Defined in utils/v2/instructionHelpers.ts:26-36:

Op Creates UTXO? Description
Deposit No Supply tokens to lending protocol
DepositCollateral No Supply as collateral (Morpho, Compound)
WithdrawCollateral Yes Withdraw collateral, creates output
Borrow Yes Borrow tokens, creates output
Repay Yes Repay debt, creates refund output (usually 0)
GetBorrowBalance Yes Query debt balance, creates output with amount
GetSupplyBalance Yes Query supply balance, creates output with amount
Swap Yes (2) Swap via DEX, creates (tokenOut, refund) outputs
SwapExactOut Yes (2) Exact output swap, creates (tokenOut, refund) outputs

Input Index Conventions

  • inputIndex: 999 - Ignore input, use amount parameter directly
  • inputIndex: 0-998 - Read amount/token from outputs[inputIndex]

Flash Loan Providers

enum FlashLoanProvider {
  BalancerV2 = 0,  // 0% fee
  BalancerV3 = 1,  // 0% fee  
  Aave = 2,        // ~5-9 bps fee
  ZeroLend = 3,    // ~5 bps fee
  UniswapV3 = 4,   // Requires pool address
  Morpho = 5,      // 0% fee
}

Important: Aave/ZeroLend flash loans create an output with the repayment amount (principal + fee), not the borrowed amount. The router receives the principal but must track repayment separately.

Common Flows

Basic Deposit

[
  PullToken(amount, token, user),      // No output
  Approve(0, protocol),                // Output[0] = empty (for sync)
  DepositCollateral(token, user, 0, inputIndex=0),  // No output
]

Withdraw Max

[
  GetSupplyBalance(token, user),       // Output[0] = balance
  WithdrawCollateral(token, user, inputIndex=0),  // Output[1] = withdrawn
  PushToken(1, user),                  // No output
]

Flash Loan Pattern

[
  ToOutput(amount, token),             // Output[0] = virtual UTXO
  FlashLoan(provider, inputIndex=0),   // Output[1] = repayment amount
  // ... use borrowed funds (router has Output[0] worth of tokens)
  // ... must leave Output[1] worth in router for repayment
]

Aave Flash Loan with Fee (Max Withdrawal)

When using Aave flash loans for max operations, use Split to account for fees:

[
  GetSupplyBalance(token),             // Output[0] = 100
  Split(0, 9),                         // Output[1] = 0.09 (fee buffer), Output[2] = 99.91
  FlashLoan(Aave, 2),                  // Output[3] = 100 (repayment), router has 99.91
  // ... operations using Output[2] (what we actually have)
  // ... withdraw Output[3] worth to repay
]

File Locations

  • Instruction Helpers: packages/nextjs/utils/v2/instructionHelpers.ts
  • Flow Builders: packages/nextjs/hooks/useKapanRouterV2.tsx:219-936
  • Move Builder: packages/nextjs/hooks/useKapanRouterV2.tsx:1519-1631
  • Starknet Instructions: packages/nextjs/hooks/useStarknetMovePosition.ts

Debugging Tips

  1. Track UTXO indices manually - Draw out the output array as you build instructions
  2. Approve creates empty output - Always increment your expected index after Approve
  3. Flash loan outputs - Remember: Aave Output = repayment (more than borrowed)
  4. Use simulation - simulateInstructions() catches most errors before execution
  5. Check authorization - getAuthorizations() returns needed approvals

Protocol Context Encoding

Different protocols need different context bytes:

// Aave: empty context
context = "0x"

// Compound: market address
context = encodeAbiParameters([{ type: "address" }], [marketAddress])

// Morpho: full MarketParams tuple
context = encodeMorphoContext({ loanToken, collateralToken, oracle, irm, lltv })