Skip to content
Back to skills

Lending Instructions

ASecurity

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

  • 666 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added February 9, 2026
developmenttypescriptnextjsdebugging

Security analysis

A100/100

Scanned February 12, 2026

npx -y skills add majiayu000/claude-skill-registry --skill lending-instructions --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Lending Instructions?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Lending Instructions
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/majiayu000-lending-instructions/badge)](https://www.skillsdirectory.com/skills/majiayu000-lending-instructions)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: lending-instructions
description: Load 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:
```typescript
// 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

```typescript
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
```typescript
[
  PullToken(amount, token, user),      // No output
  Approve(0, protocol),                // Output[0] = empty (for sync)
  DepositCollateral(token, user, 0, inputIndex=0),  // No output
]
```

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

### Flash Loan Pattern
```typescript
[
  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:
```typescript
[
  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:

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

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

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

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.