DEVNETYou are on Solana devnet. Funds are not real. Behavior matches mainnet.

Pools

A pool in MLKY is the unit of capital that funds loans. Each pool is its own on-chain account with its own USDC vault, its own configuration parameters, and its own LP positions. A loan always belongs to exactly one pool, and a pool can fund many loans simultaneously up to its utilization cap.

There is no single global pool. Instead, pools exist as long as they are useful for segmenting risk between different kinds of collateral, term profiles, or operator preferences.

How pools segment risk

Pools segment risk through three independent levers, each of which the pool admin sets at creation time and (for some) can update later.

Allowlists

Each pool publishes an explicit list of collections it will accept as collateral. A loan request against a card from a non-allowlisted collection is rejected at the on-chain create step.

This means a pool can be narrow ("high-grade Pokémon TCG only") or broad ("Pokémon TCG plus the four major sports"), and the LP knows what exposure they're taking on by depositing into that pool.

Per-asset-type exposure caps

Within a pool, the protocol tracks outstanding principal per canonical asset type (a cert + grader + grade identity). A pool admin sets a max_exposure_per_type, and the protocol refuses to draw a new loan that would push that asset type over the cap.

This prevents quiet over-concentration. Without the cap, a pool could end up with 60% of its principal in a single Pokémon Charizard variant; the exposure cap ensures the pool is forced to spread risk across many different cards.

Per-card principal cap and loan-to-value (LTV) ceiling

In addition to the exposure cap, the pool sets:

  • A max_principal_per_card — the absolute USDC ceiling for a single loan against any single card.
  • A max_ltv_bps — the maximum LTV the pool will accept, capped above by the protocol-wide ceiling of 90%.

Together these mean even if a card is priced very high by the oracle, the pool can still cap its single-loan exposure.

How pools relate to borrowers

When you borrow on MLKY, you pick a pool that:

  • Accepts the collection your card belongs to.
  • Offers a term option matching the term/rate you want.
  • Has enough headroom under its utilization cap and per-asset-type cap to fund your principal.

The MLKY app filters available pools for your card automatically.

How pools relate to lenders

When you lend on MLKY, you deposit into a specific pool. Your share of the pool's net asset value (NAV) is denominated in shares of that pool — there is no cross-pool sharing of yield or risk. If one pool has a bad month, only its LPs are affected.

(See the single-LP note for the current constraint that only the pool admin can deposit.)

Pool lifecycle

A pool moves through a simple lifecycle:

  1. Initialize

    The pool admin calls pool_init with the full parameter set: quote mint, term options, LTV ceiling, principal limits, exposure cap, utilization cap. Some parameters (the admin wallet, the quote mint) are immutable from this point on.

  2. Configure allowlist

    The admin adds AllowedCollection entries for the collections this pool will accept. Without at least one entry, the pool can only fund loans against NFTs without a verified collection.

  3. Operate

    LPs deposit (today: only the admin), borrowers open and repay loans, keepers settle defaults by selling the card. The pool continuously updates its accounting: outstanding principal, accrued interest, accumulated protocol fees, LP shares. A default returns the pool cash, never the card.

  4. Update parameters

    The admin can adjust the mutable parameters (LTV ceiling, exposure cap, utilization cap, pause flag, and a few more) via pool_update_params. Term options are not among them — they are fixed at pool_init. Values are re-bounded on update, and the exposure cap cannot be set to zero. recovery_floor_bps is still settable and no longer read by anything: the sale floor is the defaulted loan's principal. See pool parameters.

  5. Pause if needed

    The admin (or, for the global pause, the protocol governance) can halt new loans and new deposits without freezing existing loans or blocking LP withdrawals.

  6. Close when it has finished lending

    pool_close retires a pool that has run down: it sweeps whatever is left in the vault to the protocol fee recipient and the insurance fund, closes the vault, and returns the rent on those three accounts to the pool admin. The pool stops appearing at /pool and stops costing its own rent, rather than sitting on chain paused and empty.

Closing is a last step and the program enforces that. It refuses unless every lender has withdrawn, no principal is out with a borrower, no defaulted loan is waiting on its auction, the withdrawal queue is empty, and the pool is paused with the pause still in force. The pause is required rather than optional: deposits and draws are otherwise open, so without it a single one-dollar deposit between the last withdrawal and the close puts the shares back. A pool pause lapses after 72 hours, so a close more than three days after the pause has to renew it first.

What a close does not remove

Three accounts close and their rent comes back. Other accounts seeded on the pool do not, and an operator should know which before sending it:

  • Collection opt-ins (AllowedCollection, one per collection). These can be closed and their rent refunded — but only by pool_remove_allowed_collection, which reads the pool account. Send the removals before the close. Afterwards there is no pool for it to read and the refund is gone.
  • LP positions (one per lender) and asset-type exposure records (one per card type the pool lent against). Nothing closes these, before or after. Their rent — roughly 0.0015 and 0.0014 SOL each, on the order of 0.02 SOL for a pool with a handful of each — stays locked up permanently. It was paid by the lender and the borrower respectively rather than by the pool admin, which is part of why there is no sweeper: refunding it to whoever closes the pool would be paying them somebody else's deposit.

None of this affects anybody's money or anybody's card. It is rent that stops being recoverable, and it is recorded here because the alternative is discovering it while looking for it.

Two things a borrower should know before an operator closes a pool. A loan the borrower created against it but never drew can no longer draw, and the locks on the card still come free through loan_cancel_pending, so no card is stranded. And a loan the borrower repaid but never closed releases its collateral through loan_release_repaid rather than through loan_close, because loan_close reads the pool account and that account is now gone.

Multiple pools, multiple shapes

Production deployments typically run multiple pools in parallel with different risk profiles:

  • A broad allowlist, low LTV pool that accepts many collections at conservative LTVs and shorter terms.
  • A narrow allowlist, higher LTV pool focused on a specific high-end collection where the operator has deep expertise.
  • A partner-operated pool where a specific institution provides liquidity to a curated allowlist they care about.