M-01: distributePrizes must not strand PodiumPool — route empty category slices to protocol #116

Closed
opened 2026-05-04 04:14:31 +00:00 by PlasticDigits · 8 comments
PlasticDigits commented 2026-05-04 04:14:31 +00:00 (Migrated from gitlab.com)

Summary

Audit: M-01 in audits/audit_smartcontract_1777813071.md — TimeCurve.distributePrizes can set prizesDistributed = true while non-zero category slices for empty podiums (all address(0) winners) never leave PodiumPool, stranding CL8Y and blocking any retry.

Product decision (this issue): For any category slice that has no payable winners, the full slice for that category (after the existing 40/25/20/15 podium-pool split) must be forwarded to the protocol (canonical CL8Y protocol treasury / ops sink aligned with existing surplus routing), not left in PodiumPool.

Background / root cause

In contracts/src/TimeCurve.sol, distributePrizes:

  1. Reads prizePool = acceptedAsset.balanceOf(podiumPool).
  2. Computes integer slices sLast, sWar, sDef, sTime.
  3. Sets prizesDistributed = true before paying categories.
  4. Calls _payPodiumFrom per category.

_payPodiumFrom only calls podiumPool.payPodiumPayout when p.winners[i] != address(0) and the placement share is non-zero. If every winner slot for a category is zero, the entire category slice is skipped with no alternate destination.

contracts/src/sinks/PodiumPool.sol correctly rejects payPodiumPayout(..., winner = address(0), ...) — the bug is not in PodiumPool; it is the absence of a policy for “slice computed but no winners.”

Why this is realistic

Per the audit and game rules (see docs/product/primitives.md):

  • Defended streak only advances for buys in the under–15-minute window; a round can easily end with no qualifying streak podium.
  • Time booster and edge cases can yield no top-3 with non-zero effective timer credit.
  • WarBow / last-buy can also be empty in degenerate sales (e.g. very few buys), depending on _finalizeLastBuyers / ladder state.

So empty categories are not purely theoretical.

Impact

  • Reserve CL8Y can remain indefinitely in PodiumPool while prizesDistributed is true.
  • Winners in other categories do not automatically receive the stranded slice (participants get less than the documented prize pool unless fixed offchain).
  • No onchain retry of distributePrizes without upgrade or migration / privileged rescue.
  1. Define “unpaid category slice”
    For each category, after computing that category’s slice from prizePool, determine paidOut (sum of successful payPodiumPayout amounts for that category) vs slice. Any slice - paidOut (or, equivalently, slice when there are zero payable placements) is residual.

  2. Route residual to protocol
    Transfer acceptedAsset residual for that category from PodiumPool to the designated CL8Y protocol treasury (same economic bucket as other “surplus to protocol” paths — align with TimeCurveBuyRouter cl8yProtocolTreasury and deploy/registry expectations such as CL8YProtocolTreasury).

  3. PodiumPool API
    Today only payPodiumPayout(winner != 0) exists. Add a prizePusher-only path such as forwardResidual(IERC20 token, address to, uint256 amount) (names per repo conventions) with to != address(0), amount > 0, and an Emits event (e.g. PodiumResidualForwarded: token, recipient, amount, category uint8) so indexers and operators can reconcile flows.

  4. Set prizesDistributed only when the podium pool is fully resolved
    Either:

    • Option A (preferred): Set prizesDistributed = true only after all four category slices are fully allocated (winners + protocol residual), and assert acceptedAsset.balanceOf(podiumPool) == 0 (or document intentional dust tolerance if any rounding remains — ideally zero).
    • Option B: Keep ordering but never finalize until residuals are swept; if any external revert could occur, use checks-effects-interactions so a failed final sweep does not brick state (today the early flag is the bricking bug).
  5. UUPS / storage
    If the protocol sink is not yet on TimeCurve, add an initializer-safe storage slot or one-time setter (owner-gated, consistent with other wiring) for podiumResidualRecipient / reuse an existing registry read — must be non-zero before first distributePrizes that pays residuals, or hardcode the same sink as production FeeRouter topology documents.

Regression tests (Forge)

Add / extend tests in contracts/test/TimeCurve.t.sol (and split files if preferred) to cover:

Scenario Assertion
Empty defended streak, others filled After distributePrizes, podiumPool balance is 0 (or documented dust); protocol treasury balance increases by exactly the 20% slice (of pre-call pool) minus rounding; prizesDistributed == true.
Empty time booster Same for 15% remainder slice.
Empty WarBow slice Same for 25% slice (no BP podium).
Empty last-buy (possible with _totalBuys == 0 / edge setup) Same for 40% slice.
Multiple empty categories Sum of protocol receipts + winner payouts == initial prizePool (± 0 wei policy).
Partial podium (only 1st place set) Residual within category = slice minus paid placement shares; residual to protocol.
Existing: empty vault retryable Keep test_distributePrizes_empty_vault_is_retryable; ensure it still passes.
Existing: dust pool Keep test_distributePrizes_dust_pool_is_retryable behavior consistent with new semantics.
PodiumPool auth New forward path: only prizePusher (or legacy tests path if applicable); unauthorized reverts.

Fuzz is optional: random subset of categories forced empty via winner slots.

Files and surfaces likely to change

Solidity (authoritative)

Documentation

Indexer (if new events / tables)

Frontend (optional / policy display)

Audit artifact

Acceptance criteria

  1. No stranded reserves: After a successful distributePrizes when prizePool > 0 at entry, PodiumPool accepted-asset balance is 0 (unless an explicitly documented wei-level rounding exception is approved — default no strands).
  2. prizesDistributed semantics: The flag is set only when all category slices from that distribution pass have been fully assigned (winners and/or protocol per policy).
  3. Protocol routing: Any unpaid portion of a category slice is sent to the agreed protocol treasury address; no payPodiumPayout with winner == address(0).
  4. Events / ops: Emit a clear onchain event for protocol forwards (category + amount + token + recipient) so ops and indexers can reconcile.
  5. Tests: New Forge coverage for at least empty defended streak and empty time booster (audit-called), plus one multi-empty case; CI green.
  6. Docs / invariants: Product + operations + test-map docs describe the policy and point to tests.

Verification checklist

Engineering (pre-merge)

  • forge test passes for contracts/ including new tests.
  • Gas snapshot review if distributePrizes path changed materially.
  • Storage layout / upgrade diff reviewed for UUPS TimeCurve (if storage added).
  • PodiumPool forward path reviewed: only prizePusher, reentrancy not applicable if nonReentrant on distributePrizes and CEI respected.

Protocol / economics

  • Confirm protocol recipient matches production registry (CL8YProtocolTreasury or successor) and matches TimeCurveBuyRouter / ops runbooks.
  • Stakeholder signoff: empty-category funds are protocol revenue, not redistributed to other winners (this issue’s decision).

Indexer / integrations

  • If new events: decoder + persist + rollback list updated; integration test expects new variant if applicable.
  • If no new events (only existing PodiumPaid): document why protocol transfers are visible only via ERC20 Transfer from PodiumPool (may be insufficient for first-class indexing — prefer dedicated event).

Manual / Anvil (post-deploy pattern)

  • Run a sale to endSale with no defended-streak qualifying activity; fund PodiumPool; call distributePrizes; assert pool empty and treasury gained 20% slice of pool.
  • Repeat with time booster empty scenario if reproducible via scripted buys.
  • Confirm prizesDistributed prevents re-entry and no second call is required to “unstick” funds.

References: Internal audit Markdown audits/audit_smartcontract_1777813071.md § M-01; GitLab #70 / #55 context for distributePrizes gating and PodiumPool prizePusher.

## Summary **Audit:** [M-01 in `audits/audit_smartcontract_1777813071.md`](audits/audit_smartcontract_1777813071.md) — `TimeCurve.distributePrizes` can set `prizesDistributed = true` while **non-zero category slices** for **empty podiums** (all `address(0)` winners) never leave `PodiumPool`, stranding CL8Y and blocking any retry. **Product decision (this issue):** For any category slice that has **no payable winners**, the **full slice for that category** (after the existing 40/25/20/15 podium-pool split) must be **forwarded to the protocol** (canonical CL8Y protocol treasury / ops sink aligned with existing surplus routing), not left in `PodiumPool`. ## Background / root cause In [`contracts/src/TimeCurve.sol`](contracts/src/TimeCurve.sol), `distributePrizes`: 1. Reads `prizePool = acceptedAsset.balanceOf(podiumPool)`. 2. Computes integer slices `sLast`, `sWar`, `sDef`, `sTime`. 3. Sets **`prizesDistributed = true`** **before** paying categories. 4. Calls `_payPodiumFrom` per category. `_payPodiumFrom` only calls `podiumPool.payPodiumPayout` when **`p.winners[i] != address(0)`** and the placement share is non-zero. If **every** winner slot for a category is zero, **the entire category slice** is skipped with **no alternate destination**. [`contracts/src/sinks/PodiumPool.sol`](contracts/src/sinks/PodiumPool.sol) correctly rejects `payPodiumPayout(..., winner = address(0), ...)` — the bug is **not** in `PodiumPool`; it is the **absence of a policy** for “slice computed but no winners.” ### Why this is realistic Per the audit and game rules (see [`docs/product/primitives.md`](docs/product/primitives.md)): - **Defended streak** only advances for buys in the under–15-minute window; a round can easily end with **no** qualifying streak podium. - **Time booster** and edge cases can yield **no** top-3 with non-zero effective timer credit. - **WarBow / last-buy** can also be empty in degenerate sales (e.g. very few buys), depending on `_finalizeLastBuyers` / ladder state. So empty categories are **not** purely theoretical. ### Impact - **Reserve CL8Y** can remain **indefinitely** in `PodiumPool` while **`prizesDistributed`** is **true**. - Winners in **other** categories do **not** automatically receive the stranded slice (participants get **less** than the documented prize pool unless fixed offchain). - **No onchain retry** of `distributePrizes` without **upgrade** or **migration** / privileged rescue. ## Recommended fix (protocol routing for empty categories) 1. **Define “unpaid category slice”** For each category, after computing that category’s slice from `prizePool`, determine **`paidOut`** (sum of successful `payPodiumPayout` amounts for that category) vs **`slice`**. Any **`slice - paidOut`** (or, equivalently, **`slice`** when there are **zero** payable placements) is **residual**. 2. **Route residual to protocol** Transfer **`acceptedAsset`** residual for that category from **`PodiumPool`** to the **designated CL8Y protocol treasury** (same economic bucket as other “surplus to protocol” paths — align with [`TimeCurveBuyRouter`](contracts/src/TimeCurveBuyRouter.sol) **`cl8yProtocolTreasury`** and deploy/registry expectations such as [`CL8YProtocolTreasury`](contracts/src/sinks/CL8YProtocolTreasury.sol)). 3. **`PodiumPool` API** Today only **`payPodiumPayout(winner != 0)`** exists. Add a **`prizePusher`**-only path such as **`forwardResidual(IERC20 token, address to, uint256 amount)`** (names per repo conventions) with **`to != address(0)`**, **`amount > 0`**, and an **`Emits`** event (e.g. **`PodiumResidualForwarded`**: token, recipient, amount, category uint8) so indexers and operators can reconcile flows. 4. **Set `prizesDistributed` only when the podium pool is fully resolved** Either: - **Option A (preferred):** Set **`prizesDistributed = true`** only after **all four** category slices are **fully allocated** (winners + protocol residual), and assert **`acceptedAsset.balanceOf(podiumPool) == 0`** (or document intentional dust tolerance if any rounding remains — ideally **zero**). - **Option B:** Keep ordering but **never** finalize until residuals are swept; if any external revert could occur, use **checks-effects-interactions** so a failed final sweep does **not** brick state (today the early flag is the bricking bug). 5. **UUPS / storage** If the protocol sink is not yet on `TimeCurve`, add an **initializer-safe** storage slot or **one-time setter** (owner-gated, consistent with other wiring) for **`podiumResidualRecipient`** / reuse an existing registry read — must be **non-zero** before first `distributePrizes` that pays residuals, or hardcode the same sink as production **`FeeRouter`** topology documents. ## Regression tests (Forge) Add / extend tests in [`contracts/test/TimeCurve.t.sol`](contracts/test/TimeCurve.t.sol) (and split files if preferred) to cover: | Scenario | Assertion | |----------|-----------| | **Empty defended streak, others filled** | After `distributePrizes`, `podiumPool` balance is **0** (or documented dust); **protocol treasury** balance increases by **exactly** the **20%** slice (of pre-call pool) minus rounding; `prizesDistributed == true`. | | **Empty time booster** | Same for **15%** remainder slice. | | **Empty WarBow slice** | Same for **25%** slice (no BP podium). | | **Empty last-buy** (possible with `_totalBuys == 0` / edge setup) | Same for **40%** slice. | | **Multiple empty categories** | Sum of protocol receipts + winner payouts == initial `prizePool` (± **0** wei policy). | | **Partial podium** (only 1st place set) | Residual within category = slice minus paid placement shares; **residual** to protocol. | | **Existing: empty vault retryable** | Keep [`test_distributePrizes_empty_vault_is_retryable`](contracts/test/TimeCurve.t.sol); ensure it still passes. | | **Existing: dust pool** | Keep [`test_distributePrizes_dust_pool_is_retryable`](contracts/test/TimeCurve.t.sol) behavior consistent with new semantics. | | **`PodiumPool` auth** | New forward path: only **`prizePusher`** (or legacy tests path if applicable); unauthorized reverts. | Fuzz is optional: random subset of categories forced empty via winner slots. ## Files and surfaces likely to change ### Solidity (authoritative) - [`contracts/src/TimeCurve.sol`](contracts/src/TimeCurve.sol) — `distributePrizes`, `_payPodiumFrom` or successor; ordering of **`prizesDistributed`**; protocol recipient wiring. - [`contracts/src/sinks/PodiumPool.sol`](contracts/src/sinks/PodiumPool.sol) — residual forward helper + event. - [`contracts/test/TimeCurve.t.sol`](contracts/test/TimeCurve.t.sol) — new cases; possibly [`contracts/test/FeeSinks.t.sol`](contracts/test/FeeSinks.t.sol) for `PodiumPool` behavior. - [`contracts/script/UUPSDeployLib.sol`](contracts/script/UUPSDeployLib.sol) / [`contracts/script/DeployDev.s.sol`](contracts/script/DeployDev.s.sol) — if initializer or post-deploy wire must set protocol recipient. - Any other deployment scripts that set `prizePusher` / registry JSON. ### Documentation - [`docs/product/primitives.md`](docs/product/primitives.md) — podium pool: **empty category → protocol**. - [`docs/onchain/fee-routing-and-governance.md`](docs/onchain/fee-routing-and-governance.md) — cross-link residual policy. - [`docs/onchain/cl8y-flow-audit.md`](docs/onchain/cl8y-flow-audit.md) — add row for **M-01 / empty podium residual**. - [`docs/operations/final-signoff-and-value-movement.md`](docs/operations/final-signoff-and-value-movement.md) — `distributePrizes` now drains pool including protocol sweeps. - [`docs/testing/invariants-and-business-logic.md`](docs/testing/invariants-and-business-logic.md) — new invariant + test map rows. - [`docs/qa/YO-TimeCurve-QA-Checklist.md`](docs/qa/YO-TimeCurve-QA-Checklist.md) — manual check for stranded pool = **0** after distribution. ### Indexer (if new events / tables) - [`indexer/src/decoder.rs`](indexer/src/decoder.rs), [`indexer/src/persist.rs`](indexer/src/persist.rs), migrations under [`indexer/migrations/`](indexer/migrations/), [`indexer/src/reorg.rs`](indexer/src/reorg.rs) if new tables — follow [**INV-INDEXER-112**](docs/testing/invariants-and-business-logic.md). ### Frontend (optional / policy display) - If UX surfaces “prize pool” accounting: [`frontend/src/lib/timeCurvePodiumMath.ts`](frontend/src/lib/timeCurvePodiumMath.ts), TimeCurve protocol pages — only if copy must state **unallocated slice → protocol**. ### Audit artifact - [`audits/audit_smartcontract_1777813071.md`](audits/audit_smartcontract_1777813071.md) — add **status** line for M-01 when resolved (optional footnote in same file per team practice). ## Acceptance criteria 1. **No stranded reserves:** After a successful `distributePrizes` when `prizePool > 0` at entry, **`PodiumPool`** **accepted-asset** balance is **0** (unless an explicitly documented wei-level rounding exception is approved — default **no** strands). 2. **`prizesDistributed` semantics:** The flag is set **only** when **all** category slices from that distribution pass have been **fully assigned** (winners and/or **protocol** per policy). 3. **Protocol routing:** Any **unpaid** portion of a category slice is sent to the **agreed protocol treasury** address; **no** `payPodiumPayout` with **`winner == address(0)`**. 4. **Events / ops:** Emit a clear **onchain** event for protocol forwards (category + amount + token + recipient) so ops and **indexers** can reconcile. 5. **Tests:** New Forge coverage for **at least** empty **defended streak** and empty **time booster** (audit-called), plus one **multi-empty** case; CI green. 6. **Docs / invariants:** Product + operations + test-map docs describe the policy and point to tests. ## Verification checklist ### Engineering (pre-merge) - [ ] `forge test` passes for `contracts/` including new tests. - [ ] Gas snapshot review if `distributePrizes` path changed materially. - [ ] Storage layout / upgrade diff reviewed for **UUPS** `TimeCurve` (if storage added). - [ ] `PodiumPool` forward path reviewed: **only** `prizePusher`, reentrancy not applicable if **nonReentrant** on `distributePrizes` and **CEI** respected. ### Protocol / economics - [ ] Confirm **protocol recipient** matches production registry (**`CL8YProtocolTreasury`** or successor) and matches **`TimeCurveBuyRouter`** / ops runbooks. - [ ] Stakeholder signoff: empty-category funds are **protocol revenue**, not **redistributed** to other winners (this issue’s decision). ### Indexer / integrations - [ ] If new events: decoder + persist + **rollback** list updated; integration test expects new variant if applicable. - [ ] If no new events (only existing `PodiumPaid`): document why protocol transfers are visible only via **ERC20 Transfer** from `PodiumPool` (may be **insufficient** for first-class indexing — prefer dedicated event). ### Manual / Anvil (post-deploy pattern) - [ ] Run a sale to `endSale` with **no** defended-streak qualifying activity; fund `PodiumPool`; call `distributePrizes`; assert **pool empty** and **treasury** gained **20%** slice of pool. - [ ] Repeat with **time booster** empty scenario if reproducible via scripted buys. - [ ] Confirm **`prizesDistributed`** prevents re-entry and **no** second call is required to “unstick” funds. --- **References:** Internal audit Markdown `audits/audit_smartcontract_1777813071.md` § M-01; GitLab #70 / #55 context for `distributePrizes` gating and `PodiumPool` `prizePusher`.
PlasticDigits commented 2026-05-04 05:33:03 +00:00 (Migrated from gitlab.com)

mentioned in commit 6144dd30d2

mentioned in commit 6144dd30d23ad24edb249681c3c5f0bf113340d7
PlasticDigits commented 2026-05-04 05:40:46 +00:00 (Migrated from gitlab.com)

mentioned in commit 68dda7f083

mentioned in commit 68dda7f0835cf8e6888a599c13b46a03e91a4305
Brouie commented 2026-05-04 06:14:50 +00:00 (Migrated from gitlab.com)

verified on commits 6144dd3 (feat: ERC20 ingress balance deltas + podium residual hardening) + 0ca8907 (test: full conservation check in empty time booster).

19 podium/prize tests pass clean covering all your acceptance criteria:

empty-category coverage (your AC #5):

  • test_distributePrizes_empty_defended_forwards_20pct_to_protocol — empty defended-streak slice routes to protocol
  • test_distributePrizes_empty_time_booster_forwards_time_slice_to_protocol — empty time booster slice routes to protocol
  • test_distributePrizes_multi_empty_categories_sum_to_protocol — multi-empty case sums correctly

PodiumPool no-stranding (your AC #1):

  • test_podiumPool_forwardPodiumResidual_happy_path — residual forwards cleanly
  • test_podiumPool_forwardPodiumResidual_prize_pusher_wins_over_distributor_role — role precedence preserved
  • test_podiumPool_forwardPodiumResidual_unauthorized_reverts — only authorized callers can forward
  • test_podiumPool_forwardPodiumResidual_zero_recipient_reverts — zero-address guard

distributePrizes semantics (your AC #2 + #3):

  • test_distributePrizes_reduces_vault_and_sets_flag — flag set only after full distribution
  • test_distributePrizes_reverts_when_residual_recipient_unset — config sanity
  • test_distributePrizes_partial_last_buy_forwards_unfilled_placements — last-buy partial coverage
  • test_distributePrizes_dust_pool_is_retryable + test_distributePrizes_empty_vault_is_retryable — retry semantics

new PodiumResidualForwarded event (or equivalent) wired to indexer migration 20260504143000_podium_pool_residual_forwarded per your AC #4. forge unit suite passes for this surface.

verified on commits `6144dd3` (feat: ERC20 ingress balance deltas + podium residual hardening) + `0ca8907` (test: full conservation check in empty time booster). 19 podium/prize tests pass clean covering all your acceptance criteria: **empty-category coverage (your AC #5):** - `test_distributePrizes_empty_defended_forwards_20pct_to_protocol` — empty defended-streak slice routes to protocol - `test_distributePrizes_empty_time_booster_forwards_time_slice_to_protocol` — empty time booster slice routes to protocol - `test_distributePrizes_multi_empty_categories_sum_to_protocol` — multi-empty case sums correctly **PodiumPool no-stranding (your AC #1):** - `test_podiumPool_forwardPodiumResidual_happy_path` — residual forwards cleanly - `test_podiumPool_forwardPodiumResidual_prize_pusher_wins_over_distributor_role` — role precedence preserved - `test_podiumPool_forwardPodiumResidual_unauthorized_reverts` — only authorized callers can forward - `test_podiumPool_forwardPodiumResidual_zero_recipient_reverts` — zero-address guard **distributePrizes semantics (your AC #2 + #3):** - `test_distributePrizes_reduces_vault_and_sets_flag` — flag set only after full distribution - `test_distributePrizes_reverts_when_residual_recipient_unset` — config sanity - `test_distributePrizes_partial_last_buy_forwards_unfilled_placements` — last-buy partial coverage - `test_distributePrizes_dust_pool_is_retryable` + `test_distributePrizes_empty_vault_is_retryable` — retry semantics new `PodiumResidualForwarded` event (or equivalent) wired to indexer migration `20260504143000_podium_pool_residual_forwarded` per your AC #4. forge unit suite passes for this surface.
Brouie commented 2026-05-04 06:48:31 +00:00 (Migrated from gitlab.com)

indexer surface for the new PodiumResidualForwarded event also verified end-to-end.

migration apply on fresh DB:

  • 20260504143000_podium_pool_residual_forwarded.up.sql applies clean, table idx_podium_pool_residual_forwarded created with category SMALLINT (tracks Last Buy / WarBow / Defended / Time Booster as the empty bucket source)
  • existing podium tables intact (idx_podium_pool_paid, idx_podium_pool_prize_pusher_set)

full plumbing wired:

  • decoder.rs:160 event PodiumResidualForwarded(address indexed token, address indexed recipient, uint256 amount, uint8 category) ABI + dispatch at line 891
  • persist.rs:519 INSERT into idx_podium_pool_residual_forwarded
  • reorg.rs:120 added to rollback list

closes your AC #4 ("emit a clear onchain event for protocol forwards (category + amount + token + recipient) so ops and indexers can reconcile") — fully indexed, queryable, and reorg-safe.

indexer surface for the new `PodiumResidualForwarded` event also verified end-to-end. **migration apply on fresh DB:** - `20260504143000_podium_pool_residual_forwarded.up.sql` applies clean, table `idx_podium_pool_residual_forwarded` created with `category` SMALLINT (tracks Last Buy / WarBow / Defended / Time Booster as the empty bucket source) - existing podium tables intact (`idx_podium_pool_paid`, `idx_podium_pool_prize_pusher_set`) **full plumbing wired:** - decoder.rs:160 `event PodiumResidualForwarded(address indexed token, address indexed recipient, uint256 amount, uint8 category)` ABI + dispatch at line 891 - persist.rs:519 INSERT into `idx_podium_pool_residual_forwarded` - reorg.rs:120 added to rollback list closes your AC #4 ("emit a clear onchain event for protocol forwards (category + amount + token + recipient) so ops and indexers can reconcile") — fully indexed, queryable, and reorg-safe.
Brouie commented 2026-05-04 07:24:20 +00:00 (Migrated from gitlab.com)

mentioned in issue #128

mentioned in issue #128
Brouie commented 2026-05-04 07:56:02 +00:00 (Migrated from gitlab.com)

mentioned in issue #129

mentioned in issue #129
Brouie commented 2026-05-04 08:14:03 +00:00 (Migrated from gitlab.com)

mentioned in issue #131

mentioned in issue #131
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-04 08:18:59 +00:00
Brouie commented 2026-05-04 08:44:27 +00:00 (Migrated from gitlab.com)

mentioned in issue #138

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