Repository navigation
feat(layerzero): pin one DVN on pathways whose default is dead - #58
Conversation
zkSync's pathways default to LayerZero's dead DVN. A transceiver can now pin a DVN per eid, both ways and write-once, and at birth for the governor home's pathway; receivers inherit it for their home's eid at creation and change it only by their own payload. The transceiver's delegate is fixed, so its config changes only through setDvn. A fork test (skipped without ETH_RPC_URL) shows the pinned zkSync pathway quoting on Ethereum. Refs #51
There was a problem hiding this comment.
🔵 Needs a closer look
It alters the LayerZero message-authentication security model with irreversible write-once on-chain DVN/delegate configuration whose motivating zkSync path cannot be verified in the test suite, so it warrants human sign-off.
0 open findings
What changed in this PR
This PR resolves issue #51, where every LayerZero pathway touching zkSync Era defaults to the LayerZero "dead DVN" that refuses all messages, and the transceiver — being its own, immovable delegate — previously had no way to reconfigure. It adds a write-once DVN-pinning mechanism so a pathway can be pinned to a single real DVN (both send and receive, on today's default libraries) while otherwise preserving LayerZero's defaults. It fits into the src/protocols/layerzero/ binding, extending the shared LzMessage library (linked, to stay under EIP-170) and the transceiver/receiver initializers.
Changes:
- Adds
setDvn(eid, dvn)(owner-gated, write-once, pins both directions) plus birth-time pinning via a newgovernorHomeDvninitializer arg;LzMessage.pinSendDvn/pinReceiveDvnperform the endpointsetSendLibrary/setReceiveLibrary/setConfigcalls. - Fixes the transceiver's delegate: OApp's
setDelegateis refused in_checkOwner(DelegateIsFixed); receivers inheritdvnOf[homeEid]at creation; transmitters' owners self-delegate instead. - Updates all initializer call sites, deploy script/library (
LZ_GOVERNOR_HOME_DVN), the mock endpoint, tests (incl. a skip-gated Ethereum fork test), and docs (spec R6.4, CHECKS §5, README, research, todo).
| File | Description |
|---|---|
src/protocols/layerzero/LzTransceiver.sol |
Adds dvnOf, setDvn/_pinDvn, governorHomeDvn init arg, and _checkOwner override refusing setDelegate. |
src/protocols/layerzero/LzMessage.sol |
Adds UlnConfig, pinSendDvn/pinReceiveDvn/_onlyDvn and ULN config constants. |
src/protocols/layerzero/LzReceiver.sol |
Adds homeDvn init arg; pins receive-side DVN for the home eid at creation. |
src/protocols/layerzero/LzDivergentTransceiver.sol |
Threads governorHomeDvn through zkSync/Tron initializers; _checkOwner overrides resolve to LzTransceiverBase. |
script/DeployLz.s.sol, script/deploy/LzDeploy.sol |
Pass LZ_GOVERNOR_HOME_DVN; _checkHome asserts the pinned home DVN. |
test/protocols/layerzero/MockLzEndpoint.sol |
Adds library/config surface with oapp-or-delegate authorization. |
test/protocols/layerzero/LzTransceiver.t.sol, LzBinding.t.sol, LzFixture.sol |
New DVN/delegate/receiver/transmitter tests; updated init signatures. |
test/fork/LzDvnFork.t.sol |
New skip-gated fork test of Ethereum→zkSync quote before/after pinning. |
test/{CloneDerivation,DestinationNaming,deploy/DeployChecks,deploy/ChainConfig}.t.sol |
Updated initializer call sites for the new arg. |
deploy/CHECKS.md, deploy/README.md, docs/{provider-spec,provider-research,todo}.md |
Document the pinning mechanism, per-chain DVNs, and verification status. |
🧠 Review effort: Balanced
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Stacked on #57. Decision D1 from the chain checks (#51).
Every LayerZero pathway touching zkSync defaults to the dead DVN, which refuses every message.
setDvn(eid, dvn)pins one DVN for a pathway, both directions, on today's default libraries, write-once. A transceiver can be born with it for the governor home's eid (initialize(..., governorHomeDvn),LZ_GOVERNOR_HOME_DVNinDeployLz), since the bootstrap that creates its owner arrives over that pathway.setDelegate(not virtual) is refused in_checkOwner; before this, the governor's account could reassign the delegate and reconfigure any pathway.setDelegateand configures the endpoint directly; no new function.LzMessage.pinSendDvn/pinReceiveDvnare public and linked. Every LayerZero transceiver stays under EIP-170 (Tron's has 399 bytes left).Verified on an Ethereum fork:
test/fork/LzDvnFork.t.sol(skipped withoutETH_RPC_URL) shows Ethereum to zkSync refusing the quote on the default and quoting once LayerZero Labs' DVN is pinned. The zkSync side cannot be forked (EraVM) and needs a zkSync Sepolia run; tracked indocs/todo.md.Docs: spec R6.4, deploy/CHECKS.md §5 (with each chain's LayerZero Labs DVN), deploy/README.md, provider-research §8.
782 tests pass, 1 skipped (the fork test); lint clean on both profiles.
Closes #51