Repository navigation
Fix the coupon-closure review findings; keep zero-supply bonds closable - #278
Merged
Merged
Conversation
Contracts: remove withdrawFailedIssuance, the only path that could turn unsold manager-held units into paid holdings; reject a repeated holder in payCoupon (DuplicateHolder) so a duplicate cannot be paid twice while another holder is skipped; emit CouponPeriodPaid once per period so the projected payment count advances even when nobody receives cash; move the Period and PayoutTotals structs into the types block; seed the two dealer accounts on the TBD Nordea allowlist again in the TBD setup script. forge test: 25 suites, 395 passed, including an empty-holder closure of a bond bought back to zero supply. API: the coupon route accepts an empty holder set when the projected supply is zero, HoldersBody rejects duplicates, CouponPeriodPaid drives the coupon-paid transition and a COUPON_PERIOD_PAID history entry, an issued bond with remaining periods stays outstanding regardless of supply, the write-only redemptionComplete flag is dropped from the projected state, and /v1/health reports contracts.bondManagerCompatible from a GOV_RESERVE() probe. openapi.json regenerated; 248 jest tests. UI: the pay-coupon preview uses the contract's integer per-unit coupon in BigInt WNOK (42, not 42.5, at 425 bps) and compares the reserve as BigInt; the payout queue lists issued bonds that still owe periods so a bought-back bond can be closed. 125 vitest tests. Docs: a known-issue entry for the WNOK allowlist requirement on every holder, contract reference and NatSpec updates, and the plan folder docs/plans/coupon-closure-review-fixes/.
The ingestion loop only fetches block timestamps for a fixed set of event names. The new period event was not in that set, so its coupon-paid transition projected lastCouponPayment as 0 and the next due time as the coupon duration, which the fresh-sandbox run exposed. Add the event to the prefetch filter.
A full projection replay applies a batch's manager events before its token events, so IsinEnabled from the issuance block can arrive after later coupon-period events and reset lastCouponPayment to the issuance timestamp, which makes the next due time wrong until the next payment. The enabled transition now seeds the schedule only while no coupon has been reduced. Reducer test added; jest 249 green.
Phase 5 of docs/plans/coupon-closure-review-fixes/: health reports the BondManager as compatible; a bond bought back to zero supply was paid two periods with an empty holder set and closed with zero totals; a bond whose only allocation failed advanced its projected payment count on the interim period and closed by burning its 100 unsold units with the reserve untouched; a duplicate-holder body was rejected with a 400. A full projection replay projects the exact closing-block timestamps.
1 task done
Greaker
added a commit
that referenced
this pull request
Sep 14, 2026
Move docs/plans/coupon-closure-review-fixes/ to docs/plans/archive/ with its status set to Implemented (shipped via #278), tick its done criteria, close out progress.md, and move the documentation-index entry to the archive list.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
docs/plans/coupon-closure-review-fixes/). Contracts:withdrawFailedIssuanceremoved so unsold manager-held units can never become paid holdings;payCouponrejects a repeated holder (DuplicateHolder); a per-periodCouponPeriodPaidevent lets the projection advance the payment count even when nobody is paid; structs moved to the types block; the two dealer accounts are seeded on the TBD Nordea allowlist again (in08_TbdSetup.s.sol).HoldersBodyrejects duplicates and drops its redemption wording;CouponPeriodPaid→COUPON_PERIOD_PAIDhistory entry plus thecoupon-paidtransition; an issued bond with remaining coupon periods staysoutstandingregardless of supply; the write-onlyredemptionCompleteflag is dropped from the projected state (column retained);/v1/healthgainscontracts.bondManagerCompatiblefrom aGOV_RESERVE()probe and logs an error on skew.Test plan
cd contracts && forge fmt --check && forge build --sizes && bash ./check-verify-latest-mapping.sh && forge test— 25 suites, 395 tests (duplicate-with-omission, period event, empty-holder closure of a bought-back bond)cd services/nb-bond-api && npm run lint && npm run format:check && npm test && npm run build— 248 tests (composer status rows, duplicate body, health field)cd services/nb-ui && npm run format:check && npm run lint && npm test && npm run build— 125 tests (integer amounts, exact-reserve no warning, zero-supply bond queued)python3 scripts/verification/check-public-repo-hygiene.py && python3 scripts/verification/check-markdown-links.pybondManagerCompatible: true) — needs a sandbox reset