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

The loan lifecycle

A MLKY loan moves through five clearly-defined states. The protocol enforces these transitions on-chain, so the same flow applies whether you use the MLKY app, a third-party UI, or interact with the program directly.

States and transitions

Createdquote consumed
draw
ActiveUSDC paid out, card locked
repay
Repaidawaiting close
close
Closedcard returned
The five loan states. Repay is reachable from Defaulted as well as Active, so a default is not a one-way door.

There is also a sixth state on the unhappy path: Defaulted, which the loan enters after maturity plus the grace period.

It is not terminal. A defaulted loan can still be repaid, and repaying it moves it to Repaid and then Closed exactly like any other loan — the card comes back. That right lasts until the card actually sells. See default and liquidation for the mechanics of that branch, including what happens to the money if the card does sell.

There is also a transition that is not a transition. An active loan can be extended, up to three times, which settles the interest you owe, reduces your principal by at least 10% and restarts the term. The loan stays Active throughout — it is the same loan repriced rather than a close and a reopen — so it does not appear as a new state above. See extending a loan.

Walking through each step

Step 1 — Request a signed quote

Before opening anything on-chain, you ask MLKY's oracle for a price. The oracle resolves your card's grading certificate to a canonical asset, fetches the current fair market value, and returns a signed quote that includes:

  • The fair market value (FMV) in USDC.
  • The maximum loan-to-value (LTV) permitted on this card, in basis points (a hundredth of a percent; 10,000 = 100%).
  • The selected term length and interest rate.
  • A unique quote id and an expiry timestamp.

Quotes are short-lived — they expire in 5 minutes by default. If you don't open a loan in that window, you simply request a new one.

Step 2 — Verify the Merkle proof

MLKY maintains an allowlist of every card eligible to be used as collateral, hashed into a Merkle tree (a hash tree of eligible cards) whose root the protocol stores on-chain. Before you create the loan, you submit a small proof showing your card is in the current tree. This produces a proof receipt account keyed by your card's certificate hash. The next step will consume that receipt.

This is a one-time-per-loan check. The proof itself is a few hundred bytes and the transaction is cheap.

Step 3 — Create the loan

You submit:

  • The signed oracle quote (with the proper Ed25519 verify instruction).
  • The proof receipt from the previous step.
  • The pool you want to borrow from and the term option you want.

The program checks all the on-chain rules in one shot:

  • The oracle's signature is valid and the oracle is on the allowlist.
  • The Merkle root version on the quote matches a recent root the chain knows.
  • The principal you're requesting is within the pool's [min, max] range and within FMV × LTV for the card.
  • All the per-card and per-asset-type concentration limits are respected.

If everything passes, the loan is recorded in the Created state and a short pending draw window opens — about 5 minutes — during which you must complete the next step.

Step 4 — Draw the funds

Drawing is the moment money and collateral actually move:

  • Your card-backed NFT transfers into the protocol's collateral vault (frozen, or for programmable NFTs, locked via a delegate slot).
  • The pool sends you principal minus the origination fee in USDC.
  • Concentration counters update: the pool's outstanding principal goes up, and the running exposure for this asset type increases by the principal.
  • The loan transitions to Active. The maturity clock starts at this moment, not when you created the loan.

Step 5 — Repay before maturity (the happy path)

To repay, you transfer principal plus the full fixed interest in a single transaction. There is no partial-repayment flow that closes the loan; anything less than the full payoff is rejected.

When the repayment lands:

  • The pool's outstanding principal decreases by the principal you repaid.
  • The loan moves to Repaid.
  • A second instruction (often bundled in the same transaction by the app) closes the loan account, unlocks the NFT, and refunds you most of the rent that was paid for the loan's accounts.

You can repay at any point after drawing, including the same block. The interest is fixed, so there is no benefit to repaying earlier than your plan calls for, but there is no penalty either.

Closing does not immediately free the card for a new loan. One record — the one keyed to your card's grading certificate — is kept for 48 hours and carries the rent with it, so that a repayment cannot be turned straight back into another loan. After the 48 hours you close that record yourself with card_cooldown_release, which is when the last of the rent comes back and when the card becomes borrowable again. See extending a loan for why the wait exists.

Step 5 (alternate) — Extend instead of repaying

If you cannot repay, and you would rather not lose the card, you can renew. loan_extend is one transaction and one transfer, carrying your outstanding interest in full, a paydown of at least 10% of your principal, and a fresh origination fee on the new term. When it lands:

  • Your principal is lower, your maturity restarts from that moment, and your loan is still Active. The card never moves.
  • Your card is re-appraised against a fresh oracle quote, and the extension is refused if it has fallen far enough that even the reduced principal breaches the loan-to-value ceiling.
  • Your extension count goes up. Three is the maximum; the fourth attempt is refused by name.

Only the loan's borrower can send it, and it is available for the whole of Active — including after maturity and inside the grace period, which is where most people who need it are standing. Once the loan is Defaulted it is no longer available and redemption takes its place.

Extending a loan covers the arithmetic, with a worked example on a $500 loan.

Step 5 (alternate) — Default after maturity

If the maturity timestamp passes plus the grace period — which is your loan's term or 7 days, whichever is shorter — the loan becomes eligible for default. At that point anyone can submit loan_mark_default_and_start_auction: it is permissionless, because the only thing the caller asserts is that the clock has passed your deadline, and the program checks that for itself. Expect it to be your lender or a keeper bot watching for it. Submitting it atomically:

  • Marks the loan as Defaulted.
  • Initializes a new Auction account. The price starts at the card's appraised value from your loan's oracle snapshot and falls to your principal over 24 hours.
  • Starts the bidding window, which is a descending-price (Dutch) sale.

You get notice before this happens, and you can still repay afterwards.

Step 6 (alternate) — Redeem, or take the remainder

From Defaulted, two things can happen and only one of them is out of your hands:

  • You buy it back. loan_redeem_from_auction takes a Defaulted loan and only you can sign it. Any standing bidder is refunded as a credit, the sale closes, and the card returns to you on the ordinary Repaid → Closed path. This is available until the card sells, and it has to be the whole payoff — a part payment is refused, because it would leave you in default with no sale left to end it.
  • The card sells. A 5% commission comes off the sale price, your lender takes principal then interest, and the remainder is written as a BorrowerCredit you claim with a single transaction. Claim it within twelve months: after that the protocol may sweep it, and it becomes a race you can lose.

The full mechanics are covered in default and liquidation and auctions overview.

What you do not do

A handful of things that exist in other lending protocols are intentionally absent here:

  • No health-factor monitoring. Default is purely time-based: maturity passes, grace passes, you default. Price movements during the loan don't trigger early liquidation.
  • No lender-initiated refinancing. Nobody but you can renew, reprice or call your loan. Some protocols let a lender who wants out auction your position to a stranger, which can force you into liquidation on a timetable you did not choose; MLKY has no such mechanism and will not build one. A lender leaves by withdrawing from the pool, not by moving your loan.
  • No rate adjustments inside a term. The rate you accept when you open a ticket is the rate you owe when it matures. Nothing can move it: a pool's term options are not updatable at all, since PoolUpdatableParams has no field for them and term_options is written only by pool_init. An extension does not move it either — it starts a new ticket, and you pick its term option the same way you picked the first.