Security
PredMart is designed with a defense-in-depth security model. This page provides a comprehensive overview of the protocol's security architecture, covering smart contract security, access control, the timelock governance system, oracle integrity, emergency mechanisms, and the upgrade process. Understanding these security measures is essential for users who want to evaluate the trust assumptions of the protocol.
Non-Custodial Design
PredMart's most fundamental security property is its non-custodial architecture. At no point does the PredMart team, company, or any individual have direct access to user funds:
- Lender deposits (USDC) are held by the smart contract on-chain, not in any company wallet.
- Borrower collateral (Polymarket CTF shares) is held by the smart contract, not by PredMart.
- Borrowed USDC is transferred directly from the contract to the borrower's wallet.
- Repayments flow directly from the borrower's wallet to the contract.
All fund movements are governed by the smart contract's immutable logic. The only entity that can move funds is the contract itself, and it only does so according to its programmed rules.
What This Means for Users
- PredMart cannot freeze, seize, or redirect your funds (beyond what the contract logic allows)
- Even if PredMart (the company) ceased to exist, the smart contract would continue to function
- You can verify all contract logic by reading the source code on-chain or on the verified contract explorer
Smart Contract Architecture
Proxy Pattern (UUPS)
PredMart's lending pool uses the UUPS (Universal Upgradeable Proxy Standard) proxy pattern. This means the contract logic can be upgraded to fix bugs or add features, while the state (balances, positions, parameters) is preserved.
The proxy architecture has two components:
- Proxy contract: Holds all state (storage) and delegates calls to the implementation
- Implementation contract: Contains the actual logic
When an upgrade occurs, only the implementation address changes — the proxy address and all stored data remain the same.
Upgrade Process
Contract upgrades are protected by a timelocked proposal system:
- Proposal: The admin calls
proposeUpgrade(newImplementation)— this logs the new implementation address and starts the timelock countdown - Waiting period: The timelock delay must pass (the exact duration is set by the admin via
activateTimelock()) - Execution: After the delay, the admin calls
upgradeToAndCall()on the UUPS proxy to switch to the new implementation - Cancellation: At any time before execution, the admin can call
cancelUpgrade()to abort
The timelocked upgrade ensures that users have advance notice before any contract logic changes take effect. If a proposed upgrade is concerning, users can withdraw their funds during the waiting period.
Timelock Ratchet
The timelock mechanism has an important safety feature: the timelock can only be increased, never decreased. The activateTimelock(delay) function requires that the new delay is at least as long as the current delay. This prevents an admin from reducing the timelock to near-zero and pushing through a surprise upgrade.
Access Control
Admin Role
The admin is a single Ethereum address that has authority over protocol parameters. The admin can:
Without timelock (immediate):
setPaused(bool)— Pause or unpause the protocol (emergency use)setTokenFrozen(tokenId, bool)— Freeze or unfreeze specific tokenswithdrawReserves(amount)— Withdraw protocol fee revenuesetPoolCapBps(newCap)— Adjust the per-token borrow captransferAdmin(newAdmin)— Transfer admin rights to another address
With timelock (delayed):
proposeOracle(newOracle)→executeOracle()— Change the oracle signing addressproposeAnchors(prices, ltvs)→executeAnchors()— Change the LTV curve anchorsproposeUpgrade(newImpl)→ upgrade — Change the contract implementation
What the Admin Cannot Do
Even with admin access, the following actions are impossible:
- Transfer user funds directly (USDC or collateral)
- Modify individual user positions or balances
- Override the interest rate model calculations
- Bypass health factor checks for liquidation
- Force a borrow or withdrawal on behalf of a user
- Access private keys or user signatures
Relayer Role
The relayer is a separate address authorized to execute specific operations:
- Submit borrow transactions (with oracle-signed price data and user signatures)
- Submit withdrawal transactions (with oracle-signed price data and user signatures)
- Execute liquidations (with oracle-signed price data)
The relayer cannot:
- Borrow on behalf of a user without their signature
- Withdraw collateral without the user's signature
- Access or transfer funds not involved in a legitimate operation
- Modify protocol parameters
Admin Transfer
The admin address can be transferred via transferAdmin(newAdmin). This is an immediate operation (no timelock) because the timelock is already protecting the sensitive operations that the admin can perform. If the admin key is compromised, the ability to quickly transfer to a new address is a security benefit.
Oracle Security
The Oracle's Role
PredMart's oracle is a backend service that signs price and resolution data. The smart contract only accepts data signed by the oracle's authorized address. This prevents users from submitting fabricated price data.
Price Data Security
Every borrow, withdrawal, and liquidation includes oracle-signed price data containing:
chainId— Prevents cross-chain replaypool— The lending pool address (prevents use with other contracts)tokenId— The specific token being pricedprice— The current midpoint pricetimestamp— When the price was fetchedmaxBorrow— The depth-gated borrow cap
The smart contract verifies:
- The signature is from the authorized oracle address
- The timestamp is within 10 seconds of the current block time (
MAX_RELAY_PRICE_AGE) - The price is between 0 and 1 (exclusive)
Resolution Data Security
Market resolution data contains:
chainId,pool,tokenId— Same scoping as price datawon— Whether the token is the winning outcometimestamp— When the resolution was determined
The contract verifies:
- Valid oracle signature
- Timestamp within 1 hour (
MAX_RESOLUTION_AGE)
Oracle Key Rotation
If the oracle key needs to be changed (e.g., suspected compromise), the admin can propose a new oracle address through the timelocked proposeOracle() → executeOracle() flow. This ensures users have notice before the oracle changes.
Trust Assumption
The oracle is a centralized trust point in PredMart's architecture. Users must trust that:
- The oracle reports accurate prices from Polymarket's CLOB
- The oracle does not selectively delay or withhold price updates
- The oracle's private key is properly secured
PredMart mitigates this trust assumption through:
- Freshness checks (10-second max age prevents stale prices)
- Price sanity checks (0 to 1 range)
- Transparent oracle operations (all signed data is visible on-chain)
- Timelocked oracle key rotation (users can react if the oracle changes)
Emergency Mechanisms
Protocol Pause
The admin can pause the protocol at any time via setPaused(true). When paused:
- Blocked: New collateral deposits, new borrows, new lending deposits
- Allowed: Repayments and collateral withdrawals (with health check)
- Allowed: Liquidations (positions must still be liquidatable to protect lenders)
The pause is designed to protect the protocol during:
- Discovery of a smart contract vulnerability
- External events that could compromise price oracle integrity
- Other emergency situations
The admin can unpause via setPaused(false) when the situation is resolved.
Token Freezing
Individual tokens can be frozen via setTokenFrozen(tokenId, true). When a token is frozen:
- Blocked: New deposits of that token, new borrows against that token
- Allowed: Repayments, liquidations
- Blocked: Collateral withdrawals of that token
Token freezing is useful when:
- A specific market is suspected of manipulation
- A token's price data is unreliable
- There's a concern about a specific market's resolution
Auto-Monitoring and Health System
PredMart's backend includes a comprehensive health monitoring system that provides real-time visibility into protocol status and can automatically pause and unpause the protocol in response to infrastructure failures.
Health Status Levels
| Level | Keyword | Meaning |
|---|---|---|
| Healthy | ALL_OK | All systems operational — RPCs healthy, prices fresh, oracle working, Gamma API reachable |
| Degraded | DEGRADED | Some components impaired — prices stale (>120s), price divergence detected, WebSocket down, some RPCs unhealthy, oracle down, or Gamma API unreachable |
| Critical | CRITICAL | Major failure — contract auto-paused, all RPCs down, oracle price outside valid range, or liquidation/sell operations stuck for more than 5 minutes |
Monitored Components
The detailed health endpoint (/health/detailed) checks six independent components:
| Component | What's Checked | Degraded Threshold | Critical Threshold |
|---|---|---|---|
| RPC Health | Number of healthy vs total RPC providers | Some RPCs unhealthy | All RPCs down |
| WebSocket | Connection to Polymarket price feed | Disconnected (with active positions) | — |
| Price Feed | Staleness and divergence of price data | Prices older than 120 seconds, or WebSocket/REST prices diverge by more than $0.05 | — |
| Oracle | Signs a test price for an active token, validates result | Oracle unreachable | Price outside valid range (0,1) |
| Gamma API | Lightweight connectivity probe (4s timeout) | Unreachable | — |
| Liquidation Operations | Checks Redis for stuck sell/liquidation operations | — | Any operation stuck for more than 5 minutes |
Automatic Pause/Unpause System
PredMart's most critical safety feature is its automatic pause/unpause system (auto_response.py). This system continuously monitors protocol health and can autonomously pause the smart contract when infrastructure failures threaten the safety of lending operations.
How Auto-Pause Works
- The system runs a health check every 30 seconds (
CHECK_INTERVAL). - Each check produces a
pause_worthysignal, which is True when:- No healthy RPC providers are available, OR
- Prices are stale (>120s) and there are active positions, OR
- Price divergence detected between WebSocket and REST and there are active positions, OR
- Oracle price is outside valid range (0, 1)
- After 6 consecutive pause-worthy checks (3 minutes of sustained failure), the system automatically calls
setPaused(true)on the smart contract. - The 60-second startup grace period prevents false triggers during normal system restarts when WebSocket and RPCs are still initializing.
How Auto-Unpause Works
- After an auto-pause, the system continues monitoring.
- After 10 consecutive ALL_OK checks (5 minutes of sustained recovery), AND at least 15 minutes have elapsed since the auto-pause (recovery cooldown), the system automatically calls
setPaused(false). - The longer recovery threshold (10 checks vs 6) and the 15-minute cooldown prevent premature unpause from transient recoveries.
Safety Rails
| Safety Feature | Value | Purpose |
|---|---|---|
| Startup Grace | 60 seconds | Don't trigger during normal system boot |
| Consecutive Checks to Pause | 6 (3 minutes) | Confirm before acting |
| Consecutive Checks to Unpause | 10 (5 minutes) | Conservative recovery |
| Recovery Cooldown | 15 minutes | Prevent yo-yo cycling |
| Max Auto-Pauses Per Day | 3 | After 3 pauses, stay paused until manual intervention |
| Manual Override | Respected | Never override a manual admin pause |
This system ensures that even if the PredMart team is unavailable, the protocol will automatically protect itself from infrastructure failures that could compromise liquidation or oracle operations.
RPC Circuit Breaker and Failover
PredMart's backend connects to multiple Polygon RPC providers with a circuit breaker pattern. If an RPC provider starts failing or responding slowly:
- It is marked as unhealthy and removed from the active rotation
- Requests are automatically routed to remaining healthy providers
- Health pings run every 30 seconds to detect when failed providers recover
- The system tracks per-RPC metrics (success rate, latency, error counts) available at
/health/rpc-metrics
Data Integrity
Event Indexing
PredMart's indexer processes blockchain events with several integrity guarantees:
- Idempotency: Each transaction is processed exactly once, tracked via
ProcessedTransactionrecords - Ordering: Events are processed in block order, ensuring consistent state
- Confirmation depth: The indexer reads blocks with a 5-block confirmation depth, reducing reorg risk
- State tracking: The last processed block is stored in
IndexerState, enabling reliable restarts
Transaction Safety
PredMart's transaction sender (tx_sender.py) includes:
- Nonce management: Proper sequential nonce handling to prevent transaction conflicts
- Gas estimation: Dynamic gas price estimation with buffer
- Retry logic: Automatic retry for failed transactions with appropriate backoff
- Receipt verification: Confirmation that transactions are included in blocks
Key Security Properties Summary
| Property | Implementation | Trust Assumption |
|---|---|---|
| Funds custody | Smart contract | Code is correct |
| Price integrity | Oracle signatures + freshness checks | Oracle reports accurate prices |
| Upgrade safety | UUPS + timelock + one-way ratchet | Admin doesn't act maliciously during timelock |
| Liquidation speed | WebSocket + REST fallback | Backend infrastructure is operational |
| Concentration limits | Pool cap + depth gate | Limits are appropriately calibrated |
| Emergency response | Pause + token freeze | Admin can respond to emergencies |
| Parameter changes | Timelock governance | Users can exit during waiting period |
Recommendations for Users
-
Verify the contract address: Always interact with the verified contract at
0xD90D012990F0245cAD29823bdf0B4C9AF207d9eeon Polygon. -
Monitor timelocked proposals: Watch for
OracleChangeProposed,AnchorsChangeProposed, andUpgradeProposedevents. These give you advance notice of parameter changes. -
Understand the risks: PredMart is a non-custodial protocol, but it has centralized components (oracle, relayer, admin). Understand these trust assumptions before depositing significant funds.
-
Keep buffer above liquidation: Don't borrow the maximum — maintain a healthy safety margin.
-
Diversify: Don't concentrate all your lending or borrowing in a single protocol or market.
Next Steps
- Protocol Constants — All on-chain parameters
- Smart Contract — Contract addresses and developer reference
- Risk Parameters — How risk is managed in the protocol
- FAQ — Common questions about security