Add multi-chain support: Celo Mainnet (42220) alongside Base (8453)#2
Add multi-chain support: Celo Mainnet (42220) alongside Base (8453)#2devin-ai-integration[bot] wants to merge 1 commit into
Conversation
- Per-chain config map (contract, mint price, treasury, deploy block, native symbol) keyed by chainId; useActiveChainConfig resolves the active chain - wagmi: add Celo chain + transport (Base behaviour untouched) - All reads/writes (hooks, BuyModal, PlotModal, ProfileDrawer, Canvas) resolve the active chain's contract dynamically; writes pin to + auto-switch the active chain via wallet_switchEthereumChain/addEthereumChain - Canvas state isolation: clear plot/minted/image caches + scan cursor and optimistic overrides on chainId switch, refetch per network - Header NetworkSwitcher dropdown (Base square mark / Celo circle mark) - Coinbase Smart Wallet hidden on Celo; guide to MetaMask/Rabby/WalletConnect - NetworkGuard now warns only on unsupported networks - Celo mint fee = CELO equivalent of ~0.001 USD (configurable) Shipped optimizations untouched: bbox compression ladder, 9,500-block log scan chunk, optimistic canvas updates. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Devin Review found 4 potential issues.
⚠️ 1 issue in files not directly in the diff
⚠️ StatsDashboard uses stale Base-only IS_CONTRACT_CONFIGURED instead of active chain config (src/components/StatsDashboard.tsx:55)
StatsDashboard imports and checks IS_CONTRACT_CONFIGURED at line 55, which is derived solely from the Base contract address constant (src/lib/constants.ts:60-61). While useBoardStats() (line 35) correctly reads from the active chain via cfg.isConfigured, the "Contract address not configured" warning banner is gated on the Base-only IS_CONTRACT_CONFIGURED. This means: (1) if Base has a contract but Celo doesn't, the warning won't appear on Celo when it should; (2) if Base has no contract but Celo does, the warning incorrectly appears on Celo. Every other component in this PR was updated to use cfg.isConfigured from useActiveChainConfig(), making this an incomplete transformation.
There was a problem hiding this comment.
🟡 PlotModal hardcodes "ETH" labels instead of using cfg.nativeSymbol for multi-chain
The PlotModal component was updated to use cfg.contract for contract interactions (lines 48, 70, 82) and cfg is available from useActiveChainConfig() (line 21), but three user-facing price labels still hardcode "ETH". On Celo, the asking price (line 146), buy-now button (line 196), and escrowed offer display (line 209) all show "ETH" instead of "CELO". The BuyModal was correctly updated to use cfg.nativeSymbol (src/components/BuyModal.tsx:170,204), demonstrating the intended pattern that PlotModal missed.
(Refers to line 146)
Prompt for agents
PlotModal.tsx has three places that hardcode 'ETH' as the currency label. Since the component already has `cfg` from `useActiveChainConfig()` at line 21, replace all three occurrences:
1. Line 146: `{formatEther(plot.price)} ETH` → `{formatEther(plot.price)} {cfg.nativeSymbol}`
2. Line 196: `Buy Now · {plot ? formatEther(plot.price) : "0"} ETH` → `Buy Now · {plot ? formatEther(plot.price) : "0"} {cfg.nativeSymbol}`
3. Line 209: `Your active escrowed offer: {formatEther(myOffer)} ETH` → `Your active escrowed offer: {formatEther(myOffer)} {cfg.nativeSymbol}`
This matches the pattern already applied in BuyModal.tsx at lines 170 and 204.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
🟡 ProfileDrawer OwnedPlotRow hardcodes "ETH" for listing price on Celo
In the OwnedPlotRow component at line 508, the listed price badge shows Listed · {formatEther(plot.price)} ETH. The component already has cfg from useActiveChainConfig() (line 311) but doesn't use cfg.nativeSymbol for this label. On Celo, a plot listed for 0.5 CELO would display as "Listed · 0.5 ETH", misleading the user about the currency.
(Refers to line 508)
Was this helpful? React with 👍 or 👎 to provide feedback.
| export const CELO_CONTRACT_ADDRESS = (process.env | ||
| .NEXT_PUBLIC_CELO_CONTRACT_ADDRESS || | ||
| "0x71aad1110dfd8f60249cd45ce4fb05163b6f812b") as `0x${string}`; |
There was a problem hiding this comment.
🚩 Celo contract default address is non-zero "placeholder" — isConfigured will be true before deployment
At src/lib/constants.ts:85-87, the CELO_CONTRACT_ADDRESS defaults to 0x71aad1110dfd8f60249cd45ce4fb05163b6f812b (a non-zero address) with the comment "placeholder until a BaseBoard is deployed there". Because isNonZero() at line 161 returns true for this address, CELO_CONFIG.isConfigured is true. This means the app will attempt RPC calls to a possibly non-existent contract on Celo — all reads silently fail (caught by error handlers), but the UI won't show any "not configured" warning, and the stats dashboard will display 0 plots sold with no explanation. Consider either using the zero address as the default (so isConfigured is false until actually deployed) or adding a comment that this address is the real deployed contract.
Was this helpful? React with 👍 or 👎 to provide feedback.
Runtime test results — all 4 PASSED ✅Tested locally against the multi-chain production config ( T3 — Switch Celo→Base + state isolation (the core feature)
T1 — Coinbase Smart Wallet hidden on CeloOn Celo, only "MetaMask / Rabby / Browser Wallet" + the hint "Coinbase Smart Wallet isn't supported on Celo…" are shown; no Coinbase button anywhere. T2 — NetworkSwitcher brand logosDropdown lists Base (blue square mark) and Celo (yellow circle mark); active chain highlighted. T4 (regression) — Base path untouchedOn Base, disconnected shows the OnchainKit Coinbase "Connect Wallet" button. Base behaviour unchanged. Notes: Celo contract is still the placeholder Full report + screen recording are in the Devin session: https://app.devin.ai/sessions/dd0d6a8816204b759805887b2ff9e351 |
Summary
Makes BaseBoard multi-chain: Celo Mainnet (42220) now runs concurrently with Base Mainnet (8453). Every read/write resolves its contract address, mint price, treasury, native symbol and log-scan deploy block from the active chain, and switching networks fully isolates the board state. All shipped optimizations are untouched — the bbox compression ladder (
src/lib/image.ts), the 9,500-blocketh_getLogsscan chunk (LOG_CHUNK = 9_500), and the optimistic canvas updates are byte-for-byte intact. Base behaviour is unchanged (its config mirrors the original constants exactly).Core: per-chain config + active-chain resolution
New
ChainConfigmap insrc/lib/constants.ts, keyed by chainId, plus auseActiveChainConfig()hook (src/hooks/useActiveContract.ts) that everything reads from:wagmi.ts:chains: [base, celo]+ acelotransport (was[base]).useBaseBoard.ts,BuyModal,PlotModal,ProfileDrawer,BaseBoardCanvas) now usecfg.contract/cfg.isConfigured/cfg.deployBlockinstead of the staticbaseBoardAddress/IS_CONTRACT_CONFIGURED/BASEBOARD_DEPLOY_BLOCK.useBaseBoardWrite) pin to the active chain and, if the wallet is on the wrong network, firewallet_switchEthereumChain(falling back towallet_addEthereumChainwith that chain's RPC/explorer/native currency) before sending — generalised from the old Base-only switch.CELO_PLOT_PRICE, configurable via env), mirroring the Base fee model; BuyModal totals now usecfg.plotPriceWei+cfg.nativeSymbol.State isolation on network switch
BaseBoardCanvaswatcheschainId; on change it clearsplotMapRef,allMintedIdsRef, thelastScanBlockRefcursor, the decoded-image cache and optimistic overrides, then the existing load effects (rebuilt from the new chain's contract) repopulate. No Base data ever leaks onto Celo or vice-versa.Header NetworkSwitcher + wallet guard
NetworkSwitcherdropdown (next to the wallet button) shows the active chain's logo and lists Base + Celo; selecting one calls wagmiswitchChain(→wallet_switchEthereumChain). Crisp inline SVG marks inChainLogos.tsx: modern square Base mark (white disc w/ flat right edge on #0052FF) and circular Celo mark.WalletConnecthides Coinbase (Smart Wallet) on Celo and steers users to MetaMask / Rabby / WalletConnect; Base keeps the OnchainKit Coinbase-first flow.NetworkGuardnow warns only on unsupported networks (anything other than 8453/42220), offering a one-click switch to Base.Notes / placeholders
0x71aad…812bis a placeholder (no BaseBoard deployed there yet) — Celo reads return empty until a contract is deployed; the app won't crash.TREASURY = 0x71aad…812b); the frontendtreasuryconstant is for parity/display..env.example:NEXT_PUBLIC_CELO_CONTRACT_ADDRESS,NEXT_PUBLIC_CELO_TREASURY_ADDRESS,NEXT_PUBLIC_CELO_PLOT_PRICE,NEXT_PUBLIC_CELO_DEPLOY_BLOCK.npm run buildandnpm run lintboth pass with zero errors/warnings.Link to Devin session: https://app.devin.ai/sessions/dd0d6a8816204b759805887b2ff9e351
Requested by: @omiaydin1