TimeCurve post-end UI: 'Already redeemed' panel doesn't show DOUB amount received; stake-at-launch numbers can confuse #90

Closed
opened 2026-04-29 06:06:54 +00:00 by Brouie · 6 comments
Brouie commented 2026-04-29 06:06:54 +00:00 (Migrated from gitlab.com)

Summary

After redeem-charms is executed, the post-end TimeCurve page shows the wallet's historical CHARM holdings + the worth-at-launch projection AS IF those CHARM are still claimable, even though they've already been redeemed for DOUB onchain. This is potentially confusing for users.

Repro

  1. Have a wallet with charmWeight > 0 (e.g. exercised a buy during sale)
  2. Sale ends (deadline passed + endSale() called)
  3. Wallet calls redeemCharms() successfully (DOUB transferred, charmsRedeemed flag flips to true onchain)
  4. Reload localhost:5173/timecurve

Observed

The "Your stake at launch" panel still shows:

  • "YOU HOLD: 3 CHARM"
  • "WORTH AT LAUNCH ≈ 3.979 CL8Y"

The Settlement panel correctly shows "Already redeemed" (good!), but the stake-at-launch panel above it doesn't reflect that the redemption has occurred. A new user landing on the page in this state may not understand why the redeem path is locked or what they actually received.

Suggested copy / data improvements

Could be solved by either:

A. Add a "Redeemed: 1,000,000 DOUB" line to the Your-stake-at-launch panel when charmsRedeemed=true, derived from totalTokensForSale * charmWeight / totalCharmWeight at the time of redemption (or just from the wallet's DOUB balance at moment of view).

B. Replace the "WORTH AT LAUNCH" line with the actual DOUB amount received post-redeem.

C. Add a dimming / strikethrough effect on the worth-at-launch number when redemption has occurred, with a "(redeemed)" label.

D. Show a green checkmark + "Settled" pill or similar on the panel header when in already-redeemed state.

Severity

P3-low. UX clarity issue, not a functional bug. Already-redeemed protections work correctly at the contract layer (double-spend prevented, button disabled). Just a copy / data display polish.

Filed during #38 F-09 / #39 C6 verification today on the post-end stack pivot.

cc @PlasticDigits

## Summary After redeem-charms is executed, the post-end TimeCurve page shows the wallet's historical CHARM holdings + the worth-at-launch projection AS IF those CHARM are still claimable, even though they've already been redeemed for DOUB onchain. This is potentially confusing for users. ## Repro 1. Have a wallet with charmWeight > 0 (e.g. exercised a buy during sale) 2. Sale ends (deadline passed + endSale() called) 3. Wallet calls redeemCharms() successfully (DOUB transferred, charmsRedeemed flag flips to true onchain) 4. Reload localhost:5173/timecurve ## Observed The "Your stake at launch" panel still shows: - "YOU HOLD: 3 CHARM" - "WORTH AT LAUNCH ≈ 3.979 CL8Y" The Settlement panel correctly shows "Already redeemed" (good!), but the stake-at-launch panel above it doesn't reflect that the redemption has occurred. A new user landing on the page in this state may not understand why the redeem path is locked or what they actually received. ## Suggested copy / data improvements Could be solved by either: A. Add a "Redeemed: 1,000,000 DOUB" line to the Your-stake-at-launch panel when charmsRedeemed=true, derived from totalTokensForSale * charmWeight / totalCharmWeight at the time of redemption (or just from the wallet's DOUB balance at moment of view). B. Replace the "WORTH AT LAUNCH" line with the actual DOUB amount received post-redeem. C. Add a dimming / strikethrough effect on the worth-at-launch number when redemption has occurred, with a "(redeemed)" label. D. Show a green checkmark + "Settled" pill or similar on the panel header when in already-redeemed state. ## Severity P3-low. UX clarity issue, not a functional bug. Already-redeemed protections work correctly at the contract layer (double-spend prevented, button disabled). Just a copy / data display polish. Filed during #38 F-09 / #39 C6 verification today on the post-end stack pivot. cc @PlasticDigits
Brouie commented 2026-04-29 06:08:51 +00:00 (Migrated from gitlab.com)

mentioned in issue #39

mentioned in issue #39
PlasticDigits commented 2026-04-29 06:56:31 +00:00 (Migrated from gitlab.com)

mentioned in commit b558ec1d86

mentioned in commit b558ec1d86db83e2b730853d694f35f6269a9db6
PlasticDigits commented 2026-04-29 06:56:57 +00:00 (Migrated from gitlab.com)

Implemented #90 on main (options A, C, D). Option B rejected in-product: replacing “worth at launch” with DOUB-only copy is misleading because participant value is anchored in CL8Y terms while entry used CL8Y / ETH / USDM rails.

What shipped

  • TimeCurveStakeAtLaunchSection: after charmsRedeemed, shows Redeemed · X DOUB from the same sale-allocation ratio as redeemCharms (expectedTokenFromCharms in useTimeCurveSaleSession).
  • Struck + dimmed CL8Y-at-launch figure with (redeemed) on the label (historical projection, not an open claim — charmWeight stays onchain after redeem).
  • Settled chrome: green check + live-tone badge in the section header actions.
  • Docs / tests: docs/frontend/timecurve-views.md § stake redeemed, invariants § #90, play checklist skills/verify-yo-timecurve-stake-redeemed-ui/SKILL.md; Vitest TimeCurveStakeAtLaunchSection.test.tsx.

Verify checklist for @brouie

  1. Post-redeem wallet on /timecurve: Your stake at launch shows Settled (check + badge) on the right of the section heading.
  2. Redeemed row shows DOUB matching totalTokensForSale × charmWeight ÷ totalCharmWeight (same as redeem tx / Protocol reads).
  3. Worth at launch ≈ CL8Y number is struck through, tile dimmed/dashed, label includes (redeemed).
  4. Settlement CTA still Already redeemed; no regression to buy/redeem gates.
  5. During active sale (not redeemed): panel unchanged — no redeemed row, no strike-through.

Leaving issue open for your sign-off.

Implemented **#90** on `main` (options **A**, **C**, **D**). **Option B** rejected in-product: replacing “worth at launch” with DOUB-only copy is misleading because participant value is anchored in **CL8Y** terms while entry used **CL8Y / ETH / USDM** rails. ### What shipped - **`TimeCurveStakeAtLaunchSection`**: after `charmsRedeemed`, shows **Redeemed · X DOUB** from the same sale-allocation ratio as `redeemCharms` (`expectedTokenFromCharms` in `useTimeCurveSaleSession`). - **Struck + dimmed** CL8Y-at-launch figure with **(redeemed)** on the label (historical projection, not an open claim — `charmWeight` stays onchain after redeem). - **Settled** chrome: green check + live-tone badge in the section header `actions`. - **Docs / tests**: [`docs/frontend/timecurve-views.md` § stake redeemed](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/frontend/timecurve-views.md), [invariants § #90](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/testing/invariants-and-business-logic.md), play checklist [`skills/verify-yo-timecurve-stake-redeemed-ui/SKILL.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/skills/verify-yo-timecurve-stake-redeemed-ui/SKILL.md); Vitest `TimeCurveStakeAtLaunchSection.test.tsx`. ### Verify checklist for @brouie 1. Post-redeem wallet on **`/timecurve`**: **Your stake at launch** shows **Settled** (check + badge) on the right of the section heading. 2. **Redeemed** row shows **DOUB** matching `totalTokensForSale × charmWeight ÷ totalCharmWeight` (same as redeem tx / Protocol reads). 3. **Worth at launch ≈** CL8Y number is **struck through**, tile dimmed/dashed, label includes **(redeemed)**. 4. **Settlement** CTA still **Already redeemed**; no regression to buy/redeem gates. 5. **During active sale** (not redeemed): panel unchanged — no redeemed row, no strike-through. Leaving issue **open** for your sign-off.
Brouie commented 2026-04-30 01:08:29 +00:00 (Migrated from gitlab.com)

mentioned in issue #38

mentioned in issue #38
Brouie commented 2026-04-30 02:08:57 +00:00 (Migrated from gitlab.com)

Verified 282eb62 against all 5 SKILL.md acceptance items on the SKIP+KUMBAYA+NO_COOLDOWN stack today. Closing.

Stack state for verification

  • Frontend: localhost:5173 at 282eb62
  • Stack: SKIP+KUMBAYA+NO_COOLDOWN, sale started fresh, then post-end pivot
  • Wallet: deployer 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (Anvil acct #0)
  • Pre-redeem: bought 5 CHARM via cast (status 1), warped chain past 24h deadline (chain ts 1777601080 vs deadline 1777598257), called endSale() (status 1, SaleEnded event), charmRedemptionEnabled()=true
  • Live signed redeemCharms() from deployer (status 1, tx 0x08babc72...)
  • Post-redeem onchain reads:
    • charmsRedeemed(deployer) = true
    • charmWeight(deployer) = 5e18 (preserved post-redeem per B4 contract semantics)
    • TimeCurve.launchedToken() = 0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1
    • DOUB.balanceOf(deployer) = 1000000000000000000000000 (1e24 wei = 1,000,000 DOUB)
    • Math: totalTokensForSale × charmWeight ÷ totalCharmWeight = 1e24 × 5e18 ÷ 5e18 = 1e24 (deployer owns 100% of charm weight, redeems 100% of launch supply)

Item 1 - Settlement panel "Already redeemed": PASS

Loaded /timecurve (Simple page) with deployer wallet connected. Settlement section reads:

"Redeem CHARM — The sale is over. Redeem CHARM to mint your DOUB share onchain. Already redeemed — Sale ended"

CTA correctly transitions to "Already redeemed" post-redemption (existing behavior preserved).

Item 2 - Settled badge in "Your stake at launch" section header: PASS

Section heading "Your stake at launch" renders a "Settled" badge in the right-side actions area (per the section-heading__actions CSS hook noted in the SKILL.md). Settled label visible alongside the section title.

Item 3 - Redeemed row shows DOUB amount: PASS

The redeemed row renders:

Redeemed: 1m DOUB

Abbreviated display of 1,000,000 DOUB. Matches onchain DOUB.balanceOf(deployer) = 1e24 wei exactly. The integer-division math from the contract (totalTokensForSale × charmWeight ÷ totalCharmWeight) maps cleanly to the rendered value: 1M DOUB credited for 100% of the charm weight in this fresh-stack scenario.

Item 4 - Worth at launch struck-through with (redeemed) label: PASS

The historical CL8Y projection renders with strike-through and explicit qualifier:

Worth at launch ≈ (redeemed) 6.621 CL8Y

The CL8Y-at-launch figure (5 CHARM × 1.324 CL8Y/CHARM ≈ 6.62 CL8Y) stays visible as context but is visually marked as historical. The "(redeemed)" qualifier appears immediately before the strike-through value. NOT replaced by DOUB-only copy — exactly the option-B rejection dev called out in the close note.

Plus the section subtitle reinforces the framing:

"You already redeemed your allocation for DOUB. The tiles below are historical (your CHARM weight stays readable onchain after redemption). The CL8Y-at-launch figure is struck through — it described projected stake before settlement, not a balance you can claim twice."

This addresses the original #90 confusion concern: stake-at-launch numbers no longer mislead the user post-redemption because the CL8Y projection is clearly historical, the actual DOUB received is surfaced explicitly, and the copy explains why both are shown.

Item 5 - data-testid="timecurve-simple-stake-redeemed-doub" (optional): NOT EXERCISED

Marked optional in the SKILL.md, skipping live DevTools inspection. Items 1-4 cleanly verified.

Frontend <-> contract integration check

Sale-side reads all consistent with cast cross-checks:

  • "You hold 5 CHARM" matches charmWeight(deployer) = 5e18
  • "Redeemed 1m DOUB" matches DOUB.balanceOf(deployer) = 1e24
  • Strike-through CL8Y projection 6.621 matches 5 CHARM × 1.324 CL8Y/CHARM at launch per the linear price helper

Closure recommendation

All required SKILL.md items verified live. The post-redeem stake panel correctly preserves historical context (CL8Y projection retained for transparency, struck through to mark settlement) while surfacing the actual DOUB received as the primary settled value. Closing #90.

cc @PlasticDigits

Verified `282eb62` against all 5 SKILL.md acceptance items on the SKIP+KUMBAYA+NO_COOLDOWN stack today. Closing. ## Stack state for verification - Frontend: localhost:5173 at `282eb62` - Stack: SKIP+KUMBAYA+NO_COOLDOWN, sale started fresh, then post-end pivot - Wallet: deployer `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266` (Anvil acct #0) - Pre-redeem: bought 5 CHARM via cast (status 1), warped chain past 24h deadline (chain ts 1777601080 vs deadline 1777598257), called `endSale()` (status 1, SaleEnded event), `charmRedemptionEnabled()=true` - Live signed `redeemCharms()` from deployer (status 1, tx `0x08babc72...`) - Post-redeem onchain reads: - `charmsRedeemed(deployer) = true` - `charmWeight(deployer) = 5e18` (preserved post-redeem per B4 contract semantics) - `TimeCurve.launchedToken() = 0x959922bE3CAee4b8Cd9a407cc3ac1C251C2007B1` - `DOUB.balanceOf(deployer) = 1000000000000000000000000` (1e24 wei = 1,000,000 DOUB) - Math: `totalTokensForSale × charmWeight ÷ totalCharmWeight = 1e24 × 5e18 ÷ 5e18 = 1e24` (deployer owns 100% of charm weight, redeems 100% of launch supply) ## Item 1 - Settlement panel "Already redeemed": PASS Loaded `/timecurve` (Simple page) with deployer wallet connected. Settlement section reads: > "Redeem CHARM — The sale is over. Redeem CHARM to mint your DOUB share onchain. Already redeemed — Sale ended" CTA correctly transitions to "Already redeemed" post-redemption (existing behavior preserved). ## Item 2 - Settled badge in "Your stake at launch" section header: PASS Section heading "Your stake at launch" renders a "Settled" badge in the right-side actions area (per the `section-heading__actions` CSS hook noted in the SKILL.md). Settled label visible alongside the section title. ## Item 3 - Redeemed row shows DOUB amount: PASS The redeemed row renders: > Redeemed: **1m DOUB** Abbreviated display of 1,000,000 DOUB. Matches onchain `DOUB.balanceOf(deployer) = 1e24 wei` exactly. The integer-division math from the contract (`totalTokensForSale × charmWeight ÷ totalCharmWeight`) maps cleanly to the rendered value: 1M DOUB credited for 100% of the charm weight in this fresh-stack scenario. ## Item 4 - Worth at launch struck-through with (redeemed) label: PASS The historical CL8Y projection renders with strike-through and explicit qualifier: > Worth at launch ≈ (redeemed) ~~6.621~~ CL8Y The CL8Y-at-launch figure (5 CHARM × 1.324 CL8Y/CHARM ≈ 6.62 CL8Y) stays visible as context but is visually marked as historical. The "(redeemed)" qualifier appears immediately before the strike-through value. NOT replaced by DOUB-only copy — exactly the option-B rejection dev called out in the close note. Plus the section subtitle reinforces the framing: > "You already redeemed your allocation for DOUB. The tiles below are historical (your CHARM weight stays readable onchain after redemption). The CL8Y-at-launch figure is struck through — it described projected stake before settlement, not a balance you can claim twice." This addresses the original #90 confusion concern: stake-at-launch numbers no longer mislead the user post-redemption because the CL8Y projection is clearly historical, the actual DOUB received is surfaced explicitly, and the copy explains why both are shown. ## Item 5 - data-testid="timecurve-simple-stake-redeemed-doub" (optional): NOT EXERCISED Marked optional in the SKILL.md, skipping live DevTools inspection. Items 1-4 cleanly verified. ## Frontend <-> contract integration check Sale-side reads all consistent with cast cross-checks: - "You hold 5 CHARM" matches `charmWeight(deployer) = 5e18` - "Redeemed 1m DOUB" matches `DOUB.balanceOf(deployer) = 1e24` - Strike-through CL8Y projection 6.621 matches `5 CHARM × 1.324 CL8Y/CHARM at launch` per the linear price helper ## Closure recommendation All required SKILL.md items verified live. The post-redeem stake panel correctly preserves historical context (CL8Y projection retained for transparency, struck through to mark settlement) while surfacing the actual DOUB received as the primary settled value. Closing #90. cc @PlasticDigits
Brouie (Migrated from gitlab.com) closed this issue 2026-04-30 02:08:59 +00:00
PlasticDigits commented 2026-04-30 05:25:11 +00:00 (Migrated from gitlab.com)

mentioned in issue #100

mentioned in issue #100
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#90
No description provided.