Skip to main content

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:

  1. Proposal: The admin calls proposeUpgrade(newImplementation) — this logs the new implementation address and starts the timelock countdown
  2. Waiting period: The timelock delay must pass (the exact duration is set by the admin via activateTimelock())
  3. Execution: After the delay, the admin calls upgradeToAndCall() on the UUPS proxy to switch to the new implementation
  4. 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 tokens
  • withdrawReserves(amount) — Withdraw protocol fee revenue
  • setPoolCapBps(newCap) — Adjust the per-token borrow cap
  • transferAdmin(newAdmin) — Transfer admin rights to another address

With timelock (delayed):

  • proposeOracle(newOracle)executeOracle() — Change the oracle signing address
  • proposeAnchors(prices, ltvs)executeAnchors() — Change the LTV curve anchors
  • proposeUpgrade(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 replay
  • pool — The lending pool address (prevents use with other contracts)
  • tokenId — The specific token being priced
  • price — The current midpoint price
  • timestamp — When the price was fetched
  • maxBorrow — 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 data
  • won — Whether the token is the winning outcome
  • timestamp — 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

LevelKeywordMeaning
HealthyALL_OKAll systems operational — RPCs healthy, prices fresh, oracle working, Gamma API reachable
DegradedDEGRADEDSome components impaired — prices stale (>120s), price divergence detected, WebSocket down, some RPCs unhealthy, oracle down, or Gamma API unreachable
CriticalCRITICALMajor 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:

ComponentWhat's CheckedDegraded ThresholdCritical Threshold
RPC HealthNumber of healthy vs total RPC providersSome RPCs unhealthyAll RPCs down
WebSocketConnection to Polymarket price feedDisconnected (with active positions)
Price FeedStaleness and divergence of price dataPrices older than 120 seconds, or WebSocket/REST prices diverge by more than $0.05
OracleSigns a test price for an active token, validates resultOracle unreachablePrice outside valid range (0,1)
Gamma APILightweight connectivity probe (4s timeout)Unreachable
Liquidation OperationsChecks Redis for stuck sell/liquidation operationsAny 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

  1. The system runs a health check every 30 seconds (CHECK_INTERVAL).
  2. Each check produces a pause_worthy signal, 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)
  3. After 6 consecutive pause-worthy checks (3 minutes of sustained failure), the system automatically calls setPaused(true) on the smart contract.
  4. The 60-second startup grace period prevents false triggers during normal system restarts when WebSocket and RPCs are still initializing.

How Auto-Unpause Works

  1. After an auto-pause, the system continues monitoring.
  2. 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).
  3. The longer recovery threshold (10 checks vs 6) and the 15-minute cooldown prevent premature unpause from transient recoveries.

Safety Rails

Safety FeatureValuePurpose
Startup Grace60 secondsDon't trigger during normal system boot
Consecutive Checks to Pause6 (3 minutes)Confirm before acting
Consecutive Checks to Unpause10 (5 minutes)Conservative recovery
Recovery Cooldown15 minutesPrevent yo-yo cycling
Max Auto-Pauses Per Day3After 3 pauses, stay paused until manual intervention
Manual OverrideRespectedNever 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 ProcessedTransaction records
  • 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

PropertyImplementationTrust Assumption
Funds custodySmart contractCode is correct
Price integrityOracle signatures + freshness checksOracle reports accurate prices
Upgrade safetyUUPS + timelock + one-way ratchetAdmin doesn't act maliciously during timelock
Liquidation speedWebSocket + REST fallbackBackend infrastructure is operational
Concentration limitsPool cap + depth gateLimits are appropriately calibrated
Emergency responsePause + token freezeAdmin can respond to emergencies
Parameter changesTimelock governanceUsers can exit during waiting period

Recommendations for Users

  1. Verify the contract address: Always interact with the verified contract at 0xD90D012990F0245cAD29823bdf0B4C9AF207d9ee on Polygon.

  2. Monitor timelocked proposals: Watch for OracleChangeProposed, AnchorsChangeProposed, and UpgradeProposed events. These give you advance notice of parameter changes.

  3. 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.

  4. Keep buffer above liquidation: Don't borrow the maximum — maintain a healthy safety margin.

  5. Diversify: Don't concentrate all your lending or borrowing in a single protocol or market.


Next Steps