Skip to content

chore: sync upstream contracts (2026-07-29) — AllBridgeFacet v2.2.0 + Tron init-calldata (EXSC-650) [AllBridgeFacet v2.2.0] - #27

Merged
0xDEnYO merged 13 commits into
mainfrom
sync/upstream-2026-07-29
Jul 29, 2026
Merged

0xDEnYO merged 13 commits into
mainfrom
sync/upstream-2026-07-29

Conversation

@0xDEnYO

@0xDEnYO 0xDEnYO commented Jul 28, 2026

Copy link
Copy Markdown

Upstream sync — 2026-07-29

Merges upstream/main (lifinance/contracts) into the fork's main. 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:

Fork delta verified intact

  • LibAsset.sol → 2.1.3-tron (TRON_CHAIN_ID / TRON_USDT bypass branch present)
  • WithdrawablePeriphery.sol → 1.0.0-tron
  • config/networks.json → somnia.skipHealthcheck preserved
  • Audit-log -tron entries + CI carve-outs untouched

Post-merge sanity

  • forge build ✓ · TypeChain (1157 typings) ✓ · TypeScript compile ✓ · lint-staged ✓ (local pre-commit)
  • AllBridgeFacet at 2.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.

gvladika and others added 13 commits July 24, 2026 08:56
…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
lifi-action-bot marked this pull request as draft July 28, 2026 23:54
@lifi-action-bot lifi-action-bot changed the title chore: sync upstream contracts (2026-07-29) — AllBridgeFacet v2.2.0 + Tron init-calldata (EXSC-650) chore: sync upstream contracts (2026-07-29) — AllBridgeFacet v2.2.0 + Tron init-calldata (EXSC-650) [AllBridgeFacet v2.2.0] Jul 28, 2026
@0xDEnYO
0xDEnYO marked this pull request as ready for review July 29, 2026 00:06
@lifi-action-bot

Copy link
Copy Markdown
Collaborator

🤖 GitHub Action: Security Alerts Review 🔍

✅ No unresolved security alerts! 🎉

@0xDEnYO
0xDEnYO merged commit 00b0546 into main Jul 29, 2026
94 of 98 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants