Add buy energy charges #505

Merged
PlasticDigits merged 3 commits from fix/332-buy-energy-charges into main 2026-06-19 06:02:39 +00:00
PlasticDigits commented 2026-06-19 05:00:06 +00:00 (Migrated from gitlab.com)

Summary

  • Replaces flat per-wallet Arena buy pacing with onchain buy-energy charges: 300s refill interval, 5-charge cap, 15s burst gap, computed nextBuyAllowedAt, and buyEnergyState wallet reads.
  • Wires deploy params, indexer sale-head fields, frontend move/charge CTA state, simulations, docs, invariants, and third-party scripting skill notes.
  • Adds focused verify script: bash scripts/verify-buy-energy-anvil.sh.

Verification

Item Command Result
Contracts focused #332 bash scripts/verify-buy-energy-anvil.sh PASS
Contracts local suite cd contracts && forge test --no-match-contract DoubAirdropMegaethForkTest PASS
Contracts full suite cd contracts && forge test FAIL only known DoubAirdropMegaethForkTest RPC/DNS fork tests without mainnet.megaeth.com access
Indexer clippy cd indexer && cargo clippy --all-targets -- -D warnings PASS
Indexer tests cd indexer && cargo test PASS
Frontend typecheck cd frontend && npm run typecheck PASS
Frontend lint cd frontend && npm run lint PASS with existing react-refresh warnings
Frontend tests cd frontend && npm test PASS
Simulations PYTHONPATH=simulations python3 -m unittest simulations.tests.test_ecostrategy simulations.tests.test_timecurve PASS
Whitespace git diff --check PASS

Notes

  • Full forge test still attempts the gitignored/network fork airdrop tests noted in AGENTS.md; the local suite excluding that fork-only contract passes.
  • No Docker or Postgres stack was required for this contracts/indexer/frontend/simulation change.
## Summary - Replaces flat per-wallet Arena buy pacing with onchain buy-energy charges: 300s refill interval, 5-charge cap, 15s burst gap, computed nextBuyAllowedAt, and buyEnergyState wallet reads. - Wires deploy params, indexer sale-head fields, frontend move/charge CTA state, simulations, docs, invariants, and third-party scripting skill notes. - Adds focused verify script: bash scripts/verify-buy-energy-anvil.sh. ## Verification | Item | Command | Result | | --- | --- | --- | | Contracts focused #332 | bash scripts/verify-buy-energy-anvil.sh | PASS | | Contracts local suite | cd contracts && forge test --no-match-contract DoubAirdropMegaethForkTest | PASS | | Contracts full suite | cd contracts && forge test | FAIL only known DoubAirdropMegaethForkTest RPC/DNS fork tests without mainnet.megaeth.com access | | Indexer clippy | cd indexer && cargo clippy --all-targets -- -D warnings | PASS | | Indexer tests | cd indexer && cargo test | PASS | | Frontend typecheck | cd frontend && npm run typecheck | PASS | | Frontend lint | cd frontend && npm run lint | PASS with existing react-refresh warnings | | Frontend tests | cd frontend && npm test | PASS | | Simulations | PYTHONPATH=simulations python3 -m unittest simulations.tests.test_ecostrategy simulations.tests.test_timecurve | PASS | | Whitespace | git diff --check | PASS | ## Notes - Full forge test still attempts the gitignored/network fork airdrop tests noted in AGENTS.md; the local suite excluding that fork-only contract passes. - No Docker or Postgres stack was required for this contracts/indexer/frontend/simulation change.
PlasticDigits commented 2026-06-19 05:04:18 +00:00 (Migrated from gitlab.com)

Security review — MR !505

Commit reviewed: 748b8b360f7e58e0ef93e9bcb520496345807c1f
Scope: Buy-energy pacing (TimeArena charge refill/burst gap, deploy params, indexer sale-head fields, frontend CTA/preflight, simulations, verify script).

Outcome: FINDINGS: 0 medium+

Reviewed the MR diff and traced attacker-controlled inputs to sinks across contracts (_spendBuyCharge / _refilledBuyEnergy / buyEnergyState), deploy scripts, indexer RPC head polling, and frontend preflight. No inline threads — nothing met the medium+ bar.

Summary

  • Onchain pacing: _spendBuyCharge runs before external token pulls/burns inside nonReentrant buy paths; failed txs revert charge spend atomically. Charge refill math caps at maxBuyCharges; burst gap enforced. No bypass path found for buy / buyWithCred / router-only buyFor.
  • Offchain: indexer only surfaces chain-read head fields; wallet buyEnergyState remains an onchain read for submit gating. No new injection/authz/SSRF/XSS surfaces in the diff.
  • UUPS note (below medium): this change inserts buyChargeIntervalSec / maxBuyCharges / burstBuyCooldownSec before arenaStart and appends _buyEnergy; upgrading an already-initialized pre-#332 proxy would corrupt storage and reset pacing semantics. Mainnet registry still has TimeArena at zero — first deploy with this implementation is layout-consistent. Operators must not upgradeTo a live flat-cooldown proxy without an append-only migration plan.

Security review: no medium+ findings on this diff.

## Security review — MR !505 **Commit reviewed:** `748b8b360f7e58e0ef93e9bcb520496345807c1f` **Scope:** Buy-energy pacing (`TimeArena` charge refill/burst gap, deploy params, indexer sale-head fields, frontend CTA/preflight, simulations, verify script). **Outcome:** `FINDINGS: 0` medium+ Reviewed the MR diff and traced attacker-controlled inputs to sinks across contracts (`_spendBuyCharge` / `_refilledBuyEnergy` / `buyEnergyState`), deploy scripts, indexer RPC head polling, and frontend preflight. No inline threads — nothing met the medium+ bar. **Summary** - Onchain pacing: `_spendBuyCharge` runs before external token pulls/burns inside `nonReentrant` buy paths; failed txs revert charge spend atomically. Charge refill math caps at `maxBuyCharges`; burst gap enforced. No bypass path found for `buy` / `buyWithCred` / router-only `buyFor`. - Offchain: indexer only surfaces chain-read head fields; wallet `buyEnergyState` remains an onchain read for submit gating. No new injection/authz/SSRF/XSS surfaces in the diff. - UUPS note (below medium): this change inserts `buyChargeIntervalSec` / `maxBuyCharges` / `burstBuyCooldownSec` before `arenaStart` and appends `_buyEnergy`; upgrading an already-initialized pre-#332 proxy would corrupt storage and reset pacing semantics. Mainnet registry still has `TimeArena` at zero — first deploy with this implementation is layout-consistent. Operators must not `upgradeTo` a live flat-cooldown proxy without an append-only migration plan. Security review: no medium+ findings on this diff.
PlasticDigits commented 2026-06-19 05:08:16 +00:00 (Migrated from gitlab.com)

We are doing a fresh deploy, so do not need to reserve legacy slots.

We are doing a fresh deploy, so do not need to reserve legacy slots.
PlasticDigits commented 2026-06-19 05:10:17 +00:00 (Migrated from gitlab.com)

We should have the cap include level minus one - so level 2 gets +1 cap (6), level 3 gets +1 cap (7), etc, so that level 5 has cap of 9. The frontend will also need to be updated where we share with user their benefits from levelup

We should have the cap include level minus one - so level 2 gets +1 cap (6), level 3 gets +1 cap (7), etc, so that level 5 has cap of 9. The frontend will also need to be updated where we share with user their benefits from levelup
PlasticDigits commented 2026-06-19 05:20:14 +00:00 (Migrated from gitlab.com)

changed this line in version 2 of the diff

changed this line in [version 2 of the diff](/PlasticDigits/yieldomega/-/merge_requests/135/diffs?diff_id=1860382567&start_sha=748b8b360f7e58e0ef93e9bcb520496345807c1f#3f55a5adef91715c4ebcbf78a528eb50c0e73c22_749_756)
PlasticDigits commented 2026-06-19 05:20:14 +00:00 (Migrated from gitlab.com)

added 1 commit

  • ceb0affc - Scale buy energy cap by level

Compare with previous version

added 1 commit <ul><li>ceb0affc - Scale buy energy cap by level</li></ul> [Compare with previous version](/PlasticDigits/yieldomega/-/merge_requests/135/diffs?diff_id=1860382567&start_sha=748b8b360f7e58e0ef93e9bcb520496345807c1f)
PlasticDigits commented 2026-06-19 05:20:53 +00:00 (Migrated from gitlab.com)

Implemented level-adjusted buy-energy caps and frontend level-up benefit copy in ceb0affc.

Implemented level-adjusted buy-energy caps and frontend level-up benefit copy in ceb0affc.
PlasticDigits commented 2026-06-19 05:20:54 +00:00 (Migrated from gitlab.com)

Confirmed fresh deploy scope; no legacy storage-slot reservation is needed for this MR.

Confirmed fresh deploy scope; no legacy storage-slot reservation is needed for this MR.
PlasticDigits commented 2026-06-19 05:21:06 +00:00 (Migrated from gitlab.com)

resolved all threads

resolved all threads
PlasticDigits commented 2026-06-19 05:22:52 +00:00 (Migrated from gitlab.com)

Security review — MR !505

Commit reviewed: ceb0affcaf639d3ab0ddd7fcc435410d6496989a
Scope: Buy-energy pacing (TimeArena charge refill/burst gap, level-adjusted caps, deploy params, indexer sale-head fields, frontend CTA/preflight, simulations, verify script).

Outcome: FINDINGS: 0 medium+

Re-reviewed the full MR diff at head (including level-adjusted maxBuyCharges + level - 1 in ceb0affc) and traced attacker-controlled inputs to sinks across contracts, deploy scripts, indexer head polling, and frontend submit paths. No inline threads — nothing met the medium+ bar.

Summary

  • Onchain pacing: _spendBuyCharge runs before external token pulls/burns inside nonReentrant buy paths; failed txs revert charge spend atomically. Refill math caps at _effectiveMaxBuyCharges (base cap + level − 1); burst gap enforced. Level bonus applies on pre-buy level, so a single tx cannot spend a charge and simultaneously benefit from a same-tx level-up cap increase. No bypass found for buy / buyWithCred / router-only buyFor (buyFor always debits buyer, router sets buyer = msg.sender).
  • Offchain: Indexer only surfaces chain-read head fields; wallet buyEnergyState remains an onchain read for submit gating. Frontend preflight gates burst cooldown via computed nextBuyAllowedAt; zero-charge submits still fail closed onchain (TimeArena: no buy charges) — UX-only gap, not fund loss or authz bypass.
  • Fresh deploy: Confirmed in-thread; storage layout changes are consistent for first deploy with this implementation. UUPS upgrade of a live flat-cooldown proxy without migration remains an operator footgun (below medium for this MR’s stated scope).

Security review: no medium+ findings on this diff.

## Security review — MR !505 **Commit reviewed:** `ceb0affcaf639d3ab0ddd7fcc435410d6496989a` **Scope:** Buy-energy pacing (`TimeArena` charge refill/burst gap, level-adjusted caps, deploy params, indexer sale-head fields, frontend CTA/preflight, simulations, verify script). **Outcome:** `FINDINGS: 0` medium+ Re-reviewed the full MR diff at head (including level-adjusted `maxBuyCharges + level - 1` in ceb0affc) and traced attacker-controlled inputs to sinks across contracts, deploy scripts, indexer head polling, and frontend submit paths. No inline threads — nothing met the medium+ bar. **Summary** - **Onchain pacing:** `_spendBuyCharge` runs before external token pulls/burns inside `nonReentrant` buy paths; failed txs revert charge spend atomically. Refill math caps at `_effectiveMaxBuyCharges` (base cap + level − 1); burst gap enforced. Level bonus applies on pre-buy level, so a single tx cannot spend a charge and simultaneously benefit from a same-tx level-up cap increase. No bypass found for `buy` / `buyWithCred` / router-only `buyFor` (`buyFor` always debits `buyer`, router sets `buyer = msg.sender`). - **Offchain:** Indexer only surfaces chain-read head fields; wallet `buyEnergyState` remains an onchain read for submit gating. Frontend preflight gates burst cooldown via computed `nextBuyAllowedAt`; zero-charge submits still fail closed onchain (`TimeArena: no buy charges`) — UX-only gap, not fund loss or authz bypass. - **Fresh deploy:** Confirmed in-thread; storage layout changes are consistent for first deploy with this implementation. UUPS upgrade of a live flat-cooldown proxy without migration remains an operator footgun (below medium for this MR’s stated scope). Security review: no medium+ findings on this diff.
PlasticDigits commented 2026-06-19 05:26:10 +00:00 (Migrated from gitlab.com)

We do not need legacy slots as we are not upgrading, we are doing fresh deployment.

We do not need legacy slots as we are not upgrading, we are doing fresh deployment.
PlasticDigits commented 2026-06-19 05:27:50 +00:00 (Migrated from gitlab.com)

changed this line in version 3 of the diff

changed this line in [version 3 of the diff](/PlasticDigits/yieldomega/-/merge_requests/135/diffs?diff_id=1860388172&start_sha=ceb0affcaf639d3ab0ddd7fcc435410d6496989a#3f55a5adef91715c4ebcbf78a528eb50c0e73c22_113_113)
PlasticDigits commented 2026-06-19 05:27:51 +00:00 (Migrated from gitlab.com)

changed this line in version 3 of the diff

changed this line in [version 3 of the diff](/PlasticDigits/yieldomega/-/merge_requests/135/diffs?diff_id=1860388172&start_sha=ceb0affcaf639d3ab0ddd7fcc435410d6496989a#3f55a5adef91715c4ebcbf78a528eb50c0e73c22_114_113)
PlasticDigits commented 2026-06-19 05:27:51 +00:00 (Migrated from gitlab.com)

added 1 commit

  • bf607140 - Remove legacy buy cooldown slot

Compare with previous version

added 1 commit <ul><li>bf607140 - Remove legacy buy cooldown slot</li></ul> [Compare with previous version](/PlasticDigits/yieldomega/-/merge_requests/135/diffs?diff_id=1860388172&start_sha=ceb0affcaf639d3ab0ddd7fcc435410d6496989a)
PlasticDigits commented 2026-06-19 05:29:20 +00:00 (Migrated from gitlab.com)

resolved all threads

resolved all threads
PlasticDigits commented 2026-06-19 05:31:04 +00:00 (Migrated from gitlab.com)

Security review — MR !505

Commit reviewed: bf60714084c055ba611afc0efe29d81c769027c5
Scope: Buy-energy pacing (TimeArena charge refill/burst gap, level-adjusted caps, deploy params, indexer sale-head fields, frontend CTA/preflight, simulations, verify script). Delta since prior review: removal of reserved _legacyNextBuyAllowedAt storage slot (bf607140).

Outcome: FINDINGS: 0 medium+

Re-reviewed the full MR diff at head and traced attacker-controlled inputs to sinks across contracts (_spendBuyCharge / _refilledBuyEnergy / _effectiveMaxBuyCharges / buyEnergyState), TimeArenaBuyRouter.buyFor → buyer = msg.sender, deploy scripts, indexer RPC head polling, and frontend submit paths. No inline threads — nothing met the medium+ bar.

Summary

  • Onchain pacing: _spendBuyCharge runs after _requireLiveAndAutoroll / _validateCharm and before external token pulls/burns inside nonReentrant buy paths; failed txs revert charge spend atomically. Refill math caps at _effectiveMaxBuyCharges (maxBuyCharges + level - 1); burst gap enforced. Level bonus uses pre-buy level, so a single tx cannot spend a charge and simultaneously benefit from a same-tx level-up cap increase. No bypass found for buy / buyWithCred / router-only buyFor.
  • Offchain: Indexer only surfaces chain-read head fields; wallet buyEnergyState / computed nextBuyAllowedAt remain onchain reads for submit gating. Frontend preflight gates burst cooldown; zero-charge submits still fail closed onchain (TimeArena: no buy charges) — UX-only gap, not fund loss or authz bypass.
  • Fresh deploy: Confirmed in-thread; storage layout (including removal of legacy reserved slot in bf607140) is consistent for first deploy with this implementation.

Security review: no medium+ findings on this diff.

## Security review — MR !505 **Commit reviewed:** `bf60714084c055ba611afc0efe29d81c769027c5` **Scope:** Buy-energy pacing (`TimeArena` charge refill/burst gap, level-adjusted caps, deploy params, indexer sale-head fields, frontend CTA/preflight, simulations, verify script). Delta since prior review: removal of reserved `_legacyNextBuyAllowedAt` storage slot (`bf607140`). **Outcome:** `FINDINGS: 0` medium+ Re-reviewed the full MR diff at head and traced attacker-controlled inputs to sinks across contracts (`_spendBuyCharge` / `_refilledBuyEnergy` / `_effectiveMaxBuyCharges` / `buyEnergyState`), `TimeArenaBuyRouter.buyFor` → `buyer = msg.sender`, deploy scripts, indexer RPC head polling, and frontend submit paths. No inline threads — nothing met the medium+ bar. **Summary** - **Onchain pacing:** `_spendBuyCharge` runs after `_requireLiveAndAutoroll` / `_validateCharm` and before external token pulls/burns inside `nonReentrant` buy paths; failed txs revert charge spend atomically. Refill math caps at `_effectiveMaxBuyCharges` (`maxBuyCharges + level - 1`); burst gap enforced. Level bonus uses pre-buy level, so a single tx cannot spend a charge and simultaneously benefit from a same-tx level-up cap increase. No bypass found for `buy` / `buyWithCred` / router-only `buyFor`. - **Offchain:** Indexer only surfaces chain-read head fields; wallet `buyEnergyState` / computed `nextBuyAllowedAt` remain onchain reads for submit gating. Frontend preflight gates burst cooldown; zero-charge submits still fail closed onchain (`TimeArena: no buy charges`) — UX-only gap, not fund loss or authz bypass. - **Fresh deploy:** Confirmed in-thread; storage layout (including removal of legacy reserved slot in `bf607140`) is consistent for first deploy with this implementation. Security review: no medium+ findings on this diff.
PlasticDigits commented 2026-06-19 06:02:39 +00:00 (Migrated from gitlab.com)

mentioned in commit 35c938cafd

mentioned in commit 35c938cafd4989d92c2e19a8eb96c0353f64ea26
PlasticDigits (Migrated from gitlab.com) merged commit 35c938cafd into main 2026-06-19 06:02:39 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega!505
No description provided.