This proposal upgrades the four core stCELO implementation contracts — Account, Manager, DefaultStrategy, and SpecificGroupStrategy — to improve how withdrawals are accounted for and distributed across validator groups, and to add a permissionless safeguard for re-routing a scheduled withdrawal when a group's available capacity changes. Storage layouts are unchanged and the upgrade is non-breaking for existing stCELO holders.
The four upgrades execute atomically in a single Celo Governance transaction routed through the StakedCelo MultiSig (0x78DaA21FcE4D30E74fF745Da3204764a0ad40179), which owns the proxies.
stCELO withdrawals are served from a mix of a group's revocable Election votes and the unlocked CELO backing its scheduled votes. The previous accounting measured a group's available capacity in a way that could include votes that are not currently realisable, making withdrawal distribution less precise than it should be. This upgrade makes capacity accounting reflect what can actually be delivered at withdrawal time, so withdrawals are spread across groups that can fulfil them and the experience is more predictable.
Account) — introduces getRealisableCeloForGroup, which counts only the CELO a withdrawal can actually draw at the moment: revocable Election votes plus the balance-backed portion of scheduled votes, net of amounts already earmarked. Withdrawal scheduling validates against this figure.DefaultStrategy, SpecificGroupStrategy) — withdrawal vote distribution caps each group by its realisable capacity and spreads across healthy groups instead of concentrating on the largest one, skipping groups that cannot currently contribute.Account.rescueScheduledWithdrawal) — if a group's realisable capacity falls below a beneficiary's scheduled amount, anyone can re-route that scheduled withdrawal onto groups that can fulfil it. Funds can only ever flow to the original beneficiary, so this cannot be used to redirect or seize anyone's CELO.Manager — threads the distribution context through to the strategies for the above.All four implementations are deployed and source-verified on Celoscan and Blockscout. Proxy addresses, ownership, and balances are untouched.
| Contract | Proxy (unchanged) | New implementation |
|---|---|---|
| Account | 0x4aAD04D41FD7fd495503731C5a2579e19054C432 |
0x3a81b5bd5c719cd57d9122d34a093119d61c8117 |
| DefaultStrategy | 0x3A3ed74B1cC543D5EB323f70ac2F19977a0eA088 |
0xac294abf757dd2890c65751dcc095152c27b3416 |
| SpecificGroupStrategy | 0xb88af6EAc9cd146D8b03b66708EF76beBD937871 |
0x9cbbb5b84dc3dec31daea36c53bdfe145cb40e9d |
| Manager | 0x0239b96D10a434a56CC9E09383077A0490cF9398 |
0xbc465bbf2e2d71c62eda1b24061916877f6ae15e |
A shared AddressSortedLinkedList library (0x1f2030b177ea2d1ef457e4311d4ace5a920381a1) is linked into the new bytecode; it is not a proxy and is not an upgrade target.
The four stCELO proxies are OpenZeppelin UUPS proxies whose upgradeTo(address) is onlyOwner. Their owner is the StakedCelo MultiSig (0x78DaA21FcE4D30E74fF745Da3204764a0ad40179), not Celo Governance — so a Governance proposal cannot call upgradeTo on the proxies directly. Instead, the MultiSig exposes a dedicated entrypoint for Celo Governance:
// staked-celo/contracts/common/MultiSig.sol
function governanceProposeAndExecute(
address[] calldata destinations,
uint256[] calldata values,
bytes[] calldata payloads
) external onlyGovernance {
for (uint256 i = 0; i < destinations.length; i++) {
bytes memory returnData = ExternalCall.execute(destinations[i], values[i], payloads[i]);
emit GovernanceTransactionExecuted(i, returnData);
}
}
onlyGovernance resolves the Celo Registry Governance entry, which is the Celo Governance proxy at 0xD533Ca259b330c7A88f74E000a3FaEa2d63B7972. When Celo Governance invokes governanceProposeAndExecute, the MultiSig becomes the msg.sender of each forwarded upgradeTo call — and since the MultiSig is each proxy's owner(), the onlyOwner check passes. This CGP therefore self-executes through Celo Governance; there is no separate multisig signer coordination and no multisig timelock on this path.
A single Celo Governance transaction:
StakedCelo MultiSig (0x78DaA21FcE4D30E74fF745Da3204764a0ad40179)governanceProposeAndExecute(address[] destinations, uint256[] values, bytes[] payloads)destinations[] — the four stCELO proxies, in order: Account, DefaultStrategy, SpecificGroupStrategy, Managervalues[] — four entries, each 0payloads[] — four ABI-encoded upgradeTo(address newImplementation) calldata blobs (selector 0x3659cfe6), each pairing a proxy with its new implementation in the same orderThe four upgrades apply atomically when the proposal executes.
See mainnet.json. It contains the single governanceProposeAndExecute transaction with the three array arguments fully expanded.
Key addresses:
0x78DaA21FcE4D30E74fF745Da3204764a0ad40179 — owner of all four proxies; exposes governanceProposeAndExecute(...) external onlyGovernance.0xD533Ca259b330c7A88f74E000a3FaEa2d63B7972 — the onlyGovernance caller, resolved from the Celo Registry Governance entry.Confirm each proxy is owned by the MultiSig:
cast call <proxy> "owner()(address)" --rpc-url https://forno.celo.org
# expect 0x78DaA21FcE4D30E74fF745Da3204764a0ad40179 for all four
Confirm each new implementation is deployed (non-empty bytecode):
cast code <newImplementation> --rpc-url https://forno.celo.org
Simulate the full proposal execution from Celo Governance (read-only eth_call, no state change). A non-reverting result confirms the authorization path and all four upgrades:
cast call 0x78DaA21FcE4D30E74fF745Da3204764a0ad40179 \
"governanceProposeAndExecute(address[],uint256[],bytes[])" \
"[0x4aAD04D41FD7fd495503731C5a2579e19054C432,0x3A3ed74B1cC543D5EB323f70ac2F19977a0eA088,0xb88af6EAc9cd146D8b03b66708EF76beBD937871,0x0239b96D10a434a56CC9E09383077A0490cF9398]" \
"[0,0,0,0]" \
"[0x3659cfe60000000000000000000000003a81b5bd5c719cd57d9122d34a093119d61c8117,0x3659cfe6000000000000000000000000ac294abf757dd2890c65751dcc095152c27b3416,0x3659cfe60000000000000000000000009cbbb5b84dc3dec31daea36c53bdfe145cb40e9d,0x3659cfe6000000000000000000000000bc465bbf2e2d71c62eda1b24061916877f6ae15e]" \
--from 0xD533Ca259b330c7A88f74E000a3FaEa2d63B7972 --rpc-url https://forno.celo.org
This simulation was run at authoring time and returned successfully (no revert). For contrast, a direct upgradeTo from Governance (bypassing the MultiSig) reverts with Ownable: caller is not the owner, confirming the MultiSig path is required.
Low. This proposal changes implementations only; proxy addresses, ownership, and all balances are untouched. Storage layout is unchanged and the change is backward-compatible for existing stCELO holders. The new rescueScheduledWithdrawal safeguard is permissionless but can only ever move funds to the original beneficiary; it cannot redirect or seize CELO.
contracts/Account.sol, contracts/Manager.sol, contracts/DefaultStrategy.sol, contracts/SpecificGroupStrategy.sol, contracts/common/MultiSig.sol