Wallet cooldown: one buy per wallet every 5 minutes #33

Closed
opened 2026-04-15 06:34:50 +00:00 by PlasticDigits · 5 comments
PlasticDigits commented 2026-04-15 06:34:50 +00:00 (Migrated from gitlab.com)

Summary

Enforce a per-wallet cooldown so each wallet can only execute a buy once every 5 minutes (rolling window or fixed interval—TBD during implementation).

Motivation

Reduce rapid repeat buys from the same address (abuse, MEV-adjacent behavior, or unfair pacing) and align sale UX with intended pacing.

Scope (likely touch points)

  • Smart contracts: Enforce cooldown onchain (source of truth); define exact semantics (per-sale, global, block/timestamp basis).
  • Indexer: Persist and/or expose cooldown-related fields or rejection reasons if needed for UI/analytics.
  • Frontend: Surface cooldown state, remaining time, and clear errors when blocked.
  • Docs: User-facing and developer docs for behavior and configuration.
  • Skills / agent guidance: Update repo skills or guardrails if behavior or testing expectations change.

Acceptance criteria (draft)

  • Cooldown duration is 5 minutes (configurable in one place if the codebase supports it).
  • Same wallet cannot complete another buy until the cooldown has elapsed.
  • Clear feedback when a buy is rejected for cooldown (onchain + offchain as applicable).
  • Tests cover the happy path, edge cases (exactly at boundary), and regression for non-cooldown flows.

Notes

  • Coordinate authoritative rule placement: prefer onchain enforcement with indexer/UI reflecting chain state.
  • Confirm whether cooldown applies per TimeCurve / per sale / globally.
## Summary Enforce a **per-wallet cooldown** so each wallet can only execute a buy **once every 5 minutes** (rolling window or fixed interval—TBD during implementation). ## Motivation Reduce rapid repeat buys from the same address (abuse, MEV-adjacent behavior, or unfair pacing) and align sale UX with intended pacing. ## Scope (likely touch points) - **Smart contracts**: Enforce cooldown onchain (source of truth); define exact semantics (per-sale, global, block/timestamp basis). - **Indexer**: Persist and/or expose cooldown-related fields or rejection reasons if needed for UI/analytics. - **Frontend**: Surface cooldown state, remaining time, and clear errors when blocked. - **Docs**: User-facing and developer docs for behavior and configuration. - **Skills / agent guidance**: Update repo skills or guardrails if behavior or testing expectations change. ## Acceptance criteria (draft) - [ ] Cooldown duration is **5 minutes** (configurable in one place if the codebase supports it). - [ ] Same wallet cannot complete another buy until the cooldown has elapsed. - [ ] Clear feedback when a buy is rejected for cooldown (onchain + offchain as applicable). - [ ] Tests cover the happy path, edge cases (exactly at boundary), and regression for non-cooldown flows. ## Notes - Coordinate **authoritative rule** placement: prefer onchain enforcement with indexer/UI reflecting chain state. - Confirm whether cooldown applies per **TimeCurve** / per **sale** / globally.
PlasticDigits commented 2026-04-21 04:12:30 +00:00 (Migrated from gitlab.com)

Issue #33 — per-wallet buy cooldown (status)

Implemented and on main (contracts were already merged; latest commit adds frontend UX, bots, docs).

Onchain (already deployed in repo)

  • buyCooldownSec (immutable, dev default 300), nextBuyAllowedAt(address), revert TimeCurve: buy cooldown.

Frontend

  • useTimecurveHeroTimer exposes chainNowSec (same skew as hero countdown); wallet cooldown row under primary timer when connected + sale live.
  • Buy panel: CL8Y slider + text input, band capped by balance; finalizeCharmSpendForBuy; buy disabled during cooldown; ABIs + reads for cooldown fields.

Bots

  • timecurve.json + actions.buy waits until block.timestamp >= nextBuyAllowedAt.

Docs / skills

  • primitives.md, PARAMETERS.md, invariants-and-business-logic.md, play-timecurve-doubloon skill, security model one-liner.

@brouie please verify behavior on your side; leaving this issue open per request.

Refs: TimeCurve.sol, TimeCurvePage.tsx, useTimecurveHeroTimer.ts, timeCurveBuyAmount.ts, bots/timecurve/....

## Issue #33 — per-wallet buy cooldown (status) Implemented and on `main` (contracts were already merged; latest commit adds frontend UX, bots, docs). ### Onchain (already deployed in repo) - `buyCooldownSec` (immutable, dev default **300**), `nextBuyAllowedAt(address)`, revert **`TimeCurve: buy cooldown`**. ### Frontend - `useTimecurveHeroTimer` exposes `chainNowSec` (same skew as hero countdown); wallet cooldown row under primary timer when connected + sale live. - Buy panel: CL8Y **slider + text input**, band capped by **balance**; `finalizeCharmSpendForBuy`; buy disabled during cooldown; ABIs + reads for cooldown fields. ### Bots - `timecurve.json` + `actions.buy` waits until `block.timestamp >= nextBuyAllowedAt`. ### Docs / skills - `primitives.md`, `PARAMETERS.md`, `invariants-and-business-logic.md`, `play-timecurve-doubloon` skill, security model one-liner. @brouie please verify behavior on your side; leaving this issue **open** per request. Refs: `TimeCurve.sol`, `TimeCurvePage.tsx`, `useTimecurveHeroTimer.ts`, `timeCurveBuyAmount.ts`, `bots/timecurve/...`.
Brouie commented 2026-04-22 01:00:35 +00:00 (Migrated from gitlab.com)

@PlasticDigits verified cooldown implementation on 66c264d.

Contract (TimeCurve.sol):

  • uint256 public immutable buyCooldownSec — constructor param
  • mapping(address => uint256) public nextBuyAllowedAt — per-wallet watermark
  • require(block.timestamp >= nextBuyAllowedAt[msg.sender], "TimeCurve: buy cooldown") at start of buy()
  • nextBuyAllowedAt[msg.sender] = block.timestamp + buyCooldownSec at end of buy()
  • require(_buyCooldownSec > 0) prevents zero-cooldown deploys
  • Default 0 for new wallets correctly means "never bought = always eligible"

Tests (TimeCurve.t.sol):

  • test_constructor_zero_buy_cooldown_reverts ✅
  • test_buy_cooldown_same_wallet_blocks_rapid_repeat ✅
  • test_buy_cooldown_boundary_elapses (tests the >= condition at exact boundary) ✅
  • test_buy_cooldown_independent_wallets (per-wallet, not global) ✅
  • test_timer_cap_fuzz updated to use monotonic clock for fuzz+cooldown compat ✅

Full test run:

  • TimeCurveTest: 62/62 PASS (was 58/58 before this commit, +4 new cooldown tests)
  • No regressions

Implementation matches the issue spec exactly. Closing.

@PlasticDigits verified cooldown implementation on 66c264d. Contract (`TimeCurve.sol`): - `uint256 public immutable buyCooldownSec` — constructor param - `mapping(address => uint256) public nextBuyAllowedAt` — per-wallet watermark - `require(block.timestamp >= nextBuyAllowedAt[msg.sender], "TimeCurve: buy cooldown")` at start of `buy()` - `nextBuyAllowedAt[msg.sender] = block.timestamp + buyCooldownSec` at end of `buy()` - `require(_buyCooldownSec > 0)` prevents zero-cooldown deploys - Default `0` for new wallets correctly means "never bought = always eligible" Tests (`TimeCurve.t.sol`): - `test_constructor_zero_buy_cooldown_reverts` ✅ - `test_buy_cooldown_same_wallet_blocks_rapid_repeat` ✅ - `test_buy_cooldown_boundary_elapses` (tests the `>=` condition at exact boundary) ✅ - `test_buy_cooldown_independent_wallets` (per-wallet, not global) ✅ - `test_timer_cap_fuzz` updated to use monotonic clock for fuzz+cooldown compat ✅ Full test run: - TimeCurveTest: **62/62 PASS** (was 58/58 before this commit, +4 new cooldown tests) - No regressions Implementation matches the issue spec exactly. Closing.
Brouie commented 2026-04-22 01:00:58 +00:00 (Migrated from gitlab.com)

@PlasticDigits ready for you to close this one — 403 on my end per usual for reporter perms on YO.

@PlasticDigits ready for you to close this one — 403 on my end per usual for reporter perms on YO.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-04-22 02:49:40 +00:00
PlasticDigits commented 2026-04-30 05:19:21 +00:00 (Migrated from gitlab.com)

mentioned in commit 39dd51b9a1

mentioned in commit 39dd51b9a123463ea72e192130bb431dce0409af
PlasticDigits commented 2026-04-30 05:20:01 +00:00 (Migrated from gitlab.com)

mentioned in issue #99

mentioned in issue #99
Sign in to join this conversation.
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#33
No description provided.