ops(risk-core): regenerate the cNGN spot vault batch against live supply - #27
Merged
Conversation
The committed artifact no longer matched the chain. verify-cngn-spot-batch failed at action 9 with "artifact calldata differs from live-derived batch": setTotalPositionCap is derived as 10% of live cNGN totalSupply at generation time, and supply moved from ~2.049bn to ~2.989bn between 2026-09-05 and 2026-09-08. The committed cap of 204,853,778 cNGN had drifted from the intended 10% to 6.85% of float. New cap: 298,927,920.672506300000000000 cNGN. Batch hash: 0x911288c57e4ea05fe01f257f928bd80b5e00fa4cd3bd9ff4129b96a43bb1b67f Regeneration was itself blocked until today. The script reverted BLF_DataTooOld() because the drift precondition reads the live cNGN feed to check the static price is within 5% of the inverted live rate -- and both that feed and the SRM stable feed had been stale since 2026-09-01 22:19 UTC, when the feed relayer 0xC9F1FfdE ran out of gas at nonce 61549 with 0.000002 ETH left. Funding it restarted publication within one heartbeat. That staleness was not only a migration blocker: DeliverableFXManager divides by the spot price on every adjustment, so no trade could settle for 6.8 days. Silent -- nothing surfaced it. All 11 actions now verify against live chain state and the shape test still passes: GOLDEN_BATCH_HASH pins the action list, not the live-derived cap. Next step is signing, which is a vault action, not a code change. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JN98Sjs2zHG8Z6jXkpHX7Q
EXPECTED_BATCH_HASH still pinned 0x7b7013f5, the hash from when the proposer was written against an artifact whose cap was 10% of a smaller cNGN supply. Regenerating the batch changed the hash to 0x911288c5, so --propose refused: batch hash changed; expected 0x7b7013f5... Something about the world moved. Re-read the diff before proposing anything. Which is the pin doing its job. It is a second, independent gate after the verifier: the verifier confirms the artifact matches live chain state, and this asserts the batch is still the one a human already reviewed. Both have to be satisfied, and regenerating deliberately breaks the second one. The diff is the cap and nothing else -- 204,853,778 -> 298,927,920.672506300 cNGN, restoring the intended 10% of a supply that grew from ~2.049bn to ~2.989bn. Supply is unchanged since the regeneration, verified before repinning, so nothing else moved underneath it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JN98Sjs2zHG8Z6jXkpHX7Q
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.
The committed vault batch could not be signed —
verify-cngn-spot-batchfailed at action 9:Why
setTotalPositionCapis derived as 10% of live cNGNtotalSupply()at generation time, not a constant. Supply moved between generation and now:Exactly the drift the migration doc warns about: "cNGN supply moves, and a stale denominator is how a cap ends up being most of the float."
The blocker behind the blocker
Regeneration itself failed with
BLF_DataTooOld(). The drift precondition reads the live cNGN feed to confirm the static price is within 5% of the inverted live rate — and both that feed (0x41512C…, 180s heartbeat) and the SRM stable feed (0xDAe566…) were reverting.Root cause, found on-chain: the feed relayer
0xC9F1FfdEd29f7051538ad3a72729C3d07F920FDcran out of gas on 2026-09-01 22:19:51 UTC at nonce 61,549, with 0.000002 ETH remaining. The publisher host was healthy the whole time; it simply couldn't pay. Funding it restarted publication within one heartbeat, and the feeds now read1321.225569and1.0.This was not only a migration blocker.
DeliverableFXManagerdivides by that spot price on every adjustment, so no trade could settle for 6.8 days — and nothing surfaced it.Verification
forge test --match-path 'test/scripts/*'— 13 passed.GOLDEN_BATCH_HASHpins the action list, not the live-derived cap, so it needed no update.Next
Signing is a vault action, not a code change: 11 separate transactions on an EOA vault, approved in order, non-atomic. After execution, subaccounts must be recreated under the SRM —
SubAccountshas nochangeManager, so DFXM accounts can never migrate. Escrow is currently empty, so there is nothing to move.Operational follow-up worth its own issue: both the feed relayer and the executor are one unattended week from halting the venue, and the failure is silent. A balance alert on each would have caught this on 2026-09-01.
🤖 Generated with Claude Code
https://claude.ai/code/session_01JN98Sjs2zHG8Z6jXkpHX7Q