Skip to content

feat(UpgradeUSDat): route USDat upgrade through SaturnTimelock#3

Open
PierrickGT wants to merge 1 commit into
proto-963-migrate-usdat-code-to-pyusdxfrom
fix/add-timelock-support-to-upgrade-script
Open

feat(UpgradeUSDat): route USDat upgrade through SaturnTimelock#3
PierrickGT wants to merge 1 commit into
proto-963-migrate-usdat-code-to-pyusdxfrom
fix/add-timelock-support-to-upgrade-script

Conversation

@PierrickGT

Copy link
Copy Markdown
Member

The USDat ProxyAdmin was handed to SaturnTimelock at block 25,284,032, so broadcasting Upgrades.upgradeProxy as the owner EOA now reverts: ProxyAdmin.upgradeAndCall is onlyOwner and the owner is the timelock.

Replace the single-shot script with the real flow. ProposeUSDatUpgrade deploys the implementation and schedules the upgrade as the proposer; ExecuteUSDatUpgrade executes it once the 5-day delay matures, from any EOA since EXECUTOR_ROLE is held by address(0).

Both scripts build the payload via UpgradeUSDatBase so their operation ids match. Propose dry-runs the exact scheduled bytes as the timelock before broadcasting: scheduling only stores inert calldata, so a broken payload would otherwise surface after the full delay had already elapsed.

Drop the claimYield() pre-call. migrate() already calls stopEarning() and mints any surplus to the yield recipient, which is what absorbs the yield accruing across the delay.

Move the fork test to block 25,284,032, the first block under timelock ownership, and model schedule -> warp -> execute. The same handover moved the token's DEFAULT_ADMIN_ROLE to the timelock, so the proposer EOA no longer holds it.

The USDat ProxyAdmin was handed to SaturnTimelock at block 25,284,032, so
broadcasting Upgrades.upgradeProxy as the owner EOA now reverts:
ProxyAdmin.upgradeAndCall is onlyOwner and the owner is the timelock.

Replace the single-shot script with the real flow. ProposeUSDatUpgrade
deploys the implementation and schedules the upgrade as the proposer;
ExecuteUSDatUpgrade executes it once the 5-day delay matures, from any EOA
since EXECUTOR_ROLE is held by address(0).

Both scripts build the payload via UpgradeUSDatBase so their operation ids
match. Propose dry-runs the exact scheduled bytes as the timelock before
broadcasting: scheduling only stores inert calldata, so a broken payload
would otherwise surface after the full delay had already elapsed.

Drop the claimYield() pre-call. migrate() already calls stopEarning() and
mints any surplus to the yield recipient, which is what absorbs the yield
accruing across the delay.

Move the fork test to block 25,284,032, the first block under timelock
ownership, and model schedule -> warp -> execute. The same handover moved
the token's DEFAULT_ADMIN_ROLE to the timelock, so the proposer EOA no
longer holds it.

BREAKING CHANGE: `make upgrade-local` and `make upgrade-mainnet` are
replaced by `make propose-upgrade` and `make execute-upgrade`, and
PROXY_ADMIN_OWNER_PRIVATE_KEY by PROPOSER_PRIVATE_KEY plus
NEW_IMPLEMENTATION. See .env.example.
@PierrickGT
PierrickGT requested a review from toninorair July 16, 2026 11:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant