Example: coverage-guard-cached (mainnet)
A live mainnet contract that pauses when coverage gets thin or the feed goes stale. Copy it.
This is the example consumer, deployed on mainnet and calling the real feed. It holds no funds, owns nothing and can move nothing: check returns a verdict its caller acts on.
| Contract | Network | ID |
|---|---|---|
| pox5-reader | mainnet | SP2Q3XVGTTA4CW3E2AHFZPAGQ0HM9QPHTTBJTQGJY.pox5-reader |
| risk-feed-trait | mainnet | SP2Q3XVGTTA4CW3E2AHFZPAGQ0HM9QPHTTBJTQGJY.risk-feed-trait |
| coverage-cache | mainnet | SP2Q3XVGTTA4CW3E2AHFZPAGQ0HM9QPHTTBJTQGJY.coverage-cache |
| risk-feed | testnet | ST24MYZSDF0TAVZ452R2TJY3RCQAVT3KR0FJHYCAJ.risk-feed |
| risk-feed-trait | testnet | ST24MYZSDF0TAVZ452R2TJY3RCQAVT3KR0FJHYCAJ.risk-feed-trait |
| coverage-guard-cached (example consumer) | mainnet | SP2Q3XVGTTA4CW3E2AHFZPAGQ0HM9QPHTTBJTQGJY.coverage-guard-cached |
| coverage-guard | testnet | ST24MYZSDF0TAVZ452R2TJY3RCQAVT3KR0FJHYCAJ.coverage-guard |
| pox-5 (read by pox5-reader) | mainnet | SP000000000000000000002Q6VF78.pox-5 |
The whole integration
Five lines inside your own function: take the feed as a trait, check it is the one you trust, read the summary, and refuse when coverage is thin or the reading is old.
(use-trait risk-feed .risk-feed-trait.risk-feed-trait)
(define-constant TRUSTED_FEED 'SP2Q3XVGTTA4CW3E2AHFZPAGQ0HM9QPHTTBJTQGJY.coverage-cache)
(define-constant MIN_COVERAGE_BPS u20000) ;; 2.0x
(define-public (deposit (feed <risk-feed>) (amount uint))
(let ((s (try! (contract-call? feed get-coverage-summary))))
(asserts! (is-eq (contract-of feed) TRUSTED_FEED) (err u200))
(asserts! (match (get coverage-bps s) c (>= c MIN_COVERAGE_BPS) true) (err u202))
(asserts! (<= (- burn-block-height (get updated-at s)) u1300) (err u203))
;; ... your deposit logic
(ok amount)
)
)What it returns
check answers (ok { status, coverage-bps, age-blocks, stale, provenance }):
| Field | Meaning |
|---|---|
status | "ok", or "paused" when coverage is below the threshold or the reading is stale |
coverage-bps | coverage in basis points, none when there are no bonds to cover |
age-blocks | Bitcoin blocks since the feed's reading was recorded |
stale | age-blocks exceeds the staleness window |
provenance | "onchain": the figures are pox5-reader's, computed from pox-5 state |
Errors: u200 the feed is not the trusted one, u201 only the deployer may change the staleness window, and any error the feed itself returns (coverage-cache answers u200 before its first refresh).
The two settings, and why
| Setting | Value | Reasoning |
|---|---|---|
MIN_COVERAGE_BPS | u20000, 2.0× | The coverage target discussed in the Bitcoin Staking SIP thread. Equivalent to 50% headroom. Coverage is 16.68× onchain (block 967,911), so a vault using this threshold is not paused today. |
max-age-blocks | u1300 Bitcoin blocks | pox-5 computes a distribution every 1,050 blocks, and the keeper refreshes coverage-cache at least every 1,100. 1,300 leaves about 200 blocks, roughly a day and a half, before a feed that stopped being refreshed pauses the caller. Only the deployer can change it; a contract copying this pattern should set its own. |
Pick your own numbers: a conservative vault might pause at 4.0× (u40000), and one that reads the feed every block could use a tighter staleness window.
Proven on mainnet
| What | Transaction | Result |
|---|---|---|
| Called with the trusted feed | 0xa5faac80… | (ok { status "ok", coverage-bps (some u166780), age-blocks u117, stale false, provenance "onchain" }) |
| Called with another contract as the feed | 0xf51d4309… | (err u200): refused, because it is not the trusted feed |
The "paused" branches cannot be shown on mainnet without a real shortfall, so they are covered against live chain state in contracts/tests-tip, which mines past the staleness window and checks the verdict flips. See Health and degradation for how the feed behaves when a piece of the service is down.
Source: contracts/contracts/coverage-guard-cached.clar. The testnet coverage-guard is the same guard reading the testnet risk-feed.