Add Celo CIP-64 acceptance test (sysgo)#440
Draft
palango wants to merge 18 commits into
Draft
Conversation
Adds a per-chain DeployCeloContracts intent that, when set, etches the Celo predeploys into the L2 genesis allocs: CeloRegistry, GoldToken, FeeHandler, the FeeHandlerSellers, SortedOracles, AddressSortedLinkedListWithMedian, the testing FeeCurrency, and FeeCurrencyDirectory at the mainnet address 0x15F344...6276 expected by celo-org/op-geth's MainnetAddresses default. Also deploys a generic test fee currency (StableTokenV2) at the predeploy address previously labelled cUSD, registers devAccounts[0] as its oracle, reports a price, and registers it with the FeeCurrencyDirectory at 80k intrinsic gas so CIP-64 transactions can pay gas in it. Default behavior is unchanged. Wires the flag through ChainIntent and opcm.L2GenesisInput; adds an integration smoke test verifying the allocs.
Exposes the new DeployCeloContracts intent through the intentbuilder L2Configurator and a sysgo.WithCelo() deployer option. Tests can now spin up an in-process devnet with the Celo predeploys deployed and a test fee currency registered for CIP-64 coverage.
Two independent fixes are needed before CIP-64 (CeloDynamicFeeTxV2) transactions actually land in a block on a sysgo chain with WithCelo() deployed. First, the L1 rollup data fee. Even for fee-currency txs, op-geth's txpool charges the rollup data cost against the sender's native balance. On the Celo test setup the sender has zero native, so every tx is rejected with "insufficient funds". This mirrors what celo-mainnet does in production: when DeployCeloContracts is set we hand both GasPriceOracleBaseFeeScalar and BlobBaseFeeScalar to 0 so the chain operator pays all DA cost. The zero-scalar guard in DeployOPChain.s.sol is relaxed to allow this. Second, op-geth's MultiGasPool. The miner uses a per-fee-currency gas budget when building blocks. miner.DefaultConfig sets FeeCurrencyDefault=0.5 (50% of the block gas limit available to any one fee currency), but op-e2e/e2eutils/geth's L2 init constructs miner.Config as a literal that omits the field, so it defaults to 0 and every CIP-64 tx is silently popped during block building. Set FeeCurrencyDefault to the celo default and provide an explicit 0.9 entry for the test fee currency, mirroring celo-mainnet's cUSD/USDT/USDC config.
Adds tests/celo/ with a single TestCIP64_PayGasInTestFeeCurrency that spins up a sysgo chain via sysgo.WithCelo(), sends a CeloDynamicFeeTxV2 (CIP-64) signed by foundry's devAccounts[0] (pre-funded with 100k TEST and zero native CELO), waits for inclusion, and asserts the sender's native balance did not move while the TEST balance dropped. The test gracefully skips on devnets without the FeeCurrencyDirectory predeploy. Registered as the celo gate in acceptance-tests.yaml; just celo runs it.
Calling SortedOracles.initialize gives the predeploy a non-zero owner (the script deployer) and a non-zero reportExpirySeconds. Previously the proxy was etched but never initialized, leaving owner = address(0) and reportExpirySeconds = 0, which caused isOldestReportExpired() to treat every report as expired and forced deployTestFeeCurrency to prank as address(0) to satisfy the onlyOwner gate on addOracle.
The previous fix put FeeCurrencyDefault and a hardcoded cUSD entry
into the miner.Config{} literal in op-e2e/e2eutils/geth.InitL2, which
applies to every L2 EL the test suite spins up. That polluted the
generic L2 init code with Celo-specific defaults.
Move both settings to op-devstack/sysgo/l2_el_opgeth.go, gated on the
L2 genesis actually containing code at the FeeCurrencyDirectory
predeploy address. Only chains booted with sysgo.WithCelo() now
configure the MultiGasPool, and InitL2 returns to being Celo-agnostic.
…loContracts Restore the require()s on basefeeScalar and blobBaseFeeScalar in DeployOPChain.s.sol's checkInput, gated on a new deployCeloContracts field on the Types.DeployOPChainInput struct. Non-Celo chains regain the safety net against accidentally deploying with zero scalars while Celo chains continue to bypass it (where the operator pays all DA cost). The flag is propagated from the chain intent through opcm.DeployOPChainInput into the forge struct, mirroring the existing UseCustomGasToken plumbing.
…eeCurrencyDirectory + TestFeeCurrency Adds FeeCurrencyDirectory and TestFeeCurrency to op-core/predeploys alongside the other Celo predeploys, with matching *Addr vars and CeloPredeploys map entries. Removes the duplicate hardcoded addresses from sysgo/l2_el_opgeth.go and op-acceptance-tests/tests/celo/helpers.go, and references the predeploys package directly from cip64_test.go. Now there's one canonical Go source for these addresses; the Solidity constants in src/celo/CeloPredeploys.sol remain the matching on-chain source.
Replace inlined hex addresses with the existing predeploys.*Addr constants (now including the FeeCurrencyDirectory and TestFeeCurrency entries added in the previous commit). A typo in any of these literals would have produced a silent false-positive — the constants close that gap.
The same helper exists in both pipeline/opchain.go and state/deploy_config.go. They live in different packages and can't share the symbol, but the names should match for grep-ability and to make the duplication explicit. Aligns the comment text too.
L2Genesis already uses vm.prank for single-call prank scopes elsewhere (e.g., activateEcotone). Bring the four startPrank/stopPrank pairs in setFeeCurrencyDirectory and deployTestFeeCurrency in line.
The L2Configurator interface method took a bool, but the only caller (sysgo.WithCelo) always passed true. Calling with false was a no-op and confusing. Make the method nullary and unconditionally enable the flag, matching how WithCustomGasToken signals enablement via its presence.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 5eeab6b6b5
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
…inInput literals The new DeployCeloContracts field on opcm.L2GenesisInput / DeployOPChainInput broke two struct literals that exhaustruct flagged in CI: the L2 setup harness in test/setup/Setup.sol and the interop genesis path in op-chain-ops/interopgen. Setup.sol pipes the new field from DeployConfig (which already exposes it); interopgen never deploys Celo contracts so it's hardwired to false.
…ndry.toml CI runs forge build --deny-warnings --skip test (no --libraries) and produced artifacts whose bytecode still had link placeholders, which then prevented op-deployer from loading the L2Genesis script. Pin the library address in foundry.toml so every forge build invocation links it automatically; drop the now-redundant flag from the build-no-tests recipe.
…on-Celo devnets eth_call against an address with no code returns empty bytes without an RPC error, so the previous guard didn't trip on chains where the FeeCurrencyDirectory predeploy is missing — the test would fall through and fail on unrelated ABI decoding instead. Skip when eth_getCode at the FeeCurrencyDirectory address returns empty, and when getCurrencyConfig(testFeeCurrency) reports a zero oracle (the default for unregistered tokens; the call returns a zero-valued struct rather than reverting). Caught by Codex on PR #440.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## celo-rebase-17 #440 +/- ##
================================================
- Coverage 76.2% 76.2% -0.1%
================================================
Files 591 591
Lines 74215 74215
================================================
- Hits 56608 56585 -23
- Misses 17463 17486 +23
Partials 144 144
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
…ge-build recipe Putting the library address in foundry.toml's libraries array also writes it into artifact metadata in a format that breaks Go-side artifact loaders (json: cannot unmarshal string into map[string]string). Set the link via the --libraries CLI flag in the central forge-build justfile recipe instead, so every CI invocation that goes through `just forge-build` picks it up without affecting metadata.
forge test caught two more constructor mismatches: DeployOPChain.t.sol's Types.DeployOPChainInput literal and L2Genesis.t.sol's L2Genesis.Input literal. Both default the new field to false (these tests don't exercise the Celo predeploy path).
The existing memory-all gateless job already discovers and runs the celo gate alongside everything else, but adding a dedicated CI job makes celo failures visible independently and lets us iterate on the gate without sifting through the larger memory-all results.
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.
TL;DR
Adds a
sysgo.WithCelo()deployer option that etches the Celo predeploys into the L2 genesis, plus a singleop-acceptance-tests/tests/celo/cip64_test.gothat signs aCeloDynamicFeeTxV2from a TEST-funded EOA (no native CELO) and asserts it lands. Also fixes a handful of upstream OP-Stack assumptions that previously prevented CIP-64 txs from being included on a sysgo-built devnet.Run with
just celofromop-acceptance-tests/.What's in the diff
1. New
deployCeloContractsdeploy intentA per-chain
DeployCeloContracts boolonstate.ChainIntent. When set, theL2Genesisfoundry script's newsetCeloPredeploysetches:CeloRegistry,GoldToken,FeeHandler,MentoFeeHandlerSeller,UniswapFeeHandlerSeller,SortedOracles(initialized with a 1-hour report expiry soowner()is non-zero),AddressSortedLinkedListWithMedian(library), the testingFeeCurrency, andFeeCurrencyDirectoryat the celo-mainnet address0x15F344…6276that op-geth'sMainnetAddressesdefaults to.StableTokenV2namedTEST) at0x765DE816…1282a, registered with the FCD at 80 000 intrinsic gas, with 100k TEST minted todevAccounts[0].2.
sysgo.WithCelo()optionPlumbs the flag through
intentbuilder→state.ChainIntent→opcm.L2GenesisInput→ forge script. Tests opt in with:3. Three upstream assumptions fixed so CIP-64 txs can land
These were all "OP Stack defaults that quietly drop CIP-64 txs" — diagnosed by instrumenting a local op-geth.
DeployCeloContractsis set we zeroBasefeeScalarandBlobBaseFeeScalarin the deployer (operator pays all DA cost). The zero-scalarrequire()inDeployOPChain.s.solis gated on the same flag so non-Celo chains keep the safety net.MultiGasPoolper-currency budget = 0: the miner uses a per-fee-currency gas budget.miner.DefaultConfig.FeeCurrencyDefaultis0.5, butop-e2e/e2eutils/geth.InitL2'sminer.Config{}literal omitted the field. CIP-64 txs were silently popped at block-building time. The fix lives inop-devstack/sysgo/l2_el_opgeth.go, gated on the L2 genesis containing FCD code, so it doesn't pollute generic L2 init.4. The acceptance test
op-acceptance-tests/tests/celo/cip64_test.go::TestCIP64_PayGasInTestFeeCurrency:CeloDynamicFeeTxV2with foundry'sdevAccounts[0](zero native, 100k TEST),FeeCurrency = TEST.Registered as the
celogate inacceptance-tests.yaml.Verification
go test ./op-acceptance-tests/tests/celo/...— passes (~3 s after warmup).go test -run TestApplyDeployCeloContracts ./op-deployer/pkg/deployer/integration_test/...— passes; checks the predeploys actually end up in the L2 allocs.go test -run TestApplyGenesisStrategy ./op-deployer/pkg/deployer/integration_test/...— passes; confirms no regression on the non-Celo path.Test plan
celogate.Out of scope / follow-ups
WithCustomGasToken+WithCelo()interaction (Celo's scalar-zeroing currently overrides whatever scalar the user set).--librarieslink forAddressSortedLinkedListWithMedianis only onbuild-no-tests; the other forge build recipes don't carry it.params.CeloSepoliaChainID. No one hits this today.