mainnet: reserve the sink address before it exists (P1) - #54
Merged
Merged
Conversation
Breaks the sink circularity for the mainnet deploy. PositionLocker's constructor takes launchpadTreasury and script 05 reads it from choice.buybackBurnSink (readAddress, deliberately not requireCode); the sink's BURN_TOKEN is an immutable SPROUT address; SPROUT is a launch on the new core, which needs the locker to graduate anything. Writing the CREATE3 prediction first is what cuts the loop, and script 09 already asserts deployed == prediction, so the reservation is self-checking when it is finally taken. BuybackBurnSink/1.5.0 -> 0x65Dc46Ee554A27bC790710f9fAee74B427c1C57D computeAddress against factory 0xa4753315... read off 1776 on 2026-09-13, with eth_getCode answering 0x at the time. The other four launchpad salts (settler 1.4.0, locker 1.3.0, guard hook 1.0.0, cranker 2.1.0) were checked unclaimed in the same pass and are NOT written here: script 05 and script 10 write those themselves, and a pre-filled entry would be adopted rather than deployed. The new thing this has to survive, which testnet never did: the SPROUT launch is being held back several weeks, so this address stays codeless while the pad is live and taking fees. It is also where those fees go - the pad core's FEE_TREASURY and the locker's launchpadTreasury both point here - because ERC20 balances live in the token's storage, so the sink inherits every wei the moment script 09 lands it. That makes the burn retroactive with no manual transfer and no promise, which is the whole point of the sink. Script 08 stopped calling that state "not in the book yet". A codeless entry was already skipped, correctly, but the message it printed was for an ABSENT entry and this one is present and deliberate - an invitation to helpfully fill in an address that is already there. Reserved and absent now read differently, and the reserved line prints the address so it can be checked against the prediction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
P1from the sprout.fun mainnet cutover plan. No transactions — this is a book entry and a log-line fix.The circularity this cuts
Testnet never hit it — its sink already existed, because SPROUT was launch 2 on the previous core. A fresh mainnet deployment hits it head on.
Broken by predicting the sink.
computeAddress(salt)is a pure function of the salt, so the address can be written into the book before the contract exists, and script 09 already assertsdeployed == sinkwhen it finally lands there — the reservation is self-checking.Read off 1776 against factory
0xa4753315...on 2026-09-13, witheth_getCodeanswering0xat the time. The other four launchpad salts (settler 1.4.0, locker 1.3.0, guard hook 1.0.0, cranker 2.1.0) were checked unclaimed in the same pass and are deliberately not written — scripts 05 and 10 write those themselves, and a pre-filled entry would be adopted rather than deployed.What is new, and why the note is long
The SPROUT launch is being held back several weeks (Dan, 2026-09-13), so the pad goes live and graduates onto Choice v2 with no burn token in existence. That means this address stays codeless while the pad is live and taking fees — and it is also where those fees go: the pad core's
FEE_TREASURYand the locker'slaunchpadTreasuryboth point here.That is deliberate and it is the good outcome: ERC20 balances live in the token's storage, so the sink inherits every wei the moment script 09 lands at this address. The burn becomes retroactive with no manual transfer and no promise.
🔴 It does promote the CREATE3 factory lockdown from cleanup to a real surface.
Create3.addressOf(salt)is not namespaced bymsg.senderand two hot keys stay whitelisted, so until the lockdown either key can place arbitrary code at this salt and take whatever has accrued. Lock down after script 05; one timelock cycle re-whitelists for 09/10.Script 08
_requireOwnedByalready skipped a codeless entry, correctly, but printed "is not in the book yet" — the message for an absent entry. This one is present and deliberate, and over a weeks-long reservation that wording is an invitation to helpfully fill in an address that is already there. Reserved and absent now read differently, and the reserved line prints the address so it can be checked against the prediction.Checks
forge buildclean;forge fmt --checkclean on the changed script._requireOwnedBystill skips (does not flag) a codeless entry, so the ownership verifier stays green.🤖 Generated with Claude Code