> For the complete documentation index, see [llms.txt](https://knox-fi.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://knox-fi.gitbook.io/docs/integration-guide.md).

# Integration Guide

This guide shows developers how to integrate with Knox Protocol: depositing, querying positions, and withdrawing.

### Quick Start

{% stepper %}
{% step %}

### Find a Pool

Pools are deployed via `SpectrumFactory`. Query deployment events or maintain an off-chain index.

```solidity
// Listen for pool deployments
event PoolDeployed(
    address indexed accountant,
    address indexed seniorVault,
    address indexed juniorVault,
    address allocator,
    address underlyingMarket
);
```

{% endstep %}

{% step %}

### Check Pool Parameters

```solidity
ISpectrumAccountant accountant = ISpectrumAccountant(accountantAddress);

// Core parameters
uint128 seniorAPY = accountant.rSenior(); // e.g., 500 = 5%
uint128 maxSpectrumAPY = accountant.rMaxSpectrum(); // e.g., 2000 = 20%
uint128 gridStep = accountant.spectrumGridStep(); // e.g., 50 = 0.5%
uint128 period = accountant.dPeriod(); // seconds
uint256 maturity = accountant.tStart() + period;

// Capacity
uint256 seniorCap = accountant.seniorCapacity();
uint256 maxTotal = accountant.cMaxTotalDeposits();

// Fee
uint128 protocolFee = accountant.cProtocolFee(); // e.g., 500 = 5%
```

{% endstep %}

{% step %}

### Choose Your Tranche

Decision Tree:

Want guaranteed returns? → Senior

Want a specific cap with downside protection? → Spectrum (choose APY on grid)

Want maximum upside? → Junior
{% endstep %}
{% endstepper %}

### Depositing

#### Via Router (Recommended)

The `KnoxRouter` provides the cleanest UX:

**Senior Deposit**

```solidity
// Standard ERC20 approval
IERC20(asset).approve(address(router), amount);
router.depositSeniorSpectrum(accountant, amount, receiver);

// OR with Permit2 (no approval needed)
router.depositSeniorSpectrumPermit2(
    accountant,
    amount,
    receiver,
    permit2Signature
);
```

**Spectrum Deposit**

```solidity
// Choose an APY on the grid
uint128 desiredAPY = 700; // 7%

// Validate it's on the grid
require(desiredAPY > seniorAPY && desiredAPY <= maxSpectrumAPY);
require((desiredAPY - seniorAPY) % gridStep == 0);

// Deposit
IERC20(asset).approve(address(router), amount);
router.depositSpectrumTranche(accountant, desiredAPY, amount, receiver);

// OR with Permit2
router.depositSpectrumTranchePermit2(
    accountant,
    desiredAPY,
    amount,
    receiver,
    permit2Signature
);
```

**Junior Deposit**

```solidity
IERC20(asset).approve(address(router), amount);
router.depositJuniorSpectrum(accountant, amount, receiver);

// OR with Permit2
router.depositJuniorSpectrumPermit2(
    accountant,
    amount,
    receiver,
    permit2Signature
);
```

#### Via Accountant (Direct)

```solidity
// Senior
IERC20(asset).approve(address(accountant), amount);
accountant.depositSenior(amount, receiver);

// Spectrum
IERC20(asset).approve(address(accountant), amount);
accountant.depositSpectrum(apyBps, amount, receiver);

// Junior
IERC20(asset).approve(address(accountant), amount);
accountant.depositJunior(amount, receiver);
```

#### Via Vault (ERC4626)

Only for existing vaults (cannot create new spectrum vaults):

```solidity
address seniorVault = accountant.seniorTranche();
address juniorVault = accountant.juniorTranche();

// For spectrum, query existing vault
address spectrumVault = accountant.spectrumVaults(apyBps);
require(spectrumVault != address(0), "Vault doesn't exist yet");

// Deposit
IERC20(asset).approve(vaultAddress, amount);
IERC4626(vaultAddress).deposit(amount, receiver);
```

### Querying Positions

#### Tranche Shares

```solidity
// Get vault addresses
address seniorVault = accountant.seniorTranche();
address juniorVault = accountant.juniorTranche();
address spectrumVault = accountant.spectrumVaults(apyBps);

// Check balances
uint256 seniorShares = IERC4626(seniorVault).balanceOf(user);
uint256 juniorShares = IERC4626(juniorVault).balanceOf(user);
uint256 spectrumShares = IERC4626(spectrumVault).balanceOf(user);
```

#### Current Tranche Values

```solidity
// Senior value (compounded)
uint256 seniorValue = accountant.seniorTrancheCurrentValue();

// Spectrum value (compounded to cap)
uint256 spectrumValue = accountant.spectrumTrancheCurrentValue(apyBps);

// Junior value (residual after waterfall)
uint256 juniorValue = accountant.juniorTrancheCurrentValue();
```

#### User Share Value

```solidity
// Senior
uint256 seniorAssets = (seniorShares * seniorValue) / IERC4626(seniorVault).totalSupply();

// Spectrum
uint256 spectrumAssets = (spectrumShares * spectrumValue) / IERC4626(spectrumVault).totalSupply();

// Junior
uint256 juniorAssets = (juniorShares * juniorValue) / IERC4626(juniorVault).totalSupply();
```

#### Pool State

```solidity
enum PoolState { DEPLOYED, ACTIVE, REDEEMED, SETTLED }

PoolState state = accountant.poolState();

// Check if matured
bool matured = block.timestamp >= accountant.tStart() + accountant.dPeriod();

// Check async redemptions
if (state == PoolState.REDEEMED) {
    uint256 pendingExits = accountant.pendingExitCount();
    uint256 latestUnlock = accountant.maxPendingExitUnlockAt();
}
```

#### Underlying Asset Value

```solidity
// Total pool value from underlying market
uint256 underlyingValue = accountant.underlyingAssetCurrentValue();

// Asset address
address asset = accountant.asset();
```

### Withdrawing

#### After Settlement

Once the pool is in `SETTLED` state:

```solidity
require(accountant.poolState() == PoolState.SETTLED, "Not settled yet");

// Redeem all shares from a tranche
uint256 shares = IERC4626(vaultAddress).balanceOf(user);
uint256 assets = IERC4626(vaultAddress).redeem(
    shares,
    receiver, // receives assets
    owner     // owns the shares
);
```

Note: The last redeemer of a tranche gets the entire remaining balance (not a calculated proportion) to prevent dust.

#### Before Settlement

Withdrawals are blocked. However, tranche tokens are ERC-20s and can be:

* Transferred to another address
* Sold on secondary markets
* Used as collateral

```solidity
// Transfer shares
IERC20(vaultAddress).transfer(recipient, shares);

// Approve for trading
IERC20(vaultAddress).approve(dexRouter, shares);
```

### Monitoring Settlement

#### Check Redemption Progress

```solidity
// Pool state
PoolState state = accountant.poolState();

if (state == PoolState.ACTIVE && block.timestamp >= maturity) {
    // Matured but not yet redeemed
    // Anyone can call redeemShares()
}

if (state == PoolState.REDEEMED) {
    // Redemption in progress
    uint256 pending = accountant.pendingExitCount();
    if (pending > 0) {
        // Async redemptions pending
        uint256 unlock = accountant.maxPendingExitUnlockAt();
        // Cooldown expires at `unlock` timestamp
        // Anyone can call claimRedeemedAssets() after cooldowns
    }
}

if (state == PoolState.SETTLED) {
    // Settlement complete, withdrawals open
}
```

#### Trigger Redemption (Permissionless)

```solidity
// After maturity, anyone can call
accountant.redeemShares(0); // 0 = redeem all

// For async allocators, claim when ready
accountant.claimRedeemedAssets();
```

### Advanced: Reading Waterfall Results

#### Per-Tranche Payouts

```solidity
// Only available after SETTLED
require(accountant.poolState() == PoolState.SETTLED);

// Each vault's balance is its final payout
uint256 seniorPayout = IERC20(asset).balanceOf(seniorVault);
uint256 juniorPayout = IERC20(asset).balanceOf(juniorVault);
uint256 spectrumPayout = IERC20(asset).balanceOf(spectrumVault);
```

#### Protocol Fee Collected

```solidity
// Protocol fee beneficiary balance increased by fee amount
address feeBeneficiary = accountant.protocolFeeBeneficiary();
uint256 feeBalance = IERC20(asset).balanceOf(feeBeneficiary);
```

### Common Patterns

#### Deposit & Track Pattern

```solidity
contract MyVaultIntegration {
    struct Position {
        address vault;
        uint256 shares;
        uint256 depositTimestamp;
    }

    mapping(address => Position[]) public userPositions;

    function depositAndTrack(address accountant, uint128 apyBps, uint256 amount) external {
        // Deposit
        IERC20(asset).transferFrom(msg.sender, address(this), amount);
        IERC20(asset).approve(router, amount);

        address vault;
        if (apyBps == 0) {
            // Senior
            router.depositSeniorSpectrum(accountant, amount, address(this));
            vault = ISpectrumAccountant(accountant).seniorTranche();
        } else {
            // Spectrum
            router.depositSpectrumTranche(accountant, apyBps, amount, address(this));
            vault = ISpectrumAccountant(accountant).spectrumVaults(apyBps);
        }

        // Track
        uint256 shares = IERC4626(vault).balanceOf(address(this));
        userPositions[msg.sender].push(Position({
            vault: vault,
            shares: shares,
            depositTimestamp: block.timestamp
        }));
    }
}
```

#### Auto-Withdraw Pattern

```solidity
function autoWithdraw(address accountant, address user) external {
    require(ISpectrumAccountant(accountant).poolState() == PoolState.SETTLED, "Not settled");

    Position[] memory positions = userPositions[user];
    for (uint256 i = 0; i < positions.length; i++) {
        uint256 shares = IERC4626(positions[i].vault).balanceOf(address(this));
        if (shares > 0) {
            IERC4626(positions[i].vault).redeem(shares, user, address(this));
        }
    }

    delete userPositions[user];
}
```

### Error Handling

#### Common Errors

```solidity
// Capacity exceeded
if (amount + currentSeniorDeposits > accountant.seniorCapacity()) {
    revert("Senior capacity exceeded");
}

// Spectrum rate not on grid
if ((apyBps - seniorAPY) % gridStep != 0) {
    revert("Rate not on grid");
}

// Pool matured
if (block.timestamp >= accountant.tStart() + accountant.dPeriod()) {
    revert("Pool has matured");
}

// Total deposits cap
if (totalDeposits + amount > accountant.cMaxTotalDeposits()) {
    revert("Total deposits cap exceeded");
}
```

### Frontend Integration Example

#### React Hook

```typescript
import { useContractRead } from 'wagmi';

function usePoolInfo(accountantAddress: string) {
    const { data: seniorAPY } = useContractRead({
        address: accountantAddress,
        abi: accountantABI,
        functionName: 'rSenior',
    });

    const { data: maxSpectrumAPY } = useContractRead({
        address: accountantAddress,
        abi: accountantABI,
        functionName: 'rMaxSpectrum',
    });

    const { data: gridStep } = useContractRead({
        address: accountantAddress,
        abi: accountantABI,
        functionName: 'spectrumGridStep',
    });

    const { data: seniorCapacity } = useContractRead({
        address: accountantAddress,
        abi: accountantABI,
        functionName: 'seniorCapacity',
    });

    // Compute spectrum grid
    const spectrumGrid = React.useMemo(() => {
        if (!seniorAPY || !maxSpectrumAPY || !gridStep) return [];
        const rates = [];
        for (let rate = seniorAPY + gridStep; rate <= maxSpectrumAPY; rate += gridStep) {
            rates.push(rate);
        }
        return rates;
    }, [seniorAPY, maxSpectrumAPY, gridStep]);

    return { seniorAPY, maxSpectrumAPY, gridStep, seniorCapacity, spectrumGrid };
}
```

### Key Takeaways

* Use the router for deposits — cleaner UX, handles approvals
* Permit2 variants eliminate approval transactions
* Tranche tokens are ERC-20s — transferable before settlement
* Withdrawals only after SETTLED — monitor pool state
* Permissionless redemption — anyone can trigger after maturity
* Async markets may have cooldown periods — check `maxPendingExitUnlockAt()`
* Last redeemer gets dust — no rounding issues
