Skip to content

Update bounty framework#299

Merged
clemsos merged 13 commits into
mainfrom
claude/update-bounty-framework-muOn6
Apr 24, 2026
Merged

Update bounty framework#299
clemsos merged 13 commits into
mainfrom
claude/update-bounty-framework-muOn6

Conversation

@clemsos
Copy link
Copy Markdown
Collaborator

@clemsos clemsos commented Apr 23, 2026

No description provided.

claude and others added 4 commits April 23, 2026 13:39
Replace the legacy "10% of value at risk" payout rule with the severity
x likelihood matrix, fixed modifier list, worked examples, PoC
requirements, and edge-case guidance. Scope stays on researcher-facing
guidance; internal calibration and testing material is intentionally
omitted.
Replace the single-sentence "may receive $0" note with explicit
criteria: privileged-role-only exploit path, no demonstration that the
role is reachable externally, and the bug does not fire in normal
operation. Makes admin-only findings deterministically $0 instead of a
discretionary judgment call.
Add a 5-step payout flow at the top so readers see the calculation path
before the details. Pull rule-of-thumb one-liners into the severity and
likelihood tables and surface the four severity decision rules directly
under the table. Add a quick-checklist summary of modifiers alongside
the full trigger table, a rough-intuition line near the matrix, and
tighten several of the densest sentences. Same logic, less reading to
reach a number.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

Claude finished @clemsos's task in 2m 19s —— View job


Code Review — Bug Bounty Program rewrite

This is a substantial and well-structured rewrite that replaces a vague "10% of value at risk" rule with a legible severity × likelihood × modifier framework. The structure is strong; flagging a typo, a terminology slip, and several places where the framework's rules could double-apply or are ambiguous.

security/ is explicitly out of scope for the §2/§3 profile rules in CLAUDE.md, so I've mostly focused on content coherence, internal consistency, and §5 terminology (which applies repo-wide).


Must-fix

1. Typo — eligibalesecurity/bounties.mdx:65

A report is not eligibale for a bounty when:

→ "eligible". Fix this →

2. on-chainonchainsecurity/bounties.mdx:88,90

CLAUDE.md §5 says "onchain — one word, no hyphen." Two hits in the modifier table:

  • L88: "no pause, no upgrade path, and no on-chain recourse" → "no onchain recourse"
  • L90: "No on-chain event emitted" → "No onchain event emitted"

(The "cross-chain" hyphenation on L125 is correct — §5 keeps that one hyphenated.)
Fix this →


Framework consistency issues

These are where the rules can collide with each other; worth tightening before this becomes the public contract with researchers.

3. Likelihood-downgrade criteria overlap with modifiers — risk of double-counting

Two of the three likelihood-downgrade bullets (L48–52) describe the same conditions as two of the ×0.75 modifiers (L86–93):

Downgrade trigger (reduces L tier) Overlapping ×0.75 modifier
"significant capital at risk or fees that materially erode expected profit" (L51) High attacker cost — "significant capital at risk or fees that materially reduce net gain" (L91)
"pausable with active monitoring and sub-5-minute response — slow drains only" (L52) Pausable + monitored — "pausable and active monitoring with <5-minute response" (L92)

A researcher (or reviewer) applying the framework literally could take both the tier drop and the ×0.75 for the same fact pattern — meaningfully shrinking the payout for the same underlying property. Recommend stating explicitly that a given real-world fact can only reduce payout through one channel (either likelihood downgrade OR a ×0.75 modifier, not both), or collapsing the overlap.

Notably, the "Fee parameter drift" worked example (L117–121) applies Pausable + monitored as a modifier but does not downgrade likelihood from L3 — implicitly choosing the modifier path. Good choice, but the rule isn't stated.

4. Permanently unrecoverable modifier on S3 freeze — double-counting?

S3's severity definition already includes "permanent freeze" (L27). The permanent pool freeze example (L123–129) takes S3 and applies the ×1.5 "permanently unrecoverable" modifier, yielding $22,500. The <Info> callout at L95–99 only warns about double-counting on S3/S4 theft (because those encode "no time to react"). It doesn't address freeze bugs, where S3 already encodes "permanent freeze."

Either:

  • S3 means "permanent, recoverable freeze" (via pause/upgrade), and the ×1.5 adds for truly unrecoverable — worth saying explicitly; or
  • The Info callout should be extended to clarify the freeze case too.

5. Rounding at exact halfway points is undefined

L18: "Round to the nearest $500." The Fee parameter drift example lands exactly on $3,750 → $4,000 (L121). $3,750 is equidistant from $3,500 and $4,000. The worked example effectively commits to "round half up" but the rule doesn't say so. One-line fix: "Round to the nearest $500 (ties round up)."

6. Likelihood-tier floor is implicit

L48–52 ("We reduce likelihood by one tier when any of the following apply…") — there are three triggers listed with any of. Questions left open:

  • Can multiple triggers stack, or is it a one-tier cap regardless of how many apply?
  • What happens when an L1 bug meets a downgrade trigger? Presumably it stays L1 (since there is no L0), but this isn't stated.

The second point matters because an L1/S1 bug dropping further would hit the floor-vs-zero distinction at L63–71.


Smaller content nits

7. Duplicate-report rule — what about after 7 days? L136: "A duplicate submitted within 7 days receives 10% of the original as a courtesy payment." Past 7 days is presumably $0 with no courtesy payment — worth saying so explicitly rather than leaving readers to infer.

8. "a pool" is ambiguous in the flash-loan example — L109: "drain up to 100% of a pool in a single transaction." If Relay segregates funds across multiple pools, this could trigger the Bounded damage pool ×0.5 modifier (L93). The example rules out modifiers (L113), so the intent is "a pool = the whole thing" — but a researcher reading the page might reasonably disagree. Consider "the protocol's funds" or "the depository" for unambiguity.

9. "The list is fixed" — L75 reads a bit cryptically on first pass. Suggest "Modifiers are drawn from the fixed list below — do not invent new ones."

10. "the original" — L136 "10% of the original" reads as "10% of the original payout" but could be parsed as "10% of the original report." Suggest "10% of the first report's payout."

11. Intro drops "circuit breakers and alarms" — the old page explained why the 10-minute value-at-risk window existed (monitoring + pausing auto-stops longer attacks). The new framework relies on monitoring implicitly (via "Pausable + monitored" modifier) but never surfaces that Relay actually runs this infra. A one-liner in the intro or a footnote would make the modifier less abstract to researchers.


Style guide check (§5 terminology)

  • solver / solvers — lowercase throughout
  • Relay capitalized; Relay stack is fine
  • cross-chain — hyphenated (L125)
  • on-chain — see item Add sdk method docs #2

Callout length (L95–99): 5 lines, well under the §2.5 15-line cap. ✅


Summary

Strong rewrite — the framework is dramatically clearer than the old page and the worked examples land. The blockers are the typo and onchain term before merge; the framework consistency items (#3#6) are worth a second pass because the page becomes the de-facto contract with researchers and ambiguity there creates payout disputes.

Review checklist
  • Read current file and diff
  • Style guide scan (§2 + §5)
  • Framework consistency review
  • Post feedback
• [Branch](https://github.com/relayprotocol/relay-docs/tree/claude/update-bounty-framework-muOn6)

@clemsos clemsos requested a review from pedromcunha April 23, 2026 14:02
@mintlify
Copy link
Copy Markdown
Contributor

mintlify Bot commented Apr 23, 2026

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
unevenlabs 🟢 Ready View Preview Apr 23, 2026, 2:13 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

Claude finished @clemsos's task in 2m 53s —— View job


Code Review — Bug Bounty Program rewrite (follow-up)

Re-reviewed after the three recent commits (Simplify bounty page…, Clarify $0 vs $500 floor, clarify "permanently unrecoverable" + edits, small tweaks, simplify relay surface). Good progress — most of the framework-consistency concerns from the prior pass are resolved. A few stylistic items remain, plus one minor residual on the admin-only rule.

security/ is out of scope for §2/§3 profiles per CLAUDE.md, so this is coherence + §5 terminology only.


Resolved from previous review

  • Add Just-In-Time gas guide #1 eligibale typo — fixed (L61).
  • Add sdk method docs #2 on-chainonchain — fixed. L86 reads "No onchain event emitted"; the "no on-chain recourse" phrase in the Permanently unrecoverable trigger was removed entirely.
  • Improve integration docs #3 Likelihood-downgrade overlap with modifiers — fixed. The three downgrade triggers were collapsed to a single rule (L48: "we reduce likelihood by one tier … when the exploit freezes rather than steals"). No more double-counting risk against the ×0.75 modifiers.
  • Add execute bridge api + update docs #4 Permanently unrecoverable on S3 freeze — addressed. The new <Info> block at L91–99 makes the severity-vs-modifier distinction explicit ("different from how quickly the damage occurs").
  • Add deep linking guide #5 Rounding at halfway points — fixed ("ties round up", L18).
  • Fix typos #6 Likelihood-tier floor — fixed ("floored at L1", L48).

New issues in this pass

1. Dash inconsistency at L48

We reduce likelihood by one tier (floored at L1) when the exploit freezes rather than steals - unless the freeze is a step toward theft.

Every other em-style break on this page uses (L6, L22, L26–29, L40, L59, etc.). L48 uses a single hyphen. Fix this →

2. L8 scope line reads awkwardly

This framework applies across the Relay stack, protocol and services.

Parsed at a glance it's ambiguous between "(the Relay stack), (protocol), and (services)" — three things — vs. "the Relay stack: protocol and services" — two things apposed to "stack". The commit message simplify relay surface suggests the second was intended. Suggest:

  • "This framework applies across the Relay stack — both the protocol and services." or
  • "This framework applies across Relay's protocol and services."

Fix this →


Framework consistency — residual

3. Admin-only rule: S1 classification vs. $0 eligibility is under-connected

Two rules govern admin-only bugs and they land in different sections:

  • L29 / L36 — "Admin-only bugs are S1" (severity rule, with S1 base payouts of $500–$2,000).
  • L61–63 — "A report is not eligible for a bounty when: the only exploit path requires a privileged role …" (eligibility rule, $0).

Read naively, S1 promises at least $500; the eligibility rule negates that for the admin-only subset. The resolution — S1 applies to admin-only bugs that also have an external path reaching the role, otherwise $0 — is inferrable but not stated. A one-line cross-reference from the S1 admin-only bullet back to the eligibility list (or vice versa) would make the page self-consistent without a reader having to stitch it together.

4. Pausable + monitored: when does the modifier actually apply?

The Flash loan reentrancy example (L107–113) does not apply Pausable + monitored (×0.75) because "funds laundered in the same tx before any response is possible" — i.e., even a pausable contract can't help when the whole attack finishes in one tx. The Fee parameter drift example (L115–121) does apply it because the drain is slow enough for pause + monitoring to bite.

Both examples are the right call, but the implicit rule — "the modifier applies only when pause/monitoring materially reduces expected loss" — isn't spelled out. As written, L88 says the modifier applies whenever the contract "can be paused and active monitoring … is in place," which is a property of the deployment, not the bug. A researcher could reasonably claim ×0.75 for a single-tx flash loan exploit on a pausable contract. Worth tightening the trigger definition, e.g. "…monitoring is in place and would meaningfully reduce loss against this exploit."

5. Quick checklist "or" vs. stacking

- ×1.25 — hard to fix, or hard to detect
- ×0.75 — expensive to exploit, or pausable with active monitoring

These are two separate modifiers each (per the full table), and L101 says modifiers stack — so a bug that's both hard to fix and hard to detect gets ×1.25 × ×1.25 = ×1.5625. The "or" in the checklist suggests a single pick. Minor, but if a researcher scans only the checklist they'll under-claim. Either use "and/or" or drop the "or" in favor of comma-separation ("×1.25 — hard to fix; hard to detect").


Still open from previous review (minor)

These weren't addressed in the latest commits. None are blockers.

  • Add price rates api to docs #7 L136 — doesn't state what happens after 7 days (presumably $0, no courtesy). One-liner fix.
  • Add fallback logic and explain stale quotes #8 L109 — "drain up to 100% of a pool" still reads ambiguously against the Bounded damage pool ×0.5 modifier. "the protocol's funds" would be unambiguous.
  • feat: changes #10 L136 — "10% of the original" still ambiguous (of the original payout).
  • docs updates and typo fixes #11 — Intro doesn't mention that monitoring/pausing infra exists. The old page surfaced it via the 10-minute window; the new one relies on the Pausable + monitored modifier, but a researcher reading top-down has no grounding for it. One sentence in the intro would help.

Summary

The framework is now materially tighter than last pass — the overlap between likelihood downgrades and modifiers is gone, the rounding rule is explicit, and the permanently-unrecoverable callout removes the biggest ambiguity on freeze bugs. Blockers before merge: just the L48 dash and the L8 rewording. Items #3 and #4 are worth a look because they'll come up in actual researcher disputes; the rest are nice-to-haves.

Review checklist
  • Read current file
  • Diff vs origin/main
  • Verify status of prior review items
  • Scan for new issues from latest commits
  • Post follow-up review
• [Branch](https://github.com/relayprotocol/relay-docs/tree/claude/update-bounty-framework-muOn6)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

Claude finished @clemsos's task in 2m 0s —— View job


Code Review — Bug Bounty Program rewrite (follow-up)

Thanks for the thorough pass on the prior review — nearly every item is resolved. One new inconsistency was introduced in the flash-loan example, plus two small nits.


Prior review items — resolution

# Item Status
1 Typo eligibale ✅ Fixed (L61)
2 on-chainonchain ✅ Fixed (L86)
3 Likelihood-downgrade ↔ modifiers double-count ✅ Resolved — the overlapping downgrade bullets are gone; L48 now has a single freeze-vs-theft trigger that doesn't overlap any modifier
4 S3 freeze + ×1.5 double-count ✅ Addressed via the new <Info> (L91–99) drawing a clean line between severity-tier (damage speed) and the modifier (recoverability)
5 Rounding at halfway points ✅ Fixed — "ties round up" on L18
6 Likelihood floor implicit ✅ Fixed — "floored at L1" on L48
7 Duplicate report rule after 7 days ✅ Fixed (L136)
8 "a pool" ambiguity ⚠️ See new issue below
9 "The list is fixed" rewording ✅ Addressed (L71)
10 "the original" → "first report's payout" ✅ Fixed (L136)
11 Missing monitoring/circuit-breaker context in intro ✅ Fixed (L6)

New issue introduced

1. Flash-loan example now mismatches its own severity labelsecurity/bounties.mdx:109-113

The "a pool" ambiguity was fixed by softening the description, but the softening broke the S4 classification:

L109:  "drain some of the protocol's funds in a single transaction"
L111:  "**Severity: S4** — instant, total fund theft."

S4 is defined on L26 as "Total fund theft or protocol insolvency with no time to react." — the example description now says "some," which is not "total." A researcher mapping this example back to the rubric will land on S3 ("Large-scale fund theft … Requires some setup, but fast once triggered") or even S2, not S4.

Two clean fixes:

  • Restore "total theft" semantics in the description — e.g. "drain the depository in a single transaction" — and keep S4.
  • Or rewrite this example as an S3 to match the "some" framing and produce a different worked number ($15,000 base × 1.5 = $22,500 — but that now collides with the freeze example's final number, so probably the first option is preferable).

Fix this →


Smaller nits

2. Justification for the freeze-example downgrade doesn't match the stated rulesecurity/bounties.mdx:128

The framework rule at L48 is "reduce likelihood by one tier … when the exploit freezes rather than steals." That rule cleanly applies to this example. But the example justifies the downgrade with a different reason:

L128: "Likelihood: L2 — downgraded one tier from L3 because the attacker
       has no profit motive and pays to execute."

"No profit motive and pays to execute" isn't a trigger in the framework (the "High attacker cost" ×0.75 modifier captures attacker cost, not a likelihood downgrade). A researcher reading this may infer there's an unwritten rule. Cleaner to cite the rule that actually applies: "downgraded one tier from L3 because the exploit freezes rather than steals."

Fix this →

3. Stray hyphen where an em-dash is used elsewheresecurity/bounties.mdx:48

L48: "… when the exploit freezes rather than steals - unless the freeze is a step toward theft."

Everywhere else on this page the em-dash is used for parenthetical dashes (L6, L14–17, L22, L26–29, L111, etc.). L48 slipped through with a plain -. Swap to .

Fix this →


Style / terminology

  • onchain (L86) — matches §5
  • cross-chain hyphenation (L125)
  • solver lowercase throughout
  • ✅ Callout at L91–99 is 9 lines, under the §2.5 15-line cap
  • Note: security/ is out of scope for §2/§3 profile rules per CLAUDE.md §1.1, so this review only enforces §5 terminology and general clarity/consistency.

Summary

Strong iteration — the framework coherence items from the previous review are now in good shape. The only blocker-ish item is the flash-loan example's description-vs-severity mismatch (#1); the other two are style polish. Once #1 is reconciled, this reads as a clean, self-consistent researcher contract.

Review checklist
  • Read current security/bounties.mdx and diff vs origin/main
  • Check which prior review items were addressed
  • Scan for new issues introduced in latest commits
  • Post review
• [Branch](https://github.com/relayprotocol/relay-docs/tree/claude/update-bounty-framework-muOn6)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

Claude finished @clemsos's task in 2m 5s —— View job


Code Review — Bug Bounty Program rewrite (follow-up)

Re-reviewed after the latest commit (fix compile typo, which replaced <5-minute with sub-5-minute on L88 — good catch; MDX would have parsed <5 as an unclosed tag). No other content changes since the last review, so the three items flagged at 14:54:43Z are all still outstanding. No new issues introduced.

security/ is out of scope for §2/§3 profile rules per CLAUDE.md §1.1, so this review covers §5 terminology and content coherence only.


Fixes in this commit

  • MDX-unsafe <5-minute — replaced with sub-5-minute on L88. Without this, Mintlify would have failed to render (or silently swallowed text).

Still outstanding from the previous review

1. Flash-loan example: description vs severity label still mismatchedsecurity/bounties.mdx:109-111

L109: "drain some of the protocol's funds in a single transaction"
L111: "**Severity: S4** — instant, total fund theft."

S4's definition on L26 is "Total fund theft or protocol insolvency with no time to react." — "some" does not map to "total." The description and severity label point at different tiers; a researcher using this example as a calibration anchor will either under- or over-claim. Simplest fix: restore the "total" semantics in the description ("drain the depository in a single transaction") so it matches S4.

Fix this →

2. Freeze-example downgrade cites an unwritten rulesecurity/bounties.mdx:128

L128: "**Likelihood: L2** — downgraded one tier from L3 because the attacker
       has no profit motive and pays to execute."

The framework rule at L48 says the downgrade trigger is "the exploit freezes rather than steals" — which is exactly this example. "No profit motive / pays to execute" isn't a stated rule; cost-to-exploit is captured by the High attacker cost ×0.75 modifier, not a likelihood tier shift. Reword to cite the actual rule: "downgraded one tier from L3 because the exploit freezes rather than steals."

Fix this →

3. Stray hyphen where em-dash is used elsewheresecurity/bounties.mdx:48

L48: "… when the exploit freezes rather than steals - unless the freeze is a step toward theft."

Every other parenthetical dash on this page is (L6, L14–17, L22, L26–29, L103, L111, L113, L125, etc.). L48 is a plain hyphen.

Fix this →


Minor — still open from the second review

4. L8 scope line parses ambiguouslysecurity/bounties.mdx:8

L8: "This framework applies across the Relay stack, protocol and services."

Still reads as three things vs. two — the commit title simplify relay surface suggests stack = {protocol, services}, but a reader landing cold can't tell. "This framework applies across Relay's protocol and services." is unambiguous.


Style / terminology

  • onchain (L86)
  • cross-chain (L125) — hyphenated per §5
  • solver / solvers lowercase throughout
  • ✅ Info callout (L91–99) is 9 rendered lines, well under the §2.5 15-line cap
  • sub-5-minute is a good workaround for the MDX <5 parse issue

Summary

The single commit since last review was a correctness fix for MDX rendering — clean. The three items from the 14:54:43Z pass remain and are the last blockers before this page reads as a fully self-consistent researcher contract: (1) flash-loan example description/severity reconciliation, (2) L128 freeze-downgrade justification, (3) L48 em-dash. L8 wording from the earlier pass is a nice-to-have.

Review checklist
  • Read current security/bounties.mdx and diff vs origin/main
  • Check which prior review items were addressed in the latest commit
  • Scan for new issues from latest commits
  • Post review
• [Branch](https://github.com/relayprotocol/relay-docs/tree/claude/update-bounty-framework-muOn6)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

Claude finished @clemsos's task in 2m 4s —— View job


Code Review — Bug Bounty Program rewrite (follow-up)

Re-reviewed after the latest two commits (7c883bd clarify flash-loan example, de6a101 rewording). All three blockers from the 14:56 pass are resolved. The page now reads as a clean, self-consistent researcher contract. Nothing new introduced; only minor nice-to-haves remain.

security/ is out of scope for §2/§3 profile rules per CLAUDE.md §1.1, so this review covers §5 terminology and content coherence only.


Blockers from previous review — all resolved

# Item Status
1 Flash-loan example description ↔ S4 severity mismatch ✅ Fixed — L109 now reads "drain depository contract funds in a single transaction" which maps cleanly to S4 "Total fund theft … with no time to react" (L26)
2 L128 freeze-downgrade justification cited an unwritten rule ✅ Fixed — L128 now cites the actual framework rule: "downgraded one tier from L3 because the exploit freezes rather than steals"
3 L48 stray hyphen vs. em-dashes elsewhere ✅ Fixed — reworked to use parentheses: "… freezes rather than steals (unless the freeze is a step toward theft)" — removes the typographic inconsistency cleanly
4 L8 scope line ambiguity ✅ Fixed — "This framework applies across Relay's protocol and services." unambiguous

Still open (minor, nice-to-haves — not blockers)

1. Pausable + monitored trigger is a deployment property, not a bug propertysecurity/bounties.mdx:88

The two worked examples make the right call (flash-loan at L107–113 correctly skips the ×0.75 because a single-tx exploit finishes before any pause can bite; fee drift at L115–121 correctly applies it). But the written trigger — "Contract can be paused and active monitoring with sub-5-minute response is in place" — is a property of the deployment. A researcher reading only the trigger could claim ×0.75 on a single-tx flash-loan because the contract is pausable. Tightening to something like "…monitoring is in place and would meaningfully reduce loss against this specific exploit" would make the implicit rule explicit.

2. Quick checklist "or" vs. stackingsecurity/bounties.mdx:75-78

- ×1.25 — hard to fix, or hard to detect
- ×0.75 — expensive to exploit, or pausable with active monitoring

The full table lists these as two separate modifiers each, and L101 says modifiers stack — so a bug that is both hard to fix and hard to detect should get ×1.25 × ×1.25 = ×1.5625. The "or" suggests a single pick. A researcher who skims only the checklist will under-claim. Either "and/or" or semicolon-separation ("×1.25 — hard to fix; hard to detect") would be clearer.

3. S1 admin-only vs. $0 eligibility — still a bit disconnectedsecurity/bounties.mdx:36 and L61-63

Two rules govern admin-only bugs and land in different sections:

  • L36: "Admin-only bugs are S1" (severity) — implying a $500 base minimum.
  • L61–63: "A report is not eligible for a bounty when: the only exploit path requires a privileged role" (eligibility) — implying $0.

The resolution is inferrable (S1 applies when there's also an external path reaching the role; otherwise $0), but it isn't stated. A one-line cross-reference from the S1 admin-only bullet back to the eligibility list would self-contain the page. Not a blocker — the rules are correct, just separated.

4. S3 "permanent freeze" + ×1.5 modifier — example doesn't spell out why both applysecurity/bounties.mdx:127-129

The <Info> at L91–99 establishes the distinction (severity = damage magnitude/speed, ×1.5 = recoverability) cleanly in the abstract. The permanent-pool-freeze example takes S3 and ×1.5 but doesn't say why the freeze is unrecoverable in this case (e.g., no pause/upgrade path for pool accounting). A researcher applying the framework to other permanent-freeze bugs may be unsure whether ×1.5 always applies to S3 freeze or only in specific conditions. A half-sentence in L129 ("…with no pause or upgrade path for pool accounting") would close the loop.


Style / terminology

  • onchain (L86) — matches §5
  • cross-chain (L125) — hyphenated per §5
  • solver lowercase throughout
  • ✅ Info callout at L91–99 is 9 rendered lines, well under the §2.5 15-line cap
  • ✅ No stray on-chain, eligibale, or API Key variants found

Summary

The framework is now clean: no overlap between likelihood downgrades and modifiers, rounding rule is explicit, permanent-unrecoverable-vs-severity distinction is handled by the Info callout, and all three worked examples cite their rules correctly. Ready to merge from a content-consistency standpoint. The four items above are polish-tier items worth a follow-up but not a blocker.

Review checklist
  • Read current security/bounties.mdx and diff vs origin/main
  • Check status of prior review items
  • Scan for new issues from latest commits
  • Post review
• [Branch](https://github.com/relayprotocol/relay-docs/tree/claude/update-bounty-framework-muOn6)

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 23, 2026

Claude finished @clemsos's task in 1m 50s —— View job


Code Review — Bug Bounty Program rewrite (follow-up)

Re-reviewed after the latest two commits (2705e43 clarify onlyOwner and monitoring, 54e2dfa checklist fix). Three of the four nice-to-haves from the 15:04 pass are addressed. The page is in good shape to merge; one leftover content item and one minor inconsistency introduced by the checklist fix.

security/ is out of scope for §2/§3 profile rules per CLAUDE.md §1.1, so this review covers §5 terminology and content coherence only.


Prior review items — resolution

# Item Status
1 Pausable + monitored is a deployment property, not a bug property ✅ Fixed — L88 now reads "…active monitoring with sub-5-minute response is in place and would meaningfully reduce loss against this specific exploit"
2 Quick checklist "or" vs stacking 🟡 Partially fixed — see issue #1 below
3 S1 admin-only ↔ $0 eligibility disconnect ✅ Fixed — L36 now says "They are only eligible for a bounty when there is an external path to reach the role (see below)." cleanly bridging the two sections
4 S3 + ×1.5 freeze example doesn't explain why it's unrecoverable ❌ Not addressed — see issue #2 below

Remaining items (not blockers)

1. Quick checklist inconsistencysecurity/bounties.mdx:76-77

The ×1.25 line was changed from "or" to "/" to hint at stackability:

- ×1.25 — hard to fix / hard to detect
- ×0.75 — expensive to exploit, or pausable with active monitoring

Two reads:

  • The two forms are typographically inconsistent now — reads as though the ×1.25 pair behaves differently from the ×0.75 pair, when both are just "two separate modifiers at the same factor."
  • The original concern was that "or" understates stacking (a bug that's hard-to-fix and hard-to-detect gets ×1.25 × ×1.25 = ×1.5625). The / softens but doesn't fully resolve the ambiguity — a reader could still read it as "one or the other."

Either apply the same change to ×0.75 (expensive to exploit / pausable with monitoring) or use explicit ; each applies if it triggers semantics. Minor — the full trigger table below is the authoritative source and L101 does call out stacking.

Fix this →

2. Permanent-freeze example doesn't spell out why the freeze is unrecoverablesecurity/bounties.mdx:127-129

- **Severity: S3** — permanent freeze of a pool.
- **Likelihood: L2** — downgraded one tier from L3 because the exploit freezes rather than steals.
- **Base: $15,000.** Permanently unrecoverable (×1.5) → **$22,500**.

The <Info> at L91–99 establishes the severity-vs-modifier distinction cleanly: severity = damage magnitude/speed, ×1.5 = recoverability. But this worked example applies both to the same event and doesn't explain why pool accounting is unrecoverable in this specific case. A reader applying the framework to, say, a permanent freeze on a contract with an upgrade path may not know whether ×1.5 still applies.

Half-sentence fix: "…Permanently unrecoverable (×1.5) — pool accounting has no pause or upgrade path…". This mirrors the flash-loan example at L113 which cites the specific reason ("funds laundered in the same tx before any response is possible").

Fix this →


Style / terminology

  • onchain (L86) — matches §5
  • cross-chain (L125) — hyphenated per §5
  • solver lowercase throughout
  • ✅ Info callout at L91–99 is 9 rendered lines, well under the §2.5 15-line cap
  • ✅ No stray on-chain, eligibale, API Key variants found
  • ✅ Em-dash usage is consistent throughout (L48 parenthesis fix held up)

Summary

The page is mergeable. The admin-only + monitoring clarifications land cleanly — the S1/$0 cross-reference at L36 is exactly the right fix, and the "meaningfully reduce loss against this specific exploit" addition at L88 makes the modifier's trigger a property of the bug rather than the deployment, which was the important one. The two open items here are polish rather than blockers.

Review checklist
  • Read current security/bounties.mdx and diff vs origin/main
  • Check status of prior review items
  • Scan for new issues from latest commits
  • Post review
• [Branch](https://github.com/relayprotocol/relay-docs/tree/claude/update-bounty-framework-muOn6)

Mintlify treats $...$ pairs as LaTeX math delimiters, so
dollar-denominated amounts were rendering as italicized equations.
Escape every $ in the page.
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 24, 2026

Claude finished @clemsos's task in 1m 36s —— View job


Code Review — Bug Bounty Program rewrite (follow-up)

Re-reviewed after the latest commit (06e684f Escape dollar signs so Mintlify doesn't parse them as LaTeX math). The fix is correct and addresses a real rendering bug — Mintlify treats $...$ pairs as LaTeX math delimiters, which would have italicized the dollar amounts across the page. All $ in prose and tables are now escaped as \$. No new content issues introduced.

security/ is out of scope for §2/§3 profile rules per CLAUDE.md §1.1, so this review covers §5 terminology, MDX correctness, and content coherence only.


Fix in this commit

  • Dollar signs escaped. L18, L54–57, L59, L101, L103, L113, L121, L129 — all $ escaped as \$. Spot-checked: every numeric dollar amount on the page is now safe from LaTeX math parsing. Clean, minimally invasive fix.

Still outstanding (minor, non-blocking nice-to-haves)

These two items have persisted from the 15:19 review. Not blockers for merge.

1. Quick checklist separator inconsistencysecurity/bounties.mdx:76-77

- ×1.25 — hard to fix / hard to detect
- ×0.75 — expensive to exploit, or pausable with active monitoring

The ×1.25 line was updated to / to signal stackability; ×0.75 still uses , or. Both pairs behave identically (two independent modifiers at the same factor, stackable per L101), so the typographic split reads as though they don't. Normalize to the same separator.

Fix this →

2. Permanent-freeze example doesn't state why the freeze is unrecoverablesecurity/bounties.mdx:127-129

- **Severity: S3** — permanent freeze of a pool.
- **Likelihood: L2** — downgraded one tier from L3 because the exploit freezes rather than steals.
- **Base: \$15,000.** Permanently unrecoverable (×1.5) → **\$22,500**.

The <Info> at L91–99 establishes the severity-vs-modifier distinction cleanly, but this worked example applies ×1.5 without specifying what makes this particular freeze unrecoverable. The flash-loan example at L113 models this well — it justifies its ×1.5 with "funds laundered in the same tx before any response is possible." Half-sentence fix: append "— pool accounting has no pause or upgrade path" to the modifier clause so a researcher applying the framework to other freeze bugs has a concrete parallel.

Fix this →


Style / terminology

  • onchain (L86) — matches §5
  • cross-chain (L125) — hyphenated per §5
  • solver / solvers lowercase throughout
  • ✅ Info callout at L91–99 is 9 rendered lines, under the §2.5 15-line cap
  • ✅ Em-dash usage consistent across the page
  • ✅ No stray on-chain, eligibale, API Key variants

Summary

Latest commit is a correctness fix for MDX rendering — clean and contained. No content regressions introduced. The page reads as a self-consistent researcher contract; the two outstanding items are polish-tier. Ready to merge.

Review checklist
  • Read current security/bounties.mdx and diff vs origin/main
  • Verify dollar-sign escape fix (spot-checked all \$ usages)
  • Check status of prior review items
  • Scan for new issues introduced in latest commit
  • Post review
• [Branch](https://github.com/relayprotocol/relay-docs/tree/claude/update-bounty-framework-muOn6)

@clemsos clemsos merged commit 17d8dab into main Apr 24, 2026
4 checks passed
@clemsos clemsos deleted the claude/update-bounty-framework-muOn6 branch April 24, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants