Repository navigation
chore: sync upstream contracts (2026-07-29) — AllBridgeFacet v2.2.0 + Tron init-calldata (EXSC-650) [AllBridgeFacet v2.2.0] - #27
Merged
Conversation
…nnel (lifinance#2116) * chore(multisig-rollout): proceed optimistically, don't pre-gate on tunnel (EXSC-677) * docs(multisig-rollout): name lifi-connect tunnel instead of retired VPN Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ance#2110) * feat(commands): add eip7702-atomic-batch skill + engine (EXSC-660) Sponsored EIP-7702 atomic execution from an EOA: the EOA delegates to the canonical Multicall3 and a sponsor pays for a single type-4 tx running aggregate3 in the EOA's context. Lets a gas-starved or compromised/swept key execute (e.g. transferOwnership) with no fundable balance for a sweeper to front-run. Engine: script/tasks/atomicBatch7702.ts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * feat(eip7702-atomic-batch): auto-undelegate by default + delegate-choice guidance (EXSC-660) Address the open-delegation risk: the engine now clears the authority's delegation after the batch (authorization -> zero address) unless --keep-delegation is set, so no open Multicall3 delegate is left drivable by anyone. Skill doc adds a 'Choosing the delegate' decision gate (Multicall3 vs a restricted custom executor) to raise with the user, hardcodes the canonical Multicall3 address, and clarifies that a leaked key stays compromised. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(eip7702-atomic-batch): tighten description + add self-batch example (EXSC-660) Compress the frontmatter description (move Multicall3 mechanics to the body, keep trigger phrases) and add an approve+deposit self-batch config example so the skill isn't over-associated with only the compromised-key rescue case. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(eip7702-atomic-batch): note auto-undelegate + --keep-delegation in engine header (EXSC-660) Self-review: the header docstring predated the auto-undelegate feature. Sync it with the actual behavior and flags. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(eip7702-atomic-batch): address CodeRabbit review — sim + undelegate correctness (EXSC-660) - Simulate the whole authorized aggregate3 as one call (sign auth first) instead of per-call against pre-batch state, so dependent batches (approve->deposit) validate correctly. (#2) - Undelegate on any MINED batch, not just success: a 7702 delegation applies before execution and isn't rolled back on revert. (#4) - Docs: drop the false 'idempotent to re-run' claim (state re-simulation only). (#1) - Docs: warn that a public-mempool --broadcast exposes the sponsor-unbound 7702 authorization to front-running — unsafe for funded/privileged authorities; require protected orderflow / restricted executor there. (#3) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(eip7702-atomic-batch): header wording — simulate the authorized batch, not per-call (EXSC-660) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(eip7702): derive isSelf from resolved addresses; self-batch undelegate needs executor:'self' Addresses @gvladika review on lifinance#2110. 1. Self-batch auto-undelegate used the wrong authorization nonce. In self-batch mode the authority also sends the clear tx, so per EIP-7702 the clearing authorization needs nonce txNonce+1 — viem applies that +1 only with executor:'self'. The batch auth already passed it; the clear auth did not, so it carried a stale (already-consumed) nonce, was silently ignored, and the delegation was never cleared — leaving a funded self-batch authority delegated to open Multicall3. Fix: mirror the batch call and pass executor:'self' when isSelf. 2. isSelf compared env-var *names*, not resolved keys. A distinct env var holding the same private key as the sponsor wrongly took the sponsored path (executor unset). Fix: resolve the authority account first, then derive isSelf from authority.address === sponsor.address. 3. Dead `else if (KEEP_DELEGATION)` branch simplified to `else` (QA-bot nit). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: drop accidentally-committed node_modules symlink A local node_modules symlink (created for typechecking) was staged by `git add -A` because .gitignore uses `node_modules/` (dir-only) which does not match a symlink. The broken symlink made CI's `bun install` fail with 'ENOENT: could not open the node_modules directory'. Not a code change. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
…ifinance#2115) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e#2100) * feat(EXSC-611): wire GasZip and Garden config for Robinhood Enable GasZipFacet/GasZipPeriphery and GardenFacet deployment on Robinhood by wiring the config the deploy scripts read (deployRequirements.json): - config/networks.json: robinhood gasZipChainId 0 -> 526 (GasZip ShortID) - config/gaszip.json: robinhood GasZipV2 router 0x9E22ebeC84c7e4C4bD6D4aE7FF6f4D436D6D8390 - config/garden.json: robinhood htlcRegistry 0xd8BF1D34AE05B6F631dE898aE9F8f91e08661B59 Both target addresses verified to have code on Robinhood (chainId 4663). Facet deployment + diamond registration + GasZipPeriphery whitelist follow as the production Safe/timelock rollout (diamond owner is the timelock). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(EXSC-611): deploy GasZip + Garden to Robinhood, propose diamond registration Deployed to Robinhood (chainId 4663) and proposed to the Safe (timelock scheduleBatch, each proposal carries the deployer signature only): - GasZipFacet v2.0.5 0x7FFE27a83a95bF814188C3244E06C8d497589287 diamondCut nonce 13 - GardenFacet v1.0.0 0xC9A3eaD2f8f3d6ED100CAb7A5375605bbeBceAAE diamondCut nonce 14 - GasZipPeriphery v1.0.2 0x9F80FDADcA03a6e062B13BAe1B5e5c0B2A166049 diamondUpdatePeriphery nonce 15 + allowlist sync nonce 16 whitelist.json adds GasZipPeriphery selectors depositToGasZipERC20 (0x8b71ae6c) and depositToGasZipNative (0xc4af5a74). Production diamond registries update only when the timelock ops execute after signing. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(EXSC-611): sync Robinhood diamond log after timelock execution GasZipFacet v2.0.5 + GardenFacet v1.0.0 registered and GasZipPeriphery recorded following on-chain execution of the queued timelock diamond cuts. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(garden): correct robinhood HTLC registry address Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(deploy): record corrected GardenFacet robinhood deployment Re-deployed GardenFacet with the corrected htlcRegistry (0x8470E471...) to 0x19498b49FB4AA541fd208E4520a9Dc14a8A9d88E. diamondCut proposed to the robinhood Safe (nonce 18) via timelock scheduleBatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(robinhood): sync diamond log after GardenFacet v1.0.0 cut (EXSC-611) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Goran Vladika <goran.vladika@gmail.com>
…676) (lifinance#2117) * ci(coverage): add weekly coverage report cron posting to Slack (EXSC-676) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(coverage): bump weekly coverage forge-coverage retries 3->10 (EXSC-676) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * ci(coverage): address CodeRabbit review on weekly coverage workflow (EXSC-676) - checkout: persist-credentials: false (no later step needs git creds) - fetch-rpcs: write quote-stripped value to GITHUB_ENV, not the raw line - compose step: bind github.* via env, no direct run: interpolation (CONV:ACTIONS-NO-INJECTION) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… flip (EXSC-656) (lifinance#2119) * docs(EXSC-656): design spec for evm/solc version model cleanup + cancun flip Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(EXSC-656): rename to targetEvmVersion, drop deployedWithSolcVersion, flip 39 chains to cancun * chore(EXSC-656): point bash deploy tooling at targetEvmVersion; drop dead getNetworkSolcVersion * chore(EXSC-656): point TS deploy tooling at targetEvmVersion; drop deployedWithSolcVersion * fix(EXSC-656): re-verify with the contract's recorded solc/evm/optimizer toolchain * chore(EXSC-656): log verify toolchain overrides in debug output * docs(EXSC-656): document targetEvmVersion and solc derivation; drop deployedWithSolcVersion * docs(EXSC-656): fix stale zkEVM solc-version claim in multi-network README * chore(EXSC-656): drop design spec from repo (keep local only) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * docs(EXSC-656): note targetEvmVersion "n/a" for zkEVM networks in add-network Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(agents): add SC feature-PR lifecycle rule (503) Documents the canonical stage order for smart contract feature PRs — peer review and BE integration BEFORE audit — plus how the CI automation (draft bot, audit check, core-dev approval, protected labels) maps to each stage. Agents repeatedly assumed the audit precedes peer review; this rule pins the real order where agents auto-load it. Also adds an always-loaded one-paragraph summary to .agents/context.md and refreshes the stale rule/command counts (25->28, 13->32). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * docs(agents): specify exact Olympix draft-revert conditions in 503 securityAlertsReview.yml force-drafts only on unresolved Olympix alerts, comment-less dismissals, or the invalid "Used in tests" reason — spell that out so agents fix the right thing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…ifinance#2124) * chore(deploy): add ReceiverAcrossV4 v1.0.0 on robinhood (EXSC-682) * chore(deploy): update robinhood diamond log after ReceiverAcrossV4 registration (EXSC-682) Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Daniela Chybisova <iron.melmel@gmail.com> Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…nance#2118) * chore(LayerSwapFacet): add tron mainnet deployment log (v1.0.0) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * chore(LayerSwapFacet): register in tron diamond log after timelock cut The timelock `diamondCut` executed on Tron mainnet (executeBatch `673b1bc16fd5f8609bd02bd1b2ed5bd7f85edf7dac5b4351b029ef4f1be62f9e`, block 84746136), so the production diamond registry now reflects the facet. Verified against on-chain state via DiamondLoupe: `facetAddresses()` returns exactly the 18 facets now listed in `tron.diamond.json`, in the same order, and `facetFunctionSelectors(TBFPs7mxPN3nCrnmbjrJh6BS48VpGbu6oQ)` returns the 3 LayerSwapFacet selectors (`0xb48fae2f` LAYERSWAP_DEPOSITORY, `0xee9e98e0` startBridgeTokensViaLayerSwap, `0x4c279d6b` swapAndStartBridgeTokensViaLayerSwap). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Daniel Blaecker <daniel@li.finance>
…/71 networks red -> green) (lifinance#2120) * fix(healthCheck): stop reporting false failures on 54 networks The scheduled Diamond Health Check reported 68/71 networks failing. Most of that was the check disagreeing with config, not real on-chain drift. - whitelist-integrity: resolve periphery addresses from the `address` field in config/whitelist.json instead of by name from the deployments file. Contracts we whitelist but do not deploy (Composer) were absent from deployments, so every one of their on-chain pairs was reported stale; name-keyed lookup also cannot represent the several distinct Composer addresses per network. Warn, rather than silently skip, when an entry resolves to nothing, and warn when a config address disagrees with one we did deploy. Fixes 22 networks. - core facets: add CORE_FACET_EXEMPTIONS so a facet can be core going forward. LiFiIntentEscrowFacet stays in coreFacets, so newly onboarded networks are enforced by default, while the networks that predate that decision are exempt until the facet is backfilled. Table is validated in tests and the exemption is printed per network. Fixes 58 networks. - remove the feecollector-owner invariant: FeeCollector is deprecated and its owner is no longer maintained against config.feeCollectorOwner. - drop ReceiverAcrossV3 from the receiver/executor binding check: deprecated in favour of ReceiverAcrossV4, which stays checked along with the other three. - invariant banner no longer prints its severity as a `[error]`-style prefix. It runs once per invariant per network, so the token made every passing check match a grep for '[error]' and made a healthy run read as mass failure. Verified against production RPCs: katana (previously stale-pair failure) and flow (previously missing-facet failure) both pass; xdc no longer runs the FeeCollector owner check. Not addressed here (real drift, tracked separately): ERC20Proxy owner on 6 networks, and 8 networks with genuinely missing facets. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(healthCheck): don't warn about stale whitelist addresses on shared names The staleness warning compared each periphery entry's config address against a single deployedContracts[name] lookup. When several entries share one name on a network — the Composer case this PR exists to support — at most one could match, so the rest would warn spuriously. Skip the comparison when the name is not unique on that network; deployedContracts cannot disambiguate it. Latent today (Composer is the only duplicated name and is in no deployments file, so the branch was unreachable), but it would fire the moment any shared name got deployed. Exports getExpectedPairs and covers the resolution rules with unit tests: config address wins when the contract is undeployed, all same-name entries are kept, no staleness warning on shared names, warning on a unique-name mismatch, and a warning rather than a silent skip when nothing resolves. Found by CodeRabbit on lifinance#2120. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(healthCheck): stop excluded core facets reappearing as non-core nonCoreFacets was derived by filtering target state against coreFacetsToCheck — the core list AFTER per-network exclusions. So a core facet excluded for a network (GasZip unsupported, or grandfathered via CORE_FACET_EXEMPTIONS) was absent from that list, fell through the filter as "non-core", and was re-checked via target state anyway. The exclusion printed, then the facet failed regardless. Verified before the fix by adding LiFiIntentEscrowFacet to flow's target state: the exemption logged and non-core-facets-deployed still reported it missing. After the fix the same input passes. This is why updating the exemption table alone cannot suppress the alerts when a facet is listed in both coreFacets and target state. Extracts the filter into deriveNonCoreFacets so the invariant is testable, and documents that callers must pass the FULL core list. Six unit tests, including one that pins the bug shape when a pre-excluded list is handed in. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(config): LiFiIntentEscrowFacetV2 supersedes V1 as the core facet PROVISIONAL — pending confirmation that V2 fully replaces V1. Revert this commit alone to undo; the two preceding fixes are independent of it. Swaps coreFacets from LiFiIntentEscrowFacet to LiFiIntentEscrowFacetV2 and retargets CORE_FACET_EXEMPTIONS to V2, recomputed from deployments: V2 is live on 10 production networks, so 63 are exempt until backfilled. Needed because the exemption mechanism only covers CORE facets. Leaving V2 non-core while target state lists it on 75 networks would fail on the 63 where it is not deployed, with nothing available to exempt them. Verified against production RPCs with the refreshed target state applied: flow (exempt) passes, katana (V2 deployed) checks and passes V2. Also verified with the current unmodified target state so this commit stands alone. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(config,deploy): refresh target state from sheet; drop deprecated FeeCollector from corePeriphery Folds the target-state refresh into this PR. The two changes are coupled and must land together: target state now lists LiFiIntentEscrowFacetV2 on 75 networks, which only passes because this PR makes V2 the core facet and exempts the 63 where it is not yet deployed. Landing the refresh alone would fail those 63. Target state regenerated from the production sheet (1995 -> 2056 entries): - deprecations removed: FeeCollector (71 networks), LiFiDEXAggregator (70), ReceiverAcrossV3 (19), DexManagerFacet (3), GenericSwapFacet (tron) - not-supported-there removals: DeBridgeDlnFacet/boba, SquidFacet/sei, AcrossFacetV4 + AcrossFacetPackedV4 + ArbitrumBridgeFacet + ReceiverAcrossV4/plume - LiFiIntentEscrowFacet -> LiFiIntentEscrowFacetV2 - tron row added (was absent from the sheet, so a full refresh previously wiped tron's 20 entries to 0) - 363 version upgrades, no downgrades FeeCollector is deprecated, so it is removed from corePeriphery — otherwise core-periphery-deployed keeps requiring it on every network regardless of target state, which is the same false-failure class this PR removes. Deliberately NOT removing FeeCollector or LiFiDEXAggregator from whitelistPeripheryFunctions or config/whitelist.json: both are still whitelisted on-chain (75 and 74 networks). Removing them from config first would report every one of those pairs as "stale, not in config" — the exact failure this PR fixes. They must be unwhitelisted on-chain first, then dropped from config. (LiFiDEXAggregator was never in corePeriphery, so nothing to remove there.) Verified against production RPCs: flow (was missing-facet), katana (was stale pairs), xdc and botanix (were FeeCollector owner) all pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(config): drop deprecated FeeCollector and LiFiDEXAggregator from whitelistPeripheryFunctions Both are deprecated. Removing them from whitelistPeripheryFunctions stops updateWhitelistPeriphery / diamondUpdatePeriphery treating them as whitelist-eligible and stops checkDeploymentAddressConsistency demanding whitelist.json coverage for them. Safe with respect to whitelist-integrity: getExpectedPairs builds the expected pair set from config/whitelist.json PERIPHERY, not from whitelistPeripheryFunctions, so this cannot turn on-chain pairs into "stale, not in config". Verified — katana (20 pairs) and botanix (11 pairs) both still report the pair array synced. Also safe with respect to periphery-registered: that invariant only considers contracts present in the network's target state, and the target-state refresh in this PR already removed both (FeeCollector on 71 networks, LiFiDEXAggregator on 70). Still deliberately untouched: config/whitelist.json, which lists FeeCollector on 75 networks and LiFiDEXAggregator on 74 and matches what is whitelisted on-chain. Removing them there before unwhitelisting on-chain would report ~149 pairs as stale. Order has to be diamondSyncWhitelist first, then the config removal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(deployments): correct avalanche DiamondCutFacet record; record metis CalldataVerificationFacet Both are records drift, not on-chain drift. No on-chain change in this commit. avalanche DiamondCutFacet: the diamond has 0xf7993A8d…58ABf registered, while deployments/avalanche.json recorded 0xaD501185…957013, which is not among facetAddresses(). Verified 0xf7993A8d is the genuine facet before repointing: - serves exactly one selector, 0x1f931c1c = diamondCut((address,uint8,bytes4[])[],address,bytes) - runtime bytecode is byte-identical to the recorded contract once the trailing solc metadata is excluded; both solc 0.8.17. Only the metadata hash differs, explained by optimizer runs (10000 vs 1000000) - Routescan reports it verified as ContractName "DiamondCutFacet" Created 2022-10-18 (block 21218935), i.e. EARLIER than the recorded 2023-07-20 address: the diamond runs the original facet and the 2023 redeploy was never registered. Both entries are kept in the deployment log, chronologically. This went undetected because the avalanche.diamond.json facet entry had an empty Name, and checkDeploymentAddressConsistency only indexes diamond facets that have one — so the mismatch was invisible to it. Name/Version are now filled in, which also clears the no-unexpected-facets warning for that address. metis CalldataVerificationFacet: deployed and registered at 0x78659196…312E1, and already correct in metis.diamond.json and the deployment log (v1.1.1, verified), but missing from the flat deployments/metis.json the health check reads. Added. Verified: consistency check passes; avalanche and metis both pass the health check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deploy): re-parse target state — drop CBridgeFacetPacked/blast and PioneerFacet Sheet re-parsed after two upstream edits. Only four entries change: - blast/CBridgeFacetPacked (not supported there) - PioneerFacet on arbitrum, mainnet, soneium (deprecated, confirmed by Alexander; its own deprecation PR is in flight) No additions, no version changes. blast and mainnet now pass the health check. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(tron): parse addresses from callTronContract output, not the whole blob callTronContract prepends TronWeb's diagnostic lines ("⚙ Initializing TronWeb…", "⚙ Calling <fn> on <addr>", …) to the actual return value, so the address is the LAST meaningful line. parseTronAddressOutput trimmed the whole blob, which then started with the first diagnostic line and failed every "is this a T… address" check — so a correctly registered contract was reported as absent. On tron this produced four false failures in periphery-registered (ERC20Proxy, Executor, FeeForwarder, OutputValidator, each "got: null") while the registry actually held the right addresses. Probed directly to confirm: getPeripheryContract returns TDCo8wrq…dtcn9 / TUJnqSDo…kAbht / TBiwoFZp…dAF8, all matching deployments. periphery-registered also had its own ad-hoc trim instead of the shared helper — despite the Executor-binding branch documenting that it "mirror[s] the parsing used by the periphery-registration check". Both now go through parseTronAddressOutput. Six unit tests, including the exact diagnostic-prefixed output shape. Verified against tron mainnet: all four periphery contracts now report registered. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(deploy): deploy CalldataVerificationFacet v2.0.0 to zksync Deployed at 0x2E09CeC715CB767F15c46aF8c33ff780eaAbFa74 via the standard path (deploySingleContract -> DeployCalldataVerificationFacet.zksync.s.sol, pinned foundry-zksync v0.0.32, zksolc + solc 0.8.29). Cost 0.000111 ETH. Verified after deploy: - runtime bytecode at the address is byte-identical to zkout/CalldataVerificationFacet.sol/CalldataVerificationFacet.json - already verified on the zksync explorer - deployment record written to MongoDB (contract-deployments) zksync was the only production network missing this core facet. It is NOT yet registered in the diamond — that needs a governance diamondCut, proposed separately. Note: the deploy wrote the record to MongoDB only; deployments/_deployments_log_file.json was not updated by the run (update-deployment-logs.ts syncs JSON -> MongoDB, not the reverse), so the tracked master log does not yet contain this entry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(tron): send TRONGRID_API_KEY so tron's invariants stop 429-ing tron was the last network failing purely on rate limits: refund-wallet-access makes one call per approved selector and TronGrid throttled anonymous traffic before the check could finish, which reads as drift rather than reduced coverage. The viem path already sends TRON-PRO-API-KEY via the devkit's applyTronGridViemTransportExtras, but the health check's tron reads go through callTronContract -> a `bun troncast` subprocess, which talks to TronWeb directly and never saw the key. Wired it into initTronWeb using the devkit's own env var (TRONGRID_API_KEY) and header constant, gated on isTronGridRpcUrl. Also passes the secret to the nightly workflow, which previously supplied only MONGODB_URI — so CI would have kept 429-ing regardless of local config. Measured on tron mainnet: 429 count 43 -> 0. Tron's remaining failure is unrelated (whitelist config load), tracked separately. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * feat(deploy): add missing zksync facet update scripts diamondUpdateFacet resolves zksync scripts as script/deploy/zksync/<name>.zksync.s.sol and aborts when the file is absent, so a facet with only an EVM update script cannot be registered on zksync at all. That currently blocks registering CalldataVerificationFacet v2.0.0, which is deployed on zksync but not yet in the diamond. Adds update scripts for the three facets that are deployed on zksync but had none: - CalldataVerificationFacet (core; blocks the pending registration) - CBridgeFacetPacked - SymbiosisFacet Each is the standard one-line UpdateScriptBase wrapper, identical in structure to the 13 existing zksync update scripts (verified by diff against the EVM counterpart). Not included, deliberately: - CBridgeFacet and LiFiIntentEscrowFacetV2 are in zksync's target state but not deployed there, so update scripts alone would not help. CBridgeFacet is worth following up on: config/cbridge.json HAS a zksync entry and CBridgeFacetPacked is already deployed there, so the base facet looks like an oversight rather than an incompatibility — but it needs a deploy script plus a deployment, not just this. - AccessManagerFacet, DiamondCutFacet, DiamondLoupeFacet, OwnershipFacet and PeripheryRegistryFacet are covered by UpdateCoreFacets.zksync.s.sol. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(tron): parse getAllContractSelectorPairs output past troncast diagnostics The whitelist-integrity invariant parsed callTronContract's raw output for getAllContractSelectorPairs() with .trim() + startsWith('['). That output is prefixed by the troncast command echo ($ bun run ...) and TronWeb's diagnostic lines -- one of which, "Formatted params: []", itself contains a '['. So the trim left it starting with the echo line, startsWith('[') was false, and it threw "Expected array format", which the outer catch swallowed into the generic "Whitelist configuration not available" -- the last remaining tron failure. Add a tested shared helper parseTroncastArrayOutput that strips the echo and diagnostic lines before locating the bracketed payload (mirroring the fix already applied to parseTronAddressOutput), and surface the real error/stack in the catch so future throws are diagnosable rather than masked. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(healthCheck): point whitelist remediation at the bash wrapper, not source-into-zsh The stale/missing-pair remediation printed `source script/tasks/diamondSyncWhitelist.sh && diamondSyncWhitelist <net> <env>`. Sourcing runs the #!/bin/bash script's body in the caller's interactive shell, and its `read -ra` (a bash-only `read` option) fails under zsh -- the macOS default -- with "bad option: -a", leaving the network list empty so the sync runs against `./deployments/.json`. Point at ./script/tasks/syncWhitelistToNetworks.sh instead, which executes under its own bash shebang. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(healthCheck): exempt tempo from LiFiIntentEscrowFacetV2 core check tempo is active/mainnet with LiFiIntentEscrowFacetV2 in its target state but the facet is not yet deployed there, so core-facets-deployed would go red — the same not-yet-backfilled situation as the other exempted networks. Add it to CORE_FACET_EXEMPTIONS so the check stays green with a printed exemption until the facet is backfilled, rather than reintroducing a red the PR is meant to eliminate. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
* feat(tron): deliver diamondCut init calldata (EXSC-650) The Tron cut path could not carry an init call: encodeDiamondCutCalldata hardcoded address(0)/0x, so a facet needing a post-cut initializer landed uninitialised. For AllBridgeFacet v2.2.0 that means chainMappingsInitialized == false and every startBridgeTokensViaAllBridge from Tron reverting UnsupportedAllBridgeChainId — unrepairable afterwards, since setChainIdToAllBridgeChainId itself reverts NotInitialized. Thread optional init/excludeSelectors through encodeDiamondCutCalldata and proposeDiamondCut, and wire the AllBridge deploy script to build initAllBridge(ChainIdConfig[]) from config/allbridge.json — mirroring both getCallData() and getExcludes() of UpdateAllBridgeFacet.s.sol. The initializer rides inside the cut, so there is no window in which the facet is live but uninitialised. Chain ids are revived from the JSON source text: config/allbridge.json carries 9270000000000000, above Number.MAX_SAFE_INTEGER, which JSON.parse alone cannot be trusted to reproduce (the Foundry path decodes straight to uint256 and has no such hazard). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * style: mark IDiamondCutInit and IAllBridgeChainIdConfig fields readonly Both interfaces model immutable value pairs (CodeRabbit, coding guidelines). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
…650) [AllBridgeFacet v2.2.0] (lifinance#2075) * feat(allbridge): dynamic chainId mapping + Stellar destination [AllBridgeFacet v2.2.0] (EXSC-650) Replace the hardcoded LI.FI-chainId -> AllBridge-chainId if/else in AllBridgeFacet with an owner-updatable on-chain mapping (init / set / unset / get), mirroring the PolymerCCTPFacet EXSC-254 pattern, and add Stellar (LIFI_CHAIN_ID_STELLAR -> allBridgeChainId 7) so any->Stellar bridging works via the existing non-EVM path. Config gains a `mappings` array, the update script seeds it in the diamond cut, and a new proposeAllBridgeChainIdMappings.ts syncs mappings across chains. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore(deploy): roll out AllBridgeFacet v2.2.0 to staging (arbitrum, bsc, mainnet, optimism, polygon) (EXSC-650) Deployment logs for the staging diamondCut + initAllBridge of AllBridgeFacet v2.2.0 (dynamic chainId mapping + Stellar) at 0x1c1488c16d9d790e378B7354b35B0F12Dc9D4B49. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(allbridge): reject zero-valued chainId mappings at write boundary (EXSC-650) Enforce the reserved-zero sentinel in initAllBridge and setChainIdToAllBridgeChainId: a zero chainId or allBridgeChainId now reverts InvalidConfig instead of silently writing a mapping that _getAllBridgeChainId treats as unmapped. Adds init/set regression tests for both zero fields. Addresses CodeRabbit review on PR lifinance#2075. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(allbridge): harden NotInitialized tests + add re-init test; guard propose-script path Addresses open review feedback on PR lifinance#2075: - QA #2: replace fragile address(0)-owner pattern in the two NotInitialized revert tests with a real diamond owner via new _deployUninitializedAllBridgeFacet helper (createDiamond + facet mounted, initAllBridge deliberately skipped). - QA #3: add test_InitAllBridgeReInitializationPartiallyOverwrites verifying the documented partial-overwrite re-init semantics (second call wins, omitted entries preserved). - Aikido (medium): add path-traversal guard in getDeploymentsFilePath so a resolved deployments path can never escape the deployments/ directory. No AllBridgeFacet.sol changes. Full suite: 39/39 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * revert(allbridge): drop propose-script path guard (Aikido FP backfire) The path-traversal guard added in edd5301 (Aikido's own suggested fix) was re-rated by Aikido's scanner as a NEW HIGH finding, failing the security gate that was green at baseline. The network arg is trusted input (operator --network CLI flag or getAllActiveNetworks() from config), so the original MEDIUM was a false positive on a non-deployed dev-ops script. Reverting to the baseline path.join restores the green gate; the MEDIUM FP is dismissed via @AikidoSec ignore. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(audit): add AllBridgeFacet v2.2.0 audit report (Sujith Somraaj, 0 findings) Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deploy): deploy AllBridgeFacet v2.2.0 to 10 production networks (EXSC-650) Deployed and explorer-verified on arbitrum, avalanche, base, bsc, linea, mainnet, optimism, polygon, sonic and unichain. One timelock-wrapped Safe proposal per network carries the diamondCut plus the initAllBridge call that seeds the chainId -> allBridgeChainId mappings from config/allbridge.json. celo and tron are not included; see the PR description. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deploy): bump AllBridgeFacet target state to v2.2.0 on 10 networks Co-authored-by: Cursor <cursoragent@cursor.com> * chore(deploy): bump AllBridgeFacet target state to v2.2.0 on celo and tron Co-authored-by: Cursor <cursoragent@cursor.com> * chore(deploy): deploy AllBridgeFacet v2.2.0 to celo (EXSC-650) Deployed and verified at 0x5DEb48AC9906Dc57F35A27efaB4266631D5964a2 with one timelock-wrapped Safe proposal (nonce 33) carrying the diamondCut plus the initAllBridge call. Built for the london EVM target with solc 0.8.17, because this branch predates main's cancun flip for celo (4fd47cb, lifinance#2119). The resulting bytecode therefore differs from the cancun build the other 10 networks received, which is why the CREATE3 address differs from their shared 0x317D089BBe46AaE816b27Eeb1ac26cFB7AB1850D. Kept deliberately rather than redeployed. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * chore(deploy): sync AllBridgeFacet v2.2.0 diamond logs after cut execution on 11 chains (EXSC-650) diamondCut + initAllBridge timelock ops executed on-chain for arbitrum, avalanche, base, bsc, celo, linea, mainnet, optimism, polygon, sonic, unichain. Registry facet entries updated to 2.2.0; scoped to AllBridgeFacet only. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
lifi-action-bot
marked this pull request as draft
July 28, 2026 23:54
0xDEnYO
marked this pull request as ready for review
July 29, 2026 00:06
Collaborator
🤖 GitHub Action: Security Alerts Review 🔍✅ No unresolved security alerts! 🎉 |
melianessa
approved these changes
Jul 29, 2026
7 of 8 tasks
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.
Upstream sync — 2026-07-29
Merges
upstream/main(lifinance/contracts) into the fork'smain. Zero merge conflicts.Why now
Unblocks the AllBridgeFacet v2.2.0 deploy to Tron. Two upstream PRs landed on 2026-07-28 that Tron needs, in order:
2.1.2 → 2.2.0: dynamic owner-updatablechainId → allBridgeChainIdmapping (diamond storage) + Stellar destination. Brings themappingstable intoconfig/allbridge.json.diamondCutinit calldata (deploy-and-register-allbridge-facet.ts,helpers/allBridgeInit.ts,propose-diamond-cut.ts). Without it, deploying v2.2.0 to Tron would leavechainMappingsInitialized == falseand revert every bridge from Tron.Fork delta verified intact
LibAsset.sol→2.1.3-tron(TRON_CHAIN_ID / TRON_USDT bypass branch present)WithdrawablePeriphery.sol→1.0.0-tronconfig/networks.json→somnia.skipHealthcheckpreserved-tronentries + CI carve-outs untouchedPost-merge sanity
forge build✓ · TypeChain (1157 typings) ✓ · TypeScript compile ✓ · lint-staged ✓ (local pre-commit)AllBridgeFacetat2.2.0; mapping table includes Tron (1885080386571452 → 3), Solana (→4), Stellar (→7)Expected CI noise
Per
docs/TronFork.md, required checks keyed to on-chain / production state (healthchecks) may fail on this sync PR despite zero fork-authored code changes. Verify the real diff is clean rather than chasing healthcheck noise.