Portal SDK: cutover for all non-Sonic containers - #85
Draft
apexearth wants to merge 2 commits into
Draft
Conversation
Cuts origin-squid over to squid-utils' `runPortal()` (@subsquid/evm-stream + @subsquid/batch-processor), which consumes the portal's real-time /stream instead of polling RPC for the chain head. Sonic has no real-time Portal dataset and stays on `run()`; `GATEWAY_CHAIN_IDS` makes that list env-driven so any container can be rolled back without a code change. No `main-*.ts` changes — every container goes through `initProcessorFromDump()`. - dumps.ts: the one cutover branch, plus wiring the portal cache in. - otoken-2.ts: delete the `addTrace` monkeypatch. It reached into the gateway processor's private `assertNotRunning()`/`add()`, which the portal builder does not have — a hard TypeError blocking oeth/ousd/oethb. It only existed to dodge the gateway's address lowercasing; evm-stream does none, and squid-utils now normalizes registrations itself, so the second checksummed `callTo` entry goes too. Trace now comes from squid-utils, not evm-processor. - portal-cache.ts: ported to portal-client 0.7 — finality moved to `batch.meta.finalizedHeadNumber`, `getStream` grew a third `finalized` arg, and the prototype patch became an instance wrap (the tree resolves more than one physical copy of the class, so a prototype patch can attach to one nobody instantiates). Algorithm, schema, zstd and stats are unchanged. Existing .portal-cache data is 100% invalidated by the query-shape change — clear it or use a fresh PORTAL_CACHE_DIR. - portal-client 0.7.0; the 0.3.2 pnpm patch is dropped, its TraceCreateResult fix is upstream. - traceFilter/oeth.ts: optional sighash guard and a processor type annotation. Does not compile until squid-utils publishes `runPortal`, `createPortalClient` and `SquidProcessor.portalClient` — 3 errors in dumps.ts until then. Validated against a local `file:` link (see AGENTS.md).
…2 switch 335a727 repointed the OUSD/USD oracle to the 18-decimal v2 without moving the height gate, and c73c1da added the decimal correction 40 minutes later. v2 has no code until block 25,427,435, so replaying any earlier range calls a codeless address and throws FunctionResultDecodeError on decode. Route by height instead: v1 (x10^10) below the switch, v2 above. The constant is production's actual switchover block, so a resync reproduces deployed history rather than introducing a pricing change. Verified against prod: wOUSD_USD at 25841364 already carries the v2 value.
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.
Depends on OriginProtocol/squid-utils#1 — this branch does not compile until squid-utils publishes (exactly 3 errors, all in
src/utils/dumps.ts:createPortalClient,runPortal,SquidProcessor.portalClient). Merge order is squid-utils release first.Scope doc: https://github.com/oplabs/off-chain/blob/main/notes/squid-portal-migration.md
What
src/utils/dumps.ts—GATEWAY_CHAIN_IDS(default: Sonic) selectsrun()vsrunPortal(). Nomain-*.tsis touched, because none of them callrun()directly; they all go throughinitProcessorFromDump(). Per-container rollback is an env var, not a code change.addTracemonkeypatch intemplates/otoken/otoken-2.ts(−31 lines). It reached into the gateway processor's privateassertNotRunning()/add()and wouldTypeErrorat setup on the portal path, blocking oeth/ousd/oethb. It existed only to dodge the gateway's address lowercasing; evm-stream doesn't lowercase, socallTocollapses to the single lowercase address and thegetAddressimport goes away.polyfills/portal-cache.ts—batch.meta.finalizedHeadNumber(finality moved in 0.7.0), thefinalizedthird arg forwarded, andsetupPortalCache()→withPortalCache(client, stateSchema)which mutates one instance's owngetStreaminstead of patchingPortalClient.prototype. That kills a real hazard: the tree resolves multiple physically distinctportal-clientcopies, so a prototype patch can attach to a class nobody instantiates and silently cache nothing. Algorithm, SQLite schema, zstd and stats are unchanged.sighashguard (utils/traceFilter.ts),EvmBatchProcessorannotation (oeth/processors/oeth.ts),Traceimported from squid-utils.@subsquid/portal-client→^0.7.0;patches/@subsquid__portal-client@0.3.2.patchand itspnpm-workspace.yamlentry deleted — the fix is upstream in 0.7.0 and the patch no longer applies.AGENTS.mddocumentsGATEWAY_CHAIN_IDSand the localfile:link/revert procedure.Validation — local Postgres, four containers
sqd:batch-processor)sqd:processor)4,981 rows across 40 tables. Zero error-level lines and zero
block not foundacross all four.Caching — the thing most at risk, and it works
PORTAL_CACHE, oethb over base 50,400,000–50,600,000, fresh cache dir:saved=2 chunks / 200,001 blocks / 9.93 MB, sqlite 0 → 10.2 MBhit_rate=100.0%,replayed 2 chunk(s)Row parity cold vs warm identical — 3126 / 1569 / 1737 across
o_token_history/erc20_transfer/o_token_activity, both ending at height 50600000.RPC_CACHEneeded zero edits: same pair, coldeth_getCode total=578 miss=434 hit=0.0%, warmtotal=2192 hit=100.0%..portal-cachefiles are invalidated by this change — the two SDK generations build different query JSON and the cache key is a SHA of it. Old rows aren't corrupt, just unreachable. Wipe and rebuild once, thenpnpm cache:backup/cache:restoreso one person pays for it.Decisions worth a reviewer's eye
PORTAL_CACHE. The gatewaysetPortal()takes a URL, not a client, so there's no instance to wrap — and after the 0.7.0 bump, origin-squid'sPortalClientis a different physical copy from the prerelease's 0.3.2 anyway. Documented in the file header. Local-dev only; no prod impact.block not foundretry lives in squid-utils as its own revertable commit. The portal delivers blocks slightly before the RPC node has them, which the gateway path structurally cannot hit. Alternative is holding state reads back to a confirmed height.Validation note — read before reproducing
A mid-chain
BLOCK_FROMagainst an empty database is not a cold rebuild. Several origin-squid processors learn state by observing historic events, so skipping those blocks produces failures that look like contract or SDK bugs and are neither.Concretely: an early validation run started mainnet at
BLOCK_FROM=25877800, 534,849 blocks above the Ethena ARM upgrade at 25,342,951.Arm.upgradeBlockis learned at runtime from theBaseAssetAddedlog (origin-arm.ts:917), never hardcoded — so it stayed NULL,upgradedevaluated false, and the code called the now-deletedliquidityAmountInCooldown()and reverted. That is the correct consequence of skipping the upgrade event, not a defect. Production restores a dump and learnedupgradeBlock = 25342951correctly.Because a dropped log would have meant a genuine bug in this PR's filter translation, it was checked directly: processing 25,342,900–25,343,050 with no
Armrow present, portal and gateway both learnupgrade_block = 25342951, with identical row counts across all 105 tables and zero errors on either path. The{where, include}translation drops nothing.One pre-existing bug found, not introduced here
erc20duplicate-key on a cold mid-chain start.erc20-entry.tsanderc20-event.tsbothfind-then-insertthe sameERC20id within one batch, violatingPK_8d43ce15401ba044c55a72a8ceband aborting the transaction. Reproduces identically on the gateway path (GATEWAY_CHAIN_IDS=1), so it predates this work; prod is immune because it restores a dump. Fix is anupsert— out of scope here.🤖 Generated with Claude Code