Complete specification of the reward system for Bounty Challenge.
Bounty Challenge uses a points-based reward system designed to:
- Incentivize quality - Valid issues earn points
- Reward engagement - Starring repos adds bonus points
- Prevent abuse - Invalid and duplicate issues incur penalties
- Ensure fairness - Simple, transparent scoring
| Principle | Implementation |
|---|---|
| Point-Based | 1 point per valid issue |
| Star Bonus | 0.25 points per starred repo |
| Penalty System | Invalid and duplicate issues reduce balance |
| Quality Gate | Only valid labeled issues count |
| Source | Points | Description |
|---|---|---|
| Valid Issue | 1 point | Issue closed with valid label |
| Starred Repo | 0.25 points | Each starred target repository |
Where:
valid_count= number of valid issuesstar_bonus= starred repos count × 0.25penalty= see Penalty System
Your raw weight is calculated directly from your net points:
Where:
-
$W_{user}$ = Your raw weight -
$net_points$ = Valid issues + star bonus - penalty - 0.02 = Weight per point (2% per point)
Note: Weights are normalized to sum to 1.0 across all non-penalized miners when submitted on-chain.
If net_points <= 0, your weight is 0 (penalized).
These constants are defined in src/scoring.rs:
pub const WEIGHT_PER_POINT: f64 = 0.02;
pub const STAR_BONUS_PER_REPO: f64 = 0.25;| Net Points | Weight | Calculation |
|---|---|---|
| 0 or less | 0% | Penalized |
| 1 | 2% | 1 × 0.02 = 0.02 |
| 5 | 10% | 5 × 0.02 = 0.10 |
| 10 | 20% | 10 × 0.02 = 0.20 |
| 25 | 50% | 25 × 0.02 = 0.50 |
| 50 | 100% | 50 × 0.02 = 1.00 |
| 100 | 200% | 100 × 0.02 = 2.00 (raw, normalized on-chain) |
Earn 0.25 points by starring each of these repositories:
| Repository | URL |
|---|---|
| CortexLM/vgrep | https://github.com/CortexLM/vgrep |
| CortexLM/cortex | https://github.com/CortexLM/cortex |
| PlatformNetwork/platform | https://github.com/PlatformNetwork/platform |
| PlatformNetwork/term-challenge | https://github.com/PlatformNetwork/term-challenge |
| PlatformNetwork/bounty-challenge | https://github.com/PlatformNetwork/bounty-challenge |
- Bonus: 0.25 points per starred repo
- Maximum: 1.25 points (5 repos × 0.25)
| Miner | Valid Issues | Stars | Issue Points | Star Points | Net Points | Weight |
|---|---|---|---|---|---|---|
| A | 10 | 0 | 10 | 0 | 10 | 20% |
| B | 10 | 4 | 10 | 1.0 | 11 | 22% |
| C | 45 | 5 | 45 | 1.25 | 46.25 | 92.5% |
| D | 50 | 5 | 50 | 1.25 | 51.25 | 102.5% (raw, normalized on-chain) |
TL;DR: Your valid issues act as a separate shield against each penalty type. Invalid and duplicate penalties are calculated independently -- your valid count is compared against each one on its own. Only the excess above your valid count is penalized.
There are two types of bad issues that can hurt your score:
| Label | Meaning |
|---|---|
invalid |
The issue was rejected as not a real bug or not useful |
duplicate |
The issue was already reported by someone else |
Each type is penalized independently. Your valid issue count serves as a forgiveness threshold for each type separately. You are only penalized for the excess beyond that threshold.
Invalid penalty:
Duplicate penalty:
Total penalty:
Net points (final score):
If net_points <= 0, your weight becomes 0 (penalized).
This is the most important detail to understand. The two penalty types are not combined before comparison. Each one is checked against your valid count on its own.
This matters because the results are different:
| Approach | Formula | 5 valid, 4 invalid, 4 duplicate |
|---|---|---|
| Combined (NOT how it works) | max(0, (4+4) - 5) = 3 |
3 penalty points |
| Separate (how it actually works) | max(0, 4-5) + max(0, 4-5) = 0 + 0 = 0 |
0 penalty points |
In the separate model, as long as each individual type stays at or below your valid count, you get no penalty at all -- even if the combined total exceeds your valid count.
Scenario 1 -- Clean miner (no penalties)
Valid: 5, Invalid: 3, Duplicate: 2
Invalid penalty: max(0, 3 - 5) = 0 (3 <= 5, forgiven)
Duplicate penalty: max(0, 2 - 5) = 0 (2 <= 5, forgiven)
Total penalty: 0
Net points: 5 - 0 = 5
Weight: 5 × 0.02 = 10%
Scenario 2 -- Only invalid issues exceed threshold
Valid: 5, Invalid: 7, Duplicate: 2
Invalid penalty: max(0, 7 - 5) = 2 (7 > 5, excess = 2)
Duplicate penalty: max(0, 2 - 5) = 0 (2 <= 5, forgiven)
Total penalty: 2
Net points: 5 - 2 = 3
Weight: 3 × 0.02 = 6%
Scenario 3 -- Only duplicate issues exceed threshold
Valid: 5, Invalid: 3, Duplicate: 8
Invalid penalty: max(0, 3 - 5) = 0 (3 <= 5, forgiven)
Duplicate penalty: max(0, 8 - 5) = 3 (8 > 5, excess = 3)
Total penalty: 3
Net points: 5 - 3 = 2
Weight: 2 × 0.02 = 4%
Scenario 4 -- Both types exceed threshold
Valid: 5, Invalid: 7, Duplicate: 8
Invalid penalty: max(0, 7 - 5) = 2
Duplicate penalty: max(0, 8 - 5) = 3
Total penalty: 2 + 3 = 5
Net points: 5 - 5 = 0
Weight: 0 (penalized)
Scenario 5 -- Heavily penalized
Valid: 2, Invalid: 6, Duplicate: 4
Invalid penalty: max(0, 6 - 2) = 4
Duplicate penalty: max(0, 4 - 2) = 2
Total penalty: 4 + 2 = 6
Net points: 2 - 6 = -4 (negative)
Weight: 0 (penalized)
To recover from penalty status:
- Submit new valid issues
- As your valid count rises, each penalty threshold rises with it
- Weight returns as soon as
net_points > 0
| Miner | Valid | Invalid | Duplicate | Inv. Penalty | Dup. Penalty | Total Penalty | Net Points | Status |
|---|---|---|---|---|---|---|---|---|
| A | 5 | 2 | 1 | 0 | 0 | 0 | 5 | OK |
| B | 5 | 7 | 2 | 2 | 0 | 2 | 3 | OK |
| C | 5 | 3 | 8 | 0 | 3 | 3 | 2 | OK |
| D | 5 | 7 | 8 | 2 | 3 | 5 | 0 | Penalized |
| E | 2 | 6 | 4 | 4 | 2 | 6 | -4 | Penalized |
Weights are calculated by the WASM module's get_weights() function:
- Each user's raw weight is
net_points * 0.02 - Weights are normalized to sum to 1.0 across all non-penalized miners
- The Platform validator submits these weights on-chain
Weights are converted to u16 for on-chain storage:
Miner registers and submits 5 valid issues:
Valid Points: 5 issues × 1 point = 5 points
Penalty: 0 (no invalid or duplicate issues)
Net Points: 5
Weight: 5 × 0.02 = 0.10 (10%)
Miner has 20 valid issues and starred 4 repos:
Issue Points: 20 × 1 = 20
Star Points: 4 × 0.25 = 1.0
Penalty: 0
Net Points: 21
Weight: 21 × 0.02 = 0.42 (42%)
Miner has 48 valid issues and starred 5 repos:
Issue Points: 48 × 1 = 48
Star Points: 5 × 0.25 = 1.25
Penalty: 0
Net Points: 49.25
Weight: 49.25 × 0.02 = 0.985 (98.5%)
If they get 2 more valid issues:
Net Points: 51.25
Weight: 51.25 × 0.02 = 1.025 (raw, normalized on-chain)
Miner has 3 valid issues, 8 invalid issues, 0 duplicate:
Valid Points: 3
Invalid Penalty: max(0, 8 - 3) = 5
Duplicate Penalty: 0
Net Points: 3 - 5 = -2 (negative)
Weight: 0 (penalized)
To recover, they need 3 more valid issues:
Valid: 6, Invalid: 8
Invalid Penalty: max(0, 8 - 6) = 2
Net Points: 6 - 2 = 4 (positive)
Weight: 4 × 0.02 = 0.08 (8%)
Miner has 5 valid issues, 7 invalid issues, 8 duplicate issues:
Valid Points: 5
Invalid Penalty: max(0, 7 - 5) = 2
Duplicate Penalty: max(0, 8 - 5) = 3
Total Penalty: 2 + 3 = 5
Net Points: 5 - 5 = 0
Weight: 0 (penalized)
| Parameter | Value | Description |
|---|---|---|
WEIGHT_PER_POINT |
0.02 | Weight earned per point |
STAR_BONUS_PER_REPO |
0.25 | Points per starred repo |
valid_label |
"valid" | Required label for rewards |
invalid_penalty |
dynamic | max(0, invalid - valid) |
duplicate_penalty |
dynamic | max(0, duplicate - valid) |
flowchart LR
A["Valid Issue"] --> B["+1 Point"]
C["Star Repo"] --> D["+0.25 Points"]
E["Invalid Issue"] --> F["Dynamic Penalty"]
G["Duplicate Issue"] --> F
B --> H["Net Points"]
D --> H
F --> H
H --> I["Weight = Net Points × 0.02"]
I --> J["Normalized on-chain"]