# JR and YT Module

## GYT Markets and JR

This document provides a brief overview of the GYT markets including the current SLF GYT market and AAVE GYT market (the AAVE one will be similar to future ones for other protocols), and PTs (principal tokenization / JR Tokens).

***

### GYT Market

GYT Markets tokenize the yield from looping positions or YT tokens into tradeable GYT tokens. Each market instance is defined by a `convertId` that specifies the base token, expiry time, and whitelisted looper/YT configs.

#### Components

* `InterestConverter` / `AaveInterestTokenConverter`: Convert looping position/YT token (YT only for SLF market) into GYT token, and manage the interest and life-cycle of the converted positions.
* `ConvertTradeRouterGYT` / `ConvertTradeRouterAaveYT`: Allow trading of GYT tokens via signed orders, and order verification logic to ensure order validity.

The SLF and AAVE markets use separate (but similar) contract implementations:

|                     | SLF Market                                               | AAVE Market                                                              |
| ------------------- | -------------------------------------------------------- | ------------------------------------------------------------------------ |
| Converter           | `InterestConverter`                                      | `AaveInterestTokenConverter`                                             |
| Order Router        | `ConvertTradeRouterGYT`                                  | `ConvertTradeRouterAaveYT`                                               |
| Position Source     | `LooperCore` (position identified by `uint256 looperId`) | `AavePositionManager` (position identified by `address positionAccount`) |
| Order Token Sources | Looper position & YT token & minted GYT token            | AAVE position & minted GYT                                               |

Both converters do tracking of looper/position profit tracking using an index-based accounting and hold the position in order for lifecycle management

#### Market Order

The GYT market order is a signed order that allows users to trade GYT tokens. The order struct is shared across different router/converter pairs and order pools.

```solidity
struct UserSignableOrder {
    uint32 orderVersion;    // Reserved for future use, should be 0
    uint32 orderEpoch;      // Bump epoch to invalidate all previous orders
    uint64 deadline;        // Order valid until this timestamp
    uint128 flags;          // bit 0-1: order type:
                            //   00 -> looper/position order (sell collateral position for GYT payment)
                            //   01 -> YT order (sell YT tokens for GYT payment, SLF market only)
                            //   10 -> GYT order (sell GYT tokens, i.e. resale)
                            //   11 -> reserved
                            // bit 2: fill-or-kill (0 = partial fill allowed, 1 = all or nothing)
                            // bit 3-127: reserved
    uint256 conversionId;   // Conversion Id in InterestConverter
    bytes32 opaquePositionId; // For looper order (type 00): looper position id
                              // For YT/GYT order (type 01/10): user address (cast to bytes32)
    uint128 maxSoldAmount;  // Max amount to sell. uint128.max = no limit
    uint128 minPrice;       // Min acceptable price
    uint256 nonce;          // Prevent hash collision between orders with same parameters, should be random (no safe rng needed.)
}
```

#### Order Lifecycle

1. **Create Position**: User creates a looping position (via `LooperCore`/corresponding `PositionManager`) or acquires YT tokens (via `YTCore`, only available in SLF market) or GYT tokens from secondary market.
2. **Approve**: User approves position push (for looper/position orders) or token allowance (for YT/GYT orders) to the corresponding router.
3. **Sign & Submit**: User creates and signs an order with the appropriate `conversionId` and submits it to the off-chain order pool.
4. **Order Validation** (Taker): Maker submit order to order pool, then pool server calls `verifyOrder` on the router, which verifies:
   * Ownership and approval status
   * Position health (LTV under safe/given requirements)
   * Position belongs to a whitelisted looper config for the given `conversionId`
   * Remaining fillable amount (considering partial fills and available balance)
5. **Fill**: Taker get order from order pool server, then use the data to call `fillOrder`. The router handles each order in three paths based on order type:
   * **Looper/Position order**: On first fill, position is transferred from user to router via `push()`. Position is split if partially filled — filled portion goes to converter (minting GYT to buyer), unfilled portion stays in router. On subsequent fills, additional position (collateral and debt) is taken from the unfilled portion and merged into the existing filled position, then extra GYT is minted and sent to buyer.
   * **YT order**: YT tokens transferred from user to `InterestConverter`, which mints equivalent GYT to buyer, only available in SLF market.
   * **GYT order**: GYT tokens transferred directly from seller to buyer.
6. **Cancel**:
   * Off-chain: Retract signature from matching pool (unsafe — taker may still fill if they have the signature).
   * On-chain: Call `cancelOrder` to invalidate on-chain. Unfilled position is returned to the seller.
7. **Buy Back**: Seller calls `buyBackPosition` to reclaim the underlying position by returning the equivalent GYT amount. Interest is deducted during buyback:
   * Interest deduction formula: `earned = (currentIndex - indexWhenMint) × mintedAmount / currentIndex`
   * If `earned > 0`, the earned amount is skimmed from the position's collateral before returning the position.
   * **SLF only**: If `isYTLiquidated` is true, no GYT burn or skim occurs — position is returned as-is. AAVE converter always burns GYT and skims.
8. **Maturity**: After `expireTime` is reached:
   * Owner, upkeeper, or original position owner calls `matureLooper`/`matureYT` (SLF) / `maturePosition` (AAVE).
   * Interest is deducted using the same formula as buyback (skim from collateral).
   * Remaining position is returned to the original owner.
   * Skimmed interest is held in the converter and can be withdrawn by the contract owner (`withdrawERC20`) for later distribution (e.g., via Merkle airdrop).
   * Bot cancels all remaining unfilled orders for the matured position.
9. **Seize**: If a position cannot be matured normally because skimming the earned yield would not be possible (i.e., due to high ltv):
   * Owner or upkeeper calls `seizeForMature` (only callable after `expireTime`).
   * The function verifies that `earned > 0`, YT is not liquidated, and `callSkim` actually reverts.
   * If all conditions met, position ownership is cleared and the position is transferred to the caller (owner/upkeeper) for manual liquidation and resolution.

#### Lifecycle Diagram

```mermaid
sequenceDiagram
    participant User
    participant OrderPool
    participant Taker
    participant Router
    participant Converter as Converter (InterestConverter / AaveInterestTokenConverter)
    participant Bot

    User->>OrderPool: Sign and submit order
    Taker->>Router: checkOrder (LTV + ownership + balance)
    Taker->>Router: fillOrder
    Router->>Converter: mintWithLooper / mintWithYT
    Converter-->>Router: GYT minted to buyer

    alt Cancel (unfilled)
        User->>Router: cancelOrder
        Router-->>User: Return position
    end

    alt Buy Back (before maturity)
        User->>Router: buyBackPosition
        Router->>Converter: buyBackPosition (burn GYT, skim interest)
        Converter-->>User: Return position (minus yield)
    end

    alt Maturity (normal)
        Bot->>Converter: matureLooper / maturePosition (skim interest, return position)
        Bot->>Router: Cancel remaining orders
    end

    alt Seize (SLF only, skim would revert)
        Bot->>Converter: seizeForMature (transfer to bot for manual resolution)
    end
```

#### Access Control Summary

| Function                                       | Who can call                                |
| ---------------------------------------------- | ------------------------------------------- |
| `fillOrder`                                    | Anyone (taker)                              |
| `cancelOrder`                                  | Order owner only                            |
| `buyBackPosition`                              | Order owner only                            |
| `matureLooper` / `matureYT` / `maturePosition` | Position owner, contract owner, or upkeeper |
| `seizeForMature`                               | Contract owner or upkeeper only             |
| `withdrawERC20`                                | Contract owner only                         |
| `pauseToken` / `unpauseToken`                  | Contract owner only                         |
| `updateLooperConfigToMaxLtv`                   | Contract owner only                         |

***

### PTs (Principal Tokens / JR Tokens)

PTs are implemented in `PrincipalConverterSplit`(SLF) or `AavePrincipalTokenConverter`, which standardizes looping positions into a fungible token pool. Each pool is keyed by a `looperConfigId` and maintains a single merged `holdingPosition` that backs all outstanding PT tokens.

**Mint (Entry)**

When a user mints PTs from a looping position:

1. The incoming position's LTV (`positionRatio`) is compared against the pool's target ratio (`collateralRatio`):
   * If `positionRatio >= collateralRatio` (over-leveraged): User must repay part of the debt. `requiredRepayValue = depositValue × (positionRatio - collateralRatio) / 1e18`. Borrow tokens are pulled from user and repaid.
   * If `positionRatio < collateralRatio` (under-leveraged): Excess collateral is withdrawn and returned to user. `availableWithdrawValue = depositValue × (collateralRatio - positionRatio) / collateralRatio`.
2. Minted Amount will be calculated based on the "per share value" of the pool:
   * First minter (empty pool): `perShareValue = 1e18` (1 PT = 1 unit of base token in NAV terms).
   * Subsequent minters: `perShareValue = poolNAV_in_baseToken / firstPositionIssueAmount`, where `poolNAV_in_baseToken = (collateralValue - debtValue) × 1e18 / depositPrice`.
3. **Position Merge**: After minting:
   * First minter: The position will be transferred and becomes the `holdingPosition`.
   * Subsequent minters: The incoming position is merged into the existing `holdingPosition` via `looperCore.merge()` or `aavePrincipalTokenConverter.merge()`.
4. PT tokens are minted to the user's address.

**Redeem (Exit)**

When a user redeems PTs, they will receive a proportional share of the `holdingPosition` by splitting the total position, if they do not hold the full supply of PTs. In that case, the holding position is transferred to the user directly, then the field in the contract is reset to empty (type(uint256).max).

**Maintenance Operations (Owner/Manager Only)**

* `maintainPosition(configId, action, amount, payload)`: Owner can call `callUnstake` or `callClaimUnstake` on the holding position (e.g., for underlying protocol migrations).
* `adjustPosition(configId, payload)`: Owner or manager can temporarily push the holding position to a `JrAdjustHelper` contract for rebalancing (e.g., deleverage/re-leverage). The position must be returned to the converter after adjustment.

#### Lifecycle Diagram

```mermaid
sequenceDiagram
    participant User
    participant PConverter as PrincipalConverterSplit
    participant LooperCore
    participant Pool as holdingPosition

    Note over PConverter: Pool created with initialCollateralRatio

    User->>PConverter: mint(configId, looperId, to, payload)
    PConverter->>PConverter: previewMint → standardize LTV
    alt Over-leveraged
        User->>PConverter: Transfer borrow tokens (repay)
        PConverter->>LooperCore: repayForPosition
    else Under-leveraged
        PConverter->>LooperCore: callSkim (withdraw excess)
        PConverter->>User: Return excess collateral
    end
    PConverter->>LooperCore: push (transfer position in)
    alt First minter
        PConverter->>Pool: Position becomes holdingPosition
    else Subsequent
        PConverter->>LooperCore: merge into holdingPosition
    end
    PConverter->>User: Mint PT tokens

    User->>PConverter: redeem(configId, amount)
    alt Full redeem
        PConverter->>LooperCore: push entire holdingPosition to user
    else Partial redeem
        PConverter->>LooperCore: split(holdingPosition, splitAmount)
        PConverter->>User: Transfer new split position
    end
    PConverter->>PConverter: Burn PT tokens
```
