Skip to content

Add bonus rewards polling with fault-tolerant cron scheduling#6294

Open
aariya50 wants to merge 24 commits intodevelopfrom
feature/rewards-multiplier-6228
Open

Add bonus rewards polling with fault-tolerant cron scheduling#6294
aariya50 wants to merge 24 commits intodevelopfrom
feature/rewards-multiplier-6228

Conversation

@aariya50
Copy link
Copy Markdown
Contributor

@aariya50 aariya50 commented Feb 14, 2026

Summary

  • add direct-payout reward support for bonus credits in Rewards and the rewards poller
  • make the BOOE bonus scale linearly with BOOE held, capped by maxBonusBps and balanceForMaxBoost
  • smooth the bonus with a 28-run balance snapshot average, prune zeroed histories, and keep failed bonus batches durable in lastBonusRun.json

Related issue

@aariya50 aariya50 changed the base branch from master to develop February 14, 2026 00:06
@aariya50 aariya50 requested a review from witmk February 14, 2026 00:07
@aariya50 aariya50 mentioned this pull request Feb 14, 2026
Copy link
Copy Markdown

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

@aariya50
Copy link
Copy Markdown
Contributor Author

I don't appreciete how this tool: greptile-apps just changes my pr description. @hasanalqassab comments are fine but it should not be able to change (or share space) what I have written

@aariya50 aariya50 force-pushed the feature/rewards-multiplier-6228 branch from 3cf05b9 to d46abad Compare February 19, 2026 16:50
@aariya50 aariya50 changed the title Add community bonus rewards for token holders Apply claim-time community bonus with min balance thresholds Feb 20, 2026
@aariya50 aariya50 requested a review from witmk February 20, 2026 16:23
@aariya50
Copy link
Copy Markdown
Contributor Author

I will probably squash merge this lol

@aariya50 aariya50 force-pushed the feature/rewards-multiplier-6228 branch from 709e4ff to 9de2730 Compare February 24, 2026 17:49
@aariya50 aariya50 changed the title Apply claim-time community bonus with min balance thresholds Add bonus rewards polling with fault-tolerant cron scheduling Feb 24, 2026
@aariya50
Copy link
Copy Markdown
Contributor Author

Rewards Bonus Math Confirmation (Testnet)

User

  • User address: 288922eb7e5faa72222a859652ef275a1e6817f4
  • Rewards contract: 170147f58738c9f46112a874030420b823901f3b
  • Bonus token config: bonusBps = 1000 (10%)

Personal Emission Rate

  • rateByUser: 0.000064970736075567 tokens/sec
  • Per day: 5.6134715969289888 tokens/day
  • Exact on-chain value used in math: 64,970,736,075,567 contract units/sec

How Bonus Is Calculated

bonusAmount = floor(rateByUser * intervalSeconds * bonusBps / 10000)

With bonusBps = 1000, this is:
bonusAmount = floor(rateByUser * intervalSeconds / 10)

Last 4 Successful Bonus Runs (Exact Check)

Run time (UTC) Run time (ET) intervalSeconds Bonus payout (tokens) Expected = On-chain
2026-02-25 21:00:02 2026-02-25 16:00:02 8,280 0.053795769470569476
2026-02-26 03:00:02 2026-02-25 22:00:02 21,593 0.140291310407971823
2026-02-26 09:00:02 2026-02-26 04:00:02 21,594 0.140297807481579379
2026-02-26 15:00:02 2026-02-26 10:00:02 21,595 0.140304304555186936

Quick Breakdown of Current Daily Personal Rate (by contributing activities)

  • Activity 15: 4.933473354549792
  • Activity 3: 0.3173953283983872
  • Activity 2: 0.2553545627928288
  • Activity 9: 0.0959862824903232
  • Activity 8: 0.0108887497789824
  • Activity 7: 0.0003733189186752

Total: 5.6134715969289888 tokens/day


Conclusion: the bonus payouts for the last 4 successful runs match the rewards-poller math exactly.

aariya50 and others added 17 commits April 1, 2026 16:39
When a user who holds a community token performs any rewarded action, the
poller now also emits a one-time bonus event for that community's activity
on the Rewards contract. This is configurable via the COMMUNITY_BONUSES
env var (JSON array of {tokenAddress, eventName} entries) and scales to
N communities.

Holder sets are bulk-fetched in a single Cirrus query at startup and
refreshed each polling cycle in parallel with event fetching, so the
bonus check is a synchronous set lookup with zero added latency.

Co-authored-by: Cursor <cursoragent@cursor.com>
Undo the prior rewards-poller implementation so this branch can proceed
with the Rewards.sol contract-side bonus approach only.

Co-authored-by: Cursor <cursoragent@cursor.com>
Introduce a new CommunityBonusConfig struct to manage community token bonuses. Implement the setCommunityBonusMultiplier function to allow the owner to configure bonus multipliers for community tokens. Update the unclaimed rewards calculation to apply these bonuses based on user token holdings.

Add corresponding tests to ensure correct application of bonuses for both position and one-time activities, verifying that holders of community tokens receive the expected multipliers.

Co-authored-by: Cursor <cursoragent@cursor.com>
…ogic

Update the Rewards contract to include a try-catch block for calculating total rewards with bonuses. This change ensures that if an error occurs during the calculation, the function will return the base rewards instead of failing. Additionally, the logic for applying community bonuses has been streamlined for clarity.

Enhance the test suite by adding new ClaimActor contracts to simulate user actions and verify the correct settlement of rewards, both with and without bonuses, upon position closure and reward claims.

Co-authored-by: Cursor <cursoragent@cursor.com>
…rement and updating related events.

- Introduced `minBalance` to `CommunityBonusConfig` struct to set a minimum token balance for bonus eligibility.
- Updated `CommunityBonusMultiplierUpdated` event to include old and new minimum balance values.
- Modified `setCommunityBonusMultiplier` function to accept and handle the new `minBalance` parameter.
- Adjusted reward claiming logic to check for minimum balance before applying community bonuses.
- Updated tests to validate new functionality and ensure correct reward distribution based on minimum balance.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Bonus credits computed every 6h via cron, with pending retry on next run
- Eligible users determined by token holdings with max bonus percentage
- Cirrus and balance check calls wrapped with retryWithBackoff
- Revert Rewards.sol contract changes (already on separate PR)

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
- Add directPayout flag to Activity struct
- Add addOneTimeDirectPayoutActivity() function for registering direct payout activities
- Direct payout activities credit rewards immediately to unclaimedRewards (no accrual)
- Add DirectPayoutApplied event for tracking
- Add validation: directPayout only allowed for OneTime activities with zero emission rate
- Updated bonus token configuration to use `bonusBps` instead of `bonusPercentage` for clarity and consistency.
- Introduced `parseBonusTokenConfigs` function to validate and parse bonus token configurations from JSON.
- Enhanced bonus credit processing to include validation for bonus credits, ensuring all required fields are present before applying bonuses.
- Refactored `batchAddBonus` method to handle additional parameters for source contracts and event names.

This change improves the robustness of the bonus system and ensures that bonus configurations are correctly validated before use.
Co-authored-by: Cursor <cursoragent@cursor.com>
- Added `PRICE_ORACLE_ADDRESS` to the environment configuration.
- Adjusted `POLLING_INTERVAL` to 600000 milliseconds and `MAX_BATCH_SIZE` to 50.
- Introduced `BONUS_CRON_SCHEDULE` for scheduled bonus processing.
- Updated `WARNING_TRANSACTIONS_THRESHOLD` to 100 in the configuration.
- Refactored imports in `index.ts` to align with new directory structure.
- Removed unused `bonusTokenConfig.json` and related configuration logic.
- Added new features for bonus credit calculation and processing, including validation and emission rate handling.

These changes enhance the configuration clarity and improve the bonus processing logic, ensuring better maintainability and functionality.
- Add directPayout flag to Activity struct
- Add addOneTimeDirectPayoutActivity() function for registering direct payout activities
- Direct payout activities credit rewards immediately to unclaimedRewards (no accrual)
- Add DirectPayoutApplied event for tracking
- Add validation: directPayout only allowed for OneTime activities with zero emission rate
@aariya50 aariya50 force-pushed the feature/rewards-multiplier-6228 branch from e4acf27 to 2607d94 Compare April 1, 2026 21:04
@aariya50
Copy link
Copy Markdown
Contributor Author

aariya50 commented Apr 1, 2026

Admin parameters

Admins control:

  • the activity emission rate on Rewards via setEmissionRate(...)
  • maxBonusBps
  • balanceForMaxBoost

Example target:

  • base personal rate example: ~5 tokens/day = 57870370370370 contract units/sec for an 18-decimal token
  • maxBonusBps = 10000
  • balanceForMaxBoost = 1000 BOOE

Ethereum holder context:

  • BOOE is concentrated
  • a rough broad-holder / non-whale level is closer to ~800-900 BOOE
  • 1000 BOOE is much closer to a realistic user-facing threshold than 125000 BOOE

Formula

avgBalance = floor(sum(last 28 snapshots) / snapshotCount)
effectiveBalance = min(currentBalance, avgBalance)
dynamicBonusBps = min(
  maxBonusBps,
  floor(effectiveBalance * maxBonusBps / balanceForMaxBoost)
)
bonusAmount = floor(rateByUser * intervalSeconds * dynamicBonusBps / 10000)

Example outputs

Assuming avgBalance = currentBalance and a base personal rate of ~5 tokens/day:

BOOE held dynamicBonusBps Daily bonus
0 0 0
250 2,500 1.249999999999992
500 5,000 2.499999999999984
1000+ 10,000 4.999999999999968

For a 21,594 second interval:

  • 250 BOOE -> 2,500 bps -> 0.312413194444442445
  • 500 BOOE -> 5,000 bps -> 0.62482638888888489
  • 1000+ BOOE -> 10,000 bps -> 1.24965277777776978

Anti-gaming example

If someone had 0 BOOE for 27 runs and only bought 1000 BOOE right before this run:

avgBalance = floor(1000 / 28) = 35
effectiveBalance = min(1000, 35) = 35
dynamicBonusBps = floor(35 * 10000 / 1000) = 350

For the same 21,594 second interval, the bonus would only be:

  • 0.0437378472222219423

@aariya50
Copy link
Copy Markdown
Contributor Author

aariya50 commented Apr 2, 2026

aariya50 added 2 commits April 2, 2026 14:23
- Query DirectPayoutApplied events from Cirrus to get per-user bonus totals
- Break down bonus by source activity (e.g. BOOE) via activityId mapping
- Show Community Bonus card on My Rewards tab with token source badges
- Card only renders when user has bonus rewards > 0

Made-with: Cursor
@aariya50
Copy link
Copy Markdown
Contributor Author

aariya50 commented Apr 3, 2026

Added community bonus display to the My Rewards tab on the rewards page.

  • Queries DirectPayoutApplied events from Cirrus to show per-user bonus totals
  • Breaks down bonus by source token (e.g. BOOE) via activityId mapping
  • Card only renders when the user has bonus rewards > 0
Screenshot 2026-04-03 at 12 16 56 PM

Verified on testnet with user 1b7dc206ef2fe3aab27404b88c36470ccf16c0ce (84 bonus events, 9,432 points).

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.

Rewards multiplier

2 participants