Settles at $0 or $1 · on a known date

Keep the position. Draw against it

A Polymarket position is money that cannot move until the market resolves. Deposit it here and draw up to half its value in pUSD without selling it. Interest accrues per second and stops the moment you repay.

For Polymarket traders who do not want to sell

Chain Polygon
pUSD 1:1 USDC.e
Max LTV 50%
Liquidation 70%
Early close 7d
Audit none yet
Loan termsenforced on chain
Max LTV
50%
Liquidation threshold
70%
Liquidation bonus
5%
Close factor
50%
Early close
7 days
Collateral pricing
min(best bid, 1h TWAP)

Not yet audited.

Right for months.
Paid on one day

Resolution can be a weekend away or five months away. Whichever it is, the position pays on that day and not before, and the value is quoted the whole way there.

Selling is the only way to reach it, and selling ends the position. You trade the outcome you called for the money it has not made yet.

Eight steps, and none of them is selling

001 / THE POSITION

You already hold the bet: an outcome token, trading at 70¢, resolving on a known date. On Polymarket it is money that cannot move until then.

01 / 08

contractssolidity 0.8.28
LendingPool.sollines 2083 of 766
20/// @title Ketro lending pool.
21/// @notice Lenders deposit pUSD and hold ERC-4626 shares; borrowers lock Polymarket outcome
22/// tokens and draw pUSD against them.
23///
24/// @dev The pool *is* the vault — there is no separate LP token contract. Being a standard
25/// ERC-4626 is what lets yield aggregators consume the pool directly, which the PRD
26/// names as a distribution goal.
27///
28/// Debt is stored scaled by `borrowIndex` rather than as an absolute figure, so accruing
29/// interest is a single index update instead of a loop over every loan.
30///
31/// Liquidation deliberately does not pay the caller. The seized collateral and bonus go
32/// to the treasury, because the price feed is operated by the protocol itself: paying
33/// the liquidator would give whoever holds the oracle key a direct, immediate profit
34/// from writing a false price. See ADR 0001.
35contract LendingPool is ERC4626, AccessControl, Pausable, ReentrancyGuard {
36 using SafeERC20 for IERC20;
37 
38 bytes32 public constant RISK_ADMIN_ROLE = keccak256("RISK_ADMIN_ROLE");
39 bytes32 public constant LIQUIDATOR_ROLE = keccak256("LIQUIDATOR_ROLE");
40 bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
41 
42 struct Loan {
43 address borrower;
44 uint256 tokenId;
45 uint256 collateralAmount;
46 /// @dev Debt divided by `borrowIndex` at the time it was taken on.
47 uint256 principalScaled;
48 uint64 openedAt;
49 bool active;
50 }
51 
52 MarketRegistry public immutable REGISTRY;
53 IPriceOracle public immutable ORACLE;
54 ICollateralAdapter public immutable ADAPTER;
55 
56 IInterestRateModel public interestRateModel;
57 address public treasury;
58 
59 /// @notice Cumulative interest index, WAD-scaled, starting at 1e18.
60 uint256 public borrowIndex = ProtocolMath.WAD;
61 uint64 public lastAccrualTs;
62 
63 /// @notice Sum of every loan's `principalScaled`.
64 uint256 public totalBorrowsScaled;
65 /// @notice Protocol's accumulated share of interest, in pUSD. Excluded from `totalAssets`.
66 uint256 public reserves;
67 
68 uint256 public protocolFeeBps = 1_500; // 15%
69 uint256 public tvlCap;
70 /// @notice Fraction of a loan repayable in one liquidation, in bps.
71 uint256 public closeFactorBps = 5_000; // 50%
72 /// @notice Below this health factor the whole loan may be closed at once.
73 uint256 public fullLiquidationHealthFactor = 0.9e18;
74 
75 /// @notice Trailing window used to price new loans.
76 uint64 public borrowPriceWindow = 30 minutes;
77 /// @notice Maximum age of an oracle observation before it is refused.
78 uint64 public maxPriceAge = 30 minutes;
79 
80 /// @notice Delay on moving protocol reserves to an outside address.
81 uint256 public constant RESERVE_WITHDRAWAL_DELAY = 2 days;
82 
83 /// @notice How long a queued action stays executable once its delay has elapsed.

~/ketro/contracts $ forge test

try: forge build · forge inspect LendingPool abi · git log --oneline

10 files3a3a7e9fccd8

Deployed

Not deployed yet

DeploymentPolygon
  • LendingPoolthe vault and the loan book

    not deployed yet

  • PriceOraclewhat the collateral is worth

    not deployed yet

  • PolymarketAdapterhow a CTF position is taken in

    not deployed yet

$ cast call $LENDING_POOL"getPoolStats()"

The one idea

Not how healthy. How far it can fall

A prediction-market position ends at exactly $0 or $1, so the distance to liquidation and the distance to worthless are the same axis. Lending protocols report a health factor because their collateral has no ceiling and no settlement point. Here the whole risk model fits on one line, drawn in the cents you were already thinking in.

At full width, a hundred cells — one per cent. The wall is where the loan breaks, the run of equals is what the price can still give up, and the block is the market right now. Open a loan below and move the price to watch that run get shorter.

Probability scalethe module the contract is tested against
Outcome tokens held
Price when you opened70¢
Price now70¢

You can draw up to

$1,750

Collateral value
$3,500
Liquidates at
50¢
Room left to fall
29%
Collateral price70.0¢
$0$1

Worth asking

The awkward ones

Every answer here is enforced by the code, or it says plainly that it is not.

Read the contracts
Q.001 /What happens when my market resolves?

The position redeems for $1 or $0. If it wins, the redemption repays the loan and the surplus returns to you. If it loses, the collateral is worth nothing and the shortfall is written off against lenders — which is why the loan is capped at half the position's value.

Q.002 /What if it resolves while my loan is still open?

`settleResolved(loanId)` redeems the position and repays the debt from the proceeds. Without it a resolved market would leave the collateral frozen in the contract with nothing left to price it against.

Q.003 /Can I be liquidated by a price glitch?

New loans are sized off the lower of the best bid and the hourly average, so a momentary spike cannot inflate one. A genuine fall applies immediately. Smoothing a real collapse would only delay the liquidation that answers it.

Q.004 /Who runs the price feed?

The protocol does. It is a push oracle: the backend publishes min(best bid, 1h TWAP) on a heartbeat, and early when the price moves. The oracle updater and the liquidator are deliberately separate keys, and liquidation pays its caller nothing — whoever can write a price must not profit by acting on it. That removal of motive, plus timelocked treasury withdrawals, is what stands behind a feed the protocol itself operates.

Q.005 /Who runs liquidations?

The protocol, for now. Seized collateral goes to the reserve rather than to the caller, so there is no profit in liquidating against a price the protocol itself published.

Q.006 /What happens to my collateral after a liquidation?

It stops being a bet. Seized positions go to a treasury contract that exposes a permissionless complement swap: anyone delivers the opposite outcome, the pair is merged back into pUSD at the CTF's fixed $1, and the deliverer is paid for it.

Q.007 /Can a market stop being accepted as collateral?

Yes, through the adapter's liquidity flag, and only its owner can move it. The backend deliberately holds no key for it — it can block a market in the API and page an operator, but it cannot change what the pool will accept. Two of the risk checks in the spec, minimum volume and resolution risk, are not on-chain data and can only advise.

Q.008 /Can I withdraw at any time?

Whenever the pool holds free liquidity. Funds currently lent out are not withdrawable until they are repaid, which is the trade for the yield.

Q.009 /Has this been audited?

No. The contracts are readable above and the parameters are published in full, and neither of those is an audit. Conservative LTV and the early-close ramp reduce the risk of a loss; they do not remove it.

Ketrosettlement terminal
/$$$$$$ /$$ /$$ /$$
|_ $$_/| $$ | $$ | $$
| $$ /$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$$ /$$$$$$$ /$$$$$$ /$$$$$$
| $$|_ $$_/ /$$__ $$| $$__ $$ /$$__ $$ /$$_____/ |____ $$|_ $$_/
| $$ | $$ | $$$$$$$$| $$ \ $$| $$ | $$| $$$$$$ /$$$$$$$ | $$
| $$ | $$ /$$ | $$_____/| $$ | $$| $$ | $$ \____ $$ /$$__ $$ | $$ /$$
/$$$$$$| $$$$/ | $$$$$$$| $$ | $$| $$$$$$$ /$$$$$$$/ | $$$$$$$ | $$$$/
|______/ \___/ \_______/|__/ |__/ \_______/|_______/ \_______/ \___/
/$$ /$$$$$$ /$$ /$$
/$$$$$$ /$$$_ $$ /$$$$$$ /$$$$
/$$__ $$| $$$$\ $$ /$$$$$$ /$$$$$$ /$$__ $$|_ $$
| $$ \__/| $$ $$ $$ /$$__ $$ /$$__ $$ | $$ \__/ | $$
| $$$$$$ | $$\ $$$$ | $$ \ $$| $$ \__/ | $$$$$$ | $$
\____ $$| $$ \ $$$ | $$ | $$| $$ \____ $$ | $$
/$$ \ $$| $$$$$$/ | $$$$$$/| $$ /$$ \ $$ /$$$$$$
| $$$$$$/ \______/ \______/ |__/ | $$$$$$/|______/
\_ $$_/ \_ $$_/
\__/ \__/