Skip to content

feat: Script to wire existing networks to newly deployed network#802

Merged
onnovisser merged 12 commits intomainfrom
wire-to-new-network
Mar 12, 2026
Merged

feat: Script to wire existing networks to newly deployed network#802
onnovisser merged 12 commits intomainfrom
wire-to-new-network

Conversation

@onnovisser
Copy link
Contributor

@onnovisser onnovisser commented Mar 9, 2026

Product requirements

  • Wire existing chains to a newly deployed chain via a reusable script.

Design notes

  • Selects adapters from connections json
  • Setting the LZ DVN config can only be done by the Protocol Safe.

TODOs

  • [ ]

nonce = safe.getNonce();
}

IAdapter[] memory adapters = new IAdapter[](2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should build this dynamically based on the configuration

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, we can do that in a later iteration which makes this script generic for any network.

assertTrue(found, "SendPayload not emitted by MultiAdapter for Chainlink");
}

function testWireEthereum() external {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose eventually we want to run the test for every network.

Copy link
Contributor

@wischli wischli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Almost done IMO!

nonce = safe.getNonce();
}

IAdapter[] memory adapters = new IAdapter[](2);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed, we can do that in a later iteration which makes this script generic for any network.

@onnovisser onnovisser marked this pull request as ready for review March 10, 2026 16:41

if (targetConn.axelar) {
address axelarAdapter = source.contracts.axelarAdapter;
bytes memory data = abi.encode(target.adapters.axelar.axelarId, target.contracts.axelarAdapter);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to cast the axelar adapter address to string, otherwise it will silently fail to relay via Axelar which has happened multiple times already. It's a real pain.

Suggested change
bytes memory data = abi.encode(target.adapters.axelar.axelarId, target.contracts.axelarAdapter);
bytes memory data = abi.encode(target.adapters.axelar.axelarId, vm.toString(target.contracts.axelarAdapter));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wow, good catch.

Copy link
Contributor

@wischli wischli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did another round of reviews. Really only nits that you can ignore for now and can be tackled in a follow-up when we make this generic. Just wanted to flag them now so we have them on our radar.


receive() external payable {}

function _testCase(string memory networkName) internal {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: After looking over this once more, I feel like we should also validate the post wiring adapter configuration. AFAICT, if initAdapters were misconfigured (e.g., wrong threshold, wrong quorum), the test could still pass. So we shuld add something like

IMultiAdapter multiAdapter = IMultiAdapter(source.contracts.multiAdapter);
assertGt(multiAdapter.quorum(target.network.centrifugeId, PoolId.wrap(0)), 0, "Quorum not set");
assertEq(multiAdapter.threshold(target.network.centrifugeId, PoolId.wrap(0)), targetConn.threshold, "Wrong threshold");

Comment on lines +146 to +147
address sendLib = lzEndpoint.defaultSendLibrary(targetEid);
address recvLib = lzEndpoint.defaultReceiveLibrary(targetEid);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hypothetically, these could return address(0) if LayerZero hasn't configured default libraries for the targetEid on the source chain. I think we should add a validation layer here to be safe

Suggested change
address sendLib = lzEndpoint.defaultSendLibrary(targetEid);
address recvLib = lzEndpoint.defaultReceiveLibrary(targetEid);
address sendLib = lzEndpoint.defaultSendLibrary(targetEid);
address recvLib = lzEndpoint.defaultReceiveLibrary(targetEid);
require(sendLib != address(0) && recvLib != address(0), "LZ default libraries not configured for target EID");

uint256 idx;

if (targetConn.layerZero) {
address lzAdapter = source.contracts.layerZeroAdapter;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit for later generic use: If targetConn.layerZero is true but source.contracts.layerZeroAdapter == address(0) due to the adapter not being deployed yet on source chain the script calls OpsGuardian.wire(address(0), ...) which is harder to debug as its a low-level revert. Suppose we should ensure none of the addresses point to the default 0 one.

Comment on lines +162 to +164
// function testWirePharos() external {
// _testCase("pharos");
// }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: We should add a comment why this is disabled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll try enabling it, maybe the CI has a Pharos api key

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah right, that one was already wired to Monad

wischli
wischli previously approved these changes Mar 12, 2026
@onnovisser onnovisser requested a review from wischli March 12, 2026 09:51
@github-actions
Copy link

Coverage after merging wire-to-new-network into main will be

97.10%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
src/adapters
   AxelarAdapter.sol97.67%90%100%100%118
   ChainlinkAdapter.sol98.11%91.67%100%100%95
   LayerZeroAdapter.sol98.41%90%100%100%124
   RecoveryAdapter.sol100%100%100%100%
   WormholeAdapter.sol97.67%90%100%100%108
src/admin
   OpsGuardian.sol100%100%100%100%
   ProtocolGuardian.sol100%100%100%100%
   Root.sol100%100%100%100%
   TokenRecoverer.sol100%100%100%100%
src/core/hub
   Accounting.sol97.92%96%100%98.31%134, 137
   Holdings.sol97.71%88.89%100%100%118, 243, 82
   Hub.sol93.98%81.13%93.02%97.54%269, 287, 305, 339, 343, 374–375, 413, 498–499, 544, 549, 586, 601, 87
   HubHandler.sol98%90.91%100%100%71
   HubRegistry.sol92.39%76.67%100%100%118, 124, 130, 35, 46, 79, 99
   ShareClassManager.sol98.81%95.45%100%100%42
src/core/libraries
   PricingLib.sol100%100%100%100%
src/core/messaging
   GasService.sol96.55%100%87.50%96.20%106, 135, 145
   Gateway.sol100%100%100%100%
   MessageDispatcher.sol99.60%98.55%100%100%733
   MessageProcessor.sol82.42%60.26%100%99.01%101, 103, 109, 112, 114, 125, 130, 139, 142, 145, 157, 160, 163, 168, 178, 181, 190, 193, 196, 209, 220, 225, 228, 232, 83–84, 87–88, 91–92, 97, 99
   MultiAdapter.sol100%100%100%100%
src/core/messaging/libraries
   MessageLib.sol100%100%100%100%
src/core/spoke
   BalanceSheet.sol97.47%96.97%92.59%98.55%291, 348, 57
   PoolEscrow.sol100%100%100%100%
   ShareToken.sol92.41%60%94.44%98.04%100, 112, 144, 146, 32
   Spoke.sol97.46%89.71%100%100%132, 132–133, 133, 135, 92–93
   VaultRegistry.sol93.88%85.71%100%98.18%54, 60–62, 98–99
src/core/spoke/factories
   PoolEscrowFactory.sol100%100%100%100%
   TokenFactory.sol100%100%100%100%
src/core/utils
   BatchedMulticall.sol100%100%100%100%
   ContractUpdater.sol100%100%100%100%
src/deployment
   ActionBatchers.sol84.80%62.50%80%86.46%438, 440–441, 443, 443–444, 446, 448, 448–449, 453, 453–454, 456, 459, 459–460, 462, 465, 465–466, 469, 472, 472, 474, 476, 508–512, 517–518, 520–521, 523–524
src/hooks
   BaseTransferHook.sol100%100%100%100%
   FreelyTransferable.sol92.31%80%100%100%37
   FreezeOnly.sol100%100%100%100%
   FullRestrictions.sol95.24%88.89%100%100%47
   RedemptionRestrictions.sol85.71%50%100%100%37
src/hooks/libraries
   UpdateRestrictionMessageLib.sol90%50%100%100%40, 61, 82
src/managers/hub
   NAVManager.sol100%100%100%100%
   SimplePriceManager.sol100%100%100%100%
src/managers/spoke
   MerkleProofManager.sol97.01%87.50%100%100%103, 110
   OnOfframpManager.sol100%100%100%100%
   QueueManager.sol100%100%100%100%
src/managers/spoke/decoders
   BaseDecoder.sol100%100%100%100%
   CircleDecoder.sol83.33%100%100%75%22
   VaultDecoder.sol100%100%100%100%
src/misc
   Auth.sol100%100%100%100%
   ERC20.sol100%100%100%100%
   Escrow.sol56.25%33.33%100%66.67%17, 19, 23–24, 24, 24, 26
   Multicall.sol91.67%66.67%100%100%19
   Recoverable.sol100%100%100%100%
   ReentrancyProtection.sol90%75%100%100%24
src/misc/libraries
   ArrayLib.sol100%100%100%100%
   BitmapLib.sol100%100%100%100%
   BytesLib.sol90.27%56%100%100%109, 120, 131, 14, 142, 153, 16, 164, 175, 186, 87
   CastLib.sol95.24%66.67%100%100%10, 34
   EIP712Lib.sol100%100%100%100%
   ExcessivelySafeCallLib.sol100%100%100%100%
   MathLib.sol93.46%76.19%100%97.33%34–35, 44, 46, 48, 50, 52
   MerkleProofLib.sol100%100%100%100%
   SafeTransferLib.sol96.97%92.86%100%100%75
   SignatureLib.sol95.24%80%100%100%17
   StringLib.sol100%100%100%100%
   TransientArrayLib.sol100%100%100%100%
   TransientBytesLib.sol100%100%100%100%
   TransientStorageLib.sol100%100%100%100%
src/spell
   V2CleaningsSpell.sol0%0%0%0%100–103, 103–106, 111–112, 112–114, 114, 114–116, 116, 116–118, 118, 118–119, 121, 124, 126–127, 129, 133–137, 46–47, 47, 47–48, 50–53, 55, 55–56, 58, 61, 63, 63, 65, 65–66, 66–69, 71, 75, 75–76, 81, 81–82, 82–84, 86–87, 91–94, 96–97
src/utils
   RefundEscrow.sol100%100%100%100%
   RefundEscrowFactory.sol100%100%100%100%
   SubsidyManager.sol100%100%100%100%
src/valuations
   IdentityValuation.sol100%100%100%100%
   OracleValuation.sol100%100%100%100%
src/vaults
   AsyncRequestManager.sol96.85%86.36%100%99.64%195, 198, 201, 204, 215, 227, 295, 328, 455, 460, 505, 573, 580
   AsyncVault.sol96.25%83.33%95%98.15%146, 47
   BaseVaults.sol93.50%80.77%95.24%95.45%124, 137, 239, 309–310, 85–86, 86, 86–88
   BatchRequestManager.sol100%100%100%100%
   SyncDepositVault.sol100%100%100%100%
   SyncManager.sol98.58%92.59%100%100%67, 72
   VaultRouter.sol87.37%58.82%100%91.53%107, 107, 109–110, 110, 112, 149, 70, 73–74, 86–87
src/vaults/factories
   AsyncVaultFactory.sol93.75%50%100%100%32
   SyncDepositVaultFactory.sol95%50%100%100%40
src/vaults/libraries
   RequestCallbackMessageLib.sol89.58%50%100%100%104, 139, 38, 57, 77
   RequestMessageLib.sol89.74%50%100%100%37, 55, 72, 89

@onnovisser onnovisser merged commit 882b984 into main Mar 12, 2026
14 checks passed
@onnovisser onnovisser deleted the wire-to-new-network branch March 12, 2026 10:17
Copy link
Contributor

@lemunozm lemunozm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good script here for future iterations as well!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants