From 092a8e7a874203dc97f482e3323b7643c1c6088b Mon Sep 17 00:00:00 2001 From: Bruce Riley Date: Thu, 7 Nov 2024 08:15:20 -0600 Subject: [PATCH 1/4] Integration: Ntt with Modular Messaging --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 891356699..cf990df7f 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,8 @@ Wormhole’s Native Token Transfers (NTT) is an open, flexible, and composable framework for transferring tokens across blockchains without liquidity pools. Integrators have full control over how their Natively Transferred Tokens (NTTs) behave on each chain, including the token standard and metadata. For existing token deployments, the framework can be used in “locking” mode which preserves the original token supply on a single chain. Otherwise, the framework can be used in “burning” mode to deploy natively multichain tokens with the supply distributed among multiple chains. +This version of NTT uses the Wormhole Modular Messaging infrastructure. + ## Design There are two basic components to NTT: From a0bad596022ef5ce87b263644fb8d7b86cca1cc1 Mon Sep 17 00:00:00 2001 From: bruce-riley <96066700+bruce-riley@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:46:13 -0600 Subject: [PATCH 2/4] evm: Change to use Modular Messaging (#4) * EVM: Change to use Modular Messaging * Evm: More stuff * evm: Put back tests for NttManagerNoRateLimiting * evm: Put back tests for IntegrationStandalone * evm: Put back tests for RateLimit * evm: Put back tests for IntegrationWithoutRateLimiting * evm: Put back echidna * evm: Put back tests for IntegrationAdditionalTransfer * evm: Remove gmp-router module * forge install: example-messaging-endpoint * evm: Change from gmp-router to mm-endpoint * evm: Per-chain thresholds * evm: Add executor parameters to transfer * forge install: example-executor.git evm_add_functions_to_make_payloads * evm: Use executor * evm: Add per-chain gas limits * evm: Remove old executor submodule * forge install: example-messaging-executor * evm: Upgrade executor submodule * evm: Add relay instructions * evm: remove old endpoint * forge install: example-messaging-endpoint * evm: Use executor * Remove old executor and endpoint submodules * forge install: example-messaging-endpoint * forge install: example-messaging-executor * evm: Fix params to executor call --- .gitmodules | 6 + evm/echidna/FuzzNttManager.sol | 548 ++++---- evm/lib/example-messaging-endpoint | 1 + evm/lib/example-messaging-executor | 1 + evm/script/ConfigureWormholeNtt.s.sol | 49 +- evm/script/DeployWormholeNtt.s.sol | 120 +- evm/script/UpgradeNttManager.s.sol | 4 + evm/script/UpgradeWormholeTransceiver.s.sol | 85 -- evm/script/helpers/DeployWormholeNttBase.sol | 38 +- evm/script/helpers/ParseNttConfig.sol | 12 +- evm/src/NttManager/ManagerBase.sol | 427 +++--- evm/src/NttManager/NttManager.sol | 272 +++- .../NttManager/NttManagerNoRateLimiting.sol | 6 +- evm/src/NttManager/TransceiverRegistry.sol | 320 ----- evm/src/Transceiver/Transceiver.sol | 163 --- .../WormholeTransceiver.sol | 284 ---- .../WormholeTransceiverState.sol | 304 ----- evm/src/interfaces/IManagerBase.sol | 118 +- evm/src/interfaces/INttManager.sol | 72 +- evm/src/interfaces/IRateLimiter.sol | 5 +- evm/src/interfaces/ITransceiver.sol | 86 -- evm/src/interfaces/IWormholeTransceiver.sol | 81 -- .../interfaces/IWormholeTransceiverState.sol | 126 -- evm/src/libraries/RateLimiter.sol | 8 +- evm/src/libraries/TransceiverHelpers.sol | 12 + evm/test/IntegrationAdditionalTransfer.t.sol | 205 ++- evm/test/IntegrationManual.t.sol | 285 ---- evm/test/IntegrationRelayer.t.sol | 579 -------- evm/test/IntegrationStandalone.t.sol | 481 +++---- evm/test/IntegrationWithoutRateLimiting.t.sol | 482 ++++--- evm/test/NttManager.t.sol | 1207 ++++++++++------- evm/test/NttManagerNoRateLimiting.t.sol | 1035 +++++++------- evm/test/Ownership.t.sol | 42 +- evm/test/RateLimit.t.sol | 431 +++--- evm/test/Threshold.t.sol | 240 ++++ evm/test/TransceiverStructs.t.sol | 1 - evm/test/Upgrades.t.sol | 710 ---------- evm/test/interfaces/ITransceiverReceiver.sol | 2 +- evm/test/libraries/IntegrationHelpers.sol | 198 --- evm/test/libraries/NttManagerHelpers.sol | 14 +- evm/test/libraries/TransceiverHelpers.sol | 179 ++- evm/test/mocks/DummyTransceiver.sol | 87 +- evm/test/mocks/MockEndpoint.sol | 16 + evm/test/mocks/MockExecutor.sol | 57 + evm/test/mocks/MockNttManager.sol | 52 +- .../mocks/MockNttManagerAdditionalPayload.sol | 4 +- evm/test/mocks/MockTransceivers.sol | 113 -- 47 files changed, 3453 insertions(+), 6115 deletions(-) create mode 160000 evm/lib/example-messaging-endpoint create mode 160000 evm/lib/example-messaging-executor delete mode 100644 evm/script/UpgradeWormholeTransceiver.s.sol delete mode 100644 evm/src/NttManager/TransceiverRegistry.sol delete mode 100644 evm/src/Transceiver/Transceiver.sol delete mode 100644 evm/src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol delete mode 100644 evm/src/Transceiver/WormholeTransceiver/WormholeTransceiverState.sol delete mode 100644 evm/src/interfaces/ITransceiver.sol delete mode 100644 evm/src/interfaces/IWormholeTransceiver.sol delete mode 100644 evm/src/interfaces/IWormholeTransceiverState.sol delete mode 100644 evm/test/IntegrationManual.t.sol delete mode 100755 evm/test/IntegrationRelayer.t.sol create mode 100644 evm/test/Threshold.t.sol delete mode 100644 evm/test/Upgrades.t.sol delete mode 100644 evm/test/libraries/IntegrationHelpers.sol create mode 100644 evm/test/mocks/MockEndpoint.sol create mode 100644 evm/test/mocks/MockExecutor.sol delete mode 100644 evm/test/mocks/MockTransceivers.sol diff --git a/.gitmodules b/.gitmodules index 80c46c6e3..3aa2bb541 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,9 @@ [submodule "evm/lib/solidity-bytes-utils"] path = evm/lib/solidity-bytes-utils url = https://github.com/GNSPS/solidity-bytes-utils +[submodule "evm/lib/example-messaging-endpoint"] + path = evm/lib/example-messaging-endpoint + url = https://github.com/wormholelabs-xyz/example-messaging-endpoint +[submodule "evm/lib/example-messaging-executor"] + path = evm/lib/example-messaging-executor + url = https://github.com/wormholelabs-xyz/example-messaging-executor diff --git a/evm/echidna/FuzzNttManager.sol b/evm/echidna/FuzzNttManager.sol index d3c90de56..03f507648 100644 --- a/evm/echidna/FuzzNttManager.sol +++ b/evm/echidna/FuzzNttManager.sol @@ -7,7 +7,7 @@ import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import "../src/interfaces/IManagerBase.sol"; import "../src/interfaces/INttManager.sol"; import "../src/interfaces/IRateLimiter.sol"; -import "../src/NttManager/TransceiverRegistry.sol"; +import "../src/NttManager/AdapterRegistry.sol"; import "../src/libraries/external/OwnableUpgradeable.sol"; import "../src/libraries/TransceiverStructs.sol"; import "./helpers/FuzzingHelpers.sol"; @@ -20,7 +20,7 @@ contract FuzzNttManager is FuzzingHelpers { uint64[] queuedOutboundTransfersArray; mapping(uint64 => bool) queuedOutboundTransfers; mapping(uint256 => bool) executedQueuedOutboundTransfers; - + // Keep track of transceiver state uint256 numRegisteredTransceivers; uint256 numEnabledTransceivers; @@ -33,7 +33,6 @@ contract FuzzNttManager is FuzzingHelpers { bytes[] orderedInstructions; bytes[] unorderedInstructions; - NttManager nttManager; DummyToken dummyToken; DummyTransceiver dummyTransceiver; @@ -41,24 +40,31 @@ contract FuzzNttManager is FuzzingHelpers { constructor() { _initialManagerSetup(); _generateMultipleTransceiverInstructions(numTransceiverInstructions); - + dummyToken.mintDummy(address(this), type(uint256).max); IERC20(dummyToken).approve(address(nttManager), type(uint256).max); } - function transferShouldSeizeTheRightAmount(uint256 amount, uint256 randomAddress, uint16 recipientChainId) public { + function transferShouldSeizeTheRightAmount( + uint256 amount, + uint256 randomAddress, + uint16 recipientChainId + ) public { INttManager.NttManagerPeer memory peer = nttManager.getPeer(recipientChainId); randomAddress = clampBetween(randomAddress, 1, type(uint256).max); - bytes32 validAddress = bytes32(randomAddress); + bytes32 validAddress = bytes32(randomAddress); uint8 decimals = ERC20(dummyToken).decimals(); - uint8 minDecimals = minUint8(8, minUint8(decimals, peer.tokenDecimals)); + uint8 minDecimals = minUint8(8, minUint8(decimals, peer.tokenDecimals)); - amount = clampBetween(amount, 10 ** (decimals - minDecimals), type(uint64).max * 10 ** (decimals - minUint8(8, decimals))); + amount = clampBetween( + amount, + 10 ** (decimals - minDecimals), + type(uint64).max * 10 ** (decimals - minUint8(8, decimals)) + ); amount = TrimmedAmountLib.scale(amount, decimals, minDecimals); amount = TrimmedAmountLib.scale(amount, minDecimals, decimals); - uint256 nttManagerBalanceBefore = IERC20(dummyToken).balanceOf(address(nttManager)); uint256 thisAddressBalanceBefore = IERC20(dummyToken).balanceOf(address(this)); @@ -67,17 +73,21 @@ contract FuzzNttManager is FuzzingHelpers { uint64 nextMessageSequence = nttManager.nextMessageSequence(); // We always queue to ensure we're always seizing the tokens - try nttManager.transfer(amount, recipientChainId, validAddress, validAddress, true, new bytes(1)) { + try nttManager.transfer( + amount, recipientChainId, validAddress, validAddress, true, new bytes(0) + ) { assert(IERC20(dummyToken).balanceOf(address(this)) == thisAddressBalanceBefore - amount); - assert(IERC20(dummyToken).balanceOf(address(nttManager)) == amount + nttManagerBalanceBefore); + assert( + IERC20(dummyToken).balanceOf(address(nttManager)) + == amount + nttManagerBalanceBefore + ); // This transfer has queued, so let's keep track of it if (amount > currentOutboundCapacity) { queuedOutboundTransfersArray.push(nextMessageSequence); queuedOutboundTransfers[nextMessageSequence] = true; } - } - catch (bytes memory revertData) { + } catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); if (amount == 0) { @@ -85,41 +95,42 @@ contract FuzzNttManager is FuzzingHelpers { errorSelector == selectorToUint(INttManager.ZeroAmount.selector), "NttManager: transfer expected to fail if sending with 0 amount" ); - } - else if (peer.tokenDecimals == 0) { + } else if (peer.tokenDecimals == 0) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidPeerDecimals.selector), "NttManager: transfer expected to fail if sending to a peer with 0 decimals" ); - } - else if (numEnabledTransceivers == 0) { + } else if (numEnabledTransceivers == 0) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.NoEnabledTransceivers.selector), "NttManager: transfer expected to fail if sending when no transceivers are enabled" ); - } - else if (peer.peerAddress == bytes32(0)) { + } else if (peer.peerAddress == bytes32(0)) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.PeerNotRegistered.selector), "NttManager: transfer expected to fail if sending to an unset peer" ); - } - else { - assertWithMsg( - false, - "NttManager: transfer unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: transfer unexpected revert"); } } } - function transferWithQoLEntrypoint(uint256 amount, uint16 recipientChainId, bytes32 recipient) public { + function transferWithQoLEntrypoint( + uint256 amount, + uint16 recipientChainId, + bytes32 recipient + ) public { INttManager.NttManagerPeer memory peer = nttManager.getPeer(recipientChainId); uint8 decimals = ERC20(dummyToken).decimals(); - uint8 minDecimals = minUint8(8, minUint8(decimals, peer.tokenDecimals)); + uint8 minDecimals = minUint8(8, minUint8(decimals, peer.tokenDecimals)); - amount = clampBetween(amount, 10 ** (decimals - minDecimals), type(uint64).max * 10 ** (decimals - minUint8(8, decimals))); + amount = clampBetween( + amount, + 10 ** (decimals - minDecimals), + type(uint64).max * 10 ** (decimals - minUint8(8, decimals)) + ); amount = TrimmedAmountLib.scale(amount, decimals, minDecimals); amount = TrimmedAmountLib.scale(amount, minDecimals, decimals); @@ -135,21 +146,25 @@ contract FuzzNttManager is FuzzingHelpers { ); // Check we increase the inbound rate limit as expected - IRateLimiter.RateLimitParams memory inboundParams = nttManager.getInboundLimitParams(recipientChainId); - assertWithMsg( - nttManager.getCurrentInboundCapacity(recipientChainId) == minUint256(currentInboundCapacity + amount, TrimmedAmountLib.scale(TrimmedAmountLib.getAmount(inboundParams.limit), minUint8(8, decimals), decimals)), + IRateLimiter.RateLimitParams memory inboundParams = + nttManager.getInboundLimitParams(recipientChainId); + assertWithMsg( + nttManager.getCurrentInboundCapacity(recipientChainId) + == minUint256( + currentInboundCapacity + amount, + TrimmedAmountLib.scale( + TrimmedAmountLib.getAmount(inboundParams.limit), + minUint8(8, decimals), + decimals + ) + ), "NttManager: transfer inbound rate limit not increased as expected" ); - } - else { + } else { // We were able to transfer when we shouldn't have been able to - assertWithMsg( - false, - "NttManager: transfer unexpectedly succeeded" - ); + assertWithMsg(false, "NttManager: transfer unexpectedly succeeded"); } - } - catch (bytes memory revertData) { + } catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); if (amount == 0) { @@ -157,53 +172,53 @@ contract FuzzNttManager is FuzzingHelpers { errorSelector == selectorToUint(INttManager.ZeroAmount.selector), "NttManager: transfer expected to fail if sending with 0 amount" ); - } - else if (recipient == bytes32(0)) { + } else if (recipient == bytes32(0)) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidRecipient.selector), "NttManager: transfer expected to fail if sending to 0 address" ); - } - else if (peer.tokenDecimals == 0) { + } else if (peer.tokenDecimals == 0) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidPeerDecimals.selector), "NttManager: transfer expected to fail if sending to a peer with 0 decimals" ); - } - else if (amount > currentOutboundCapacity) { + } else if (amount > currentOutboundCapacity) { assertWithMsg( errorSelector == selectorToUint(IRateLimiter.NotEnoughCapacity.selector), "NttManager: transfer expected to fail if exceeding rate limit" ); - } - else if (numEnabledTransceivers == 0) { + } else if (numEnabledTransceivers == 0) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.NoEnabledTransceivers.selector), "NttManager: transfer expected to fail if sending when no transceivers are enabled" ); - } - else if (peer.peerAddress == bytes32(0)) { + } else if (peer.peerAddress == bytes32(0)) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.PeerNotRegistered.selector), "NttManager: transfer expected to fail if sending to an unset peer" ); - } - else { - assertWithMsg( - false, - "NttManager: transfer unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: transfer unexpected revert"); } } } - function transferShouldQueueProperly(uint256 amount, uint16 recipientChainId, bytes32 recipient, bool shouldQueue) public { + function transferShouldQueueProperly( + uint256 amount, + uint16 recipientChainId, + bytes32 recipient, + bool shouldQueue + ) public { INttManager.NttManagerPeer memory peer = nttManager.getPeer(recipientChainId); - + uint8 decimals = ERC20(dummyToken).decimals(); - uint8 minDecimals = minUint8(8, minUint8(decimals, peer.tokenDecimals)); + uint8 minDecimals = minUint8(8, minUint8(decimals, peer.tokenDecimals)); - amount = clampBetween(amount, 10 ** (decimals - minDecimals), type(uint64).max * 10 ** (decimals - minUint8(8, decimals))); + amount = clampBetween( + amount, + 10 ** (decimals - minDecimals), + type(uint64).max * 10 ** (decimals - minUint8(8, decimals)) + ); amount = TrimmedAmountLib.scale(amount, decimals, minDecimals); amount = TrimmedAmountLib.scale(amount, minDecimals, decimals); @@ -211,20 +226,24 @@ contract FuzzNttManager is FuzzingHelpers { uint256 currentInboundCapacity = nttManager.getCurrentInboundCapacity(recipientChainId); uint64 nextMessageSequence = nttManager.nextMessageSequence(); - try nttManager.transfer(amount, recipientChainId, recipient, recipient, shouldQueue, new bytes(1)) { + try nttManager.transfer( + amount, recipientChainId, recipient, recipient, shouldQueue, new bytes(0) + ) { // If we queued, we should have an item in the queue if ((shouldQueue && amount > currentOutboundCapacity)) { // This transfer has queued, so let's keep track of it queuedOutboundTransfersArray.push(nextMessageSequence); queuedOutboundTransfers[nextMessageSequence] = true; - IRateLimiter.OutboundQueuedTransfer memory queuedTransfer = nttManager.getOutboundQueuedTransfer(nextMessageSequence); + IRateLimiter.OutboundQueuedTransfer memory queuedTransfer = + nttManager.getOutboundQueuedTransfer(nextMessageSequence); assertWithMsg( queuedTransfer.recipient == recipient, "NttManager: transfer queued recipient unexpected" ); assertWithMsg( - TrimmedAmountLib.getAmount(queuedTransfer.amount) == TrimmedAmountLib.scale(amount, decimals, minDecimals), + TrimmedAmountLib.getAmount(queuedTransfer.amount) + == TrimmedAmountLib.scale(amount, decimals, minDecimals), "NttManager: transfer queued amount unexpected" ); assertWithMsg( @@ -257,14 +276,22 @@ contract FuzzNttManager is FuzzingHelpers { ); // Check we increase the inbound rate limit as expected - IRateLimiter.RateLimitParams memory inboundParams = nttManager.getInboundLimitParams(recipientChainId); - assertWithMsg( - nttManager.getCurrentInboundCapacity(recipientChainId) == minUint256(currentInboundCapacity + amount, TrimmedAmountLib.scale(TrimmedAmountLib.getAmount(inboundParams.limit), minUint8(8, decimals), decimals)), + IRateLimiter.RateLimitParams memory inboundParams = + nttManager.getInboundLimitParams(recipientChainId); + assertWithMsg( + nttManager.getCurrentInboundCapacity(recipientChainId) + == minUint256( + currentInboundCapacity + amount, + TrimmedAmountLib.scale( + TrimmedAmountLib.getAmount(inboundParams.limit), + minUint8(8, decimals), + decimals + ) + ), "NttManager: transfer inbound rate limit not increased as expected" ); } - } - catch (bytes memory revertData) { + } catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); if (amount == 0) { @@ -272,47 +299,45 @@ contract FuzzNttManager is FuzzingHelpers { errorSelector == selectorToUint(INttManager.ZeroAmount.selector), "NttManager: transfer expected to fail if sending with 0 amount" ); - } - else if (recipient == bytes32(0)) { + } else if (recipient == bytes32(0)) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidRecipient.selector), "NttManager: transfer expected to fail if sending to 0 address" ); - } - else if (peer.tokenDecimals == 0) { + } else if (peer.tokenDecimals == 0) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidPeerDecimals.selector), "NttManager: transfer expected to fail if sending to a peer with 0 decimals" ); - } - else if (!shouldQueue && amount > currentOutboundCapacity) { + } else if (!shouldQueue && amount > currentOutboundCapacity) { assertWithMsg( errorSelector == selectorToUint(IRateLimiter.NotEnoughCapacity.selector), "NttManager: transfer expected to fail if exceeding rate limit and not queueing" ); - } - else if (numEnabledTransceivers == 0) { + } else if (numEnabledTransceivers == 0) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.NoEnabledTransceivers.selector), "NttManager: transfer expected to fail if sending when no transceivers are enabled" ); - } - else if (peer.peerAddress == bytes32(0)) { + } else if (peer.peerAddress == bytes32(0)) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.PeerNotRegistered.selector), "NttManager: transfer expected to fail if sending to an unset peer" ); - } - else { - assertWithMsg( - false, - "NttManager: transfer unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: transfer unexpected revert"); } } } - function transferWithCustomTransceiverInstructions(uint256 amount, uint16 recipientChainId, bytes32 recipient, bool isOrdered, uint256 instructionIndex, bool shouldQueue) public { + function transferWithCustomTransceiverInstructions( + uint256 amount, + uint16 recipientChainId, + bytes32 recipient, + bool isOrdered, + uint256 instructionIndex, + bool shouldQueue + ) public { INttManager.NttManagerPeer memory peer = nttManager.getPeer(recipientChainId); instructionIndex = clampBetween(instructionIndex, 0, numTransceiverInstructions - 1); @@ -320,30 +345,34 @@ contract FuzzNttManager is FuzzingHelpers { if (isOrdered) { encodedInstructions = orderedInstructions[instructionIndex]; - } - else { + } else { encodedInstructions = unorderedInstructions[instructionIndex]; } uint8 decimals = ERC20(dummyToken).decimals(); - uint8 minDecimals = minUint8(8, minUint8(decimals, peer.tokenDecimals)); + uint8 minDecimals = minUint8(8, minUint8(decimals, peer.tokenDecimals)); - amount = clampBetween(amount, 10 ** (decimals - minDecimals), type(uint64).max * 10 ** (decimals - minUint8(8, decimals))); + amount = clampBetween( + amount, + 10 ** (decimals - minDecimals), + type(uint64).max * 10 ** (decimals - minUint8(8, decimals)) + ); amount = TrimmedAmountLib.scale(amount, decimals, minDecimals); amount = TrimmedAmountLib.scale(amount, minDecimals, decimals); uint256 currentOutboundCapacity = nttManager.getCurrentOutboundCapacity(); uint64 nextMessageSequence = nttManager.nextMessageSequence(); - - try nttManager.transfer(amount, recipientChainId, recipient, recipient, shouldQueue, encodedInstructions) { + + try nttManager.transfer( + amount, recipientChainId, recipient, recipient, shouldQueue, encodedInstructions + ) { // If we queued, we should have an item in the queue if ((shouldQueue && amount > currentOutboundCapacity)) { // This transfer has queued, so let's keep track of it queuedOutboundTransfersArray.push(nextMessageSequence); queuedOutboundTransfers[nextMessageSequence] = true; } - } - catch (bytes memory revertData) { + } catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); if (amount == 0) { @@ -351,40 +380,36 @@ contract FuzzNttManager is FuzzingHelpers { errorSelector == selectorToUint(INttManager.ZeroAmount.selector), "NttManager: transfer expected to fail if sending with 0 amount" ); - } - else if (recipient == bytes32(0)) { + } else if (recipient == bytes32(0)) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidRecipient.selector), "NttManager: transfer expected to fail if sending to 0 address" ); - } - else if (peer.tokenDecimals == 0) { + } else if (peer.tokenDecimals == 0) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidPeerDecimals.selector), "NttManager: transfer expected to fail if sending to a peer with 0 decimals" ); - } - else if (!shouldQueue && amount > currentOutboundCapacity) { + } else if (!shouldQueue && amount > currentOutboundCapacity) { assertWithMsg( errorSelector == selectorToUint(IRateLimiter.NotEnoughCapacity.selector), "NttManager: transfer expected to fail if exceeding rate limit and not queueing" ); - } - else if (numEnabledTransceivers == 0) { + } else if (numEnabledTransceivers == 0) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.NoEnabledTransceivers.selector), "NttManager: transfer expected to fail if sending when no transceivers are enabled" ); - } - else if (peer.peerAddress == bytes32(0)) { + } else if (peer.peerAddress == bytes32(0)) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.PeerNotRegistered.selector), "NttManager: transfer expected to fail if sending to an unset peer" ); - } - else if (errorSelector == selectorToUint(TransceiverStructs.InvalidInstructionIndex.selector)) { - TransceiverStructs.TransceiverInstruction[] memory instructions = - TransceiverStructs.parseTransceiverInstructions(encodedInstructions, numRegisteredTransceivers); + } else if ( + errorSelector == selectorToUint(TransceiverStructs.InvalidInstructionIndex.selector) + ) { + TransceiverStructs.TransceiverInstruction[] memory instructions = TransceiverStructs + .parseTransceiverInstructions(encodedInstructions, numRegisteredTransceivers); for (uint256 i = 0; i < instructions.length; ++i) { if (instructions[i].index < numRegisteredTransceivers) { assertWithMsg( @@ -393,67 +418,76 @@ contract FuzzNttManager is FuzzingHelpers { ); } } - } - else { - assertWithMsg( - false, - "NttManager: transfer unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: transfer unexpected revert"); } } } - function canOnlyPlayQueuedOutboundTransferOnce(uint256 warpTime, uint64 messageSequence, bool pickQueuedSequence) public { + function canOnlyPlayQueuedOutboundTransferOnce( + uint256 warpTime, + uint64 messageSequence, + bool pickQueuedSequence + ) public { if (pickQueuedSequence) { - messageSequence = queuedOutboundTransfersArray[clampBetween(uint256(messageSequence), 0, queuedOutboundTransfersArray.length)]; + messageSequence = queuedOutboundTransfersArray[clampBetween( + uint256(messageSequence), 0, queuedOutboundTransfersArray.length + )]; } - IRateLimiter.OutboundQueuedTransfer memory queuedTransfer = nttManager.getOutboundQueuedTransfer(messageSequence); - + IRateLimiter.OutboundQueuedTransfer memory queuedTransfer = + nttManager.getOutboundQueuedTransfer(messageSequence); + warpTime = clampBetween(warpTime, 0, 365 days); uint64 newTimestamp = uint64(block.timestamp + warpTime); // We can safely cast here as the warp is clamped hevm.warp(newTimestamp); uint256 currentOutboundCapacity = nttManager.getCurrentOutboundCapacity(); - uint256 currentInboundCapacity = nttManager.getCurrentInboundCapacity(queuedTransfer.recipientChain); + uint256 currentInboundCapacity = + nttManager.getCurrentInboundCapacity(queuedTransfer.recipientChain); try nttManager.completeOutboundQueuedTransfer(messageSequence) { // Rate limits stay untouched assertWithMsg( - currentOutboundCapacity == nttManager.getCurrentOutboundCapacity() && - currentInboundCapacity == nttManager.getCurrentInboundCapacity(queuedTransfer.recipientChain), + currentOutboundCapacity == nttManager.getCurrentOutboundCapacity() + && currentInboundCapacity + == nttManager.getCurrentInboundCapacity(queuedTransfer.recipientChain), "NttManager: completeOutboundQueuedTransfer expected not to change rate limit capacity" ); // Set that this message sequence has been played executedQueuedOutboundTransfers[messageSequence] = true; - } - catch (bytes memory revertData) { + } catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); // If the message has been executed before it shouldn't be found again if (executedQueuedOutboundTransfers[messageSequence]) { assertWithMsg( - errorSelector == selectorToUint(IRateLimiter.OutboundQueuedTransferNotFound.selector), + errorSelector + == selectorToUint(IRateLimiter.OutboundQueuedTransferNotFound.selector), "NttManager: completeOutboundQueuedTransfer expected to fail if not found" ); } if (queuedTransfer.txTimestamp == 0) { assertWithMsg( - errorSelector == selectorToUint(IRateLimiter.OutboundQueuedTransferNotFound.selector), + errorSelector + == selectorToUint(IRateLimiter.OutboundQueuedTransferNotFound.selector), "NttManager: completeOutboundQueuedTransfer expected to fail if not found" ); - } - else if (newTimestamp - queuedTransfer.txTimestamp < 1 days) { + } else if (newTimestamp - queuedTransfer.txTimestamp < 1 days) { assertWithMsg( - errorSelector == selectorToUint(IRateLimiter.OutboundQueuedTransferStillQueued.selector), + errorSelector + == selectorToUint(IRateLimiter.OutboundQueuedTransferStillQueued.selector), "NttManager: completeOutboundQueuedTransfer expected to fail if not queued for long enough" ); - } - else if (errorSelector == selectorToUint(TransceiverStructs.InvalidInstructionIndex.selector)) { - TransceiverStructs.TransceiverInstruction[] memory instructions = - TransceiverStructs.parseTransceiverInstructions(queuedTransfer.transceiverInstructions, numRegisteredTransceivers); + } else if ( + errorSelector == selectorToUint(TransceiverStructs.InvalidInstructionIndex.selector) + ) { + TransceiverStructs.TransceiverInstruction[] memory instructions = TransceiverStructs + .parseTransceiverInstructions( + queuedTransfer.transceiverInstructions, numRegisteredTransceivers + ); for (uint256 i = 0; i < instructions.length; ++i) { if (instructions[i].index < numRegisteredTransceivers) { assertWithMsg( @@ -462,36 +496,35 @@ contract FuzzNttManager is FuzzingHelpers { ); } } - } - else if (numEnabledTransceivers == 0) { + } else if (numEnabledTransceivers == 0) { // In this case the sender should be able to cancel their outbound queued transfer try nttManager.cancelOutboundQueuedTransfer(messageSequence) { // Set that this message sequence has been played executedQueuedOutboundTransfers[messageSequence] = true; - } - catch { + } catch { assertWithMsg( - false, - "NttManager: cancelOutboundQueuedTransfer unexpected revert" + false, "NttManager: cancelOutboundQueuedTransfer unexpected revert" ); } + } else { + assertWithMsg(false, "NttManager: completeOutboundQueuedTransfer unexpected revert"); } - else { - assertWithMsg( - false, - "NttManager: completeOutboundQueuedTransfer unexpected revert" - ); - } - } } - function cancelOutboundQueuedTransfer(bool pickQueuedSequence, uint64 messageSequence, address sender) public { + function cancelOutboundQueuedTransfer( + bool pickQueuedSequence, + uint64 messageSequence, + address sender + ) public { if (pickQueuedSequence) { - messageSequence = queuedOutboundTransfersArray[clampBetween(uint256(messageSequence), 0, queuedOutboundTransfersArray.length)]; + messageSequence = queuedOutboundTransfersArray[clampBetween( + uint256(messageSequence), 0, queuedOutboundTransfersArray.length + )]; } - IRateLimiter.OutboundQueuedTransfer memory queuedTransfer = nttManager.getOutboundQueuedTransfer(messageSequence); + IRateLimiter.OutboundQueuedTransfer memory queuedTransfer = + nttManager.getOutboundQueuedTransfer(messageSequence); uint256 nttManagerBalanceBefore = IERC20(dummyToken).balanceOf(address(nttManager)); uint256 senderBalanceBefore = IERC20(dummyToken).balanceOf(address(queuedTransfer.sender)); @@ -500,64 +533,71 @@ contract FuzzNttManager is FuzzingHelpers { hevm.prank(sender); try nttManager.cancelOutboundQueuedTransfer(messageSequence) { - assert(IERC20(dummyToken).balanceOf(address(queuedTransfer.sender)) == senderBalanceBefore + amount); - assert(IERC20(dummyToken).balanceOf(address(nttManager)) == nttManagerBalanceBefore - amount); + assert( + IERC20(dummyToken).balanceOf(address(queuedTransfer.sender)) + == senderBalanceBefore + amount + ); + assert( + IERC20(dummyToken).balanceOf(address(nttManager)) + == nttManagerBalanceBefore - amount + ); // Set that this message sequence has been played since this has the same effect as executing an outbound transfer executedQueuedOutboundTransfers[messageSequence] = true; - } - catch (bytes memory revertData) { + } catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); - if (executedQueuedOutboundTransfers[messageSequence] || !queuedOutboundTransfers[messageSequence]) { + if ( + executedQueuedOutboundTransfers[messageSequence] + || !queuedOutboundTransfers[messageSequence] + ) { assertWithMsg( - errorSelector == selectorToUint(IRateLimiter.OutboundQueuedTransferNotFound.selector), + errorSelector + == selectorToUint(IRateLimiter.OutboundQueuedTransferNotFound.selector), "NttManager: cancelOutboundQueuedTransfer expected to fail if not found or already executed/cancelled" ); - } - else if (sender != queuedTransfer.sender) { + } else if (sender != queuedTransfer.sender) { assertWithMsg( errorSelector == selectorToUint(INttManager.CancellerNotSender.selector), "NttManager: cancelOutboundQueuedTransfer expected to fail if called by a different sender" ); - } - else { - assertWithMsg( - false, - "NttManager: cancelOutboundQueuedTransfer unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: cancelOutboundQueuedTransfer unexpected revert"); } } } - function setPeer(uint16 peerChainId, + function setPeer( + uint16 peerChainId, bytes32 peerContract, uint8 decimals, uint256 inboundLimit, bool clampLimit ) public { uint8 localDecimals = ERC20(dummyToken).decimals(); - if (clampLimit) inboundLimit = clampBetween(inboundLimit, 0, type(uint64).max * 10 ** (localDecimals - minUint8(8, localDecimals))); - - try nttManager.setPeer(peerChainId, peerContract, decimals, inboundLimit) { - + if (clampLimit) { + inboundLimit = clampBetween( + inboundLimit, + 0, + type(uint64).max * 10 ** (localDecimals - minUint8(8, localDecimals)) + ); } + + try nttManager.setPeer(peerChainId, peerContract, decimals, 10000000000, inboundLimit) {} catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); - + if (peerChainId == 0) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidPeerChainIdZero.selector), "NttManager: setPeer expected to fail if setting zero peer chain id" ); - } - else if (peerContract == bytes32(0)) { + } else if (peerContract == bytes32(0)) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidPeerZeroAddress.selector), "NttManager: setPeer expected to fail if setting zero peer contract address" ); - } - else if (decimals == 0) { + } else if (decimals == 0) { assertWithMsg( errorSelector == selectorToUint(INttManager.InvalidPeerDecimals.selector), "NttManager: setPeer expected to fail if setting zero peer decimals" @@ -569,83 +609,76 @@ contract FuzzNttManager is FuzzingHelpers { errorSelector == selectorToUint(INttManager.InvalidPeerSameChainId.selector), "NttManager: setPeer expected to fail if setting for the same chain id" ); - } - else if (!clampLimit) { + } else if (!clampLimit) { bytes32 errorStringHash = extractErrorString(revertData); assertWithMsg( - errorStringHash == keccak256(abi.encodePacked("SafeCast: value doesn't fit in 64 bits")), + errorStringHash + == keccak256(abi.encodePacked("SafeCast: value doesn't fit in 64 bits")), "NttManager: setPeer expected to fail if setting too large an inbound limit" ); - } - else { - assertWithMsg( - false, - "NttManager: setPeer unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: setPeer unexpected revert"); } } } function setOutboundLimit(uint256 limit, bool clampLimit) public { uint8 localDecimals = ERC20(dummyToken).decimals(); - if (clampLimit) limit = clampBetween(limit, 0, type(uint64).max * 10 ** (localDecimals - minUint8(8, localDecimals))); - - try nttManager.setOutboundLimit(limit) { - + if (clampLimit) { + limit = clampBetween( + limit, 0, type(uint64).max * 10 ** (localDecimals - minUint8(8, localDecimals)) + ); } + + try nttManager.setOutboundLimit(limit) {} catch (bytes memory revertData) { if (!clampLimit) { bytes32 errorStringHash = extractErrorString(revertData); assertWithMsg( - errorStringHash == keccak256(abi.encodePacked("SafeCast: value doesn't fit in 64 bits")), + errorStringHash + == keccak256(abi.encodePacked("SafeCast: value doesn't fit in 64 bits")), "NttManager: setOutboundLimit expected to fail if setting too large a limit" ); - } - else { - assertWithMsg( - false, - "NttManager: setOutboundLimit unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: setOutboundLimit unexpected revert"); } } } function setInboundLimit(uint256 limit, uint16 chainId, bool clampLimit) public { uint8 localDecimals = ERC20(dummyToken).decimals(); - if (clampLimit) limit = clampBetween(limit, 0, type(uint64).max * 10 ** (localDecimals - minUint8(8, localDecimals))); - - try nttManager.setInboundLimit(limit, chainId) { - + if (clampLimit) { + limit = clampBetween( + limit, 0, type(uint64).max * 10 ** (localDecimals - minUint8(8, localDecimals)) + ); } + + try nttManager.setInboundLimit(limit, chainId) {} catch (bytes memory revertData) { if (!clampLimit) { bytes32 errorStringHash = extractErrorString(revertData); assertWithMsg( - errorStringHash == keccak256(abi.encodePacked("SafeCast: value doesn't fit in 64 bits")), + errorStringHash + == keccak256(abi.encodePacked("SafeCast: value doesn't fit in 64 bits")), "NttManager: setInboundLimit expected to fail if setting too large a limit" ); - } - else { - assertWithMsg( - false, - "NttManager: setInboundLimit unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: setInboundLimit unexpected revert"); } } } function setTransceiver(bool newTransceiver, uint256 transceiverIndex) public { address transceiver; - + if (newTransceiver) { DummyTransceiver newDummyTransceiver = new DummyTransceiver(address(nttManager)); transceiver = address(newDummyTransceiver); - } - else { + } else { transceiverIndex = clampBetween(transceiverIndex, 0, registeredTransceivers.length - 1); transceiver = registeredTransceivers[transceiverIndex]; } - + try nttManager.setTransceiver(transceiver) { // We only set these if the transceiver wasn't registered before if (!isTransceiverRegistered[transceiver]) { @@ -656,92 +689,79 @@ contract FuzzNttManager is FuzzingHelpers { isTransceiverEnabled[transceiver] = true; numEnabledTransceivers++; - } - catch (bytes memory revertData) { + } catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); if (isTransceiverRegistered[transceiver]) { assertWithMsg( - errorSelector == selectorToUint(TransceiverRegistry.TransceiverAlreadyEnabled.selector), + errorSelector == selectorToUint(AdapterRegistry.AdapterAlreadyEnabled.selector), "NttManager: setTransceiver expected to fail if enabling an already enabled transceiver" ); - } - else if (transceiver == address(0)) { + } else if (transceiver == address(0)) { assertWithMsg( - errorSelector == selectorToUint(TransceiverRegistry.InvalidTransceiverZeroAddress.selector), + errorSelector + == selectorToUint(AdapterRegistry.InvalidTransceiverZeroAddress.selector), "NttManager: setTransceiver expected to fail if registering the 0 address" ); - } - else if (numRegisteredTransceivers >= 64) { + } else if (numRegisteredTransceivers >= 64) { assertWithMsg( - errorSelector == selectorToUint(TransceiverRegistry.TooManyTransceivers.selector), + errorSelector == selectorToUint(AdapterRegistry.TooManyAdapters.selector), "NttManager: setTransceiver expected to fail if registering too many transceivers" ); - } - else { - assertWithMsg( - false, - "NttManager: setTransceiver unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: setTransceiver unexpected revert"); } } } function removeTransceiver(bool registeredTransceiver, uint256 transceiverIndex) public { address transceiver; - + if (registeredTransceiver) { transceiverIndex = clampBetween(transceiverIndex, 0, registeredTransceivers.length - 1); transceiver = registeredTransceivers[transceiverIndex]; - } - else { + } else { transceiver = address(uint160(transceiverIndex)); } try nttManager.removeTransceiver(transceiver) { isTransceiverEnabled[transceiver] = false; numEnabledTransceivers--; - } - catch (bytes memory revertData) { + } catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); if (transceiver == address(0)) { assertWithMsg( - errorSelector == selectorToUint(TransceiverRegistry.InvalidTransceiverZeroAddress.selector), + errorSelector + == selectorToUint(AdapterRegistry.InvalidTransceiverZeroAddress.selector), "NttManager: removeTransceiver expected to fail if removing the 0 address" ); - } - else if (!isTransceiverRegistered[transceiver]) { + } else if (!isTransceiverRegistered[transceiver]) { assertWithMsg( - errorSelector == selectorToUint(TransceiverRegistry.NonRegisteredTransceiver.selector), + errorSelector + == selectorToUint(AdapterRegistry.NonRegisteredTransceiver.selector), "NttManager: removeTransceiver expected to fail if removing a non-registered transceiver" ); - } - else if (!isTransceiverEnabled[transceiver]) { + } else if (!isTransceiverEnabled[transceiver]) { assertWithMsg( - errorSelector == selectorToUint(TransceiverRegistry.DisabledTransceiver.selector), + errorSelector == selectorToUint(AdapterRegistry.DisabledTransceiver.selector), "NttManager: removeTransceiver expected to fail if removing an already disabled transceiver" ); - } - else if (numEnabledTransceivers == 1) { + } else if (numEnabledTransceivers == 1) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.ZeroThreshold.selector), "NttManager: removeTransceiver expected to fail if trying to remove the last enabled transceiver" ); - } - else { - assertWithMsg( - false, - "NttManager: removeTransceiver unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: removeTransceiver unexpected revert"); } } } - function setThreshold(uint8 threshold) public { - try nttManager.setThreshold(threshold) { - - } + function setThreshold( + uint8 threshold + ) public { + try nttManager.setThreshold(threshold) {} catch (bytes memory revertData) { uint256 errorSelector = extractErrorSelector(revertData); @@ -750,18 +770,13 @@ contract FuzzNttManager is FuzzingHelpers { errorSelector == selectorToUint(IManagerBase.ZeroThreshold.selector), "NttManager: setThreshold expected to fail if setting threshold to 0" ); - } - else if (threshold > numEnabledTransceivers) { + } else if (threshold > numEnabledTransceivers) { assertWithMsg( errorSelector == selectorToUint(IManagerBase.ThresholdTooHigh.selector), "NttManager: setThreshold expected to fail if trying to set threshold above num enabled transceivers" ); - } - else { - assertWithMsg( - false, - "NttManager: setThreshold unexpected revert" - ); + } else { + assertWithMsg(false, "NttManager: setThreshold unexpected revert"); } } } @@ -772,18 +787,23 @@ contract FuzzNttManager is FuzzingHelpers { // Deploy an NTT token dummyToken = new DummyToken(); // Deploy an implementation of the manager - NttManager implementation = new NttManager(address(dummyToken), IManagerBase.Mode.LOCKING, 1, 1 days, false); + NttManager implementation = + new NttManager(address(dummyToken), IManagerBase.Mode.LOCKING, 1, 1 days, false); // Place the manager behind a proxy nttManager = NttManager(address(new ERC1967Proxy(address(implementation), ""))); // Initialize the proxy nttManager.initialize(); } - function _psuedoRandomNumber(uint256 seed) internal returns(uint256) { - return uint(keccak256(abi.encodePacked(block.timestamp, msg.sender, seed))); + function _psuedoRandomNumber( + uint256 seed + ) internal returns (uint256) { + return uint256(keccak256(abi.encodePacked(block.timestamp, msg.sender, seed))); } - function _generateMultipleTransceiverInstructions(uint256 num) internal { + function _generateMultipleTransceiverInstructions( + uint256 num + ) internal { for (uint256 i = 0; i < num; ++i) { _generateTransceiverInstructions(true, i); _generateTransceiverInstructions(false, i); @@ -793,8 +813,9 @@ contract FuzzNttManager is FuzzingHelpers { function _generateTransceiverInstructions(bool isOrdered, uint256 seed) internal { bytes memory encodedInstructions; uint256 numInstructions = clampBetween(_psuedoRandomNumber(seed), 0, type(uint8).max); - - TransceiverStructs.TransceiverInstruction[] memory instructions = new TransceiverStructs.TransceiverInstruction[](numInstructions); + + TransceiverStructs.TransceiverInstruction[] memory instructions = + new TransceiverStructs.TransceiverInstruction[](numInstructions); uint256 previousIndex = 0; @@ -808,12 +829,12 @@ contract FuzzNttManager is FuzzingHelpers { if (isOrdered) { newIndex = clampBetween(newIndex, previousIndex + 1, type(uint8).max); - } - else { + } else { newIndex = clampBetween(newIndex, 0, type(uint8).max); } - TransceiverStructs.TransceiverInstruction memory instruction = TransceiverStructs.TransceiverInstruction({ + TransceiverStructs.TransceiverInstruction memory instruction = TransceiverStructs + .TransceiverInstruction({ index: uint8(newIndex), payload: new bytes(newIndex) // We generate an arbitrary length byte array (of zeros for now) }); @@ -827,9 +848,8 @@ contract FuzzNttManager is FuzzingHelpers { if (isOrdered) { orderedInstructions.push(encodedInstructions); - } - else { + } else { unorderedInstructions.push(encodedInstructions); } } -} \ No newline at end of file +} diff --git a/evm/lib/example-messaging-endpoint b/evm/lib/example-messaging-endpoint new file mode 160000 index 000000000..0f853ea03 --- /dev/null +++ b/evm/lib/example-messaging-endpoint @@ -0,0 +1 @@ +Subproject commit 0f853ea0335937d611217f5048d677a4f46249fd diff --git a/evm/lib/example-messaging-executor b/evm/lib/example-messaging-executor new file mode 160000 index 000000000..d6125d67a --- /dev/null +++ b/evm/lib/example-messaging-executor @@ -0,0 +1 @@ +Subproject commit d6125d67ac3f68c65045e954941cf3fd2d78387a diff --git a/evm/script/ConfigureWormholeNtt.s.sol b/evm/script/ConfigureWormholeNtt.s.sol index 083b1e831..282ccb6a3 100644 --- a/evm/script/ConfigureWormholeNtt.s.sol +++ b/evm/script/ConfigureWormholeNtt.s.sol @@ -4,12 +4,12 @@ pragma solidity >=0.8.8 <0.9.0; import {console2} from "forge-std/Script.sol"; import {stdJson} from "forge-std/StdJson.sol"; +import "example-messaging-endpoint/evm/src/interfaces/IAdapter.sol"; + import "../src/interfaces/INttManager.sol"; -import "../src/interfaces/IWormholeTransceiver.sol"; import "../src/interfaces/IOwnableUpgradeable.sol"; import {ParseNttConfig} from "./helpers/ParseNttConfig.sol"; -import {WormholeTransceiver} from "../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; contract ConfigureWormholeNtt is ParseNttConfig { using stdJson for string; @@ -24,42 +24,6 @@ contract ConfigureWormholeNtt is ParseNttConfig { require(params.wormholeChainId != 0, "Invalid chain ID"); } - function configureWormholeTransceiver( - IWormholeTransceiver wormholeTransceiver, - ChainConfig[] memory config, - ConfigParams memory params - ) internal { - for (uint256 i = 0; i < config.length; i++) { - ChainConfig memory targetConfig = config[i]; - if (targetConfig.chainId == params.wormholeChainId) { - continue; - } else { - // Set relayer. - if (targetConfig.isWormholeRelayingEnabled) { - wormholeTransceiver.setIsWormholeRelayingEnabled(targetConfig.chainId, true); - console2.log("Wormhole relaying enabled for chain", targetConfig.chainId); - } else if (targetConfig.isSpecialRelayingEnabled) { - wormholeTransceiver.setIsSpecialRelayingEnabled(targetConfig.chainId, true); - console2.log("Special relaying enabled for chain", targetConfig.chainId); - } - - // Set peer. - wormholeTransceiver.setWormholePeer( - targetConfig.chainId, targetConfig.wormholeTransceiver - ); - console2.log("Wormhole peer set for chain", targetConfig.chainId); - - // Set EVM chain. - if (targetConfig.isEvmChain) { - wormholeTransceiver.setIsWormholeEvmChain(targetConfig.chainId, true); - console2.log("EVM chain set for chain", targetConfig.chainId); - } else { - console2.log("This is not an EVM chain, doing nothing"); - } - } - } - } - function configureNttManager( INttManager nttManager, ChainConfig[] memory config, @@ -75,6 +39,7 @@ contract ConfigureWormholeNtt is ParseNttConfig { targetConfig.chainId, targetConfig.nttManager, targetConfig.decimals, + targetConfig.gasLimit, targetConfig.inboundLimit ); console2.log("Peer set for chain", targetConfig.chainId); @@ -87,13 +52,9 @@ contract ConfigureWormholeNtt is ParseNttConfig { // Sanity check deployment parameters. ConfigParams memory params = _readEnvVariables(); - ( - ChainConfig[] memory config, - INttManager nttManager, - IWormholeTransceiver wormholeTransceiver - ) = _parseAndValidateConfigFile(params.wormholeChainId); + (ChainConfig[] memory config, INttManager nttManager,) = + _parseAndValidateConfigFile(params.wormholeChainId); - configureWormholeTransceiver(wormholeTransceiver, config, params); configureNttManager(nttManager, config, params); vm.stopBroadcast(); diff --git a/evm/script/DeployWormholeNtt.s.sol b/evm/script/DeployWormholeNtt.s.sol index a55e8e52e..adaf7689f 100644 --- a/evm/script/DeployWormholeNtt.s.sol +++ b/evm/script/DeployWormholeNtt.s.sol @@ -4,7 +4,6 @@ pragma solidity >=0.8.8 <0.9.0; import {Script, console} from "forge-std/Script.sol"; import {DeployWormholeNttBase} from "./helpers/DeployWormholeNttBase.sol"; import {INttManager} from "../src/interfaces/INttManager.sol"; -import {IWormholeTransceiver} from "../src/interfaces/IWormholeTransceiver.sol"; import "../src/interfaces/IManagerBase.sol"; import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import {NttManager} from "../src/NttManager/NttManager.sol"; @@ -15,7 +14,10 @@ interface IWormhole { contract DeployWormholeNtt is Script, DeployWormholeNttBase { function run( + address endpoint, + address executor, address wormhole, + address transceiver, address token, address wormholeRelayer, address specialRelayer, @@ -28,66 +30,72 @@ contract DeployWormholeNtt is Script, DeployWormholeNttBase { IWormhole wh = IWormhole(wormhole); // sanity check decimals - (bool success, bytes memory queriedDecimals) = - token.staticcall(abi.encodeWithSignature("decimals()")); - - if (success) { - uint8 queriedDecimals = abi.decode(queriedDecimals, (uint8)); - if (queriedDecimals != decimals) { - console.log("Decimals mismatch: ", queriedDecimals, " != ", decimals); - vm.stopBroadcast(); - return; + { + (bool success, bytes memory queriedDecimals) = + token.staticcall(abi.encodeWithSignature("decimals()")); + + if (success) { + uint8 queriedDec = abi.decode(queriedDecimals, (uint8)); + if (queriedDec != decimals) { + console.log("Decimals mismatch: ", queriedDec, " != ", decimals); + vm.stopBroadcast(); + return; + } + } else { + // NOTE: this might not be a critical error. It could just mean that + // the token contract was compiled against a different EVM version than what the forge script is running on. + // In this case, it's the responsibility of the caller to ensure that the provided decimals are correct + // and that the token contract is valid. + // The best way to ensure that is by calling this script with the queried token decimals (which is what the NTT CLI does). + console.log( + "Failed to query token decimals. Proceeding with provided decimals.", decimals + ); + // the NTT manager initialiser calls the token contract to get the + // decimals as well. We're just going to mock that call to return the provided decimals. + // This is a bit of a hack, but in the worst case (i.e. if the token contract is actually broken), the + // NTT manager initialiser will fail anyway. + vm.mockCall( + token, abi.encodeWithSelector(ERC20.decimals.selector), abi.encode(decimals) + ); } - } else { - // NOTE: this might not be a critical error. It could just mean that - // the token contract was compiled against a different EVM version than what the forge script is running on. - // In this case, it's the responsibility of the caller to ensure that the provided decimals are correct - // and that the token contract is valid. - // The best way to ensure that is by calling this script with the queried token decimals (which is what the NTT CLI does). - console.log( - "Failed to query token decimals. Proceeding with provided decimals.", decimals - ); - // the NTT manager initialiser calls the token contract to get the - // decimals as well. We're just going to mock that call to return the provided decimals. - // This is a bit of a hack, but in the worst case (i.e. if the token contract is actually broken), the - // NTT manager initialiser will fail anyway. - vm.mockCall( - token, abi.encodeWithSelector(ERC20.decimals.selector), abi.encode(decimals) - ); } - uint16 chainId = wh.chainId(); - - console.log("Chain ID: ", chainId); - - uint256 scale = - decimals > TRIMMED_DECIMALS ? uint256(10 ** (decimals - TRIMMED_DECIMALS)) : 1; - - DeploymentParams memory params = DeploymentParams({ - token: token, - mode: mode, - wormholeChainId: chainId, - rateLimitDuration: 86400, - shouldSkipRatelimiter: false, - wormholeCoreBridge: wormhole, - wormholeRelayerAddr: wormholeRelayer, - specialRelayerAddr: specialRelayer, - consistencyLevel: 202, - gasLimit: 500000, - // the trimming will trim this number to uint64.max - outboundLimit: uint256(type(uint64).max) * scale - }); + DeploymentParams memory params; + { + uint16 chainId = wh.chainId(); + + console.log("Chain ID: ", chainId); + + uint256 scale = + decimals > TRIMMED_DECIMALS ? uint256(10 ** (decimals - TRIMMED_DECIMALS)) : 1; + + params = DeploymentParams({ + endpointAddr: endpoint, + executorAddr: executor, + token: token, + mode: mode, + wormholeChainId: chainId, + rateLimitDuration: 86400, + shouldSkipRatelimiter: false, + wormholeCoreBridge: wormhole, + wormholeRelayerAddr: wormholeRelayer, + specialRelayerAddr: specialRelayer, + consistencyLevel: 202, + gasLimit: 500000, + // the trimming will trim this number to uint64.max + outboundLimit: uint256(type(uint64).max) * scale + }); + } // Deploy NttManager. - address manager = deployNttManager(params); - - // Deploy Wormhole Transceiver. - address transceiver = deployWormholeTransceiver(params, manager); + { + address manager = deployNttManager(params); - // Configure NttManager. - configureNttManager( - manager, transceiver, params.outboundLimit, params.shouldSkipRatelimiter - ); + // Configure NttManager. + configureNttManager( + manager, transceiver, params.outboundLimit, params.shouldSkipRatelimiter + ); + } vm.stopBroadcast(); } @@ -105,6 +113,8 @@ contract DeployWormholeNtt is Script, DeployWormholeNttBase { bool shouldSkipRatelimiter = rateLimitDuration == 0; NttManager implementation = new NttManager( + address(nttManager.endpoint()), + address(nttManager.executor()), nttManager.token(), nttManager.mode(), nttManager.chainId(), diff --git a/evm/script/UpgradeNttManager.s.sol b/evm/script/UpgradeNttManager.s.sol index 2ae864624..e98204c7c 100644 --- a/evm/script/UpgradeNttManager.s.sol +++ b/evm/script/UpgradeNttManager.s.sol @@ -13,6 +13,8 @@ import {ParseNttConfig} from "./helpers/ParseNttConfig.sol"; contract UpgradeNttManager is ParseNttConfig { struct DeploymentParams { + address endpoint; + address executor; address token; INttManager.Mode mode; uint16 wormholeChainId; @@ -26,6 +28,8 @@ contract UpgradeNttManager is ParseNttConfig { ) internal { // Deploy the Manager Implementation. NttManager implementation = new NttManager( + params.endpoint, + params.executor, params.token, params.mode, params.wormholeChainId, diff --git a/evm/script/UpgradeWormholeTransceiver.s.sol b/evm/script/UpgradeWormholeTransceiver.s.sol deleted file mode 100644 index e6783629a..000000000 --- a/evm/script/UpgradeWormholeTransceiver.s.sol +++ /dev/null @@ -1,85 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.8 <0.9.0; - -import {console2} from "forge-std/Script.sol"; - -import "../src/interfaces/IWormholeTransceiver.sol"; -import "../src/interfaces/ITransceiver.sol"; -import "../src/interfaces/INttManager.sol"; - -import {WormholeTransceiver} from "../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; -import {ERC1967Proxy} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - -import {ParseNttConfig} from "./helpers/ParseNttConfig.sol"; - -contract UpgradeWormholeTransceiver is ParseNttConfig { - struct DeploymentParams { - uint16 wormholeChainId; - address wormholeCoreBridge; - address wormholeRelayerAddr; - address specialRelayerAddr; - uint8 consistencyLevel; - uint256 gasLimit; - uint256 outboundLimit; - } - - // The minimum gas limit to verify a message on mainnet. If you're worried about saving - // gas on testnet, pick up the phone and start dialing! - uint256 constant MIN_WORMHOLE_GAS_LIMIT = 150000; - - function upgradeWormholeTransceiver( - IWormholeTransceiver wormholeTransceiverProxy, - DeploymentParams memory params, - address nttManager - ) internal { - // Deploy the Wormhole Transceiver. - WormholeTransceiver implementation = new WormholeTransceiver( - nttManager, - params.wormholeCoreBridge, - params.wormholeRelayerAddr, - params.specialRelayerAddr, - params.consistencyLevel, - params.gasLimit - ); - - console2.log("WormholeTransceiver Implementation deployed at: ", address(implementation)); - - // Upgrade the proxy. - ITransceiver(address(wormholeTransceiverProxy)).upgrade(address(implementation)); - } - - function _readEnvVariables() internal view returns (DeploymentParams memory params) { - // Chain ID. - params.wormholeChainId = uint16(vm.envUint("RELEASE_WORMHOLE_CHAIN_ID")); - require(params.wormholeChainId != 0, "Invalid chain ID"); - - // Wormhole Core Bridge address. - params.wormholeCoreBridge = vm.envAddress("RELEASE_CORE_BRIDGE_ADDRESS"); - require(params.wormholeCoreBridge != address(0), "Invalid wormhole core bridge address"); - - // Wormhole relayer, special relayer, consistency level. - params.wormholeRelayerAddr = vm.envAddress("RELEASE_WORMHOLE_RELAYER_ADDRESS"); - params.specialRelayerAddr = vm.envAddress("RELEASE_SPECIAL_RELAYER_ADDRESS"); - params.consistencyLevel = uint8(vm.envUint("RELEASE_CONSISTENCY_LEVEL")); - - params.gasLimit = vm.envUint("RELEASE_GAS_LIMIT"); - require(params.gasLimit >= MIN_WORMHOLE_GAS_LIMIT, "Invalid gas limit"); - - // Outbound rate limiter limit. - params.outboundLimit = vm.envUint("RELEASE_OUTBOUND_LIMIT"); - } - - function run() public { - vm.startBroadcast(); - - // Sanity check deployment parameters. - DeploymentParams memory params = _readEnvVariables(); - (, INttManager nttManager, IWormholeTransceiver wormholeTransceiver) = - _parseAndValidateConfigFile(params.wormholeChainId); - - // Upgrade WormholeTransceiver. - upgradeWormholeTransceiver(wormholeTransceiver, params, address(nttManager)); - - vm.stopBroadcast(); - } -} diff --git a/evm/script/helpers/DeployWormholeNttBase.sol b/evm/script/helpers/DeployWormholeNttBase.sol index 848b3cfda..d08157295 100644 --- a/evm/script/helpers/DeployWormholeNttBase.sol +++ b/evm/script/helpers/DeployWormholeNttBase.sol @@ -5,11 +5,8 @@ import {console2} from "forge-std/Script.sol"; import {ParseNttConfig} from "./ParseNttConfig.sol"; import "../../src/interfaces/IManagerBase.sol"; import "../../src/interfaces/INttManager.sol"; -import "../../src/interfaces/IWormholeTransceiver.sol"; import {NttManager} from "../../src/NttManager/NttManager.sol"; -import {WormholeTransceiver} from - "../../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; import {ERC1967Proxy} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; contract DeployWormholeNttBase is ParseNttConfig { @@ -22,6 +19,8 @@ contract DeployWormholeNttBase is ParseNttConfig { address wormholeCoreBridge; address wormholeRelayerAddr; address specialRelayerAddr; + address endpointAddr; + address executorAddr; uint8 consistencyLevel; uint256 gasLimit; uint256 outboundLimit; @@ -36,6 +35,8 @@ contract DeployWormholeNttBase is ParseNttConfig { ) internal returns (address) { // Deploy the Manager Implementation. NttManager implementation = new NttManager( + params.endpointAddr, + params.executorAddr, params.token, params.mode, params.wormholeChainId, @@ -54,30 +55,6 @@ contract DeployWormholeNttBase is ParseNttConfig { return address(nttManagerProxy); } - function deployWormholeTransceiver( - DeploymentParams memory params, - address nttManager - ) public returns (address) { - // Deploy the Wormhole Transceiver. - WormholeTransceiver implementation = new WormholeTransceiver( - nttManager, - params.wormholeCoreBridge, - params.wormholeRelayerAddr, - params.specialRelayerAddr, - params.consistencyLevel, - params.gasLimit - ); - - WormholeTransceiver transceiverProxy = - WormholeTransceiver(address(new ERC1967Proxy(address(implementation), ""))); - - transceiverProxy.initialize(); - - console2.log("WormholeTransceiver:", address(transceiverProxy)); - - return address(transceiverProxy); - } - function configureNttManager( address nttManager, address transceiver, @@ -92,9 +69,10 @@ contract DeployWormholeNttBase is ParseNttConfig { console2.log("Outbound rate limit set on NttManager: ", outboundLimit); } - // Hardcoded to one since these scripts handle Wormhole-only deployments. - INttManager(nttManager).setThreshold(1); - console2.log("Threshold set on NttManager: %d", uint256(1)); + // TODO: Need to enable sending and receiving and set the threshold for the destination chains. + // // Hardcoded to one since these scripts handle Wormhole-only deployments. + // INttManager(nttManager).setThreshold(1); + // console2.log("Threshold set on NttManager: %d", uint256(1)); } function _readEnvVariables() internal view returns (DeploymentParams memory params) { diff --git a/evm/script/helpers/ParseNttConfig.sol b/evm/script/helpers/ParseNttConfig.sol index 78739f10e..177fe2a05 100644 --- a/evm/script/helpers/ParseNttConfig.sol +++ b/evm/script/helpers/ParseNttConfig.sol @@ -5,7 +5,7 @@ import {Script, console2} from "forge-std/Script.sol"; import {stdJson} from "forge-std/StdJson.sol"; import "../../src/interfaces/INttManager.sol"; -import "../../src/interfaces/IWormholeTransceiver.sol"; +import "example-messaging-endpoint/evm/src/interfaces/IAdapter.sol"; contract ParseNttConfig is Script { using stdJson for string; @@ -15,6 +15,7 @@ contract ParseNttConfig is Script { struct ChainConfig { uint16 chainId; uint8 decimals; + uint128 gasLimit; uint256 inboundLimit; bool isEvmChain; bool isSpecialRelayingEnabled; @@ -47,11 +48,7 @@ contract ParseNttConfig is Script { uint16 wormholeChainId ) internal - returns ( - ChainConfig[] memory config, - INttManager nttManager, - IWormholeTransceiver wormholeTransceiver - ) + returns (ChainConfig[] memory config, INttManager nttManager, IAdapter wormholeTransceiver) { string memory root = vm.projectRoot(); string memory path = string.concat(root, "/cfg/WormholeNttConfig.json"); @@ -83,8 +80,7 @@ contract ParseNttConfig is Script { // Set the contract addresses for this chain. if (config[i].chainId == wormholeChainId) { nttManager = INttManager(fromUniversalAddress(config[i].nttManager)); - wormholeTransceiver = - IWormholeTransceiver(fromUniversalAddress(config[i].wormholeTransceiver)); + wormholeTransceiver = IAdapter(fromUniversalAddress(config[i].wormholeTransceiver)); } } } diff --git a/evm/src/NttManager/ManagerBase.sol b/evm/src/NttManager/ManagerBase.sol index 8a336fccc..3e6228df6 100644 --- a/evm/src/NttManager/ManagerBase.sol +++ b/evm/src/NttManager/ManagerBase.sol @@ -4,6 +4,10 @@ pragma solidity >=0.8.8 <0.9.0; import "wormhole-solidity-sdk/Utils.sol"; import "wormhole-solidity-sdk/libraries/BytesParsing.sol"; +import "example-messaging-endpoint/evm/src/interfaces/IEndpointAdmin.sol"; +import "example-messaging-endpoint/evm/src/interfaces/IEndpointIntegrator.sol"; +import "example-messaging-executor/evm/src/interfaces/IExecutor.sol"; + import "../libraries/external/OwnableUpgradeable.sol"; import "../libraries/external/ReentrancyGuardUpgradeable.sol"; import "../libraries/TransceiverStructs.sol"; @@ -11,14 +15,10 @@ import "../libraries/TransceiverHelpers.sol"; import "../libraries/PausableOwnable.sol"; import "../libraries/Implementation.sol"; -import "../interfaces/ITransceiver.sol"; import "../interfaces/IManagerBase.sol"; -import "./TransceiverRegistry.sol"; - abstract contract ManagerBase is IManagerBase, - TransceiverRegistry, PausableOwnable, ReentrancyGuardUpgradeable, Implementation @@ -40,9 +40,17 @@ abstract contract ManagerBase is /// This chain ID is formatted based on standardized chain IDs, e.g. Ethereum mainnet is 1, Sepolia is 11155111, etc. uint256 immutable evmChainId; + // The modular messaging endpoint used for sending and receiving attestations. + IEndpointIntegrator public immutable endpoint; + + // The executor used for publishing message payloads. + IExecutor public immutable executor; + // =============== Setup ================================================================= - constructor(address _token, Mode _mode, uint16 _chainId) { + constructor(address _endpoint, address _executor, address _token, Mode _mode, uint16 _chainId) { + endpoint = IEndpointIntegrator(_endpoint); + executor = IExecutor(_executor); token = _token; mode = _mode; chainId = _chainId; @@ -53,41 +61,40 @@ abstract contract ManagerBase is function _migrate() internal virtual override { _checkThresholdInvariants(); - _checkTransceiversInvariants(); } // =============== Storage ============================================================== - bytes32 private constant MESSAGE_ATTESTATIONS_SLOT = - bytes32(uint256(keccak256("ntt.messageAttestations")) - 1); - bytes32 private constant MESSAGE_SEQUENCE_SLOT = bytes32(uint256(keccak256("ntt.messageSequence")) - 1); bytes32 private constant THRESHOLD_SLOT = bytes32(uint256(keccak256("ntt.threshold")) - 1); + bytes32 private constant RECV_ENABLED_CHAINS_SLOT = + bytes32(uint256(keccak256("ntt.recvEnabledChains")) - 1); + // =============== Storage Getters/Setters ============================================== - function _getThresholdStorage() private pure returns (_Threshold storage $) { + function _getThresholdStorage() + private + pure + returns (mapping(uint16 => _Threshold) storage $) + { uint256 slot = uint256(THRESHOLD_SLOT); assembly ("memory-safe") { $.slot := slot } } - function _getMessageAttestationsStorage() - internal - pure - returns (mapping(bytes32 => AttestationInfo) storage $) - { - uint256 slot = uint256(MESSAGE_ATTESTATIONS_SLOT); + function _getMessageSequenceStorage() internal pure returns (_Sequence storage $) { + uint256 slot = uint256(MESSAGE_SEQUENCE_SLOT); assembly ("memory-safe") { $.slot := slot } } - function _getMessageSequenceStorage() internal pure returns (_Sequence storage $) { - uint256 slot = uint256(MESSAGE_SEQUENCE_SLOT); + function _getChainsEnabledForReceiveStorage() internal pure returns (uint16[] storage $) { + uint256 slot = uint256(RECV_ENABLED_CHAINS_SLOT); assembly ("memory-safe") { $.slot := slot } @@ -99,167 +106,12 @@ abstract contract ManagerBase is function quoteDeliveryPrice( uint16 recipientChain, bytes memory transceiverInstructions - ) public view returns (uint256[] memory, uint256) { - address[] memory enabledTransceivers = _getEnabledTransceiversStorage(); - - TransceiverStructs.TransceiverInstruction[] memory instructions = TransceiverStructs - .parseTransceiverInstructions(transceiverInstructions, enabledTransceivers.length); - - return _quoteDeliveryPrice(recipientChain, instructions, enabledTransceivers); + ) public view returns (uint256) { + return endpoint.quoteDeliveryPrice(recipientChain, transceiverInstructions); } // =============== Internal Logic =========================================================== - function _quoteDeliveryPrice( - uint16 recipientChain, - TransceiverStructs.TransceiverInstruction[] memory transceiverInstructions, - address[] memory enabledTransceivers - ) internal view returns (uint256[] memory, uint256) { - uint256 numEnabledTransceivers = enabledTransceivers.length; - mapping(address => TransceiverInfo) storage transceiverInfos = _getTransceiverInfosStorage(); - - uint256[] memory priceQuotes = new uint256[](numEnabledTransceivers); - uint256 totalPriceQuote = 0; - for (uint256 i = 0; i < numEnabledTransceivers; i++) { - address transceiverAddr = enabledTransceivers[i]; - uint8 registeredTransceiverIndex = transceiverInfos[transceiverAddr].index; - uint256 transceiverPriceQuote = ITransceiver(transceiverAddr).quoteDeliveryPrice( - recipientChain, transceiverInstructions[registeredTransceiverIndex] - ); - priceQuotes[i] = transceiverPriceQuote; - totalPriceQuote += transceiverPriceQuote; - } - return (priceQuotes, totalPriceQuote); - } - - function _recordTransceiverAttestation( - uint16 sourceChainId, - TransceiverStructs.NttManagerMessage memory payload - ) internal returns (bytes32) { - bytes32 nttManagerMessageHash = - TransceiverStructs.nttManagerMessageDigest(sourceChainId, payload); - - // set the attested flag for this transceiver. - // NOTE: Attestation is idempotent (bitwise or 1), but we revert - // anyway to ensure that the client does not continue to initiate calls - // to receive the same message through the same transceiver. - if ( - transceiverAttestedToMessage( - nttManagerMessageHash, _getTransceiverInfosStorage()[msg.sender].index - ) - ) { - revert TransceiverAlreadyAttestedToMessage(nttManagerMessageHash); - } - _setTransceiverAttestedToMessage(nttManagerMessageHash, msg.sender); - - return nttManagerMessageHash; - } - - function _isMessageExecuted( - uint16 sourceChainId, - bytes32 sourceNttManagerAddress, - TransceiverStructs.NttManagerMessage memory message - ) internal returns (bytes32, bool) { - bytes32 digest = TransceiverStructs.nttManagerMessageDigest(sourceChainId, message); - - if (!isMessageApproved(digest)) { - revert MessageNotApproved(digest); - } - - bool msgAlreadyExecuted = _replayProtect(digest); - if (msgAlreadyExecuted) { - // end execution early to mitigate the possibility of race conditions from transceivers - // attempting to deliver the same message when (threshold < number of transceiver messages) - // notify client (off-chain process) so they don't attempt redundant msg delivery - emit MessageAlreadyExecuted(sourceNttManagerAddress, digest); - return (bytes32(0), msgAlreadyExecuted); - } - - return (digest, msgAlreadyExecuted); - } - - function _sendMessageToTransceivers( - uint16 recipientChain, - bytes32 refundAddress, - bytes32 peerAddress, - uint256[] memory priceQuotes, - TransceiverStructs.TransceiverInstruction[] memory transceiverInstructions, - address[] memory enabledTransceivers, - bytes memory nttManagerMessage - ) internal { - uint256 numEnabledTransceivers = enabledTransceivers.length; - mapping(address => TransceiverInfo) storage transceiverInfos = _getTransceiverInfosStorage(); - - if (peerAddress == bytes32(0)) { - revert PeerNotRegistered(recipientChain); - } - - // push onto the stack again to avoid stack too deep error - bytes32 refundRecipient = refundAddress; - - // call into transceiver contracts to send the message - for (uint256 i = 0; i < numEnabledTransceivers; i++) { - address transceiverAddr = enabledTransceivers[i]; - - // send it to the recipient nttManager based on the chain - ITransceiver(transceiverAddr).sendMessage{value: priceQuotes[i]}( - recipientChain, - transceiverInstructions[transceiverInfos[transceiverAddr].index], - nttManagerMessage, - peerAddress, - refundRecipient - ); - } - } - - function _prepareForTransfer( - uint16 recipientChain, - bytes memory transceiverInstructions - ) - internal - returns ( - address[] memory, - TransceiverStructs.TransceiverInstruction[] memory, - uint256[] memory, - uint256 - ) - { - // cache enabled transceivers to avoid multiple storage reads - address[] memory enabledTransceivers = _getEnabledTransceiversStorage(); - - TransceiverStructs.TransceiverInstruction[] memory instructions; - - { - uint256 numRegisteredTransceivers = _getRegisteredTransceiversStorage().length; - uint256 numEnabledTransceivers = enabledTransceivers.length; - - if (numEnabledTransceivers == 0) { - revert NoEnabledTransceivers(); - } - - instructions = TransceiverStructs.parseTransceiverInstructions( - transceiverInstructions, numRegisteredTransceivers - ); - } - - (uint256[] memory priceQuotes, uint256 totalPriceQuote) = - _quoteDeliveryPrice(recipientChain, instructions, enabledTransceivers); - { - // check up front that msg.value will cover the delivery price - if (msg.value < totalPriceQuote) { - revert DeliveryPaymentTooLow(totalPriceQuote, msg.value); - } - - // refund user extra excess value from msg.value - uint256 excessValue = msg.value - totalPriceQuote; - if (excessValue > 0) { - _refundToSender(excessValue); - } - } - - return (enabledTransceivers, instructions, priceQuotes, totalPriceQuote); - } - function _refundToSender( uint256 refundAmount ) internal { @@ -280,16 +132,23 @@ abstract contract ManagerBase is } /// @inheritdoc IManagerBase - function getThreshold() public view returns (uint8) { - return _getThresholdStorage().num; + function getThreshold( + uint16 chain + ) public view returns (uint8) { + return _getThresholdStorage()[chain].num; } /// @inheritdoc IManagerBase function isMessageApproved( - bytes32 digest + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + UniversalAddress dstAddr, + bytes32 payloadHash ) public view returns (bool) { - uint8 threshold = getThreshold(); - return messageAttestations(digest) >= threshold && threshold > 0; + uint8 numAttested = messageAttestations(srcChain, srcAddr, sequence, dstAddr, payloadHash); + uint8 threshold = getThreshold(srcChain); + return (numAttested >= threshold); } /// @inheritdoc IManagerBase @@ -299,22 +158,42 @@ abstract contract ManagerBase is /// @inheritdoc IManagerBase function isMessageExecuted( - bytes32 digest + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + UniversalAddress dstAddr, + bytes32 payloadHash ) public view returns (bool) { - return _getMessageAttestationsStorage()[digest].executed; + (,,, bool executed) = + endpoint.getMessageStatus(srcChain, srcAddr, sequence, dstAddr, payloadHash); + + return executed; } /// @inheritdoc IManagerBase - function transceiverAttestedToMessage(bytes32 digest, uint8 index) public view returns (bool) { - return - _getMessageAttestationsStorage()[digest].attestedTransceivers & uint64(1 << index) > 0; + function transceiverAttestedToMessage( + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + UniversalAddress dstAddr, + bytes32 payloadHash, + uint8 index + ) public view returns (bool) { + (, uint128 attested,,) = + endpoint.getMessageStatus(srcChain, srcAddr, sequence, dstAddr, payloadHash); + + return attested & uint64(1 << index) > 0; } /// @inheritdoc IManagerBase function messageAttestations( - bytes32 digest + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + UniversalAddress dstAddr, + bytes32 payloadHash ) public view returns (uint8 count) { - return countSetBits(_getMessageAttestations(digest)); + (,, count,) = endpoint.getMessageStatus(srcChain, srcAddr, sequence, dstAddr, payloadHash); } // =============== Admin ============================================================== @@ -339,23 +218,28 @@ abstract contract ManagerBase is function transferOwnership( address newOwner ) public override onlyOwner { + // TODO: Just delete this function and let the Ownable one be called directly? super.transferOwnership(newOwner); - // loop through all the registered transceivers and set the new owner of each transceiver to the newOwner - address[] storage _registeredTransceivers = _getRegisteredTransceiversStorage(); - _checkRegisteredTransceiversInvariants(); - - for (uint256 i = 0; i < _registeredTransceivers.length; i++) { - ITransceiver(_registeredTransceivers[i]).transferTransceiverOwnership(newOwner); - } } /// @inheritdoc IManagerBase function setTransceiver( address transceiver ) external onlyOwner { - _setTransceiver(transceiver); + uint8 index = IEndpointAdmin(address(endpoint)).addAdapter(address(this), transceiver); + emit TransceiverAdded(transceiver, index); + } + + /// @inheritdoc IManagerBase + function enableSendTransceiver(uint16 chain, address transceiver) external { + IEndpointAdmin(address(endpoint)).enableSendAdapter(address(this), chain, transceiver); + } + + /// @inheritdoc IManagerBase + function enableRecvTransceiver(uint16 chain, address transceiver) external { + IEndpointAdmin(address(endpoint)).enableRecvAdapter(address(this), chain, transceiver); - _Threshold storage _threshold = _getThresholdStorage(); + _Threshold storage _threshold = _getThresholdStorage()[chain]; // We do not automatically increase the threshold here. // Automatically increasing the threshold can result in a scenario // where in-flight messages can't be redeemed. @@ -371,97 +255,95 @@ abstract contract ManagerBase is // However if the threshold is 0 (the initial case) we do increment to 1. if (_threshold.num == 0) { _threshold.num = 1; + _addChainEnabledForReceive(chain); } - emit TransceiverAdded(transceiver, _getNumTransceiversStorage().enabled, _threshold.num); + _checkThresholdInvariantsForChain(chain); + } - _checkThresholdInvariants(); + /// @inheritdoc IManagerBase + function disableSendTransceiver(uint16 chain, address transceiver) external { + IEndpointAdmin(address(endpoint)).disableSendAdapter(address(this), chain, transceiver); } /// @inheritdoc IManagerBase - function removeTransceiver( - address transceiver - ) external onlyOwner { - _removeTransceiver(transceiver); + function disableRecvTransceiver(uint16 chain, address transceiver) external { + IEndpointAdmin(address(endpoint)).disableRecvAdapter(address(this), chain, transceiver); - _Threshold storage _threshold = _getThresholdStorage(); - uint8 numEnabledTransceivers = _getNumTransceiversStorage().enabled; + _Threshold storage _threshold = _getThresholdStorage()[chain]; + uint8 numEnabled = IEndpointAdmin(address(endpoint)).getNumEnabledRecvAdaptersForChain( + address(this), chain + ); - if (numEnabledTransceivers < _threshold.num) { - _threshold.num = numEnabledTransceivers; + // TODO: Should we do this or just let _checkThresholdInvariantsForChain revert and make them reduce the threshold before disabling the chain? + if (_threshold.num > numEnabled) { + uint8 oldThreshold = _threshold.num; + _threshold.num = numEnabled; + emit ThresholdChanged(chainId, oldThreshold, numEnabled); } - emit TransceiverRemoved(transceiver, _threshold.num); + if (numEnabled == 0) { + _removeChainEnabledForReceive(chain); + } - _checkThresholdInvariants(); + _checkThresholdInvariantsForChain(chain); } /// @inheritdoc IManagerBase - function setThreshold( - uint8 threshold - ) external onlyOwner { + function setThreshold(uint16 chain, uint8 threshold) external onlyOwner { if (threshold == 0) { revert ZeroThreshold(); } - _Threshold storage _threshold = _getThresholdStorage(); + _Threshold storage _threshold = _getThresholdStorage()[chain]; uint8 oldThreshold = _threshold.num; _threshold.num = threshold; - _checkThresholdInvariants(); - - emit ThresholdChanged(oldThreshold, threshold); + _addChainEnabledForReceive(chain); + _checkThresholdInvariantsForChain(chain); + emit ThresholdChanged(chainId, oldThreshold, threshold); } // =============== Internal ============================================================== - function _setTransceiverAttestedToMessage(bytes32 digest, uint8 index) internal { - _getMessageAttestationsStorage()[digest].attestedTransceivers |= uint64(1 << index); - } - - function _setTransceiverAttestedToMessage(bytes32 digest, address transceiver) internal { - _setTransceiverAttestedToMessage(digest, _getTransceiverInfosStorage()[transceiver].index); - - emit MessageAttestedTo( - digest, transceiver, _getTransceiverInfosStorage()[transceiver].index - ); - } - - /// @dev Returns the bitmap of attestations from enabled transceivers for a given message. - function _getMessageAttestations( - bytes32 digest - ) internal view returns (uint64) { - uint64 enabledTransceiverBitmap = _getEnabledTransceiversBitmap(); - return - _getMessageAttestationsStorage()[digest].attestedTransceivers & enabledTransceiverBitmap; - } - - function _getEnabledTransceiverAttestedToMessage( - bytes32 digest, - uint8 index - ) internal view returns (bool) { - return _getMessageAttestations(digest) & uint64(1 << index) != 0; + function _useMessageSequence() internal returns (uint64 currentSequence) { + currentSequence = _getMessageSequenceStorage().num; + _getMessageSequenceStorage().num++; } - // @dev Mark a message as executed. - // This function will retuns `true` if the message has already been executed. - function _replayProtect( - bytes32 digest - ) internal returns (bool) { - // check if this message has already been executed - if (isMessageExecuted(digest)) { - return true; + /// @dev It's not an error if the chain is not in the list. + function _removeChainEnabledForReceive( + uint16 chain + ) internal { + uint16[] storage chains = _getChainsEnabledForReceiveStorage(); + uint256 len = chains.length; + for (uint256 idx = 0; (idx < len);) { + if (chains[idx] == chain) { + chains[idx] = chains[len - 1]; + chains.pop(); + return; + } + unchecked { + ++idx; + } } - - // mark this message as executed - _getMessageAttestationsStorage()[digest].executed = true; - - return false; } - function _useMessageSequence() internal returns (uint64 currentSequence) { - currentSequence = _getMessageSequenceStorage().num; - _getMessageSequenceStorage().num++; + /// @dev It's not an error if the chain is already in the list. + function _addChainEnabledForReceive( + uint16 chain + ) internal { + uint16[] storage chains = _getChainsEnabledForReceiveStorage(); + uint256 len = chains.length; + for (uint256 idx = 0; (idx < len);) { + if (chains[idx] == chain) { + return; + } + unchecked { + ++idx; + } + } + chains.push(chain); } /// ============== Invariants ============================================= @@ -471,26 +353,35 @@ abstract contract ManagerBase is assert(this.token() == token); assert(this.mode() == mode); assert(this.chainId() == chainId); + assert(this.endpoint() == endpoint); } - function _checkRegisteredTransceiversInvariants() internal view { - if (_getRegisteredTransceiversStorage().length != _getNumTransceiversStorage().registered) { - revert RetrievedIncorrectRegisteredTransceivers( - _getRegisteredTransceiversStorage().length, _getNumTransceiversStorage().registered - ); + function _checkThresholdInvariants() internal view { + uint16[] storage chains = _getChainsEnabledForReceiveStorage(); + uint256 len = chains.length; + for (uint256 idx = 0; (idx < len);) { + _checkThresholdInvariantsForChain(chains[idx]); + unchecked { + ++idx; + } } } - function _checkThresholdInvariants() internal view { - uint8 threshold = _getThresholdStorage().num; - _NumTransceivers memory numTransceivers = _getNumTransceiversStorage(); + /// @dev This can be called directly when we are only manipulating a single chain. Otherwise use _checkThresholdInvariants. + function _checkThresholdInvariantsForChain( + uint16 chain + ) internal view { + uint8 threshold = _getThresholdStorage()[chain].num; + uint8 numEnabled = IEndpointAdmin(address(endpoint)).getNumEnabledRecvAdaptersForChain( + address(this), chain + ); // invariant: threshold <= enabledTransceivers.length - if (threshold > numTransceivers.enabled) { - revert ThresholdTooHigh(threshold, numTransceivers.enabled); + if (threshold > numEnabled) { + revert ThresholdTooHigh(threshold, numEnabled); } - if (numTransceivers.registered > 0) { + if (numEnabled > 0) { if (threshold == 0) { revert ZeroThreshold(); } diff --git a/evm/src/NttManager/NttManager.sol b/evm/src/NttManager/NttManager.sol index 7ac021b91..17ee9bb17 100644 --- a/evm/src/NttManager/NttManager.sol +++ b/evm/src/NttManager/NttManager.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.8 <0.9.0; +import "example-messaging-executor/evm/src/libraries/ExecutorMessages.sol"; +import "example-messaging-executor/evm/src/libraries/RelayInstructions.sol"; import "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol"; import "openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol"; import "openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol"; @@ -11,7 +13,6 @@ import "../libraries/RateLimiter.sol"; import "../interfaces/INttManager.sol"; import "../interfaces/INttToken.sol"; -import "../interfaces/ITransceiver.sol"; import {ManagerBase} from "./ManagerBase.sol"; @@ -46,12 +47,17 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { // =============== Setup ================================================================= constructor( + address _endpoint, + address _executor, address _token, Mode _mode, uint16 _chainId, uint64 _rateLimitDuration, bool _skipRateLimiting - ) RateLimiter(_rateLimitDuration, _skipRateLimiting) ManagerBase(_token, _mode, _chainId) {} + ) + RateLimiter(_rateLimitDuration, _skipRateLimiting) + ManagerBase(_endpoint, _executor, _token, _mode, _chainId) + {} function __NttManager_init() internal onlyInitializing { // check if the owner is the deployer of this contract @@ -64,12 +70,14 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { __PausedOwnable_init(msg.sender, msg.sender); __ReentrancyGuard_init(); _setOutboundLimit(TrimmedAmountLib.max(tokenDecimals())); + + // Register the proxy as the integrator and the admin. + endpoint.register(address(this)); } function _initialize() internal virtual override { __NttManager_init(); _checkThresholdInvariants(); - _checkTransceiversInvariants(); } // =============== Storage ============================================================== @@ -105,6 +113,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { uint16 peerChainId, bytes32 peerContract, uint8 decimals, + uint128 gasLimit, uint256 inboundLimit ) public onlyOwner { if (peerChainId == 0) { @@ -116,6 +125,9 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { if (decimals == 0) { revert InvalidPeerDecimals(); } + if (gasLimit == 0) { + revert InvalidGasLimitZero(peerChainId); + } if (peerChainId == chainId) { revert InvalidPeerSameChainId(); } @@ -124,6 +136,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { _getPeersStorage()[peerChainId].peerAddress = peerContract; _getPeersStorage()[peerChainId].tokenDecimals = decimals; + _getPeersStorage()[peerChainId].gasLimit = gasLimit; uint8 toDecimals = tokenDecimals(); _setInboundLimit(inboundLimit.trim(toDecimals, toDecimals), peerChainId); @@ -133,6 +146,17 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { ); } + /// @inheritdoc INttManager + function setGasLimit(uint16 peerChainId, uint128 gasLimit) external onlyOwner { + if (gasLimit == 0) { + revert InvalidGasLimitZero(peerChainId); + } + if (_getPeersStorage()[peerChainId].peerAddress == bytes32(0)) { + revert InvalidPeerZeroAddress(); + } + _getPeersStorage()[peerChainId].gasLimit = gasLimit; + } + /// @inheritdoc INttManager function setOutboundLimit( uint256 limit @@ -161,10 +185,21 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { function transfer( uint256 amount, uint16 recipientChain, - bytes32 recipient + bytes32 recipient, + bytes calldata executorQuote, + bytes calldata relayInstructions, + bytes calldata transceiverInstructions ) external payable nonReentrant whenNotPaused returns (uint64) { - return - _transferEntryPoint(amount, recipientChain, recipient, recipient, false, new bytes(1)); + return _transferEntryPoint( + amount, + recipientChain, + recipient, + recipient, + false, + executorQuote, + relayInstructions, + transceiverInstructions + ); } /// @inheritdoc INttManager @@ -174,43 +209,60 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { bytes32 recipient, bytes32 refundAddress, bool shouldQueue, - bytes memory transceiverInstructions + bytes calldata executorQuote, + bytes calldata relayInstructions, + bytes calldata transceiverInstructions ) external payable nonReentrant whenNotPaused returns (uint64) { return _transferEntryPoint( - amount, recipientChain, recipient, refundAddress, shouldQueue, transceiverInstructions + amount, + recipientChain, + recipient, + refundAddress, + shouldQueue, + executorQuote, + relayInstructions, + transceiverInstructions ); } /// @inheritdoc INttManager - function attestationReceived( + function executeMsg( uint16 sourceChainId, - bytes32 sourceNttManagerAddress, - TransceiverStructs.NttManagerMessage memory payload - ) external onlyTransceiver whenNotPaused { - _verifyPeer(sourceChainId, sourceNttManagerAddress); + UniversalAddress sourceNttManagerAddress, + uint64 epSeq, + bytes memory payload + ) public whenNotPaused { + // We should only except messages from a peer. + bytes32 peerAddress = _getPeersStorage()[sourceChainId].peerAddress; + if (sourceNttManagerAddress != UniversalAddressLibrary.fromBytes32(peerAddress)) { + revert InvalidPeer( + sourceChainId, UniversalAddressLibrary.toBytes32(sourceNttManagerAddress) + ); + } - // Compute manager message digest and record transceiver attestation. - bytes32 nttManagerMessageHash = _recordTransceiverAttestation(sourceChainId, payload); + // The endpoint uses the payload hash, not the actual payload. + bytes32 payloadHash = keccak256(payload); - if (isMessageApproved(nttManagerMessageHash)) { - executeMsg(sourceChainId, sourceNttManagerAddress, payload); - } - } + // The endpoint does replay protection and verifies that there has been at least one attestation. + (,, uint8 numAttested) = + endpoint.recvMessage(sourceChainId, sourceNttManagerAddress, epSeq, payloadHash); - /// @inheritdoc INttManager - function executeMsg( - uint16 sourceChainId, - bytes32 sourceNttManagerAddress, - TransceiverStructs.NttManagerMessage memory message - ) public whenNotPaused { - (bytes32 digest, bool alreadyExecuted) = - _isMessageExecuted(sourceChainId, sourceNttManagerAddress, message); + uint8 threshold = getThreshold(sourceChainId); - if (alreadyExecuted) { - return; + if (numAttested < threshold) { + revert ThresholdNotMet(threshold, numAttested); } - _handleMsg(sourceChainId, sourceNttManagerAddress, message, digest); + TransceiverStructs.NttManagerMessage memory message = + TransceiverStructs.parseNttManagerMessage(payload); + + bytes32 digest = TransceiverStructs.nttManagerMessageDigest(sourceChainId, message); + _handleMsg( + sourceChainId, + UniversalAddressLibrary.toBytes32(sourceNttManagerAddress), + message, + digest + ); } /// @dev Override this function to handle custom NttManager payloads. @@ -346,6 +398,8 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { queuedTransfer.recipient, queuedTransfer.refundAddress, queuedTransfer.sender, + queuedTransfer.executorQuote, + queuedTransfer.relayInstructions, queuedTransfer.transceiverInstructions ); } @@ -382,6 +436,8 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { bytes32 recipient, bytes32 refundAddress, bool shouldQueue, + bytes memory executorQuote, + bytes memory relayInstructions, bytes memory transceiverInstructions ) internal returns (uint64) { if (amount == 0) { @@ -446,6 +502,8 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { recipient, refundAddress, shouldQueue, + executorQuote, + relayInstructions, transceiverInstructions, trimmedAmount, sequence @@ -462,6 +520,8 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { recipient, refundAddress, msg.sender, + executorQuote, + relayInstructions, transceiverInstructions ); } @@ -472,6 +532,8 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { bytes32 recipient, bytes32 refundAddress, bool shouldQueue, + bytes memory executorQuote, + bytes memory relayInstructions, bytes memory transceiverInstructions, TrimmedAmount trimmedAmount, uint64 sequence @@ -500,13 +562,15 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { recipient, refundAddress, msg.sender, + executorQuote, + relayInstructions, transceiverInstructions ); // refund price quote back to sender _refundToSender(msg.value); - // return that the transfer has been enqued + // return that the transfer has been enqueued return true; } @@ -525,66 +589,130 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { bytes32 recipient, bytes32 refundAddress, address sender, + bytes memory executorQuote, + bytes memory relayInstructions, bytes memory transceiverInstructions ) internal returns (uint64 msgSequence) { // verify chain has not forked checkFork(evmChainId); - ( - address[] memory enabledTransceivers, - TransceiverStructs.TransceiverInstruction[] memory instructions, - uint256[] memory priceQuotes, - uint256 totalPriceQuote - ) = _prepareForTransfer(recipientChain, transceiverInstructions); + // Compute the quote price and refund user excess value from msg.value + uint256 epTotalPriceQuote = quoteAndRefund(recipientChain, transceiverInstructions); - // push it on the stack again to avoid a stack too deep error - uint64 seq = sequence; - - TransceiverStructs.NativeTokenTransfer memory ntt = _prepareNativeTokenTransfer( - amount, recipient, recipientChain, seq, sender, refundAddress + return _transfer( + _TransferArgs({ + sequence: sequence, + amount: amount, + recipientChain: recipientChain, + recipient: recipient, + refundAddress: refundAddress, + sender: sender, + executorQuote: executorQuote, + relayInstructions: relayInstructions, + transceiverInstructions: transceiverInstructions, + epTotalPriceQuote: epTotalPriceQuote + }) ); + } - // construct the NttManagerMessage payload - bytes memory encodedNttManagerPayload = TransceiverStructs.encodeNttManagerMessage( - TransceiverStructs.NttManagerMessage( - bytes32(uint256(seq)), - toWormholeFormat(sender), - TransceiverStructs.encodeNativeTokenTransfer(ntt) - ) - ); + /// @dev Used to get around "stack too deep. + struct _TransferArgs { + uint64 sequence; + TrimmedAmount amount; + uint16 recipientChain; + bytes32 recipient; + bytes32 refundAddress; + address sender; + bytes executorQuote; + bytes relayInstructions; + bytes transceiverInstructions; + uint256 epTotalPriceQuote; + } - // push onto the stack again to avoid stack too deep error - uint16 destinationChain = recipientChain; + function _transfer( + _TransferArgs memory args + ) internal returns (uint64 msgSequence) { + NttManagerPeer storage peerData = _getPeersStorage()[args.recipientChain]; + bytes memory encodedNttManagerPayload = buildEncodedPayload(args); // send the message - _sendMessageToTransceivers( - recipientChain, - refundAddress, - _getPeersStorage()[destinationChain].peerAddress, - priceQuotes, - instructions, - enabledTransceivers, - encodedNttManagerPayload + bytes32 payloadHash = keccak256(encodedNttManagerPayload); + uint64 epSeqNo = endpoint.sendMessage{value: args.epTotalPriceQuote}( + args.recipientChain, + UniversalAddressLibrary.fromBytes32(peerData.peerAddress), + payloadHash, + UniversalAddressLibrary.toAddress( + UniversalAddressLibrary.fromBytes32(args.refundAddress) + ), + args.transceiverInstructions ); - // push it on the stack again to avoid a stack too deep error - TrimmedAmount amt = amount; - emit TransferSent( - recipient, - refundAddress, - amt.untrim(tokenDecimals()), - totalPriceQuote, - destinationChain, - seq + args.recipient, + args.refundAddress, + args.amount.untrim(tokenDecimals()), + args.epTotalPriceQuote, + args.recipientChain, + args.sequence, + payloadHash ); - emit TransferSent( - TransceiverStructs._nttManagerMessageDigest(chainId, encodedNttManagerPayload) + uint128 gasLimit = peerData.gasLimit; + if (gasLimit == 0) { + revert InvalidGasLimitZero(args.recipientChain); + } + + bytes memory relayInstructions = RelayInstructions.encodeGas(gasLimit, 0); + if (args.relayInstructions.length != 0) { + relayInstructions = abi.encodePacked(relayInstructions, args.relayInstructions); + } + + executor.requestExecution( + args.recipientChain, + peerData.peerAddress, + UniversalAddressLibrary.fromBytes32(args.refundAddress).toAddress(), + args.executorQuote, + ExecutorMessages.makeMMRequest( + chainId, address(this), epSeqNo, encodedNttManagerPayload + ), + relayInstructions ); // return the sequence number - return seq; + return args.sequence; + } + + function quoteAndRefund( + uint16 recipientChain, + bytes memory transceiverInstructions + ) internal returns (uint256 epTotalPriceQuote) { + epTotalPriceQuote = quoteDeliveryPrice(recipientChain, transceiverInstructions); + uint256 excessValue = msg.value - epTotalPriceQuote; + if (excessValue > 0) { + _refundToSender(excessValue); + } + } + + function buildEncodedPayload( + _TransferArgs memory args + ) internal returns (bytes memory encodedNttManagerPayload) { + TransceiverStructs.NativeTokenTransfer memory ntt = _prepareNativeTokenTransfer( + args.amount, + args.recipient, + args.recipientChain, + args.sequence, + args.sender, + args.refundAddress + ); + + // construct the NttManagerMessage payload + encodedNttManagerPayload = TransceiverStructs.encodeNttManagerMessage( + TransceiverStructs.NttManagerMessage( + bytes32(uint256(args.sequence)), + toWormholeFormat(args.sender), + TransceiverStructs.encodeNativeTokenTransfer(ntt) + ) + ); } /// @dev Override this function to provide an additional payload on the NativeTokenTransfer diff --git a/evm/src/NttManager/NttManagerNoRateLimiting.sol b/evm/src/NttManager/NttManagerNoRateLimiting.sol index 82358c5e8..1e9e8aae0 100644 --- a/evm/src/NttManager/NttManagerNoRateLimiting.sol +++ b/evm/src/NttManager/NttManagerNoRateLimiting.sol @@ -12,10 +12,12 @@ import "./NttManager.sol"; /// @dev All of the developer notes from `NttManager` apply here. contract NttManagerNoRateLimiting is NttManager { constructor( + address _endpoint, + address _executor, address _token, Mode _mode, uint16 _chainId - ) NttManager(_token, _mode, _chainId, 0, true) {} + ) NttManager(_endpoint, _executor, _token, _mode, _chainId, 0, true) {} // ==================== Override RateLimiter functions ========================= @@ -95,6 +97,8 @@ contract NttManagerNoRateLimiting is NttManager { bytes32, // recipient bytes32, // refundAddress bool, // shouldQueue + bytes memory, // executorQuote + bytes memory, // relayInstructions bytes memory, // transceiverInstructions TrimmedAmount, // trimmedAmount uint64 // sequence diff --git a/evm/src/NttManager/TransceiverRegistry.sol b/evm/src/NttManager/TransceiverRegistry.sol deleted file mode 100644 index d95f39b73..000000000 --- a/evm/src/NttManager/TransceiverRegistry.sol +++ /dev/null @@ -1,320 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.8 <0.9.0; - -/// @title TransceiverRegistry -/// @author Wormhole Project Contributors. -/// @notice This contract is responsible for handling the registration of Transceivers. -/// @dev This contract checks that a few critical invariants hold when transceivers are added or removed, -/// including: -/// 1. If a transceiver is not registered, it should be enabled. -/// 2. The value set in the bitmap of trannsceivers -/// should directly correspond to the whether the transceiver is enabled -abstract contract TransceiverRegistry { - constructor() { - _checkTransceiversInvariants(); - } - - /// @dev Information about registered transceivers. - struct TransceiverInfo { - // whether this transceiver is registered - bool registered; - // whether this transceiver is enabled - bool enabled; - uint8 index; - } - - /// @dev Bitmap encoding the enabled transceivers. - /// invariant: forall (i: uint8), enabledTransceiverBitmap & i == 1 <=> transceiverInfos[i].enabled - struct _EnabledTransceiverBitmap { - uint64 bitmap; - } - - /// @dev Total number of registered transceivers. This number can only increase. - /// invariant: numRegisteredTransceivers <= MAX_TRANSCEIVERS - /// invariant: forall (i: uint8), - /// i < numRegisteredTransceivers <=> exists (a: address), transceiverInfos[a].index == i - struct _NumTransceivers { - uint8 registered; - uint8 enabled; - } - - uint8 constant MAX_TRANSCEIVERS = 64; - - /// @notice Error when the caller is not the transceiver. - /// @dev Selector 0xa0ae911d. - /// @param caller The address of the caller. - error CallerNotTransceiver(address caller); - - /// @notice Error when the transceiver is the zero address. - /// @dev Selector 0x2f44bd77. - error InvalidTransceiverZeroAddress(); - - /// @notice Error when the transceiver is disabled. - /// @dev Selector 0x1f61ba44. - error DisabledTransceiver(address transceiver); - - /// @notice Error when the number of registered transceivers - /// exceeeds (MAX_TRANSCEIVERS = 64). - /// @dev Selector 0x891684c3. - error TooManyTransceivers(); - - /// @notice Error when attempting to remove a transceiver - /// that is not registered. - /// @dev Selector 0xd583f470. - /// @param transceiver The address of the transceiver. - error NonRegisteredTransceiver(address transceiver); - - /// @notice Error when attempting to enable a transceiver that is already enabled. - /// @dev Selector 0x8d68f84d. - /// @param transceiver The address of the transceiver. - error TransceiverAlreadyEnabled(address transceiver); - - modifier onlyTransceiver() { - if (!_getTransceiverInfosStorage()[msg.sender].enabled) { - revert CallerNotTransceiver(msg.sender); - } - _; - } - - // =============== Storage =============================================== - - bytes32 private constant TRANSCEIVER_INFOS_SLOT = - bytes32(uint256(keccak256("ntt.transceiverInfos")) - 1); - - bytes32 private constant TRANSCEIVER_BITMAP_SLOT = - bytes32(uint256(keccak256("ntt.transceiverBitmap")) - 1); - - bytes32 private constant ENABLED_TRANSCEIVERS_SLOT = - bytes32(uint256(keccak256("ntt.enabledTransceivers")) - 1); - - bytes32 private constant REGISTERED_TRANSCEIVERS_SLOT = - bytes32(uint256(keccak256("ntt.registeredTransceivers")) - 1); - - bytes32 private constant NUM_REGISTERED_TRANSCEIVERS_SLOT = - bytes32(uint256(keccak256("ntt.numRegisteredTransceivers")) - 1); - - function _getTransceiverInfosStorage() - internal - pure - returns (mapping(address => TransceiverInfo) storage $) - { - uint256 slot = uint256(TRANSCEIVER_INFOS_SLOT); - assembly ("memory-safe") { - $.slot := slot - } - } - - function _getEnabledTransceiversStorage() internal pure returns (address[] storage $) { - uint256 slot = uint256(ENABLED_TRANSCEIVERS_SLOT); - assembly ("memory-safe") { - $.slot := slot - } - } - - function _getTransceiverBitmapStorage() - private - pure - returns (_EnabledTransceiverBitmap storage $) - { - uint256 slot = uint256(TRANSCEIVER_BITMAP_SLOT); - assembly ("memory-safe") { - $.slot := slot - } - } - - function _getRegisteredTransceiversStorage() internal pure returns (address[] storage $) { - uint256 slot = uint256(REGISTERED_TRANSCEIVERS_SLOT); - assembly ("memory-safe") { - $.slot := slot - } - } - - function _getNumTransceiversStorage() internal pure returns (_NumTransceivers storage $) { - uint256 slot = uint256(NUM_REGISTERED_TRANSCEIVERS_SLOT); - assembly ("memory-safe") { - $.slot := slot - } - } - - // =============== Storage Getters/Setters ======================================== - - function _setTransceiver( - address transceiver - ) internal returns (uint8 index) { - mapping(address => TransceiverInfo) storage transceiverInfos = _getTransceiverInfosStorage(); - _EnabledTransceiverBitmap storage _enabledTransceiverBitmap = _getTransceiverBitmapStorage(); - address[] storage _enabledTransceivers = _getEnabledTransceiversStorage(); - - _NumTransceivers storage _numTransceivers = _getNumTransceiversStorage(); - - if (transceiver == address(0)) { - revert InvalidTransceiverZeroAddress(); - } - - if (transceiverInfos[transceiver].registered) { - transceiverInfos[transceiver].enabled = true; - } else { - if (_numTransceivers.registered >= MAX_TRANSCEIVERS) { - revert TooManyTransceivers(); - } - - transceiverInfos[transceiver] = TransceiverInfo({ - registered: true, - enabled: true, - index: _numTransceivers.registered - }); - _numTransceivers.registered++; - _getRegisteredTransceiversStorage().push(transceiver); - } - - _enabledTransceivers.push(transceiver); - _numTransceivers.enabled++; - - uint64 updatedEnabledTransceiverBitmap = - _enabledTransceiverBitmap.bitmap | uint64(1 << transceiverInfos[transceiver].index); - // ensure that this actually changed the bitmap - if (updatedEnabledTransceiverBitmap == _enabledTransceiverBitmap.bitmap) { - revert TransceiverAlreadyEnabled(transceiver); - } - _enabledTransceiverBitmap.bitmap = updatedEnabledTransceiverBitmap; - - _checkTransceiversInvariants(); - - return transceiverInfos[transceiver].index; - } - - function _removeTransceiver( - address transceiver - ) internal { - mapping(address => TransceiverInfo) storage transceiverInfos = _getTransceiverInfosStorage(); - _EnabledTransceiverBitmap storage _enabledTransceiverBitmap = _getTransceiverBitmapStorage(); - address[] storage _enabledTransceivers = _getEnabledTransceiversStorage(); - - if (transceiver == address(0)) { - revert InvalidTransceiverZeroAddress(); - } - - if (!transceiverInfos[transceiver].registered) { - revert NonRegisteredTransceiver(transceiver); - } - - if (!transceiverInfos[transceiver].enabled) { - revert DisabledTransceiver(transceiver); - } - - transceiverInfos[transceiver].enabled = false; - _getNumTransceiversStorage().enabled--; - - uint64 updatedEnabledTransceiverBitmap = - _enabledTransceiverBitmap.bitmap & uint64(~(1 << transceiverInfos[transceiver].index)); - // ensure that this actually changed the bitmap - assert(updatedEnabledTransceiverBitmap < _enabledTransceiverBitmap.bitmap); - _enabledTransceiverBitmap.bitmap = updatedEnabledTransceiverBitmap; - - bool removed = false; - - uint256 numEnabledTransceivers = _enabledTransceivers.length; - for (uint256 i = 0; i < numEnabledTransceivers; i++) { - if (_enabledTransceivers[i] == transceiver) { - _enabledTransceivers[i] = _enabledTransceivers[numEnabledTransceivers - 1]; - _enabledTransceivers.pop(); - removed = true; - break; - } - } - assert(removed); - - _checkTransceiversInvariants(); - // we call the invariant check on the transceiver here as well, since - // the above check only iterates through the enabled transceivers. - _checkTransceiverInvariants(transceiver); - } - - function _getEnabledTransceiversBitmap() internal view virtual returns (uint64 bitmap) { - return _getTransceiverBitmapStorage().bitmap; - } - - /// @notice Returns the Transceiver contracts that have been enabled via governance. - function getTransceivers() external pure returns (address[] memory result) { - result = _getEnabledTransceiversStorage(); - } - - /// @notice Returns the info for all enabled transceivers - function getTransceiverInfo() external view returns (TransceiverInfo[] memory) { - address[] memory enabledTransceivers = _getEnabledTransceiversStorage(); - uint256 numEnabledTransceivers = enabledTransceivers.length; - TransceiverInfo[] memory result = new TransceiverInfo[](numEnabledTransceivers); - - for (uint256 i = 0; i < numEnabledTransceivers; ++i) { - result[i] = _getTransceiverInfosStorage()[enabledTransceivers[i]]; - } - - return result; - } - - // ============== Invariants ============================================= - - /// @dev Check that the transceiver nttManager is in a valid state. - /// Checking these invariants is somewhat costly, but we only need to do it - /// when modifying the transceivers, which happens infrequently. - function _checkTransceiversInvariants() internal view { - _NumTransceivers storage _numTransceivers = _getNumTransceiversStorage(); - address[] storage _enabledTransceivers = _getEnabledTransceiversStorage(); - - uint256 numTransceiversEnabled = _numTransceivers.enabled; - assert(numTransceiversEnabled == _enabledTransceivers.length); - - for (uint256 i = 0; i < numTransceiversEnabled; i++) { - _checkTransceiverInvariants(_enabledTransceivers[i]); - } - - // invariant: each transceiver is only enabled once - for (uint256 i = 0; i < numTransceiversEnabled; i++) { - for (uint256 j = i + 1; j < numTransceiversEnabled; j++) { - assert(_enabledTransceivers[i] != _enabledTransceivers[j]); - } - } - - // invariant: numRegisteredTransceivers <= MAX_TRANSCEIVERS - assert(_numTransceivers.registered <= MAX_TRANSCEIVERS); - } - - // @dev Check that the transceiver is in a valid state. - function _checkTransceiverInvariants( - address transceiver - ) private view { - mapping(address => TransceiverInfo) storage transceiverInfos = _getTransceiverInfosStorage(); - _EnabledTransceiverBitmap storage _enabledTransceiverBitmap = _getTransceiverBitmapStorage(); - _NumTransceivers storage _numTransceivers = _getNumTransceiversStorage(); - address[] storage _enabledTransceivers = _getEnabledTransceiversStorage(); - - TransceiverInfo memory transceiverInfo = transceiverInfos[transceiver]; - - // if an transceiver is not registered, it should not be enabled - assert( - transceiverInfo.registered || (!transceiverInfo.enabled && transceiverInfo.index == 0) - ); - - bool transceiverInEnabledBitmap = - (_enabledTransceiverBitmap.bitmap & uint64(1 << transceiverInfo.index)) != 0; - bool transceiverEnabled = transceiverInfo.enabled; - - bool transceiverInEnabledTransceivers = false; - - for (uint256 i = 0; i < _numTransceivers.enabled; i++) { - if (_enabledTransceivers[i] == transceiver) { - transceiverInEnabledTransceivers = true; - break; - } - } - - // invariant: transceiverInfos[transceiver].enabled - // <=> enabledTransceiverBitmap & (1 << transceiverInfos[transceiver].index) != 0 - assert(transceiverInEnabledBitmap == transceiverEnabled); - - // invariant: transceiverInfos[transceiver].enabled <=> transceiver in _enabledTransceivers - assert(transceiverInEnabledTransceivers == transceiverEnabled); - - assert(transceiverInfo.index < _numTransceivers.registered); - } -} diff --git a/evm/src/Transceiver/Transceiver.sol b/evm/src/Transceiver/Transceiver.sol deleted file mode 100644 index d0dfa2f7f..000000000 --- a/evm/src/Transceiver/Transceiver.sol +++ /dev/null @@ -1,163 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.8 <0.9.0; - -import "wormhole-solidity-sdk/Utils.sol"; - -import "../libraries/TransceiverStructs.sol"; -import "../libraries/PausableOwnable.sol"; -import "../libraries/external/ReentrancyGuardUpgradeable.sol"; -import "../libraries/Implementation.sol"; - -import "../interfaces/INttManager.sol"; -import "../interfaces/ITransceiver.sol"; - -/// @title Transceiver -/// @author Wormhole Project Contributors. -/// @notice This contract is a base contract for Transceivers. -/// @dev The Transceiver provides basic functionality for transmitting / receiving NTT messages. -/// The contract supports pausing via an admin or owner and is upgradable. -/// -/// @dev The interface for receiving messages is not enforced by this contract. -/// Instead, inheriting contracts should implement their own receiving logic, -/// based on the verification model and serde logic associated with message handling. -abstract contract Transceiver is - ITransceiver, - PausableOwnable, - ReentrancyGuardUpgradeable, - Implementation -{ - /// @dev updating bridgeNttManager requires a new Transceiver deployment. - /// Projects should implement their own governance to remove the old Transceiver - /// contract address and then add the new one. - address public immutable nttManager; - address public immutable nttManagerToken; - address immutable deployer; - - constructor( - address _nttManager - ) { - nttManager = _nttManager; - nttManagerToken = INttManager(nttManager).token(); - deployer = msg.sender; - } - - /// =============== MODIFIERS =============================================== - - modifier onlyNttManager() { - if (msg.sender != nttManager) { - revert CallerNotNttManager(msg.sender); - } - _; - } - - /// =============== ADMIN =============================================== - - function _initialize() internal virtual override { - // check if the owner is the deployer of this contract - if (msg.sender != deployer) { - revert UnexpectedDeployer(deployer, msg.sender); - } - - __ReentrancyGuard_init(); - // owner of the transceiver is set to the owner of the nttManager - __PausedOwnable_init(msg.sender, getNttManagerOwner()); - } - - /// @dev transfer the ownership of the transceiver to a new address - /// the nttManager should be able to update transceiver ownership. - function transferTransceiverOwnership( - address newOwner - ) external onlyNttManager { - _transferOwnership(newOwner); - } - - function upgrade( - address newImplementation - ) external onlyOwner { - _upgrade(newImplementation); - } - - function _migrate() internal virtual override {} - - // @define This method checks that the the referecnes to the nttManager and its corresponding function - // are correct When new immutable variables are added, this function should be updated. - function _checkImmutables() internal view virtual override { - assert(this.nttManager() == nttManager); - assert(this.nttManagerToken() == nttManagerToken); - } - - /// =============== GETTERS & SETTERS =============================================== - - function getNttManagerOwner() public view returns (address) { - return IOwnableUpgradeable(nttManager).owner(); - } - - function getNttManagerToken() public view virtual returns (address) { - return nttManagerToken; - } - - function getTransceiverType() external view virtual returns (string memory); - - /// =============== TRANSCEIVING LOGIC =============================================== - - /// @inheritdoc ITransceiver - function quoteDeliveryPrice( - uint16 targetChain, - TransceiverStructs.TransceiverInstruction memory instruction - ) external view returns (uint256) { - return _quoteDeliveryPrice(targetChain, instruction); - } - - /// @inheritdoc ITransceiver - function sendMessage( - uint16 recipientChain, - TransceiverStructs.TransceiverInstruction memory instruction, - bytes memory nttManagerMessage, - bytes32 recipientNttManagerAddress, - bytes32 refundAddress - ) external payable nonReentrant onlyNttManager { - _sendMessage( - recipientChain, - msg.value, - msg.sender, - recipientNttManagerAddress, - refundAddress, - instruction, - nttManagerMessage - ); - } - - /// ============================= INTERNAL ========================================= - - function _sendMessage( - uint16 recipientChain, - uint256 deliveryPayment, - address caller, - bytes32 recipientNttManagerAddress, - bytes32 refundAddress, - TransceiverStructs.TransceiverInstruction memory transceiverInstruction, - bytes memory nttManagerMessage - ) internal virtual; - - // @define This method is called by the BridgeNttManager contract to send a cross-chain message. - // @reverts if: - // - `recipientNttManagerAddress` does not match the address of this manager contract - function _deliverToNttManager( - uint16 sourceChainId, - bytes32 sourceNttManagerAddress, - bytes32 recipientNttManagerAddress, - TransceiverStructs.NttManagerMessage memory payload - ) internal virtual { - if (recipientNttManagerAddress != toWormholeFormat(nttManager)) { - revert UnexpectedRecipientNttManagerAddress( - toWormholeFormat(nttManager), recipientNttManagerAddress - ); - } - INttManager(nttManager).attestationReceived(sourceChainId, sourceNttManagerAddress, payload); - } - - function _quoteDeliveryPrice( - uint16 targetChain, - TransceiverStructs.TransceiverInstruction memory transceiverInstruction - ) internal view virtual returns (uint256); -} diff --git a/evm/src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol b/evm/src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol deleted file mode 100644 index 2cf3550c5..000000000 --- a/evm/src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol +++ /dev/null @@ -1,284 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.8 <0.9.0; - -import "wormhole-solidity-sdk/WormholeRelayerSDK.sol"; -import "wormhole-solidity-sdk/libraries/BytesParsing.sol"; -import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; - -import "../../libraries/TransceiverHelpers.sol"; -import "../../libraries/TransceiverStructs.sol"; - -import "../../interfaces/IWormholeTransceiver.sol"; -import "../../interfaces/ISpecialRelayer.sol"; -import "../../interfaces/INttManager.sol"; - -import "./WormholeTransceiverState.sol"; - -/// @title WormholeTransceiver -/// @author Wormhole Project Contributors. -/// @notice Transceiver implementation for Wormhole. -/// -/// @dev This contract is responsible for sending and receiving NTT messages -/// that are authenticated through Wormhole Core. -/// -/// @dev Messages can be delivered either via standard relaying or special relaying, or -/// manually via the core layer. -/// -/// @dev Once a message is received, it is delivered to its corresponding -/// NttManager contract. -contract WormholeTransceiver is - IWormholeTransceiver, - IWormholeReceiver, - WormholeTransceiverState -{ - using BytesParsing for bytes; - - string public constant WORMHOLE_TRANSCEIVER_VERSION = "1.1.0"; - - constructor( - address nttManager, - address wormholeCoreBridge, - address wormholeRelayerAddr, - address specialRelayerAddr, - uint8 _consistencyLevel, - uint256 _gasLimit - ) - WormholeTransceiverState( - nttManager, - wormholeCoreBridge, - wormholeRelayerAddr, - specialRelayerAddr, - _consistencyLevel, - _gasLimit - ) - {} - - // ==================== External Interface =============================================== - - function getTransceiverType() external pure override returns (string memory) { - return "wormhole"; - } - - /// @inheritdoc IWormholeTransceiver - function receiveMessage( - bytes memory encodedMessage - ) external { - uint16 sourceChainId; - bytes memory payload; - (sourceChainId, payload) = _verifyMessage(encodedMessage); - - // parse the encoded Transceiver payload - TransceiverStructs.TransceiverMessage memory parsedTransceiverMessage; - TransceiverStructs.NttManagerMessage memory parsedNttManagerMessage; - (parsedTransceiverMessage, parsedNttManagerMessage) = TransceiverStructs - .parseTransceiverAndNttManagerMessage(WH_TRANSCEIVER_PAYLOAD_PREFIX, payload); - - _deliverToNttManager( - sourceChainId, - parsedTransceiverMessage.sourceNttManagerAddress, - parsedTransceiverMessage.recipientNttManagerAddress, - parsedNttManagerMessage - ); - } - - /// @inheritdoc IWormholeReceiver - function receiveWormholeMessages( - bytes memory payload, - bytes[] memory additionalMessages, - bytes32 sourceAddress, - uint16 sourceChain, - bytes32 deliveryHash - ) external payable onlyRelayer { - if (getWormholePeer(sourceChain) != sourceAddress) { - revert InvalidWormholePeer(sourceChain, sourceAddress); - } - - // VAA replay protection: - // - Note that this VAA is for the AR delivery, not for the raw message emitted by the source - // - chain Transceiver contract. The VAAs received by this entrypoint are different than the - // - VAA received by the receiveMessage entrypoint. - if (isVAAConsumed(deliveryHash)) { - revert TransferAlreadyCompleted(deliveryHash); - } - _setVAAConsumed(deliveryHash); - - // We don't honor additional messages in this handler. - if (additionalMessages.length > 0) { - revert UnexpectedAdditionalMessages(); - } - - // emit `ReceivedRelayedMessage` event - emit ReceivedRelayedMessage(deliveryHash, sourceChain, sourceAddress); - - // parse the encoded Transceiver payload - TransceiverStructs.TransceiverMessage memory parsedTransceiverMessage; - TransceiverStructs.NttManagerMessage memory parsedNttManagerMessage; - (parsedTransceiverMessage, parsedNttManagerMessage) = TransceiverStructs - .parseTransceiverAndNttManagerMessage(WH_TRANSCEIVER_PAYLOAD_PREFIX, payload); - - _deliverToNttManager( - sourceChain, - parsedTransceiverMessage.sourceNttManagerAddress, - parsedTransceiverMessage.recipientNttManagerAddress, - parsedNttManagerMessage - ); - } - - /// @inheritdoc IWormholeTransceiver - function parseWormholeTransceiverInstruction( - bytes memory encoded - ) public pure returns (WormholeTransceiverInstruction memory instruction) { - // If the user doesn't pass in any transceiver instructions then the default is false - if (encoded.length == 0) { - instruction.shouldSkipRelayerSend = false; - return instruction; - } - - uint256 offset = 0; - (instruction.shouldSkipRelayerSend, offset) = encoded.asBoolUnchecked(offset); - encoded.checkLength(offset); - } - - /// @inheritdoc IWormholeTransceiver - function encodeWormholeTransceiverInstruction( - WormholeTransceiverInstruction memory instruction - ) public pure returns (bytes memory) { - return abi.encodePacked(instruction.shouldSkipRelayerSend); - } - - // ==================== Internal ======================================================== - - function _quoteDeliveryPrice( - uint16 targetChain, - TransceiverStructs.TransceiverInstruction memory instruction - ) internal view override returns (uint256 nativePriceQuote) { - // Check the special instruction up front to see if we should skip sending via a relayer - WormholeTransceiverInstruction memory weIns = - parseWormholeTransceiverInstruction(instruction.payload); - if (weIns.shouldSkipRelayerSend) { - return wormhole.messageFee(); - } - - if (_checkInvalidRelayingConfig(targetChain)) { - revert InvalidRelayingConfig(targetChain); - } - - if (_shouldRelayViaStandardRelaying(targetChain)) { - (uint256 cost,) = wormholeRelayer.quoteEVMDeliveryPrice(targetChain, 0, gasLimit); - return cost; - } else if (isSpecialRelayingEnabled(targetChain)) { - uint256 cost = specialRelayer.quoteDeliveryPrice(getNttManagerToken(), targetChain, 0); - // We need to pay both the special relayer cost and the Wormhole message fee independently - return cost + wormhole.messageFee(); - } else { - return wormhole.messageFee(); - } - } - - function _sendMessage( - uint16 recipientChain, - uint256 deliveryPayment, - address caller, - bytes32 recipientNttManagerAddress, - bytes32 refundAddress, - TransceiverStructs.TransceiverInstruction memory instruction, - bytes memory nttManagerMessage - ) internal override { - ( - TransceiverStructs.TransceiverMessage memory transceiverMessage, - bytes memory encodedTransceiverPayload - ) = TransceiverStructs.buildAndEncodeTransceiverMessage( - WH_TRANSCEIVER_PAYLOAD_PREFIX, - toWormholeFormat(caller), - recipientNttManagerAddress, - nttManagerMessage, - new bytes(0) - ); - - WormholeTransceiverInstruction memory weIns = - parseWormholeTransceiverInstruction(instruction.payload); - - if (!weIns.shouldSkipRelayerSend && _shouldRelayViaStandardRelaying(recipientChain)) { - // NOTE: standard relaying supports refunds. The amount to be refunded will be sent - // to a refundAddress specified by the client on the destination chain. - - // push onto the stack again to avoid stack too deep error - bytes32 refundRecipient = refundAddress; - uint16 destinationChain = recipientChain; - - wormholeRelayer.sendToEvm{value: deliveryPayment}( - destinationChain, - fromWormholeFormat(getWormholePeer(destinationChain)), - encodedTransceiverPayload, - 0, // receiverValue - 0, // paymentForExtraReceiverValue, - gasLimit, - destinationChain, - fromWormholeFormat(refundRecipient), - wormholeRelayer.getDefaultDeliveryProvider(), - new VaaKey[](0), - consistencyLevel - ); - - emit RelayingInfo(uint8(RelayingType.Standard), refundAddress, deliveryPayment); - } else if (!weIns.shouldSkipRelayerSend && isSpecialRelayingEnabled(recipientChain)) { - uint256 wormholeFee = wormhole.messageFee(); - uint64 sequence = wormhole.publishMessage{value: wormholeFee}( - 0, encodedTransceiverPayload, consistencyLevel - ); - specialRelayer.requestDelivery{value: deliveryPayment - wormholeFee}( - getNttManagerToken(), recipientChain, 0, sequence - ); - - // NOTE: specialized relaying does not currently support refunds. The zero address - // is used as a placeholder for the refund address until support is added. - emit RelayingInfo(uint8(RelayingType.Special), bytes32(0), deliveryPayment); - } else { - wormhole.publishMessage{value: deliveryPayment}( - 0, encodedTransceiverPayload, consistencyLevel - ); - - // NOTE: manual relaying does not currently support refunds. The zero address - // is used as refundAddress. - emit RelayingInfo(uint8(RelayingType.Manual), bytes32(0), deliveryPayment); - } - - emit SendTransceiverMessage(recipientChain, transceiverMessage); - } - - function _verifyMessage( - bytes memory encodedMessage - ) internal returns (uint16, bytes memory) { - // verify VAA against Wormhole Core Bridge contract - (IWormhole.VM memory vm, bool valid, string memory reason) = - wormhole.parseAndVerifyVM(encodedMessage); - - // ensure that the VAA is valid - if (!valid) { - revert InvalidVaa(reason); - } - - // ensure that the message came from a registered peer contract - if (!_verifyBridgeVM(vm)) { - revert InvalidWormholePeer(vm.emitterChainId, vm.emitterAddress); - } - - // save the VAA hash in storage to protect against replay attacks. - if (isVAAConsumed(vm.hash)) { - revert TransferAlreadyCompleted(vm.hash); - } - _setVAAConsumed(vm.hash); - - // emit `ReceivedMessage` event - emit ReceivedMessage(vm.hash, vm.emitterChainId, vm.emitterAddress, vm.sequence); - - return (vm.emitterChainId, vm.payload); - } - - function _verifyBridgeVM( - IWormhole.VM memory vm - ) internal view returns (bool) { - checkFork(wormholeTransceiver_evmChainId); - return getWormholePeer(vm.emitterChainId) == vm.emitterAddress; - } -} diff --git a/evm/src/Transceiver/WormholeTransceiver/WormholeTransceiverState.sol b/evm/src/Transceiver/WormholeTransceiver/WormholeTransceiverState.sol deleted file mode 100644 index b58db7dc2..000000000 --- a/evm/src/Transceiver/WormholeTransceiver/WormholeTransceiverState.sol +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.8 <0.9.0; - -import "wormhole-solidity-sdk/WormholeRelayerSDK.sol"; -import "wormhole-solidity-sdk/libraries/BytesParsing.sol"; -import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; - -import "../../libraries/TransceiverHelpers.sol"; -import "../../libraries/BooleanFlag.sol"; -import "../../libraries/TransceiverStructs.sol"; - -import "../../interfaces/IWormholeTransceiver.sol"; -import "../../interfaces/IWormholeTransceiverState.sol"; -import "../../interfaces/ISpecialRelayer.sol"; -import "../../interfaces/INttManager.sol"; - -import "../Transceiver.sol"; - -abstract contract WormholeTransceiverState is IWormholeTransceiverState, Transceiver { - using BytesParsing for bytes; - using BooleanFlagLib for bool; - using BooleanFlagLib for BooleanFlag; - - // ==================== Immutables =============================================== - uint8 public immutable consistencyLevel; - IWormhole public immutable wormhole; - IWormholeRelayer public immutable wormholeRelayer; - ISpecialRelayer public immutable specialRelayer; - /// @dev We don't check this in `_checkImmutables` since it's set at construction - /// through `block.chainid`. - uint256 immutable wormholeTransceiver_evmChainId; - /// @dev We purposely avoid checking this in `_checkImmutables` to allow tweaking it - /// without needing to allow modification of security critical immutables. - uint256 public immutable gasLimit; - - // ==================== Constants ================================================ - - /// @dev Prefix for all TransceiverMessage payloads - /// @notice Magic string (constant value set by messaging provider) that idenfies the payload as an transceiver-emitted payload. - /// Note that this is not a security critical field. It's meant to be used by messaging providers to identify which messages are Transceiver-related. - bytes4 constant WH_TRANSCEIVER_PAYLOAD_PREFIX = 0x9945FF10; - - /// @dev Prefix for all Wormhole transceiver initialisation payloads - /// This is bytes4(keccak256("WormholeTransceiverInit")) - bytes4 constant WH_TRANSCEIVER_INIT_PREFIX = 0x9c23bd3b; - - /// @dev Prefix for all Wormhole peer registration payloads - /// This is bytes4(keccak256("WormholePeerRegistration")) - bytes4 constant WH_PEER_REGISTRATION_PREFIX = 0x18fc67c2; - - constructor( - address nttManager, - address wormholeCoreBridge, - address wormholeRelayerAddr, - address specialRelayerAddr, - uint8 _consistencyLevel, - uint256 _gasLimit - ) Transceiver(nttManager) { - wormhole = IWormhole(wormholeCoreBridge); - wormholeRelayer = IWormholeRelayer(wormholeRelayerAddr); - specialRelayer = ISpecialRelayer(specialRelayerAddr); - wormholeTransceiver_evmChainId = block.chainid; - consistencyLevel = _consistencyLevel; - gasLimit = _gasLimit; - } - - enum RelayingType { - Standard, - Special, - Manual - } - - function _initialize() internal override { - super._initialize(); - _initializeTransceiver(); - } - - function _initializeTransceiver() internal { - TransceiverStructs.TransceiverInit memory init = TransceiverStructs.TransceiverInit({ - transceiverIdentifier: WH_TRANSCEIVER_INIT_PREFIX, - nttManagerAddress: toWormholeFormat(nttManager), - nttManagerMode: INttManager(nttManager).getMode(), - tokenAddress: toWormholeFormat(nttManagerToken), - tokenDecimals: INttManager(nttManager).tokenDecimals() - }); - wormhole.publishMessage{value: msg.value}( - 0, TransceiverStructs.encodeTransceiverInit(init), consistencyLevel - ); - } - - function _checkImmutables() internal view override { - super._checkImmutables(); - assert(this.wormhole() == wormhole); - assert(this.wormholeRelayer() == wormholeRelayer); - assert(this.specialRelayer() == specialRelayer); - assert(this.consistencyLevel() == consistencyLevel); - } - - // =============== Storage =============================================== - - bytes32 private constant WORMHOLE_CONSUMED_VAAS_SLOT = - bytes32(uint256(keccak256("whTransceiver.consumedVAAs")) - 1); - - bytes32 private constant WORMHOLE_PEERS_SLOT = - bytes32(uint256(keccak256("whTransceiver.peers")) - 1); - - bytes32 private constant WORMHOLE_RELAYING_ENABLED_CHAINS_SLOT = - bytes32(uint256(keccak256("whTransceiver.relayingEnabledChains")) - 1); - - bytes32 private constant SPECIAL_RELAYING_ENABLED_CHAINS_SLOT = - bytes32(uint256(keccak256("whTransceiver.specialRelayingEnabledChains")) - 1); - - bytes32 private constant WORMHOLE_EVM_CHAIN_IDS = - bytes32(uint256(keccak256("whTransceiver.evmChainIds")) - 1); - - // =============== Storage Setters/Getters ======================================== - - function _getWormholeConsumedVAAsStorage() - internal - pure - returns (mapping(bytes32 => bool) storage $) - { - uint256 slot = uint256(WORMHOLE_CONSUMED_VAAS_SLOT); - assembly ("memory-safe") { - $.slot := slot - } - } - - function _getWormholePeersStorage() - internal - pure - returns (mapping(uint16 => bytes32) storage $) - { - uint256 slot = uint256(WORMHOLE_PEERS_SLOT); - assembly ("memory-safe") { - $.slot := slot - } - } - - function _getWormholeRelayingEnabledChainsStorage() - internal - pure - returns (mapping(uint16 => BooleanFlag) storage $) - { - uint256 slot = uint256(WORMHOLE_RELAYING_ENABLED_CHAINS_SLOT); - assembly ("memory-safe") { - $.slot := slot - } - } - - function _getSpecialRelayingEnabledChainsStorage() - internal - pure - returns (mapping(uint16 => BooleanFlag) storage $) - { - uint256 slot = uint256(SPECIAL_RELAYING_ENABLED_CHAINS_SLOT); - assembly ("memory-safe") { - $.slot := slot - } - } - - function _getWormholeEvmChainIdsStorage() - internal - pure - returns (mapping(uint16 => BooleanFlag) storage $) - { - uint256 slot = uint256(WORMHOLE_EVM_CHAIN_IDS); - assembly ("memory-safe") { - $.slot := slot - } - } - - // =============== Public Getters ====================================================== - - /// @inheritdoc IWormholeTransceiverState - function isVAAConsumed( - bytes32 hash - ) public view returns (bool) { - return _getWormholeConsumedVAAsStorage()[hash]; - } - - /// @inheritdoc IWormholeTransceiverState - function getWormholePeer( - uint16 chainId - ) public view returns (bytes32) { - return _getWormholePeersStorage()[chainId]; - } - - /// @inheritdoc IWormholeTransceiverState - function isWormholeRelayingEnabled( - uint16 chainId - ) public view returns (bool) { - return _getWormholeRelayingEnabledChainsStorage()[chainId].toBool(); - } - - /// @inheritdoc IWormholeTransceiverState - function isSpecialRelayingEnabled( - uint16 chainId - ) public view returns (bool) { - return _getSpecialRelayingEnabledChainsStorage()[chainId].toBool(); - } - - /// @inheritdoc IWormholeTransceiverState - function isWormholeEvmChain( - uint16 chainId - ) public view returns (bool) { - return _getWormholeEvmChainIdsStorage()[chainId].toBool(); - } - - // =============== Admin =============================================================== - - /// @inheritdoc IWormholeTransceiverState - function setWormholePeer(uint16 peerChainId, bytes32 peerContract) external payable onlyOwner { - if (peerChainId == 0) { - revert InvalidWormholeChainIdZero(); - } - if (peerContract == bytes32(0)) { - revert InvalidWormholePeerZeroAddress(); - } - - bytes32 oldPeerContract = _getWormholePeersStorage()[peerChainId]; - - // We don't want to allow updating a peer since this adds complexity in the accountant - // If the owner makes a mistake with peer registration they should deploy a new Wormhole - // transceiver and register this new transceiver with the NttManager - if (oldPeerContract != bytes32(0)) { - revert PeerAlreadySet(peerChainId, oldPeerContract); - } - - _getWormholePeersStorage()[peerChainId] = peerContract; - - // Publish a message for this transceiver registration - TransceiverStructs.TransceiverRegistration memory registration = TransceiverStructs - .TransceiverRegistration({ - transceiverIdentifier: WH_PEER_REGISTRATION_PREFIX, - transceiverChainId: peerChainId, - transceiverAddress: peerContract - }); - wormhole.publishMessage{value: msg.value}( - 0, TransceiverStructs.encodeTransceiverRegistration(registration), consistencyLevel - ); - - emit SetWormholePeer(peerChainId, peerContract); - } - - /// @inheritdoc IWormholeTransceiverState - function setIsWormholeEvmChain(uint16 chainId, bool isEvm) external onlyOwner { - if (chainId == 0) { - revert InvalidWormholeChainIdZero(); - } - _getWormholeEvmChainIdsStorage()[chainId] = isEvm.toWord(); - - emit SetIsWormholeEvmChain(chainId, isEvm); - } - - /// @inheritdoc IWormholeTransceiverState - function setIsWormholeRelayingEnabled(uint16 chainId, bool isEnabled) external onlyOwner { - if (chainId == 0) { - revert InvalidWormholeChainIdZero(); - } - _getWormholeRelayingEnabledChainsStorage()[chainId] = isEnabled.toWord(); - - emit SetIsWormholeRelayingEnabled(chainId, isEnabled); - } - - /// @inheritdoc IWormholeTransceiverState - function setIsSpecialRelayingEnabled(uint16 chainId, bool isEnabled) external onlyOwner { - if (chainId == 0) { - revert InvalidWormholeChainIdZero(); - } - _getSpecialRelayingEnabledChainsStorage()[chainId] = isEnabled.toWord(); - - emit SetIsSpecialRelayingEnabled(chainId, isEnabled); - } - - // ============= Internal =============================================================== - - function _checkInvalidRelayingConfig( - uint16 chainId - ) internal view returns (bool) { - return isWormholeRelayingEnabled(chainId) && !isWormholeEvmChain(chainId); - } - - function _shouldRelayViaStandardRelaying( - uint16 chainId - ) internal view returns (bool) { - return isWormholeRelayingEnabled(chainId) && isWormholeEvmChain(chainId); - } - - function _setVAAConsumed( - bytes32 hash - ) internal { - _getWormholeConsumedVAAsStorage()[hash] = true; - } - - // =============== MODIFIERS =============================================== - - modifier onlyRelayer() { - if (msg.sender != address(wormholeRelayer)) { - revert CallerNotRelayer(msg.sender); - } - _; - } -} diff --git a/evm/src/interfaces/IManagerBase.sol b/evm/src/interfaces/IManagerBase.sol index c4d1d565f..706200397 100644 --- a/evm/src/interfaces/IManagerBase.sol +++ b/evm/src/interfaces/IManagerBase.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.8 <0.9.0; +import "example-messaging-endpoint/evm/src/libraries/UniversalAddress.sol"; + import "../libraries/TransceiverStructs.sol"; interface IManagerBase { @@ -39,27 +41,20 @@ interface IManagerBase { /// @param index The index of the transceiver in the bitmap. event MessageAttestedTo(bytes32 digest, address transceiver, uint8 index); - /// @notice Emmitted when the threshold required transceivers is changed. + /// @notice Emitted when the threshold for a chain is changed. /// @dev Topic0 - /// 0x2a855b929b9a53c6fb5b5ed248b27e502b709c088e036a5aa17620c8fc5085a9. + /// 0x77bc50d64a1fb4b6ef32894bc26fc008f4ee08223914c45472c9d62088c97f38. + /// @param chainId The chain for which the threshold was updated. /// @param oldThreshold The old threshold. /// @param threshold The new threshold. - event ThresholdChanged(uint8 oldThreshold, uint8 threshold); + event ThresholdChanged(uint16 chainId, uint8 oldThreshold, uint8 threshold); /// @notice Emitted when an transceiver is removed from the nttManager. /// @dev Topic0 - /// 0xf05962b5774c658e85ed80c91a75af9d66d2af2253dda480f90bce78aff5eda5. + /// 0x2fb241a51a63da05063ac6be1f963395b281e455e8085bd246a7e8502b8950d5. /// @param transceiver The address of the transceiver. /// @param transceiversNum The current number of transceivers. - /// @param threshold The current threshold of transceivers. - event TransceiverAdded(address transceiver, uint256 transceiversNum, uint8 threshold); - - /// @notice Emitted when an transceiver is removed from the nttManager. - /// @dev Topic0 - /// 0x697a3853515b88013ad432f29f53d406debc9509ed6d9313dcfe115250fcd18f. - /// @param transceiver The address of the transceiver. - /// @param threshold The current threshold of transceivers. - event TransceiverRemoved(address transceiver, uint8 threshold); + event TransceiverAdded(address transceiver, uint256 transceiversNum); /// @notice payment for a transfer is too low. /// @param requiredPayment The required payment. @@ -92,14 +87,6 @@ interface IManagerBase { /// @param msgHash The hash of the message. error MessageNotApproved(bytes32 msgHash); - /// @notice Emitted when a message has already been executed to - /// notify client of against retries. - /// @dev Topic0 - /// 0x4069dff8c9df7e38d2867c0910bd96fd61787695e5380281148c04932d02bef2. - /// @param sourceNttManager The address of the source nttManager. - /// @param msgHash The keccak-256 hash of the message. - event MessageAlreadyExecuted(bytes32 indexed sourceNttManager, bytes32 indexed msgHash); - /// @notice There are no transceivers enabled with the Manager /// @dev Selector 0x69cf632a error NoEnabledTransceivers(); @@ -111,20 +98,19 @@ interface IManagerBase { /// @notice Fetch the delivery price for a given recipient chain transfer. /// @param recipientChain The Wormhole chain ID of the transfer destination. - /// @param transceiverInstructions The transceiver specific instructions for quoting and sending + /// @param transceiverInstructions The encoded adapter instructions to be passed to the endpoint. /// @return - The delivery prices associated with each enabled endpoint and the total price. function quoteDeliveryPrice( uint16 recipientChain, bytes memory transceiverInstructions - ) external view returns (uint256[] memory, uint256); + ) external view returns (uint256); /// @notice Sets the threshold for the number of attestations required for a message /// to be considered valid. + /// @param chain The chain to which the threshold update applies. /// @param threshold The new threshold (number of attestations). /// @dev This method can only be executed by the `owner`. - function setThreshold( - uint8 threshold - ) external; + function setThreshold(uint16 chain, uint8 threshold) external; /// @notice Sets the transceiver for the given chain. /// @param transceiver The address of the transceiver. @@ -133,26 +119,55 @@ interface IManagerBase { address transceiver ) external; - /// @notice Removes the transceiver for the given chain. - /// @param transceiver The address of the transceiver. - /// @dev This method can only be executed by the `owner`. - function removeTransceiver( - address transceiver - ) external; + /// @notice This enables the sending of messages from the given transceiver on the given chain. + /// @param transceiver The address of the Transceiver contract. + /// @param chain The chain ID of the Transceiver contract. + function enableSendTransceiver(uint16 chain, address transceiver) external; + + /// @notice This enables the receiving of messages by the given transceiver on the given chain. + /// @param transceiver The address of the Transceiver contract. + /// @param chain The chain ID of the Transceiver contract. + function enableRecvTransceiver(uint16 chain, address transceiver) external; + + /// @notice This disables the sending of messages from the given transceiver on the given chain. + /// @param transceiver The address of the Transceiver contract. + /// @param chain The chain ID of the Transceiver contract. + function disableSendTransceiver(uint16 chain, address transceiver) external; + + /// @notice This disables the receiving of messages by the given transceiver on the given chain. + /// @param transceiver The address of the Transceiver contract. + /// @param chain The chain ID of the Transceiver contract. + function disableRecvTransceiver(uint16 chain, address transceiver) external; /// @notice Checks if a message has been approved. The message should have at least /// the minimum threshold of attestations from distinct endpoints. - /// @param digest The digest of the message. + /// @param srcChain The Wormhole chain ID of the sender. + /// @param srcAddr The universal address of the message. + /// @param sequence The sequence number of the message. + /// @param dstAddr The destination address of the message. + /// @param payloadHash The keccak256 of payload from the integrator. /// @return - Boolean indicating if message has been approved. function isMessageApproved( - bytes32 digest + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + UniversalAddress dstAddr, + bytes32 payloadHash ) external view returns (bool); /// @notice Checks if a message has been executed. - /// @param digest The digest of the message. + /// @param srcChain The Wormhole chain ID of the sender. + /// @param srcAddr The universal address of the message. + /// @param sequence The sequence number of the message. + /// @param dstAddr The destination address of the message. + /// @param payloadHash The keccak256 of payload from the integrator. /// @return - Boolean indicating if message has been executed. function isMessageExecuted( - bytes32 digest + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + UniversalAddress dstAddr, + bytes32 payloadHash ) external view returns (bool); /// @notice Returns the next message sequence. @@ -173,24 +188,43 @@ interface IManagerBase { /// @return mode A uint8 corresponding to the mode function getMode() external view returns (uint8); - /// @notice Returns the number of Transceivers that must attest to a msgId for + /// @notice Returns the threshold for the specified chain. /// it to be considered valid and acted upon. - function getThreshold() external view returns (uint8); + /// @param chain The chain for which the threshold is requested. + function getThreshold( + uint16 chain + ) external view returns (uint8); /// @notice Returns a boolean indicating if the transceiver has attested to the message. - /// @param digest The digest of the message. + /// @param srcChain The Wormhole chain ID of the sender. + /// @param srcAddr The universal address of the message. + /// @param sequence The sequence number of the message. + /// @param dstAddr The destination address of the message. + /// @param payloadHash The keccak256 of payload from the integrator. /// @param index The index of the transceiver /// @return - Boolean indicating whether the transceiver at index `index` attested to a message digest function transceiverAttestedToMessage( - bytes32 digest, + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + UniversalAddress dstAddr, + bytes32 payloadHash, uint8 index ) external view returns (bool); /// @notice Returns the number of attestations for a given message. - /// @param digest The digest of the message. + /// @param srcChain The Wormhole chain ID of the sender. + /// @param srcAddr The universal address of the message. + /// @param sequence The sequence number of the message. + /// @param dstAddr The destination address of the message. + /// @param payloadHash The keccak256 of payload from the integrator. /// @return count The number of attestations received for the given message digest function messageAttestations( - bytes32 digest + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + UniversalAddress dstAddr, + bytes32 payloadHash ) external view returns (uint8 count); /// @notice Returns of the address of the token managed by this contract. diff --git a/evm/src/interfaces/INttManager.sol b/evm/src/interfaces/INttManager.sol index 3ace687fe..57e67f37a 100644 --- a/evm/src/interfaces/INttManager.sol +++ b/evm/src/interfaces/INttManager.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.8 <0.9.0; +import "example-messaging-endpoint/evm/src/libraries/UniversalAddress.sol"; + import "../libraries/TrimmedAmount.sol"; import "../libraries/TransceiverStructs.sol"; @@ -11,11 +13,12 @@ interface INttManager is IManagerBase { struct NttManagerPeer { bytes32 peerAddress; uint8 tokenDecimals; + uint128 gasLimit; } /// @notice Emitted when a message is sent from the nttManager. /// @dev Topic0 - /// 0xe54e51e42099622516fa3b48e9733581c9dbdcb771cafb093f745a0532a35982. + /// 0x75eb8927cc7c4810b30fa2e8011fce37da6da7d18eb82c642c367ae4445c3625. /// @param recipient The recipient of the message. /// @param refundAddress The address on the destination chain to which the /// refund of unused gas will be paid @@ -29,7 +32,8 @@ interface INttManager is IManagerBase { uint256 amount, uint256 fee, uint16 recipientChain, - uint64 msgSequence + uint64 msgSequence, + bytes32 msgHash ); /// @notice Emitted when a message is sent from the nttManager. @@ -141,9 +145,20 @@ interface INttManager is IManagerBase { /// @dev Selector 0x20371f2a. error InvalidPeerSameChainId(); + /// @notice Threshold has not been met for an attestation. + /// @dev Selector 0xf6f12287. + /// @param threshold The required threshold. + /// @param numAttested The number of attestations for this message. + error ThresholdNotMet(uint8 threshold, uint8 numAttested); + /// @notice Feature is not implemented. error NotImplemented(); + /// @notice The gas limit for the specified chain is not set. + /// @dev Selector 0xb30dea62. + /// @param destChain The chain ID for which the gas limit is not set. + error InvalidGasLimitZero(uint16 destChain); + /// @notice Transfer a given amount to a recipient on a given chain. This function is called /// by the user to send the token cross-chain. This function will either lock or burn the /// sender's tokens. Finally, this function will call into registered `Endpoint` contracts @@ -151,11 +166,17 @@ interface INttManager is IManagerBase { /// @param amount The amount to transfer. /// @param recipientChain The Wormhole chain ID for the destination. /// @param recipient The recipient address. + /// @param executorQuote The signed quote to be passed to the executor. + /// @param relayInstructions The relay instructions to be passed to the executor. + /// @param transceiverInstructions The adapter instructions to be passed to the endpoint. /// @return msgId The resulting message ID of the transfer function transfer( uint256 amount, uint16 recipientChain, - bytes32 recipient + bytes32 recipient, + bytes calldata executorQuote, + bytes calldata relayInstructions, + bytes calldata transceiverInstructions ) external payable returns (uint64 msgId); /// @notice Transfer a given amount to a recipient on a given chain. This function is called @@ -166,9 +187,11 @@ interface INttManager is IManagerBase { /// @param amount The amount to transfer. /// @param recipientChain The Wormhole chain ID for the destination. /// @param recipient The recipient address. - /// @param refundAddress The address to which a refund for unussed gas is issued on the recipient chain. + /// @param refundAddress The address to which a refund for unused gas is issued on the recipient chain. /// @param shouldQueue Whether the transfer should be queued if the outbound limit is hit. - /// @param encodedInstructions Additional instructions to be forwarded to the recipient chain. + /// @param executorQuote The signed quote to be passed to the executor. + /// @param relayInstructions The relay instructions to be passed to the executor. + /// @param transceiverInstructions The adapter instructions to be passed to the endpoint. /// @return msgId The resulting message ID of the transfer function transfer( uint256 amount, @@ -176,7 +199,9 @@ interface INttManager is IManagerBase { bytes32 recipient, bytes32 refundAddress, bool shouldQueue, - bytes memory encodedInstructions + bytes calldata executorQuote, + bytes calldata relayInstructions, + bytes calldata transceiverInstructions ) external payable returns (uint64 msgId); /// @notice Complete an outbound transfer that's been queued. @@ -200,31 +225,20 @@ interface INttManager is IManagerBase { bytes32 digest ) external; - /// @notice Called by an Endpoint contract to deliver a verified attestation. - /// @dev This function enforces attestation threshold and replay logic for messages. Once all - /// validations are complete, this function calls `executeMsg` to execute the command specified - /// by the message. - /// @param sourceChainId The Wormhole chain id of the sender. - /// @param sourceNttManagerAddress The address of the sender's NTT Manager contract. - /// @param payload The VAA payload. - function attestationReceived( - uint16 sourceChainId, - bytes32 sourceNttManagerAddress, - TransceiverStructs.NttManagerMessage memory payload - ) external; - /// @notice Called after a message has been sufficiently verified to execute /// the command in the message. This function will decode the payload /// as an NttManagerMessage to extract the sequence, msgType, and other parameters. /// @dev This function is exposed as a fallback for when an `Transceiver` is deregistered /// when a message is in flight. - /// @param sourceChainId The Wormhole chain id of the sender. - /// @param sourceNttManagerAddress The address of the sender's nttManager contract. - /// @param message The message to execute. + /// @param srcChain The Wormhole chain ID of the sender. + /// @param srcAddr The universal address of the peer on the sending chain. + /// @param sequence The sequence number of the message (per integrator). + /// @param payload The message to execute. function executeMsg( - uint16 sourceChainId, - bytes32 sourceNttManagerAddress, - TransceiverStructs.NttManagerMessage memory message + uint16 srcChain, + UniversalAddress srcAddr, + uint64 sequence, + bytes memory payload ) external; /// @notice Returns the number of decimals of the token managed by the NttManager. @@ -242,15 +256,23 @@ interface INttManager is IManagerBase { /// @param peerChainId The Wormhole chain ID of the peer. /// @param peerContract The address of the peer nttManager contract. /// @param decimals The number of decimals of the token on the peer chain. + /// @param gasLimit The gas limit for the peer chain. /// @param inboundLimit The inbound rate limit for the peer chain id. This is formatted in the normal /// token representation. e.g. a limit of 100 for a token with 6 decimals = 100_000_000 function setPeer( uint16 peerChainId, bytes32 peerContract, uint8 decimals, + uint128 gasLimit, uint256 inboundLimit ) external; + /// @notice Sets the gas limit for a given chain. + /// @dev This method can only be executed by the `owner`. + /// @param peerChainId The Wormhole chain ID of the peer. + /// @param limit The new gas limit. + function setGasLimit(uint16 peerChainId, uint128 limit) external; + /// @notice Sets the outbound transfer limit for a given chain. /// @dev This method can only be executed by the `owner`. /// @param limit The new outbound limit. This is formatted in the normal diff --git a/evm/src/interfaces/IRateLimiter.sol b/evm/src/interfaces/IRateLimiter.sol index 05aec9751..c3555945d 100644 --- a/evm/src/interfaces/IRateLimiter.sol +++ b/evm/src/interfaces/IRateLimiter.sol @@ -62,7 +62,8 @@ interface IRateLimiter { /// - txTimestamp: the timestamp of the transfer. /// - recipientChain: the chain of the recipient. /// - sender: the sender of the transfer. - /// - transceiverInstructions: additional instructions to be forwarded to the recipient chain. + /// - relayInstructions: additional instructions to be forwarded to the relayer. + /// - transceiverInstructions: instructions to be passed into the adapters. struct OutboundQueuedTransfer { bytes32 recipient; bytes32 refundAddress; @@ -70,6 +71,8 @@ interface IRateLimiter { uint64 txTimestamp; uint16 recipientChain; address sender; + bytes executorQuote; + bytes relayInstructions; bytes transceiverInstructions; } diff --git a/evm/src/interfaces/ITransceiver.sol b/evm/src/interfaces/ITransceiver.sol deleted file mode 100644 index 9e6ea79ec..000000000 --- a/evm/src/interfaces/ITransceiver.sol +++ /dev/null @@ -1,86 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.8 <0.9.0; - -import "../libraries/TransceiverStructs.sol"; - -interface ITransceiver { - /// @notice The caller is not the deployer. - /// @dev Selector: 0xc68a0e42. - /// @param deployer The address of the deployer. - /// @param caller The address of the caller. - error UnexpectedDeployer(address deployer, address caller); - - /// @notice The caller is not the NttManager. - /// @dev Selector: 0xc5aa6153. - /// @param caller The address of the caller. - error CallerNotNttManager(address caller); - - /// @notice Error when trying renounce transceiver ownership. - /// Ensures the owner of the transceiver is in sync with - /// the owner of the NttManager. - /// @dev Selector: 0x66791dd6. - /// @param currentOwner he current owner of the transceiver. - error CannotRenounceTransceiverOwnership(address currentOwner); - - /// @notice Error when trying to transfer transceiver ownership. - /// @dev Selector: 0x306239eb. - /// @param currentOwner The current owner of the transceiver. - /// @param newOwner The new owner of the transceiver. - error CannotTransferTransceiverOwnership(address currentOwner, address newOwner); - - /// @notice Error when the recipient NttManager address is not the - /// corresponding manager of the transceiver. - /// @dev Selector: 0x73bdd322. - /// @param recipientNttManagerAddress The address of the recipient NttManager. - /// @param expectedRecipientNttManagerAddress The expected address of the recipient NttManager. - error UnexpectedRecipientNttManagerAddress( - bytes32 recipientNttManagerAddress, bytes32 expectedRecipientNttManagerAddress - ); - - /// @notice Returns the owner address of the NTT Manager that this transceiver is related to. - function getNttManagerOwner() external view returns (address); - - /// @notice Returns the address of the token associated with this NTT deployment. - function getNttManagerToken() external view returns (address); - - /// @notice Returns the string type of the transceiver. E.g. "wormhole", "axelar", etc. - function getTransceiverType() external view returns (string memory); - - /// @notice Fetch the delivery price for a given recipient chain transfer. - /// @param recipientChain The Wormhole chain ID of the target chain. - /// @param instruction An additional Instruction provided by the Transceiver to be - /// executed on the recipient chain. - /// @return deliveryPrice The cost of delivering a message to the recipient chain, - /// in this chain's native token. - function quoteDeliveryPrice( - uint16 recipientChain, - TransceiverStructs.TransceiverInstruction memory instruction - ) external view returns (uint256); - - /// @dev Send a message to another chain. - /// @param recipientChain The Wormhole chain ID of the recipient. - /// @param instruction An additional Instruction provided by the Transceiver to be - /// executed on the recipient chain. - /// @param nttManagerMessage A message to be sent to the nttManager on the recipient chain. - /// @param recipientNttManagerAddress The Wormhole formatted address of the peer NTT Manager on the recipient chain. - /// @param refundAddress The Wormhole formatted address of the refund recipient - function sendMessage( - uint16 recipientChain, - TransceiverStructs.TransceiverInstruction memory instruction, - bytes memory nttManagerMessage, - bytes32 recipientNttManagerAddress, - bytes32 refundAddress - ) external payable; - - /// @notice Upgrades the transceiver to a new implementation. - /// @param newImplementation The address of the new implementation contract - function upgrade( - address newImplementation - ) external; - - /// @notice Transfers the ownership of the transceiver to a new address. - /// @param newOwner The address of the new owner - function transferTransceiverOwnership( - address newOwner - ) external; -} diff --git a/evm/src/interfaces/IWormholeTransceiver.sol b/evm/src/interfaces/IWormholeTransceiver.sol deleted file mode 100644 index 74142178f..000000000 --- a/evm/src/interfaces/IWormholeTransceiver.sol +++ /dev/null @@ -1,81 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.8 <0.9.0; - -import "../libraries/TransceiverStructs.sol"; - -import "./IWormholeTransceiverState.sol"; - -interface IWormholeTransceiver is IWormholeTransceiverState { - /// @notice The instruction for the WormholeTransceiver contract - /// to skip delivery via the relayer. - struct WormholeTransceiverInstruction { - bool shouldSkipRelayerSend; - } - - /// @notice Emitted when a relayed message is received. - /// @dev Topic0 - /// 0xf557dbbb087662f52c815f6c7ee350628a37a51eae9608ff840d996b65f87475 - /// @param digest The digest of the message. - /// @param emitterChainId The chain ID of the emitter. - /// @param emitterAddress The address of the emitter. - event ReceivedRelayedMessage(bytes32 digest, uint16 emitterChainId, bytes32 emitterAddress); - - /// @notice Emitted when a message is received. - /// @dev Topic0 - /// 0xf6fc529540981400dc64edf649eb5e2e0eb5812a27f8c81bac2c1d317e71a5f0. - /// @param digest The digest of the message. - /// @param emitterChainId The chain ID of the emitter. - /// @param emitterAddress The address of the emitter. - /// @param sequence The sequence of the message. - event ReceivedMessage( - bytes32 digest, uint16 emitterChainId, bytes32 emitterAddress, uint64 sequence - ); - - /// @notice Emitted when a message is sent from the transceiver. - /// @dev Topic0 - /// 0x79376a0dc6cbfe6f6f8f89ad24c262a8c6233f8df181d3fe5abb2e2442e8c738. - /// @param recipientChain The chain ID of the recipient. - /// @param message The message. - event SendTransceiverMessage( - uint16 recipientChain, TransceiverStructs.TransceiverMessage message - ); - - /// @notice Error when the relaying configuration is invalid. (e.g. chainId is not registered) - /// @dev Selector: 0x9449a36c. - /// @param chainId The chain ID that is invalid. - error InvalidRelayingConfig(uint16 chainId); - - /// @notice Error when the peer transceiver is invalid. - /// @dev Selector: 0x79b1ce56. - /// @param chainId The chain ID of the peer. - /// @param peerAddress The address of the invalid peer. - error InvalidWormholePeer(uint16 chainId, bytes32 peerAddress); - - /// @notice Error when the VAA has already been consumed. - /// @dev Selector: 0x406e719e. - /// @param vaaHash The hash of the VAA. - error TransferAlreadyCompleted(bytes32 vaaHash); - - /// @notice Receive an attested message from the verification layer. - /// This function should verify the `encodedVm` and then deliver the attestation - /// to the transceiver NttManager contract. - /// @param encodedMessage The attested message. - function receiveMessage( - bytes memory encodedMessage - ) external; - - /// @notice Parses the encoded instruction and returns the instruction struct. - /// This instruction is specific to the WormholeTransceiver contract. - /// @param encoded The encoded instruction. - /// @return instruction The parsed `WormholeTransceiverInstruction`. - function parseWormholeTransceiverInstruction( - bytes memory encoded - ) external pure returns (WormholeTransceiverInstruction memory instruction); - - /// @notice Encodes the `WormholeTransceiverInstruction` into a byte array. - /// @param instruction The `WormholeTransceiverInstruction` to encode. - /// @return encoded The encoded instruction. - function encodeWormholeTransceiverInstruction( - WormholeTransceiverInstruction memory instruction - ) external pure returns (bytes memory); -} diff --git a/evm/src/interfaces/IWormholeTransceiverState.sol b/evm/src/interfaces/IWormholeTransceiverState.sol deleted file mode 100644 index 6b41c7077..000000000 --- a/evm/src/interfaces/IWormholeTransceiverState.sol +++ /dev/null @@ -1,126 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.8 <0.9.0; - -import "../libraries/TransceiverStructs.sol"; - -interface IWormholeTransceiverState { - /// @notice Emitted when a message is sent from the transceiver. - /// @dev Topic0 - /// 0xc3192e083c87c556db539f071d8a298869f487e951327b5616a6f85ae3da958e. - /// @param relayingType The type of relaying. - /// @param deliveryPayment The amount of ether sent along with the tx to cover the delivery fee. - event RelayingInfo(uint8 relayingType, bytes32 refundAddress, uint256 deliveryPayment); - - /// @notice Emitted when a peer transceiver is set. - /// @dev Topic0 - /// 0xa559263ee060c7a2560843b3a064ff0376c9753ae3e2449b595a3b615d326466. - /// @param chainId The chain ID of the peer. - /// @param peerContract The address of the peer contract. - event SetWormholePeer(uint16 chainId, bytes32 peerContract); - - /// @notice Emitted when relaying is enabled for the given chain. - /// @dev Topic0 - /// 0x528b18a533e892b5401d1fb63597275df9d2bb45b13e7695c3147cd07b9746c3. - /// @param chainId The chain ID to set. - /// @param isRelayingEnabled A boolean indicating whether relaying is enabled. - event SetIsWormholeRelayingEnabled(uint16 chainId, bool isRelayingEnabled); - - /// @notice Emitted when special relaying is enabled for the given chain. - /// @dev Topic0 - /// 0x0fe301480713b2c2072ee91b3bcfcbf2c0014f0447c89046f020f0f80727003c. - /// @param chainId The chain ID to set. - event SetIsSpecialRelayingEnabled(uint16 chainId, bool isRelayingEnabled); - - /// @notice Emitted when the chain is EVM compatible. - /// @dev Topic0 - /// 0x4add57d97a7bf5035340ea1212aeeb3d4d3887eb1faf3821a8224c3a6956a10c. - /// @param chainId The chain ID to set. - /// @param isEvm A boolean indicating whether relaying is enabled. - event SetIsWormholeEvmChain(uint16 chainId, bool isEvm); - - /// @notice Additonal messages are not allowed. - /// @dev Selector: 0xc504ea29. - error UnexpectedAdditionalMessages(); - - /// @notice Error if the VAA is invalid. - /// @dev Selector: 0x8ee2e336. - /// @param reason The reason the VAA is invalid. - error InvalidVaa(string reason); - - /// @notice Error if the peer has already been set. - /// @dev Selector: 0xb55eeae9. - /// @param chainId The chain ID of the peer. - /// @param peerAddress The address of the peer. - error PeerAlreadySet(uint16 chainId, bytes32 peerAddress); - - /// @notice Error the peer contract cannot be the zero address. - /// @dev Selector: 0x26e0c7de. - error InvalidWormholePeerZeroAddress(); - - /// @notice The chain ID cannot be zero. - /// @dev Selector: 0x3dd98b24. - error InvalidWormholeChainIdZero(); - - /// @notice The caller is not the relayer. - /// @dev Selector: 0x1c269589. - /// @param caller The caller. - error CallerNotRelayer(address caller); - - /// @notice Get the corresponding Transceiver contract on other chains that have been registered - /// via governance. This design should be extendable to other chains, so each Transceiver would - /// be potentially concerned with Transceivers on multiple other chains. - /// @dev that peers are registered under Wormhole chain ID values. - /// @param chainId The Wormhole chain ID of the peer to get. - /// @return peerContract The address of the peer contract on the given chain. - function getWormholePeer( - uint16 chainId - ) external view returns (bytes32); - - /// @notice Returns a boolean indicating whether the given VAA hash has been consumed. - /// @param hash The VAA hash to check. - function isVAAConsumed( - bytes32 hash - ) external view returns (bool); - - /// @notice Returns a boolean indicating whether Wormhole relaying is enabled for the given chain. - /// @param chainId The Wormhole chain ID to check. - function isWormholeRelayingEnabled( - uint16 chainId - ) external view returns (bool); - - /// @notice Returns a boolean indicating whether special relaying is enabled for the given chain. - /// @param chainId The Wormhole chain ID to check. - function isSpecialRelayingEnabled( - uint16 chainId - ) external view returns (bool); - - /// @notice Returns a boolean indicating whether the given chain is EVM compatible. - /// @param chainId The Wormhole chain ID to check. - function isWormholeEvmChain( - uint16 chainId - ) external view returns (bool); - - /// @notice Set the Wormhole peer contract for the given chain. - /// @dev This function is only callable by the `owner`. - /// @param chainId The Wormhole chain ID of the peer to set. - /// @param peerContract The address of the peer contract on the given chain. - function setWormholePeer(uint16 chainId, bytes32 peerContract) external payable; - - /// @notice Set whether the chain is EVM compatible. - /// @dev This function is only callable by the `owner`. - /// @param chainId The Wormhole chain ID to set. - /// @param isEvm A boolean indicating whether the chain is an EVM chain. - function setIsWormholeEvmChain(uint16 chainId, bool isEvm) external; - - /// @notice Set whether Wormhole relaying is enabled for the given chain. - /// @dev This function is only callable by the `owner`. - /// @param chainId The Wormhole chain ID to set. - /// @param isRelayingEnabled A boolean indicating whether relaying is enabled. - function setIsWormholeRelayingEnabled(uint16 chainId, bool isRelayingEnabled) external; - - /// @notice Set whether special relaying is enabled for the given chain. - /// @dev This function is only callable by the `owner`. - /// @param chainId The Wormhole chain ID to set. - /// @param isRelayingEnabled A boolean indicating whether special relaying is enabled. - function setIsSpecialRelayingEnabled(uint16 chainId, bool isRelayingEnabled) external; -} diff --git a/evm/src/libraries/RateLimiter.sol b/evm/src/libraries/RateLimiter.sol index 877c86ee1..8bee4396d 100644 --- a/evm/src/libraries/RateLimiter.sol +++ b/evm/src/libraries/RateLimiter.sol @@ -70,8 +70,8 @@ abstract contract RateLimiter is IRateLimiter, IRateLimiterEvents { constructor(uint64 _rateLimitDuration, bool _skipRateLimiting) { if ( - _rateLimitDuration == 0 && !_skipRateLimiting - || _rateLimitDuration != 0 && _skipRateLimiting + (_rateLimitDuration == 0 && !_skipRateLimiting) + || (_rateLimitDuration != 0 && _skipRateLimiting) ) { revert UndefinedRateLimiting(); } @@ -302,6 +302,8 @@ abstract contract RateLimiter is IRateLimiter, IRateLimiterEvents { bytes32 recipient, bytes32 refundAddress, address senderAddress, + bytes memory executorQuote, + bytes memory relayInstructions, bytes memory transceiverInstructions ) internal { _getOutboundQueueStorage()[sequence] = OutboundQueuedTransfer({ @@ -311,6 +313,8 @@ abstract contract RateLimiter is IRateLimiter, IRateLimiterEvents { refundAddress: refundAddress, txTimestamp: uint64(block.timestamp), sender: senderAddress, + executorQuote: executorQuote, + relayInstructions: relayInstructions, transceiverInstructions: transceiverInstructions }); diff --git a/evm/src/libraries/TransceiverHelpers.sol b/evm/src/libraries/TransceiverHelpers.sol index 44906ae4d..038b8ed92 100644 --- a/evm/src/libraries/TransceiverHelpers.sol +++ b/evm/src/libraries/TransceiverHelpers.sol @@ -32,3 +32,15 @@ function countSetBits( return count; } + +// @dev Count the number of set bits in a uint128 +function countSetBits128( + uint128 x +) pure returns (uint8 count) { + while (x != 0) { + x &= x - 1; + count++; + } + + return count; +} diff --git a/evm/test/IntegrationAdditionalTransfer.t.sol b/evm/test/IntegrationAdditionalTransfer.t.sol index b281c4abb..965bed964 100755 --- a/evm/test/IntegrationAdditionalTransfer.t.sol +++ b/evm/test/IntegrationAdditionalTransfer.t.sol @@ -5,31 +5,40 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import "../src/NttManager/NttManagerNoRateLimiting.sol"; -import "../src/Transceiver/Transceiver.sol"; import "../src/interfaces/INttManager.sol"; import "../src/interfaces/IRateLimiter.sol"; -import "../src/interfaces/ITransceiver.sol"; import "../src/interfaces/IManagerBase.sol"; import "../src/interfaces/IRateLimiterEvents.sol"; import {Utils} from "./libraries/Utils.sol"; import {DummyToken, DummyTokenMintAndBurn} from "./NttManager.t.sol"; -import "../src/interfaces/IWormholeTransceiver.sol"; -import {WormholeTransceiver} from "../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; import "../src/libraries/TransceiverStructs.sol"; +import "./libraries/TransceiverHelpers.sol"; import "./mocks/MockNttManagerAdditionalPayload.sol"; -import "./mocks/MockTransceivers.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockExecutor.sol"; +import "./mocks/DummyTransceiver.sol"; import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; import "wormhole-solidity-sdk/testing/helpers/WormholeSimulator.sol"; import "wormhole-solidity-sdk/Utils.sol"; +import "example-messaging-endpoint/evm/src/Endpoint.sol"; //import "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; contract TestAdditionalPayload is Test { + MockEndpoint endpointChain1; + MockEndpoint endpointChain2; + + MockExecutor executorChain1; + MockExecutor executorChain2; + NttManagerNoRateLimiting nttManagerChain1; NttManagerNoRateLimiting nttManagerChain2; + DummyTransceiver transceiverChain1; + DummyTransceiver transceiverChain2; + using TrimmedAmountLib for uint256; using TrimmedAmountLib for TrimmedAmount; @@ -44,8 +53,6 @@ contract TestAdditionalPayload is Test { WormholeSimulator guardian; uint256 initialBlockTimestamp; - WormholeTransceiver wormholeTransceiverChain1; - WormholeTransceiver wormholeTransceiverChain2; address userA = address(0x123); address userB = address(0x456); address userC = address(0x789); @@ -61,10 +68,20 @@ contract TestAdditionalPayload is Test { guardian = new WormholeSimulator(address(wormhole), DEVNET_GUARDIAN_PK); + endpointChain1 = new MockEndpoint(chainId1); + endpointChain2 = new MockEndpoint(chainId2); + + executorChain1 = new MockExecutor(chainId1); + executorChain2 = new MockExecutor(chainId2); + vm.chainId(chainId1); DummyToken t1 = new DummyToken(); NttManagerNoRateLimiting implementation = new MockNttManagerAdditionalPayloadContract( - address(t1), IManagerBase.Mode.LOCKING, chainId1 + address(endpointChain1), + address(executorChain1), + address(t1), + IManagerBase.Mode.LOCKING, + chainId1 ); nttManagerChain1 = MockNttManagerAdditionalPayloadContract( @@ -72,37 +89,20 @@ contract TestAdditionalPayload is Test { ); nttManagerChain1.initialize(); - WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain1 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain1Implementation), "")) - ); - - // Only the deployer should be able to initialize - vm.prank(userA); - vm.expectRevert( - abi.encodeWithSelector(ITransceiver.UnexpectedDeployer.selector, address(this), userA) - ); - wormholeTransceiverChain1.initialize(); - - // Actually initialize properly now - wormholeTransceiverChain1.initialize(); - - nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1)); - // nttManagerChain1.setOutboundLimit(type(uint64).max); - // nttManagerChain1.setInboundLimit(type(uint64).max, chainId2); + transceiverChain1 = new DummyTransceiver(chainId1, address(endpointChain1)); + nttManagerChain1.setTransceiver(address(transceiverChain1)); + nttManagerChain1.enableSendTransceiver(chainId2, address(transceiverChain1)); + nttManagerChain1.enableRecvTransceiver(chainId2, address(transceiverChain1)); // Chain 2 setup vm.chainId(chainId2); DummyToken t2 = new DummyTokenMintAndBurn(); NttManagerNoRateLimiting implementationChain2 = new MockNttManagerAdditionalPayloadContract( - address(t2), IManagerBase.Mode.BURNING, chainId2 + address(endpointChain2), + address(executorChain2), + address(t2), + IManagerBase.Mode.BURNING, + chainId2 ); nttManagerChain2 = MockNttManagerAdditionalPayloadContract( @@ -110,49 +110,42 @@ contract TestAdditionalPayload is Test { ); nttManagerChain2.initialize(); - WormholeTransceiver wormholeTransceiverChain2Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain2), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain2Implementation), "")) - ); - wormholeTransceiverChain2.initialize(); - - nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2)); - // nttManagerChain2.setOutboundLimit(type(uint64).max); - // nttManagerChain2.setInboundLimit(type(uint64).max, chainId1); + transceiverChain2 = new DummyTransceiver(chainId2, address(endpointChain2)); + nttManagerChain2.setTransceiver(address(transceiverChain2)); + nttManagerChain2.enableSendTransceiver(chainId1, address(transceiverChain2)); + nttManagerChain2.enableRecvTransceiver(chainId1, address(transceiverChain2)); // Register peer contracts for the nttManager and transceiver. Transceivers and nttManager each have the concept of peers here. nttManagerChain1.setPeer( - chainId2, bytes32(uint256(uint160(address(nttManagerChain2)))), 9, type(uint64).max + chainId2, + bytes32(uint256(uint160(address(nttManagerChain2)))), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); nttManagerChain2.setPeer( - chainId1, bytes32(uint256(uint160(address(nttManagerChain1)))), 7, type(uint64).max + chainId1, + bytes32(uint256(uint160(address(nttManagerChain1)))), + 7, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); - // Set peers for the transceivers - wormholeTransceiverChain1.setWormholePeer( - chainId2, bytes32(uint256(uint160(address(wormholeTransceiverChain2)))) - ); - wormholeTransceiverChain2.setWormholePeer( - chainId1, bytes32(uint256(uint160(address(wormholeTransceiverChain1)))) + require( + nttManagerChain1.getThreshold(chainId2) != 0, + "Threshold is zero with active transceivers" ); - require(nttManagerChain1.getThreshold() != 0, "Threshold is zero with active transceivers"); - // Actually set it - nttManagerChain1.setThreshold(1); - nttManagerChain2.setThreshold(1); + nttManagerChain1.setThreshold(chainId2, 1); + nttManagerChain2.setThreshold(chainId1, 1); INttManager.NttManagerPeer memory peer = nttManagerChain1.getPeer(chainId2); require(9 == peer.tokenDecimals, "Peer has the wrong number of token decimals"); } + function test_setUp() public {} + function test_transferWithAdditionalPayload() public { vm.chainId(chainId1); @@ -169,10 +162,18 @@ contract TestAdditionalPayload is Test { vm.recordLogs(); // Send token through standard means (not relayer) + uint64 seqNo; { uint256 nttManagerBalanceBefore = token1.balanceOf(address(nttManagerChain1)); uint256 userBalanceBefore = token1.balanceOf(address(userA)); - nttManagerChain1.transfer(sendingAmount, chainId2, bytes32(uint256(uint160(userB)))); + seqNo = nttManagerChain1.transfer( + sendingAmount, + chainId2, + bytes32(uint256(uint160(userB))), + executorChain1.createSignedQuote(executorChain2.chainId()), + executorChain1.createRelayInstructions(), + endpointChain1.createAdapterInstructions() + ); // Balance check on funds going in and out working as expected uint256 nttManagerBalanceAfter = token1.balanceOf(address(nttManagerChain1)); @@ -187,10 +188,18 @@ contract TestAdditionalPayload is Test { ); } + assertEq(seqNo, 0); + DummyTransceiver.Message[] memory rmsgs = transceiverChain1.getMessages(); + assertEq(1, rmsgs.length); + + // Get the execution events from the logs. + Vm.Log[] memory recordedLogs = vm.getRecordedLogs(); + bytes memory encoded = + TransceiverHelpersLib.getExecutionSent(recordedLogs, address(nttManagerChain1), seqNo); + vm.stopPrank(); // Get the AdditionalPayloadSent(bytes) event to ensure it matches up with the AdditionalPayloadReceived(bytes) event later - Vm.Log[] memory recordedLogs = vm.getRecordedLogs(); string memory expectedAP = "banana"; string memory sentAP; for (uint256 i = 0; i < recordedLogs.length; i++) { @@ -201,22 +210,21 @@ contract TestAdditionalPayload is Test { } assertEq(sentAP, expectedAP); - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(recordedLogs); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId1); - } - // Chain2 verification and checks vm.chainId(chainId2); // Wrong chain receiving the signed VAA - vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, chainId1, chainId2)); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + vm.expectRevert(Endpoint.InvalidDestinationChain.selector); + transceiverChain1.receiveMessage(rmsgs[0]); + + // Attest on the correct chain. + transceiverChain2.receiveMessage(rmsgs[0]); + { uint256 supplyBefore = token2.totalSupply(); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); + nttManagerChain2.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); uint256 supplyAfter = token2.totalSupply(); require(sendingAmount + supplyBefore == supplyAfter, "Supplies dont match"); @@ -238,15 +246,6 @@ contract TestAdditionalPayload is Test { } assertEq(receivedAP, expectedAP); - // Can't resubmit the same message twice - (IWormhole.VM memory wormholeVM,,) = wormhole.parseAndVerifyVM(encodedVMs[0]); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.TransferAlreadyCompleted.selector, wormholeVM.hash - ) - ); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); - // Go back the other way from a THIRD user vm.prank(userB); token2.transfer(userC, sendingAmount); @@ -258,13 +257,15 @@ contract TestAdditionalPayload is Test { // Supply checks on the transfer { uint256 supplyBefore = token2.totalSupply(); - nttManagerChain2.transfer( + seqNo = nttManagerChain2.transfer( sendingAmount, chainId1, toWormholeFormat(userD), toWormholeFormat(userC), false, - encodeTransceiverInstruction(true) + executorChain2.createSignedQuote(executorChain1.chainId()), + executorChain2.createRelayInstructions(), + endpointChain2.createAdapterInstructions() ); uint256 supplyAfter = token2.totalSupply(); @@ -278,19 +279,24 @@ contract TestAdditionalPayload is Test { ); } - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId2); - } + recordedLogs = vm.getRecordedLogs(); + encoded = + TransceiverHelpersLib.getExecutionSent(recordedLogs, address(nttManagerChain2), seqNo); // Chain1 verification and checks with the receiving of the message vm.chainId(chainId1); + // Attest the message. + rmsgs = transceiverChain2.getMessages(); + assertEq(1, rmsgs.length); + transceiverChain1.receiveMessage(rmsgs[0]); + { uint256 supplyBefore = token1.totalSupply(); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + + nttManagerChain1.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); uint256 supplyAfter = token1.totalSupply(); @@ -300,19 +306,4 @@ contract TestAdditionalPayload is Test { require(token1.balanceOf(userD) == sendingAmount, "User received funds"); } } - - function encodeTransceiverInstruction( - bool relayer_off - ) public view returns (bytes memory) { - WormholeTransceiver.WormholeTransceiverInstruction memory instruction = - IWormholeTransceiver.WormholeTransceiverInstruction(relayer_off); - bytes memory encodedInstructionWormhole = - wormholeTransceiverChain1.encodeWormholeTransceiverInstruction(instruction); - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction = TransceiverStructs - .TransceiverInstruction({index: 0, payload: encodedInstructionWormhole}); - TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](1); - TransceiverInstructions[0] = TransceiverInstruction; - return TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions); - } } diff --git a/evm/test/IntegrationManual.t.sol b/evm/test/IntegrationManual.t.sol deleted file mode 100644 index 56d762996..000000000 --- a/evm/test/IntegrationManual.t.sol +++ /dev/null @@ -1,285 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity 0.8.19; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import {WormholeRelayerBasicTest} from "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; -import "./libraries/IntegrationHelpers.sol"; -import "wormhole-solidity-sdk/testing/helpers/WormholeSimulator.sol"; -import "../src/NttManager/NttManager.sol"; -import "./mocks/MockNttManager.sol"; -import "./mocks/MockTransceivers.sol"; - -import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; - -contract TestRelayerEndToEndManual is IntegrationHelpers, IRateLimiterEvents { - NttManager nttManagerChain1; - NttManager nttManagerChain2; - - using TrimmedAmountLib for uint256; - using TrimmedAmountLib for TrimmedAmount; - - uint16 constant chainId1 = 4; - uint16 constant chainId2 = 5; - uint8 constant FAST_CONSISTENCY_LEVEL = 200; - uint256 constant GAS_LIMIT = 500000; - - uint256 constant DEVNET_GUARDIAN_PK = - 0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0; - WormholeSimulator guardian; - uint256 initialBlockTimestamp; - - address userA = address(0x123); - address userB = address(0x456); - address userC = address(0x789); - address userD = address(0xABC); - - address relayer = address(0x80aC94316391752A193C1c47E27D382b507c93F3); - IWormhole wormhole = IWormhole(0x68605AD7b15c732a30b1BbC62BE8F2A509D74b4D); - - function setUp() public { - string memory url = "https://bsc-testnet-rpc.publicnode.com"; - vm.createSelectFork(url); - initialBlockTimestamp = vm.getBlockTimestamp(); - - guardian = new WormholeSimulator(address(wormhole), DEVNET_GUARDIAN_PK); - - vm.chainId(chainId1); - DummyToken t1 = new DummyToken(); - NttManager implementation = new MockNttManagerContract( - address(t1), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - - nttManagerChain1 = - MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); - nttManagerChain1.initialize(); - - wormholeTransceiverChain1 = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain1 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain1), "")) - ); - wormholeTransceiverChain1.initialize(); - - nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1)); - nttManagerChain1.setOutboundLimit(type(uint64).max); - nttManagerChain1.setInboundLimit(type(uint64).max, chainId2); - - // Chain 2 setup - vm.chainId(chainId2); - DummyToken t2 = new DummyTokenMintAndBurn(); - NttManager implementationChain2 = new MockNttManagerContract( - address(t2), IManagerBase.Mode.BURNING, chainId2, 1 days, false - ); - - nttManagerChain2 = - MockNttManagerContract(address(new ERC1967Proxy(address(implementationChain2), ""))); - nttManagerChain2.initialize(); - wormholeTransceiverChain2 = new MockWormholeTransceiverContract( - address(nttManagerChain2), - address(wormhole), - address(relayer), // TODO - add support for this later - address(0x0), // TODO - add support for this later - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain2), "")) - ); - wormholeTransceiverChain2.initialize(); - - nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2)); - nttManagerChain2.setOutboundLimit(type(uint64).max); - nttManagerChain2.setInboundLimit(type(uint64).max, chainId1); - - // Register peer contracts for the nttManager and transceiver. Transceivers and nttManager each have the concept of peers here. - nttManagerChain1.setPeer( - chainId2, bytes32(uint256(uint160(address(nttManagerChain2)))), 9, type(uint64).max - ); - nttManagerChain2.setPeer( - chainId1, bytes32(uint256(uint160(address(nttManagerChain1)))), 7, type(uint64).max - ); - } - - function test_relayerTransceiverAuth() public { - // Set up sensible WH transceiver peers - _setTransceiverPeers( - [wormholeTransceiverChain1, wormholeTransceiverChain2], - [wormholeTransceiverChain2, wormholeTransceiverChain1], - [chainId2, chainId1] - ); - - vm.recordLogs(); - vm.chainId(chainId1); - - // Setting up the transfer - DummyToken token1 = DummyToken(nttManagerChain1.token()); - - uint8 decimals = token1.decimals(); - uint256 sendingAmount = 5 * 10 ** decimals; - _prepareTransfer(token1, userA, address(nttManagerChain1), sendingAmount); - - vm.deal(userA, 1 ether); - WormholeTransceiver[] memory transceiver = new WormholeTransceiver[](1); - transceiver[0] = wormholeTransceiverChain1; - - // Send token through the relayer - transferToken(userB, userA, nttManagerChain1, sendingAmount, chainId2, transceiver, false); - - // Get the messages from the logs for the sender - vm.chainId(chainId2); - - bytes[] memory encodedVMs = _getWormholeMessage(guardian, vm.getRecordedLogs(), chainId1); - - IWormhole.VM memory vaa = wormhole.parseVM(encodedVMs[0]); - - vm.stopPrank(); - vm.chainId(chainId2); - - // Set bad manager peer (0x1) - nttManagerChain2.setPeer(chainId1, toWormholeFormat(address(0x1)), 9, type(uint64).max); - - vm.startPrank(relayer); - - bytes[] memory a; - vm.expectRevert( - abi.encodeWithSelector( - INttManager.InvalidPeer.selector, chainId1, address(nttManagerChain1) - ) - ); - _receiveWormholeMessage( - vaa, wormholeTransceiverChain1, wormholeTransceiverChain2, vaa.emitterChainId, a - ); - vm.stopPrank(); - - _setManagerPeer(nttManagerChain2, nttManagerChain1, chainId1, 9, type(uint64).max); - - // Wrong caller - aka not relayer contract - vm.prank(userD); - vm.expectRevert( - abi.encodeWithSelector(IWormholeTransceiverState.CallerNotRelayer.selector, userD) - ); - _receiveWormholeMessage( - vaa, wormholeTransceiverChain1, wormholeTransceiverChain2, vaa.emitterChainId, a - ); - - vm.startPrank(relayer); - - // Bad chain ID for a given transceiver - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.InvalidWormholePeer.selector, - 0xFF, - address(wormholeTransceiverChain1) - ) - ); - wormholeTransceiverChain2.receiveWormholeMessages( - vaa.payload, - a, - bytes32(uint256(uint160(address(wormholeTransceiverChain1)))), - 0xFF, - vaa.hash - ); - - /* - This information is assumed to be trusted since ONLY the relayer on a given chain can call it. - However, it's still good to test various things. - - This attempt should actually work this time. - */ - wormholeTransceiverChain2.receiveWormholeMessages( - vaa.payload, // Verified - a, // Should be zero - bytes32(uint256(uint160(address(wormholeTransceiverChain1)))), // Must be a wormhole peers - vaa.emitterChainId, // ChainID from the call - vaa.hash // Hash of the VAA being used - ); - - // Should from sending a *duplicate* message - vm.expectRevert( - abi.encodeWithSelector(IWormholeTransceiver.TransferAlreadyCompleted.selector, vaa.hash) - ); - wormholeTransceiverChain2.receiveWormholeMessages( - vaa.payload, - a, // Should be zero - bytes32(uint256(uint160(address(wormholeTransceiverChain1)))), // Must be a wormhole peers - vaa.emitterChainId, // ChainID from the call - vaa.hash // Hash of the VAA being used - ); - } - - function test_relayerWithInvalidWHTransceiver() public { - // Set up dodgy wormhole transceiver peers - wormholeTransceiverChain2.setWormholePeer(chainId1, bytes32(uint256(uint160(address(0x1))))); - wormholeTransceiverChain1.setWormholePeer( - chainId2, bytes32(uint256(uint160(address(wormholeTransceiverChain2)))) - ); - - vm.recordLogs(); - vm.chainId(chainId1); - - // Setting up the transfer - DummyToken token1 = DummyToken(nttManagerChain1.token()); - - uint8 decimals = token1.decimals(); - uint256 sendingAmount = 5 * 10 ** decimals; - token1.mintDummy(address(userA), 5 * 10 ** decimals); - vm.startPrank(userA); - token1.approve(address(nttManagerChain1), sendingAmount); - - // Send token through the relayer - { - vm.deal(userA, 1 ether); - nttManagerChain1.transfer{ - value: wormholeTransceiverChain1.quoteDeliveryPrice( - chainId2, buildTransceiverInstruction(false) - ) - }( - sendingAmount, - chainId2, - bytes32(uint256(uint160(userB))), - bytes32(uint256(uint160(userA))), - false, - encodeTransceiverInstruction(false) - ); - } - - // Get the messages from the logs for the sender - vm.chainId(chainId2); - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId1); - } - - IWormhole.VM memory vaa = wormhole.parseVM(encodedVMs[0]); - - vm.stopPrank(); - vm.chainId(chainId2); - - // Caller is not proper who to receive messages from - bytes[] memory a; - vm.startPrank(relayer); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.InvalidWormholePeer.selector, - chainId1, - address(wormholeTransceiverChain1) - ) - ); - wormholeTransceiverChain2.receiveWormholeMessages( - vaa.payload, - a, - bytes32(uint256(uint160(address(wormholeTransceiverChain1)))), - vaa.emitterChainId, - vaa.hash - ); - vm.stopPrank(); - } -} diff --git a/evm/test/IntegrationRelayer.t.sol b/evm/test/IntegrationRelayer.t.sol deleted file mode 100755 index 081e4f4ba..000000000 --- a/evm/test/IntegrationRelayer.t.sol +++ /dev/null @@ -1,579 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity 0.8.19; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; - -import "../src/NttManager/NttManager.sol"; -import "../src/Transceiver/Transceiver.sol"; -import "../src/interfaces/INttManager.sol"; -import "../src/interfaces/IRateLimiter.sol"; -import "../src/interfaces/IManagerBase.sol"; -import "../src/interfaces/IRateLimiterEvents.sol"; -import "../src/interfaces/IWormholeTransceiver.sol"; -import "../src/interfaces/IWormholeTransceiverState.sol"; -import {Utils} from "./libraries/Utils.sol"; -import {DummyToken, DummyTokenMintAndBurn} from "../src/mocks/DummyToken.sol"; -import {WormholeTransceiver} from "../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; -import "../src/libraries/TransceiverStructs.sol"; -import "./libraries/TransceiverHelpers.sol"; -import "./mocks/MockNttManager.sol"; -import "./mocks/MockTransceivers.sol"; - -import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; -import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; -import "wormhole-solidity-sdk/testing/helpers/WormholeSimulator.sol"; -import "wormhole-solidity-sdk/Utils.sol"; -import {WormholeRelayerBasicTest} from "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; -import "./libraries/IntegrationHelpers.sol"; - -contract TestEndToEndRelayer is IntegrationHelpers, IRateLimiterEvents, WormholeRelayerBasicTest { - NttManager nttManagerChain1; - NttManager nttManagerChain2; - - using TrimmedAmountLib for uint256; - using TrimmedAmountLib for TrimmedAmount; - - uint16 constant chainId1 = 4; - uint16 constant chainId2 = 6; - uint8 constant FAST_CONSISTENCY_LEVEL = 200; - uint256 constant GAS_LIMIT = 500000; - - WormholeSimulator guardian; - uint256 initialBlockTimestamp; - - address userA = address(0x123); - address userB = address(0x456); - address userC = address(0x789); - address userD = address(0xABC); - - constructor() { - setTestnetForkChains(chainId1, chainId2); - } - - // https://github.com/wormhole-foundation/hello-wormhole/blob/main/test/HelloWormhole.t.sol#L14C1-L20C6 - // Setup the starting point of the network - function setUpSource() public override { - vm.deal(userA, 1 ether); - DummyToken t1 = new DummyToken(); - - NttManager implementation = new MockNttManagerContract( - address(t1), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - - nttManagerChain1 = - MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); - nttManagerChain1.initialize(); - - wormholeTransceiverChain1 = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(chainInfosTestnet[chainId1].wormhole), - address(relayerSource), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - wormholeTransceiverChain1 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain1), "")) - ); - wormholeTransceiverChain1.initialize(); - wormholeTransceiverChain1Other = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(chainInfosTestnet[chainId1].wormhole), - address(relayerSource), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - wormholeTransceiverChain1Other = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain1Other), "")) - ); - wormholeTransceiverChain1Other.initialize(); - - nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1)); - nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1Other)); - nttManagerChain1.setOutboundLimit(type(uint64).max); - nttManagerChain1.setInboundLimit(type(uint64).max, chainId2); - nttManagerChain1.setThreshold(1); - } - - // Setup the chain to relay to of the network - function setUpTarget() public override { - vm.deal(userC, 1 ether); - - // Chain 2 setup - DummyToken t2 = new DummyTokenMintAndBurn(); - NttManager implementationChain2 = new MockNttManagerContract( - address(t2), IManagerBase.Mode.BURNING, chainId2, 1 days, false - ); - - nttManagerChain2 = - MockNttManagerContract(address(new ERC1967Proxy(address(implementationChain2), ""))); - nttManagerChain2.initialize(); - wormholeTransceiverChain2 = new MockWormholeTransceiverContract( - address(nttManagerChain2), - address(chainInfosTestnet[chainId2].wormhole), - address(relayerTarget), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - wormholeTransceiverChain2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain2), "")) - ); - wormholeTransceiverChain2.initialize(); - - wormholeTransceiverChain2Other = new MockWormholeTransceiverContract( - address(nttManagerChain2), - address(chainInfosTestnet[chainId2].wormhole), - address(relayerTarget), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - wormholeTransceiverChain2Other = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain2Other), "")) - ); - wormholeTransceiverChain2Other.initialize(); - - nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2)); - nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2Other)); - nttManagerChain2.setOutboundLimit(type(uint64).max); - nttManagerChain2.setInboundLimit(type(uint64).max, chainId1); - - nttManagerChain2.setThreshold(1); - } - - function test_chainToChainReverts() public { - // record all of the logs for all of the occuring events - vm.recordLogs(); - - // Setup the information for interacting with the chains - vm.selectFork(targetFork); - wormholeTransceiverChain2.setWormholePeer( - chainId1, bytes32(uint256(uint160(address(wormholeTransceiverChain1)))) - ); - nttManagerChain2.setPeer( - chainId1, bytes32(uint256(uint160(address(nttManagerChain1)))), 9, type(uint64).max - ); - DummyToken token2 = DummyTokenMintAndBurn(nttManagerChain2.token()); - wormholeTransceiverChain2.setIsWormholeRelayingEnabled(chainId1, true); - wormholeTransceiverChain2.setIsWormholeEvmChain(chainId1, true); - - // Register peer contracts for the nttManager and transceiver. Transceivers and nttManager each have the concept of peers here. - vm.selectFork(sourceFork); - DummyToken token1 = DummyToken(nttManagerChain1.token()); - wormholeTransceiverChain1.setWormholePeer( - chainId2, bytes32(uint256(uint160((address(wormholeTransceiverChain2))))) - ); - nttManagerChain1.setPeer( - chainId2, bytes32(uint256(uint160(address(nttManagerChain2)))), 7, type(uint64).max - ); - - // Enable general relaying on the chain to transfer for the funds. - wormholeTransceiverChain1.setIsWormholeRelayingEnabled(chainId2, true); - wormholeTransceiverChain1.setIsWormholeEvmChain(chainId2, true); - - // Setting up the transfer - uint8 decimals = token1.decimals(); - uint256 sendingAmount = 5 * 10 ** decimals; - token1.mintDummy(address(userA), sendingAmount); - vm.startPrank(userA); - token1.approve(address(nttManagerChain1), sendingAmount); - - // Send token through standard means (not relayer) - { - uint256 nttManagerBalanceBefore = token1.balanceOf(address(nttManagerChain1)); - uint256 userBalanceBefore = token1.balanceOf(address(userA)); - - uint256 priceQuote1 = wormholeTransceiverChain1.quoteDeliveryPrice( - chainId2, buildTransceiverInstruction(false) - ); - bytes memory instructions = encodeTransceiverInstruction(false); - - // set invalid config - vm.stopPrank(); - wormholeTransceiverChain1.setIsWormholeEvmChain(chainId2, false); - - // config not set correctly - vm.startPrank(userA); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.InvalidRelayingConfig.selector, chainId2 - ) - ); - nttManagerChain1.transfer{value: priceQuote1}( - sendingAmount, - chainId2, - bytes32(uint256(uint160(userB))), - bytes32(uint256(uint160(userB))), - false, - instructions - ); - - // set valid config - vm.stopPrank(); - wormholeTransceiverChain1.setIsWormholeEvmChain(chainId2, true); - vm.startPrank(userA); - - // revert if transfer amount has dust - uint256 amount = sendingAmount - 1; - TrimmedAmount trimmedAmount = amount.trim(decimals, 7); - uint256 newAmount = trimmedAmount.untrim(decimals); - vm.expectRevert( - abi.encodeWithSelector( - INttManager.TransferAmountHasDust.selector, amount, amount - newAmount - ) - ); - nttManagerChain1.transfer{value: priceQuote1}( - sendingAmount - 1, - chainId2, - bytes32(uint256(uint160(userB))), - bytes32(uint256(uint160(userB))), - false, - instructions - ); - - // Zero funds error - vm.expectRevert(abi.encodeWithSelector(INttManager.ZeroAmount.selector)); - nttManagerChain1.transfer{value: priceQuote1}( - 0, - chainId2, - bytes32(uint256(uint160(userB))), - bytes32(uint256(uint160(userB))), - false, - instructions - ); - - // Not enough in gas costs from the 'quote'. - vm.expectRevert( - abi.encodeWithSelector( - IManagerBase.DeliveryPaymentTooLow.selector, priceQuote1, priceQuote1 - 1 - ) - ); - nttManagerChain1.transfer{value: priceQuote1 - 1}( - sendingAmount, - chainId2, - bytes32(uint256(uint160(userB))), - bytes32(uint256(uint160(userB))), - false, - instructions - ); - - // Do the payment with slightly more gas than needed. This should result in a *payback* of 1 wei. - nttManagerChain1.transfer{value: priceQuote1 + 1}( - sendingAmount, - chainId2, - bytes32(uint256(uint160(userB))), - bytes32(uint256(uint160(userB))), - false, - instructions - ); - - // Balance check on funds going in and out working as expected - uint256 nttManagerBalanceAfter = token1.balanceOf(address(nttManagerChain1)); - uint256 userBalanceAfter = token1.balanceOf(address(userB)); - require( - nttManagerBalanceBefore + sendingAmount == nttManagerBalanceAfter, - "Should be locking the tokens" - ); - require( - userBalanceBefore - sendingAmount == userBalanceAfter, - "User should have sent tokens" - ); - } - - vm.stopPrank(); - - vm.selectFork(targetFork); // Move to the target chain briefly to get the total supply - uint256 supplyBefore = token2.totalSupply(); - - // Deliver the TX via the relayer mechanism. That's pretty fly! - vm.selectFork(sourceFork); // Move to back to the source chain for things to be processed - - // Turn on the log recording because we want the test framework to pick up the events. - // TODO - can't do easy testing on this. - // Foundry *eats* the logs. So, once this fails, they're gone forever. Need to set up signer then, prank then make the call to relay manually. - performDelivery(); - - vm.selectFork(targetFork); // Move to back to the target chain to look at how things were processed - - uint256 supplyAfter = token2.totalSupply(); - - require(sendingAmount + supplyBefore == supplyAfter, "Supplies dont match"); - require(token2.balanceOf(userB) == sendingAmount, "User didn't receive tokens"); - require(token2.balanceOf(address(nttManagerChain2)) == 0, "NttManager has unintended funds"); - } - - function test_chainToChainBase() public { - // record all of the logs for all of the occuring events - vm.recordLogs(); - - // Setup the information for interacting with the chains - vm.selectFork(targetFork); - wormholeTransceiverChain2.setWormholePeer( - chainId1, bytes32(uint256(uint160(address(wormholeTransceiverChain1)))) - ); - nttManagerChain2.setPeer( - chainId1, bytes32(uint256(uint160(address(nttManagerChain1)))), 9, type(uint64).max - ); - DummyToken token2 = DummyTokenMintAndBurn(nttManagerChain2.token()); - wormholeTransceiverChain2.setIsWormholeRelayingEnabled(chainId1, true); - wormholeTransceiverChain2.setIsWormholeEvmChain(chainId1, true); - - // Register peer contracts for the nttManager and transceiver. Transceivers and nttManager each have the concept of peers here. - vm.selectFork(sourceFork); - nttManagerChain1.setPeer( - chainId2, bytes32(uint256(uint160(address(nttManagerChain2)))), 7, type(uint64).max - ); - wormholeTransceiverChain1.setWormholePeer( - chainId2, bytes32(uint256(uint160((address(wormholeTransceiverChain2))))) - ); - DummyToken token1 = DummyToken(nttManagerChain1.token()); - - // Enable general relaying on the chain to transfer for the funds. - wormholeTransceiverChain1.setIsWormholeRelayingEnabled(chainId2, true); - wormholeTransceiverChain1.setIsWormholeEvmChain(chainId2, true); - - // Setting up the transfer - uint8 decimals = token1.decimals(); - uint256 sendingAmount = 5 * 10 ** decimals; - token1.mintDummy(address(userA), 5 * 10 ** decimals); - vm.startPrank(userA); - token1.approve(address(nttManagerChain1), sendingAmount); - - // Send token through standard means (not relayer) - { - uint256 nttManagerBalanceBefore = token1.balanceOf(address(nttManagerChain1)); - uint256 userBalanceBefore = token1.balanceOf(address(userA)); - - nttManagerChain1.transfer{ - value: wormholeTransceiverChain1.quoteDeliveryPrice( - chainId2, buildTransceiverInstruction(false) - ) - }( - sendingAmount, - chainId2, - bytes32(uint256(uint160(userB))), - // refund the amount back to the user that sent the transfer - bytes32(uint256(uint160(userA))), - false, - encodeTransceiverInstruction(false) - ); - - // Balance check on funds going in and out working as expected - uint256 nttManagerBalanceAfter = token1.balanceOf(address(nttManagerChain1)); - uint256 userBalanceAfter = token1.balanceOf(address(userB)); - require( - nttManagerBalanceBefore + sendingAmount == nttManagerBalanceAfter, - "Should be locking the tokens" - ); - require( - userBalanceBefore - sendingAmount == userBalanceAfter, - "User should have sent tokens" - ); - } - - vm.stopPrank(); - - vm.selectFork(targetFork); // Move to the target chain briefly to get the total supply - uint256 supplyBefore = token2.totalSupply(); - - // Deliver the TX via the relayer mechanism. That's pretty fly! - vm.selectFork(sourceFork); // Move to back to the source chain for things to be processed - // Turn on the log recording because we want the test framework to pick up the events. - performDelivery(); - - vm.selectFork(targetFork); // Move to back to the target chain to look at how things were processed - - uint256 supplyAfter = token2.totalSupply(); - - require(sendingAmount + supplyBefore == supplyAfter, "Supplies not changed - minting"); - require(token2.balanceOf(userB) == sendingAmount, "User didn't receive tokens"); - require(token2.balanceOf(address(nttManagerChain2)) == 0, "NttManager has unintended funds"); - - // Go back the other way from a THIRD user - vm.prank(userB); - token2.transfer(userC, sendingAmount); - - vm.startPrank(userC); - token2.approve(address(nttManagerChain2), sendingAmount); - - { - supplyBefore = token2.totalSupply(); - nttManagerChain2.transfer{ - value: wormholeTransceiverChain2.quoteDeliveryPrice( - chainId1, buildTransceiverInstruction(false) - ) - }( - sendingAmount, - chainId1, - bytes32(uint256(uint160(userD))), - bytes32(uint256(uint160(userC))), - false, - encodeTransceiverInstruction(false) - ); - - supplyAfter = token2.totalSupply(); - - require( - sendingAmount - supplyBefore == supplyAfter, - "Supplies don't match - tokens not burned" - ); - require(token2.balanceOf(userB) == 0, "OG user receive tokens"); - require(token2.balanceOf(userC) == 0, "Sending user didn't receive tokens"); - require( - token2.balanceOf(address(nttManagerChain2)) == 0, - "NttManager didn't receive unintended funds" - ); - } - - // Receive the transfer - vm.selectFork(sourceFork); // Move to the source chain briefly to get the total supply - supplyBefore = token1.totalSupply(); - - vm.selectFork(targetFork); // Move to the target chain for log processing - - // Deliver the TX via the relayer mechanism. That's pretty fly! - performDelivery(); - - vm.selectFork(sourceFork); // Move back to the source chain to check out the balances - - require(supplyBefore - sendingAmount == supplyAfter, "Supplies weren't burned as expected"); - require(token1.balanceOf(userA) == 0, "UserA received funds on the transfer back"); - require(token1.balanceOf(userB) == 0, "UserB received funds on the transfer back"); - require(token1.balanceOf(userC) == 0, "UserC received funds on the transfer back"); - require(token1.balanceOf(userD) == sendingAmount, "User didn't receive tokens going back"); - require( - token1.balanceOf(address(nttManagerChain1)) == 0, - "NttManager has unintended funds going back" - ); - } - - function deliverViaRelayer() public { - vm.selectFork(sourceFork); - performDelivery(); - } - - /// @dev Checks that a refund is issued to an address - /// specified by the client on a transfer via the - /// standard relaying path. - function test_getRefundsAfterStandardRelay() public { - // record all of the logs for all of the occuring events - vm.recordLogs(); - - // Setup the information for interacting with the chains - vm.selectFork(targetFork); - - // set the manager and transceiver peers - _setTransceiverPeers( - [wormholeTransceiverChain2, wormholeTransceiverChain2Other], - [wormholeTransceiverChain1, wormholeTransceiverChain1Other], - [chainId1, chainId1] - ); - nttManagerChain2.setPeer( - chainId1, toWormholeFormat(address(nttManagerChain1)), 9, type(uint64).max - ); - - // setup token - DummyToken token2 = DummyTokenMintAndBurn(nttManagerChain2.token()); - - // enable standard relaying - _enableSR([wormholeTransceiverChain2, wormholeTransceiverChain2Other], chainId1); - - // set the manager and transceiver peers - vm.selectFork(sourceFork); - _setTransceiverPeers( - [wormholeTransceiverChain1, wormholeTransceiverChain1Other], - [wormholeTransceiverChain2, wormholeTransceiverChain2Other], - [chainId2, chainId2] - ); - nttManagerChain1.setPeer( - chainId2, toWormholeFormat(address(nttManagerChain2)), 7, type(uint64).max - ); - - DummyToken token1 = DummyToken(nttManagerChain1.token()); - uint8 decimals = token1.decimals(); - - // enable standard relaying - _enableSR([wormholeTransceiverChain1, wormholeTransceiverChain1Other], chainId2); - - uint256 sendingAmount = 5 * 10 ** token1.decimals(); - _prepareTransfer(token1, userA, address(nttManagerChain1), sendingAmount); - - uint256 nttManagerBalanceBefore = token1.balanceOf(address(nttManagerChain1)); - uint256 userBalanceBefore = token1.balanceOf(address(userA)); - - // send token through standard relayer - WormholeTransceiver[] memory transceivers = new WormholeTransceiver[](2); - transceivers[0] = wormholeTransceiverChain1; - transceivers[1] = wormholeTransceiverChain1Other; - - // create fresh address so ether balance before transfer is 0. - address refundAddress = address(0x478); - transferToken( - userB, refundAddress, nttManagerChain1, sendingAmount, chainId2, transceivers, false - ); - // Balance check on funds going in and out working as expected - uint256 nttManagerBalanceAfter = token1.balanceOf(address(nttManagerChain1)); - uint256 userBalanceAfter = token1.balanceOf(address(userB)); - - assertEq( - nttManagerBalanceBefore + sendingAmount, - nttManagerBalanceAfter, - "Should be locking the tokens" - ); - assertEq( - userBalanceBefore - sendingAmount, userBalanceAfter, "User should have sent tokens" - ); - vm.stopPrank(); - uint256 supplyBefore = getTotalSupply(targetFork, token2); - - // Deliver the TX via the relayer mechanism. - deliverViaRelayer(); - - // sanity checks - vm.selectFork(targetFork); - uint256 supplyAfter = token2.totalSupply(); - assertEq(sendingAmount + supplyBefore, supplyAfter, "Supplies not changed - minting"); - assertEq(token2.balanceOf(userB), sendingAmount, "User didn't receive tokens"); - assertEq(token2.balanceOf(address(nttManagerChain2)), 0, "NttManager has unintended funds"); - - // push variables onto the stack again to avoid stack too deep error - uint256 sendingAmt = sendingAmount; - uint8 decs = decimals; - DummyToken tokenPush = token1; - bytes32 hash = _computeManagerMessageDigest( - userA, userB, sendingAmt.trim(decs, 7), address(tokenPush), chainId1, chainId2 - ); - - // number of attestations on the message should be equal to the number of transceivers - assertEq(nttManagerChain2.messageAttestations(hash), 2); - - // check that the message has been executed at this point - // replay protecion in `executeMsg` should emit the `MessageAlreadyExecuted` event. - assertTrue(nttManagerChain2.isMessageExecuted(hash)); - - // ether balance of refund address should be > 0, given that - // the threshold < # of enabled transceivers - assertGt(refundAddress.balance, 0); - } - - function copyBytes( - bytes memory _bytes - ) private pure returns (bytes memory) { - bytes memory copy = new bytes(_bytes.length); - uint256 max = _bytes.length + 31; - for (uint256 i = 32; i <= max; i += 32) { - assembly { - mstore(add(copy, i), mload(add(_bytes, i))) - } - } - return copy; - } -} diff --git a/evm/test/IntegrationStandalone.t.sol b/evm/test/IntegrationStandalone.t.sol index ecaa2b310..e9d329018 100755 --- a/evm/test/IntegrationStandalone.t.sol +++ b/evm/test/IntegrationStandalone.t.sol @@ -5,31 +5,39 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import "../src/NttManager/NttManager.sol"; -import "../src/Transceiver/Transceiver.sol"; import "../src/interfaces/INttManager.sol"; import "../src/interfaces/IRateLimiter.sol"; -import "../src/interfaces/ITransceiver.sol"; import "../src/interfaces/IManagerBase.sol"; import "../src/interfaces/IRateLimiterEvents.sol"; import {Utils} from "./libraries/Utils.sol"; import {DummyToken, DummyTokenMintAndBurn} from "./NttManager.t.sol"; -import "../src/interfaces/IWormholeTransceiver.sol"; -import {WormholeTransceiver} from "../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; import "../src/libraries/TransceiverStructs.sol"; +import "./libraries/TransceiverHelpers.sol"; +import "./mocks/DummyTransceiver.sol"; import "./mocks/MockNttManager.sol"; -import "./mocks/MockTransceivers.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockExecutor.sol"; import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; import "wormhole-solidity-sdk/testing/helpers/WormholeSimulator.sol"; import "wormhole-solidity-sdk/Utils.sol"; -//import "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; +import "example-messaging-endpoint/evm/src/Endpoint.sol"; contract TestEndToEndBase is Test, IRateLimiterEvents { NttManager nttManagerChain1; NttManager nttManagerChain2; + MockEndpoint endpointChain1; + MockEndpoint endpointChain2; + + MockExecutor executorChain1; + MockExecutor executorChain2; + + DummyTransceiver transceiverChain1; + DummyTransceiver transceiverChain2; + using TrimmedAmountLib for uint256; using TrimmedAmountLib for TrimmedAmount; @@ -44,8 +52,6 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { WormholeSimulator guardian; uint256 initialBlockTimestamp; - WormholeTransceiver wormholeTransceiverChain1; - WormholeTransceiver wormholeTransceiverChain2; address userA = address(0x123); address userB = address(0x456); address userC = address(0x789); @@ -61,39 +67,33 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { guardian = new WormholeSimulator(address(wormhole), DEVNET_GUARDIAN_PK); + endpointChain1 = new MockEndpoint(chainId1); + endpointChain2 = new MockEndpoint(chainId2); + + executorChain1 = new MockExecutor(chainId1); + executorChain2 = new MockExecutor(chainId2); + vm.chainId(chainId1); DummyToken t1 = new DummyToken(); NttManager implementation = new MockNttManagerContract( - address(t1), IManagerBase.Mode.LOCKING, chainId1, 1 days, false + address(endpointChain1), + address(executorChain1), + address(t1), + IManagerBase.Mode.LOCKING, + chainId1, + 1 days, + false ); nttManagerChain1 = MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); nttManagerChain1.initialize(); - WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain1 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain1Implementation), "")) - ); - - // Only the deployer should be able to initialize - vm.prank(userA); - vm.expectRevert( - abi.encodeWithSelector(ITransceiver.UnexpectedDeployer.selector, address(this), userA) - ); - wormholeTransceiverChain1.initialize(); - - // Actually initialize properly now - wormholeTransceiverChain1.initialize(); + transceiverChain1 = new DummyTransceiver(chainId1, address(endpointChain1)); + nttManagerChain1.setTransceiver(address(transceiverChain1)); + nttManagerChain1.enableSendTransceiver(chainId2, address(transceiverChain1)); + nttManagerChain1.enableRecvTransceiver(chainId2, address(transceiverChain1)); - nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1)); nttManagerChain1.setOutboundLimit(type(uint64).max); nttManagerChain1.setInboundLimit(type(uint64).max, chainId2); @@ -101,53 +101,54 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { vm.chainId(chainId2); DummyToken t2 = new DummyTokenMintAndBurn(); NttManager implementationChain2 = new MockNttManagerContract( - address(t2), IManagerBase.Mode.BURNING, chainId2, 1 days, false + address(endpointChain2), + address(executorChain2), + address(t2), + IManagerBase.Mode.BURNING, + chainId2, + 1 days, + false ); nttManagerChain2 = MockNttManagerContract(address(new ERC1967Proxy(address(implementationChain2), ""))); nttManagerChain2.initialize(); - WormholeTransceiver wormholeTransceiverChain2Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain2), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain2Implementation), "")) - ); - wormholeTransceiverChain2.initialize(); + transceiverChain2 = new DummyTransceiver(chainId2, address(endpointChain2)); + nttManagerChain2.setTransceiver(address(transceiverChain2)); + nttManagerChain2.enableSendTransceiver(chainId1, address(transceiverChain2)); + nttManagerChain2.enableRecvTransceiver(chainId1, address(transceiverChain2)); - nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2)); nttManagerChain2.setOutboundLimit(type(uint64).max); - nttManagerChain2.setInboundLimit(type(uint64).max, chainId1); + nttManagerChain2.setInboundLimit(type(uint64).max, chainId2); // Register peer contracts for the nttManager and transceiver. Transceivers and nttManager each have the concept of peers here. nttManagerChain1.setPeer( - chainId2, bytes32(uint256(uint160(address(nttManagerChain2)))), 9, type(uint64).max + chainId2, + bytes32(uint256(uint160(address(nttManagerChain2)))), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); nttManagerChain2.setPeer( - chainId1, bytes32(uint256(uint160(address(nttManagerChain1)))), 7, type(uint64).max + chainId1, + bytes32(uint256(uint160(address(nttManagerChain1)))), + 7, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); - // Set peers for the transceivers - wormholeTransceiverChain1.setWormholePeer( - chainId2, bytes32(uint256(uint160(address(wormholeTransceiverChain2)))) - ); - wormholeTransceiverChain2.setWormholePeer( - chainId1, bytes32(uint256(uint160(address(wormholeTransceiverChain1)))) + require( + nttManagerChain1.getThreshold(chainId2) != 0, + "Threshold is zero with active transceivers" ); - require(nttManagerChain1.getThreshold() != 0, "Threshold is zero with active transceivers"); - - // Actually set it - nttManagerChain1.setThreshold(1); - nttManagerChain2.setThreshold(1); + nttManagerChain1.setThreshold(chainId2, 1); + nttManagerChain2.setThreshold(chainId1, 1); } + function test_setUp() public {} + function test_chainToChainBase() public { vm.chainId(chainId1); @@ -164,10 +165,18 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { vm.recordLogs(); // Send token through standard means (not relayer) + uint64 seqNo; { uint256 nttManagerBalanceBefore = token1.balanceOf(address(nttManagerChain1)); uint256 userBalanceBefore = token1.balanceOf(address(userA)); - nttManagerChain1.transfer(sendingAmount, chainId2, bytes32(uint256(uint160(userB)))); + seqNo = nttManagerChain1.transfer( + sendingAmount, + chainId2, + bytes32(uint256(uint160(userB))), + executorChain1.createSignedQuote(executorChain2.chainId()), + executorChain1.createRelayInstructions(), + endpointChain1.createAdapterInstructions() + ); // Balance check on funds going in and out working as expected uint256 nttManagerBalanceAfter = token1.balanceOf(address(nttManagerChain1)); @@ -182,35 +191,33 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { ); } - vm.stopPrank(); + assertEq(0, seqNo); + DummyTransceiver.Message[] memory rmsgs = transceiverChain1.getMessages(); + assertEq(1, rmsgs.length); - // Get the TransferSent(bytes32) event to ensure it matches up with the TransferRedeemed(bytes32) event later - Vm.Log[] memory recordedLogs = vm.getRecordedLogs(); - bytes32 sentEventDigest; - for (uint256 i = 0; i < recordedLogs.length; i++) { - if (recordedLogs[i].topics[0] == keccak256("TransferSent(bytes32)")) { - sentEventDigest = recordedLogs[i].topics[1]; - break; - } - } - require(sentEventDigest != bytes32(0), "TransferSent(bytes32) event should be found"); + // Get the execution events from the logs. + Vm.Log[] memory logEvents = vm.getRecordedLogs(); - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(recordedLogs); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId1); - } + bytes memory encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain1), seqNo); + + vm.stopPrank(); // Chain2 verification and checks vm.chainId(chainId2); - // Wrong chain receiving the signed VAA - vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, chainId1, chainId2)); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + // Wrong chain receiving the attestation. + vm.expectRevert(abi.encodeWithSelector(Endpoint.InvalidDestinationChain.selector)); + transceiverChain1.receiveMessage(rmsgs[0]); + + // Right chain receiving the attestation. + transceiverChain2.receiveMessage(rmsgs[0]); + { uint256 supplyBefore = token2.totalSupply(); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); + nttManagerChain2.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); uint256 supplyAfter = token2.totalSupply(); require(sendingAmount + supplyBefore == supplyAfter, "Supplies dont match"); @@ -220,28 +227,9 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { ); } - // Get the TransferRedeemed(bytes32) event to ensure it matches up with the TransferSent(bytes32) event earlier - recordedLogs = vm.getRecordedLogs(); - bytes32 recvEventDigest; - for (uint256 i = 0; i < recordedLogs.length; i++) { - if (recordedLogs[i].topics[0] == keccak256("TransferRedeemed(bytes32)")) { - recvEventDigest = recordedLogs[i].topics[1]; - break; - } - } - require( - sentEventDigest == recvEventDigest, - "TransferRedeemed(bytes32) event should match TransferSent(bytes32)" - ); - // Can't resubmit the same message twice - (IWormhole.VM memory wormholeVM,,) = wormhole.parseAndVerifyVM(encodedVMs[0]); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.TransferAlreadyCompleted.selector, wormholeVM.hash - ) - ); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); + vm.expectRevert(abi.encodeWithSelector(Endpoint.DuplicateMessageAttestation.selector)); + transceiverChain2.receiveMessage(rmsgs[0]); // Go back the other way from a THIRD user vm.prank(userB); @@ -254,13 +242,15 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { // Supply checks on the transfer { uint256 supplyBefore = token2.totalSupply(); - nttManagerChain2.transfer( + seqNo = nttManagerChain2.transfer( sendingAmount, chainId1, toWormholeFormat(userD), toWormholeFormat(userC), false, - encodeTransceiverInstruction(true) + executorChain2.createSignedQuote(executorChain1.chainId()), + executorChain2.createRelayInstructions(), + endpointChain2.createAdapterInstructions() ); uint256 supplyAfter = token2.totalSupply(); @@ -274,19 +264,26 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { ); } - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId2); - } + assertEq(0, seqNo); + rmsgs = transceiverChain2.getMessages(); + assertEq(1, rmsgs.length); + + // Get the execution events from the logs. + logEvents = vm.getRecordedLogs(); + encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain2), seqNo); // Chain1 verification and checks with the receiving of the message vm.chainId(chainId1); + // Attest the transfer. + transceiverChain1.receiveMessage(rmsgs[0]); + { uint256 supplyBefore = token1.totalSupply(); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + nttManagerChain1.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); uint256 supplyAfter = token1.totalSupply(); @@ -313,16 +310,19 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { vm.recordLogs(); // Send token through standard means (not relayer) + uint64 seqNo; { uint256 nttManagerBalanceBefore = token1.balanceOf(address(nttManagerChain1)); uint256 userBalanceBefore = token1.balanceOf(address(userA)); - nttManagerChain1.transfer( + seqNo = nttManagerChain1.transfer( sendingAmount, chainId2, toWormholeFormat(userB), toWormholeFormat(userA), true, - encodeTransceiverInstruction(true) + executorChain1.createSignedQuote(executorChain2.chainId()), + executorChain1.createRelayInstructions(), + endpointChain1.createAdapterInstructions() ); // Balance check on funds going in and out working as expected @@ -338,28 +338,30 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { ); } + assertEq(0, seqNo); + DummyTransceiver.Message[] memory rmsgs = transceiverChain1.getMessages(); + assertEq(1, rmsgs.length); + + // Get the execution events from the logs. + Vm.Log[] memory logEvents = vm.getRecordedLogs(); + bytes memory encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain1), seqNo); + vm.stopPrank(); - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId1); - } + // Wrong chain receiving the attestation. + vm.expectRevert(abi.encodeWithSelector(Endpoint.InvalidDestinationChain.selector)); + transceiverChain1.receiveMessage(rmsgs[0]); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.InvalidWormholePeer.selector, - chainId1, - wormholeTransceiverChain1 - ) - ); // Wrong chain receiving the signed VAA - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + // Right chain receiving the attestation. + transceiverChain2.receiveMessage(rmsgs[0]); vm.chainId(chainId2); { uint256 supplyBefore = token2.totalSupply(); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); + nttManagerChain2.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); uint256 supplyAfter = token2.totalSupply(); require(sendingAmount + supplyBefore == supplyAfter, "Supplies dont match"); @@ -370,13 +372,8 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { } // Can't resubmit the same message twice - (IWormhole.VM memory wormholeVM,,) = wormhole.parseAndVerifyVM(encodedVMs[0]); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.TransferAlreadyCompleted.selector, wormholeVM.hash - ) - ); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); + vm.expectRevert(abi.encodeWithSelector(Endpoint.DuplicateMessageAttestation.selector)); + transceiverChain2.receiveMessage(rmsgs[0]); // Go back the other way from a THIRD user vm.prank(userB); @@ -394,13 +391,15 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { nttManagerChain2.setOutboundLimit(0); vm.startPrank(userC); - nttManagerChain2.transfer( + seqNo = nttManagerChain2.transfer( sendingAmount, chainId1, toWormholeFormat(userD), toWormholeFormat(userC), true, - encodeTransceiverInstruction(true) + executorChain2.createSignedQuote(executorChain1.chainId(), 2 days), // We are going to warp the time below. + executorChain2.createRelayInstructions(), + endpointChain2.createAdapterInstructions() ); // Test timing on the queues @@ -449,23 +448,31 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { ); } - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId2); - } + // This should be the first message sent on chain2. + assertEq(0, seqNo); + rmsgs = transceiverChain2.getMessages(); + assertEq(1, rmsgs.length); + + // Get the execution events from the logs. + logEvents = vm.getRecordedLogs(); + encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain2), seqNo); // Chain1 verification and checks with the receiving of the message vm.chainId(chainId1); vm.stopPrank(); // Back to the owner of everything for this one. vm.recordLogs(); + // Attest the transfer on chain1. + transceiverChain1.receiveMessage(rmsgs[0]); + { uint256 supplyBefore = token1.totalSupply(); nttManagerChain1.setInboundLimit(0, chainId2); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + nttManagerChain1.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); bytes32[] memory queuedDigests = Utils.fetchQueuedTransferDigestsFromLogs(vm.getRecordedLogs()); @@ -491,57 +498,19 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { } } - function test_multiTransceiver() public { + function test_multIAdapter() public { vm.chainId(chainId1); - WormholeTransceiver wormholeTransceiverChain1_1 = wormholeTransceiverChain1; - - // Dual transceiver setup - WormholeTransceiver wormholeTransceiverChain1_2 = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - wormholeTransceiverChain1_2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain1_2), "")) - ); - wormholeTransceiverChain1_2.initialize(); - - vm.chainId(chainId2); - WormholeTransceiver wormholeTransceiverChain2_1 = wormholeTransceiverChain2; - - // Dual transceiver setup - WormholeTransceiver wormholeTransceiverChain2_2 = new MockWormholeTransceiverContract( - address(nttManagerChain2), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); + // Create a dual transceiver for each manager. + DummyTransceiver[] memory transceiversChain1 = new DummyTransceiver[](2); + (transceiversChain1[0], transceiversChain1[1]) = + TransceiverHelpersLib.addTransceiver(nttManagerChain1, transceiverChain1, chainId2); - wormholeTransceiverChain2_2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain2_2), "")) - ); - wormholeTransceiverChain2_2.initialize(); - - // Setup the new entrypoint hook ups to allow the transfers to occur - wormholeTransceiverChain1_2.setWormholePeer( - chainId2, bytes32(uint256(uint160((address(wormholeTransceiverChain2_2))))) - ); - wormholeTransceiverChain2_2.setWormholePeer( - chainId1, bytes32(uint256(uint160((address(wormholeTransceiverChain1_2))))) - ); - nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2_2)); - nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1_2)); - - // Change the threshold from the setUp functions 1 to 2. - nttManagerChain1.setThreshold(2); - nttManagerChain2.setThreshold(2); + nttManagerChain2.disableSendTransceiver(chainId1, address(transceiverChain2)); + nttManagerChain2.disableRecvTransceiver(chainId1, address(transceiverChain2)); + DummyTransceiver[] memory transceiversChain2 = new DummyTransceiver[](2); + (transceiversChain2[0], transceiversChain2[1]) = + TransceiverHelpersLib.setup_transceivers(nttManagerChain2, chainId1); // Setting up the transfer DummyToken token1 = DummyToken(nttManagerChain1.token()); @@ -558,47 +527,50 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { vm.recordLogs(); // Send token through standard means (not relayer) + uint64 seqNo; { - nttManagerChain1.transfer( + seqNo = nttManagerChain1.transfer( sendingAmount, chainId2, toWormholeFormat(userB), toWormholeFormat(userA), false, - encodeTransceiverInstructions(true) + executorChain1.createSignedQuote(executorChain2.chainId()), + executorChain1.createRelayInstructions(), + endpointChain1.createAdapterInstructions() ); } - // Get and sign the event emissions to go to the other chain. - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId1); - } + // This should be the first message sent on chain1 on both transceivers. + assertEq(0, seqNo); + DummyTransceiver.Message[] memory rmsgs1 = transceiversChain1[0].getMessages(); + assertEq(1, rmsgs1.length); + + DummyTransceiver.Message[] memory rmsgs2 = transceiversChain1[1].getMessages(); + assertEq(1, rmsgs2.length); + + // Get the execution events from the logs. + Vm.Log[] memory logEvents = vm.getRecordedLogs(); + bytes memory encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain1), seqNo); vm.chainId(chainId2); - // Send in the messages for the two transceivers to complete the transfer from chain1 to chain2 + // Attest the transfer on both transceivers on chain2. + transceiversChain2[0].receiveMessage(rmsgs1[0]); + transceiversChain2[1].receiveMessage(rmsgs2[0]); + + // Execute the message to complete the transfer from chain1 to chain2. Only need to execute once. { - // vm.stopPrank(); + // Nothing should update until we call execute. uint256 supplyBefore = token2.totalSupply(); - wormholeTransceiverChain2_1.receiveMessage(encodedVMs[0]); - - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.InvalidWormholePeer.selector, - chainId1, - wormholeTransceiverChain1_1 - ) - ); - wormholeTransceiverChain2_2.receiveMessage(encodedVMs[0]); - - // Threshold check require(supplyBefore == token2.totalSupply(), "Supplies have been updated too early"); require(token2.balanceOf(userB) == 0, "User received tokens to early"); - // Finish the transfer out once the second VAA arrives - wormholeTransceiverChain2_2.receiveMessage(encodedVMs[1]); + nttManagerChain2.executeMsg( + rmsgs1[0].srcChain, rmsgs1[0].srcAddr, rmsgs1[0].sequence, encoded + ); + uint256 supplyAfter = token2.totalSupply(); require(sendingAmount + supplyBefore == supplyAfter, "Supplies dont match"); @@ -617,13 +589,15 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { // Send token through standard means (not relayer) { uint256 userBalanceBefore = token1.balanceOf(address(userB)); - nttManagerChain2.transfer( + seqNo = nttManagerChain2.transfer( sendingAmount, chainId1, toWormholeFormat(userA), toWormholeFormat(userB), false, - encodeTransceiverInstructions(true) + executorChain2.createSignedQuote(executorChain1.chainId()), + executorChain2.createRelayInstructions(), + endpointChain2.createAdapterInstructions() ); uint256 nttManagerBalanceAfter = token1.balanceOf(address(nttManagerChain2)); uint256 userBalanceAfter = token1.balanceOf(address(userB)); @@ -632,25 +606,35 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { require(nttManagerBalanceAfter == 0, "NttManager should burn all tranferred tokens"); } - // Get the VAA proof for the transfers to use - entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId2); - } + // This should be the first message sent on chain2 on both transceivers. + assertEq(0, seqNo); + rmsgs1 = transceiversChain2[0].getMessages(); + assertEq(1, rmsgs1.length); + + rmsgs2 = transceiversChain2[1].getMessages(); + assertEq(1, rmsgs2.length); + + // Get the execution events from the logs. + logEvents = vm.getRecordedLogs(); + encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain2), seqNo); vm.chainId(chainId1); + + // Attest the transfer on both transceivers on chain2. + transceiversChain1[0].receiveMessage(rmsgs1[0]); + transceiversChain1[1].receiveMessage(rmsgs2[0]); + { uint256 supplyBefore = token1.totalSupply(); - wormholeTransceiverChain1_1.receiveMessage(encodedVMs[0]); - require(supplyBefore == token1.totalSupply(), "Supplies have been updated too early"); require(token2.balanceOf(userA) == 0, "User received tokens to early"); - // Finish the transfer out once the second VAA arrives - wormholeTransceiverChain1_2.receiveMessage(encodedVMs[1]); - uint256 supplyAfter = token1.totalSupply(); + nttManagerChain1.executeMsg( + rmsgs1[0].srcChain, rmsgs1[0].srcAddr, rmsgs1[0].sequence, encoded + ); + uint256 supplyAfter = token1.totalSupply(); require( supplyBefore == supplyAfter, "Supplies don't match between operations. Should not increase." @@ -676,43 +660,4 @@ contract TestEndToEndBase is Test, IRateLimiterEvents { } return copy; } - - function encodeTransceiverInstruction( - bool relayer_off - ) public view returns (bytes memory) { - WormholeTransceiver.WormholeTransceiverInstruction memory instruction = - IWormholeTransceiver.WormholeTransceiverInstruction(relayer_off); - bytes memory encodedInstructionWormhole = - wormholeTransceiverChain1.encodeWormholeTransceiverInstruction(instruction); - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction = TransceiverStructs - .TransceiverInstruction({index: 0, payload: encodedInstructionWormhole}); - TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](1); - TransceiverInstructions[0] = TransceiverInstruction; - return TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions); - } - - // Encode an instruction for each of the relayers - function encodeTransceiverInstructions( - bool relayer_off - ) public view returns (bytes memory) { - WormholeTransceiver.WormholeTransceiverInstruction memory instruction = - IWormholeTransceiver.WormholeTransceiverInstruction(relayer_off); - - bytes memory encodedInstructionWormhole = - wormholeTransceiverChain1.encodeWormholeTransceiverInstruction(instruction); - - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction1 = - TransceiverStructs.TransceiverInstruction({index: 0, payload: encodedInstructionWormhole}); - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction2 = - TransceiverStructs.TransceiverInstruction({index: 1, payload: encodedInstructionWormhole}); - - TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](2); - - TransceiverInstructions[0] = TransceiverInstruction1; - TransceiverInstructions[1] = TransceiverInstruction2; - - return TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions); - } } diff --git a/evm/test/IntegrationWithoutRateLimiting.t.sol b/evm/test/IntegrationWithoutRateLimiting.t.sol index 8d7e5d0fe..4b58b54f6 100755 --- a/evm/test/IntegrationWithoutRateLimiting.t.sol +++ b/evm/test/IntegrationWithoutRateLimiting.t.sol @@ -5,31 +5,39 @@ import "forge-std/Test.sol"; import "forge-std/console.sol"; import "../src/NttManager/NttManagerNoRateLimiting.sol"; -import "../src/Transceiver/Transceiver.sol"; import "../src/interfaces/INttManager.sol"; import "../src/interfaces/IRateLimiter.sol"; -import "../src/interfaces/ITransceiver.sol"; import "../src/interfaces/IManagerBase.sol"; import "../src/interfaces/IRateLimiterEvents.sol"; import {Utils} from "./libraries/Utils.sol"; import {DummyToken, DummyTokenMintAndBurn} from "./NttManager.t.sol"; -import "../src/interfaces/IWormholeTransceiver.sol"; -import {WormholeTransceiver} from "../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; import "../src/libraries/TransceiverStructs.sol"; +import "./libraries/TransceiverHelpers.sol"; +import "./mocks/DummyTransceiver.sol"; import "./mocks/MockNttManager.sol"; -import "./mocks/MockTransceivers.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockExecutor.sol"; import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; import "wormhole-solidity-sdk/testing/helpers/WormholeSimulator.sol"; import "wormhole-solidity-sdk/Utils.sol"; -//import "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; +import "example-messaging-endpoint/evm/src/Endpoint.sol"; -contract TestEndToEndNoRateLimiting is Test { +contract TestNoRateLimitingEndToEndBase is Test, IRateLimiterEvents { NttManagerNoRateLimiting nttManagerChain1; NttManagerNoRateLimiting nttManagerChain2; + MockEndpoint endpointChain1; + MockEndpoint endpointChain2; + + MockExecutor executorChain1; + MockExecutor executorChain2; + + DummyTransceiver transceiverChain1; + DummyTransceiver transceiverChain2; + using TrimmedAmountLib for uint256; using TrimmedAmountLib for TrimmedAmount; @@ -44,8 +52,6 @@ contract TestEndToEndNoRateLimiting is Test { WormholeSimulator guardian; uint256 initialBlockTimestamp; - WormholeTransceiver wormholeTransceiverChain1; - WormholeTransceiver wormholeTransceiverChain2; address userA = address(0x123); address userB = address(0x456); address userC = address(0x789); @@ -61,10 +67,20 @@ contract TestEndToEndNoRateLimiting is Test { guardian = new WormholeSimulator(address(wormhole), DEVNET_GUARDIAN_PK); + endpointChain1 = new MockEndpoint(chainId1); + endpointChain2 = new MockEndpoint(chainId2); + + executorChain1 = new MockExecutor(chainId1); + executorChain2 = new MockExecutor(chainId2); + vm.chainId(chainId1); DummyToken t1 = new DummyToken(); NttManagerNoRateLimiting implementation = new MockNttManagerNoRateLimitingContract( - address(t1), IManagerBase.Mode.LOCKING, chainId1 + address(endpointChain1), + address(executorChain1), + address(t1), + IManagerBase.Mode.LOCKING, + chainId1 ); nttManagerChain1 = MockNttManagerNoRateLimitingContract( @@ -72,37 +88,20 @@ contract TestEndToEndNoRateLimiting is Test { ); nttManagerChain1.initialize(); - WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain1 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain1Implementation), "")) - ); - - // Only the deployer should be able to initialize - vm.prank(userA); - vm.expectRevert( - abi.encodeWithSelector(ITransceiver.UnexpectedDeployer.selector, address(this), userA) - ); - wormholeTransceiverChain1.initialize(); - - // Actually initialize properly now - wormholeTransceiverChain1.initialize(); - - nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1)); - // nttManagerChain1.setOutboundLimit(type(uint64).max); - // nttManagerChain1.setInboundLimit(type(uint64).max, chainId2); + transceiverChain1 = new DummyTransceiver(chainId1, address(endpointChain1)); + nttManagerChain1.setTransceiver(address(transceiverChain1)); + nttManagerChain1.enableSendTransceiver(chainId2, address(transceiverChain1)); + nttManagerChain1.enableRecvTransceiver(chainId2, address(transceiverChain1)); // Chain 2 setup vm.chainId(chainId2); DummyToken t2 = new DummyTokenMintAndBurn(); NttManagerNoRateLimiting implementationChain2 = new MockNttManagerNoRateLimitingContract( - address(t2), IManagerBase.Mode.BURNING, chainId2 + address(endpointChain2), + address(executorChain2), + address(t2), + IManagerBase.Mode.BURNING, + chainId2 ); nttManagerChain2 = MockNttManagerNoRateLimitingContract( @@ -110,49 +109,41 @@ contract TestEndToEndNoRateLimiting is Test { ); nttManagerChain2.initialize(); - WormholeTransceiver wormholeTransceiverChain2Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain2), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain2Implementation), "")) - ); - wormholeTransceiverChain2.initialize(); - - nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2)); - // nttManagerChain2.setOutboundLimit(type(uint64).max); - // nttManagerChain2.setInboundLimit(type(uint64).max, chainId1); + transceiverChain2 = new DummyTransceiver(chainId2, address(endpointChain2)); + nttManagerChain2.setTransceiver(address(transceiverChain2)); + nttManagerChain2.enableSendTransceiver(chainId1, address(transceiverChain2)); + nttManagerChain2.enableRecvTransceiver(chainId1, address(transceiverChain2)); // Register peer contracts for the nttManager and transceiver. Transceivers and nttManager each have the concept of peers here. nttManagerChain1.setPeer( - chainId2, bytes32(uint256(uint160(address(nttManagerChain2)))), 9, type(uint64).max + chainId2, + bytes32(uint256(uint160(address(nttManagerChain2)))), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); nttManagerChain2.setPeer( - chainId1, bytes32(uint256(uint160(address(nttManagerChain1)))), 7, type(uint64).max + chainId1, + bytes32(uint256(uint160(address(nttManagerChain1)))), + 7, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); - // Set peers for the transceivers - wormholeTransceiverChain1.setWormholePeer( - chainId2, bytes32(uint256(uint160(address(wormholeTransceiverChain2)))) - ); - wormholeTransceiverChain2.setWormholePeer( - chainId1, bytes32(uint256(uint160(address(wormholeTransceiverChain1)))) + require( + nttManagerChain1.getThreshold(chainId2) != 0, + "Threshold is zero with active transceivers" ); - require(nttManagerChain1.getThreshold() != 0, "Threshold is zero with active transceivers"); - - // Actually set it - nttManagerChain1.setThreshold(1); - nttManagerChain2.setThreshold(1); + nttManagerChain1.setThreshold(chainId2, 1); + nttManagerChain2.setThreshold(chainId1, 1); INttManager.NttManagerPeer memory peer = nttManagerChain1.getPeer(chainId2); require(9 == peer.tokenDecimals, "Peer has the wrong number of token decimals"); } + function test_setUp() public {} + function test_chainToChainBase() public { vm.chainId(chainId1); @@ -169,10 +160,18 @@ contract TestEndToEndNoRateLimiting is Test { vm.recordLogs(); // Send token through standard means (not relayer) + uint64 seqNo; { uint256 nttManagerBalanceBefore = token1.balanceOf(address(nttManagerChain1)); uint256 userBalanceBefore = token1.balanceOf(address(userA)); - nttManagerChain1.transfer(sendingAmount, chainId2, bytes32(uint256(uint160(userB)))); + seqNo = nttManagerChain1.transfer( + sendingAmount, + chainId2, + bytes32(uint256(uint160(userB))), + executorChain1.createSignedQuote(executorChain2.chainId()), + executorChain1.createRelayInstructions(), + endpointChain1.createAdapterInstructions() + ); // Balance check on funds going in and out working as expected uint256 nttManagerBalanceAfter = token1.balanceOf(address(nttManagerChain1)); @@ -187,24 +186,32 @@ contract TestEndToEndNoRateLimiting is Test { ); } - vm.stopPrank(); + assertEq(0, seqNo); + DummyTransceiver.Message[] memory rmsgs = transceiverChain1.getMessages(); + assertEq(1, rmsgs.length); - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId1); - } + // Get the execution events from the logs. + Vm.Log[] memory logEvents = vm.getRecordedLogs(); + bytes memory encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain1), seqNo); + + vm.stopPrank(); // Chain2 verification and checks vm.chainId(chainId2); - // Wrong chain receiving the signed VAA - vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, chainId1, chainId2)); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + // Wrong chain receiving the attestation. + vm.expectRevert(abi.encodeWithSelector(Endpoint.InvalidDestinationChain.selector)); + transceiverChain1.receiveMessage(rmsgs[0]); + + // Right chain receiving the attestation. + transceiverChain2.receiveMessage(rmsgs[0]); + { uint256 supplyBefore = token2.totalSupply(); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); + nttManagerChain2.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); uint256 supplyAfter = token2.totalSupply(); require(sendingAmount + supplyBefore == supplyAfter, "Supplies dont match"); @@ -216,13 +223,8 @@ contract TestEndToEndNoRateLimiting is Test { } // Can't resubmit the same message twice - (IWormhole.VM memory wormholeVM,,) = wormhole.parseAndVerifyVM(encodedVMs[0]); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.TransferAlreadyCompleted.selector, wormholeVM.hash - ) - ); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); + vm.expectRevert(abi.encodeWithSelector(Endpoint.DuplicateMessageAttestation.selector)); + transceiverChain2.receiveMessage(rmsgs[0]); // Go back the other way from a THIRD user vm.prank(userB); @@ -235,13 +237,15 @@ contract TestEndToEndNoRateLimiting is Test { // Supply checks on the transfer { uint256 supplyBefore = token2.totalSupply(); - nttManagerChain2.transfer( + seqNo = nttManagerChain2.transfer( sendingAmount, chainId1, toWormholeFormat(userD), toWormholeFormat(userC), false, - encodeTransceiverInstruction(true) + executorChain2.createSignedQuote(executorChain1.chainId()), + executorChain2.createRelayInstructions(), + endpointChain2.createAdapterInstructions() ); uint256 supplyAfter = token2.totalSupply(); @@ -255,19 +259,26 @@ contract TestEndToEndNoRateLimiting is Test { ); } - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId2); - } + assertEq(0, seqNo); + rmsgs = transceiverChain2.getMessages(); + assertEq(1, rmsgs.length); + + // Get the execution events from the logs. + logEvents = vm.getRecordedLogs(); + encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain2), seqNo); // Chain1 verification and checks with the receiving of the message vm.chainId(chainId1); + // Attest the transfer. + transceiverChain1.receiveMessage(rmsgs[0]); + { uint256 supplyBefore = token1.totalSupply(); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + nttManagerChain1.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); uint256 supplyAfter = token1.totalSupply(); @@ -297,20 +308,34 @@ contract TestEndToEndNoRateLimiting is Test { // Everything else should. vm.expectRevert(abi.encodeWithSelector(INttManager.InvalidPeerChainIdZero.selector)); nttManagerChain1.setPeer( - 0, bytes32(uint256(uint160(address(nttManagerChain2)))), 9, type(uint64).max + 0, + bytes32(uint256(uint160(address(nttManagerChain2)))), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); vm.expectRevert(abi.encodeWithSelector(INttManager.InvalidPeerZeroAddress.selector)); - nttManagerChain1.setPeer(chainId2, bytes32(0), 9, type(uint64).max); + nttManagerChain1.setPeer( + chainId2, bytes32(0), 9, NttManagerHelpersLib.gasLimit, type(uint64).max + ); vm.expectRevert(abi.encodeWithSelector(INttManager.InvalidPeerDecimals.selector)); nttManagerChain1.setPeer( - chainId2, bytes32(uint256(uint160(address(nttManagerChain2)))), 0, type(uint64).max + chainId2, + bytes32(uint256(uint160(address(nttManagerChain2)))), + 0, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); vm.expectRevert(abi.encodeWithSelector(INttManager.InvalidPeerSameChainId.selector)); nttManagerChain1.setPeer( - chainId1, bytes32(uint256(uint160(address(nttManagerChain2)))), 9, type(uint64).max + chainId1, + bytes32(uint256(uint160(address(nttManagerChain2)))), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); vm.expectRevert(abi.encodeWithSelector(INttManager.NotImplemented.selector)); @@ -345,16 +370,19 @@ contract TestEndToEndNoRateLimiting is Test { vm.recordLogs(); // Send token through standard means (not relayer) + uint64 seqNo; { uint256 nttManagerBalanceBefore = token1.balanceOf(address(nttManagerChain1)); uint256 userBalanceBefore = token1.balanceOf(address(userA)); - nttManagerChain1.transfer( + seqNo = nttManagerChain1.transfer( sendingAmount, chainId2, toWormholeFormat(userB), toWormholeFormat(userA), true, - encodeTransceiverInstruction(true) + executorChain1.createSignedQuote(executorChain2.chainId()), + executorChain1.createRelayInstructions(), + endpointChain1.createAdapterInstructions() ); // Balance check on funds going in and out working as expected @@ -370,28 +398,30 @@ contract TestEndToEndNoRateLimiting is Test { ); } + assertEq(0, seqNo); + DummyTransceiver.Message[] memory rmsgs = transceiverChain1.getMessages(); + assertEq(1, rmsgs.length); + + // Get the execution events from the logs. + Vm.Log[] memory logEvents = vm.getRecordedLogs(); + bytes memory encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain1), seqNo); + vm.stopPrank(); - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId1); - } + // Wrong chain receiving the attestation. + vm.expectRevert(abi.encodeWithSelector(Endpoint.InvalidDestinationChain.selector)); + transceiverChain1.receiveMessage(rmsgs[0]); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.InvalidWormholePeer.selector, - chainId1, - wormholeTransceiverChain1 - ) - ); // Wrong chain receiving the signed VAA - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + // Right chain receiving the attestation. + transceiverChain2.receiveMessage(rmsgs[0]); vm.chainId(chainId2); { uint256 supplyBefore = token2.totalSupply(); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); + nttManagerChain2.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); uint256 supplyAfter = token2.totalSupply(); require(sendingAmount + supplyBefore == supplyAfter, "Supplies dont match"); @@ -403,13 +433,8 @@ contract TestEndToEndNoRateLimiting is Test { } // Can't resubmit the same message twice - (IWormhole.VM memory wormholeVM,,) = wormhole.parseAndVerifyVM(encodedVMs[0]); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.TransferAlreadyCompleted.selector, wormholeVM.hash - ) - ); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); + vm.expectRevert(abi.encodeWithSelector(Endpoint.DuplicateMessageAttestation.selector)); + transceiverChain2.receiveMessage(rmsgs[0]); // Go back the other way from a THIRD user vm.prank(userB); @@ -427,13 +452,15 @@ contract TestEndToEndNoRateLimiting is Test { // nttManagerChain2.setOutboundLimit(0); vm.startPrank(userC); - nttManagerChain2.transfer( + seqNo = nttManagerChain2.transfer( sendingAmount, chainId1, toWormholeFormat(userD), toWormholeFormat(userC), true, - encodeTransceiverInstruction(true) + executorChain2.createSignedQuote(executorChain1.chainId()), + executorChain2.createRelayInstructions(), + endpointChain2.createAdapterInstructions() ); uint256 supplyAfter = token2.totalSupply(); @@ -447,23 +474,30 @@ contract TestEndToEndNoRateLimiting is Test { ); } - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId2); - } + // This should be the first message sent on chain2. + assertEq(0, seqNo); + rmsgs = transceiverChain2.getMessages(); + assertEq(1, rmsgs.length); + + // Get the execution events from the logs. + logEvents = vm.getRecordedLogs(); + encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain2), seqNo); // Chain1 verification and checks with the receiving of the message vm.chainId(chainId1); vm.stopPrank(); // Back to the owner of everything for this one. vm.recordLogs(); + // Attest the transfer on chain1. + transceiverChain1.receiveMessage(rmsgs[0]); + { uint256 supplyBefore = token1.totalSupply(); - // nttManagerChain1.setInboundLimit(0, chainId2); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); + nttManagerChain1.executeMsg( + rmsgs[0].srcChain, rmsgs[0].srcAddr, rmsgs[0].sequence, encoded + ); bytes32[] memory queuedDigests = Utils.fetchQueuedTransferDigestsFromLogs(vm.getRecordedLogs()); @@ -479,57 +513,19 @@ contract TestEndToEndNoRateLimiting is Test { } } - function test_multiTransceiver() public { + function test_multIAdapter() public { vm.chainId(chainId1); - WormholeTransceiver wormholeTransceiverChain1_1 = wormholeTransceiverChain1; + // Create a dual transceiver for each manager. + DummyTransceiver[] memory transceiversChain1 = new DummyTransceiver[](2); + (transceiversChain1[0], transceiversChain1[1]) = + TransceiverHelpersLib.addTransceiver(nttManagerChain1, transceiverChain1, chainId2); - // Dual transceiver setup - WormholeTransceiver wormholeTransceiverChain1_2 = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - wormholeTransceiverChain1_2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain1_2), "")) - ); - wormholeTransceiverChain1_2.initialize(); - - vm.chainId(chainId2); - WormholeTransceiver wormholeTransceiverChain2_1 = wormholeTransceiverChain2; - - // Dual transceiver setup - WormholeTransceiver wormholeTransceiverChain2_2 = new MockWormholeTransceiverContract( - address(nttManagerChain2), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - wormholeTransceiverChain2_2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain2_2), "")) - ); - wormholeTransceiverChain2_2.initialize(); - - // Setup the new entrypoint hook ups to allow the transfers to occur - wormholeTransceiverChain1_2.setWormholePeer( - chainId2, bytes32(uint256(uint160((address(wormholeTransceiverChain2_2))))) - ); - wormholeTransceiverChain2_2.setWormholePeer( - chainId1, bytes32(uint256(uint160((address(wormholeTransceiverChain1_2))))) - ); - nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2_2)); - nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1_2)); - - // Change the threshold from the setUp functions 1 to 2. - nttManagerChain1.setThreshold(2); - nttManagerChain2.setThreshold(2); + nttManagerChain2.disableSendTransceiver(chainId1, address(transceiverChain2)); + nttManagerChain2.disableRecvTransceiver(chainId1, address(transceiverChain2)); + DummyTransceiver[] memory transceiversChain2 = new DummyTransceiver[](2); + (transceiversChain2[0], transceiversChain2[1]) = + TransceiverHelpersLib.setup_transceivers(nttManagerChain2, chainId1); // Setting up the transfer DummyToken token1 = DummyToken(nttManagerChain1.token()); @@ -546,47 +542,50 @@ contract TestEndToEndNoRateLimiting is Test { vm.recordLogs(); // Send token through standard means (not relayer) + uint64 seqNo; { - nttManagerChain1.transfer( + seqNo = nttManagerChain1.transfer( sendingAmount, chainId2, toWormholeFormat(userB), toWormholeFormat(userA), false, - encodeTransceiverInstructions(true) + executorChain1.createSignedQuote(executorChain2.chainId()), + executorChain1.createRelayInstructions(), + endpointChain1.createAdapterInstructions() ); } - // Get and sign the event emissions to go to the other chain. - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId1); - } + // This should be the first message sent on chain1 on both transceivers. + assertEq(0, seqNo); + DummyTransceiver.Message[] memory rmsgs1 = transceiversChain1[0].getMessages(); + assertEq(1, rmsgs1.length); + + DummyTransceiver.Message[] memory rmsgs2 = transceiversChain1[1].getMessages(); + assertEq(1, rmsgs2.length); + + // Get the execution events from the logs. + Vm.Log[] memory logEvents = vm.getRecordedLogs(); + bytes memory encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain1), seqNo); vm.chainId(chainId2); - // Send in the messages for the two transceivers to complete the transfer from chain1 to chain2 + // Attest the transfer on both transceivers on chain2. + transceiversChain2[0].receiveMessage(rmsgs1[0]); + transceiversChain2[1].receiveMessage(rmsgs2[0]); + + // Execute the message to complete the transfer from chain1 to chain2. Only need to execute once. { - // vm.stopPrank(); + // Nothing should update until we call execute. uint256 supplyBefore = token2.totalSupply(); - wormholeTransceiverChain2_1.receiveMessage(encodedVMs[0]); - - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.InvalidWormholePeer.selector, - chainId1, - wormholeTransceiverChain1_1 - ) - ); - wormholeTransceiverChain2_2.receiveMessage(encodedVMs[0]); - - // Threshold check require(supplyBefore == token2.totalSupply(), "Supplies have been updated too early"); require(token2.balanceOf(userB) == 0, "User received tokens to early"); - // Finish the transfer out once the second VAA arrives - wormholeTransceiverChain2_2.receiveMessage(encodedVMs[1]); + nttManagerChain2.executeMsg( + rmsgs1[0].srcChain, rmsgs1[0].srcAddr, rmsgs1[0].sequence, encoded + ); + uint256 supplyAfter = token2.totalSupply(); require(sendingAmount + supplyBefore == supplyAfter, "Supplies dont match"); @@ -606,13 +605,15 @@ contract TestEndToEndNoRateLimiting is Test { // Send token through standard means (not relayer) { uint256 userBalanceBefore = token1.balanceOf(address(userB)); - nttManagerChain2.transfer( + seqNo = nttManagerChain2.transfer( sendingAmount, chainId1, toWormholeFormat(userA), toWormholeFormat(userB), false, - encodeTransceiverInstructions(true) + executorChain2.createSignedQuote(executorChain1.chainId()), + executorChain2.createRelayInstructions(), + endpointChain2.createAdapterInstructions() ); uint256 nttManagerBalanceAfter = token1.balanceOf(address(nttManagerChain2)); uint256 userBalanceAfter = token1.balanceOf(address(userB)); @@ -624,25 +625,35 @@ contract TestEndToEndNoRateLimiting is Test { ); } - // Get the VAA proof for the transfers to use - entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId2); - } + // This should be the first message sent on chain2 on both transceivers. + assertEq(0, seqNo); + rmsgs1 = transceiversChain2[0].getMessages(); + assertEq(1, rmsgs1.length); + + rmsgs2 = transceiversChain2[1].getMessages(); + assertEq(1, rmsgs2.length); + + // Get the execution events from the logs. + logEvents = vm.getRecordedLogs(); + encoded = + TransceiverHelpersLib.getExecutionSent(logEvents, address(nttManagerChain2), seqNo); vm.chainId(chainId1); + + // Attest the transfer on both transceivers on chain2. + transceiversChain1[0].receiveMessage(rmsgs1[0]); + transceiversChain1[1].receiveMessage(rmsgs2[0]); + { uint256 supplyBefore = token1.totalSupply(); - wormholeTransceiverChain1_1.receiveMessage(encodedVMs[0]); - require(supplyBefore == token1.totalSupply(), "Supplies have been updated too early"); require(token2.balanceOf(userA) == 0, "User received tokens to early"); - // Finish the transfer out once the second VAA arrives - wormholeTransceiverChain1_2.receiveMessage(encodedVMs[1]); - uint256 supplyAfter = token1.totalSupply(); + nttManagerChain1.executeMsg( + rmsgs1[0].srcChain, rmsgs1[0].srcAddr, rmsgs1[0].sequence, encoded + ); + uint256 supplyAfter = token1.totalSupply(); require( supplyBefore == supplyAfter, "Supplies don't match between operations. Should not increase." @@ -668,43 +679,4 @@ contract TestEndToEndNoRateLimiting is Test { } return copy; } - - function encodeTransceiverInstruction( - bool relayer_off - ) public view returns (bytes memory) { - WormholeTransceiver.WormholeTransceiverInstruction memory instruction = - IWormholeTransceiver.WormholeTransceiverInstruction(relayer_off); - bytes memory encodedInstructionWormhole = - wormholeTransceiverChain1.encodeWormholeTransceiverInstruction(instruction); - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction = TransceiverStructs - .TransceiverInstruction({index: 0, payload: encodedInstructionWormhole}); - TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](1); - TransceiverInstructions[0] = TransceiverInstruction; - return TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions); - } - - // Encode an instruction for each of the relayers - function encodeTransceiverInstructions( - bool relayer_off - ) public view returns (bytes memory) { - WormholeTransceiver.WormholeTransceiverInstruction memory instruction = - IWormholeTransceiver.WormholeTransceiverInstruction(relayer_off); - - bytes memory encodedInstructionWormhole = - wormholeTransceiverChain1.encodeWormholeTransceiverInstruction(instruction); - - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction1 = - TransceiverStructs.TransceiverInstruction({index: 0, payload: encodedInstructionWormhole}); - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction2 = - TransceiverStructs.TransceiverInstruction({index: 1, payload: encodedInstructionWormhole}); - - TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](2); - - TransceiverInstructions[0] = TransceiverInstruction1; - TransceiverInstructions[1] = TransceiverInstruction2; - - return TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions); - } } diff --git a/evm/test/NttManager.t.sol b/evm/test/NttManager.t.sol index 171940e0d..ef1765b90 100644 --- a/evm/test/NttManager.t.sol +++ b/evm/test/NttManager.t.sol @@ -9,7 +9,7 @@ import "../src/interfaces/INttManager.sol"; import "../src/interfaces/IRateLimiter.sol"; import "../src/interfaces/IManagerBase.sol"; import "../src/interfaces/IRateLimiterEvents.sol"; -import "../src/NttManager/TransceiverRegistry.sol"; +import "../src/libraries/external/OwnableUpgradeable.sol"; import "../src/libraries/PausableUpgradeable.sol"; import "../src/libraries/TransceiverHelpers.sol"; import {Utils} from "./libraries/Utils.sol"; @@ -19,18 +19,27 @@ import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; import "wormhole-solidity-sdk/testing/helpers/WormholeSimulator.sol"; import "wormhole-solidity-sdk/Utils.sol"; +import "example-messaging-endpoint/evm/src/AdapterRegistry.sol"; import "./libraries/TransceiverHelpers.sol"; import "./libraries/NttManagerHelpers.sol"; -import "./interfaces/ITransceiverReceiver.sol"; import "./mocks/DummyTransceiver.sol"; import "../src/mocks/DummyToken.sol"; import "./mocks/MockNttManager.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockExecutor.sol"; // TODO: set this up so the common functionality tests can be run against both contract TestNttManager is Test, IRateLimiterEvents { MockNttManagerContract nttManager; MockNttManagerContract nttManagerOther; MockNttManagerContract nttManagerZeroRateLimiter; + MockNttManagerContract nttManagerZeroRateLimiterOther; + MockEndpoint endpoint; + MockEndpoint endpointOther; + MockExecutor executor; + MockExecutor executorOther; + DummyTransceiver transceiver; + DummyTransceiver transceiverOther; using TrimmedAmountLib for uint256; using TrimmedAmountLib for TrimmedAmount; @@ -38,11 +47,14 @@ contract TestNttManager is Test, IRateLimiterEvents { // 0x99'E''T''T' uint16 constant chainId = 7; uint16 constant chainId2 = 8; + + address user_A = address(0x123); + address user_B = address(0x456); + uint256 constant DEVNET_GUARDIAN_PK = 0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0; WormholeSimulator guardian; uint256 initialBlockTimestamp; - DummyTransceiver dummyTransceiver; function setUp() public { string memory url = "https://ethereum-sepolia-rpc.publicnode.com"; @@ -52,26 +64,72 @@ contract TestNttManager is Test, IRateLimiterEvents { guardian = new WormholeSimulator(address(wormhole), DEVNET_GUARDIAN_PK); + endpoint = new MockEndpoint(chainId); + endpointOther = new MockEndpoint(chainId2); + + executor = new MockExecutor(chainId); + executorOther = new MockExecutor(chainId2); + DummyToken t = new DummyToken(); NttManager implementation = new MockNttManagerContract( - address(t), IManagerBase.Mode.LOCKING, chainId, 1 days, false + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + chainId, + 1 days, + false ); - NttManager otherImplementation = new MockNttManagerContract( - address(t), IManagerBase.Mode.LOCKING, chainId, 1 days, false + NttManager implementationOther = new MockNttManagerContract( + address(endpointOther), + address(executorOther), + address(t), + IManagerBase.Mode.LOCKING, + chainId2, + 1 days, + false ); nttManager = MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); nttManager.initialize(); + assertEq(uint8(IManagerBase.Mode.LOCKING), nttManager.getMode()); + assertEq(0, nttManager.nextMessageSequence()); + nttManagerOther = - MockNttManagerContract(address(new ERC1967Proxy(address(otherImplementation), ""))); + MockNttManagerContract(address(new ERC1967Proxy(address(implementationOther), ""))); nttManagerOther.initialize(); - dummyTransceiver = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(dummyTransceiver)); + nttManager.setPeer( + chainId2, + toWormholeFormat(address(nttManagerOther)), + t.decimals(), + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); + + nttManagerOther.setPeer( + chainId, + toWormholeFormat(address(nttManager)), + t.decimals(), + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); + + transceiver = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(transceiver)); + nttManager.enableSendTransceiver(chainId2, address(transceiver)); + nttManager.enableRecvTransceiver(chainId2, address(transceiver)); + + transceiverOther = new DummyTransceiver(chainId2, address(endpointOther)); + nttManagerOther.setTransceiver(address(transceiverOther)); + nttManagerOther.enableSendTransceiver(chainId, address(transceiverOther)); + nttManagerOther.enableRecvTransceiver(chainId, address(transceiverOther)); } + function test_setUp() public {} + // === pure unit tests // naive implementation of countSetBits to test against @@ -97,40 +155,86 @@ contract TestNttManager is Test, IRateLimiterEvents { // === Deployments with rate limiter disabled function test_disabledRateLimiter() public { - DummyToken t = new DummyToken(); - NttManager implementation = - new MockNttManagerContract(address(t), IManagerBase.Mode.LOCKING, chainId, 0, true); + DummyToken token = new DummyToken(); + uint8 decimals = token.decimals(); + // Create the first NttManager without rate limiting with two transceivers. + NttManager implementation = new MockNttManagerContract( + address(endpoint), + address(executor), + address(token), + IManagerBase.Mode.LOCKING, + chainId, + 0, + true + ); nttManagerZeroRateLimiter = MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); nttManagerZeroRateLimiter.initialize(); - - DummyTransceiver e = new DummyTransceiver(address(nttManagerZeroRateLimiter)); - nttManagerZeroRateLimiter.setTransceiver(address(e)); - - address user_A = address(0x123); - address user_B = address(0x456); - - uint8 decimals = t.decimals(); + TransceiverHelpersLib.setup_transceivers(nttManagerZeroRateLimiter, chainId2); + + // Create the second NttManager without rate limiting with two transceivers. + implementation = new MockNttManagerContract( + address(endpointOther), + address(executorOther), + address(token), + IManagerBase.Mode.LOCKING, + chainId2, + 0, + true + ); + nttManagerZeroRateLimiterOther = + MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); + nttManagerZeroRateLimiterOther.initialize(); + DummyTransceiver[] memory transceiversOther = new DummyTransceiver[](2); + (transceiversOther[0], transceiversOther[1]) = + TransceiverHelpersLib.setup_transceivers(nttManagerZeroRateLimiterOther, chainId); nttManagerZeroRateLimiter.setPeer( - chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max + chainId2, + toWormholeFormat(address(nttManagerZeroRateLimiterOther)), + token.decimals(), + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); + + nttManagerZeroRateLimiterOther.setPeer( + chainId, + toWormholeFormat(address(nttManagerZeroRateLimiter)), + token.decimals(), + NttManagerHelpersLib.gasLimit, + type(uint64).max ); - t.mintDummy(address(user_A), 5 * 10 ** decimals); + token.mintDummy(address(user_A), 5 * 10 ** decimals); // Test outgoing transfers complete successfully with rate limit disabled vm.startPrank(user_A); - t.approve(address(nttManagerZeroRateLimiter), 3 * 10 ** decimals); + token.approve(address(nttManagerZeroRateLimiter), 3 * 10 ** decimals); uint64 s1 = nttManagerZeroRateLimiter.transfer( - 1 * 10 ** decimals, chainId2, toWormholeFormat(user_B) + 1 * 10 ** decimals, + chainId2, + toWormholeFormat(user_B), + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); uint64 s2 = nttManagerZeroRateLimiter.transfer( - 1 * 10 ** decimals, chainId2, toWormholeFormat(user_B) + 1 * 10 ** decimals, + chainId2, + toWormholeFormat(user_B), + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); uint64 s3 = nttManagerZeroRateLimiter.transfer( - 1 * 10 ** decimals, chainId2, toWormholeFormat(user_B) + 1 * 10 ** decimals, + chainId2, + toWormholeFormat(user_B), + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -139,32 +243,24 @@ contract TestNttManager is Test, IRateLimiterEvents { assertEq(s3, 2); // Test incoming transfer completes successfully with rate limit disabled - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerZeroRateLimiter); - nttManagerZeroRateLimiter.setThreshold(2); + TrimmedAmount amount = packTrimmedAmount(50, 8); + token.mintDummy(address(nttManagerZeroRateLimiterOther), amount.untrim(token.decimals())); - // register nttManager peer - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManagerZeroRateLimiter.setPeer( - TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max + TransceiverStructs.NttManagerMessage memory m = TransceiverHelpersLib.buildNttManagerMessage( + user_B, 0, chainId2, nttManagerZeroRateLimiter, amount ); + bytes memory encodedM = TransceiverStructs.encodeNttManagerMessage(m); - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - bytes memory transceiverMessage; - (nttManagerMessage, transceiverMessage) = TransceiverHelpersLib - .buildTransceiverMessageWithNttManagerPayload( + // Attest and receive the message on the other manager. + DummyTransceiver.Message memory rmsg = TransceiverHelpersLib.attestAndReceiveMsg( + nttManagerZeroRateLimiter, + nttManagerZeroRateLimiterOther, 0, - bytes32(0), - peer, - toWormholeFormat(address(nttManagerZeroRateLimiter)), - abi.encode("payload") + transceiversOther, + encodedM ); - e1.receiveMessage(transceiverMessage); - - bytes32 hash = TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, nttManagerMessage - ); - assertEq(nttManagerZeroRateLimiter.messageAttestations(hash), 1); + checkAttestationAndExecution(nttManagerZeroRateLimiterOther, rmsg, 2); } // === ownership @@ -195,12 +291,18 @@ contract TestNttManager is Test, IRateLimiterEvents { nttManager.pause(); assertEq(nttManager.isPaused(), true); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + // When the NttManager is paused, initiating transfers, completing queued transfers on both source and destination chains, // executing transfers and attesting to transfers should all revert vm.expectRevert( abi.encodeWithSelector(PausableUpgradeable.RequireContractIsNotPaused.selector) ); - nttManager.transfer(0, 0, bytes32(0)); + nttManager.transfer( + 0, 0, bytes32(0), executorSignedQuote, executorRelayInstructions, adapterInstructions + ); vm.expectRevert( abi.encodeWithSelector(PausableUpgradeable.RequireContractIsNotPaused.selector) @@ -212,27 +314,26 @@ contract TestNttManager is Test, IRateLimiterEvents { ); nttManager.completeInboundQueuedTransfer(bytes32(0)); - vm.expectRevert( - abi.encodeWithSelector(PausableUpgradeable.RequireContractIsNotPaused.selector) - ); - TransceiverStructs.NttManagerMessage memory message; - nttManager.executeMsg(0, bytes32(0), message); + // The endpoint and transceiver are not pausable, so calling receiveMessage should still work. + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); - bytes memory transceiverMessage; - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - 0, - bytes32(0), - toWormholeFormat(address(nttManagerOther)), - toWormholeFormat(address(nttManager)), - abi.encode("payload") - ); + transceiver.receiveMessage(rmsg); + + bytes memory message = "Hello, World"; + + // But executeMsg should be paused in the NttManager. vm.expectRevert( abi.encodeWithSelector(PausableUpgradeable.RequireContractIsNotPaused.selector) ); - dummyTransceiver.receiveMessage(transceiverMessage); - - nttManager.unpause(); - assertEq(nttManager.isPaused(), false); + nttManager.executeMsg(0, UniversalAddressLibrary.fromAddress(address(0)), 0, message); } function test_pausePauserUnpauseOnlyOwner() public { @@ -262,7 +363,13 @@ contract TestNttManager is Test, IRateLimiterEvents { function test_brokenToken() public { DummyToken t = new DummyTokenBroken(); NttManager implementation = new MockNttManagerContract( - address(t), IManagerBase.Mode.LOCKING, chainId, 1 days, false + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + chainId, + 1 days, + false ); NttManager newNttManager = @@ -270,19 +377,25 @@ contract TestNttManager is Test, IRateLimiterEvents { vm.expectRevert(abi.encodeWithSelector(INttManager.StaticcallFailed.selector)); newNttManager.initialize(); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + vm.expectRevert(abi.encodeWithSelector(INttManager.StaticcallFailed.selector)); - newNttManager.transfer(1, 1, bytes32("1")); + newNttManager.transfer( + 1, 1, bytes32("1"), executorSignedQuote, executorRelayInstructions, adapterInstructions + ); } // === transceiver registration function test_registerTransceiver() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); + DummyTransceiver e = new DummyTransceiver(chainId, address(endpoint)); nttManager.setTransceiver(address(e)); } function test_onlyOwnerCanModifyTransceivers() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); + DummyTransceiver e = new DummyTransceiver(chainId, address(endpoint)); nttManager.setTransceiver(address(e)); address notOwner = address(0x123); @@ -292,79 +405,72 @@ contract TestNttManager is Test, IRateLimiterEvents { abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, notOwner) ); nttManager.setTransceiver(address(e)); - - vm.expectRevert( - abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, notOwner) - ); - nttManager.removeTransceiver(address(e)); } function test_cantEnableTransceiverTwice() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); + DummyTransceiver e = new DummyTransceiver(chainId, address(endpoint)); nttManager.setTransceiver(address(e)); vm.expectRevert( - abi.encodeWithSelector( - TransceiverRegistry.TransceiverAlreadyEnabled.selector, address(e) - ) + abi.encodeWithSelector(AdapterRegistry.AdapterAlreadyRegistered.selector, address(e)) ); nttManager.setTransceiver(address(e)); } function test_disableReenableTransceiver() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e)); - nttManager.removeTransceiver(address(e)); + DummyTransceiver e = new DummyTransceiver(chainId, address(endpoint)); nttManager.setTransceiver(address(e)); + nttManager.enableSendTransceiver(chainId2, address(e)); + nttManager.enableRecvTransceiver(chainId2, address(e)); + nttManager.disableSendTransceiver(chainId2, address(e)); + nttManager.disableRecvTransceiver(chainId2, address(e)); + nttManager.enableSendTransceiver(chainId2, address(e)); + nttManager.enableRecvTransceiver(chainId2, address(e)); } - function test_disableAllTransceiversFails() public { - vm.expectRevert(abi.encodeWithSelector(IManagerBase.ZeroThreshold.selector)); - nttManager.removeTransceiver(address(dummyTransceiver)); - } + // TODO: Not sure what this test should do now. + // function test_disableAllTransceiversFails() public { + // vm.expectRevert(abi.encodeWithSelector(IManagerBase.ZeroThreshold.selector)); + // nttManager.removeTransceiver(address(transceiver)); + // } function test_multipleTransceivers() public { - DummyTransceiver e1 = new DummyTransceiver(address(nttManager)); - DummyTransceiver e2 = new DummyTransceiver(address(nttManager)); + // Setup already added one transceiver for chainId2 so we'll add a couple more. + DummyTransceiver e1 = new DummyTransceiver(chainId2, address(endpoint)); + DummyTransceiver e2 = new DummyTransceiver(chainId2, address(endpoint)); nttManager.setTransceiver(address(e1)); nttManager.setTransceiver(address(e2)); } - function test_transceiverIncompatibleNttManager() public { - // Transceiver instantiation reverts if the nttManager doesn't have the proper token method - vm.expectRevert(bytes("")); - new DummyTransceiver(address(0xBEEF)); - } - - function test_transceiverWrongNttManager() public { - // TODO: this is accepted currently. should we include a check to ensure - // only transceivers whose nttManager is us can be registered? (this would be - // a convenience check, not a security one) - DummyToken t = new DummyToken(); - NttManager altNttManager = new MockNttManagerContract( - address(t), IManagerBase.Mode.LOCKING, chainId, 1 days, false - ); - DummyTransceiver e = new DummyTransceiver(address(altNttManager)); - nttManager.setTransceiver(address(e)); - } - function test_noEnabledTransceivers() public { DummyToken token = new DummyToken(); NttManager implementation = new MockNttManagerContract( - address(token), IManagerBase.Mode.LOCKING, chainId, 1 days, false + address(endpoint), + address(executor), + address(token), + IManagerBase.Mode.LOCKING, + chainId, + 1 days, + false ); MockNttManagerContract newNttManager = MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); newNttManager.initialize(); - address user_A = address(0x123); - address user_B = address(0x456); + user_A = address(0x123); + user_B = address(0x456); uint8 decimals = token.decimals(); - newNttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); + newNttManager.setPeer( + chainId2, + toWormholeFormat(address(0x1)), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); newNttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals)); token.mintDummy(address(user_A), 5 * 10 ** decimals); @@ -373,14 +479,20 @@ contract TestNttManager is Test, IRateLimiterEvents { token.approve(address(newNttManager), 3 * 10 ** decimals); - vm.expectRevert(abi.encodeWithSelector(IManagerBase.NoEnabledTransceivers.selector)); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + + vm.expectRevert(abi.encodeWithSelector(Endpoint.AdapterNotEnabled.selector)); newNttManager.transfer( 1 * 10 ** decimals, chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); } @@ -391,78 +503,31 @@ contract TestNttManager is Test, IRateLimiterEvents { } function test_maxOutTransceivers() public { - // Let's register a transceiver and then disable it. We now have 2 registered managers - // since we register 1 in the setup - DummyTransceiver e = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e)); - nttManager.removeTransceiver(address(e)); - - // We should be able to register 64 transceivers total - for (uint256 i = 0; i < 62; ++i) { - DummyTransceiver d = new DummyTransceiver(address(nttManager)); + // We should be able to register 128 transceivers total. We registered one in set up, so go with one less than the max. + uint256 numTransceivers = endpoint.maxAdapters() - 1; + for (uint256 i = 0; i < numTransceivers; ++i) { + DummyTransceiver d = new DummyTransceiver(chainId, address(endpoint)); nttManager.setTransceiver(address(d)); } // Registering a new transceiver should fail as we've hit the cap - DummyTransceiver c = new DummyTransceiver(address(nttManager)); - vm.expectRevert(TransceiverRegistry.TooManyTransceivers.selector); + DummyTransceiver c = new DummyTransceiver(chainId, address(endpoint)); + vm.expectRevert(AdapterRegistry.TooManyAdapters.selector); nttManager.setTransceiver(address(c)); - - // We should be able to renable an already registered transceiver at the cap - nttManager.setTransceiver(address(e)); } - function test_passingInstructionsToTransceivers() public { - // Let's register a transceiver and then disable the original transceiver. We now have 2 registered transceivers - // since we register 1 in the setup - DummyTransceiver e = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e)); - nttManager.removeTransceiver(address(dummyTransceiver)); - - address user_A = address(0x123); - address user_B = address(0x456); - + function test_cancellingOutboundQueuedTransfers() public { DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); - nttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals)); - - token.mintDummy(address(user_A), 5 * 10 ** decimals); - - vm.startPrank(user_A); - - token.approve(address(nttManager), 3 * 10 ** decimals); - - // Pass some instructions for the enabled transceiver - TransceiverStructs.TransceiverInstruction memory transceiverInstruction = - TransceiverStructs.TransceiverInstruction({index: 1, payload: new bytes(1)}); - TransceiverStructs.TransceiverInstruction[] memory transceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](1); - transceiverInstructions[0] = transceiverInstruction; - bytes memory instructions = - TransceiverStructs.encodeTransceiverInstructions(transceiverInstructions); - - nttManager.transfer( - 1 * 10 ** decimals, + nttManager.setPeer( chainId2, - toWormholeFormat(user_B), - toWormholeFormat(user_A), - false, - instructions + toWormholeFormat(address(0x1)), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); - } - - function test_cancellingOutboundQueuedTransfers() public { - address user_A = address(0x123); - address user_B = address(0x456); - - DummyToken token = DummyToken(nttManager.token()); - - uint8 decimals = token.decimals(); - - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); nttManager.setOutboundLimit(0); token.mintDummy(address(user_A), 5 * 10 ** decimals); @@ -480,7 +545,9 @@ contract TestNttManager is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), true, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -517,201 +584,159 @@ contract TestNttManager is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), true, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); assertEq(s2, s1 + 1); } - // == threshold - - function test_cantSetThresholdTooHigh() public { - // 1 transceiver set, so can't set threshold to 2 - vm.expectRevert(abi.encodeWithSelector(IManagerBase.ThresholdTooHigh.selector, 2, 1)); - nttManager.setThreshold(2); - } - - function test_canSetThreshold() public { - DummyTransceiver e1 = new DummyTransceiver(address(nttManager)); - DummyTransceiver e2 = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e1)); - nttManager.setTransceiver(address(e2)); - - nttManager.setThreshold(1); - nttManager.setThreshold(2); - nttManager.setThreshold(1); - } - - function test_cantSetThresholdToZero() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e)); - - vm.expectRevert(abi.encodeWithSelector(IManagerBase.ZeroThreshold.selector)); - nttManager.setThreshold(0); - } - - function test_onlyOwnerCanSetThreshold() public { - address notOwner = address(0x123); - vm.startPrank(notOwner); - - vm.expectRevert( - abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, notOwner) - ); - nttManager.setThreshold(1); - } - - // == threshold - - function test_peerRegistrationLimitsCanBeUpdated() public { - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManager.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, 0); - - IRateLimiter.RateLimitParams memory params = - nttManager.getInboundLimitParams(TransceiverHelpersLib.SENDING_CHAIN_ID); - assertEq(params.limit.getAmount(), 0); - assertEq(params.limit.getDecimals(), 8); - - nttManager.setInboundLimit(type(uint64).max, TransceiverHelpersLib.SENDING_CHAIN_ID); - params = nttManager.getInboundLimitParams(TransceiverHelpersLib.SENDING_CHAIN_ID); - assertEq(params.limit.getAmount(), type(uint64).max / 10 ** (18 - 8)); - assertEq(params.limit.getDecimals(), 8); - } - // === attestation function test_onlyEnabledTransceiversCanAttest() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.removeTransceiver(address(e1)); - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManagerOther.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max); - - bytes memory transceiverMessage; - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - 0, bytes32(0), peer, toWormholeFormat(address(nttManagerOther)), abi.encode("payload") - ); - - vm.expectRevert( - abi.encodeWithSelector(TransceiverRegistry.CallerNotTransceiver.selector, address(e1)) - ); - e1.receiveMessage(transceiverMessage); + // Setup created two managers, each with one enabled transceiver pointed at the other. + + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); + + // This should work. + transceiver.receiveMessage(rmsg); + checkAttestationOnly(nttManager, rmsg, 1, 0); + + // But if we disable the transceiver for receiving, it should fail. + nttManager.disableRecvTransceiver(chainId2, address(transceiver)); + + vm.expectRevert(abi.encodeWithSelector(Endpoint.AdapterNotEnabled.selector)); + transceiver.receiveMessage(rmsg); } function test_onlyPeerNttManagerCanAttest() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.setThreshold(2); - - bytes32 peer = toWormholeFormat(address(nttManager)); - - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - bytes memory transceiverMessage; - (nttManagerMessage, transceiverMessage) = TransceiverHelpersLib - .buildTransceiverMessageWithNttManagerPayload( - 0, bytes32(0), peer, toWormholeFormat(address(nttManagerOther)), abi.encode("payload") - ); - + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(0xdeadbeef)), // This is not the peer NttManager. + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); + + // The endpoint and transceiver don't block this, so this should succeed. + transceiver.receiveMessage(rmsg); + checkAttestationOnly(nttManager, rmsg, 1, 0); + + // But the call to executeMsg should check the peer. + bytes memory message = "Hello, World"; vm.expectRevert( abi.encodeWithSelector( - INttManager.InvalidPeer.selector, TransceiverHelpersLib.SENDING_CHAIN_ID, peer + INttManager.InvalidPeer.selector, + chainId2, + UniversalAddressLibrary.fromAddress(address(0xdeadbeef)).toBytes32() ) ); - e1.receiveMessage(transceiverMessage); - } - - function test_attestSimple() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.setThreshold(2); - - // register nttManager peer - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManagerOther.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max); - - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - bytes memory transceiverMessage; - (nttManagerMessage, transceiverMessage) = TransceiverHelpersLib - .buildTransceiverMessageWithNttManagerPayload( - 0, bytes32(0), peer, toWormholeFormat(address(nttManagerOther)), abi.encode("payload") + nttManager.executeMsg( + chainId2, UniversalAddressLibrary.fromAddress(address(0xdeadbeef)), 0, message ); - - e1.receiveMessage(transceiverMessage); - - bytes32 hash = TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, nttManagerMessage - ); - assertEq(nttManagerOther.messageAttestations(hash), 1); } - function test_attestTwice() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.setThreshold(2); - - // register nttManager peer - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManagerOther.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max); - - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - bytes memory transceiverMessage; - (nttManagerMessage, transceiverMessage) = TransceiverHelpersLib - .buildTransceiverMessageWithNttManagerPayload( - 0, bytes32(0), peer, toWormholeFormat(address(nttManagerOther)), abi.encode("payload") - ); - - bytes32 hash = TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, nttManagerMessage - ); - - e1.receiveMessage(transceiverMessage); - vm.expectRevert( - abi.encodeWithSelector(IManagerBase.TransceiverAlreadyAttestedToMessage.selector, hash) - ); - e1.receiveMessage(transceiverMessage); - - // can't double vote - assertEq(nttManagerOther.messageAttestations(hash), 1); + function test_attestSimple() public { + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); + + // Attest the message. This should work. + transceiver.receiveMessage(rmsg); + checkAttestationOnly(nttManager, rmsg, 1, 0); + + // Can't attest the same message twice. + vm.expectRevert(abi.encodeWithSelector(Endpoint.DuplicateMessageAttestation.selector)); + transceiver.receiveMessage(rmsg); + + // Can't attest when the transceiver is disabled. + nttManager.disableRecvTransceiver(chainId2, address(transceiver)); + + rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 1, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); + + vm.expectRevert(abi.encodeWithSelector(Endpoint.AdapterNotEnabled.selector)); + transceiver.receiveMessage(rmsg); } - function test_attestDisabled() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.setThreshold(2); - - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManagerOther.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max); - - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](1); - transceivers[0] = e1; - - TransceiverStructs.NttManagerMessage memory m; - (m,) = TransceiverHelpersLib.attestTransceiversHelper( - address(0x456), - 0, - chainId, - nttManager, - nttManagerOther, - packTrimmedAmount(50, 8), - packTrimmedAmount(type(uint64).max, 8), - transceivers + function test_executeWhenUnderThresholdShouldRevert() public { + DummyTransceiver[] memory transceivers = new DummyTransceiver[](2); + (transceivers[0], transceivers[1]) = + TransceiverHelpersLib.addTransceiver(nttManager, transceiver, chainId2); + + nttManager.setThreshold(chainId2, 2); + + bytes memory payload = "Hello, World"; + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256(payload), + refundAddr: address(user_A) + }); + + // Attest the message. This should work. + transceiver.receiveMessage(rmsg); + + // The attestation should've been counted. + require( + 1 + == nttManager.messageAttestations( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ), + "Message did not attest" + ); + + // But the message should not yet be approved. + require( + !nttManager.isMessageApproved( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ) ); - nttManagerOther.removeTransceiver(address(e1)); - - bytes32 hash = - TransceiverStructs.nttManagerMessageDigest(TransceiverHelpersLib.SENDING_CHAIN_ID, m); - // a disabled transceiver's vote no longer counts - assertEq(nttManagerOther.messageAttestations(hash), 0); - - nttManagerOther.setTransceiver(address(e1)); - // it counts again when reenabled - assertEq(nttManagerOther.messageAttestations(hash), 1); + // Execute should revert because we haven't met the threshold yet. + vm.expectRevert(abi.encodeWithSelector(INttManager.ThresholdNotMet.selector, 2, 1)); + nttManager.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, payload); } function test_transfer_sequences() public { - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); + nttManager.setPeer( + chainId2, + toWormholeFormat(address(0x1)), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); nttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals)); token.mintDummy(address(user_A), 5 * 10 ** decimals); @@ -726,7 +751,9 @@ contract TestNttManager is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); uint64 s2 = nttManager.transfer( 1 * 10 ** decimals, @@ -734,7 +761,9 @@ contract TestNttManager is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); uint64 s3 = nttManager.transfer( 1 * 10 ** decimals, @@ -742,7 +771,9 @@ contract TestNttManager is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); assertEq(s1, 0); @@ -752,10 +783,14 @@ contract TestNttManager is Test, IRateLimiterEvents { function test_transferWithAmountAndDecimalsThatCouldOverflow() public { // The source chain has 18 decimals trimmed to 8, and the peer has 6 decimals trimmed to 6 - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 6, type(uint64).max); + nttManager.setPeer( + chainId2, + toWormholeFormat(address(0x1)), + 6, + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); - address user_A = address(0x123); - address user_B = address(0x456); DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); assertEq(decimals, 18); @@ -771,6 +806,10 @@ contract TestNttManager is Test, IRateLimiterEvents { uint256 amount = type(uint64).max * 10 ** (decimals - 6); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + vm.expectRevert("SafeCast: value doesn't fit in 64 bits"); nttManager.transfer( amount, @@ -778,7 +817,9 @@ contract TestNttManager is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); // A (slightly) more sensible amount should work normally @@ -789,75 +830,44 @@ contract TestNttManager is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); } - function test_attestationQuorum() public { - address user_B = address(0x456); - - (DummyTransceiver e1, DummyTransceiver e2) = - TransceiverHelpersLib.setup_transceivers(nttManagerOther); - + function test_alreadyExecuted() public { TrimmedAmount transferAmount = packTrimmedAmount(50, 8); + DummyTransceiver[] memory transceiversOther = new DummyTransceiver[](2); + (transceiversOther[0], transceiversOther[1]) = + TransceiverHelpersLib.addTransceiver(nttManagerOther, transceiverOther, chainId); + TransceiverStructs.NttManagerMessage memory m; - bytes memory encodedEm; - { - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](2); - transceivers[0] = e1; - transceivers[1] = e2; - - TransceiverStructs.TransceiverMessage memory em; - (m, em) = TransceiverHelpersLib.attestTransceiversHelper( - user_B, - 0, - chainId, - nttManager, - nttManagerOther, - transferAmount, - packTrimmedAmount(type(uint64).max, 8), - transceivers - ); - encodedEm = TransceiverStructs.encodeTransceiverMessage( - TransceiverHelpersLib.TEST_TRANSCEIVER_PAYLOAD_PREFIX, em - ); - } + DummyTransceiver.Message memory rmsg; + (m, rmsg) = TransceiverHelpersLib.transferAttestAndReceive( + user_B, + 0, + nttManager, + nttManagerOther, + transferAmount, + packTrimmedAmount(type(uint64).max, 8), + transceiversOther + ); - { - DummyToken token = DummyToken(nttManager.token()); - assertEq(token.balanceOf(address(user_B)), transferAmount.untrim(token.decimals())); - } + checkAttestationAndExecution(nttManagerOther, rmsg, 2); - // replay protection for transceiver - vm.recordLogs(); - vm.expectRevert( - abi.encodeWithSelector( - IManagerBase.TransceiverAlreadyAttestedToMessage.selector, - TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, m - ) - ) - ); - e2.receiveMessage(encodedEm); + // Replay protection should revert. + vm.expectRevert(abi.encodeWithSelector(Endpoint.DuplicateMessageAttestation.selector)); + transceiversOther[0].receiveMessage(rmsg); } function test_transfersOnForkedChains() public { uint256 evmChainId = block.chainid; - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); - uint8 decimals = token.decimals(); - nttManager.setPeer( - TransceiverHelpersLib.SENDING_CHAIN_ID, - toWormholeFormat(address(nttManagerOther)), - 9, - type(uint64).max - ); nttManager.setOutboundLimit(0); token.mintDummy(address(user_A), 5 * 10 ** decimals); @@ -868,13 +878,19 @@ contract TestNttManager is Test, IRateLimiterEvents { uint64 sequence = nttManager.transfer( 1 * 10 ** decimals, - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), - true, - new bytes(1) + true, // Should queue + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); + // We should have enqueued message zero and not have sent anything out. + assertEq(sequence, 0); + require(0 == transceiver.getMessages().length, "Should not have sent a message out"); + vm.warp(vm.getBlockTimestamp() + 1 days); vm.chainId(chainId); @@ -887,15 +903,21 @@ contract TestNttManager is Test, IRateLimiterEvents { vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId)); nttManager.cancelOutboundQueuedTransfer(sequence); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + // Outbound transfers fail when queued vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId)); nttManager.transfer( 1 * 10 ** decimals, - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), - true, - new bytes(1) + true, // Should queue + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); vm.stopPrank(); @@ -905,11 +927,13 @@ contract TestNttManager is Test, IRateLimiterEvents { vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId)); nttManager.transfer( 1 * 10 ** decimals, - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); // INBOUND @@ -924,35 +948,42 @@ contract TestNttManager is Test, IRateLimiterEvents { }) ); - bytes memory transceiverMessage; - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - (nttManagerMessage, transceiverMessage) = TransceiverHelpersLib - .buildTransceiverMessageWithNttManagerPayload( - 0, - toWormholeFormat(address(0x1)), - toWormholeFormat(address(nttManagerOther)), - toWormholeFormat(address(nttManager)), - tokenTransferMessage + TransceiverStructs.NttManagerMessage memory m = TransceiverStructs.NttManagerMessage( + 0, toWormholeFormat(address(0x1)), tokenTransferMessage ); + bytes memory nttManagerMessage = TransceiverStructs.encodeNttManagerMessage(m); + + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256(nttManagerMessage), + refundAddr: address(user_A) + }); + + // The endpoint doesn't do fork detection so the attestation will succeed. + transceiver.receiveMessage(rmsg); - // Inbound transfers can't be completed + // But the execute should fail. vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId)); - dummyTransceiver.receiveMessage(transceiverMessage); + nttManager.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage); // Inbound queued transfers can't be completed - nttManager.setInboundLimit(0, TransceiverHelpersLib.SENDING_CHAIN_ID); + nttManager.setInboundLimit(0, chainId2); vm.chainId(evmChainId); - bytes32 hash = TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, nttManagerMessage - ); - dummyTransceiver.receiveMessage(transceiverMessage); + rmsg.sequence = 1; // Update the endpoint sequence number so we don't get duplicate attestation. + transceiver.receiveMessage(rmsg); + nttManager.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage); vm.chainId(chainId); vm.warp(vm.getBlockTimestamp() + 1 days); + bytes32 hash = TransceiverStructs.nttManagerMessageDigest(chainId2, m); vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId)); nttManager.completeInboundQueuedTransfer(hash); } @@ -969,8 +1000,15 @@ contract TestNttManager is Test, IRateLimiterEvents { function test_noAutomaticSlot() public { DummyToken t = new DummyToken(); - MockNttManagerContract c = - new MockNttManagerContract(address(t), IManagerBase.Mode.LOCKING, 1, 1 days, false); + MockNttManagerContract c = new MockNttManagerContract( + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + 1, + 1 days, + false + ); assertEq(c.lastSlot(), 0x0); } @@ -979,7 +1017,15 @@ contract TestNttManager is Test, IRateLimiterEvents { vm.startStateDiffRecording(); - new MockNttManagerContract(address(t), IManagerBase.Mode.LOCKING, 1, 1 days, false); + new MockNttManagerContract( + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + 1, + 1 days, + false + ); Utils.assertSafeUpgradeableConstructor(vm.stopAndReturnStateDiff()); } @@ -992,28 +1038,40 @@ contract TestNttManager is Test, IRateLimiterEvents { address to = address(0x456); DummyToken token = DummyToken(nttManager.token()); - uint8 decimals = token.decimals(); uint256 maxAmount = 5 * 10 ** decimals; token.mintDummy(from, maxAmount); - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); + nttManager.setPeer( + chainId2, + toWormholeFormat(address(0x1)), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); nttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals)); nttManager.setInboundLimit( - packTrimmedAmount(type(uint64).max, 8).untrim(decimals), - TransceiverHelpersLib.SENDING_CHAIN_ID + packTrimmedAmount(type(uint64).max, 8).untrim(decimals), nttManagerOther.chainId() ); vm.startPrank(from); - uint256 transferAmount = 3 * 10 ** decimals; - assertEq( - transferAmount < maxAmount - 500, true, "Transferring more tokens than what exists" - ); + uint256 amountWithDust; + uint256 dustAmount; + { + uint256 transferAmount = 3 * 10 ** decimals; + assertEq( + transferAmount < maxAmount - 500, true, "Transferring more tokens than what exists" + ); + + dustAmount = 500; + amountWithDust = transferAmount + dustAmount; // An amount with 19 digits, which will result in dust due to 18 decimals + token.approve(address(nttManager), amountWithDust); + } - uint256 dustAmount = 500; - uint256 amountWithDust = transferAmount + dustAmount; // An amount with 19 digits, which will result in dust due to 18 decimals - token.approve(address(nttManager), amountWithDust); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); vm.expectRevert( abi.encodeWithSelector( @@ -1026,7 +1084,9 @@ contract TestNttManager is Test, IRateLimiterEvents { toWormholeFormat(to), toWormholeFormat(from), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); vm.stopPrank(); @@ -1054,48 +1114,45 @@ contract TestNttManager is Test, IRateLimiterEvents { // transceivers) and receive a message through it. // This ensures that the storage slots don't get clobbered through the upgrades. - address user_B = address(0x456); DummyToken token = DummyToken(nttManager.token()); TrimmedAmount transferAmount = packTrimmedAmount(50, 8); - (ITransceiverReceiver e1, ITransceiverReceiver e2) = - TransceiverHelpersLib.setup_transceivers(nttManagerOther); // Step 1 (contract is deployed by setUp()) - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](2); - transceivers[0] = e1; - transceivers[1] = e2; + DummyTransceiver[] memory transceivers = new DummyTransceiver[](2); + (transceivers[0], transceivers[1]) = + TransceiverHelpersLib.addTransceiver(nttManagerOther, transceiverOther, chainId); TransceiverStructs.NttManagerMessage memory m; - bytes memory encodedEm; - { - TransceiverStructs.TransceiverMessage memory em; - (m, em) = TransceiverHelpersLib.attestTransceiversHelper( - user_B, - 0, - chainId, - nttManager, - nttManagerOther, - transferAmount, - packTrimmedAmount(type(uint64).max, 8), - transceivers - ); - encodedEm = TransceiverStructs.encodeTransceiverMessage( - TransceiverHelpersLib.TEST_TRANSCEIVER_PAYLOAD_PREFIX, em - ); - } + DummyTransceiver.Message memory rmsg; + (m, rmsg) = TransceiverHelpersLib.transferAttestAndReceive( + user_B, + 0, + nttManager, + nttManagerOther, + transferAmount, + packTrimmedAmount(type(uint64).max, 8), + transceivers + ); + checkAttestationAndExecution(nttManagerOther, rmsg, 2); assertEq(token.balanceOf(address(user_B)), transferAmount.untrim(token.decimals())); // Step 2 (upgrade to a new nttManager) MockNttManagerContract newNttManager = new MockNttManagerContract( - nttManager.token(), IManagerBase.Mode.LOCKING, chainId, 1 days, false + address(nttManagerOther.endpoint()), + address(nttManagerOther.executor()), + nttManagerOther.token(), + nttManagerOther.mode(), + nttManagerOther.chainId(), + 1 days, + false ); + nttManagerOther.upgrade(address(newNttManager)); - TransceiverHelpersLib.attestTransceiversHelper( + (m, rmsg) = TransceiverHelpersLib.transferAttestAndReceive( user_B, bytes32(uint256(1)), - chainId, nttManager, // this is the proxy nttManagerOther, // this is the proxy transferAmount, @@ -1103,6 +1160,7 @@ contract TestNttManager is Test, IRateLimiterEvents { transceivers ); + checkAttestationAndExecution(nttManagerOther, rmsg, 2); assertEq(token.balanceOf(address(user_B)), transferAmount.untrim(token.decimals()) * 2); } @@ -1114,7 +1172,13 @@ contract TestNttManager is Test, IRateLimiterEvents { DummyTokenMintAndBurn(address(new ERC1967Proxy(address(dummy1), ""))); NttManager implementation = new MockNttManagerContract( - address(t), IManagerBase.Mode.LOCKING, chainId, 1 days, false + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + chainId, + 1 days, + false ); MockNttManagerContract newNttManager = @@ -1123,14 +1187,12 @@ contract TestNttManager is Test, IRateLimiterEvents { // register nttManager peer and transceiver bytes32 peer = toWormholeFormat(address(nttManager)); - newNttManager.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max); - { - DummyTransceiver e = new DummyTransceiver(address(newNttManager)); - newNttManager.setTransceiver(address(e)); - } + newNttManager.setPeer(chainId2, peer, 9, NttManagerHelpersLib.gasLimit, type(uint64).max); + DummyTransceiver e1 = new DummyTransceiver(chainId, address(endpoint)); + newNttManager.setTransceiver(address(e1)); + newNttManager.enableSendTransceiver(chainId2, address(e1)); + newNttManager.enableRecvTransceiver(chainId2, address(e1)); - address user_A = address(0x123); - address user_B = address(0x456); t.mintDummy(address(user_A), 5 * 10 ** t.decimals()); // Check that we can initiate a transfer @@ -1138,19 +1200,17 @@ contract TestNttManager is Test, IRateLimiterEvents { t.approve(address(newNttManager), 3 * 10 ** t.decimals()); newNttManager.transfer( 1 * 10 ** t.decimals(), - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); // Check that we can receive a transfer - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(newNttManager); - newNttManager.setThreshold(1); - - bytes memory transceiverMessage; bytes memory tokenTransferMessage; TrimmedAmount transferAmount = packTrimmedAmount(100, 8); @@ -1165,11 +1225,25 @@ contract TestNttManager is Test, IRateLimiterEvents { }) ); - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - 0, bytes32(0), peer, toWormholeFormat(address(newNttManager)), tokenTransferMessage + TransceiverStructs.NttManagerMessage memory m = TransceiverStructs.NttManagerMessage( + 0, toWormholeFormat(address(0x1)), tokenTransferMessage ); + bytes memory nttManagerMessage = TransceiverStructs.encodeNttManagerMessage(m); + + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(newNttManager)), + payloadHash: keccak256(nttManagerMessage), + refundAddr: address(user_A) + }); + + // The endpoint doesn't do fork detection so the attestation will succeed. + e1.receiveMessage(rmsg); + newNttManager.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage); - e1.receiveMessage(transceiverMessage); uint256 userBBalanceBefore = t.balanceOf(address(user_B)); assertEq(userBBalanceBefore, transferAmount.untrim(t.decimals())); @@ -1179,23 +1253,27 @@ contract TestNttManager is Test, IRateLimiterEvents { vm.startPrank(user_A); newNttManager.transfer( - 1 * 10 ** 10, - TransceiverHelpersLib.SENDING_CHAIN_ID, + 1 * 10 ** t.decimals(), + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - bytes32("1"), - bytes32(0), - peer, - toWormholeFormat(address(newNttManager)), - tokenTransferMessage + m = TransceiverStructs.NttManagerMessage( + bytes32("1"), toWormholeFormat(address(0x1)), tokenTransferMessage ); - e1.receiveMessage(transceiverMessage); + nttManagerMessage = TransceiverStructs.encodeNttManagerMessage(m); + + rmsg.sequence++; + rmsg.payloadHash = keccak256(nttManagerMessage); + e1.receiveMessage(rmsg); + newNttManager.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage); + assertEq( t.balanceOf(address(user_B)), userBBalanceBefore + transferAmount.untrim(t.decimals()) ); @@ -1204,64 +1282,151 @@ contract TestNttManager is Test, IRateLimiterEvents { DummyTokenDifferentDecimals dummy3 = new DummyTokenDifferentDecimals(7); // 7 is 7 trimmed t.upgrade(address(dummy3)); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + vm.startPrank(user_A); vm.expectRevert(abi.encodeWithSelector(NumberOfDecimalsNotEqual.selector, 8, 7)); newNttManager.transfer( 1 * 10 ** 7, - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); vm.stopPrank(); - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - bytes32("2"), - bytes32(0), - peer, - toWormholeFormat(address(newNttManager)), - tokenTransferMessage + m = TransceiverStructs.NttManagerMessage( + bytes32("2"), toWormholeFormat(address(0x1)), tokenTransferMessage ); + nttManagerMessage = TransceiverStructs.encodeNttManagerMessage(m); + + rmsg.sequence++; + rmsg.payloadHash = keccak256(nttManagerMessage); + e1.receiveMessage(rmsg); vm.expectRevert(abi.encodeWithSelector(NumberOfDecimalsNotEqual.selector, 8, 7)); - e1.receiveMessage(transceiverMessage); + newNttManager.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage); } - function test_transferWithInstructionIndexOutOfBounds() public { - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction = - TransceiverStructs.TransceiverInstruction({index: 100, payload: new bytes(1)}); - TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](1); - TransceiverInstructions[0] = TransceiverInstruction; - bytes memory encodedInstructions = - TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions); - - address user_A = address(0x123); - address user_B = address(0x456); + function test_cantSetGasLimitToZero() public { + assertEq(NttManagerHelpersLib.gasLimit, nttManager.getPeer(chainId2).gasLimit); + + // Can't set the gas limit to zero directly. + vm.expectRevert(abi.encodeWithSelector(INttManager.InvalidGasLimitZero.selector, chainId2)); + nttManager.setGasLimit(chainId2, 0); + assertEq(NttManagerHelpersLib.gasLimit, nttManager.getPeer(chainId2).gasLimit); + + // Can't specify a gas limit of zero when setting the peer. + bytes32 peer = toWormholeFormat(address(nttManagerOther)); + uint8 decimals = DummyToken(nttManagerOther.token()).decimals(); + vm.expectRevert(abi.encodeWithSelector(INttManager.InvalidGasLimitZero.selector, chainId2)); + nttManager.setPeer(chainId2, peer, decimals, 0, type(uint64).max); + assertEq(NttManagerHelpersLib.gasLimit, nttManager.getPeer(chainId2).gasLimit); + + // Can't set the gas limit if the peer is not already set. + vm.expectRevert(abi.encodeWithSelector(INttManager.InvalidPeerZeroAddress.selector)); + nttManager.setGasLimit(chainId2 + 1, NttManagerHelpersLib.gasLimit); + assertEq(0, nttManager.getPeer(chainId2 + 1).gasLimit); + + // Can update the gas on an existing peer. + nttManager.setGasLimit(chainId2, NttManagerHelpersLib.gasLimit - 100000); + assertEq(NttManagerHelpersLib.gasLimit - 100000, nttManager.getPeer(chainId2).gasLimit); + } + function test_cantTransferWithZeroGasLimit() public { DummyToken token = DummyToken(nttManager.token()); + uint256 amount = 1 * 10 ** token.decimals(); + uint256 limit = 5 * amount; - uint8 decimals = token.decimals(); + token.mintDummy(address(user_A), limit); - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); - nttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals)); - - token.mintDummy(address(user_A), 5 * 10 ** decimals); + nttManager.setGasLimitToZero(nttManagerOther.chainId()); vm.startPrank(user_A); + token.approve(address(nttManager), amount); - token.approve(address(nttManager), 3 * 10 ** decimals); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); - vm.expectRevert( - abi.encodeWithSelector(TransceiverStructs.InvalidInstructionIndex.selector, 100, 1) - ); + vm.expectRevert(abi.encodeWithSelector(INttManager.InvalidGasLimitZero.selector, chainId2)); nttManager.transfer( - 1 * 10 ** decimals, + amount, chainId2, toWormholeFormat(user_B), - toWormholeFormat(user_A), - false, - encodedInstructions + executorSignedQuote, + executorRelayInstructions, + adapterInstructions + ); + } + + function checkAttestationOnly( + NttManager nttm, + DummyTransceiver.Message memory rmsg, + uint8 expectedAttestations, + uint8 transceiverIdx + ) public view { + // Verify that it shows as attested. + require( + expectedAttestations + == nttm.messageAttestations( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ), + "Message did not attest" + ); + + // Verify that the right transceiver attested. + require( + nttm.transceiverAttestedToMessage( + rmsg.srcChain, + rmsg.srcAddr, + rmsg.sequence, + rmsg.dstAddr, + rmsg.payloadHash, + transceiverIdx + ), + "Transceiver did not attest to message" + ); + + require( + nttm.isMessageApproved( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ) + ); + + // But the message should not be marked as executed. + require( + !nttm.isMessageExecuted( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ), + "Message should not be marked executed yet" + ); + } + + error WrongNumberOfAttestations(uint8 expected, uint8 actual); + + function checkAttestationAndExecution( + NttManager nttm, + DummyTransceiver.Message memory rmsg, + uint8 expectedAttestations + ) public view { + // Verify that it shows as attested. + uint8 actual = nttm.messageAttestations( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ); + if (actual != expectedAttestations) { + revert WrongNumberOfAttestations(expectedAttestations, actual); + } + + require( + nttManagerOther.isMessageExecuted( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ), + "Message should be marked executed yet" ); } } diff --git a/evm/test/NttManagerNoRateLimiting.t.sol b/evm/test/NttManagerNoRateLimiting.t.sol index 9df4d4d80..a5525547d 100644 --- a/evm/test/NttManagerNoRateLimiting.t.sol +++ b/evm/test/NttManagerNoRateLimiting.t.sol @@ -9,7 +9,7 @@ import "../src/interfaces/INttManager.sol"; import "../src/interfaces/IRateLimiter.sol"; import "../src/interfaces/IManagerBase.sol"; import "../src/interfaces/IRateLimiterEvents.sol"; -import "../src/NttManager/TransceiverRegistry.sol"; +import "../src/libraries/external/OwnableUpgradeable.sol"; import "../src/libraries/PausableUpgradeable.sol"; import "../src/libraries/TransceiverHelpers.sol"; import {Utils} from "./libraries/Utils.sol"; @@ -19,18 +19,25 @@ import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; import "wormhole-solidity-sdk/testing/helpers/WormholeSimulator.sol"; import "wormhole-solidity-sdk/Utils.sol"; +import "example-messaging-endpoint/evm/src/AdapterRegistry.sol"; import "./libraries/TransceiverHelpers.sol"; import "./libraries/NttManagerHelpers.sol"; -import "./interfaces/ITransceiverReceiver.sol"; import "./mocks/DummyTransceiver.sol"; import "../src/mocks/DummyToken.sol"; import "./mocks/MockNttManager.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockExecutor.sol"; // TODO: set this up so the common functionality tests can be run against both -contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { +contract TestNoRateLimitingNttManager is Test, IRateLimiterEvents { MockNttManagerNoRateLimitingContract nttManager; MockNttManagerNoRateLimitingContract nttManagerOther; - MockNttManagerNoRateLimitingContract nttManagerZeroRateLimiter; + MockEndpoint endpoint; + MockEndpoint endpointOther; + MockExecutor executor; + MockExecutor executorOther; + DummyTransceiver transceiver; + DummyTransceiver transceiverOther; using TrimmedAmountLib for uint256; using TrimmedAmountLib for TrimmedAmount; @@ -38,11 +45,14 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { // 0x99'E''T''T' uint16 constant chainId = 7; uint16 constant chainId2 = 8; + + address user_A = address(0x123); + address user_B = address(0x456); + uint256 constant DEVNET_GUARDIAN_PK = 0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0; WormholeSimulator guardian; uint256 initialBlockTimestamp; - DummyTransceiver dummyTransceiver; function setUp() public { string memory url = "https://ethereum-sepolia-rpc.publicnode.com"; @@ -52,12 +62,24 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { guardian = new WormholeSimulator(address(wormhole), DEVNET_GUARDIAN_PK); + endpoint = new MockEndpoint(chainId); + endpointOther = new MockEndpoint(chainId2); + + executor = new MockExecutor(chainId); + executorOther = new MockExecutor(chainId2); + DummyToken t = new DummyToken(); - NttManagerNoRateLimiting implementation = - new MockNttManagerNoRateLimitingContract(address(t), IManagerBase.Mode.LOCKING, chainId); + NttManagerNoRateLimiting implementation = new MockNttManagerNoRateLimitingContract( + address(endpoint), address(executor), address(t), IManagerBase.Mode.LOCKING, chainId + ); - NttManagerNoRateLimiting otherImplementation = - new MockNttManagerNoRateLimitingContract(address(t), IManagerBase.Mode.LOCKING, chainId); + NttManagerNoRateLimiting otherImplementation = new MockNttManagerNoRateLimitingContract( + address(endpointOther), + address(executorOther), + address(t), + IManagerBase.Mode.LOCKING, + chainId2 + ); nttManager = MockNttManagerNoRateLimitingContract( address(new ERC1967Proxy(address(implementation), "")) @@ -69,31 +91,34 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { ); nttManagerOther.initialize(); - dummyTransceiver = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(dummyTransceiver)); - } - - // === pure unit tests + nttManager.setPeer( + chainId2, + toWormholeFormat(address(nttManagerOther)), + t.decimals(), + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); - // naive implementation of countSetBits to test against - function simpleCount( - uint64 n - ) public pure returns (uint8) { - uint8 count; + nttManagerOther.setPeer( + chainId, + toWormholeFormat(address(nttManager)), + t.decimals(), + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); - while (n > 0) { - count += uint8(n & 1); - n >>= 1; - } + transceiver = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(transceiver)); + nttManager.enableSendTransceiver(chainId2, address(transceiver)); + nttManager.enableRecvTransceiver(chainId2, address(transceiver)); - return count; + transceiverOther = new DummyTransceiver(chainId2, address(endpointOther)); + nttManagerOther.setTransceiver(address(transceiverOther)); + nttManagerOther.enableSendTransceiver(chainId, address(transceiverOther)); + nttManagerOther.enableRecvTransceiver(chainId, address(transceiverOther)); } - function testFuzz_countSetBits( - uint64 n - ) public { - assertEq(simpleCount(n), countSetBits(n)); - } + function test_setUp() public {} // === ownership @@ -123,12 +148,18 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { nttManager.pause(); assertEq(nttManager.isPaused(), true); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + // When the NttManagerNoRateLimiting is paused, initiating transfers, completing queued transfers on both source and destination chains, // executing transfers and attesting to transfers should all revert vm.expectRevert( abi.encodeWithSelector(PausableUpgradeable.RequireContractIsNotPaused.selector) ); - nttManager.transfer(0, 0, bytes32(0)); + nttManager.transfer( + 0, 0, bytes32(0), executorSignedQuote, executorRelayInstructions, adapterInstructions + ); vm.expectRevert( abi.encodeWithSelector(PausableUpgradeable.RequireContractIsNotPaused.selector) @@ -140,27 +171,26 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { ); nttManager.completeInboundQueuedTransfer(bytes32(0)); - vm.expectRevert( - abi.encodeWithSelector(PausableUpgradeable.RequireContractIsNotPaused.selector) - ); - TransceiverStructs.NttManagerMessage memory message; - nttManager.executeMsg(0, bytes32(0), message); + // The endpoint and transceiver are not pausable, so calling receiveMessage should still work. + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); - bytes memory transceiverMessage; - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - 0, - bytes32(0), - toWormholeFormat(address(nttManagerOther)), - toWormholeFormat(address(nttManager)), - abi.encode("payload") - ); + transceiver.receiveMessage(rmsg); + + bytes memory message = "Hello, World"; + + // But executeMsg should be paused in the NttManagerNoRateLimiting. vm.expectRevert( abi.encodeWithSelector(PausableUpgradeable.RequireContractIsNotPaused.selector) ); - dummyTransceiver.receiveMessage(transceiverMessage); - - nttManager.unpause(); - assertEq(nttManager.isPaused(), false); + nttManager.executeMsg(0, UniversalAddressLibrary.fromAddress(address(0)), 0, message); } function test_pausePauserUnpauseOnlyOwner() public { @@ -189,8 +219,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { // === deployment with invalid token function test_brokenToken() public { DummyToken t = new DummyTokenBroken(); - NttManagerNoRateLimiting implementation = - new MockNttManagerNoRateLimitingContract(address(t), IManagerBase.Mode.LOCKING, chainId); + NttManagerNoRateLimiting implementation = new MockNttManagerNoRateLimitingContract( + address(endpoint), address(executor), address(t), IManagerBase.Mode.LOCKING, chainId + ); NttManagerNoRateLimiting newNttManagerNoRateLimiting = MockNttManagerNoRateLimitingContract( address(new ERC1967Proxy(address(implementation), "")) @@ -198,19 +229,25 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { vm.expectRevert(abi.encodeWithSelector(INttManager.StaticcallFailed.selector)); newNttManagerNoRateLimiting.initialize(); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + vm.expectRevert(abi.encodeWithSelector(INttManager.StaticcallFailed.selector)); - newNttManagerNoRateLimiting.transfer(1, 1, bytes32("1")); + newNttManagerNoRateLimiting.transfer( + 1, 1, bytes32("1"), executorSignedQuote, executorRelayInstructions, adapterInstructions + ); } // === transceiver registration function test_registerTransceiver() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); + DummyTransceiver e = new DummyTransceiver(chainId, address(endpoint)); nttManager.setTransceiver(address(e)); } function test_onlyOwnerCanModifyTransceivers() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); + DummyTransceiver e = new DummyTransceiver(chainId, address(endpoint)); nttManager.setTransceiver(address(e)); address notOwner = address(0x123); @@ -220,79 +257,69 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, notOwner) ); nttManager.setTransceiver(address(e)); - - vm.expectRevert( - abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, notOwner) - ); - nttManager.removeTransceiver(address(e)); } function test_cantEnableTransceiverTwice() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); + DummyTransceiver e = new DummyTransceiver(chainId, address(endpoint)); nttManager.setTransceiver(address(e)); vm.expectRevert( - abi.encodeWithSelector( - TransceiverRegistry.TransceiverAlreadyEnabled.selector, address(e) - ) + abi.encodeWithSelector(AdapterRegistry.AdapterAlreadyRegistered.selector, address(e)) ); nttManager.setTransceiver(address(e)); } function test_disableReenableTransceiver() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e)); - nttManager.removeTransceiver(address(e)); + DummyTransceiver e = new DummyTransceiver(chainId, address(endpoint)); nttManager.setTransceiver(address(e)); + nttManager.enableSendTransceiver(chainId2, address(e)); + nttManager.enableRecvTransceiver(chainId2, address(e)); + nttManager.disableSendTransceiver(chainId2, address(e)); + nttManager.disableRecvTransceiver(chainId2, address(e)); + nttManager.enableSendTransceiver(chainId2, address(e)); + nttManager.enableRecvTransceiver(chainId2, address(e)); } - function test_disableAllTransceiversFails() public { - vm.expectRevert(abi.encodeWithSelector(IManagerBase.ZeroThreshold.selector)); - nttManager.removeTransceiver(address(dummyTransceiver)); - } + // TODO: Not sure what this test should do now. + // function test_disableAllTransceiversFails() public { + // vm.expectRevert(abi.encodeWithSelector(IManagerBase.ZeroThreshold.selector)); + // nttManager.removeTransceiver(address(transceiver)); + // } function test_multipleTransceivers() public { - DummyTransceiver e1 = new DummyTransceiver(address(nttManager)); - DummyTransceiver e2 = new DummyTransceiver(address(nttManager)); + // Setup already added one transceiver for chainId2 so we'll add a couple more. + DummyTransceiver e1 = new DummyTransceiver(chainId2, address(endpoint)); + DummyTransceiver e2 = new DummyTransceiver(chainId2, address(endpoint)); nttManager.setTransceiver(address(e1)); nttManager.setTransceiver(address(e2)); } - function test_transceiverIncompatibleNttManagerNoRateLimiting() public { - // Transceiver instantiation reverts if the nttManager doesn't have the proper token method - vm.expectRevert(bytes("")); - new DummyTransceiver(address(0xBEEF)); - } - - function test_transceiverWrongNttManagerNoRateLimiting() public { - // TODO: this is accepted currently. should we include a check to ensure - // only transceivers whose nttManager is us can be registered? (this would be - // a convenience check, not a security one) - DummyToken t = new DummyToken(); - NttManagerNoRateLimiting altNttManagerNoRateLimiting = - new MockNttManagerNoRateLimitingContract(address(t), IManagerBase.Mode.LOCKING, chainId); - DummyTransceiver e = new DummyTransceiver(address(altNttManagerNoRateLimiting)); - nttManager.setTransceiver(address(e)); - } - function test_noEnabledTransceivers() public { DummyToken token = new DummyToken(); NttManagerNoRateLimiting implementation = new MockNttManagerNoRateLimitingContract( - address(token), IManagerBase.Mode.LOCKING, chainId + address(endpoint), + address(executorOther), + address(token), + IManagerBase.Mode.LOCKING, + chainId ); MockNttManagerNoRateLimitingContract newNttManagerNoRateLimiting = MockNttManagerNoRateLimitingContract(address(new ERC1967Proxy(address(implementation), ""))); newNttManagerNoRateLimiting.initialize(); - address user_A = address(0x123); - address user_B = address(0x456); + user_A = address(0x123); + user_B = address(0x456); uint8 decimals = token.decimals(); newNttManagerNoRateLimiting.setPeer( - chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max + chainId2, + toWormholeFormat(address(0x1)), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max ); newNttManagerNoRateLimiting.setOutboundLimit( packTrimmedAmount(type(uint64).max, 8).untrim(decimals) @@ -304,14 +331,20 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { token.approve(address(newNttManagerNoRateLimiting), 3 * 10 ** decimals); - vm.expectRevert(abi.encodeWithSelector(IManagerBase.NoEnabledTransceivers.selector)); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + + vm.expectRevert(abi.encodeWithSelector(Endpoint.AdapterNotEnabled.selector)); newNttManagerNoRateLimiting.transfer( 1 * 10 ** decimals, chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); } @@ -321,120 +354,17 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { nttManager.setTransceiver(address(0x123)); } - function test_maxOutTransceivers() public { - // Let's register a transceiver and then disable it. We now have 2 registered managers - // since we register 1 in the setup - DummyTransceiver e = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e)); - nttManager.removeTransceiver(address(e)); - - // We should be able to register 64 transceivers total - for (uint256 i = 0; i < 62; ++i) { - DummyTransceiver d = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(d)); - } - - // Registering a new transceiver should fail as we've hit the cap - DummyTransceiver c = new DummyTransceiver(address(nttManager)); - vm.expectRevert(TransceiverRegistry.TooManyTransceivers.selector); - nttManager.setTransceiver(address(c)); - - // We should be able to renable an already registered transceiver at the cap - nttManager.setTransceiver(address(e)); - } - - function test_passingInstructionsToTransceivers() public { - // Let's register a transceiver and then disable the original transceiver. We now have 2 registered transceivers - // since we register 1 in the setup - DummyTransceiver e = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e)); - nttManager.removeTransceiver(address(dummyTransceiver)); - - address user_A = address(0x123); - address user_B = address(0x456); - - DummyToken token = DummyToken(nttManager.token()); - - uint8 decimals = token.decimals(); - - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); - nttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals)); - - token.mintDummy(address(user_A), 5 * 10 ** decimals); - - vm.startPrank(user_A); - - token.approve(address(nttManager), 3 * 10 ** decimals); - - // Pass some instructions for the enabled transceiver - TransceiverStructs.TransceiverInstruction memory transceiverInstruction = - TransceiverStructs.TransceiverInstruction({index: 1, payload: new bytes(1)}); - TransceiverStructs.TransceiverInstruction[] memory transceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](1); - transceiverInstructions[0] = transceiverInstruction; - bytes memory instructions = - TransceiverStructs.encodeTransceiverInstructions(transceiverInstructions); - - nttManager.transfer( - 1 * 10 ** decimals, - chainId2, - toWormholeFormat(user_B), - toWormholeFormat(user_A), - false, - instructions - ); - } - - // == threshold - - function test_cantSetThresholdTooHigh() public { - // 1 transceiver set, so can't set threshold to 2 - vm.expectRevert(abi.encodeWithSelector(IManagerBase.ThresholdTooHigh.selector, 2, 1)); - nttManager.setThreshold(2); - } - - function test_canSetThreshold() public { - DummyTransceiver e1 = new DummyTransceiver(address(nttManager)); - DummyTransceiver e2 = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e1)); - nttManager.setTransceiver(address(e2)); - - nttManager.setThreshold(1); - nttManager.setThreshold(2); - nttManager.setThreshold(1); - } - - function test_cantSetThresholdToZero() public { - DummyTransceiver e = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e)); - - vm.expectRevert(abi.encodeWithSelector(IManagerBase.ZeroThreshold.selector)); - nttManager.setThreshold(0); - } - - function test_onlyOwnerCanSetThreshold() public { - address notOwner = address(0x123); - vm.startPrank(notOwner); - - vm.expectRevert( - abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, notOwner) - ); - nttManager.setThreshold(1); - } - - // == threshold - function test_peerRegistrationLimitsCantBeUpdated() public { bytes32 peer = toWormholeFormat(address(nttManager)); - nttManager.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, 0); + nttManager.setPeer(nttManagerOther.chainId(), peer, 9, NttManagerHelpersLib.gasLimit, 0); IRateLimiter.RateLimitParams memory params = - nttManager.getInboundLimitParams(TransceiverHelpersLib.SENDING_CHAIN_ID); + nttManager.getInboundLimitParams(nttManagerOther.chainId()); assertEq(params.limit.getAmount(), 0); assertEq(params.limit.getDecimals(), 0); - nttManager.setInboundLimit(type(uint64).max, TransceiverHelpersLib.SENDING_CHAIN_ID); - params = nttManager.getInboundLimitParams(TransceiverHelpersLib.SENDING_CHAIN_ID); + nttManager.setInboundLimit(type(uint64).max, nttManagerOther.chainId()); + params = nttManager.getInboundLimitParams(nttManagerOther.chainId()); assertEq(params.limit.getAmount(), 0); assertEq(params.limit.getDecimals(), 0); } @@ -442,138 +372,106 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { // === attestation function test_onlyEnabledTransceiversCanAttest() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.removeTransceiver(address(e1)); - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManagerOther.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max); - - bytes memory transceiverMessage; - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - 0, bytes32(0), peer, toWormholeFormat(address(nttManagerOther)), abi.encode("payload") - ); - - vm.expectRevert( - abi.encodeWithSelector(TransceiverRegistry.CallerNotTransceiver.selector, address(e1)) - ); - e1.receiveMessage(transceiverMessage); + // Setup created two managers, each with one enabled transceiver pointed at the other. + + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); + + // This should work. + transceiver.receiveMessage(rmsg); + checkAttestationOnly(nttManager, rmsg, 1, 0); + + // But if we disable the transceiver for receiving, it should fail. + nttManager.disableRecvTransceiver(chainId2, address(transceiver)); + + vm.expectRevert(abi.encodeWithSelector(Endpoint.AdapterNotEnabled.selector)); + transceiver.receiveMessage(rmsg); } - function test_onlyPeerNttManagerNoRateLimitingCanAttest() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.setThreshold(2); - - bytes32 peer = toWormholeFormat(address(nttManager)); - - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - bytes memory transceiverMessage; - (nttManagerMessage, transceiverMessage) = TransceiverHelpersLib - .buildTransceiverMessageWithNttManagerPayload( - 0, bytes32(0), peer, toWormholeFormat(address(nttManagerOther)), abi.encode("payload") - ); - + function test_onlyPeerNttManagerCanAttest() public { + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(0xdeadbeef)), // This is not the peer NttManagerNoRateLimiting. + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); + + // The endpoint and transceiver don't block this, so this should succeed. + transceiver.receiveMessage(rmsg); + checkAttestationOnly(nttManager, rmsg, 1, 0); + + // But the call to executeMsg should check the peer. + bytes memory message = "Hello, World"; vm.expectRevert( abi.encodeWithSelector( - INttManager.InvalidPeer.selector, TransceiverHelpersLib.SENDING_CHAIN_ID, peer + INttManager.InvalidPeer.selector, + chainId2, + UniversalAddressLibrary.fromAddress(address(0xdeadbeef)).toBytes32() ) ); - e1.receiveMessage(transceiverMessage); - } - - function test_attestSimple() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.setThreshold(2); - - // register nttManager peer - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManagerOther.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max); - - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - bytes memory transceiverMessage; - (nttManagerMessage, transceiverMessage) = TransceiverHelpersLib - .buildTransceiverMessageWithNttManagerPayload( - 0, bytes32(0), peer, toWormholeFormat(address(nttManagerOther)), abi.encode("payload") + nttManager.executeMsg( + chainId2, UniversalAddressLibrary.fromAddress(address(0xdeadbeef)), 0, message ); - - e1.receiveMessage(transceiverMessage); - - bytes32 hash = TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, nttManagerMessage - ); - assertEq(nttManagerOther.messageAttestations(hash), 1); - } - - function test_attestTwice() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.setThreshold(2); - - // register nttManager peer - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManagerOther.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max); - - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - bytes memory transceiverMessage; - (nttManagerMessage, transceiverMessage) = TransceiverHelpersLib - .buildTransceiverMessageWithNttManagerPayload( - 0, bytes32(0), peer, toWormholeFormat(address(nttManagerOther)), abi.encode("payload") - ); - - bytes32 hash = TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, nttManagerMessage - ); - - e1.receiveMessage(transceiverMessage); - vm.expectRevert( - abi.encodeWithSelector(IManagerBase.TransceiverAlreadyAttestedToMessage.selector, hash) - ); - e1.receiveMessage(transceiverMessage); - - // can't double vote - assertEq(nttManagerOther.messageAttestations(hash), 1); } - function test_attestDisabled() public { - (DummyTransceiver e1,) = TransceiverHelpersLib.setup_transceivers(nttManagerOther); - nttManagerOther.setThreshold(2); - - bytes32 peer = toWormholeFormat(address(nttManager)); - nttManagerOther.setPeer(TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max); - - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](1); - transceivers[0] = e1; - - TransceiverStructs.NttManagerMessage memory m; - (m,) = TransceiverHelpersLib.attestTransceiversHelper( - address(0x456), - 0, - chainId, - nttManager, - nttManagerOther, - packTrimmedAmount(50, 8), - packTrimmedAmount(type(uint64).max, 8), - transceivers - ); - - nttManagerOther.removeTransceiver(address(e1)); - - bytes32 hash = - TransceiverStructs.nttManagerMessageDigest(TransceiverHelpersLib.SENDING_CHAIN_ID, m); - // a disabled transceiver's vote no longer counts - assertEq(nttManagerOther.messageAttestations(hash), 0); - - nttManagerOther.setTransceiver(address(e1)); - // it counts again when reenabled - assertEq(nttManagerOther.messageAttestations(hash), 1); + function test_attestSimple() public { + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); + + // Attest the message. This should work. + transceiver.receiveMessage(rmsg); + checkAttestationOnly(nttManager, rmsg, 1, 0); + + // Can't attest the same message twice. + vm.expectRevert(abi.encodeWithSelector(Endpoint.DuplicateMessageAttestation.selector)); + transceiver.receiveMessage(rmsg); + + // Can't attest when the transceiver is disabled. + nttManager.disableRecvTransceiver(chainId2, address(transceiver)); + + rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 1, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256("Hello, World"), + refundAddr: address(user_A) + }); + + vm.expectRevert(abi.encodeWithSelector(Endpoint.AdapterNotEnabled.selector)); + transceiver.receiveMessage(rmsg); } function test_transfer_sequences() public { - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); + nttManager.setPeer( + chainId2, + toWormholeFormat(address(0x1)), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); nttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals)); token.mintDummy(address(user_A), 5 * 10 ** decimals); @@ -588,7 +486,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); uint64 s2 = nttManager.transfer( 1 * 10 ** decimals, @@ -596,7 +496,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); uint64 s3 = nttManager.transfer( 1 * 10 ** decimals, @@ -604,7 +506,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); assertEq(s1, 0); @@ -614,10 +518,14 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { function test_transferWithAmountAndDecimalsThatCouldOverflow() public { // The source chain has 18 decimals trimmed to 8, and the peer has 6 decimals trimmed to 6 - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 6, type(uint64).max); + nttManager.setPeer( + chainId2, + toWormholeFormat(address(0x1)), + 6, + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); - address user_A = address(0x123); - address user_B = address(0x456); DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); assertEq(decimals, 18); @@ -627,6 +535,10 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { vm.startPrank(user_A); token.approve(address(nttManager), type(uint256).max); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + // When transferring to a chain with 6 decimals the amount will get trimmed to 6 decimals. // Without rate limiting, this won't be scaled back up to 8 for local accounting. uint256 amount = type(uint64).max * 10 ** (decimals - 6); @@ -636,7 +548,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); // However, attempting to transfer an amount higher than the destination chain can handle will revert. @@ -648,7 +562,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); // A (slightly) more sensible amount should work normally @@ -659,75 +575,44 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); } - function test_attestationQuorum() public { - address user_B = address(0x456); - - (DummyTransceiver e1, DummyTransceiver e2) = - TransceiverHelpersLib.setup_transceivers(nttManagerOther); - + function test_alreadyExecuted() public { TrimmedAmount transferAmount = packTrimmedAmount(50, 8); + DummyTransceiver[] memory transceivers = new DummyTransceiver[](2); + (transceivers[0], transceivers[1]) = + TransceiverHelpersLib.addTransceiver(nttManagerOther, transceiverOther, chainId); + TransceiverStructs.NttManagerMessage memory m; - bytes memory encodedEm; - { - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](2); - transceivers[0] = e1; - transceivers[1] = e2; - - TransceiverStructs.TransceiverMessage memory em; - (m, em) = TransceiverHelpersLib.attestTransceiversHelper( - user_B, - 0, - chainId, - nttManager, - nttManagerOther, - transferAmount, - packTrimmedAmount(type(uint64).max, 8), - transceivers - ); - encodedEm = TransceiverStructs.encodeTransceiverMessage( - TransceiverHelpersLib.TEST_TRANSCEIVER_PAYLOAD_PREFIX, em - ); - } + DummyTransceiver.Message memory rmsg; + (m, rmsg) = TransceiverHelpersLib.transferAttestAndReceive( + user_B, + 0, + nttManager, + nttManagerOther, + transferAmount, + packTrimmedAmount(type(uint64).max, 8), + transceivers + ); - { - DummyToken token = DummyToken(nttManager.token()); - assertEq(token.balanceOf(address(user_B)), transferAmount.untrim(token.decimals())); - } + checkAttestationAndExecution(nttManagerOther, rmsg, 2); - // replay protection for transceiver - vm.recordLogs(); - vm.expectRevert( - abi.encodeWithSelector( - IManagerBase.TransceiverAlreadyAttestedToMessage.selector, - TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, m - ) - ) - ); - e2.receiveMessage(encodedEm); + // Replay protection should revert. + vm.expectRevert(abi.encodeWithSelector(Endpoint.DuplicateMessageAttestation.selector)); + transceivers[0].receiveMessage(rmsg); } function test_transfersOnForkedChains() public { uint256 evmChainId = block.chainid; - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); - uint8 decimals = token.decimals(); - nttManager.setPeer( - TransceiverHelpersLib.SENDING_CHAIN_ID, - toWormholeFormat(address(nttManagerOther)), - 9, - type(uint64).max - ); nttManager.setOutboundLimit(0); token.mintDummy(address(user_A), 5 * 10 ** decimals); @@ -738,13 +623,19 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { uint64 sequence = nttManager.transfer( 1 * 10 ** decimals, - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), - true, - new bytes(1) + true, // Should queue + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); + // We should have sent out message zero. + assertEq(sequence, 0); + require(1 == transceiver.getMessages().length, "Should have sent a message out"); + vm.warp(vm.getBlockTimestamp() + 1 days); vm.chainId(chainId); @@ -757,15 +648,21 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { vm.expectRevert(abi.encodeWithSelector(INttManager.NotImplemented.selector)); nttManager.cancelOutboundQueuedTransfer(sequence); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + // Outbound transfers fail when queued vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId)); nttManager.transfer( 1 * 10 ** decimals, - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), - true, - new bytes(1) + true, // Should queue + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); vm.stopPrank(); @@ -775,11 +672,13 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId)); nttManager.transfer( 1 * 10 ** decimals, - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); // INBOUND @@ -794,35 +693,42 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { }) ); - bytes memory transceiverMessage; - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - (nttManagerMessage, transceiverMessage) = TransceiverHelpersLib - .buildTransceiverMessageWithNttManagerPayload( - 0, - toWormholeFormat(address(0x1)), - toWormholeFormat(address(nttManagerOther)), - toWormholeFormat(address(nttManager)), - tokenTransferMessage + TransceiverStructs.NttManagerMessage memory m = TransceiverStructs.NttManagerMessage( + 0, toWormholeFormat(address(0x1)), tokenTransferMessage ); - - // Inbound transfers can't be completed + bytes memory nttManagerMessage = TransceiverStructs.encodeNttManagerMessage(m); + + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManagerOther)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + payloadHash: keccak256(nttManagerMessage), + refundAddr: address(user_A) + }); + + // The endpoint doesn't do fork detection so the attestation will succeed. + transceiver.receiveMessage(rmsg); + + // But the execute should fail. vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, evmChainId, chainId)); - dummyTransceiver.receiveMessage(transceiverMessage); + nttManager.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage); // Inbound queued transfers can't be completed, per usual - nttManager.setInboundLimit(0, TransceiverHelpersLib.SENDING_CHAIN_ID); + nttManager.setInboundLimit(0, chainId2); vm.chainId(evmChainId); - bytes32 hash = TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, nttManagerMessage - ); - dummyTransceiver.receiveMessage(transceiverMessage); + rmsg.sequence = 1; // Update the endpoint sequence number so we don't get duplicate attestation. + transceiver.receiveMessage(rmsg); + nttManager.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage); vm.chainId(chainId); vm.warp(vm.getBlockTimestamp() + 1 days); + bytes32 hash = TransceiverStructs.nttManagerMessageDigest(chainId2, m); vm.expectRevert(abi.encodeWithSelector(INttManager.NotImplemented.selector)); nttManager.completeInboundQueuedTransfer(hash); } @@ -839,8 +745,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { function test_noAutomaticSlot() public { DummyToken t = new DummyToken(); - MockNttManagerNoRateLimitingContract c = - new MockNttManagerNoRateLimitingContract(address(t), IManagerBase.Mode.LOCKING, 1); + MockNttManagerNoRateLimitingContract c = new MockNttManagerNoRateLimitingContract( + address(endpoint), address(executor), address(t), IManagerBase.Mode.LOCKING, 1 + ); assertEq(c.lastSlot(), 0x0); } @@ -849,7 +756,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { vm.startStateDiffRecording(); - new MockNttManagerNoRateLimitingContract(address(t), IManagerBase.Mode.LOCKING, 1); + new MockNttManagerNoRateLimitingContract( + address(endpoint), address(executor), address(t), IManagerBase.Mode.LOCKING, 1 + ); Utils.assertSafeUpgradeableConstructor(vm.stopAndReturnStateDiff()); } @@ -862,28 +771,40 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { address to = address(0x456); DummyToken token = DummyToken(nttManager.token()); - uint8 decimals = token.decimals(); uint256 maxAmount = 5 * 10 ** decimals; token.mintDummy(from, maxAmount); - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); + nttManager.setPeer( + chainId2, + toWormholeFormat(address(0x1)), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); nttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals)); nttManager.setInboundLimit( - packTrimmedAmount(type(uint64).max, 8).untrim(decimals), - TransceiverHelpersLib.SENDING_CHAIN_ID + packTrimmedAmount(type(uint64).max, 8).untrim(decimals), nttManagerOther.chainId() ); vm.startPrank(from); - uint256 transferAmount = 3 * 10 ** decimals; - assertEq( - transferAmount < maxAmount - 500, true, "Transferring more tokens than what exists" - ); + uint256 amountWithDust; + uint256 dustAmount; + { + uint256 transferAmount = 3 * 10 ** decimals; + assertEq( + transferAmount < maxAmount - 500, true, "Transferring more tokens than what exists" + ); - uint256 dustAmount = 500; - uint256 amountWithDust = transferAmount + dustAmount; // An amount with 19 digits, which will result in dust due to 18 decimals - token.approve(address(nttManager), amountWithDust); + dustAmount = 500; + amountWithDust = transferAmount + dustAmount; // An amount with 19 digits, which will result in dust due to 18 decimals + token.approve(address(nttManager), amountWithDust); + } + + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); vm.expectRevert( abi.encodeWithSelector( @@ -896,7 +817,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { toWormholeFormat(to), toWormholeFormat(from), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); vm.stopPrank(); @@ -924,48 +847,43 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { // transceivers) and receive a message through it. // This ensures that the storage slots don't get clobbered through the upgrades. - address user_B = address(0x456); DummyToken token = DummyToken(nttManager.token()); TrimmedAmount transferAmount = packTrimmedAmount(50, 8); - (ITransceiverReceiver e1, ITransceiverReceiver e2) = - TransceiverHelpersLib.setup_transceivers(nttManagerOther); // Step 1 (contract is deployed by setUp()) - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](2); - transceivers[0] = e1; - transceivers[1] = e2; + DummyTransceiver[] memory transceivers = new DummyTransceiver[](2); + (transceivers[0], transceivers[1]) = + TransceiverHelpersLib.addTransceiver(nttManagerOther, transceiverOther, chainId); TransceiverStructs.NttManagerMessage memory m; - bytes memory encodedEm; - { - TransceiverStructs.TransceiverMessage memory em; - (m, em) = TransceiverHelpersLib.attestTransceiversHelper( - user_B, - 0, - chainId, - nttManager, - nttManagerOther, - transferAmount, - packTrimmedAmount(type(uint64).max, 8), - transceivers - ); - encodedEm = TransceiverStructs.encodeTransceiverMessage( - TransceiverHelpersLib.TEST_TRANSCEIVER_PAYLOAD_PREFIX, em - ); - } + DummyTransceiver.Message memory rmsg; + (m, rmsg) = TransceiverHelpersLib.transferAttestAndReceive( + user_B, + 0, + nttManager, + nttManagerOther, + transferAmount, + packTrimmedAmount(type(uint64).max, 8), + transceivers + ); + checkAttestationAndExecution(nttManagerOther, rmsg, 2); assertEq(token.balanceOf(address(user_B)), transferAmount.untrim(token.decimals())); // Step 2 (upgrade to a new nttManager) MockNttManagerNoRateLimitingContract newNttManagerNoRateLimiting = new MockNttManagerNoRateLimitingContract( - nttManager.token(), IManagerBase.Mode.LOCKING, chainId + address(nttManagerOther.endpoint()), + address(nttManagerOther.executor()), + nttManagerOther.token(), + nttManagerOther.mode(), + nttManagerOther.chainId() ); + nttManagerOther.upgrade(address(newNttManagerNoRateLimiting)); - TransceiverHelpersLib.attestTransceiversHelper( + (m, rmsg) = TransceiverHelpersLib.transferAttestAndReceive( user_B, bytes32(uint256(1)), - chainId, nttManager, // this is the proxy nttManagerOther, // this is the proxy transferAmount, @@ -973,6 +891,7 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { transceivers ); + checkAttestationAndExecution(nttManagerOther, rmsg, 2); assertEq(token.balanceOf(address(user_B)), transferAmount.untrim(token.decimals()) * 2); } @@ -981,18 +900,32 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { function test_canUpgradeFromNoRateLimitingToRateLimitingDisabled() public { // Create a standard manager with rate limiting disabled. DummyToken t = new DummyToken(); - NttManager implementation = - new MockNttManagerContract(address(t), IManagerBase.Mode.LOCKING, chainId, 0, true); + NttManager implementation = new MockNttManagerContract( + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + chainId, + 0, + true + ); MockNttManagerContract thisNttManager = MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); thisNttManager.initialize(); - thisNttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); + thisNttManager.setPeer( + chainId2, + toWormholeFormat(address(0x1)), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); // Upgrade from NttManagerNoRateLimiting to NttManager with rate limiting enabled. This should work. - NttManager rateLimitingImplementation = - new MockNttManagerNoRateLimitingContract(address(t), IManagerBase.Mode.LOCKING, chainId); + NttManager rateLimitingImplementation = new MockNttManagerNoRateLimitingContract( + address(endpoint), address(executor), address(t), IManagerBase.Mode.LOCKING, chainId + ); thisNttManager.upgrade(address(rateLimitingImplementation)); } @@ -1001,18 +934,31 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { // Create a standard manager with rate limiting enabled. DummyToken t = new DummyToken(); NttManager implementation = new MockNttManagerContract( - address(t), IManagerBase.Mode.LOCKING, chainId, 1 days, false + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + chainId, + 1 days, + false ); MockNttManagerContract thisNttManager = MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); thisNttManager.initialize(); - thisNttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); + thisNttManager.setPeer( + chainId2, + toWormholeFormat(address(0x1)), + 9, + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); // Upgrade from NttManagerNoRateLimiting to NttManager with rate limiting enabled. The immutable check should panic. - NttManager rateLimitingImplementation = - new MockNttManagerNoRateLimitingContract(address(t), IManagerBase.Mode.LOCKING, chainId); + NttManager rateLimitingImplementation = new MockNttManagerNoRateLimitingContract( + address(endpoint), address(executor), address(t), IManagerBase.Mode.LOCKING, chainId + ); vm.expectRevert(); // Reverts with a panic on the assert. So, no way to tell WHY this happened. thisNttManager.upgrade(address(rateLimitingImplementation)); @@ -1025,8 +971,9 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { DummyTokenMintAndBurn t = DummyTokenMintAndBurn(address(new ERC1967Proxy(address(dummy1), ""))); - NttManagerNoRateLimiting implementation = - new MockNttManagerNoRateLimitingContract(address(t), IManagerBase.Mode.LOCKING, chainId); + NttManagerNoRateLimiting implementation = new MockNttManagerNoRateLimitingContract( + address(endpoint), address(executor), address(t), IManagerBase.Mode.LOCKING, chainId + ); MockNttManagerNoRateLimitingContract newNttManagerNoRateLimiting = MockNttManagerNoRateLimitingContract(address(new ERC1967Proxy(address(implementation), ""))); @@ -1035,15 +982,13 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { // register nttManager peer and transceiver bytes32 peer = toWormholeFormat(address(nttManager)); newNttManagerNoRateLimiting.setPeer( - TransceiverHelpersLib.SENDING_CHAIN_ID, peer, 9, type(uint64).max + chainId2, peer, 9, NttManagerHelpersLib.gasLimit, type(uint64).max ); - { - DummyTransceiver e = new DummyTransceiver(address(newNttManagerNoRateLimiting)); - newNttManagerNoRateLimiting.setTransceiver(address(e)); - } + DummyTransceiver e1 = new DummyTransceiver(chainId, address(endpoint)); + newNttManagerNoRateLimiting.setTransceiver(address(e1)); + newNttManagerNoRateLimiting.enableSendTransceiver(chainId2, address(e1)); + newNttManagerNoRateLimiting.enableRecvTransceiver(chainId2, address(e1)); - address user_A = address(0x123); - address user_B = address(0x456); t.mintDummy(address(user_A), 5 * 10 ** t.decimals()); // Check that we can initiate a transfer @@ -1051,20 +996,17 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { t.approve(address(newNttManagerNoRateLimiting), 3 * 10 ** t.decimals()); newNttManagerNoRateLimiting.transfer( 1 * 10 ** t.decimals(), - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); // Check that we can receive a transfer - (DummyTransceiver e1,) = - TransceiverHelpersLib.setup_transceivers(newNttManagerNoRateLimiting); - newNttManagerNoRateLimiting.setThreshold(1); - - bytes memory transceiverMessage; bytes memory tokenTransferMessage; TrimmedAmount transferAmount = packTrimmedAmount(100, 8); @@ -1079,15 +1021,27 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { }) ); - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - 0, - bytes32(0), - peer, - toWormholeFormat(address(newNttManagerNoRateLimiting)), - tokenTransferMessage + TransceiverStructs.NttManagerMessage memory m = TransceiverStructs.NttManagerMessage( + 0, toWormholeFormat(address(0x1)), tokenTransferMessage + ); + bytes memory nttManagerMessage = TransceiverStructs.encodeNttManagerMessage(m); + + DummyTransceiver.Message memory rmsg = DummyTransceiver.Message({ + srcChain: chainId2, + srcAddr: UniversalAddressLibrary.fromAddress(address(nttManager)), + sequence: 0, + dstChain: chainId, + dstAddr: UniversalAddressLibrary.fromAddress(address(newNttManagerNoRateLimiting)), + payloadHash: keccak256(nttManagerMessage), + refundAddr: address(user_A) + }); + + // The endpoint doesn't do fork detection so the attestation will succeed. + e1.receiveMessage(rmsg); + newNttManagerNoRateLimiting.executeMsg( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage ); - e1.receiveMessage(transceiverMessage); uint256 userBExpectedBalance = transferAmount.untrim(t.decimals()); assertEq(t.balanceOf(address(user_B)), userBExpectedBalance); @@ -1097,23 +1051,29 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { vm.startPrank(user_A); newNttManagerNoRateLimiting.transfer( - 1 * 10 ** 10, - TransceiverHelpersLib.SENDING_CHAIN_ID, + 1 * 10 ** t.decimals(), + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - bytes32("1"), - bytes32(0), - peer, - toWormholeFormat(address(newNttManagerNoRateLimiting)), - tokenTransferMessage + m = TransceiverStructs.NttManagerMessage( + bytes32("1"), toWormholeFormat(address(0x1)), tokenTransferMessage + ); + nttManagerMessage = TransceiverStructs.encodeNttManagerMessage(m); + + rmsg.sequence++; + rmsg.payloadHash = keccak256(nttManagerMessage); + e1.receiveMessage(rmsg); + newNttManagerNoRateLimiting.executeMsg( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage ); - e1.receiveMessage(transceiverMessage); + userBExpectedBalance = userBExpectedBalance + transferAmount.untrim(t.decimals()); assertEq(t.balanceOf(address(user_B)), userBExpectedBalance); @@ -1125,61 +1085,88 @@ contract TestNttManagerNoRateLimiting is Test, IRateLimiterEvents { vm.startPrank(user_A); newNttManagerNoRateLimiting.transfer( 1 * 10 ** 7, - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); - (, transceiverMessage) = TransceiverHelpersLib.buildTransceiverMessageWithNttManagerPayload( - bytes32("2"), - bytes32(0), - peer, - toWormholeFormat(address(newNttManagerNoRateLimiting)), - tokenTransferMessage + m = TransceiverStructs.NttManagerMessage( + bytes32("2"), toWormholeFormat(address(0x1)), tokenTransferMessage + ); + nttManagerMessage = TransceiverStructs.encodeNttManagerMessage(m); + + rmsg.sequence++; + rmsg.payloadHash = keccak256(nttManagerMessage); + e1.receiveMessage(rmsg); + newNttManagerNoRateLimiting.executeMsg( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, nttManagerMessage ); - e1.receiveMessage(transceiverMessage); userBExpectedBalance = userBExpectedBalance + transferAmount.untrim(t.decimals()); assertEq(t.balanceOf(address(user_B)), userBExpectedBalance); } - function test_transferWithInstructionIndexOutOfBounds() public { - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction = - TransceiverStructs.TransceiverInstruction({index: 100, payload: new bytes(1)}); - TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](1); - TransceiverInstructions[0] = TransceiverInstruction; - bytes memory encodedInstructions = - TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions); - - address user_A = address(0x123); - address user_B = address(0x456); - - DummyToken token = DummyToken(nttManager.token()); - - uint8 decimals = token.decimals(); - - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); - nttManager.setOutboundLimit(packTrimmedAmount(type(uint64).max, 8).untrim(decimals)); + function checkAttestationOnly( + NttManagerNoRateLimiting nttm, + DummyTransceiver.Message memory rmsg, + uint8 expectedAttestations, + uint8 transceiverIdx + ) public view { + // Verify that it shows as attested. + require( + expectedAttestations + == nttm.messageAttestations( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ), + "Message did not attest" + ); - token.mintDummy(address(user_A), 5 * 10 ** decimals); + // Verify that the right transceiver attested. + require( + nttm.transceiverAttestedToMessage( + rmsg.srcChain, + rmsg.srcAddr, + rmsg.sequence, + rmsg.dstAddr, + rmsg.payloadHash, + transceiverIdx + ), + "Transceiver did not attest to message" + ); - vm.startPrank(user_A); + // But the message should not be marked as executed. + require( + !nttm.isMessageExecuted( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ), + "Message should not be marked executed yet" + ); + } - token.approve(address(nttManager), 3 * 10 ** decimals); + error WrongNumberOfAttestations(uint8 expected, uint8 actual); - vm.expectRevert( - abi.encodeWithSelector(TransceiverStructs.InvalidInstructionIndex.selector, 100, 1) + function checkAttestationAndExecution( + NttManagerNoRateLimiting nttm, + DummyTransceiver.Message memory rmsg, + uint8 expectedAttestations + ) public view { + // Verify that it shows as attested. + uint8 actual = nttm.messageAttestations( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash ); - nttManager.transfer( - 1 * 10 ** decimals, - chainId2, - toWormholeFormat(user_B), - toWormholeFormat(user_A), - false, - encodedInstructions + if (actual != expectedAttestations) { + revert WrongNumberOfAttestations(expectedAttestations, actual); + } + + require( + nttManagerOther.isMessageExecuted( + rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, rmsg.dstAddr, rmsg.payloadHash + ), + "Message should be marked executed yet" ); } } diff --git a/evm/test/Ownership.t.sol b/evm/test/Ownership.t.sol index d2c0127dd..cb4a3e93f 100644 --- a/evm/test/Ownership.t.sol +++ b/evm/test/Ownership.t.sol @@ -8,33 +8,49 @@ import "../src/interfaces/IManagerBase.sol"; import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import {DummyTransceiver} from "./NttManager.t.sol"; import {DummyToken} from "./NttManager.t.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockExecutor.sol"; contract OwnershipTests is Test { + MockEndpoint endpoint; + MockExecutor executor; NttManager nttManager; uint16 constant chainId = 7; function setUp() public { DummyToken t = new DummyToken(); + endpoint = new MockEndpoint(chainId); + executor = new MockExecutor(chainId); + NttManager implementation = new MockNttManagerContract( - address(t), IManagerBase.Mode.LOCKING, chainId, 1 days, false + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + chainId, + 1 days, + false ); nttManager = MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); nttManager.initialize(); } - function checkOwnership(DummyTransceiver e, address nttManagerOwner) public { - address transceiverNttManager = e.getNttManagerOwner(); - assertEq(transceiverNttManager, nttManagerOwner); - } + function test_setUp() public {} - /// transceiver retrieves the nttManager owner correctly - function testTransceiverOwnership() public { - // TODO: use setup_transceivers here - DummyTransceiver e1 = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e1)); - nttManager.setThreshold(1); + // The transceiver is no longer owned by the NttManager. It's owned by the endpoint. Nothing to test here. + // function checkOwnership(DummyTransceiver e, address nttManagerOwner) public { + // address transceiverNttManager = e.getNttManagerOwner(); + // assertEq(transceiverNttManager, nttManagerOwner); + // } - checkOwnership(e1, nttManager.owner()); - } + // /// transceiver retrieves the nttManager owner correctly + // function testTransceiverOwnership() public { + // // TODO: use setup_transceivers here + // DummyTransceiver e1 = new DummyTransceiver(address(nttManager)); + // nttManager.setTransceiver(address(e1)); + // nttManager.setThreshold(1); + + // checkOwnership(e1, nttManager.owner()); + // } } diff --git a/evm/test/RateLimit.t.sol b/evm/test/RateLimit.t.sol index 159246e20..7c67dc54c 100644 --- a/evm/test/RateLimit.t.sol +++ b/evm/test/RateLimit.t.sol @@ -12,11 +12,22 @@ import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; import "./libraries/TransceiverHelpers.sol"; import "./libraries/NttManagerHelpers.sol"; import "wormhole-solidity-sdk/libraries/BytesParsing.sol"; +import "./mocks/MockNttManager.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockExecutor.sol"; +import "./mocks/DummyTransceiver.sol"; pragma solidity >=0.8.8 <0.9.0; contract TestRateLimit is Test, IRateLimiterEvents { + MockEndpoint endpoint; + MockEndpoint endpointOther; + MockExecutor executor; + MockExecutor executorOther; MockNttManagerContract nttManager; + MockNttManagerContract nttManagerOther; + DummyTransceiver transceiver; + DummyTransceiver transceiverOther; using TrimmedAmountLib for uint256; using TrimmedAmountLib for TrimmedAmount; @@ -25,6 +36,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { uint16 constant chainId = 7; uint16 constant chainId2 = 8; + address user_A = address(0x123); + address user_B = address(0x456); + uint256 constant DEVNET_GUARDIAN_PK = 0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0; WormholeSimulator guardian; @@ -38,20 +52,69 @@ contract TestRateLimit is Test, IRateLimiterEvents { guardian = new WormholeSimulator(address(wormhole), DEVNET_GUARDIAN_PK); + endpoint = new MockEndpoint(chainId); + endpointOther = new MockEndpoint(chainId2); + + executor = new MockExecutor(chainId); + executorOther = new MockExecutor(chainId2); + DummyToken t = new DummyToken(); NttManager implementation = new MockNttManagerContract( - address(t), IManagerBase.Mode.LOCKING, chainId, 1 days, false + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + chainId, + 1 days, + false + ); + + NttManager implementationOther = new MockNttManagerContract( + address(endpointOther), + address(executorOther), + address(t), + IManagerBase.Mode.LOCKING, + chainId2, + 1 days, + false ); nttManager = MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); nttManager.initialize(); - nttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); + nttManagerOther = + MockNttManagerContract(address(new ERC1967Proxy(address(implementationOther), ""))); + nttManagerOther.initialize(); + + nttManager.setPeer( + chainId2, + toWormholeFormat(address(nttManagerOther)), + t.decimals(), + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); + + nttManagerOther.setPeer( + chainId, + toWormholeFormat(address(nttManager)), + t.decimals(), + NttManagerHelpersLib.gasLimit, + type(uint64).max + ); + + transceiver = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(transceiver)); + nttManager.enableSendTransceiver(chainId2, address(transceiver)); + nttManager.enableRecvTransceiver(chainId2, address(transceiver)); - DummyTransceiver e = new DummyTransceiver(address(nttManager)); - nttManager.setTransceiver(address(e)); + transceiverOther = new DummyTransceiver(chainId2, address(endpointOther)); + nttManagerOther.setTransceiver(address(transceiverOther)); + nttManagerOther.enableSendTransceiver(chainId, address(transceiverOther)); + nttManagerOther.enableRecvTransceiver(chainId, address(transceiverOther)); } + function test_setUp() public {} + function test_outboundRateLimit_setLimitSimple() public { DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -72,9 +135,6 @@ contract TestRateLimit is Test, IRateLimiterEvents { function test_outboundRateLimit() public { // transfer 3 tokens - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -93,7 +153,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -119,9 +181,6 @@ contract TestRateLimit is Test, IRateLimiterEvents { function test_outboundRateLimit_setHigherLimit() public { // transfer 3 tokens - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -140,7 +199,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -166,9 +227,6 @@ contract TestRateLimit is Test, IRateLimiterEvents { function test_outboundRateLimit_setLowerLimit() public { // transfer 3 tokens - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -187,7 +245,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -208,9 +268,6 @@ contract TestRateLimit is Test, IRateLimiterEvents { function test_outboundRateLimit_setHigherLimit_duration() public { // transfer 3 tokens - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -229,7 +286,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -264,9 +323,6 @@ contract TestRateLimit is Test, IRateLimiterEvents { function test_outboundRateLimit_setLowerLimit_durationCaseOne() public { // transfer 3 tokens - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -285,7 +341,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -312,9 +370,6 @@ contract TestRateLimit is Test, IRateLimiterEvents { } function test_outboundRateLimit_setLowerLimit_durationCaseTwo() public { - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -335,7 +390,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -369,9 +426,6 @@ contract TestRateLimit is Test, IRateLimiterEvents { } function test_outboundRateLimit_singleHit() public { - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -385,6 +439,10 @@ contract TestRateLimit is Test, IRateLimiterEvents { uint256 transferAmount = 3 * 10 ** decimals; token.approve(address(nttManager), transferAmount); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + vm.expectRevert( abi.encodeWithSelector( IRateLimiter.NotEnoughCapacity.selector, outboundLimit, transferAmount @@ -396,14 +454,13 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); } function test_outboundRateLimit_multiHit() public { - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -422,7 +479,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); // assert that first transfer went through @@ -437,6 +496,10 @@ contract TestRateLimit is Test, IRateLimiterEvents { uint256 badTransferAmount = 2 * 10 ** decimals; token.approve(address(nttManager), badTransferAmount); + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + vm.expectRevert( abi.encodeWithSelector( IRateLimiter.NotEnoughCapacity.selector, @@ -450,7 +513,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); } @@ -460,9 +525,6 @@ contract TestRateLimit is Test, IRateLimiterEvents { // test that it exits queue after >= rateLimitDuration // test that it's removed from queue and can't be replayed function test_outboundRateLimit_queue() public { - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -483,7 +545,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), true, - new bytes(1) + executor.createSignedQuote(executorOther.chainId(), 2 days), // We are going to warp the time below. + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); // assert that the transfer got queued up @@ -523,21 +587,17 @@ contract TestRateLimit is Test, IRateLimiterEvents { } function test_inboundRateLimit_simple() public { - address user_B = address(0x456); - - (DummyTransceiver e1, DummyTransceiver e2) = - TransceiverHelpersLib.setup_transceivers(nttManager); + DummyTransceiver[] memory transceiversOther = new DummyTransceiver[](2); + (transceiversOther[0], transceiversOther[1]) = + TransceiverHelpersLib.addTransceiver(nttManagerOther, transceiverOther, chainId); DummyToken token = DummyToken(nttManager.token()); - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](2); - transceivers[0] = e1; - transceivers[1] = e2; - TrimmedAmount transferAmount = packTrimmedAmount(50, 8); TrimmedAmount limitAmount = packTrimmedAmount(100, 8); - TransceiverHelpersLib.attestTransceiversHelper( - user_B, 0, chainId, nttManager, nttManager, transferAmount, limitAmount, transceivers + + TransceiverHelpersLib.transferAttestAndReceive( + user_B, 0, nttManager, nttManagerOther, transferAmount, limitAmount, transceiversOther ); // assert that the user received tokens @@ -545,7 +605,7 @@ contract TestRateLimit is Test, IRateLimiterEvents { // assert that the inbound limits updated IRateLimiter.RateLimitParams memory inboundLimitParams = - nttManager.getInboundLimitParams(TransceiverHelpersLib.SENDING_CHAIN_ID); + nttManagerOther.getInboundLimitParams(chainId); assertEq( inboundLimitParams.currentCapacity.getAmount(), (limitAmount - (transferAmount)).getAmount() @@ -563,49 +623,39 @@ contract TestRateLimit is Test, IRateLimiterEvents { } function test_inboundRateLimit_queue() public { - address user_B = address(0x456); - - (DummyTransceiver e1, DummyTransceiver e2) = - TransceiverHelpersLib.setup_transceivers(nttManager); - DummyToken token = DummyToken(nttManager.token()); - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](1); - transceivers[0] = e1; - - TransceiverStructs.NttManagerMessage memory m; - bytes memory encodedEm; - { - TransceiverStructs.TransceiverMessage memory em; - (m, em) = TransceiverHelpersLib.attestTransceiversHelper( - user_B, - 0, - chainId, - nttManager, - nttManager, - packTrimmedAmount(50, 8), - uint256(5).trim(token.decimals(), token.decimals()), - transceivers - ); - encodedEm = TransceiverStructs.encodeTransceiverMessage( - TransceiverHelpersLib.TEST_TRANSCEIVER_PAYLOAD_PREFIX, em - ); - } + DummyTransceiver[] memory transceivers = new DummyTransceiver[](2); + (transceivers[0], transceivers[1]) = + TransceiverHelpersLib.addTransceiver(nttManagerOther, transceiverOther, chainId); + + ( + TransceiverStructs.NttManagerMessage memory m, + bytes memory encodedM, + DummyTransceiver.Message memory rmsg + ) = TransceiverHelpersLib.transferAndAttest( + user_B, + 0, + nttManager, + nttManagerOther, + packTrimmedAmount(50, 8), + uint256(5).trim(token.decimals(), token.decimals()), + transceivers + ); - bytes32 digest = - TransceiverStructs.nttManagerMessageDigest(TransceiverHelpersLib.SENDING_CHAIN_ID, m); + bytes32 digest = TransceiverStructs.nttManagerMessageDigest(chainId, m); - // no quorum yet + // Haven't executed yet. assertEq(token.balanceOf(address(user_B)), 0); - vm.expectEmit(address(nttManager)); + vm.expectEmit(address(nttManagerOther)); emit InboundTransferQueued(digest); - e2.receiveMessage(encodedEm); + nttManagerOther.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, encodedM); { // now we have quorum but it'll hit limit IRateLimiter.InboundQueuedTransfer memory qt = - nttManager.getInboundQueuedTransfer(digest); + nttManagerOther.getInboundQueuedTransfer(digest); assertEq(qt.amount.getAmount(), 50); assertEq(qt.txTimestamp, initialBlockTimestamp); assertEq(qt.recipient, user_B); @@ -615,7 +665,7 @@ contract TestRateLimit is Test, IRateLimiterEvents { assertEq(token.balanceOf(address(user_B)), 0); // change block time to (duration - 1) seconds later - uint256 durationElapsedTime = initialBlockTimestamp + nttManager.rateLimitDuration(); + uint256 durationElapsedTime = initialBlockTimestamp + nttManagerOther.rateLimitDuration(); vm.warp(durationElapsedTime - 1); { @@ -627,49 +677,26 @@ contract TestRateLimit is Test, IRateLimiterEvents { initialBlockTimestamp ) ); - nttManager.completeInboundQueuedTransfer(digest); + nttManagerOther.completeInboundQueuedTransfer(digest); } // now complete transfer vm.warp(durationElapsedTime); - nttManager.completeInboundQueuedTransfer(digest); + nttManagerOther.completeInboundQueuedTransfer(digest); { // assert transfer no longer in queue vm.expectRevert( abi.encodeWithSelector(IRateLimiter.InboundQueuedTransferNotFound.selector, digest) ); - nttManager.completeInboundQueuedTransfer(digest); + nttManagerOther.completeInboundQueuedTransfer(digest); } // assert user now has funds assertEq(token.balanceOf(address(user_B)), 50 * 10 ** (token.decimals() - 8)); - - // replay protection on executeMsg - vm.recordLogs(); - nttManager.executeMsg( - TransceiverHelpersLib.SENDING_CHAIN_ID, toWormholeFormat(address(nttManager)), m - ); - - { - Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 1); - assertEq(entries[0].topics.length, 3); - assertEq(entries[0].topics[0], keccak256("MessageAlreadyExecuted(bytes32,bytes32)")); - assertEq(entries[0].topics[1], toWormholeFormat(address(nttManager))); - assertEq( - entries[0].topics[2], - TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, m - ) - ); - } } function test_circular_flow() public { - address user_A = address(0x123); - address user_B = address(0x456); - DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -691,7 +718,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -717,15 +746,13 @@ contract TestRateLimit is Test, IRateLimiterEvents { vm.warp(receiveTime); // now receive 10 tokens from user_B -> user_A - (DummyTransceiver e1, DummyTransceiver e2) = - TransceiverHelpersLib.setup_transceivers(nttManager); - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](2); - transceivers[0] = e1; - transceivers[1] = e2; + DummyTransceiver[] memory transceivers = new DummyTransceiver[](2); + (transceivers[0], transceivers[1]) = + TransceiverHelpersLib.addTransceiver(nttManager, transceiver, chainId2); - TransceiverHelpersLib.attestTransceiversHelper( - user_A, 0, chainId, nttManager, nttManager, transferAmount, mintAmount, transceivers + TransceiverHelpersLib.transferAttestAndReceive( + user_A, 0, nttManagerOther, nttManager, transferAmount, mintAmount, transceivers ); // assert that user_A has original amount @@ -735,7 +762,7 @@ contract TestRateLimit is Test, IRateLimiterEvents { // consume capacity on the inbound side // assert that the inbound capacity decreased IRateLimiter.RateLimitParams memory inboundLimitParams = - nttManager.getInboundLimitParams(TransceiverHelpersLib.SENDING_CHAIN_ID); + nttManager.getInboundLimitParams(chainId2); assertEq( inboundLimitParams.currentCapacity.getAmount(), (inboundLimitParams.limit - transferAmount).getAmount() @@ -766,11 +793,13 @@ contract TestRateLimit is Test, IRateLimiterEvents { nttManager.transfer( transferAmount.untrim(decimals), - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(userA), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -789,7 +818,7 @@ contract TestRateLimit is Test, IRateLimiterEvents { { // assert that the inbound limit is at max again (because of backflow) IRateLimiter.RateLimitParams memory inboundLimitParams = - nttManager.getInboundLimitParams(TransceiverHelpersLib.SENDING_CHAIN_ID); + nttManager.getInboundLimitParams(chainId2); assertEq( inboundLimitParams.currentCapacity.getAmount(), inboundLimitParams.limit.getAmount() ); @@ -798,26 +827,19 @@ contract TestRateLimit is Test, IRateLimiterEvents { } // helper functions - function setupToken() public returns (address, address, DummyToken, uint8) { - address user_A = address(0x123); - address user_B = address(0x456); - + function setupToken() public returns (DummyToken, uint8) { DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); assertEq(decimals, 18); - return (user_A, user_B, token, decimals); + return (token, decimals); } - function initializeTransceivers() public returns (ITransceiverReceiver[] memory) { - (DummyTransceiver e1, DummyTransceiver e2) = - TransceiverHelpersLib.setup_transceivers(nttManager); - - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](2); - transceivers[0] = e1; - transceivers[1] = e2; - + function initializeTransceivers() public returns (DummyTransceiver[] memory) { + DummyTransceiver[] memory transceivers = new DummyTransceiver[](2); + (transceivers[0], transceivers[1]) = + TransceiverHelpersLib.addTransceiver(nttManager, transceiver, chainId2); return transceivers; } @@ -849,7 +871,7 @@ contract TestRateLimit is Test, IRateLimiterEvents { // enforces transferAmt <= mintAmt transferAmt = bound(transferAmt, 0, mintAmt); - (address user_A, address user_B, DummyToken token, uint8 decimals) = setupToken(); + (DummyToken token, uint8 decimals) = setupToken(); // allow for amounts greater than uint64 to check if [`setOutboundLimit`] reverts // on amounts greater than u64 MAX. @@ -873,6 +895,10 @@ contract TestRateLimit is Test, IRateLimiterEvents { // check error conditions // revert if amount to be transferred is 0 if (transferAmount.getAmount() == 0) { + bytes memory executorSignedQuote = executor.createSignedQuote(executorOther.chainId()); + bytes memory executorRelayInstructions = executor.createRelayInstructions(); + bytes memory adapterInstructions = endpoint.createAdapterInstructions(); + vm.expectRevert(abi.encodeWithSelector(INttManager.ZeroAmount.selector)); nttManager.transfer( transferAmount.untrim(decimals), @@ -880,7 +906,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executorSignedQuote, + executorRelayInstructions, + adapterInstructions ); return; @@ -893,7 +921,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -918,10 +948,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { uint256 receiveTime = initialBlockTimestamp + 1; vm.warp(receiveTime); - ITransceiverReceiver[] memory transceivers = initializeTransceivers(); - // now receive tokens from user_B -> user_A - TransceiverHelpersLib.attestTransceiversHelper( - user_A, 0, chainId, nttManager, nttManager, transferAmount, mintAmount, transceivers + DummyTransceiver[] memory transceivers = initializeTransceivers(); + TransceiverHelpersLib.transferAttestAndReceive( + user_A, 0, nttManagerOther, nttManager, transferAmount, mintAmount, transceivers ); // assert that user_A has original amount @@ -931,7 +960,7 @@ contract TestRateLimit is Test, IRateLimiterEvents { // consume capacity on the inbound side // assert that the inbound capacity decreased IRateLimiter.RateLimitParams memory inboundLimitParams = - nttManager.getInboundLimitParams(TransceiverHelpersLib.SENDING_CHAIN_ID); + nttManager.getInboundLimitParams(chainId2); assertEq( inboundLimitParams.currentCapacity.getAmount(), (inboundLimitParams.limit - transferAmount).getAmount() @@ -959,11 +988,13 @@ contract TestRateLimit is Test, IRateLimiterEvents { nttManager.transfer( transferAmount.untrim(decimals), - TransceiverHelpersLib.SENDING_CHAIN_ID, + chainId2, toWormholeFormat(user_B), toWormholeFormat(user_A), false, - new bytes(1) + executor.createSignedQuote(executorOther.chainId()), + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); vm.stopPrank(); @@ -982,7 +1013,7 @@ contract TestRateLimit is Test, IRateLimiterEvents { { // assert that the inbound limit is at max again (because of backflow) IRateLimiter.RateLimitParams memory inboundLimitParams = - nttManager.getInboundLimitParams(TransceiverHelpersLib.SENDING_CHAIN_ID); + nttManager.getInboundLimitParams(chainId2); assertEq( inboundLimitParams.currentCapacity.getAmount(), inboundLimitParams.limit.getAmount() ); @@ -992,8 +1023,6 @@ contract TestRateLimit is Test, IRateLimiterEvents { function testFuzz_outboundRateLimitShouldQueue(uint256 limitAmt, uint256 transferAmt) public { // setup - address user_A = address(0x123); - address user_B = address(0x456); DummyToken token = DummyToken(nttManager.token()); uint8 decimals = token.decimals(); @@ -1023,7 +1052,9 @@ contract TestRateLimit is Test, IRateLimiterEvents { toWormholeFormat(user_B), toWormholeFormat(user_A), true, - new bytes(1) + executor.createSignedQuote(executorOther.chainId(), 2 days), // We are going to warp the time below. + executor.createRelayInstructions(), + endpoint.createAdapterInstructions() ); // assert that the transfer got queued up @@ -1065,52 +1096,60 @@ contract TestRateLimit is Test, IRateLimiterEvents { function testFuzz_inboundRateLimitShouldQueue(uint256 inboundLimitAmt, uint256 amount) public { amount = bound(amount, 1, type(uint64).max); inboundLimitAmt = bound(amount, 0, amount - 1); - - address user_B = address(0x456); - - (DummyTransceiver e1, DummyTransceiver e2) = - TransceiverHelpersLib.setup_transceivers(nttManager); - DummyToken token = DummyToken(nttManager.token()); - ITransceiverReceiver[] memory transceivers = new ITransceiverReceiver[](1); - transceivers[0] = e1; + DummyTransceiver[] memory transceivers = new DummyTransceiver[](2); + (transceivers[0], transceivers[1]) = + TransceiverHelpersLib.addTransceiver(nttManagerOther, transceiverOther, chainId); - TransceiverStructs.NttManagerMessage memory m; - bytes memory encodedEm; + // TransceiverStructs.NttManagerMessage memory m; + // bytes memory encodedEm; uint256 inboundLimit = inboundLimitAmt; TrimmedAmount trimmedAmount = packTrimmedAmount(uint64(amount), 8); - { - TransceiverStructs.TransceiverMessage memory em; - (m, em) = TransceiverHelpersLib.attestTransceiversHelper( - user_B, - 0, - chainId, - nttManager, - nttManager, - trimmedAmount, - inboundLimit.trim(token.decimals(), token.decimals()), - transceivers - ); - encodedEm = TransceiverStructs.encodeTransceiverMessage( - TransceiverHelpersLib.TEST_TRANSCEIVER_PAYLOAD_PREFIX, em - ); - } + // { + // TransceiverStructs.TransceiverMessage memory em; + // (m, em) = TransceiverHelpersLib.attestTransceiversHelper( + // user_B, + // 0, + // chainId, + // nttManager, + // nttManager, + // trimmedAmount, + // inboundLimit.trim(token.decimals(), token.decimals()), + // transceivers + // ); + // encodedEm = TransceiverStructs.encodeTransceiverMessage( + // TransceiverHelpersLib.TEST_TRANSCEIVER_PAYLOAD_PREFIX, em + // ); + // } + + ( + TransceiverStructs.NttManagerMessage memory m, + bytes memory encodedM, + DummyTransceiver.Message memory rmsg + ) = TransceiverHelpersLib.transferAndAttest( + user_B, + 0, + nttManager, + nttManagerOther, + trimmedAmount, + inboundLimit.trim(token.decimals(), token.decimals()), + transceivers + ); - bytes32 digest = - TransceiverStructs.nttManagerMessageDigest(TransceiverHelpersLib.SENDING_CHAIN_ID, m); + bytes32 digest = TransceiverStructs.nttManagerMessageDigest(chainId, m); - // no quorum yet + // Haven't executed yet. assertEq(token.balanceOf(address(user_B)), 0); - vm.expectEmit(address(nttManager)); + vm.expectEmit(address(nttManagerOther)); emit InboundTransferQueued(digest); - e2.receiveMessage(encodedEm); + nttManagerOther.executeMsg(rmsg.srcChain, rmsg.srcAddr, rmsg.sequence, encodedM); { // now we have quorum but it'll hit limit IRateLimiter.InboundQueuedTransfer memory qt = - nttManager.getInboundQueuedTransfer(digest); + nttManagerOther.getInboundQueuedTransfer(digest); assertEq(qt.amount.getAmount(), trimmedAmount.getAmount()); assertEq(qt.txTimestamp, initialBlockTimestamp); assertEq(qt.recipient, user_B); @@ -1120,7 +1159,7 @@ contract TestRateLimit is Test, IRateLimiterEvents { assertEq(token.balanceOf(address(user_B)), 0); // change block time to (duration - 1) seconds later - uint256 durationElapsedTime = initialBlockTimestamp + nttManager.rateLimitDuration(); + uint256 durationElapsedTime = initialBlockTimestamp + nttManagerOther.rateLimitDuration(); vm.warp(durationElapsedTime - 1); { @@ -1132,19 +1171,19 @@ contract TestRateLimit is Test, IRateLimiterEvents { initialBlockTimestamp ) ); - nttManager.completeInboundQueuedTransfer(digest); + nttManagerOther.completeInboundQueuedTransfer(digest); } // now complete transfer vm.warp(durationElapsedTime); - nttManager.completeInboundQueuedTransfer(digest); + nttManagerOther.completeInboundQueuedTransfer(digest); { // assert transfer no longer in queue vm.expectRevert( abi.encodeWithSelector(IRateLimiter.InboundQueuedTransferNotFound.selector, digest) ); - nttManager.completeInboundQueuedTransfer(digest); + nttManagerOther.completeInboundQueuedTransfer(digest); } // assert user now has funds @@ -1152,25 +1191,5 @@ contract TestRateLimit is Test, IRateLimiterEvents { token.balanceOf(address(user_B)), trimmedAmount.getAmount() * 10 ** (token.decimals() - 8) ); - - // replay protection on executeMsg - vm.recordLogs(); - nttManager.executeMsg( - TransceiverHelpersLib.SENDING_CHAIN_ID, toWormholeFormat(address(nttManager)), m - ); - - { - Vm.Log[] memory entries = vm.getRecordedLogs(); - assertEq(entries.length, 1); - assertEq(entries[0].topics.length, 3); - assertEq(entries[0].topics[0], keccak256("MessageAlreadyExecuted(bytes32,bytes32)")); - assertEq(entries[0].topics[1], toWormholeFormat(address(nttManager))); - assertEq( - entries[0].topics[2], - TransceiverStructs.nttManagerMessageDigest( - TransceiverHelpersLib.SENDING_CHAIN_ID, m - ) - ); - } } } diff --git a/evm/test/Threshold.t.sol b/evm/test/Threshold.t.sol new file mode 100644 index 000000000..aab888620 --- /dev/null +++ b/evm/test/Threshold.t.sol @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity >=0.8.8 <0.9.0; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; + +import "../src/NttManager/NttManager.sol"; +import "../src/interfaces/INttManager.sol"; +import "../src/interfaces/IManagerBase.sol"; +import "../src/libraries/external/OwnableUpgradeable.sol"; + +import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; +import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; +import "./mocks/DummyTransceiver.sol"; +import "../src/mocks/DummyToken.sol"; +import "./mocks/MockNttManager.sol"; +import "./mocks/MockEndpoint.sol"; +import "./mocks/MockExecutor.sol"; + +contract TestThreshold is Test { + MockNttManagerContract nttManager; + MockEndpoint endpoint; + MockExecutor executor; + + uint16 constant chainId = 7; + uint16 constant chainId2 = 8; + uint16 constant chainId3 = 9; + + function setUp() public { + endpoint = new MockEndpoint(chainId); + endpoint = new MockEndpoint(chainId); + + DummyToken t = new DummyToken(); + NttManager implementation = new MockNttManagerContract( + address(endpoint), + address(executor), + address(t), + IManagerBase.Mode.LOCKING, + chainId, + 1 days, + false + ); + + nttManager = MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); + nttManager.initialize(); + } + + function test_setUp() public {} + + function test_canSetThreshold() public { + DummyTransceiver e1 = new DummyTransceiver(chainId, address(endpoint)); + DummyTransceiver e2 = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e1)); + nttManager.setTransceiver(address(e2)); + + nttManager.enableRecvTransceiver(chainId2, address(e1)); + nttManager.enableRecvTransceiver(chainId2, address(e2)); + + nttManager.setThreshold(chainId2, 1); + nttManager.setThreshold(chainId2, 2); + nttManager.setThreshold(chainId2, 1); + } + + function test_cantSetThresholdToZero() public { + DummyTransceiver e = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e)); + nttManager.enableRecvTransceiver(chainId2, address(e)); + vm.expectRevert(abi.encodeWithSelector(IManagerBase.ZeroThreshold.selector)); + nttManager.setThreshold(chainId2, 0); + } + + function test_cantSetThresholdTooHigh() public { + // With one transceiver, can't set the threshold to two. + DummyTransceiver e1 = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e1)); + nttManager.enableRecvTransceiver(chainId2, address(e1)); + vm.expectRevert(abi.encodeWithSelector(IManagerBase.ThresholdTooHigh.selector, 2, 1)); + nttManager.setThreshold(chainId2, 2); + } + + function test_onlyOwnerCanSetThreshold() public { + address notOwner = address(0x123); + vm.startPrank(notOwner); + + vm.expectRevert( + abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, notOwner) + ); + nttManager.setThreshold(chainId2, 1); + } + + function test_thresholdGetsSetOnFirstEnable() public { + // The threshold starts at zero. + assertEq(0, nttManager.getThreshold(chainId2)); + + // When we enable the first transceiver, it should go to one. + DummyTransceiver e1 = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e1)); + nttManager.enableRecvTransceiver(chainId2, address(e1)); + assertEq(1, nttManager.getThreshold(chainId2)); + + // But it should not increase when we enable more. + DummyTransceiver e2 = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e2)); + nttManager.enableRecvTransceiver(chainId2, address(e2)); + assertEq(1, nttManager.getThreshold(chainId2)); + } + + function test_thresholdReducesOnDisable() public { + //Create and enable a few transceivers on a couple of chains. + DummyTransceiver e1 = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e1)); + nttManager.enableRecvTransceiver(chainId2, address(e1)); + + DummyTransceiver e2 = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e2)); + nttManager.enableRecvTransceiver(chainId2, address(e2)); + + DummyTransceiver e3 = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e3)); + nttManager.enableRecvTransceiver(chainId2, address(e3)); + + DummyTransceiver e4 = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e4)); + nttManager.enableRecvTransceiver(chainId3, address(e4)); + + DummyTransceiver e5 = new DummyTransceiver(chainId, address(endpoint)); + nttManager.setTransceiver(address(e5)); + nttManager.enableRecvTransceiver(chainId3, address(e5)); + + // The thresholds should have been set to one automatically. + assertEq(1, nttManager.getThreshold(chainId2)); + assertEq(1, nttManager.getThreshold(chainId3)); + + // Bump the thresholds up. + nttManager.setThreshold(chainId2, 3); + nttManager.setThreshold(chainId3, 2); + assertEq(3, nttManager.getThreshold(chainId2)); + assertEq(2, nttManager.getThreshold(chainId3)); + + // Disabling should reduce the threshold if necessary. + nttManager.disableRecvTransceiver(chainId2, address(e3)); + assertEq(2, nttManager.getThreshold(chainId2)); + assertEq(2, nttManager.getThreshold(chainId3)); + + // But disabling should not reduce the threshold if it's not necessary. + nttManager.setThreshold(chainId2, 1); + assertEq(1, nttManager.getThreshold(chainId2)); + nttManager.disableRecvTransceiver(chainId2, address(e2)); + assertEq(1, nttManager.getThreshold(chainId2)); + assertEq(2, nttManager.getThreshold(chainId3)); + + // Threshold should go to zero when we disable the last one. + nttManager.disableRecvTransceiver(chainId2, address(e1)); + assertEq(0, nttManager.getThreshold(chainId2)); + assertEq(2, nttManager.getThreshold(chainId3)); + } + + function test_chainsEnabledForReceive() public { + uint16[] memory chains = nttManager.getChainsEnabledForReceive(); + assertEq(0, chains.length); + + nttManager.addChainEnabledForReceive(42); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(1, chains.length); + assertEq(42, chains[0]); + + nttManager.addChainEnabledForReceive(41); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(2, chains.length); + assertEq(42, chains[0]); + assertEq(41, chains[1]); + + nttManager.addChainEnabledForReceive(43); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(3, chains.length); + assertEq(42, chains[0]); + assertEq(41, chains[1]); + assertEq(43, chains[2]); + + // Adding the same thing again shouldn't do anything. + nttManager.addChainEnabledForReceive(43); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(3, chains.length); + assertEq(42, chains[0]); + assertEq(41, chains[1]); + assertEq(43, chains[2]); + + nttManager.addChainEnabledForReceive(41); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(3, chains.length); + assertEq(42, chains[0]); + assertEq(41, chains[1]); + assertEq(43, chains[2]); + + // Add one more. + nttManager.addChainEnabledForReceive(44); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(4, chains.length); + assertEq(42, chains[0]); + assertEq(41, chains[1]); + assertEq(43, chains[2]); + assertEq(44, chains[3]); + + // Now test removing. + + // Remove one from the middle. The last one should get moved into its slot. + nttManager.removeChainEnabledForReceive(41); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(3, chains.length); + assertEq(42, chains[0]); + assertEq(44, chains[1]); + assertEq(43, chains[2]); + + // Removing something not in the list shouldn't do anything. + nttManager.removeChainEnabledForReceive(410); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(3, chains.length); + assertEq(42, chains[0]); + assertEq(44, chains[1]); + assertEq(43, chains[2]); + + // Remove the first one. The last one should get moved into its slot. + nttManager.removeChainEnabledForReceive(42); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(2, chains.length); + assertEq(43, chains[0]); + assertEq(44, chains[1]); + + // Remove the last one. + nttManager.removeChainEnabledForReceive(44); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(1, chains.length); + assertEq(43, chains[0]); + + // Remove the only one. + nttManager.removeChainEnabledForReceive(43); + chains = nttManager.getChainsEnabledForReceive(); + assertEq(0, chains.length); + } +} diff --git a/evm/test/TransceiverStructs.t.sol b/evm/test/TransceiverStructs.t.sol index 178871522..b71524736 100644 --- a/evm/test/TransceiverStructs.t.sol +++ b/evm/test/TransceiverStructs.t.sol @@ -5,7 +5,6 @@ import "forge-std/Test.sol"; import "../src/libraries/TransceiverStructs.sol"; import "../src/interfaces/IManagerBase.sol"; -import "../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; import "../src/interfaces/INttManager.sol"; contract TestTransceiverStructs is Test { diff --git a/evm/test/Upgrades.t.sol b/evm/test/Upgrades.t.sol deleted file mode 100644 index 6819ee279..000000000 --- a/evm/test/Upgrades.t.sol +++ /dev/null @@ -1,710 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity >=0.8.8 <0.9.0; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; - -import "../src/NttManager/NttManager.sol"; -import "../src/interfaces/INttManager.sol"; -import "../src/interfaces/IManagerBase.sol"; -import "../src/interfaces/IRateLimiter.sol"; -import "../src/interfaces/IRateLimiterEvents.sol"; -import "../src/libraries/external/OwnableUpgradeable.sol"; -import "../src/libraries/external/Initializable.sol"; -import "../src/libraries/Implementation.sol"; -import {Utils} from "./libraries/Utils.sol"; -import {DummyToken, DummyTokenMintAndBurn} from "./NttManager.t.sol"; -import {WormholeTransceiver} from "../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; -import "../src/libraries/TransceiverStructs.sol"; -import "./mocks/MockNttManager.sol"; -import "./mocks/MockTransceivers.sol"; - -import "openzeppelin-contracts/contracts/token/ERC20/ERC20.sol"; -import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol"; -import "wormhole-solidity-sdk/interfaces/IWormhole.sol"; -import "wormhole-solidity-sdk/testing/helpers/WormholeSimulator.sol"; -import "wormhole-solidity-sdk/Utils.sol"; - -contract TestUpgrades is Test, IRateLimiterEvents { - NttManager nttManagerChain1; - NttManager nttManagerChain2; - - using TrimmedAmountLib for uint256; - using TrimmedAmountLib for TrimmedAmount; - - uint16 constant chainId1 = 7; - uint16 constant chainId2 = 100; - - uint16 constant SENDING_CHAIN_ID = 1; - uint256 constant DEVNET_GUARDIAN_PK = - 0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0; - WormholeSimulator guardian; - uint256 initialBlockTimestamp; - uint8 constant FAST_CONSISTENCY_LEVEL = 200; - uint256 constant GAS_LIMIT = 500000; - - WormholeTransceiver wormholeTransceiverChain1; - WormholeTransceiver wormholeTransceiverChain2; - address userA = address(0x123); - address userB = address(0x456); - address userC = address(0x789); - address userD = address(0xABC); - - address relayer = address(0x28D8F1Be96f97C1387e94A53e00eCcFb4E75175a); - IWormhole wormhole = IWormhole(0x4a8bc80Ed5a4067f1CCf107057b8270E0cC11A78); - - function setUp() public virtual { - string memory url = "https://ethereum-sepolia-rpc.publicnode.com"; - vm.createSelectFork(url); - initialBlockTimestamp = vm.getBlockTimestamp(); - - guardian = new WormholeSimulator(address(wormhole), DEVNET_GUARDIAN_PK); - - vm.chainId(chainId1); - DummyToken t1 = new DummyToken(); - NttManager implementation = new MockNttManagerContract( - address(t1), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - - nttManagerChain1 = - MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); - nttManagerChain1.initialize(); - - WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain1 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain1Implementation), "")) - ); - wormholeTransceiverChain1.initialize(); - - nttManagerChain1.setTransceiver(address(wormholeTransceiverChain1)); - nttManagerChain1.setOutboundLimit(type(uint64).max); - nttManagerChain1.setInboundLimit(type(uint64).max, chainId2); - - // Chain 2 setup - vm.chainId(chainId2); - DummyToken t2 = new DummyTokenMintAndBurn(); - NttManager implementationChain2 = new MockNttManagerContract( - address(t2), IManagerBase.Mode.BURNING, chainId2, 1 days, false - ); - - nttManagerChain2 = - MockNttManagerContract(address(new ERC1967Proxy(address(implementationChain2), ""))); - nttManagerChain2.initialize(); - - WormholeTransceiver wormholeTransceiverChain2Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain2), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain2 = MockWormholeTransceiverContract( - address(new ERC1967Proxy(address(wormholeTransceiverChain2Implementation), "")) - ); - wormholeTransceiverChain2.initialize(); - - nttManagerChain2.setTransceiver(address(wormholeTransceiverChain2)); - nttManagerChain2.setOutboundLimit(type(uint64).max); - nttManagerChain2.setInboundLimit(type(uint64).max, chainId1); - - // Register peer contracts for the nttManager and transceiver. Transceivers and nttManager each have the concept of peers here. - nttManagerChain1.setPeer( - chainId2, - bytes32(uint256(uint160(address(nttManagerChain2)))), - DummyToken(nttManagerChain2.token()).decimals(), - type(uint64).max - ); - nttManagerChain2.setPeer( - chainId1, - bytes32(uint256(uint160(address(nttManagerChain1)))), - DummyToken(nttManagerChain1.token()).decimals(), - type(uint64).max - ); - - wormholeTransceiverChain1.setWormholePeer( - chainId2, bytes32(uint256(uint160((address(wormholeTransceiverChain2))))) - ); - wormholeTransceiverChain2.setWormholePeer( - chainId1, bytes32(uint256(uint160(address(wormholeTransceiverChain1)))) - ); - - nttManagerChain1.setThreshold(1); - nttManagerChain2.setThreshold(1); - vm.chainId(chainId1); - } - - function test_basicUpgradeNttManager() public { - // Basic call to upgrade with the same contact as ewll - NttManager newImplementation = new MockNttManagerContract( - address(nttManagerChain1.token()), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - nttManagerChain1.upgrade(address(newImplementation)); - - basicFunctionality(); - } - - //Upgradability stuff for transceivers is real borked because of some missing implementation. Test this later once fixed. - function test_basicUpgradeTransceiver() public { - // Basic call to upgrade with the same contact as well - WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain1.upgrade(address(wormholeTransceiverChain1Implementation)); - - basicFunctionality(); - } - - // Confirm that we can handle multiple upgrades as a nttManager - function test_doubleUpgradeNttManager() public { - // Basic call to upgrade with the same contact as ewll - NttManager newImplementation = new MockNttManagerContract( - address(nttManagerChain1.token()), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - nttManagerChain1.upgrade(address(newImplementation)); - basicFunctionality(); - - newImplementation = new MockNttManagerContract( - address(nttManagerChain1.token()), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - nttManagerChain1.upgrade(address(newImplementation)); - - basicFunctionality(); - } - - // NOTE: There are additional tests in `Upgrades.t.sol` to verifying downgrading from `NttManagerNoRateLimiting` to `NttManager`. - - function test_cannotUpgradeToNoRateLimitingIfItWasEnabled() public { - // The default set up has rate limiting enabled. When we attempt to upgrade to no rate limiting, the immutable check should panic. - NttManager rateLimitingImplementation = new MockNttManagerNoRateLimitingContract( - address(nttManagerChain1.token()), IManagerBase.Mode.LOCKING, chainId1 - ); - - vm.expectRevert(); // Reverts with a panic on the assert. So, no way to tell WHY this happened. - nttManagerChain1.upgrade(address(rateLimitingImplementation)); - } - - function test_upgradeToNoRateLimiting() public { - // Create a standard manager with rate limiting disabled. - DummyToken t = new DummyToken(); - NttManager implementation = - new MockNttManagerContract(address(t), IManagerBase.Mode.LOCKING, chainId1, 0, true); - - MockNttManagerContract thisNttManager = - MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); - thisNttManager.initialize(); - - thisNttManager.setPeer(chainId2, toWormholeFormat(address(0x1)), 9, type(uint64).max); - - // Upgrade from NttManager with rate limiting disabled to NttManagerNoRateLimiting. - NttManager rateLimitingImplementation = new MockNttManagerNoRateLimitingContract( - address(t), IManagerBase.Mode.LOCKING, chainId1 - ); - thisNttManager.upgrade(address(rateLimitingImplementation)); - basicFunctionality(); - - // Upgrade from NttManagerNoRateLimiting to NttManagerNoRateLimiting. - rateLimitingImplementation = new MockNttManagerNoRateLimitingContract( - address(t), IManagerBase.Mode.LOCKING, chainId1 - ); - thisNttManager.upgrade(address(rateLimitingImplementation)); - basicFunctionality(); - - // Upgrade from NttManagerNoRateLimiting back to NttManager. - NttManager nttManagerImplementation = - new MockNttManagerContract(address(t), IManagerBase.Mode.LOCKING, chainId1, 0, true); - thisNttManager.upgrade(address(nttManagerImplementation)); - basicFunctionality(); - } - - //Upgradability stuff for transceivers is real borked because of some missing implementation. Test this later once fixed. - function test_doubleUpgradeTransceiver() public { - // Basic call to upgrade with the same contact as well - WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain1.upgrade(address(wormholeTransceiverChain1Implementation)); - - basicFunctionality(); - - // Basic call to upgrade with the same contact as well - wormholeTransceiverChain1.upgrade(address(wormholeTransceiverChain1Implementation)); - - basicFunctionality(); - } - - function test_storageSlotNttManager() public { - // Basic call to upgrade with the same contact as ewll - NttManager newImplementation = new MockNttManagerStorageLayoutChange( - address(nttManagerChain1.token()), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - nttManagerChain1.upgrade(address(newImplementation)); - - address oldOwner = nttManagerChain1.owner(); - MockNttManagerStorageLayoutChange(address(nttManagerChain1)).setData(); - - // If we overrode something important, it would probably break here - basicFunctionality(); - - require(oldOwner == nttManagerChain1.owner(), "Owner changed in an unintended way."); - } - - function test_storageSlotTransceiver() public { - // Basic call to upgrade with the same contact as ewll - WormholeTransceiver newImplementation = new MockWormholeTransceiverLayoutChange( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain1.upgrade(address(newImplementation)); - - address oldOwner = nttManagerChain1.owner(); - MockWormholeTransceiverLayoutChange(address(wormholeTransceiverChain1)).setData(); - - // If we overrode something important, it would probably break here - basicFunctionality(); - - require(oldOwner == nttManagerChain1.owner(), "Owner changed in an unintended way."); - } - - function test_callMigrateNttManager() public { - // Basic call to upgrade with the same contact as ewll - NttManager newImplementation = new MockNttManagerMigrateBasic( - address(nttManagerChain1.token()), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - - vm.expectRevert("Proper migrate called"); - nttManagerChain1.upgrade(address(newImplementation)); - - basicFunctionality(); - } - - //Upgradability stuff for transceivers is real borked because of some missing implementation. Test this later once fixed. - function test_callMigrateTransceiver() public { - // Basic call to upgrade with the same contact as well - MockWormholeTransceiverMigrateBasic wormholeTransceiverChain1Implementation = new MockWormholeTransceiverMigrateBasic( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - vm.expectRevert("Proper migrate called"); - wormholeTransceiverChain1.upgrade(address(wormholeTransceiverChain1Implementation)); - - basicFunctionality(); - } - - function test_immutableBlockUpdateFailureNttManager() public { - DummyToken tnew = new DummyToken(); - - // Basic call to upgrade with the same contact as ewll - NttManager newImplementation = new MockNttManagerImmutableCheck( - address(tnew), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - - vm.expectRevert(); // Reverts with a panic on the assert. So, no way to tell WHY this happened. - nttManagerChain1.upgrade(address(newImplementation)); - - require(nttManagerChain1.token() != address(tnew), "Token updated when it shouldn't be"); - - basicFunctionality(); - } - - function test_immutableBlockUpdateFailureTransceiver() public { - // Don't allow upgrade to work with a change immutable - - address oldNttManager = wormholeTransceiverChain1.nttManager(); - WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverMigrateBasic( - address(nttManagerChain2), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - vm.expectRevert(); // Reverts with a panic on the assert. So, no way to tell WHY this happened. - wormholeTransceiverChain1.upgrade(address(wormholeTransceiverChain1Implementation)); - - require( - wormholeTransceiverChain1.nttManager() == oldNttManager, - "NttManager updated when it shouldn't be" - ); - } - - function test_immutableBlockUpdateSuccessNttManager() public { - DummyToken tnew = new DummyToken(); - - // Basic call to upgrade with the same contact as ewll - NttManager newImplementation = new MockNttManagerImmutableRemoveCheck( - address(tnew), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - - // Allow an upgrade, since we enabled the ability to edit the immutables within the code - nttManagerChain1.upgrade(address(newImplementation)); - require(nttManagerChain1.token() == address(tnew), "Token not updated"); - - basicFunctionality(); - } - - function test_immutableBlockUpdateSuccessTransceiver() public { - WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverImmutableAllow( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - - //vm.expectRevert(); // Reverts with a panic on the assert. So, no way to tell WHY this happened. - wormholeTransceiverChain1.upgrade(address(wormholeTransceiverChain1Implementation)); - - require( - wormholeTransceiverChain1.nttManager() == address(nttManagerChain1), - "NttManager updated when it shouldn't be" - ); - } - - function test_authNttManager() public { - // User not owner so this should fail - vm.prank(userA); - vm.expectRevert( - abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, userA) - ); - nttManagerChain1.upgrade(address(0x1)); - - // Basic call to upgrade so that we can get the real implementation. - NttManager newImplementation = new MockNttManagerContract( - address(nttManagerChain1.token()), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - nttManagerChain1.upgrade(address(newImplementation)); - - basicFunctionality(); // Ensure that the upgrade was proper - - vm.expectRevert(abi.encodeWithSelector(Implementation.NotMigrating.selector)); - nttManagerChain1.migrate(); - - // Test if we can 'migrate' from this point - // Migrate without delegatecall - vm.expectRevert(abi.encodeWithSelector(Implementation.OnlyDelegateCall.selector)); - newImplementation.migrate(); - - // Transfer the ownership - shouldn't have permission for that - vm.prank(userA); - vm.expectRevert( - abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, userA) - ); - nttManagerChain1.transferOwnership(address(0x1)); - - // Should fail because it's already initialized - vm.expectRevert(Initializable.InvalidInitialization.selector); - nttManagerChain1.initialize(); - - // Should fail because we're calling the implementation directly instead of the proxy. - vm.expectRevert(Implementation.OnlyDelegateCall.selector); - newImplementation.initialize(); - } - - function test_authTransceiver() public { - // User not owner so this should fail - vm.prank(userA); - vm.expectRevert( - abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, userA) - ); - wormholeTransceiverChain1.upgrade(address(0x01)); - - // Basic call so that we can easily see what the new transceiver is. - WormholeTransceiver wormholeTransceiverChain1Implementation = new MockWormholeTransceiverContract( - address(nttManagerChain1), - address(wormhole), - address(relayer), - address(0x0), - FAST_CONSISTENCY_LEVEL, - GAS_LIMIT - ); - wormholeTransceiverChain1.upgrade(address(wormholeTransceiverChain1Implementation)); - basicFunctionality(); // Ensure that the upgrade was proper - - // Test if we can 'migrate' from this point - // Migrate without delegatecall - vm.expectRevert(abi.encodeWithSelector(Implementation.OnlyDelegateCall.selector)); - wormholeTransceiverChain1Implementation.migrate(); - - // Migrate - should fail since we're executing something outside of a migration - vm.expectRevert(abi.encodeWithSelector(Implementation.NotMigrating.selector)); - wormholeTransceiverChain1.migrate(); - - // Transfer the ownership - shouldn't have permission for that - vm.prank(userA); - vm.expectRevert( - abi.encodeWithSelector(OwnableUpgradeable.OwnableUnauthorizedAccount.selector, userA) - ); - wormholeTransceiverChain1.transferOwnership(address(0x1)); - - // Should fail because it's already initialized - vm.expectRevert(Initializable.InvalidInitialization.selector); - wormholeTransceiverChain1.initialize(); - - // // Should fail because we're calling the implementation directly instead of the proxy. - vm.expectRevert(Implementation.OnlyDelegateCall.selector); - wormholeTransceiverChain1Implementation.initialize(); - } - - function test_nonZeroWormholeFee() public { - // Set the message fee to be non-zero - vm.chainId(11155111); // Sepolia testnet id - uint256 fee = 0.000001e18; - guardian.setMessageFee(fee); - uint256 balanceBefore = address(userA).balance; - basicFunctionality(); - uint256 balanceAfter = address(userA).balance; - assertEq(balanceAfter + fee, balanceBefore); - } - - function basicFunctionality() public { - vm.chainId(chainId1); - - // Setting up the transfer - DummyToken token1 = DummyToken(nttManagerChain1.token()); - DummyToken token2 = DummyTokenMintAndBurn(nttManagerChain2.token()); - - uint8 decimals = token1.decimals(); - uint256 sendingAmount = 5 * 10 ** decimals; - token1.mintDummy(address(userA), 5 * 10 ** decimals); - vm.startPrank(userA); - token1.approve(address(nttManagerChain1), sendingAmount); - - vm.recordLogs(); - - // Fetch quote - (, uint256 totalQuote) = - nttManagerChain1.quoteDeliveryPrice(chainId2, encodeTransceiverInstruction(true)); - - // Send token through standard means (not relayer) - { - uint256 nttManagerBalanceBefore = token1.balanceOf(address(nttManagerChain1)); - uint256 userBalanceBefore = token1.balanceOf(address(userA)); - nttManagerChain1.transfer{value: totalQuote}( - sendingAmount, - chainId2, - toWormholeFormat(userB), - toWormholeFormat(userA), - false, - encodeTransceiverInstruction(true) - ); - - // Balance check on funds going in and out working as expected - uint256 nttManagerBalanceAfter = token1.balanceOf(address(nttManagerChain1)); - uint256 userBalanceAfter = token1.balanceOf(address(userB)); - require( - nttManagerBalanceBefore + sendingAmount == nttManagerBalanceAfter, - "Should be locking the tokens" - ); - require( - userBalanceBefore - sendingAmount == userBalanceAfter, - "User should have sent tokens" - ); - } - - vm.stopPrank(); - - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId1); - } - - // Chain2 verification and checks - vm.chainId(chainId2); - - // Wrong chain receiving the signed VAA - vm.expectRevert(abi.encodeWithSelector(InvalidFork.selector, chainId1, chainId2)); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); - { - uint256 supplyBefore = token2.totalSupply(); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); - uint256 supplyAfter = token2.totalSupply(); - - require(sendingAmount + supplyBefore == supplyAfter, "Supplies dont match"); - require(token2.balanceOf(userB) == sendingAmount, "User didn't receive tokens"); - require( - token2.balanceOf(address(nttManagerChain2)) == 0, "NttManager has unintended funds" - ); - } - - // Can't resubmit the same message twice - (IWormhole.VM memory wormholeVM,,) = wormhole.parseAndVerifyVM(encodedVMs[0]); - vm.expectRevert( - abi.encodeWithSelector( - IWormholeTransceiver.TransferAlreadyCompleted.selector, wormholeVM.hash - ) - ); - wormholeTransceiverChain2.receiveMessage(encodedVMs[0]); - - // Go back the other way from a THIRD user - vm.prank(userB); - token2.transfer(userC, sendingAmount); - - vm.startPrank(userC); - - token2.approve(address(nttManagerChain2), sendingAmount); - vm.recordLogs(); - - // Fetch quote - (, totalQuote) = - nttManagerChain2.quoteDeliveryPrice(chainId1, encodeTransceiverInstruction(true)); - - // Supply checks on the transfer - { - uint256 supplyBefore = token2.totalSupply(); - nttManagerChain2.transfer{value: totalQuote}( - sendingAmount, - chainId1, - toWormholeFormat(userD), - toWormholeFormat(userC), - false, - encodeTransceiverInstruction(true) - ); - - uint256 supplyAfter = token2.totalSupply(); - - require(sendingAmount - supplyBefore == supplyAfter, "Supplies don't match"); - require(token2.balanceOf(userB) == 0, "OG user receive tokens"); - require(token2.balanceOf(userC) == 0, "Sending user didn't receive tokens"); - require( - token2.balanceOf(address(nttManagerChain2)) == 0, - "NttManager didn't receive unintended funds" - ); - } - - // Get and sign the log to go down the other pipe. Thank you to whoever wrote this code in the past! - entries = guardian.fetchWormholeMessageFromLog(vm.getRecordedLogs()); - encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], chainId2); - } - - // Chain1 verification and checks with the receiving of the message - vm.chainId(chainId1); - - { - uint256 supplyBefore = token1.totalSupply(); - uint256 userDBalanceBefore = token1.balanceOf(userD); - wormholeTransceiverChain1.receiveMessage(encodedVMs[0]); - - uint256 supplyAfter = token1.totalSupply(); - - require(supplyBefore == supplyAfter, "Supplies don't match between operations"); - require(token1.balanceOf(userB) == 0, "OG user receive tokens"); - require(token1.balanceOf(userC) == 0, "Sending user didn't receive tokens"); - require( - token1.balanceOf(userD) == sendingAmount + userDBalanceBefore, "User received funds" - ); - } - - vm.stopPrank(); - } - - function encodeTransceiverInstruction( - bool relayer_off - ) public view returns (bytes memory) { - WormholeTransceiver.WormholeTransceiverInstruction memory instruction = - IWormholeTransceiver.WormholeTransceiverInstruction(relayer_off); - bytes memory encodedInstructionWormhole = - wormholeTransceiverChain1.encodeWormholeTransceiverInstruction(instruction); - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction = TransceiverStructs - .TransceiverInstruction({index: 0, payload: encodedInstructionWormhole}); - TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](1); - TransceiverInstructions[0] = TransceiverInstruction; - return TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions); - } -} - -contract TestInitialize is Test { - function setUp() public {} - - NttManager nttManagerChain1; - NttManager nttManagerChain2; - - using TrimmedAmountLib for uint256; - using TrimmedAmountLib for TrimmedAmount; - - uint16 constant chainId1 = 7; - - uint256 constant DEVNET_GUARDIAN_PK = - 0xcfb12303a19cde580bb4dd771639b0d26bc68353645571a8cff516ab2ee113a0; - - WormholeTransceiver wormholeTransceiverChain1; - address userA = address(0x123); - - address relayer = address(0x28D8F1Be96f97C1387e94A53e00eCcFb4E75175a); - IWormhole wormhole = IWormhole(0x4a8bc80Ed5a4067f1CCf107057b8270E0cC11A78); - - function test_doubleInitialize() public { - string memory url = "https://ethereum-sepolia-rpc.publicnode.com"; - vm.createSelectFork(url); - - vm.chainId(chainId1); - DummyToken t1 = new DummyToken(); - NttManager implementation = new MockNttManagerContract( - address(t1), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - - nttManagerChain1 = - MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); - - // Initialize once - nttManagerChain1.initialize(); - - // Initialize twice - vm.expectRevert(Initializable.InvalidInitialization.selector); - nttManagerChain1.initialize(); - } - - function test_cannotFrontrunInitialize() public { - string memory url = "https://ethereum-sepolia-rpc.publicnode.com"; - vm.createSelectFork(url); - - vm.chainId(chainId1); - DummyToken t1 = new DummyToken(); - NttManager implementation = new MockNttManagerContract( - address(t1), IManagerBase.Mode.LOCKING, chainId1, 1 days, false - ); - - nttManagerChain1 = - MockNttManagerContract(address(new ERC1967Proxy(address(implementation), ""))); - - // Attempt to initialize the contract from a non-deployer account. - vm.prank(userA); - vm.expectRevert( - abi.encodeWithSelector(INttManager.UnexpectedDeployer.selector, address(this), userA) - ); - nttManagerChain1.initialize(); - } -} diff --git a/evm/test/interfaces/ITransceiverReceiver.sol b/evm/test/interfaces/ITransceiverReceiver.sol index 6e707a6e5..68e71bfb7 100644 --- a/evm/test/interfaces/ITransceiverReceiver.sol +++ b/evm/test/interfaces/ITransceiverReceiver.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Apache 2 pragma solidity >=0.8.8 <0.9.0; -interface ITransceiverReceiver { +interface IAdapterReceiver { function receiveMessage( bytes memory encodedMessage ) external; diff --git a/evm/test/libraries/IntegrationHelpers.sol b/evm/test/libraries/IntegrationHelpers.sol deleted file mode 100644 index 22f595805..000000000 --- a/evm/test/libraries/IntegrationHelpers.sol +++ /dev/null @@ -1,198 +0,0 @@ -// SPDX-License-Identifier: Apache 2 -pragma solidity 0.8.19; - -import "forge-std/Test.sol"; -import "forge-std/console.sol"; -import {WormholeTransceiver} from - "../../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; -import {DummyToken, DummyTokenMintAndBurn} from "../../src/mocks/DummyToken.sol"; -import "../../src/libraries/TrimmedAmount.sol"; -import {Utils} from "./../libraries/Utils.sol"; -import "../../src/libraries/TransceiverStructs.sol"; -import "wormhole-solidity-sdk/Utils.sol"; -import "../../src/interfaces/IWormholeTransceiver.sol"; -import {WormholeRelayerBasicTest} from "wormhole-solidity-sdk/testing/WormholeRelayerTest.sol"; -import "../../src/NttManager/NttManager.sol"; -import "wormhole-solidity-sdk/testing/helpers/WormholeSimulator.sol"; - -contract IntegrationHelpers is Test { - using TrimmedAmountLib for uint256; - using TrimmedAmountLib for TrimmedAmount; - - WormholeTransceiver wormholeTransceiverChain1; - WormholeTransceiver wormholeTransceiverChain1Other; - WormholeTransceiver wormholeTransceiverChain2; - WormholeTransceiver wormholeTransceiverChain2Other; - - function buildTransceiverInstruction( - bool relayer_off - ) public view returns (TransceiverStructs.TransceiverInstruction memory) { - WormholeTransceiver.WormholeTransceiverInstruction memory instruction = - IWormholeTransceiver.WormholeTransceiverInstruction(relayer_off); - - bytes memory encodedInstructionWormhole; - // Source fork has id 0 and corresponds to chain 1 - if (vm.activeFork() == 0) { - encodedInstructionWormhole = - wormholeTransceiverChain1.encodeWormholeTransceiverInstruction(instruction); - } else { - encodedInstructionWormhole = - wormholeTransceiverChain2.encodeWormholeTransceiverInstruction(instruction); - } - return TransceiverStructs.TransceiverInstruction({ - index: 0, - payload: encodedInstructionWormhole - }); - } - - function encodeTransceiverInstruction( - bool relayer_off - ) public view returns (bytes memory) { - TransceiverStructs.TransceiverInstruction memory TransceiverInstruction = - buildTransceiverInstruction(relayer_off); - TransceiverStructs.TransceiverInstruction[] memory TransceiverInstructions = - new TransceiverStructs.TransceiverInstruction[](1); - TransceiverInstructions[0] = TransceiverInstruction; - return TransceiverStructs.encodeTransceiverInstructions(TransceiverInstructions); - } - - function _setTransceiverPeers( - WormholeTransceiver[2] memory transceivers, - WormholeTransceiver[2] memory transceiverPeers, - uint16[2] memory chainIds - ) internal { - for (uint256 i; i < transceivers.length; i++) { - transceivers[i].setWormholePeer( - chainIds[i], toWormholeFormat(address(transceiverPeers[i])) - ); - } - } - - function _setManagerPeer( - NttManager sourceManager, - NttManager peerManagerAddr, - uint16 peerChainId, - uint8 peerDecimals, - uint64 inboundLimit - ) internal { - sourceManager.setPeer( - peerChainId, toWormholeFormat(address(peerManagerAddr)), peerDecimals, inboundLimit - ); - } - - function _enableSR(WormholeTransceiver[2] memory transceivers, uint16 chainId) internal { - for (uint256 i; i < transceivers.length; i++) { - transceivers[i].setIsWormholeRelayingEnabled(chainId, true); - transceivers[i].setIsWormholeEvmChain(chainId, true); - } - } - - function _quotePrices( - WormholeTransceiver[] memory transceivers, - uint16 recipientChainId, - bool shouldSkipRelay - ) internal view returns (uint256) { - uint256 quoteSum; - for (uint256 i; i < transceivers.length; i++) { - quoteSum = quoteSum - + transceivers[i].quoteDeliveryPrice( - recipientChainId, buildTransceiverInstruction(shouldSkipRelay) - ); - } - - return quoteSum; - } - - // Setting up the transfer - function _prepareTransfer( - DummyToken token, - address user, - address contractAddr, - uint256 amount - ) internal { - vm.startPrank(user); - - token.mintDummy(user, amount); - token.approve(contractAddr, amount); - } - - function _computeManagerMessageDigest( - address from, - address to, - TrimmedAmount sendingAmount, - address tokenAddr, - uint16 sourceChainId, - uint16 recipientChainId - ) internal pure returns (bytes32) { - TransceiverStructs.NttManagerMessage memory nttManagerMessage; - nttManagerMessage = TransceiverStructs.NttManagerMessage( - 0, - toWormholeFormat(from), - TransceiverStructs.encodeNativeTokenTransfer( - TransceiverStructs.NativeTokenTransfer({ - amount: sendingAmount, - sourceToken: toWormholeFormat(tokenAddr), - to: toWormholeFormat(to), - toChain: recipientChainId, - additionalPayload: "" - }) - ) - ); - - return TransceiverStructs.nttManagerMessageDigest(sourceChainId, nttManagerMessage); - } - - function getTotalSupply(uint256 forkId, DummyToken token) public returns (uint256) { - vm.selectFork(forkId); - return token.totalSupply(); - } - - // Send token through standard relayer - function transferToken( - address to, - address refund, - NttManager sourceManager, - uint256 sendingAmount, - uint16 recipientChainId, - WormholeTransceiver[] memory transceivers, - bool relayer_off - ) public { - uint256 quoteSum = _quotePrices(transceivers, recipientChainId, relayer_off); - - // refund the amount back to the user that sent the transfer - sourceManager.transfer{value: quoteSum}( - sendingAmount, - recipientChainId, - toWormholeFormat(to), - toWormholeFormat(refund), - relayer_off, - encodeTransceiverInstruction(relayer_off) - ); - } - - function _getWormholeMessage( - WormholeSimulator guardian, - Vm.Log[] memory logs, - uint16 emitterChain - ) internal view returns (bytes[] memory) { - Vm.Log[] memory entries = guardian.fetchWormholeMessageFromLog(logs); - bytes[] memory encodedVMs = new bytes[](entries.length); - for (uint256 i = 0; i < encodedVMs.length; i++) { - encodedVMs[i] = guardian.fetchSignedMessageFromLogs(entries[i], emitterChain); - } - - return encodedVMs; - } - - function _receiveWormholeMessage( - IWormhole.VM memory vaa, - WormholeTransceiver sourceTransceiver, - WormholeTransceiver targetTransceiver, - uint16 emitterChainId, - bytes[] memory a - ) internal { - targetTransceiver.receiveWormholeMessages( - vaa.payload, a, toWormholeFormat(address(sourceTransceiver)), emitterChainId, vaa.hash - ); - } -} diff --git a/evm/test/libraries/NttManagerHelpers.sol b/evm/test/libraries/NttManagerHelpers.sol index dbef7b6fe..78ae46bff 100644 --- a/evm/test/libraries/NttManagerHelpers.sol +++ b/evm/test/libraries/NttManagerHelpers.sol @@ -2,15 +2,17 @@ pragma solidity >=0.8.8 <0.9.0; +import "forge-std/Test.sol"; + import "../../src/libraries/TrimmedAmount.sol"; import "../../src/NttManager/NttManager.sol"; import "../../src/interfaces/INttManager.sol"; library NttManagerHelpersLib { - uint16 constant SENDING_CHAIN_ID = 1; - using TrimmedAmountLib for TrimmedAmount; + uint128 public constant gasLimit = 100000000; + function setConfigs( TrimmedAmount inboundLimit, NttManager nttManager, @@ -26,8 +28,12 @@ library NttManagerHelpersLib { uint8 tokenDecimals = abi.decode(queriedDecimals, (uint8)); recipientNttManager.setPeer( - SENDING_CHAIN_ID, toWormholeFormat(address(nttManager)), tokenDecimals, type(uint64).max + nttManager.chainId(), + toWormholeFormat(address(nttManager)), + tokenDecimals, + gasLimit, + type(uint64).max ); - recipientNttManager.setInboundLimit(inboundLimit.untrim(decimals), SENDING_CHAIN_ID); + recipientNttManager.setInboundLimit(inboundLimit.untrim(decimals), nttManager.chainId()); } } diff --git a/evm/test/libraries/TransceiverHelpers.sol b/evm/test/libraries/TransceiverHelpers.sol index 7b7c39a4d..66a9573e9 100644 --- a/evm/test/libraries/TransceiverHelpers.sol +++ b/evm/test/libraries/TransceiverHelpers.sol @@ -2,6 +2,7 @@ pragma solidity >=0.8.8 <0.9.0; +import "wormhole-solidity-sdk/libraries/BytesParsing.sol"; import "./NttManagerHelpers.sol"; import "../mocks/DummyTransceiver.sol"; import "../../src/mocks/DummyToken.sol"; @@ -9,61 +10,127 @@ import "../../src/NttManager/NttManager.sol"; import "../../src/libraries/TrimmedAmount.sol"; library TransceiverHelpersLib { + using BytesParsing for bytes; using TrimmedAmountLib for TrimmedAmount; // 0x99'E''T''T' bytes4 constant TEST_TRANSCEIVER_PAYLOAD_PREFIX = 0x99455454; - uint16 constant SENDING_CHAIN_ID = 1; function setup_transceivers( - NttManager nttManager + NttManager nttManager, + uint16 peerChainId ) internal returns (DummyTransceiver, DummyTransceiver) { - DummyTransceiver e1 = new DummyTransceiver(address(nttManager)); - DummyTransceiver e2 = new DummyTransceiver(address(nttManager)); + DummyTransceiver e1 = + new DummyTransceiver(nttManager.chainId(), address(nttManager.endpoint())); + DummyTransceiver e2 = + new DummyTransceiver(nttManager.chainId(), address(nttManager.endpoint())); nttManager.setTransceiver(address(e1)); + nttManager.enableSendTransceiver(peerChainId, address(e1)); + nttManager.enableRecvTransceiver(peerChainId, address(e1)); + nttManager.setTransceiver(address(e2)); + nttManager.enableSendTransceiver(peerChainId, address(e2)); + nttManager.enableRecvTransceiver(peerChainId, address(e2)); + nttManager.setThreshold(peerChainId, 2); + return (e1, e2); + } + + function addTransceiver( + NttManager nttManager, + DummyTransceiver e1, + uint16 peerChainId + ) internal returns (DummyTransceiver, DummyTransceiver) { + DummyTransceiver e2 = + new DummyTransceiver(nttManager.chainId(), address(nttManager.endpoint())); nttManager.setTransceiver(address(e2)); - nttManager.setThreshold(2); + nttManager.enableSendTransceiver(peerChainId, address(e2)); + nttManager.enableRecvTransceiver(peerChainId, address(e2)); + nttManager.setThreshold(peerChainId, 2); return (e1, e2); } - function attestTransceiversHelper( + function transferAndAttest( address to, bytes32 id, - uint16 toChain, NttManager nttManager, NttManager recipientNttManager, TrimmedAmount amount, TrimmedAmount inboundLimit, - ITransceiverReceiver[] memory transceivers + DummyTransceiver[] memory transceivers ) internal returns ( - TransceiverStructs.NttManagerMessage memory, - TransceiverStructs.TransceiverMessage memory + TransceiverStructs.NttManagerMessage memory m, + bytes memory encodedM, + DummyTransceiver.Message memory rmsg ) { - TransceiverStructs.NttManagerMessage memory m = - buildNttManagerMessage(to, id, toChain, nttManager, amount); - bytes memory encodedM = TransceiverStructs.encodeNttManagerMessage(m); + m = buildNttManagerMessage(to, id, recipientNttManager.chainId(), nttManager, amount); + encodedM = TransceiverStructs.encodeNttManagerMessage(m); + prepTokenReceive(nttManager, recipientNttManager, amount, inboundLimit); + rmsg = attestMsg(nttManager, recipientNttManager, 0, transceivers, encodedM); + } + function transferAttestAndReceive( + address to, + bytes32 id, + NttManager nttManager, + NttManager recipientNttManager, + TrimmedAmount amount, + TrimmedAmount inboundLimit, + DummyTransceiver[] memory transceivers + ) + internal + returns ( + TransceiverStructs.NttManagerMessage memory m, + DummyTransceiver.Message memory rmsg + ) + { + m = buildNttManagerMessage(to, id, recipientNttManager.chainId(), nttManager, amount); + bytes memory encodedM = TransceiverStructs.encodeNttManagerMessage(m); prepTokenReceive(nttManager, recipientNttManager, amount, inboundLimit); + rmsg = attestAndReceiveMsg(nttManager, recipientNttManager, 0, transceivers, encodedM); + } - TransceiverStructs.TransceiverMessage memory em; - bytes memory encodedEm; - (em, encodedEm) = TransceiverStructs.buildAndEncodeTransceiverMessage( - TEST_TRANSCEIVER_PAYLOAD_PREFIX, - toWormholeFormat(address(nttManager)), - toWormholeFormat(address(recipientNttManager)), - encodedM, - new bytes(0) - ); + function attestMsg( + NttManager srcNttManager, + NttManager dstNttManager, + uint64 sequence, + DummyTransceiver[] memory transceivers, + bytes memory encodedM + ) internal returns (DummyTransceiver.Message memory rmsg) { + rmsg = DummyTransceiver.Message({ + srcChain: srcNttManager.chainId(), + srcAddr: UniversalAddressLibrary.fromAddress(address(srcNttManager)), + sequence: sequence, + dstChain: dstNttManager.chainId(), + dstAddr: UniversalAddressLibrary.fromAddress(address(dstNttManager)), + payloadHash: keccak256(encodedM), + refundAddr: address(0) + }); - for (uint256 i; i < transceivers.length; i++) { - ITransceiverReceiver e = transceivers[i]; - e.receiveMessage(encodedEm); + // Attest the message on all the transceivers. + uint8 numTrans = uint8(transceivers.length); + for (uint8 i; i < numTrans; i++) { + transceivers[i].receiveMessage(rmsg); } + } + + function attestAndReceiveMsg( + NttManager srcNttManager, + NttManager dstNttManager, + uint64 sequence, + DummyTransceiver[] memory transceivers, + bytes memory encodedM + ) internal returns (DummyTransceiver.Message memory rmsg) { + rmsg = attestMsg(srcNttManager, dstNttManager, sequence, transceivers, encodedM); - return (m, em); + // Execute the message. + dstNttManager.executeMsg( + srcNttManager.chainId(), + UniversalAddressLibrary.fromAddress(address(srcNttManager)), + 0, + encodedM + ); } function buildNttManagerMessage( @@ -123,4 +190,64 @@ library TransceiverHelpersLib { ); return (m, transceiverMessage); } + + error TransferSentEventNotFoundInLogs(uint64 nttSeqNo); + error ExecutorEventNotFoundInLogs(uint64 nttSeqNo, bytes32 payloadHash); + + function getExecutionSent( + Vm.Log[] memory events, + address nttManager, + uint64 nttSeqNo + ) public pure returns (bytes memory payload) { + // To find the payload bytes from the logs we need to do the following steps: + // 1. Look for the TransferSent event for our NttManager and sequence number. + // 2. Immediately following that should be the RequestForExecution event. + // 3. Extract the payload of that, which is an MMRequest. Parse that to get the NTT payload. + for (uint256 idx = 0; idx < events.length; ++idx) { + if ( + events[idx].topics[0] + == bytes32(0x75eb8927cc7c4810b30fa2e8011fce37da6da7d18eb82c642c367ae4445c3625) + && events[idx].emitter == nttManager + ) { + (,,, uint64 sequence,) = + abi.decode(events[idx].data, (uint256, uint256, uint16, uint64, bytes32)); + + if (sequence == nttSeqNo) { + // The next event in the log should be from the executor + if ( + (idx + 1 < events.length) + && ( + events[idx + 1].topics[0] + == bytes32( + 0xd870d87e4a7c33d0943b0a3d2822b174e239cc55c169af14cc56467a4489e3b5 + ) + ) + ) { + bytes memory execPayload; + (,,,,, execPayload,) = abi.decode( + events[idx + 1].data, + (uint256, uint16, bytes32, address, bytes, bytes, bytes) + ); + return decodeMMRequest(execPayload); + } + } + } + } + revert TransferSentEventNotFoundInLogs(nttSeqNo); + } + + /// @dev This decodes an ExecutorMessages.makeMMRequest and returns the enclosed payload. + function decodeMMRequest( + bytes memory execPayload + ) internal pure returns (bytes memory payload) { + uint256 offset = 0; + uint32 payloadLen; + + (, offset) = execPayload.asBytes4(offset); // msgType + (, offset) = execPayload.asUint16(offset); // srcChain + (, offset) = execPayload.asBytes32(offset); // srcAddr + (, offset) = execPayload.asUint64(offset); // seqNo + (payloadLen, offset) = execPayload.asUint32(offset); + (payload, offset) = execPayload.sliceUnchecked(offset, payloadLen); + } } diff --git a/evm/test/mocks/DummyTransceiver.sol b/evm/test/mocks/DummyTransceiver.sol index 52598acd0..24ea1a194 100644 --- a/evm/test/mocks/DummyTransceiver.sol +++ b/evm/test/mocks/DummyTransceiver.sol @@ -3,56 +3,73 @@ pragma solidity >=0.8.8 <0.9.0; import "forge-std/Test.sol"; -import "../../src/Transceiver/Transceiver.sol"; -import "../interfaces/ITransceiverReceiver.sol"; -contract DummyTransceiver is Transceiver, ITransceiverReceiver { - uint16 constant SENDING_CHAIN_ID = 1; +import "example-messaging-endpoint/evm/src/interfaces/IEndpointAdapter.sol"; +import "example-messaging-endpoint/evm/src/interfaces/IAdapter.sol"; + +contract DummyTransceiver is IAdapter { + uint16 public immutable chainId; + address public immutable endpoint; bytes4 constant TEST_TRANSCEIVER_PAYLOAD_PREFIX = 0x99455454; - constructor( - address nttManager - ) Transceiver(nttManager) {} + constructor(uint16 _chainId, address _endpoint) { + chainId = _chainId; + endpoint = _endpoint; + } - function getTransceiverType() external pure override returns (string memory) { + function getAdapterType() external pure returns (string memory) { return "dummy"; } - function _quoteDeliveryPrice( + function quoteDeliveryPrice( uint16, /* recipientChain */ - TransceiverStructs.TransceiverInstruction memory /* transceiverInstruction */ - ) internal pure override returns (uint256) { + bytes calldata /* adapterInstructions */ + ) external pure returns (uint256) { return 0; } - function _sendMessage( - uint16, /* recipientChain */ - uint256, /* deliveryPayment */ - address, /* caller */ - bytes32, /* recipientNttManagerAddress */ - bytes32, /* refundAddres */ - TransceiverStructs.TransceiverInstruction memory, /* instruction */ - bytes memory /* payload */ - ) internal override { - // do nothing + struct Message { + uint16 srcChain; + UniversalAddress srcAddr; + uint64 sequence; + uint16 dstChain; + UniversalAddress dstAddr; + bytes32 payloadHash; + address refundAddr; + } + + Message[] public messages; + + function getMessages() external view returns (Message[] memory) { + return messages; + } + + function sendMessage( + UniversalAddress srcAddr, + uint64 sequence, + uint16 dstChain, + UniversalAddress dstAddr, + bytes32 payloadHash, + address refundAddr, + bytes calldata // adapterInstructions + ) external payable { + Message memory m = Message({ + srcChain: chainId, + srcAddr: srcAddr, + sequence: sequence, + dstChain: dstChain, + dstAddr: dstAddr, + payloadHash: payloadHash, + refundAddr: refundAddr + }); + messages.push(m); } function receiveMessage( - bytes memory encodedMessage + Message memory m ) external { - TransceiverStructs.TransceiverMessage memory parsedTransceiverMessage; - TransceiverStructs.NttManagerMessage memory parsedNttManagerMessage; - (parsedTransceiverMessage, parsedNttManagerMessage) = TransceiverStructs - .parseTransceiverAndNttManagerMessage(TEST_TRANSCEIVER_PAYLOAD_PREFIX, encodedMessage); - _deliverToNttManager( - SENDING_CHAIN_ID, - parsedTransceiverMessage.sourceNttManagerAddress, - parsedTransceiverMessage.recipientNttManagerAddress, - parsedNttManagerMessage + IEndpointAdapter(endpoint).attestMessage( + m.srcChain, m.srcAddr, m.sequence, m.dstChain, m.dstAddr, m.payloadHash ); } - - function parseMessageFromLogs( - Vm.Log[] memory logs - ) public pure returns (uint16 recipientChain, bytes memory payload) {} } diff --git a/evm/test/mocks/MockEndpoint.sol b/evm/test/mocks/MockEndpoint.sol new file mode 100644 index 000000000..b19b03feb --- /dev/null +++ b/evm/test/mocks/MockEndpoint.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.19; + +import "example-messaging-endpoint/evm/src/Endpoint.sol"; +import "example-messaging-endpoint/evm/src/libraries/AdapterInstructions.sol"; + +contract MockEndpoint is Endpoint { + constructor( + uint16 _chainId + ) Endpoint(_chainId) {} + + function createAdapterInstructions() public pure returns (bytes memory encoded) { + AdapterInstructions.Instruction[] memory insts = new AdapterInstructions.Instruction[](0); + encoded = AdapterInstructions.encodeInstructions(insts); + } +} diff --git a/evm/test/mocks/MockExecutor.sol b/evm/test/mocks/MockExecutor.sol new file mode 100644 index 000000000..213033fd0 --- /dev/null +++ b/evm/test/mocks/MockExecutor.sol @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache 2 +pragma solidity ^0.8.19; + +import "example-messaging-executor/evm/src/Executor.sol"; + +contract MockExecutor is Executor { + constructor( + uint16 _chainId + ) Executor(_chainId) {} + + function chainId() public view returns (uint16) { + return ourChain; + } + + // NOTE: This was copied from the tests in the executor repo. + function encodeSignedQuoteHeader( + Executor.SignedQuoteHeader memory signedQuote + ) public pure returns (bytes memory) { + return abi.encodePacked( + signedQuote.prefix, + signedQuote.quoterAddress, + signedQuote.payeeAddress, + signedQuote.srcChain, + signedQuote.dstChain, + signedQuote.expiryTime + ); + } + + function createSignedQuote( + uint16 dstChain + ) public view returns (bytes memory) { + return createSignedQuote(dstChain, 60); + } + + function createSignedQuote( + uint16 dstChain, + uint64 quoteLife + ) public view returns (bytes memory) { + Executor.SignedQuoteHeader memory signedQuote = IExecutor.SignedQuoteHeader({ + prefix: "EQ01", + quoterAddress: address(0), + payeeAddress: bytes32(0), + srcChain: ourChain, + dstChain: dstChain, + expiryTime: uint64(block.timestamp + quoteLife) + }); + return encodeSignedQuoteHeader(signedQuote); + } + + function createRelayInstructions() public pure returns (bytes memory) { + return new bytes(0); + } + + function msgValue() public pure returns (uint256) { + return 0; + } +} diff --git a/evm/test/mocks/MockNttManager.sol b/evm/test/mocks/MockNttManager.sol index 6c3fd09fe..baa7fa78b 100644 --- a/evm/test/mocks/MockNttManager.sol +++ b/evm/test/mocks/MockNttManager.sol @@ -7,12 +7,14 @@ import "../../src/NttManager/NttManagerNoRateLimiting.sol"; contract MockNttManagerContract is NttManager { constructor( + address endpoint, + address executor, address token, Mode mode, uint16 chainId, uint64 rateLimitDuration, bool skipRateLimiting - ) NttManager(token, mode, chainId, rateLimitDuration, skipRateLimiting) {} + ) NttManager(endpoint, executor, token, mode, chainId, rateLimitDuration, skipRateLimiting) {} /// We create a dummy storage variable here with standard solidity slot assignment. /// Then we check that its assigned slot is 0, i.e. that the super contract doesn't @@ -25,14 +27,43 @@ contract MockNttManagerContract is NttManager { result := my_slot.slot } } + + function removeChainEnabledForReceive( + uint16 chain + ) public { + _removeChainEnabledForReceive(chain); + } + + function addChainEnabledForReceive( + uint16 chain + ) public { + _addChainEnabledForReceive(chain); + } + + function getChainsEnabledForReceive() public pure returns (uint16[] memory result) { + result = _getChainsEnabledForReceiveStorage(); + } + + function setGasLimitToZero( + uint16 peerChainId + ) external onlyOwner { + _getPeersStorage()[peerChainId].gasLimit = 0; + } + + struct OldNttManagerPeer { + bytes32 peerAddress; + uint8 tokenDecimals; + } } contract MockNttManagerNoRateLimitingContract is NttManagerNoRateLimiting { constructor( + address endpoint, + address executor, address token, Mode mode, uint16 chainId - ) NttManagerNoRateLimiting(token, mode, chainId) {} + ) NttManagerNoRateLimiting(endpoint, executor, token, mode, chainId) {} /// We create a dummy storage variable here with standard solidity slot assignment. /// Then we check that its assigned slot is 0, i.e. that the super contract doesn't @@ -50,16 +81,17 @@ contract MockNttManagerNoRateLimitingContract is NttManagerNoRateLimiting { contract MockNttManagerMigrateBasic is NttManager { // Call the parents constructor constructor( + address endpoint, + address executor, address token, Mode mode, uint16 chainId, uint64 rateLimitDuration, bool skipRateLimiting - ) NttManager(token, mode, chainId, rateLimitDuration, skipRateLimiting) {} + ) NttManager(endpoint, executor, token, mode, chainId, rateLimitDuration, skipRateLimiting) {} function _migrate() internal view override { _checkThresholdInvariants(); - _checkTransceiversInvariants(); revert("Proper migrate called"); } } @@ -67,23 +99,27 @@ contract MockNttManagerMigrateBasic is NttManager { contract MockNttManagerImmutableCheck is NttManager { // Call the parents constructor constructor( + address endpoint, + address executor, address token, Mode mode, uint16 chainId, uint64 rateLimitDuration, bool skipRateLimiting - ) NttManager(token, mode, chainId, rateLimitDuration, skipRateLimiting) {} + ) NttManager(endpoint, executor, token, mode, chainId, rateLimitDuration, skipRateLimiting) {} } contract MockNttManagerImmutableRemoveCheck is NttManager { // Call the parents constructor constructor( + address endpoint, + address executor, address token, Mode mode, uint16 chainId, uint64 rateLimitDuration, bool skipRateLimiting - ) NttManager(token, mode, chainId, rateLimitDuration, skipRateLimiting) {} + ) NttManager(endpoint, executor, token, mode, chainId, rateLimitDuration, skipRateLimiting) {} // Turns on the capability to EDIT the immutables function _migrate() internal override { @@ -98,12 +134,14 @@ contract MockNttManagerStorageLayoutChange is NttManager { // Call the parents constructor constructor( + address endpoint, + address executor, address token, Mode mode, uint16 chainId, uint64 rateLimitDuration, bool skipRateLimiting - ) NttManager(token, mode, chainId, rateLimitDuration, skipRateLimiting) {} + ) NttManager(endpoint, executor, token, mode, chainId, rateLimitDuration, skipRateLimiting) {} function setData() public { a = address(0x1); diff --git a/evm/test/mocks/MockNttManagerAdditionalPayload.sol b/evm/test/mocks/MockNttManagerAdditionalPayload.sol index a313c3d86..8ef9a9de8 100644 --- a/evm/test/mocks/MockNttManagerAdditionalPayload.sol +++ b/evm/test/mocks/MockNttManagerAdditionalPayload.sol @@ -6,10 +6,12 @@ import "../../src/NttManager/NttManagerNoRateLimiting.sol"; contract MockNttManagerAdditionalPayloadContract is NttManagerNoRateLimiting { constructor( + address endpoint, + address executor, address token, Mode mode, uint16 chainId - ) NttManagerNoRateLimiting(token, mode, chainId) {} + ) NttManagerNoRateLimiting(endpoint, executor, token, mode, chainId) {} event AdditionalPayloadSent(bytes payload); event AdditionalPayloadReceived(bytes payload); diff --git a/evm/test/mocks/MockTransceivers.sol b/evm/test/mocks/MockTransceivers.sol deleted file mode 100644 index 742270a18..000000000 --- a/evm/test/mocks/MockTransceivers.sol +++ /dev/null @@ -1,113 +0,0 @@ -// SPDX-License-Identifier: Apache 2 - -pragma solidity >=0.8.8 <0.9.0; - -import "../../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol"; - -contract MockWormholeTransceiverContract is WormholeTransceiver { - constructor( - address nttManager, - address wormholeCoreBridge, - address wormholeRelayerAddr, - address specialRelayerAddr, - uint8 _consistencyLevel, - uint256 _gasLimit - ) - WormholeTransceiver( - nttManager, - wormholeCoreBridge, - wormholeRelayerAddr, - specialRelayerAddr, - _consistencyLevel, - _gasLimit - ) - {} - - /// @dev Override the [`transferOwnership`] method from OwnableUpgradeable - /// to ensure owner of this contract is in sync with the onwer of the NttManager contract. - function transferOwnership( - address newOwner - ) public view override onlyOwner { - revert CannotTransferTransceiverOwnership(owner(), newOwner); - } -} - -contract MockWormholeTransceiverMigrateBasic is WormholeTransceiver { - constructor( - address nttManager, - address wormholeCoreBridge, - address wormholeRelayerAddr, - address specialRelayerAddr, - uint8 _consistencyLevel, - uint256 _gasLimit - ) - WormholeTransceiver( - nttManager, - wormholeCoreBridge, - wormholeRelayerAddr, - specialRelayerAddr, - _consistencyLevel, - _gasLimit - ) - {} - - function _migrate() internal pure override { - revert("Proper migrate called"); - } -} - -contract MockWormholeTransceiverImmutableAllow is WormholeTransceiver { - constructor( - address nttManager, - address wormholeCoreBridge, - address wormholeRelayerAddr, - address specialRelayerAddr, - uint8 _consistencyLevel, - uint256 _gasLimit - ) - WormholeTransceiver( - nttManager, - wormholeCoreBridge, - wormholeRelayerAddr, - specialRelayerAddr, - _consistencyLevel, - _gasLimit - ) - {} - - // Allow for the immutables to be migrated - function _migrate() internal override { - _setMigratesImmutables(true); - } -} - -contract MockWormholeTransceiverLayoutChange is WormholeTransceiver { - address a; - address b; - address c; - - // Call the parents constructor - constructor( - address nttManager, - address wormholeCoreBridge, - address wormholeRelayerAddr, - address specialRelayerAddr, - uint8 _consistencyLevel, - uint256 _gasLimit - ) - WormholeTransceiver( - nttManager, - wormholeCoreBridge, - wormholeRelayerAddr, - specialRelayerAddr, - _consistencyLevel, - _gasLimit - ) - {} - - function setData() public { - a = address(0x1); - b = address(0x2); - c = address(0x3); - } -} From 04afa0f9685e58d9bef2161aa044226caed84f6e Mon Sep 17 00:00:00 2001 From: Evan Gray Date: Mon, 16 Dec 2024 15:43:14 -0500 Subject: [PATCH 3/4] tilt: e2e with mm and executor --- Tiltfile | 59 +- evm/lib/example-messaging-executor | 2 +- evm/src/NttManager/NttManager.sol | 20 +- evm/ts/package.json | 2 +- evm/ts/src/bindings.ts | 10 +- .../src/ethers-contracts/2_0_0/NttManager.ts | 1849 ++++++++++++++ evm/ts/src/ethers-contracts/2_0_0/common.ts | 131 + .../2_0_0/factories/NttManager__factory.ts | 2147 +++++++++++++++++ .../ethers-contracts/2_0_0/factories/index.ts | 4 + evm/ts/src/ethers-contracts/2_0_0/index.ts | 6 + evm/ts/src/ethers-contracts/index.ts | 9 +- evm/ts/src/executor/BinaryReader.ts | 106 + evm/ts/src/executor/BinaryWriter.ts | 91 + evm/ts/src/executor/fetch.ts | 23 + evm/ts/src/executor/relayInstructions.ts | 92 + evm/ts/src/ntt.ts | 128 +- package-lock.json | 7 +- package.json | 1 + sdk/Dockerfile | 3 +- sdk/__tests__/index.test.ts | 16 +- sdk/__tests__/utils.ts | 352 +-- sdk/definitions/src/ntt.ts | 4 +- solana/tests/anchor.test.ts | 2 +- solana/ts/sdk/ntt.ts | 2 + tilt/Dockerfile.deploy | 40 + tilt/Dockerfile.executor | 13 + tilt/deploy.sh | 29 + tilt/executor-deploy.yaml | 24 + tilt/executor.yaml | 48 + 29 files changed, 4950 insertions(+), 270 deletions(-) create mode 100644 evm/ts/src/ethers-contracts/2_0_0/NttManager.ts create mode 100644 evm/ts/src/ethers-contracts/2_0_0/common.ts create mode 100644 evm/ts/src/ethers-contracts/2_0_0/factories/NttManager__factory.ts create mode 100644 evm/ts/src/ethers-contracts/2_0_0/factories/index.ts create mode 100644 evm/ts/src/ethers-contracts/2_0_0/index.ts create mode 100644 evm/ts/src/executor/BinaryReader.ts create mode 100644 evm/ts/src/executor/BinaryWriter.ts create mode 100644 evm/ts/src/executor/fetch.ts create mode 100644 evm/ts/src/executor/relayInstructions.ts create mode 100644 tilt/Dockerfile.deploy create mode 100644 tilt/Dockerfile.executor create mode 100644 tilt/deploy.sh create mode 100644 tilt/executor-deploy.yaml create mode 100644 tilt/executor.yaml diff --git a/Tiltfile b/Tiltfile index 0655dcba9..526d86b3e 100644 --- a/Tiltfile +++ b/Tiltfile @@ -14,20 +14,20 @@ docker_build( ignore=["./sdk/__tests__", "./sdk/Dockerfile", "./sdk/ci.yaml", "./sdk/**/dist", "./sdk/node_modules", "./sdk/**/node_modules"], dockerfile = "./solana/Dockerfile", ) -docker_build( - ref = "solana-test-validator", - context = "solana", - dockerfile = "solana/Dockerfile.test-validator" -) -k8s_yaml_with_ns("./solana/solana-devnet.yaml") -k8s_resource( - "solana-devnet", - labels = ["anchor-ntt"], - port_forwards = [ - port_forward(8899, name = "Solana RPC [:8899]"), - port_forward(8900, name = "Solana WS [:8900]"), - ], -) +# docker_build( +# ref = "solana-test-validator", +# context = "solana", +# dockerfile = "solana/Dockerfile.test-validator" +# ) +# k8s_yaml_with_ns("./solana/solana-devnet.yaml") +# k8s_resource( +# "solana-devnet", +# labels = ["anchor-ntt"], +# port_forwards = [ +# port_forward(8899, name = "Solana RPC [:8899]"), +# port_forward(8900, name = "Solana WS [:8900]"), +# ], +# ) # EVM build docker_build( @@ -36,6 +36,35 @@ docker_build( dockerfile = "./evm/Dockerfile", ) +# MM and Executor Contract Deploy / Setup +docker_build( + ref = "executor-deploy", + context = "tilt", + dockerfile = "tilt/Dockerfile.deploy", + only = ["deploy.sh"] +) +docker_build( + ref = "executor", + context = "tilt", + dockerfile = "tilt/Dockerfile.executor", + only = [], +) +k8s_yaml_with_ns("tilt/executor-deploy.yaml") +k8s_resource( + "executor-deploy", + labels = ["executor"], + resource_deps = ["eth-devnet", "eth-devnet2"], +) +k8s_yaml_with_ns("tilt/executor.yaml") +k8s_resource( + "executor", + labels = ["executor"], + resource_deps = ["eth-devnet", "eth-devnet2", "guardian", "executor-deploy"], + port_forwards = [ + port_forward(3000, name = "Executor [:3000]"), + ], +) + # CI tests docker_build( ref = "ntt-ci", @@ -47,5 +76,5 @@ k8s_yaml_with_ns("./sdk/ci.yaml") k8s_resource( "ntt-ci-tests", labels = ["ntt"], - resource_deps = ["eth-devnet", "eth-devnet2", "solana-devnet", "guardian", "relayer-engine", "wormchain"], + resource_deps = ["eth-devnet", "eth-devnet2", "solana-devnet", "guardian", "relayer-engine", "wormchain", "executor"], ) diff --git a/evm/lib/example-messaging-executor b/evm/lib/example-messaging-executor index d6125d67a..57c49d9ad 160000 --- a/evm/lib/example-messaging-executor +++ b/evm/lib/example-messaging-executor @@ -1 +1 @@ -Subproject commit d6125d67ac3f68c65045e954941cf3fd2d78387a +Subproject commit 57c49d9ad7c410b9a7f938e07a5444f07872159d diff --git a/evm/src/NttManager/NttManager.sol b/evm/src/NttManager/NttManager.sol index 17ee9bb17..78f9f3fb8 100644 --- a/evm/src/NttManager/NttManager.sol +++ b/evm/src/NttManager/NttManager.sol @@ -42,7 +42,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { using TrimmedAmountLib for uint256; using TrimmedAmountLib for TrimmedAmount; - string public constant NTT_MANAGER_VERSION = "1.1.0"; + string public constant NTT_MANAGER_VERSION = "2.0.0"; // =============== Setup ================================================================= @@ -597,7 +597,8 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { checkFork(evmChainId); // Compute the quote price and refund user excess value from msg.value - uint256 epTotalPriceQuote = quoteAndRefund(recipientChain, transceiverInstructions); + (uint256 epTotalPriceQuote, uint256 excessValue) = + quoteEndpoint(recipientChain, transceiverInstructions); return _transfer( _TransferArgs({ @@ -610,7 +611,8 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { executorQuote: executorQuote, relayInstructions: relayInstructions, transceiverInstructions: transceiverInstructions, - epTotalPriceQuote: epTotalPriceQuote + epTotalPriceQuote: epTotalPriceQuote, + excessValue: excessValue }) ); } @@ -627,6 +629,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { bytes relayInstructions; bytes transceiverInstructions; uint256 epTotalPriceQuote; + uint256 excessValue; } function _transfer( @@ -667,7 +670,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { relayInstructions = abi.encodePacked(relayInstructions, args.relayInstructions); } - executor.requestExecution( + executor.requestExecution{value: args.excessValue}( args.recipientChain, peerData.peerAddress, UniversalAddressLibrary.fromBytes32(args.refundAddress).toAddress(), @@ -682,15 +685,12 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { return args.sequence; } - function quoteAndRefund( + function quoteEndpoint( uint16 recipientChain, bytes memory transceiverInstructions - ) internal returns (uint256 epTotalPriceQuote) { + ) internal returns (uint256 epTotalPriceQuote, uint256 excessValue) { epTotalPriceQuote = quoteDeliveryPrice(recipientChain, transceiverInstructions); - uint256 excessValue = msg.value - epTotalPriceQuote; - if (excessValue > 0) { - _refundToSender(excessValue); - } + excessValue = msg.value - epTotalPriceQuote; } function buildEncodedPayload( diff --git a/evm/ts/package.json b/evm/ts/package.json index 07a0e6a1f..43fc8a36c 100644 --- a/evm/ts/package.json +++ b/evm/ts/package.json @@ -37,7 +37,7 @@ "build": "npm run build:esm && npm run build:cjs", "rebuild": "npm run clean && npm run build", "clean": "rm -rf ./dist", - "generate:test": "typechain --node16-modules --target ethers-v6 --out-dir ethers-ci-contracts '../out/**/*.json'", + "generate:test": "typechain --node16-modules --target ethers-v6 --out-dir ethers-ci-contracts '../out/?(WormholeTransceiver.sol|NttManager.sol)/*.json'", "generate:slim": "ABI_VERSION=`tsx scripts/readVersion.ts` && typechain --node16-modules --target ethers-v6 --out-dir src/ethers-contracts/$ABI_VERSION '../out/?(WormholeTransceiver.sol|NttManager.sol)/*.json'", "generate": "npm run generate:slim", "build:contracts": "cd ../.. && make build-evm-prod", diff --git a/evm/ts/src/bindings.ts b/evm/ts/src/bindings.ts index 40beff958..6083be15e 100644 --- a/evm/ts/src/bindings.ts +++ b/evm/ts/src/bindings.ts @@ -1,16 +1,12 @@ import { Provider } from "ethers"; -import { _0_1_0, _1_0_0, _1_1_0 } from "./ethers-contracts/index.js"; +import { _0_1_0, _1_0_0, _1_1_0, _2_0_0 } from "./ethers-contracts/index.js"; import { Ntt } from "@wormhole-foundation/sdk-definitions-ntt"; // This is a descending list of all ABI versions the SDK is aware of. // We check for the first match in descending order, allowing for higher minor and patch versions // being used by the live contract (these are supposed to still be compatible with older ABIs). -export const abiVersions = [ - ["1.1.0", _1_1_0], - ["1.0.0", _1_0_0], - ["0.1.0", _0_1_0], -] as const; +export const abiVersions = [["2.0.0", _2_0_0]] as const; export type AbiVersion = (typeof abiVersions)[number][0]; export interface NttBindings { @@ -32,7 +28,7 @@ export interface NttTransceiverBindings { } export namespace NttManagerBindings { - export type NttManager = ReturnType; + export type NttManager = ReturnType; } export interface NttManagerBindings { diff --git a/evm/ts/src/ethers-contracts/2_0_0/NttManager.ts b/evm/ts/src/ethers-contracts/2_0_0/NttManager.ts new file mode 100644 index 000000000..545673451 --- /dev/null +++ b/evm/ts/src/ethers-contracts/2_0_0/NttManager.ts @@ -0,0 +1,1849 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + BaseContract, + BigNumberish, + BytesLike, + FunctionFragment, + Result, + Interface, + EventFragment, + AddressLike, + ContractRunner, + ContractMethod, + Listener, +} from "ethers"; +import type { + TypedContractEvent, + TypedDeferredTopicFilter, + TypedEventLog, + TypedLogDescription, + TypedListener, + TypedContractMethod, +} from "./common.js"; + +export declare namespace IRateLimiter { + export type RateLimitParamsStruct = { + limit: BigNumberish; + currentCapacity: BigNumberish; + lastTxTimestamp: BigNumberish; + }; + + export type RateLimitParamsStructOutput = [ + limit: bigint, + currentCapacity: bigint, + lastTxTimestamp: bigint + ] & { limit: bigint; currentCapacity: bigint; lastTxTimestamp: bigint }; + + export type InboundQueuedTransferStruct = { + amount: BigNumberish; + txTimestamp: BigNumberish; + recipient: AddressLike; + }; + + export type InboundQueuedTransferStructOutput = [ + amount: bigint, + txTimestamp: bigint, + recipient: string + ] & { amount: bigint; txTimestamp: bigint; recipient: string }; + + export type OutboundQueuedTransferStruct = { + recipient: BytesLike; + refundAddress: BytesLike; + amount: BigNumberish; + txTimestamp: BigNumberish; + recipientChain: BigNumberish; + sender: AddressLike; + executorQuote: BytesLike; + relayInstructions: BytesLike; + transceiverInstructions: BytesLike; + }; + + export type OutboundQueuedTransferStructOutput = [ + recipient: string, + refundAddress: string, + amount: bigint, + txTimestamp: bigint, + recipientChain: bigint, + sender: string, + executorQuote: string, + relayInstructions: string, + transceiverInstructions: string + ] & { + recipient: string; + refundAddress: string; + amount: bigint; + txTimestamp: bigint; + recipientChain: bigint; + sender: string; + executorQuote: string; + relayInstructions: string; + transceiverInstructions: string; + }; +} + +export declare namespace INttManager { + export type NttManagerPeerStruct = { + peerAddress: BytesLike; + tokenDecimals: BigNumberish; + gasLimit: BigNumberish; + }; + + export type NttManagerPeerStructOutput = [ + peerAddress: string, + tokenDecimals: bigint, + gasLimit: bigint + ] & { peerAddress: string; tokenDecimals: bigint; gasLimit: bigint }; +} + +export interface NttManagerInterface extends Interface { + getFunction( + nameOrSignature: + | "NTT_MANAGER_VERSION" + | "cancelOutboundQueuedTransfer" + | "chainId" + | "completeInboundQueuedTransfer" + | "completeOutboundQueuedTransfer" + | "disableRecvTransceiver" + | "disableSendTransceiver" + | "enableRecvTransceiver" + | "enableSendTransceiver" + | "endpoint" + | "executeMsg" + | "executor" + | "getCurrentInboundCapacity" + | "getCurrentOutboundCapacity" + | "getInboundLimitParams" + | "getInboundQueuedTransfer" + | "getMigratesImmutables" + | "getMode" + | "getOutboundLimitParams" + | "getOutboundQueuedTransfer" + | "getPeer" + | "getThreshold" + | "initialize" + | "isMessageApproved" + | "isMessageExecuted" + | "isPaused" + | "messageAttestations" + | "migrate" + | "mode" + | "nextMessageSequence" + | "owner" + | "pause" + | "pauser" + | "quoteDeliveryPrice" + | "rateLimitDuration" + | "setGasLimit" + | "setInboundLimit" + | "setOutboundLimit" + | "setPeer" + | "setThreshold" + | "setTransceiver" + | "token" + | "tokenDecimals" + | "transceiverAttestedToMessage" + | "transfer(uint256,uint16,bytes32,bytes32,bool,bytes,bytes,bytes)" + | "transfer(uint256,uint16,bytes32,bytes,bytes,bytes)" + | "transferOwnership" + | "transferPauserCapability" + | "unpause" + | "upgrade" + ): FunctionFragment; + + getEvent( + nameOrSignatureOrTopic: + | "AdminChanged" + | "BeaconUpgraded" + | "InboundTransferLimitUpdated" + | "InboundTransferQueued" + | "Initialized" + | "MessageAttestedTo" + | "NotPaused" + | "OutboundTransferCancelled" + | "OutboundTransferLimitUpdated" + | "OutboundTransferQueued" + | "OutboundTransferRateLimited" + | "OwnershipTransferred" + | "Paused" + | "PauserTransferred" + | "PeerUpdated" + | "ThresholdChanged" + | "TransceiverAdded" + | "TransferRedeemed" + | "TransferSent(bytes32,bytes32,uint256,uint256,uint16,uint64,bytes32)" + | "TransferSent(bytes32)" + | "Upgraded" + ): EventFragment; + + encodeFunctionData( + functionFragment: "NTT_MANAGER_VERSION", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "cancelOutboundQueuedTransfer", + values: [BigNumberish] + ): string; + encodeFunctionData(functionFragment: "chainId", values?: undefined): string; + encodeFunctionData( + functionFragment: "completeInboundQueuedTransfer", + values: [BytesLike] + ): string; + encodeFunctionData( + functionFragment: "completeOutboundQueuedTransfer", + values: [BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "disableRecvTransceiver", + values: [BigNumberish, AddressLike] + ): string; + encodeFunctionData( + functionFragment: "disableSendTransceiver", + values: [BigNumberish, AddressLike] + ): string; + encodeFunctionData( + functionFragment: "enableRecvTransceiver", + values: [BigNumberish, AddressLike] + ): string; + encodeFunctionData( + functionFragment: "enableSendTransceiver", + values: [BigNumberish, AddressLike] + ): string; + encodeFunctionData(functionFragment: "endpoint", values?: undefined): string; + encodeFunctionData( + functionFragment: "executeMsg", + values: [BigNumberish, BytesLike, BigNumberish, BytesLike] + ): string; + encodeFunctionData(functionFragment: "executor", values?: undefined): string; + encodeFunctionData( + functionFragment: "getCurrentInboundCapacity", + values: [BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "getCurrentOutboundCapacity", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "getInboundLimitParams", + values: [BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "getInboundQueuedTransfer", + values: [BytesLike] + ): string; + encodeFunctionData( + functionFragment: "getMigratesImmutables", + values?: undefined + ): string; + encodeFunctionData(functionFragment: "getMode", values?: undefined): string; + encodeFunctionData( + functionFragment: "getOutboundLimitParams", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "getOutboundQueuedTransfer", + values: [BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "getPeer", + values: [BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "getThreshold", + values: [BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "initialize", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "isMessageApproved", + values: [BigNumberish, BytesLike, BigNumberish, BytesLike, BytesLike] + ): string; + encodeFunctionData( + functionFragment: "isMessageExecuted", + values: [BigNumberish, BytesLike, BigNumberish, BytesLike, BytesLike] + ): string; + encodeFunctionData(functionFragment: "isPaused", values?: undefined): string; + encodeFunctionData( + functionFragment: "messageAttestations", + values: [BigNumberish, BytesLike, BigNumberish, BytesLike, BytesLike] + ): string; + encodeFunctionData(functionFragment: "migrate", values?: undefined): string; + encodeFunctionData(functionFragment: "mode", values?: undefined): string; + encodeFunctionData( + functionFragment: "nextMessageSequence", + values?: undefined + ): string; + encodeFunctionData(functionFragment: "owner", values?: undefined): string; + encodeFunctionData(functionFragment: "pause", values?: undefined): string; + encodeFunctionData(functionFragment: "pauser", values?: undefined): string; + encodeFunctionData( + functionFragment: "quoteDeliveryPrice", + values: [BigNumberish, BytesLike] + ): string; + encodeFunctionData( + functionFragment: "rateLimitDuration", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "setGasLimit", + values: [BigNumberish, BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "setInboundLimit", + values: [BigNumberish, BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "setOutboundLimit", + values: [BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "setPeer", + values: [BigNumberish, BytesLike, BigNumberish, BigNumberish, BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "setThreshold", + values: [BigNumberish, BigNumberish] + ): string; + encodeFunctionData( + functionFragment: "setTransceiver", + values: [AddressLike] + ): string; + encodeFunctionData(functionFragment: "token", values?: undefined): string; + encodeFunctionData( + functionFragment: "tokenDecimals", + values?: undefined + ): string; + encodeFunctionData( + functionFragment: "transceiverAttestedToMessage", + values: [ + BigNumberish, + BytesLike, + BigNumberish, + BytesLike, + BytesLike, + BigNumberish + ] + ): string; + encodeFunctionData( + functionFragment: "transfer(uint256,uint16,bytes32,bytes32,bool,bytes,bytes,bytes)", + values: [ + BigNumberish, + BigNumberish, + BytesLike, + BytesLike, + boolean, + BytesLike, + BytesLike, + BytesLike + ] + ): string; + encodeFunctionData( + functionFragment: "transfer(uint256,uint16,bytes32,bytes,bytes,bytes)", + values: [ + BigNumberish, + BigNumberish, + BytesLike, + BytesLike, + BytesLike, + BytesLike + ] + ): string; + encodeFunctionData( + functionFragment: "transferOwnership", + values: [AddressLike] + ): string; + encodeFunctionData( + functionFragment: "transferPauserCapability", + values: [AddressLike] + ): string; + encodeFunctionData(functionFragment: "unpause", values?: undefined): string; + encodeFunctionData( + functionFragment: "upgrade", + values: [AddressLike] + ): string; + + decodeFunctionResult( + functionFragment: "NTT_MANAGER_VERSION", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "cancelOutboundQueuedTransfer", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "chainId", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "completeInboundQueuedTransfer", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "completeOutboundQueuedTransfer", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "disableRecvTransceiver", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "disableSendTransceiver", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "enableRecvTransceiver", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "enableSendTransceiver", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "endpoint", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "executeMsg", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "executor", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "getCurrentInboundCapacity", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "getCurrentOutboundCapacity", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "getInboundLimitParams", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "getInboundQueuedTransfer", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "getMigratesImmutables", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "getMode", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "getOutboundLimitParams", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "getOutboundQueuedTransfer", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "getPeer", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "getThreshold", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "initialize", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "isMessageApproved", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "isMessageExecuted", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "isPaused", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "messageAttestations", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "migrate", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "mode", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "nextMessageSequence", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "owner", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "pause", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "pauser", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "quoteDeliveryPrice", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "rateLimitDuration", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "setGasLimit", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "setInboundLimit", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "setOutboundLimit", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "setPeer", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "setThreshold", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "setTransceiver", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "token", data: BytesLike): Result; + decodeFunctionResult( + functionFragment: "tokenDecimals", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "transceiverAttestedToMessage", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "transfer(uint256,uint16,bytes32,bytes32,bool,bytes,bytes,bytes)", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "transfer(uint256,uint16,bytes32,bytes,bytes,bytes)", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "transferOwnership", + data: BytesLike + ): Result; + decodeFunctionResult( + functionFragment: "transferPauserCapability", + data: BytesLike + ): Result; + decodeFunctionResult(functionFragment: "unpause", data: BytesLike): Result; + decodeFunctionResult(functionFragment: "upgrade", data: BytesLike): Result; +} + +export namespace AdminChangedEvent { + export type InputTuple = [previousAdmin: AddressLike, newAdmin: AddressLike]; + export type OutputTuple = [previousAdmin: string, newAdmin: string]; + export interface OutputObject { + previousAdmin: string; + newAdmin: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace BeaconUpgradedEvent { + export type InputTuple = [beacon: AddressLike]; + export type OutputTuple = [beacon: string]; + export interface OutputObject { + beacon: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace InboundTransferLimitUpdatedEvent { + export type InputTuple = [ + chainId: BigNumberish, + oldLimit: BigNumberish, + newLimit: BigNumberish + ]; + export type OutputTuple = [ + chainId: bigint, + oldLimit: bigint, + newLimit: bigint + ]; + export interface OutputObject { + chainId: bigint; + oldLimit: bigint; + newLimit: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace InboundTransferQueuedEvent { + export type InputTuple = [digest: BytesLike]; + export type OutputTuple = [digest: string]; + export interface OutputObject { + digest: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace InitializedEvent { + export type InputTuple = [version: BigNumberish]; + export type OutputTuple = [version: bigint]; + export interface OutputObject { + version: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace MessageAttestedToEvent { + export type InputTuple = [ + digest: BytesLike, + transceiver: AddressLike, + index: BigNumberish + ]; + export type OutputTuple = [ + digest: string, + transceiver: string, + index: bigint + ]; + export interface OutputObject { + digest: string; + transceiver: string; + index: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace NotPausedEvent { + export type InputTuple = [notPaused: boolean]; + export type OutputTuple = [notPaused: boolean]; + export interface OutputObject { + notPaused: boolean; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace OutboundTransferCancelledEvent { + export type InputTuple = [ + sequence: BigNumberish, + recipient: AddressLike, + amount: BigNumberish + ]; + export type OutputTuple = [ + sequence: bigint, + recipient: string, + amount: bigint + ]; + export interface OutputObject { + sequence: bigint; + recipient: string; + amount: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace OutboundTransferLimitUpdatedEvent { + export type InputTuple = [oldLimit: BigNumberish, newLimit: BigNumberish]; + export type OutputTuple = [oldLimit: bigint, newLimit: bigint]; + export interface OutputObject { + oldLimit: bigint; + newLimit: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace OutboundTransferQueuedEvent { + export type InputTuple = [queueSequence: BigNumberish]; + export type OutputTuple = [queueSequence: bigint]; + export interface OutputObject { + queueSequence: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace OutboundTransferRateLimitedEvent { + export type InputTuple = [ + sender: AddressLike, + sequence: BigNumberish, + amount: BigNumberish, + currentCapacity: BigNumberish + ]; + export type OutputTuple = [ + sender: string, + sequence: bigint, + amount: bigint, + currentCapacity: bigint + ]; + export interface OutputObject { + sender: string; + sequence: bigint; + amount: bigint; + currentCapacity: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace OwnershipTransferredEvent { + export type InputTuple = [previousOwner: AddressLike, newOwner: AddressLike]; + export type OutputTuple = [previousOwner: string, newOwner: string]; + export interface OutputObject { + previousOwner: string; + newOwner: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace PausedEvent { + export type InputTuple = [paused: boolean]; + export type OutputTuple = [paused: boolean]; + export interface OutputObject { + paused: boolean; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace PauserTransferredEvent { + export type InputTuple = [oldPauser: AddressLike, newPauser: AddressLike]; + export type OutputTuple = [oldPauser: string, newPauser: string]; + export interface OutputObject { + oldPauser: string; + newPauser: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace PeerUpdatedEvent { + export type InputTuple = [ + chainId_: BigNumberish, + oldPeerContract: BytesLike, + oldPeerDecimals: BigNumberish, + peerContract: BytesLike, + peerDecimals: BigNumberish + ]; + export type OutputTuple = [ + chainId_: bigint, + oldPeerContract: string, + oldPeerDecimals: bigint, + peerContract: string, + peerDecimals: bigint + ]; + export interface OutputObject { + chainId_: bigint; + oldPeerContract: string; + oldPeerDecimals: bigint; + peerContract: string; + peerDecimals: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace ThresholdChangedEvent { + export type InputTuple = [ + chainId: BigNumberish, + oldThreshold: BigNumberish, + threshold: BigNumberish + ]; + export type OutputTuple = [ + chainId: bigint, + oldThreshold: bigint, + threshold: bigint + ]; + export interface OutputObject { + chainId: bigint; + oldThreshold: bigint; + threshold: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace TransceiverAddedEvent { + export type InputTuple = [ + transceiver: AddressLike, + transceiversNum: BigNumberish + ]; + export type OutputTuple = [transceiver: string, transceiversNum: bigint]; + export interface OutputObject { + transceiver: string; + transceiversNum: bigint; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace TransferRedeemedEvent { + export type InputTuple = [digest: BytesLike]; + export type OutputTuple = [digest: string]; + export interface OutputObject { + digest: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace TransferSent_bytes32_bytes32_uint256_uint256_uint16_uint64_bytes32_Event { + export type InputTuple = [ + recipient: BytesLike, + refundAddress: BytesLike, + amount: BigNumberish, + fee: BigNumberish, + recipientChain: BigNumberish, + msgSequence: BigNumberish, + msgHash: BytesLike + ]; + export type OutputTuple = [ + recipient: string, + refundAddress: string, + amount: bigint, + fee: bigint, + recipientChain: bigint, + msgSequence: bigint, + msgHash: string + ]; + export interface OutputObject { + recipient: string; + refundAddress: string; + amount: bigint; + fee: bigint; + recipientChain: bigint; + msgSequence: bigint; + msgHash: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace TransferSent_bytes32_Event { + export type InputTuple = [digest: BytesLike]; + export type OutputTuple = [digest: string]; + export interface OutputObject { + digest: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export namespace UpgradedEvent { + export type InputTuple = [implementation: AddressLike]; + export type OutputTuple = [implementation: string]; + export interface OutputObject { + implementation: string; + } + export type Event = TypedContractEvent; + export type Filter = TypedDeferredTopicFilter; + export type Log = TypedEventLog; + export type LogDescription = TypedLogDescription; +} + +export interface NttManager extends BaseContract { + connect(runner?: ContractRunner | null): NttManager; + waitForDeployment(): Promise; + + interface: NttManagerInterface; + + queryFilter( + event: TCEvent, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>>; + queryFilter( + filter: TypedDeferredTopicFilter, + fromBlockOrBlockhash?: string | number | undefined, + toBlock?: string | number | undefined + ): Promise>>; + + on( + event: TCEvent, + listener: TypedListener + ): Promise; + on( + filter: TypedDeferredTopicFilter, + listener: TypedListener + ): Promise; + + once( + event: TCEvent, + listener: TypedListener + ): Promise; + once( + filter: TypedDeferredTopicFilter, + listener: TypedListener + ): Promise; + + listeners( + event: TCEvent + ): Promise>>; + listeners(eventName?: string): Promise>; + removeAllListeners( + event?: TCEvent + ): Promise; + + NTT_MANAGER_VERSION: TypedContractMethod<[], [string], "view">; + + cancelOutboundQueuedTransfer: TypedContractMethod< + [messageSequence: BigNumberish], + [void], + "nonpayable" + >; + + chainId: TypedContractMethod<[], [bigint], "view">; + + completeInboundQueuedTransfer: TypedContractMethod< + [digest: BytesLike], + [void], + "nonpayable" + >; + + completeOutboundQueuedTransfer: TypedContractMethod< + [messageSequence: BigNumberish], + [bigint], + "payable" + >; + + disableRecvTransceiver: TypedContractMethod< + [chain: BigNumberish, transceiver: AddressLike], + [void], + "nonpayable" + >; + + disableSendTransceiver: TypedContractMethod< + [chain: BigNumberish, transceiver: AddressLike], + [void], + "nonpayable" + >; + + enableRecvTransceiver: TypedContractMethod< + [chain: BigNumberish, transceiver: AddressLike], + [void], + "nonpayable" + >; + + enableSendTransceiver: TypedContractMethod< + [chain: BigNumberish, transceiver: AddressLike], + [void], + "nonpayable" + >; + + endpoint: TypedContractMethod<[], [string], "view">; + + executeMsg: TypedContractMethod< + [ + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + epSeq: BigNumberish, + payload: BytesLike + ], + [void], + "nonpayable" + >; + + executor: TypedContractMethod<[], [string], "view">; + + getCurrentInboundCapacity: TypedContractMethod< + [chainId_: BigNumberish], + [bigint], + "view" + >; + + getCurrentOutboundCapacity: TypedContractMethod<[], [bigint], "view">; + + getInboundLimitParams: TypedContractMethod< + [chainId_: BigNumberish], + [IRateLimiter.RateLimitParamsStructOutput], + "view" + >; + + getInboundQueuedTransfer: TypedContractMethod< + [digest: BytesLike], + [IRateLimiter.InboundQueuedTransferStructOutput], + "view" + >; + + getMigratesImmutables: TypedContractMethod<[], [boolean], "view">; + + getMode: TypedContractMethod<[], [bigint], "view">; + + getOutboundLimitParams: TypedContractMethod< + [], + [IRateLimiter.RateLimitParamsStructOutput], + "view" + >; + + getOutboundQueuedTransfer: TypedContractMethod< + [queueSequence: BigNumberish], + [IRateLimiter.OutboundQueuedTransferStructOutput], + "view" + >; + + getPeer: TypedContractMethod< + [chainId_: BigNumberish], + [INttManager.NttManagerPeerStructOutput], + "view" + >; + + getThreshold: TypedContractMethod<[chain: BigNumberish], [bigint], "view">; + + initialize: TypedContractMethod<[], [void], "payable">; + + isMessageApproved: TypedContractMethod< + [ + srcChain: BigNumberish, + srcAddr: BytesLike, + sequence: BigNumberish, + dstAddr: BytesLike, + payloadHash: BytesLike + ], + [boolean], + "view" + >; + + isMessageExecuted: TypedContractMethod< + [ + srcChain: BigNumberish, + srcAddr: BytesLike, + sequence: BigNumberish, + dstAddr: BytesLike, + payloadHash: BytesLike + ], + [boolean], + "view" + >; + + isPaused: TypedContractMethod<[], [boolean], "view">; + + messageAttestations: TypedContractMethod< + [ + srcChain: BigNumberish, + srcAddr: BytesLike, + sequence: BigNumberish, + dstAddr: BytesLike, + payloadHash: BytesLike + ], + [bigint], + "view" + >; + + migrate: TypedContractMethod<[], [void], "nonpayable">; + + mode: TypedContractMethod<[], [bigint], "view">; + + nextMessageSequence: TypedContractMethod<[], [bigint], "view">; + + owner: TypedContractMethod<[], [string], "view">; + + pause: TypedContractMethod<[], [void], "nonpayable">; + + pauser: TypedContractMethod<[], [string], "view">; + + quoteDeliveryPrice: TypedContractMethod< + [recipientChain: BigNumberish, transceiverInstructions: BytesLike], + [bigint], + "view" + >; + + rateLimitDuration: TypedContractMethod<[], [bigint], "view">; + + setGasLimit: TypedContractMethod< + [peerChainId: BigNumberish, gasLimit: BigNumberish], + [void], + "nonpayable" + >; + + setInboundLimit: TypedContractMethod< + [limit: BigNumberish, chainId_: BigNumberish], + [void], + "nonpayable" + >; + + setOutboundLimit: TypedContractMethod< + [limit: BigNumberish], + [void], + "nonpayable" + >; + + setPeer: TypedContractMethod< + [ + peerChainId: BigNumberish, + peerContract: BytesLike, + decimals: BigNumberish, + gasLimit: BigNumberish, + inboundLimit: BigNumberish + ], + [void], + "nonpayable" + >; + + setThreshold: TypedContractMethod< + [chain: BigNumberish, threshold: BigNumberish], + [void], + "nonpayable" + >; + + setTransceiver: TypedContractMethod< + [transceiver: AddressLike], + [void], + "nonpayable" + >; + + token: TypedContractMethod<[], [string], "view">; + + tokenDecimals: TypedContractMethod<[], [bigint], "view">; + + transceiverAttestedToMessage: TypedContractMethod< + [ + srcChain: BigNumberish, + srcAddr: BytesLike, + sequence: BigNumberish, + dstAddr: BytesLike, + payloadHash: BytesLike, + index: BigNumberish + ], + [boolean], + "view" + >; + + "transfer(uint256,uint16,bytes32,bytes32,bool,bytes,bytes,bytes)": TypedContractMethod< + [ + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + refundAddress: BytesLike, + shouldQueue: boolean, + executorQuote: BytesLike, + relayInstructions: BytesLike, + transceiverInstructions: BytesLike + ], + [bigint], + "payable" + >; + + "transfer(uint256,uint16,bytes32,bytes,bytes,bytes)": TypedContractMethod< + [ + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + executorQuote: BytesLike, + relayInstructions: BytesLike, + transceiverInstructions: BytesLike + ], + [bigint], + "payable" + >; + + transferOwnership: TypedContractMethod< + [newOwner: AddressLike], + [void], + "nonpayable" + >; + + transferPauserCapability: TypedContractMethod< + [newPauser: AddressLike], + [void], + "nonpayable" + >; + + unpause: TypedContractMethod<[], [void], "nonpayable">; + + upgrade: TypedContractMethod< + [newImplementation: AddressLike], + [void], + "nonpayable" + >; + + getFunction( + key: string | FunctionFragment + ): T; + + getFunction( + nameOrSignature: "NTT_MANAGER_VERSION" + ): TypedContractMethod<[], [string], "view">; + getFunction( + nameOrSignature: "cancelOutboundQueuedTransfer" + ): TypedContractMethod<[messageSequence: BigNumberish], [void], "nonpayable">; + getFunction( + nameOrSignature: "chainId" + ): TypedContractMethod<[], [bigint], "view">; + getFunction( + nameOrSignature: "completeInboundQueuedTransfer" + ): TypedContractMethod<[digest: BytesLike], [void], "nonpayable">; + getFunction( + nameOrSignature: "completeOutboundQueuedTransfer" + ): TypedContractMethod<[messageSequence: BigNumberish], [bigint], "payable">; + getFunction( + nameOrSignature: "disableRecvTransceiver" + ): TypedContractMethod< + [chain: BigNumberish, transceiver: AddressLike], + [void], + "nonpayable" + >; + getFunction( + nameOrSignature: "disableSendTransceiver" + ): TypedContractMethod< + [chain: BigNumberish, transceiver: AddressLike], + [void], + "nonpayable" + >; + getFunction( + nameOrSignature: "enableRecvTransceiver" + ): TypedContractMethod< + [chain: BigNumberish, transceiver: AddressLike], + [void], + "nonpayable" + >; + getFunction( + nameOrSignature: "enableSendTransceiver" + ): TypedContractMethod< + [chain: BigNumberish, transceiver: AddressLike], + [void], + "nonpayable" + >; + getFunction( + nameOrSignature: "endpoint" + ): TypedContractMethod<[], [string], "view">; + getFunction( + nameOrSignature: "executeMsg" + ): TypedContractMethod< + [ + sourceChainId: BigNumberish, + sourceNttManagerAddress: BytesLike, + epSeq: BigNumberish, + payload: BytesLike + ], + [void], + "nonpayable" + >; + getFunction( + nameOrSignature: "executor" + ): TypedContractMethod<[], [string], "view">; + getFunction( + nameOrSignature: "getCurrentInboundCapacity" + ): TypedContractMethod<[chainId_: BigNumberish], [bigint], "view">; + getFunction( + nameOrSignature: "getCurrentOutboundCapacity" + ): TypedContractMethod<[], [bigint], "view">; + getFunction( + nameOrSignature: "getInboundLimitParams" + ): TypedContractMethod< + [chainId_: BigNumberish], + [IRateLimiter.RateLimitParamsStructOutput], + "view" + >; + getFunction( + nameOrSignature: "getInboundQueuedTransfer" + ): TypedContractMethod< + [digest: BytesLike], + [IRateLimiter.InboundQueuedTransferStructOutput], + "view" + >; + getFunction( + nameOrSignature: "getMigratesImmutables" + ): TypedContractMethod<[], [boolean], "view">; + getFunction( + nameOrSignature: "getMode" + ): TypedContractMethod<[], [bigint], "view">; + getFunction( + nameOrSignature: "getOutboundLimitParams" + ): TypedContractMethod< + [], + [IRateLimiter.RateLimitParamsStructOutput], + "view" + >; + getFunction( + nameOrSignature: "getOutboundQueuedTransfer" + ): TypedContractMethod< + [queueSequence: BigNumberish], + [IRateLimiter.OutboundQueuedTransferStructOutput], + "view" + >; + getFunction( + nameOrSignature: "getPeer" + ): TypedContractMethod< + [chainId_: BigNumberish], + [INttManager.NttManagerPeerStructOutput], + "view" + >; + getFunction( + nameOrSignature: "getThreshold" + ): TypedContractMethod<[chain: BigNumberish], [bigint], "view">; + getFunction( + nameOrSignature: "initialize" + ): TypedContractMethod<[], [void], "payable">; + getFunction( + nameOrSignature: "isMessageApproved" + ): TypedContractMethod< + [ + srcChain: BigNumberish, + srcAddr: BytesLike, + sequence: BigNumberish, + dstAddr: BytesLike, + payloadHash: BytesLike + ], + [boolean], + "view" + >; + getFunction( + nameOrSignature: "isMessageExecuted" + ): TypedContractMethod< + [ + srcChain: BigNumberish, + srcAddr: BytesLike, + sequence: BigNumberish, + dstAddr: BytesLike, + payloadHash: BytesLike + ], + [boolean], + "view" + >; + getFunction( + nameOrSignature: "isPaused" + ): TypedContractMethod<[], [boolean], "view">; + getFunction( + nameOrSignature: "messageAttestations" + ): TypedContractMethod< + [ + srcChain: BigNumberish, + srcAddr: BytesLike, + sequence: BigNumberish, + dstAddr: BytesLike, + payloadHash: BytesLike + ], + [bigint], + "view" + >; + getFunction( + nameOrSignature: "migrate" + ): TypedContractMethod<[], [void], "nonpayable">; + getFunction( + nameOrSignature: "mode" + ): TypedContractMethod<[], [bigint], "view">; + getFunction( + nameOrSignature: "nextMessageSequence" + ): TypedContractMethod<[], [bigint], "view">; + getFunction( + nameOrSignature: "owner" + ): TypedContractMethod<[], [string], "view">; + getFunction( + nameOrSignature: "pause" + ): TypedContractMethod<[], [void], "nonpayable">; + getFunction( + nameOrSignature: "pauser" + ): TypedContractMethod<[], [string], "view">; + getFunction( + nameOrSignature: "quoteDeliveryPrice" + ): TypedContractMethod< + [recipientChain: BigNumberish, transceiverInstructions: BytesLike], + [bigint], + "view" + >; + getFunction( + nameOrSignature: "rateLimitDuration" + ): TypedContractMethod<[], [bigint], "view">; + getFunction( + nameOrSignature: "setGasLimit" + ): TypedContractMethod< + [peerChainId: BigNumberish, gasLimit: BigNumberish], + [void], + "nonpayable" + >; + getFunction( + nameOrSignature: "setInboundLimit" + ): TypedContractMethod< + [limit: BigNumberish, chainId_: BigNumberish], + [void], + "nonpayable" + >; + getFunction( + nameOrSignature: "setOutboundLimit" + ): TypedContractMethod<[limit: BigNumberish], [void], "nonpayable">; + getFunction( + nameOrSignature: "setPeer" + ): TypedContractMethod< + [ + peerChainId: BigNumberish, + peerContract: BytesLike, + decimals: BigNumberish, + gasLimit: BigNumberish, + inboundLimit: BigNumberish + ], + [void], + "nonpayable" + >; + getFunction( + nameOrSignature: "setThreshold" + ): TypedContractMethod< + [chain: BigNumberish, threshold: BigNumberish], + [void], + "nonpayable" + >; + getFunction( + nameOrSignature: "setTransceiver" + ): TypedContractMethod<[transceiver: AddressLike], [void], "nonpayable">; + getFunction( + nameOrSignature: "token" + ): TypedContractMethod<[], [string], "view">; + getFunction( + nameOrSignature: "tokenDecimals" + ): TypedContractMethod<[], [bigint], "view">; + getFunction( + nameOrSignature: "transceiverAttestedToMessage" + ): TypedContractMethod< + [ + srcChain: BigNumberish, + srcAddr: BytesLike, + sequence: BigNumberish, + dstAddr: BytesLike, + payloadHash: BytesLike, + index: BigNumberish + ], + [boolean], + "view" + >; + getFunction( + nameOrSignature: "transfer(uint256,uint16,bytes32,bytes32,bool,bytes,bytes,bytes)" + ): TypedContractMethod< + [ + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + refundAddress: BytesLike, + shouldQueue: boolean, + executorQuote: BytesLike, + relayInstructions: BytesLike, + transceiverInstructions: BytesLike + ], + [bigint], + "payable" + >; + getFunction( + nameOrSignature: "transfer(uint256,uint16,bytes32,bytes,bytes,bytes)" + ): TypedContractMethod< + [ + amount: BigNumberish, + recipientChain: BigNumberish, + recipient: BytesLike, + executorQuote: BytesLike, + relayInstructions: BytesLike, + transceiverInstructions: BytesLike + ], + [bigint], + "payable" + >; + getFunction( + nameOrSignature: "transferOwnership" + ): TypedContractMethod<[newOwner: AddressLike], [void], "nonpayable">; + getFunction( + nameOrSignature: "transferPauserCapability" + ): TypedContractMethod<[newPauser: AddressLike], [void], "nonpayable">; + getFunction( + nameOrSignature: "unpause" + ): TypedContractMethod<[], [void], "nonpayable">; + getFunction( + nameOrSignature: "upgrade" + ): TypedContractMethod< + [newImplementation: AddressLike], + [void], + "nonpayable" + >; + + getEvent( + key: "AdminChanged" + ): TypedContractEvent< + AdminChangedEvent.InputTuple, + AdminChangedEvent.OutputTuple, + AdminChangedEvent.OutputObject + >; + getEvent( + key: "BeaconUpgraded" + ): TypedContractEvent< + BeaconUpgradedEvent.InputTuple, + BeaconUpgradedEvent.OutputTuple, + BeaconUpgradedEvent.OutputObject + >; + getEvent( + key: "InboundTransferLimitUpdated" + ): TypedContractEvent< + InboundTransferLimitUpdatedEvent.InputTuple, + InboundTransferLimitUpdatedEvent.OutputTuple, + InboundTransferLimitUpdatedEvent.OutputObject + >; + getEvent( + key: "InboundTransferQueued" + ): TypedContractEvent< + InboundTransferQueuedEvent.InputTuple, + InboundTransferQueuedEvent.OutputTuple, + InboundTransferQueuedEvent.OutputObject + >; + getEvent( + key: "Initialized" + ): TypedContractEvent< + InitializedEvent.InputTuple, + InitializedEvent.OutputTuple, + InitializedEvent.OutputObject + >; + getEvent( + key: "MessageAttestedTo" + ): TypedContractEvent< + MessageAttestedToEvent.InputTuple, + MessageAttestedToEvent.OutputTuple, + MessageAttestedToEvent.OutputObject + >; + getEvent( + key: "NotPaused" + ): TypedContractEvent< + NotPausedEvent.InputTuple, + NotPausedEvent.OutputTuple, + NotPausedEvent.OutputObject + >; + getEvent( + key: "OutboundTransferCancelled" + ): TypedContractEvent< + OutboundTransferCancelledEvent.InputTuple, + OutboundTransferCancelledEvent.OutputTuple, + OutboundTransferCancelledEvent.OutputObject + >; + getEvent( + key: "OutboundTransferLimitUpdated" + ): TypedContractEvent< + OutboundTransferLimitUpdatedEvent.InputTuple, + OutboundTransferLimitUpdatedEvent.OutputTuple, + OutboundTransferLimitUpdatedEvent.OutputObject + >; + getEvent( + key: "OutboundTransferQueued" + ): TypedContractEvent< + OutboundTransferQueuedEvent.InputTuple, + OutboundTransferQueuedEvent.OutputTuple, + OutboundTransferQueuedEvent.OutputObject + >; + getEvent( + key: "OutboundTransferRateLimited" + ): TypedContractEvent< + OutboundTransferRateLimitedEvent.InputTuple, + OutboundTransferRateLimitedEvent.OutputTuple, + OutboundTransferRateLimitedEvent.OutputObject + >; + getEvent( + key: "OwnershipTransferred" + ): TypedContractEvent< + OwnershipTransferredEvent.InputTuple, + OwnershipTransferredEvent.OutputTuple, + OwnershipTransferredEvent.OutputObject + >; + getEvent( + key: "Paused" + ): TypedContractEvent< + PausedEvent.InputTuple, + PausedEvent.OutputTuple, + PausedEvent.OutputObject + >; + getEvent( + key: "PauserTransferred" + ): TypedContractEvent< + PauserTransferredEvent.InputTuple, + PauserTransferredEvent.OutputTuple, + PauserTransferredEvent.OutputObject + >; + getEvent( + key: "PeerUpdated" + ): TypedContractEvent< + PeerUpdatedEvent.InputTuple, + PeerUpdatedEvent.OutputTuple, + PeerUpdatedEvent.OutputObject + >; + getEvent( + key: "ThresholdChanged" + ): TypedContractEvent< + ThresholdChangedEvent.InputTuple, + ThresholdChangedEvent.OutputTuple, + ThresholdChangedEvent.OutputObject + >; + getEvent( + key: "TransceiverAdded" + ): TypedContractEvent< + TransceiverAddedEvent.InputTuple, + TransceiverAddedEvent.OutputTuple, + TransceiverAddedEvent.OutputObject + >; + getEvent( + key: "TransferRedeemed" + ): TypedContractEvent< + TransferRedeemedEvent.InputTuple, + TransferRedeemedEvent.OutputTuple, + TransferRedeemedEvent.OutputObject + >; + getEvent( + key: "TransferSent(bytes32,bytes32,uint256,uint256,uint16,uint64,bytes32)" + ): TypedContractEvent< + TransferSent_bytes32_bytes32_uint256_uint256_uint16_uint64_bytes32_Event.InputTuple, + TransferSent_bytes32_bytes32_uint256_uint256_uint16_uint64_bytes32_Event.OutputTuple, + TransferSent_bytes32_bytes32_uint256_uint256_uint16_uint64_bytes32_Event.OutputObject + >; + getEvent( + key: "TransferSent(bytes32)" + ): TypedContractEvent< + TransferSent_bytes32_Event.InputTuple, + TransferSent_bytes32_Event.OutputTuple, + TransferSent_bytes32_Event.OutputObject + >; + getEvent( + key: "Upgraded" + ): TypedContractEvent< + UpgradedEvent.InputTuple, + UpgradedEvent.OutputTuple, + UpgradedEvent.OutputObject + >; + + filters: { + "AdminChanged(address,address)": TypedContractEvent< + AdminChangedEvent.InputTuple, + AdminChangedEvent.OutputTuple, + AdminChangedEvent.OutputObject + >; + AdminChanged: TypedContractEvent< + AdminChangedEvent.InputTuple, + AdminChangedEvent.OutputTuple, + AdminChangedEvent.OutputObject + >; + + "BeaconUpgraded(address)": TypedContractEvent< + BeaconUpgradedEvent.InputTuple, + BeaconUpgradedEvent.OutputTuple, + BeaconUpgradedEvent.OutputObject + >; + BeaconUpgraded: TypedContractEvent< + BeaconUpgradedEvent.InputTuple, + BeaconUpgradedEvent.OutputTuple, + BeaconUpgradedEvent.OutputObject + >; + + "InboundTransferLimitUpdated(uint16,uint256,uint256)": TypedContractEvent< + InboundTransferLimitUpdatedEvent.InputTuple, + InboundTransferLimitUpdatedEvent.OutputTuple, + InboundTransferLimitUpdatedEvent.OutputObject + >; + InboundTransferLimitUpdated: TypedContractEvent< + InboundTransferLimitUpdatedEvent.InputTuple, + InboundTransferLimitUpdatedEvent.OutputTuple, + InboundTransferLimitUpdatedEvent.OutputObject + >; + + "InboundTransferQueued(bytes32)": TypedContractEvent< + InboundTransferQueuedEvent.InputTuple, + InboundTransferQueuedEvent.OutputTuple, + InboundTransferQueuedEvent.OutputObject + >; + InboundTransferQueued: TypedContractEvent< + InboundTransferQueuedEvent.InputTuple, + InboundTransferQueuedEvent.OutputTuple, + InboundTransferQueuedEvent.OutputObject + >; + + "Initialized(uint64)": TypedContractEvent< + InitializedEvent.InputTuple, + InitializedEvent.OutputTuple, + InitializedEvent.OutputObject + >; + Initialized: TypedContractEvent< + InitializedEvent.InputTuple, + InitializedEvent.OutputTuple, + InitializedEvent.OutputObject + >; + + "MessageAttestedTo(bytes32,address,uint8)": TypedContractEvent< + MessageAttestedToEvent.InputTuple, + MessageAttestedToEvent.OutputTuple, + MessageAttestedToEvent.OutputObject + >; + MessageAttestedTo: TypedContractEvent< + MessageAttestedToEvent.InputTuple, + MessageAttestedToEvent.OutputTuple, + MessageAttestedToEvent.OutputObject + >; + + "NotPaused(bool)": TypedContractEvent< + NotPausedEvent.InputTuple, + NotPausedEvent.OutputTuple, + NotPausedEvent.OutputObject + >; + NotPaused: TypedContractEvent< + NotPausedEvent.InputTuple, + NotPausedEvent.OutputTuple, + NotPausedEvent.OutputObject + >; + + "OutboundTransferCancelled(uint256,address,uint256)": TypedContractEvent< + OutboundTransferCancelledEvent.InputTuple, + OutboundTransferCancelledEvent.OutputTuple, + OutboundTransferCancelledEvent.OutputObject + >; + OutboundTransferCancelled: TypedContractEvent< + OutboundTransferCancelledEvent.InputTuple, + OutboundTransferCancelledEvent.OutputTuple, + OutboundTransferCancelledEvent.OutputObject + >; + + "OutboundTransferLimitUpdated(uint256,uint256)": TypedContractEvent< + OutboundTransferLimitUpdatedEvent.InputTuple, + OutboundTransferLimitUpdatedEvent.OutputTuple, + OutboundTransferLimitUpdatedEvent.OutputObject + >; + OutboundTransferLimitUpdated: TypedContractEvent< + OutboundTransferLimitUpdatedEvent.InputTuple, + OutboundTransferLimitUpdatedEvent.OutputTuple, + OutboundTransferLimitUpdatedEvent.OutputObject + >; + + "OutboundTransferQueued(uint64)": TypedContractEvent< + OutboundTransferQueuedEvent.InputTuple, + OutboundTransferQueuedEvent.OutputTuple, + OutboundTransferQueuedEvent.OutputObject + >; + OutboundTransferQueued: TypedContractEvent< + OutboundTransferQueuedEvent.InputTuple, + OutboundTransferQueuedEvent.OutputTuple, + OutboundTransferQueuedEvent.OutputObject + >; + + "OutboundTransferRateLimited(address,uint64,uint256,uint256)": TypedContractEvent< + OutboundTransferRateLimitedEvent.InputTuple, + OutboundTransferRateLimitedEvent.OutputTuple, + OutboundTransferRateLimitedEvent.OutputObject + >; + OutboundTransferRateLimited: TypedContractEvent< + OutboundTransferRateLimitedEvent.InputTuple, + OutboundTransferRateLimitedEvent.OutputTuple, + OutboundTransferRateLimitedEvent.OutputObject + >; + + "OwnershipTransferred(address,address)": TypedContractEvent< + OwnershipTransferredEvent.InputTuple, + OwnershipTransferredEvent.OutputTuple, + OwnershipTransferredEvent.OutputObject + >; + OwnershipTransferred: TypedContractEvent< + OwnershipTransferredEvent.InputTuple, + OwnershipTransferredEvent.OutputTuple, + OwnershipTransferredEvent.OutputObject + >; + + "Paused(bool)": TypedContractEvent< + PausedEvent.InputTuple, + PausedEvent.OutputTuple, + PausedEvent.OutputObject + >; + Paused: TypedContractEvent< + PausedEvent.InputTuple, + PausedEvent.OutputTuple, + PausedEvent.OutputObject + >; + + "PauserTransferred(address,address)": TypedContractEvent< + PauserTransferredEvent.InputTuple, + PauserTransferredEvent.OutputTuple, + PauserTransferredEvent.OutputObject + >; + PauserTransferred: TypedContractEvent< + PauserTransferredEvent.InputTuple, + PauserTransferredEvent.OutputTuple, + PauserTransferredEvent.OutputObject + >; + + "PeerUpdated(uint16,bytes32,uint8,bytes32,uint8)": TypedContractEvent< + PeerUpdatedEvent.InputTuple, + PeerUpdatedEvent.OutputTuple, + PeerUpdatedEvent.OutputObject + >; + PeerUpdated: TypedContractEvent< + PeerUpdatedEvent.InputTuple, + PeerUpdatedEvent.OutputTuple, + PeerUpdatedEvent.OutputObject + >; + + "ThresholdChanged(uint16,uint8,uint8)": TypedContractEvent< + ThresholdChangedEvent.InputTuple, + ThresholdChangedEvent.OutputTuple, + ThresholdChangedEvent.OutputObject + >; + ThresholdChanged: TypedContractEvent< + ThresholdChangedEvent.InputTuple, + ThresholdChangedEvent.OutputTuple, + ThresholdChangedEvent.OutputObject + >; + + "TransceiverAdded(address,uint256)": TypedContractEvent< + TransceiverAddedEvent.InputTuple, + TransceiverAddedEvent.OutputTuple, + TransceiverAddedEvent.OutputObject + >; + TransceiverAdded: TypedContractEvent< + TransceiverAddedEvent.InputTuple, + TransceiverAddedEvent.OutputTuple, + TransceiverAddedEvent.OutputObject + >; + + "TransferRedeemed(bytes32)": TypedContractEvent< + TransferRedeemedEvent.InputTuple, + TransferRedeemedEvent.OutputTuple, + TransferRedeemedEvent.OutputObject + >; + TransferRedeemed: TypedContractEvent< + TransferRedeemedEvent.InputTuple, + TransferRedeemedEvent.OutputTuple, + TransferRedeemedEvent.OutputObject + >; + + "TransferSent(bytes32,bytes32,uint256,uint256,uint16,uint64,bytes32)": TypedContractEvent< + TransferSent_bytes32_bytes32_uint256_uint256_uint16_uint64_bytes32_Event.InputTuple, + TransferSent_bytes32_bytes32_uint256_uint256_uint16_uint64_bytes32_Event.OutputTuple, + TransferSent_bytes32_bytes32_uint256_uint256_uint16_uint64_bytes32_Event.OutputObject + >; + "TransferSent(bytes32)": TypedContractEvent< + TransferSent_bytes32_Event.InputTuple, + TransferSent_bytes32_Event.OutputTuple, + TransferSent_bytes32_Event.OutputObject + >; + + "Upgraded(address)": TypedContractEvent< + UpgradedEvent.InputTuple, + UpgradedEvent.OutputTuple, + UpgradedEvent.OutputObject + >; + Upgraded: TypedContractEvent< + UpgradedEvent.InputTuple, + UpgradedEvent.OutputTuple, + UpgradedEvent.OutputObject + >; + }; +} diff --git a/evm/ts/src/ethers-contracts/2_0_0/common.ts b/evm/ts/src/ethers-contracts/2_0_0/common.ts new file mode 100644 index 000000000..56b5f21e9 --- /dev/null +++ b/evm/ts/src/ethers-contracts/2_0_0/common.ts @@ -0,0 +1,131 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import type { + FunctionFragment, + Typed, + EventFragment, + ContractTransaction, + ContractTransactionResponse, + DeferredTopicFilter, + EventLog, + TransactionRequest, + LogDescription, +} from "ethers"; + +export interface TypedDeferredTopicFilter<_TCEvent extends TypedContractEvent> + extends DeferredTopicFilter {} + +export interface TypedContractEvent< + InputTuple extends Array = any, + OutputTuple extends Array = any, + OutputObject = any +> { + (...args: Partial): TypedDeferredTopicFilter< + TypedContractEvent + >; + name: string; + fragment: EventFragment; + getFragment(...args: Partial): EventFragment; +} + +type __TypechainAOutputTuple = T extends TypedContractEvent< + infer _U, + infer W +> + ? W + : never; +type __TypechainOutputObject = T extends TypedContractEvent< + infer _U, + infer _W, + infer V +> + ? V + : never; + +export interface TypedEventLog + extends Omit { + args: __TypechainAOutputTuple & __TypechainOutputObject; +} + +export interface TypedLogDescription + extends Omit { + args: __TypechainAOutputTuple & __TypechainOutputObject; +} + +export type TypedListener = ( + ...listenerArg: [ + ...__TypechainAOutputTuple, + TypedEventLog, + ...undefined[] + ] +) => void; + +export type MinEthersFactory = { + deploy(...a: ARGS[]): Promise; +}; + +export type GetContractTypeFromFactory = F extends MinEthersFactory< + infer C, + any +> + ? C + : never; +export type GetARGsTypeFromFactory = F extends MinEthersFactory + ? Parameters + : never; + +export type StateMutability = "nonpayable" | "payable" | "view"; + +export type BaseOverrides = Omit; +export type NonPayableOverrides = Omit< + BaseOverrides, + "value" | "blockTag" | "enableCcipRead" +>; +export type PayableOverrides = Omit< + BaseOverrides, + "blockTag" | "enableCcipRead" +>; +export type ViewOverrides = Omit; +export type Overrides = S extends "nonpayable" + ? NonPayableOverrides + : S extends "payable" + ? PayableOverrides + : ViewOverrides; + +export type PostfixOverrides, S extends StateMutability> = + | A + | [...A, Overrides]; +export type ContractMethodArgs< + A extends Array, + S extends StateMutability +> = PostfixOverrides<{ [I in keyof A]-?: A[I] | Typed }, S>; + +export type DefaultReturnType = R extends Array ? R[0] : R; + +// export interface ContractMethod = Array, R = any, D extends R | ContractTransactionResponse = R | ContractTransactionResponse> { +export interface TypedContractMethod< + A extends Array = Array, + R = any, + S extends StateMutability = "payable" +> { + (...args: ContractMethodArgs): S extends "view" + ? Promise> + : Promise; + + name: string; + + fragment: FunctionFragment; + + getFragment(...args: ContractMethodArgs): FunctionFragment; + + populateTransaction( + ...args: ContractMethodArgs + ): Promise; + staticCall( + ...args: ContractMethodArgs + ): Promise>; + send(...args: ContractMethodArgs): Promise; + estimateGas(...args: ContractMethodArgs): Promise; + staticCallResult(...args: ContractMethodArgs): Promise; +} diff --git a/evm/ts/src/ethers-contracts/2_0_0/factories/NttManager__factory.ts b/evm/ts/src/ethers-contracts/2_0_0/factories/NttManager__factory.ts new file mode 100644 index 000000000..6e4c3c4d4 --- /dev/null +++ b/evm/ts/src/ethers-contracts/2_0_0/factories/NttManager__factory.ts @@ -0,0 +1,2147 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +import { + Contract, + ContractFactory, + ContractTransactionResponse, + Interface, +} from "ethers"; +import type { + Signer, + BigNumberish, + AddressLike, + ContractDeployTransaction, + ContractRunner, +} from "ethers"; +import type { NonPayableOverrides } from "../common.js"; +import type { NttManager, NttManagerInterface } from "../NttManager.js"; + +const _abi = [ + { + type: "constructor", + inputs: [ + { + name: "_endpoint", + type: "address", + internalType: "address", + }, + { + name: "_executor", + type: "address", + internalType: "address", + }, + { + name: "_token", + type: "address", + internalType: "address", + }, + { + name: "_mode", + type: "uint8", + internalType: "enum IManagerBase.Mode", + }, + { + name: "_chainId", + type: "uint16", + internalType: "uint16", + }, + { + name: "_rateLimitDuration", + type: "uint64", + internalType: "uint64", + }, + { + name: "_skipRateLimiting", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "NTT_MANAGER_VERSION", + inputs: [], + outputs: [ + { + name: "", + type: "string", + internalType: "string", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "cancelOutboundQueuedTransfer", + inputs: [ + { + name: "messageSequence", + type: "uint64", + internalType: "uint64", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "chainId", + inputs: [], + outputs: [ + { + name: "", + type: "uint16", + internalType: "uint16", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "completeInboundQueuedTransfer", + inputs: [ + { + name: "digest", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "completeOutboundQueuedTransfer", + inputs: [ + { + name: "messageSequence", + type: "uint64", + internalType: "uint64", + }, + ], + outputs: [ + { + name: "", + type: "uint64", + internalType: "uint64", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "disableRecvTransceiver", + inputs: [ + { + name: "chain", + type: "uint16", + internalType: "uint16", + }, + { + name: "transceiver", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "disableSendTransceiver", + inputs: [ + { + name: "chain", + type: "uint16", + internalType: "uint16", + }, + { + name: "transceiver", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "enableRecvTransceiver", + inputs: [ + { + name: "chain", + type: "uint16", + internalType: "uint16", + }, + { + name: "transceiver", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "enableSendTransceiver", + inputs: [ + { + name: "chain", + type: "uint16", + internalType: "uint16", + }, + { + name: "transceiver", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "endpoint", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IEndpointIntegrator", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "executeMsg", + inputs: [ + { + name: "sourceChainId", + type: "uint16", + internalType: "uint16", + }, + { + name: "sourceNttManagerAddress", + type: "bytes32", + internalType: "UniversalAddress", + }, + { + name: "epSeq", + type: "uint64", + internalType: "uint64", + }, + { + name: "payload", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "executor", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "contract IExecutor", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getCurrentInboundCapacity", + inputs: [ + { + name: "chainId_", + type: "uint16", + internalType: "uint16", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getCurrentOutboundCapacity", + inputs: [], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getInboundLimitParams", + inputs: [ + { + name: "chainId_", + type: "uint16", + internalType: "uint16", + }, + ], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct IRateLimiter.RateLimitParams", + components: [ + { + name: "limit", + type: "uint72", + internalType: "TrimmedAmount", + }, + { + name: "currentCapacity", + type: "uint72", + internalType: "TrimmedAmount", + }, + { + name: "lastTxTimestamp", + type: "uint64", + internalType: "uint64", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getInboundQueuedTransfer", + inputs: [ + { + name: "digest", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct IRateLimiter.InboundQueuedTransfer", + components: [ + { + name: "amount", + type: "uint72", + internalType: "TrimmedAmount", + }, + { + name: "txTimestamp", + type: "uint64", + internalType: "uint64", + }, + { + name: "recipient", + type: "address", + internalType: "address", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getMigratesImmutables", + inputs: [], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getMode", + inputs: [], + outputs: [ + { + name: "", + type: "uint8", + internalType: "uint8", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getOutboundLimitParams", + inputs: [], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct IRateLimiter.RateLimitParams", + components: [ + { + name: "limit", + type: "uint72", + internalType: "TrimmedAmount", + }, + { + name: "currentCapacity", + type: "uint72", + internalType: "TrimmedAmount", + }, + { + name: "lastTxTimestamp", + type: "uint64", + internalType: "uint64", + }, + ], + }, + ], + stateMutability: "pure", + }, + { + type: "function", + name: "getOutboundQueuedTransfer", + inputs: [ + { + name: "queueSequence", + type: "uint64", + internalType: "uint64", + }, + ], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct IRateLimiter.OutboundQueuedTransfer", + components: [ + { + name: "recipient", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "refundAddress", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "amount", + type: "uint72", + internalType: "TrimmedAmount", + }, + { + name: "txTimestamp", + type: "uint64", + internalType: "uint64", + }, + { + name: "recipientChain", + type: "uint16", + internalType: "uint16", + }, + { + name: "sender", + type: "address", + internalType: "address", + }, + { + name: "executorQuote", + type: "bytes", + internalType: "bytes", + }, + { + name: "relayInstructions", + type: "bytes", + internalType: "bytes", + }, + { + name: "transceiverInstructions", + type: "bytes", + internalType: "bytes", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getPeer", + inputs: [ + { + name: "chainId_", + type: "uint16", + internalType: "uint16", + }, + ], + outputs: [ + { + name: "", + type: "tuple", + internalType: "struct INttManager.NttManagerPeer", + components: [ + { + name: "peerAddress", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "tokenDecimals", + type: "uint8", + internalType: "uint8", + }, + { + name: "gasLimit", + type: "uint128", + internalType: "uint128", + }, + ], + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "getThreshold", + inputs: [ + { + name: "chain", + type: "uint16", + internalType: "uint16", + }, + ], + outputs: [ + { + name: "", + type: "uint8", + internalType: "uint8", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "initialize", + inputs: [], + outputs: [], + stateMutability: "payable", + }, + { + type: "function", + name: "isMessageApproved", + inputs: [ + { + name: "srcChain", + type: "uint16", + internalType: "uint16", + }, + { + name: "srcAddr", + type: "bytes32", + internalType: "UniversalAddress", + }, + { + name: "sequence", + type: "uint64", + internalType: "uint64", + }, + { + name: "dstAddr", + type: "bytes32", + internalType: "UniversalAddress", + }, + { + name: "payloadHash", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "isMessageExecuted", + inputs: [ + { + name: "srcChain", + type: "uint16", + internalType: "uint16", + }, + { + name: "srcAddr", + type: "bytes32", + internalType: "UniversalAddress", + }, + { + name: "sequence", + type: "uint64", + internalType: "uint64", + }, + { + name: "dstAddr", + type: "bytes32", + internalType: "UniversalAddress", + }, + { + name: "payloadHash", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "isPaused", + inputs: [], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "messageAttestations", + inputs: [ + { + name: "srcChain", + type: "uint16", + internalType: "uint16", + }, + { + name: "srcAddr", + type: "bytes32", + internalType: "UniversalAddress", + }, + { + name: "sequence", + type: "uint64", + internalType: "uint64", + }, + { + name: "dstAddr", + type: "bytes32", + internalType: "UniversalAddress", + }, + { + name: "payloadHash", + type: "bytes32", + internalType: "bytes32", + }, + ], + outputs: [ + { + name: "count", + type: "uint8", + internalType: "uint8", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "migrate", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "mode", + inputs: [], + outputs: [ + { + name: "", + type: "uint8", + internalType: "enum IManagerBase.Mode", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "nextMessageSequence", + inputs: [], + outputs: [ + { + name: "", + type: "uint64", + internalType: "uint64", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "owner", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "pause", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "pauser", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "quoteDeliveryPrice", + inputs: [ + { + name: "recipientChain", + type: "uint16", + internalType: "uint16", + }, + { + name: "transceiverInstructions", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "uint256", + internalType: "uint256", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "rateLimitDuration", + inputs: [], + outputs: [ + { + name: "", + type: "uint64", + internalType: "uint64", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "setGasLimit", + inputs: [ + { + name: "peerChainId", + type: "uint16", + internalType: "uint16", + }, + { + name: "gasLimit", + type: "uint128", + internalType: "uint128", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setInboundLimit", + inputs: [ + { + name: "limit", + type: "uint256", + internalType: "uint256", + }, + { + name: "chainId_", + type: "uint16", + internalType: "uint16", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setOutboundLimit", + inputs: [ + { + name: "limit", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setPeer", + inputs: [ + { + name: "peerChainId", + type: "uint16", + internalType: "uint16", + }, + { + name: "peerContract", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "decimals", + type: "uint8", + internalType: "uint8", + }, + { + name: "gasLimit", + type: "uint128", + internalType: "uint128", + }, + { + name: "inboundLimit", + type: "uint256", + internalType: "uint256", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setThreshold", + inputs: [ + { + name: "chain", + type: "uint16", + internalType: "uint16", + }, + { + name: "threshold", + type: "uint8", + internalType: "uint8", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "setTransceiver", + inputs: [ + { + name: "transceiver", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "token", + inputs: [], + outputs: [ + { + name: "", + type: "address", + internalType: "address", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "tokenDecimals", + inputs: [], + outputs: [ + { + name: "", + type: "uint8", + internalType: "uint8", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "transceiverAttestedToMessage", + inputs: [ + { + name: "srcChain", + type: "uint16", + internalType: "uint16", + }, + { + name: "srcAddr", + type: "bytes32", + internalType: "UniversalAddress", + }, + { + name: "sequence", + type: "uint64", + internalType: "uint64", + }, + { + name: "dstAddr", + type: "bytes32", + internalType: "UniversalAddress", + }, + { + name: "payloadHash", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "index", + type: "uint8", + internalType: "uint8", + }, + ], + outputs: [ + { + name: "", + type: "bool", + internalType: "bool", + }, + ], + stateMutability: "view", + }, + { + type: "function", + name: "transfer", + inputs: [ + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + { + name: "recipientChain", + type: "uint16", + internalType: "uint16", + }, + { + name: "recipient", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "refundAddress", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "shouldQueue", + type: "bool", + internalType: "bool", + }, + { + name: "executorQuote", + type: "bytes", + internalType: "bytes", + }, + { + name: "relayInstructions", + type: "bytes", + internalType: "bytes", + }, + { + name: "transceiverInstructions", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "uint64", + internalType: "uint64", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "transfer", + inputs: [ + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + { + name: "recipientChain", + type: "uint16", + internalType: "uint16", + }, + { + name: "recipient", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "executorQuote", + type: "bytes", + internalType: "bytes", + }, + { + name: "relayInstructions", + type: "bytes", + internalType: "bytes", + }, + { + name: "transceiverInstructions", + type: "bytes", + internalType: "bytes", + }, + ], + outputs: [ + { + name: "", + type: "uint64", + internalType: "uint64", + }, + ], + stateMutability: "payable", + }, + { + type: "function", + name: "transferOwnership", + inputs: [ + { + name: "newOwner", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "transferPauserCapability", + inputs: [ + { + name: "newPauser", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "unpause", + inputs: [], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "function", + name: "upgrade", + inputs: [ + { + name: "newImplementation", + type: "address", + internalType: "address", + }, + ], + outputs: [], + stateMutability: "nonpayable", + }, + { + type: "event", + name: "AdminChanged", + inputs: [ + { + name: "previousAdmin", + type: "address", + indexed: false, + internalType: "address", + }, + { + name: "newAdmin", + type: "address", + indexed: false, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "BeaconUpgraded", + inputs: [ + { + name: "beacon", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "InboundTransferLimitUpdated", + inputs: [ + { + name: "chainId", + type: "uint16", + indexed: true, + internalType: "uint16", + }, + { + name: "oldLimit", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "newLimit", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "InboundTransferQueued", + inputs: [ + { + name: "digest", + type: "bytes32", + indexed: false, + internalType: "bytes32", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Initialized", + inputs: [ + { + name: "version", + type: "uint64", + indexed: false, + internalType: "uint64", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "MessageAttestedTo", + inputs: [ + { + name: "digest", + type: "bytes32", + indexed: false, + internalType: "bytes32", + }, + { + name: "transceiver", + type: "address", + indexed: false, + internalType: "address", + }, + { + name: "index", + type: "uint8", + indexed: false, + internalType: "uint8", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "NotPaused", + inputs: [ + { + name: "notPaused", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "OutboundTransferCancelled", + inputs: [ + { + name: "sequence", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "recipient", + type: "address", + indexed: false, + internalType: "address", + }, + { + name: "amount", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "OutboundTransferLimitUpdated", + inputs: [ + { + name: "oldLimit", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "newLimit", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "OutboundTransferQueued", + inputs: [ + { + name: "queueSequence", + type: "uint64", + indexed: false, + internalType: "uint64", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "OutboundTransferRateLimited", + inputs: [ + { + name: "sender", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "sequence", + type: "uint64", + indexed: false, + internalType: "uint64", + }, + { + name: "amount", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "currentCapacity", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "OwnershipTransferred", + inputs: [ + { + name: "previousOwner", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "newOwner", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Paused", + inputs: [ + { + name: "paused", + type: "bool", + indexed: false, + internalType: "bool", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "PauserTransferred", + inputs: [ + { + name: "oldPauser", + type: "address", + indexed: true, + internalType: "address", + }, + { + name: "newPauser", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "PeerUpdated", + inputs: [ + { + name: "chainId_", + type: "uint16", + indexed: true, + internalType: "uint16", + }, + { + name: "oldPeerContract", + type: "bytes32", + indexed: false, + internalType: "bytes32", + }, + { + name: "oldPeerDecimals", + type: "uint8", + indexed: false, + internalType: "uint8", + }, + { + name: "peerContract", + type: "bytes32", + indexed: false, + internalType: "bytes32", + }, + { + name: "peerDecimals", + type: "uint8", + indexed: false, + internalType: "uint8", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "ThresholdChanged", + inputs: [ + { + name: "chainId", + type: "uint16", + indexed: false, + internalType: "uint16", + }, + { + name: "oldThreshold", + type: "uint8", + indexed: false, + internalType: "uint8", + }, + { + name: "threshold", + type: "uint8", + indexed: false, + internalType: "uint8", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "TransceiverAdded", + inputs: [ + { + name: "transceiver", + type: "address", + indexed: false, + internalType: "address", + }, + { + name: "transceiversNum", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "TransferRedeemed", + inputs: [ + { + name: "digest", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "TransferSent", + inputs: [ + { + name: "recipient", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + { + name: "refundAddress", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + { + name: "amount", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "fee", + type: "uint256", + indexed: false, + internalType: "uint256", + }, + { + name: "recipientChain", + type: "uint16", + indexed: false, + internalType: "uint16", + }, + { + name: "msgSequence", + type: "uint64", + indexed: false, + internalType: "uint64", + }, + { + name: "msgHash", + type: "bytes32", + indexed: false, + internalType: "bytes32", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "TransferSent", + inputs: [ + { + name: "digest", + type: "bytes32", + indexed: true, + internalType: "bytes32", + }, + ], + anonymous: false, + }, + { + type: "event", + name: "Upgraded", + inputs: [ + { + name: "implementation", + type: "address", + indexed: true, + internalType: "address", + }, + ], + anonymous: false, + }, + { + type: "error", + name: "BurnAmountDifferentThanBalanceDiff", + inputs: [ + { + name: "burnAmount", + type: "uint256", + internalType: "uint256", + }, + { + name: "balanceDiff", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "CancellerNotSender", + inputs: [ + { + name: "canceller", + type: "address", + internalType: "address", + }, + { + name: "sender", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "CapacityCannotExceedLimit", + inputs: [ + { + name: "newCurrentCapacity", + type: "uint72", + internalType: "TrimmedAmount", + }, + { + name: "newLimit", + type: "uint72", + internalType: "TrimmedAmount", + }, + ], + }, + { + type: "error", + name: "DeliveryPaymentTooLow", + inputs: [ + { + name: "requiredPayment", + type: "uint256", + internalType: "uint256", + }, + { + name: "providedPayment", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "InboundQueuedTransferNotFound", + inputs: [ + { + name: "digest", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + { + type: "error", + name: "InboundQueuedTransferStillQueued", + inputs: [ + { + name: "digest", + type: "bytes32", + internalType: "bytes32", + }, + { + name: "transferTimestamp", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "InvalidFork", + inputs: [ + { + name: "evmChainId", + type: "uint256", + internalType: "uint256", + }, + { + name: "blockChainId", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "InvalidGasLimitZero", + inputs: [ + { + name: "destChain", + type: "uint16", + internalType: "uint16", + }, + ], + }, + { + type: "error", + name: "InvalidInitialization", + inputs: [], + }, + { + type: "error", + name: "InvalidMode", + inputs: [ + { + name: "mode", + type: "uint8", + internalType: "uint8", + }, + ], + }, + { + type: "error", + name: "InvalidPauser", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "InvalidPeer", + inputs: [ + { + name: "chainId", + type: "uint16", + internalType: "uint16", + }, + { + name: "peerAddress", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + { + type: "error", + name: "InvalidPeerChainIdZero", + inputs: [], + }, + { + type: "error", + name: "InvalidPeerDecimals", + inputs: [], + }, + { + type: "error", + name: "InvalidPeerSameChainId", + inputs: [], + }, + { + type: "error", + name: "InvalidPeerZeroAddress", + inputs: [], + }, + { + type: "error", + name: "InvalidRecipient", + inputs: [], + }, + { + type: "error", + name: "InvalidRefundAddress", + inputs: [], + }, + { + type: "error", + name: "InvalidTargetChain", + inputs: [ + { + name: "targetChain", + type: "uint16", + internalType: "uint16", + }, + { + name: "thisChain", + type: "uint16", + internalType: "uint16", + }, + ], + }, + { + type: "error", + name: "MessageNotApproved", + inputs: [ + { + name: "msgHash", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + { + type: "error", + name: "NoEnabledTransceivers", + inputs: [], + }, + { + type: "error", + name: "NotAnEvmAddress", + inputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + { + type: "error", + name: "NotAnEvmAddress", + inputs: [ + { + name: "", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + { + type: "error", + name: "NotEnoughCapacity", + inputs: [ + { + name: "currentCapacity", + type: "uint256", + internalType: "uint256", + }, + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "NotImplemented", + inputs: [], + }, + { + type: "error", + name: "NotInitializing", + inputs: [], + }, + { + type: "error", + name: "NotMigrating", + inputs: [], + }, + { + type: "error", + name: "NumberOfDecimalsNotEqual", + inputs: [ + { + name: "decimals", + type: "uint8", + internalType: "uint8", + }, + { + name: "decimalsOther", + type: "uint8", + internalType: "uint8", + }, + ], + }, + { + type: "error", + name: "OnlyDelegateCall", + inputs: [], + }, + { + type: "error", + name: "OutboundQueuedTransferNotFound", + inputs: [ + { + name: "queueSequence", + type: "uint64", + internalType: "uint64", + }, + ], + }, + { + type: "error", + name: "OutboundQueuedTransferStillQueued", + inputs: [ + { + name: "queueSequence", + type: "uint64", + internalType: "uint64", + }, + { + name: "transferTimestamp", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "OwnableInvalidOwner", + inputs: [ + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "OwnableUnauthorizedAccount", + inputs: [ + { + name: "account", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "PeerNotRegistered", + inputs: [ + { + name: "chainId", + type: "uint16", + internalType: "uint16", + }, + ], + }, + { + type: "error", + name: "ReentrancyGuardReentrantCall", + inputs: [], + }, + { + type: "error", + name: "RefundFailed", + inputs: [ + { + name: "refundAmount", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "RequireContractIsNotPaused", + inputs: [], + }, + { + type: "error", + name: "RequireContractIsPaused", + inputs: [], + }, + { + type: "error", + name: "RetrievedIncorrectRegisteredTransceivers", + inputs: [ + { + name: "retrieved", + type: "uint256", + internalType: "uint256", + }, + { + name: "registered", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "StaticcallFailed", + inputs: [], + }, + { + type: "error", + name: "ThresholdNotMet", + inputs: [ + { + name: "threshold", + type: "uint8", + internalType: "uint8", + }, + { + name: "numAttested", + type: "uint8", + internalType: "uint8", + }, + ], + }, + { + type: "error", + name: "ThresholdTooHigh", + inputs: [ + { + name: "threshold", + type: "uint256", + internalType: "uint256", + }, + { + name: "transceivers", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "TransceiverAlreadyAttestedToMessage", + inputs: [ + { + name: "nttManagerMessageHash", + type: "bytes32", + internalType: "bytes32", + }, + ], + }, + { + type: "error", + name: "TransferAmountHasDust", + inputs: [ + { + name: "amount", + type: "uint256", + internalType: "uint256", + }, + { + name: "dust", + type: "uint256", + internalType: "uint256", + }, + ], + }, + { + type: "error", + name: "UndefinedRateLimiting", + inputs: [], + }, + { + type: "error", + name: "UnexpectedDeployer", + inputs: [ + { + name: "expectedOwner", + type: "address", + internalType: "address", + }, + { + name: "owner", + type: "address", + internalType: "address", + }, + ], + }, + { + type: "error", + name: "UnexpectedMsgValue", + inputs: [], + }, + { + type: "error", + name: "ZeroAmount", + inputs: [], + }, + { + type: "error", + name: "ZeroThreshold", + inputs: [], + }, +] as const; + +const _bytecode = + "0x6101a03462000374576001600160401b0390601f6200614038819003918201601f1916830191908483118484101762000379578160e09285926040958652833981010312620003745762000053826200038f565b62000061602084016200038f565b916200006f8185016200038f565b6060850151956002871015620003745760808601519561ffff87168703620003745760a08101519082821690818303620003745760c00151801591821582036200037457158092816200036b575b5082156200034f575b50506200033e576080527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff82861c166200032d578080831603620002e8575b5050503060a05260018060a01b0380610160941684526101809416845260c052610100948552610120938452610140904682523360e0525193615d9b9586620003a58739608051868181611246015281816115ab01528181611be20152818161282d0152818161354401528181613f60015281816154d3015281816155e60152818161567a015281816157a10152615852015260a05186614eaa015260c0518681816102b1015281816104e10152818161052a015281816126cc0152818161385d01528181613b270152818161467301528181614c9e01528181614cd80152614dad015260e0518661170901525185818161047c015281816120440152818161213001528181612718015281816138e201528181613ba40152614c2e015251848181610d4c015281816111010152818161199101528181611ca70152818161278001526133c501525183818161042c0152818161400d015281816145750152614be001525182818161083501528181610c0801528181610fb401528181611052015281816117e601528181611d5501528181611dab01528181611e4601528181611fb00152818161225c015281816127d201528181612e7f01528181612fc8015281816132060152614878015251818181610ab001526149cb0152f35b6001600160401b0319909116811790915582519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a13880806200010b565b845163f92ee8a960e01b8152600490fd5b835163e543ef0560e01b8152600490fd5b1591508162000362575b503880620000c6565b90503862000359565b925038620000bd565b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620003745756fe608060405260048036101561001357600080fd5b6000803560e01c80630271725014612b84578063036de8af14612af957806307105bf414612a2b5780630900f0101461254c578063186ce6121461252157806319017175146124ea5780631f97c9a814612328578063203e4a9b146122035780632310dd3c146121bd57806323d75e3114612177578063295a52121461211d5780633b97e856146121025780633f4ba83a1461208e5780634b4fd03b146120315780634b604fe714611f285780635960108e14611e1f57806359611e2314611d845780635e280f1114611d3f578063689f90c314611cfd5780636a4db16014611c0657806374aa7bfc14611bc2578063786c3ae614611afc578063801ad374146119255780638129fc1c146116725780638413bcba146115495780638456cb59146114c957806386e11ffa146114a45780638da5cb5b1461146e5780638fd3ab80146113385780639057412d146112f257806397c35146146111255780639a8a0592146110e65780639bb5243a1461102b5780639e1ac5d414610f505780639fd0506d14610f1a578063a1e7aec814610e26578063a5645b7f14610dfe578063b00c626414610be3578063b187bd2614610bb5578063c0b07bde14610b6f578063c128d17014610adf578063c34c08e514610a9a578063d30dfa8914610775578063d788c147146106ea578063e9c9b2dd1461069b578063f2fde38b14610666578063f5cfec1814610643578063f7514fbc146102e0578063fc0c546a1461029b5763fd96063c1461024457600080fd5b346102985760203660031901126102985760606102618335615431565b6040805182516001600160481b031681526020808401516001600160401b031690820152918101516001600160a01b031690820152f35b80fd5b50346102985780600319360112610298576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50903461063f576020908160031936011261063b576102fd612c71565b610305615bf5565b6002600080516020615ca6833981519152541461062b576001600160401b038091169182600052600080516020615ce68339815191529182855260406000206040519361035185612d0e565b815485526001820154878601526002820154918360408701936001600160481b038116855261ffff6060890191838160481c16835260881c16608089015260018060a01b03976101006103d560068b6003880154169660a085019788526103b98c82016136fe565b60c08601526103ca600582016136fe565b60e0860152016136fe565b910152511615610613575185163381036105e257507ff80e572ae1b63e2449629b6c7d783add85c36473926f216077f17ee002bcfd0792606092610467928860005289526104266040600020613802565b516104507f0000000000000000000000000000000000000000000000000000000000000000615970565b610458614d87565b9160ff82169160081c16615a63565b946040519081523387820152856040820152a17f000000000000000000000000000000000000000000000000000000000000000060028110156105cd578061051b57505060405163a9059cbb60e01b9381019390935233602484015260448084019290925290825261050691906104df606483612d7b565b7f000000000000000000000000000000000000000000000000000000000000000016613d63565b6001600080516020615d068339815191525580f35b92935091600181036105b257507f00000000000000000000000000000000000000000000000000000000000000001691823b156105ad57604080516340c10f1960e01b8152339381019384526020840192909252909260009284928391859183910103925af180156105a157610592575b50610506565b61059b90612ce5565b3861058c565b6040513d6000823e3d90fd5b600080fd5b6040516366001a8960e01b815260ff90911681840152602490fd5b602182634e487b7160e01b6000525260246000fd5b6040805163ceb40a8560e01b8152338188019081526001600160a01b039093166020840152918291010390fd5b0390fd5b604051635feafa3160e11b8152808601889052602490fd5b506040516309e3d0f360e11b8152fd5b8280fd5b5080fd5b5034610298578060031936011261029857602061065e61522f565b604051908152f35b503461029857602036600319011261029857610698610683612c18565b61068b615bbc565b610693615bbc565b615b48565b80f35b50346102985760206106be61ffff6106b236612e0c565b92849694929192612f76565b9116600052600080516020615c46833981519152825260ff806040600020541691161015604051908152f35b50346102985760203660031901126102985761ffff610707612bf6565b61070f6132c9565b5016600052600080516020615cc683398151915260205261077161073660406000206151cf565b6040519182918291909160406001600160401b038160608401956001600160481b038082511686526020820151166020860152015116910152565b0390f35b50903461063f57608036600319011261063f57610790612bf6565b6024359161079c612c87565b916001600160401b0391606435838111610a96576107bd9036908301612dee565b6002600080516020615ca6833981519152541461062b5761ffff83169586600052602095600080516020615c2683398151915287526040600020548203610a79578251878401206040516303b5521f60e41b81528581018a90526024810193909352908616604483015260648201526060816084818b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af19081156109ee578891610a23575b5086600052600080516020615c46833981519152865260ff60406000205416908160ff8216106109f9575050906108d5918773__$93083e246e55d56d98f3df2872cd16bfd0$__916040518095819263107383fb60e01b83528a878401526024830190612cc0565b0381845af49485156109ee5786938996610973575b505060405196879263b3f07bbd60e01b8452830152604060248301528180610915604482018861332a565b03915af4928315610968578593610932575b506106989350613360565b90925083813d8311610961575b6109498183612d7b565b8101031261095d5761069892519138610927565b8380fd5b503d61093f565b6040513d87823e3d90fd5b91955092503d8089833e6109878183612d7b565b81019086818303126109ea578051908482116109e657016060818303126109ea57604051916109b583612d2a565b81518352878201518884015260408201519485116109e65787946109d992016132e8565b60408201529338806108ea565b8980fd5b8880fd5b6040513d8a823e3d90fd5b6040805163f6f1228760e01b815260ff938416868201908152929093166020830152908290030190fd5b90506060813d8211610a71575b81610a3d60609383612d7b565b81010312610a6d57604081610a54610a6793612f0c565b50610a60888201612f0c565b5001612f20565b3861086d565b8780fd5b3d9150610a30565b604484838a60405192635788c0fd60e11b84528301526024820152fd5b8680fd5b50346102985780600319360112610298576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b50346102985760203660031901126102985761ffff610afc612bf6565b610b046132c9565b5016600052600080516020615c2683398151915260205260606040600020604051610b2e81612d2a565b600182549283835201549060ff60406020830192828516845201916001600160801b03809460081c1683526040519485525116602084015251166040820152f35b5034610298578060031936011261029857610771604051610b8f81612d60565b60058152640322e302e360dc1b6020820152604051918291602083526020830190612cc0565b503461029857806003193601126102985760206002600080516020615ca68339815191525414604051908152f35b50903461063f57604036600319011261063f57610bfe612bf6565b610c06612c2e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692908490843b1561063f5760408051632932213360e21b81523085820190815261ffff871660208201526001600160a01b039093169183019190915290829082908190606001038183895af18015610df357610ddf575b505061ffff82166000818152600080516020615c4683398151915260209081526040918290208251635a6ad4fd60e11b8152309581019586528583019490945292959293909286929091839182910103915afa8015610dd4578490610d9a575b610698935081549060ff821660ff82169384808311610d18575b50505050506131b557610d138161307d565b6131b5565b7f77bc50d64a1fb4b6ef32894bc26fc008f4ee08223914c45472c9d62088c97f389460ff1916179055610d8d6040519283927f00000000000000000000000000000000000000000000000000000000000000008491604091949360ff809261ffff606087019816865216602085015216910152565b0390a13880808084610d01565b506020833d8211610dcc575b81610db360209383612d7b565b8101031261095d57610dc761069893612f20565b610ce7565b3d9150610da6565b6040513d86823e3d90fd5b610de890612ce5565b61095d578338610c87565b6040513d84823e3d90fd5b5034610298576020610e1b610e1236612e0c565b93929092612f76565b60ff60405191168152f35b5060c036600319011261029857610e3b612c07565b6001600160401b039160443560643584811161063b57610e5e9036908701612c44565b608494919435868111610f1657610e789036908901612c44565b9460a435908882116102985750610e929036908a01612c44565b969092610e9d615bf5565b6002600080516020615ca68339815191525414610f05578593610edb610eea999794610ed360209d9a9895610ee3953691612db7565b973691612db7565b963691612db7565b953561383f565b6001600080516020615d068339815191525560405191168152f35b6040516309e3d0f360e11b81528a90fd5b8480fd5b5034610298578060031936011261029857600080516020615d26833981519152546040516001600160a01b039091168152602090f35b50903461063f576080610fb0610f6536612e0c565b916040979495939751978896879663b898e70960e01b88528701916080936001600160401b0391979695929761ffff60a0860199168552602085015216604083015260608201520152565b03817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa908115610df35760209291610ff9575b506040519015158152f35b61101a915060803d8111611024575b6110128183612d7b565b810190612f3b565b9250505038610fee565b503d611008565b508091346110e35760403660031901126110e357611047612bf6565b90611050612c2e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610f1657604080516355525ded60e11b81523094810194855261ffff90951660208501526001600160a01b039092169183019190915283918391908290849082906060015b03925af18015610df3576110d35750f35b6110dc90612ce5565b6102985780f35b50fd5b5034610298578060031936011261029857602060405161ffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b506020366003190112610298575061113b612c71565b90611144615bf5565b6002600080516020615ca683398151915254146112e3576001600160401b0380918184169081600052600080516020615ce6833981519152908160205260406000209260405161119381612d0e565b8454815260018501549160208201928352600286015490604083016001600160481b0390818416815260608501978a8560481c168952608086019460881c61ffff168552600160a01b60019003938460038c0154169860a08801998a52808c016111fc906136fe565b60c089019081529a61121060058e016136fe565b9c60e08a019d8e52600601611224906136fe565b9d6101008a019e8f52815116156112cc578e6112438183511642612ee9565b907f000000000000000000000000000000000000000000000000000000000000000016116112ac5750509260209d9a9896949261ffff92610eea9d9b9997956000528f526112946040600020613802565b51169251169251935194511694519551965197614567565b918e6044935116906040519263c06cf05f60e01b84528301526024820152fd5b5060249160405191635feafa3160e11b8352820152fd5b6040516309e3d0f360e11b8152fd5b50903461063f57604036600319011261063f5761130d612bf6565b91602435906001600160401b03821161029857602061065e8561133236868801612dee565b90612e49565b50903461063f578160031936011261063f57611352614ea7565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054916001600160401b0380841660018101908282116114595760ff8660401c1690811561144c575b5061143b5790600160401b911680946001600160481b03191617179081835560ff7f7487ca88d037ca20519908b1ee7556206bef53bce0226a348750cb9d4f688e4e54161561142c5750916020917fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d293611414614e64565b68ff000000000000000019169055604051908152a180f35b604051632866815360e11b8152fd5b60405163f92ee8a960e01b81528390fd5b905082821611153861139c565b601184634e487b7160e01b6000525260246000fd5b5034610298578060031936011261029857600080516020615c86833981519152546040516001600160a01b039091168152602090f35b50346102985780600319360112610298576114bd6132c9565b50610771610736615187565b50903461063f578160031936011261063f57600080516020615c86833981519152546114fd906001600160a01b0316614f08565b600080516020615ca68339815191529060028254146112e35750600290557f0e2fb031ee032dc02d8011dc50b816eb450cf856abd8261680dac74f72165bd2602060405160018152a180f35b5034610298576020366003190112610298578135611565615bf5565b6002600080516020615ca683398151915254146116615761158581615431565b60208101936001600160401b038086511615611649576115a88187511642612ee9565b817f00000000000000000000000000000000000000000000000000000000000000001611611629578385527f4e8909a01183a67951f5a6147d6e76ed02ea890c72afea01a9edee91edc609b8602052604080862086815560010186905583015183518691610506916001600160481b0316906001600160a01b031687614bd5565b85516040516301cb739d60e71b8152928301859052166024820152604490fd5b604051630301bcaf60e61b8152808301859052602490fd5b6040516309e3d0f360e11b81528390fd5b50908160031936011261063f57611687614ea7565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c1615916001600160401b0381168015908161191d575b6001149081611913575b15908161190a575b506118fa5767ffffffffffffffff1981166001178455826118db575b506116ff615b07565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081811633036118ac57503461189c57908491611742615b07565b61174a615b07565b611752615b07565b6001600080516020615ca683398151915255600080516020615d2683398151915280546001600160a01b0319163317905561178b615b07565b611793615b07565b61179c33615b48565b6117a4615b07565b6117ac615b07565b6001600080516020615d06833981519152556117e468ffffffffffffffff0060ff6117dd6117d8614d87565b615a2c565b1617614f5b565b7f00000000000000000000000000000000000000000000000000000000000000001690813b1561063b578291602483926040519485938492632210724360e11b845230908401525af18015610df357611888575b5050611842614e64565b61184a575080f35b68ff00000000000000001981541690557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b61189190612ce5565b61063b578238611838565b5060405163bd28e88960e01b8152fd5b60408051636345072160e11b81526001600160a01b039092169382019384523360208501529092839250010390fd5b68ffffffffffffffffff191668010000000000000001178355386116f6565b5060405163f92ee8a960e01b8152fd5b905015386116da565b303b1591506116d2565b8491506116c8565b50903461063f5760a036600319011261063f57611940612bf6565b6024356044359060ff82168092036105ad576064356001600160801b0393848216958683036105ad57611971615bbc565b61ffff808316978815611aeb578515611ada578615611ac95715611ab1577f0000000000000000000000000000000000000000000000000000000000000000168714611aa257509160ff60809492611a857f1456404e7f41f35c3daac941bb50bad417a66275c3040061b4287d787719599d979589600052611a6c600080516020615c268339815191529384602052604060002094600160405196611a1588612d2a565b80548852015499602087019a8881168c5260081c1660408701528c600052602052600160406000208881550188871982541617815590610100600160881b0382549160081b1690610100600160881b031916179055565b611a80611a77614d87565b80608435615abe565b615246565b51935116604051938452602084015260408301526060820152a280f35b60405163101b8f9560e11b8152fd5b60405163d028893560e01b8152808301899052602490fd5b60405163ade64f0b60e01b81528390fd5b60405163f839a0cb60e01b81528390fd5b60405163100b0f2760e11b81528390fd5b503461029857604036600319011261029857611b16612bf6565b602435906001600160801b0382168083036105ad57611b33615bbc565b15611ba65761ffff1680600052600080516020615c26833981519152908160205260406000205415611b9557610698939450600052602052600160406000200190610100600160881b0382549160081b1690610100600160881b031916179055565b60405163f839a0cb60e01b81528590fd5b60405163d028893560e01b815261ffff90911681850152602490fd5b503461029857806003193601126102985760206040516001600160401b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50903461063f57604036600319011261063f57611c21612bf6565b6024359060ff8216928383036105ad57611c39615bbc565b8315611cee575091611ce860ff92611c9e8561ffff7f77bc50d64a1fb4b6ef32894bc26fc008f4ee08223914c45472c9d62088c97f389716600052600080516020615c46833981519152602052604060002080549587198716179055610d138161314b565b604051938493167f00000000000000000000000000000000000000000000000000000000000000008491604091949360ff809261ffff606087019816865216602085015216910152565b0390a180f35b60405163831761d760e01b8152fd5b5034610298578060031936011261029857602060ff7f5443fea4dc453d96b81ce55b62e11a4094cc4cbb8a360956a7253cfdb42506cb54166040519015158152f35b50346102985780600319360112610298576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b508091346110e35760403660031901126110e357611da0612bf6565b90611da9612c2e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610f16576040805163e43fd89d60e01b81523094810194855261ffff90951660208501526001600160a01b039092169183019190915283918391908290849082906060016110c2565b50903461063f57604036600319011261063f5781611e3b612bf6565b91611e44612c2e565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169190823b1561095d5760408051630fa2754560e11b81523093810193845261ffff871660208501526001600160a01b039092169083015291839183919082908490829060600103925af18015610df357611f14575b50508061ffff6106989216600052600080516020615c468339815191526020526040600020805460ff811615611efb575b50506131b5565b60ff19166001179055611f0d8161314b565b3880611ef4565b611f1d90612ce5565b61063f578138611ec3565b50903461063f5760c036600319011261063f57611f43612bf6565b90611f4c612c87565b60a4359260ff84168094036105ad576040805163b898e70960e01b815261ffff90921693820193845260243560208501526001600160401b0390921691830191909152606435606083015260843560808381019190915290918290819060a00103817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa90811561202657906001600160401b039160209491612004575b506001604051931b161615158152f35b61201c915060803d8111611024576110128183612d7b565b5050905038611ff4565b6040513d85823e3d90fd5b50346102985780600319360112610298577f000000000000000000000000000000000000000000000000000000000000000091600283101561207b5760208360ff60405191168152f35b634e487b7160e01b825260219052602490fd5b50903461063f578160031936011261063f576120a8615bbc565b600080516020615ca68339815191529060028254036120f35750600190557fe11c2112add17fb763d3bd59f63b10429c3e11373da4fb8ef6725107a2fdc4b06020604051838152a180f35b604051637e38d1d360e11b8152fd5b50346102985780600319360112610298576020610e1b614d87565b50346102985780600319360112610298577f000000000000000000000000000000000000000000000000000000000000000060405191600282101561216457602083838152f35b634e487b7160e01b815260218452602490fd5b503461029857806003193601126102985760206001600160401b037fad78307a8b51804c575f26039dcb87c58925afb3b7c08732f3b21b942aed7a765416604051908152f35b50346102985760203660031901126102985761ffff6121da612bf6565b16600052600080516020615c46833981519152602052602060ff60406000205416604051908152f35b50903461063f5760208060031936011261063b5761221f612c18565b8092612229615bbc565b60408051636dacd3d760e01b8152309281019283526001600160a01b0393841660208401529291849184918290030181887f000000000000000000000000000000000000000000000000000000000000000086165af19182156109685785926122c6575b509160ff7f2fb241a51a63da05063ac6be1f963395b281e455e8085bd246a7e8502b8950d594926040948551941684521690820152a180f35b939150918084813d8311612321575b6122df8183612d7b565b81010312610f165760ff6040936123167f2fb241a51a63da05063ac6be1f963395b281e455e8085bd246a7e8502b8950d596612f20565b93955091935061228d565b503d6122d5565b50903461063f57602036600319011261063f576001600160401b03610771926060610100612354612c71565b926040519061236282612d0e565b808252806020830152806040830152808483015280608083015260a08201528260c08201528260e0820152015216600052600080516020615ce68339815191526020526124366006604060002061241a604051946123bf86612d0e565b825486526001830154602087015260028301546001600160481b0381166040880152604881901c6001600160401b0316606088015260881c61ffff16608087015260038301546001600160a01b031660a087015282016136fe565b60c085015261242b600582016136fe565b60e0850152016136fe565b6101008201526040519182916020835280516020840152602081015160408401526001600160481b0360408201511660608401526001600160401b03606082015116608084015261ffff60808201511660a084015260018060a01b0360a08201511660c084015260c08101516124ba610120918260e0870152610140860190612cc0565b906101006124d960e085015193601f19948589830301848a0152612cc0565b930151918584030190850152612cc0565b50903461063f57602036600319011261063f5761251c6106989161250c615bbc565b612514614d87565b809135615abe565b614f5b565b50903461063f57604036600319011261063f5761069890611a80612543612c07565b9161250c615bbc565b50903461063f5760208060031936011261063b57612568612c18565b90612571615bbc565b612579614ea7565b813b156129d2577f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b039384169081179091557fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b8580a27f7487ca88d037ca20519908b1ee7556206bef53bce0226a348750cb9d4f688e4e9283549261261460ff851615614eeb565b60ff199384166001178555303b156129ce5760405163011fa75760e71b81528681848183305af180156128c4576129bb575b5060405163689f90c360e01b815283818481305afa9081156128c4578791612986575b50156126a2575b5050507f5443fea4dc453d96b81ce55b62e11a4094cc4cbb8a360956a7253cfdb42506cb818154169055815416905580f35b604051637e062a3560e11b815283818481305afa80156128c457879061294e575b6126f3915082167f0000000000000000000000000000000000000000000000000000000000000000831614614eeb565b6040516314ad290960e11b815283818481305afa9081156128c4578791612918575b507f0000000000000000000000000000000000000000000000000000000000000000906002821015612905576002811015612905576127549114614eeb565b604051634d4502c960e11b815283818481305afa80156128c45787906128cf575b6127a8915061ffff807f000000000000000000000000000000000000000000000000000000000000000016911614614eeb565b604051635e280f1160e01b815283818481305afa9081156128c457879161288a575b50816127fa927f000000000000000000000000000000000000000000000000000000000000000016911614614eeb565b816040518092631d2a9eff60e21b825281305afa9081156109685761285592869261285d575b50506001600160401b03807f000000000000000000000000000000000000000000000000000000000000000016911614614eeb565b388080612670565b61287c9250803d10612883575b6128748183612d7b565b810190614b65565b3880612820565b503d61286a565b90508381813d83116128bd575b6128a18183612d7b565b81010312610a965751908082168203610a9657906127fa6127ca565b503d612897565b6040513d89823e3d90fd5b508381813d83116128fe575b6128e58183612d7b565b81010312610a96576128f96127a891613351565b612775565b503d6128db565b634e487b7160e01b885260218452602488fd5b90508381813d8311612947575b61292f8183612d7b565b81010312610a9657516002811015610a965738612715565b503d612925565b508381813d831161297f575b6129648183612d7b565b81010312610a9657518181168103610a96576126f3906126c3565b503d61295a565b90508381813d83116129b4575b61299d8183612d7b565b81010312610a96576129ae90612f2e565b38612669565b503d612993565b6129c790969196612ce5565b9438612646565b8580fd5b826084916040519162461bcd60e51b8352820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152fd5b5061010036600319011261029857612a41612c07565b608435918215158303610298576001600160401b039260a43584811161063b57612a6e9036908701612c44565b60c494919435868111610f1657612a889036908901612c44565b9460e435908882116102985750612aa29036908a01612c44565b969092612aad615bf5565b6002600080516020615ca68339815191525414610f055792612ae260209a979593610edb612aea94610eea9b99973691612db7565b953691612db7565b94606435916044359135613b04565b503461029857602036600319011261029857612b13612c18565b600080516020615c86833981519152546001600160a01b0390612b37908216614f08565b600080516020615d2683398151915280546001600160a01b03198116938316938417909155167f51c4874e0f23f262e04a38c51751336dde72126d67f53eb672aaff02996b3ef68380a380f35b50346102985760203660031901126102985761065e612bd7612bd2604060209461ffff612baf612bf6565b612bb76132c9565b50168152600080516020615cc68339815191528652206151cf565b6154c7565b612bdf614d87565b906001600160401b0360ff82169160081c16615a63565b6004359061ffff821682036105ad57565b6024359061ffff821682036105ad57565b600435906001600160a01b03821682036105ad57565b602435906001600160a01b03821682036105ad57565b9181601f840112156105ad578235916001600160401b0383116105ad57602083818601950101116105ad57565b600435906001600160401b03821682036105ad57565b604435906001600160401b03821682036105ad57565b60005b838110612cb05750506000910152565b8181015183820152602001612ca0565b90602091612cd981518092818552858086019101612c9d565b601f01601f1916010190565b6001600160401b038111612cf857604052565b634e487b7160e01b600052604160045260246000fd5b61012081019081106001600160401b03821117612cf857604052565b606081019081106001600160401b03821117612cf857604052565b60a081019081106001600160401b03821117612cf857604052565b604081019081106001600160401b03821117612cf857604052565b90601f801991011681019081106001600160401b03821117612cf857604052565b6001600160401b038111612cf857601f01601f191660200190565b929192612dc382612d9c565b91612dd16040519384612d7b565b8294818452818301116105ad578281602093846000960137010152565b9080601f830112156105ad57816020612e0993359101612db7565b90565b60a09060031901126105ad5760043561ffff811681036105ad5790602435906044356001600160401b03811681036105ad57906064359060843590565b61ffff602091612e7b6040519485938493639057412d60e01b8552166004840152604060248401526044830190612cc0565b03817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156105a157600091612ebb575090565b906020823d8211612ee1575b81612ed460209383612d7b565b8101031261029857505190565b3d9150612ec7565b91908203918211612ef657565b634e487b7160e01b600052601160045260246000fd5b51906001600160801b03821682036105ad57565b519060ff821682036105ad57565b519081151582036105ad57565b91908260809103126105ad57612f5082612f0c565b91612f5d60208201612f0c565b91612e096060612f6f60408501612f20565b9301612f2e565b60405163b898e70960e01b815261ffff91909116600482015260248101919091526001600160401b039190911660448201526064810191909152608481019190915260808160a4816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156105a157600091612ffc575090565b613014915060803d8111611024576110128183612d7b565b5091505090565b90600080516020615c66833981519152805483101561306757600052601e8260041c7f34ce55f2b99e47d17ebf1d2c46b9bfbded3dc7f2fc1994ce754d522080e8355b019260011b1690565b634e487b7160e01b600052603260045260246000fd5b600080516020615c668339815191529081549160005b8381106130a05750505050565b6130a98161301b565b905460039161ffff9190831b1c8116858216146130ca575050600101613093565b9293509360001991828201918211612ef6576130f9846130ec6131149461301b565b905490891b1c169161301b565b90919082549060031b9161ffff809116831b921b1916179055565b825480156131355701926131278461301b565b81939154921b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b600080516020615c668339815191529081549160005b83811061318a5750600160401b831015612cf857826130f99160016131889501905561301b565b565b6131938161301b565b905461ffff908186169260031b1c16146131af57600101613161565b50505050565b61ffff166000818152600080516020615c4683398151915260209081526040808320549051635a6ad4fd60e11b8152306004820152602481019490945260ff16929190826044816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156132bd578091613281575b5060ff9150168082116132635761324a5750565b1561325157565b60405163831761d760e01b8152600490fd5b60449250604051916313c3d1b160e01b835260048301526024820152fd5b90506020823d82116132b5575b8161329b60209383612d7b565b8101031261029857506132af60ff91612f20565b38613236565b3d915061328e565b604051903d90823e3d90fd5b604051906132d682612d2a565b60006040838281528260208201520152565b81601f820112156105ad5780516132fe81612d9c565b9261330c6040519485612d7b565b818452602082840101116105ad57612e099160208085019101612c9d565b9060606040612e099380518452602081015160208501520151918160408201520190612cc0565b519061ffff821682036105ad57565b6040809201519161338f815193635399ded560e11b855260206004860152848060009384936024830190612cc0565b038173__$93083e246e55d56d98f3df2872cd16bfd0$__5af493841561352a578194613485575b505061ffff80606085015116907f00000000000000000000000000000000000000000000000000000000000000001680820361346a5750508061341c6133fa614d87565b806134178188516001600160401b0360ff82169160081c16615a63565b615abe565b930151908160a01c61345357506001600160a01b031690613441908290849086613536565b61344e5761318892614bd5565b505050565b60249250519063033b960d60e41b82526004820152fd5b6044925191631ee5902560e11b835260048301526024820152fd5b909193503d8083833e6134988183612d7b565b81019060208183031261063b5780516001600160401b0391828211610f16570160a08184031261095d578551936134ce85612d45565b81516001600160481b038116810361063f57855260208201516020860152868201518786015261350060608301613351565b60608601526080820151928311610298575061351d9291016132e8565b60808201529138806133b6565b509051903d90823e3d90fd5b90926001600160401b0360007f00000000000000000000000000000000000000000000000000000000000000008216156136bf57506135736132c9565b5061ffff8516600052600080516020615cc683398151915260205261359e612bd260406000206151cf565b6135a885826159ba565b81808660081c169160081c16105b6135d4575050506135ca6135cf9282615794565b61566e565b600090565b7f7f63c9251d82a933210c2b6d0b0f116252c3c116788120e64e8e8215df6f31629450906001602094926136996040519161360e83612d2a565b6001600160481b03958616835242811688840190815260a085901b859003958616604080860191825260008a81527f4e8909a01183a67951f5a6147d6e76ed02ea890c72afea01a9edee91edc609b88c522094518554925170ffffffffffffffffffffffffffffffffff19909316981697909717911660481b67ffffffffffffffff60481b16178255565b019151166bffffffffffffffffffffffff60a01b825416179055604051908152a1600190565b6135b6565b90600182811c921680156136f4575b60208310146136de57565b634e487b7160e01b600052602260045260246000fd5b91607f16916136d3565b9060405191826000825492613712846136c4565b90818452600194858116908160001461377f575060011461373c575b505061318892500383612d7b565b9093915060005260209081600020936000915b8183106137675750506131889350820101388061372e565b8554888401850152948501948794509183019161374f565b91505061318894506020925060ff191682840152151560051b820101388061372e565b8181106137ad575050565b600081556001016137a2565b6137c381546136c4565b90816137cd575050565b81601f600093116001146137df575055565b9080839182526137fe601f60208420940160051c8401600185016137a2565b5555565b6006613188916000815560006001820155600060028201556000600382015561382d600482016137b9565b613839600582016137b9565b016137b9565b919594939290956000968315613af2578115613ae0578215613ace577f000000000000000000000000000000000000000000000000000000000000000097613887308a614e07565b60018060a01b038a169060409a6138cc8c516323b872dd60e01b6020820152602499338b8301523060448301526064820152606481526138c681612d45565b84613d63565b6138df826138da3084614e07565b612ee9565b927f00000000000000000000000000000000000000000000000000000000000000006002811015613abb57600114613a39575b50505061ffff8316600052600080516020615c2683398151915260205260ff60018b6000200154168015613a285761395361394b614d87565b918284615abe565b9a6139706001600160401b03928d8460ff82169160081c16615a63565b808403613a025750507fad78307a8b51804c575f26039dcb87c58925afb3b7c08732f3b21b942aed7a769081549080821698818a146139f05750896139d695898f958f958f95988c928f9a928d9360018d0116906001600160401b031916179055613f0b565b6139e557612e09973394614567565b505050935050505090565b634e487b7160e01b8652601160045285fd5b60449189613a11869384612ee9565b9151926338f831a560e11b84526004840152820152fd5b8a5163ade64f0b60e01b8152600490fd5b803b15610f16578480918a8f5180948193630852cd8d60e31b83528960048401525af18015613ab157613a9c575b50613a73903090614e07565b808203613a805780613912565b60449250878c51926302156a8f60e01b84526004840152820152fd5b93613aaa613a739295612ce5565b9390613a67565b8d513d87823e3d90fd5b634e487b7160e01b865260216004528986fd5b60405163717f139360e11b8152600490fd5b604051634e46966960e11b8152600490fd5b604051631f2a200560e01b8152600490fd5b97969594939291906000938915613af2578215613ae0578315613ace57613b93997f000000000000000000000000000000000000000000000000000000000000000090613b513083614e07565b60018060a01b0383169260409d8e516323b872dd60e01b60208201526024943386830152306044830152606482015260648152613b8d81612d45565b85613d63565b613ba1826138da3084614e07565b937f00000000000000000000000000000000000000000000000000000000000000006002811015613d5057600114613cce575b50505061ffff8416600052600080516020615c2683398151915260205260ff60018d600020015416908115613cbd57613c369b9c613c1b613c13614d87565b938486615abe565b9c8d936001600160401b03948560ff82169160081c16615a63565b808503613cae5750507fad78307a8b51804c575f26039dcb87c58925afb3b7c08732f3b21b942aed7a769182549181831699828b14613c9d575050896139d695898f958f958f95988c928f9a928d9360018d0116906001600160401b031916179055613f0b565b634e487b7160e01b81526011600452fd5b90604492613a11869384612ee9565b8c5163ade64f0b60e01b8152600490fd5b803b156109e657898f918582935180948193630852cd8d60e31b83528a60048401525af18015613d4557613d30575b50613d09903090614e07565b808203613d165780613bd4565b6044928e51926302156a8f60e01b84526004840152820152fd5b98613d3e613d09929a612ce5565b9890613cfd565b508e513d8b823e3d90fd5b634e487b7160e01b8b526021600452848bfd5b604051613dc1916001600160a01b0316613d7c82612d60565b6000806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1613dbb613e42565b91613e72565b805180613dcd57505050565b818391810103126105ad5781613de39101612f2e565b15613deb5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b3d15613e6d573d90613e5382612d9c565b91613e616040519384612d7b565b82523d6000602084013e565b606090565b91929015613ed45750815115613e86575090565b3b15613e8f5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015613ee75750805190602001fd5b60405162461bcd60e51b81526020600482015290819061060f906024830190612cc0565b9998959097999691929493968a68ffffffffffffffff0060ff613f51613f4c613f356117d8614d87565b856001600160401b03858398169160081c16615a63565b615574565b60081b919091169116179760007f00000000000000000000000000000000000000000000000000000000000000006001600160401b0316156145625750613f966132c9565b50613fa2612bd2615187565b613fac8a826159ba565b6001600160401b03808b60081c169160081c16105b81158061455b575b614535578161452d575b50613ff357505050505050506135cf929350613fee816155dc565b615847565b6141339294969893959750906001600160401b03916140317f0000000000000000000000000000000000000000000000000000000000000000615970565b61403961522f565b60405191848c168352602083015260408201527ff33512b84e24a49905c26c6991942fc5a9652411769fc1e448f967cdb049f08a60603392a26040519a8b9661408188612d0e565b87526020870198895260408701956001600160481b0380921687526060880192844216845261ffff60808a019716875260e060a08a0199338b5260c081019b8c5201526101008d0152828a16600052600080516020615ce68339815191526020526040600020988c518a555160018a0155600289019551166001600160481b03198654161785555116839067ffffffffffffffff60481b82549160481b169067ffffffffffffffff60481b1916179055565b51815461ffff60881b191660889190911b61ffff60881b16179055516003830180546001600160a01b0319166001600160a01b0392909216919091179055518051906001600160401b038211612cf857819061419260048501546136c4565b601f81116144f8575b50602090601f83116001146144865760009261447b575b50508160011b916000199060031b1c19161760048201555b60e08401518051906001600160401b038211612cf8576141ed60058401546136c4565b601f8111614446575b50602090601f83116001146143d057918061010094926006946000926143c5575b50508160011b916000199060031b1c19161760058201555b019301519283516001600160401b038111612cf85761424e82546136c4565b601f8111614388575b506020601f82116001146142fb57908060209493927f69add1952a6a6b9cb86f04d05f0cb605cbb469a50ae916139d34495a9991481f96976000926142f0575b50508160011b916000199060031b1c19161790555b6001600160401b0360405191168152a1600080808034335af16142cd613e42565b50156142d857600190565b604051630b288dc560e21b8152346004820152602490fd5b015190503880614297565b601f198216958360005260206000209660005b818110614370575096600192849260209796957f69add1952a6a6b9cb86f04d05f0cb605cbb469a50ae916139d34495a9991481f999a10614357575b505050811b0190556142ac565b015160001960f88460031b161c1916905538808061434a565b8383015189556001909801976020938401930161430e565b6143b590836000526020600020601f840160051c810191602085106143bb575b601f0160051c01906137a2565b38614257565b90915081906143a8565b015190503880614217565b906005840160005260206000209160005b601f198516811061442e575092610100949260019260069583601f19811610614415575b505050811b01600582015561422f565b015160001960f88460031b161c19169055388080614405565b919260206001819286850151815501940192016143e1565b61447590600585016000526020600020601f850160051c810191602086106143bb57601f0160051c01906137a2565b386141f6565b0151905038806141b2565b9250600484016000526020600020906000935b601f19841685106144dd576001945083601f198116106144c4575b505050811b0160048201556141ca565b015160001960f88460031b161c191690553880806144b4565b81810151835560209485019460019093019290910190614499565b61452790600486016000526020600020601f850160051c810191602086106143bb57601f0160051c01906137a2565b3861419b565b905038613fd3565b60448361454061522f565b90604051916326fb55dd60e01b835260048301526024820152fd5b5080613fc9565b613fc1565b9591949293969790976145997f0000000000000000000000000000000000000000000000000000000000000000615970565b6145a38287612e49565b926145ae8434612ee9565b94604051996101608b018b81106001600160401b03821117612cf85761ffff9a6001600160401b0391604052168b526001600160481b038c1660208c015289891660408c01528760608c015260808b015260018060a01b031660a08a015260c089015260e08801526101008701526101208601526101408501526060608060405161463881612d45565b60008152600060208201526000604082015260008382015201526001600160481b036040519561466787612d45565b16855260018060a01b037f00000000000000000000000000000000000000000000000000000000000000001660208601526040850152166060830152604051918260208101106001600160401b03602085011117612cf85761ffff90602084016040526000845260808101938452600060606001600160401b038551169261474d60018060a01b0360a0880151169760405196879485946315cfa3cb60e11b8652602060048701526001600160481b038151166024870152602081015160448701526040810151606487015201511660848401525160a060a484015260c4830190612cc0565b038173__$93083e246e55d56d98f3df2872cd16bfd0$__5af480156105a1576147b6946000938492614b49575b506040519261478884612d2a565b835260208301526040820152604051809481926311692f3760e31b835260206004840152602483019061332a565b038173__$93083e246e55d56d98f3df2872cd16bfd0$__5af49182156105a157600092614b24575b5081516020830120610120820151602061ffff6040850151169182600052600080516020615c268339815191528252604060002054906148216080870151614b84565b6101008701516040516312a12adb60e01b815260048101969096526024860193909352604485018690526001600160a01b0316606485015260a060848501528391829081906148749060a4830190612cc0565b03917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165af180156105a157614b05575b5060608201517f75eb8927cc7c4810b30fa2e8011fce37da6da7d18eb82c642c367ae4445c362560a06080850151936148ec6020870151612bdf614d87565b9061012087015161ffff6040890151166001600160401b03895116916040519485526020850152604084015260608301526080820152a361ffff604082015116600052600080516020615c26833981519152602052600160406000200154916001600160801b038360081c1615614ae5576040516020810193600160f81b85526001600160801b03199060781b166021820152600060318201526021815261499381612d2a565b809360e0840151908151614a9c575b50505061014082015160408301516060840151608085015161ffff909216926001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016926149f590614b84565b9360c087015197843b156105ad57600096614a6e91614a5f614a4c6040519c8d9a8b998a9863c513d43760e01b8a5260048a0152602489015260018060a01b0316604488015260c0606488015260c4870190612cc0565b6003199384878303016084880152612cc0565b918483030160a4850152612cc0565b03925af19182156105a1576001600160401b0392614a8d575b50511690565b614a9690612ce5565b38614a87565b614abc93955091602091614adc9360405195869351809286860190612c9d565b8201614ad082518093868085019101612c9d565b01038084520182612d7b565b913880806149a2565b602461ffff6040840151166040519063d028893560e01b82526004820152fd5b614b1d9060203d602011612883576128748183612d7b565b50386148ad565b614b429192503d806000833e614b3a8183612d7b565b810190614bb0565b90386147de565b614b5e9192503d8086833e614b3a8183612d7b565b903861477a565b908160209103126105ad57516001600160401b03811681036105ad5790565b8060a01c614b98576001600160a01b031690565b6024906040519063033b960d60e41b82526004820152fd5b906020828203126105ad5781516001600160401b0381116105ad57612e0992016132e8565b9291614c0490612bd77f0000000000000000000000000000000000000000000000000000000000000000615970565b906000937f504e6efe18ab9eed10dc6501a417f5b12a2f7f2b1593aed9b89f9bce3cf29a918580a27f0000000000000000000000000000000000000000000000000000000000000000936002851015614d735784614ccb575060405163a9059cbb60e01b60208201526001600160a01b0391909116602482015260448082019290925290815290915061318890614c9c606482612d7b565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316613d63565b919360018103614d5757507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b1561063b576040516340c10f1960e01b81526001600160a01b03919091166004820152602481019490945291929181908390604490829084905af19081156132bd5750614d4e575b50565b61318890612ce5565b6040516366001a8960e01b815260ff9091166004820152602490fd5b634e487b7160e01b81526021600452602490fd5b600080604051602081019063313ce56760e01b825260048152614da981612d60565b51907f00000000000000000000000000000000000000000000000000000000000000005afa614dd6613e42565b9015614df5576020818051810103126105ad576020612e099101612f20565b604051631222cd8360e01b8152600490fd5b6040516370a0823160e01b602082019081526001600160a01b039093166024808301919091528152600092839291614e3e81612d2a565b51915afa614e4a613e42565b9015614df5576020818051810103126105ad576020015190565b600080516020615c668339815191525460005b818110614e82575050565b80614ea161ffff614e9460019461301b565b90549060031b1c166131b5565b01614e77565b307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614614ed957565b604051633c64f99360e21b8152600490fd5b15614ef257565b634e487b7160e01b600052600160045260246000fd5b600080516020615d2683398151915254336001600160a01b039182161415919082614f4e575b5050614f3657565b60405163e2a08e5d60e01b8152336004820152602490fd5b1633141590503880614f2e565b600080516020615d46833981519152805491614f75614d87565b916001600160481b0393848116916001600160401b0395868360081c169384158061517c575b156150525750856150459460ff6040989585615036957f7e3b0fc388be9d36273f66210aed83be975df3a9adfffa4c734033f498f362cd9c9a9754600160481b600160901b038960481b1690600160481b600160901b0319161782555b815479ffffffffffffffff000000000000000000ffffffffffffffffff1916908816174260901b67ffffffffffffffff60901b161790551690615a63565b9360ff82169160081c16615a63565b82519182526020820152a1565b61505d612bd2615187565b8660009261506b8282615998565b156151255761507b9293506159ef565b6150858183615998565b1561511c57615093916159ef565b61509d8682615998565b6150fa57956150459460ff82957f7e3b0fc388be9d36273f66210aed83be975df3a9adfffa4c734033f498f362cd9a989561503695600160481b600160901b0360409c60481b16600160481b600160901b03198416178255614ff8565b604051631e74e8fb60e31b815290821660048201529085166024820152604490fd5b5060ff16615093565b61512e916159ef565b9061513982826159ba565b89808360081c169160081c160191898311615168575060ff68ffffffffffffffff0091169160081b1617615093565b634e487b7160e01b81526011600452602490fd5b5060ff841615614f9b565b6040519061519482612d2a565b8160406001600160401b03600080516020615d46833981519152546001600160481b0380821685528160481c16602085015260901c16910152565b906040516151dc81612d2a565b60406001600160401b038294546001600160481b0380821685528160481c16602085015260901c16910152565b90600160481b600160901b0382549160481b1690600160481b600160901b031916179055565b6152376132c9565b50612e09612bd7612bd2615187565b9061ffff16906000828152600080516020615cc68339815191526020526040812091825491615273614d87565b926001600160481b0394858216956001600160401b0396878460081c1694851580615426575b15615342575050856153359460ff6040989561532e86615036966152e08c7f739ed886fd81a3ddc9f4b327ab69152e513cd45b26fda0c73660eaca8e1193019f9d9a615209565b815467ffffffffffffffff60901b19918a169190911679ffffffffffffffff000000000000000000ffffffffffffffffff1990911617428c1660901b67ffffffffffffffff60901b16179055565b1690615a63565b82519182526020820152a2565b61534e612bd2856151cf565b908761535a8185615998565b156153e15761536a9192936159ef565b906153758284615998565b1590506153d857615385916159ef565b61538f8682615998565b6150fa57956153359460ff829561532e86615036966153d360409d7f739ed886fd81a3ddc9f4b327ab69152e513cd45b26fda0c73660eaca8e1193019f9d9a615209565b6152e0565b5060ff16615385565b926153ec91936159ef565b906153f782826159ba565b89808360081c169160081c160191898311615168575060ff68ffffffffffffffff0091169160081b1617615385565b5060ff851615615299565b6154396132c9565b506000527f4e8909a01183a67951f5a6147d6e76ed02ea890c72afea01a9edee91edc609b860205260406000206040519061547382612d2a565b80546001600160481b038116835260481c6001600160401b03166020830152600101546001600160a01b0316604082015290565b81156154b1570490565b634e487b7160e01b600052601260045260246000fd5b6001600160401b0390817f00000000000000000000000000000000000000000000000000000000000000001691821561555c57615549828260ff93604068ffffffffffffffff009601511642038161552d602085019889519383875160081c16026154a7565b9160081c1601915160081c168082106000146155555750615574565b9251169160081b161790565b9050615574565b506020015160ff1668ffffffffffffffff0017919050565b6001600160401b0390818111615588571690565b60405162461bcd60e51b815260206004820152602660248201527f53616665436173743a2076616c756520646f65736e27742066697420696e203660448201526534206269747360d01b6064820152608490fd5b6001600160401b037f00000000000000000000000000000000000000000000000000000000000000001615614d4b576156136132c9565b5061561f612bd2615187565b600160481b600160901b03615645600080516020615d46833981519152938454936159ef565b60481b16906001600160401b0360901b4260901b1690600160481b600160d01b03191617179055565b6001600160401b0390817f00000000000000000000000000000000000000000000000000000000000000001615615790576156a76132c9565b506156b3612bd2615187565b90600160481b600160901b03600080516020615d4683398151915293845493816001600160401b0360901b4260901b16946156ee81846159ba565b60081c16828260081c160182811160001461577b575068ffffffffffffffff0060ff61571984615574565b92169160081b1617906001600160481b0385169061573782846159ba565b80856001600160401b0360901b1988161760081c16908360081c161060001461577457505b60481b1691600160481b600160d01b03191617179055565b905061575c565b60ff61571968ffffffffffffffff0092615574565b5050565b906001600160401b0390817f0000000000000000000000000000000000000000000000000000000000000000161561344e576131889261ffff615841926157d96132c9565b501680600052600080516020615cc683398151915280602052615802612bd260406000206151cf565b60009283526020919091526040909120805467ffffffffffffffff60901b19164290951660901b67ffffffffffffffff60901b169490941784556159ef565b90615209565b6001600160401b03807f0000000000000000000000000000000000000000000000000000000000000000161561344e5761ffff613188936158866132c9565b5016600090808252600080516020615cc6833981519152806020526158b0612bd2604085206151cf565b91835260205260408220805467ffffffffffffffff60901b191642851660901b67ffffffffffffffff60901b161781559383906158ed81846159ba565b60081c16838260081c160183811160001461595b575068ffffffffffffffff0060ff61591885615574565b92169160081b1617918084546001600160481b0381169361593985876159ba565b5060081c16908360081c1610600014615953575090615209565b905090615209565b60ff61591868ffffffffffffffff0092615574565b46810361597a5750565b604490604051906377d879fb60e01b82526004820152466024820152fd5b906159a381836159ba565b6001600160401b03809160081c169160081c161190565b60ff91821691168181036159cc575050565b604051635ce6db6160e11b815260ff918216600482015291166024820152604490fd5b6159f982826159ba565b6001600160401b03809260081c16828260081c1603918211612ef65760ff68ffffffffffffffff0091169160081b161790565b60ff811660081015612e095750600890565b9060ff8091169116039060ff8211612ef657565b60ff16604d8111612ef657600a0a90565b919060ff821660ff8216818114615ab7571115615a9657615a8b612e099392615a9092615a3e565b615a52565b906154a7565b615a8b90615aa392615a3e565b90818102918183041490151715612ef65790565b5050505090565b615af2613f4c60ff9268ffffffffffffffff009495615adc87615a2c565b908581168683161015615afd5750958691615a63565b92169160081b161790565b9050958691615a63565b60ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c1615615b3657565b604051631afcd79f60e31b8152600490fd5b6001600160a01b03908116908115615ba357600080516020615c8683398151915280546001600160a01b031981168417909155167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3565b604051631e4fbdf760e01b815260006004820152602490fd5b600080516020615c86833981519152546001600160a01b03163303615bdd57565b60405163118cdaa760e01b8152336004820152602490fd5b600080516020615d068339815191526002815414615c135760029055565b604051633ee5aeb560e01b8152600490fdfeebcc9f646b0f459ff8f387587d536b0af3484cf442e1577400f322334e7d1ef047028352a8b3feae1a85fba43bc13f990568bb1637dce33d3dbfd791a0808b7f3b7aba7dd7414fe6f86067371f2a5b07a6b0344c1b86c5f36adb59df9895cd629016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930064bacf405c5d7f563d3ba5252584a52c37e4fee380fd825b10666c27b8258022efb21dcaedea63b55c44882f329622e13a8d0f5b947b3a372826208a9003da15852fa0677fef8612c6c15b518c9fa56761e9ed15cfd5c6e5399e5467985ac7ed9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00bfa91572ce1e5fe8776a160d3b1f862e83f5ee2c080a7423b4761602a3ad12497c25289a27ec8c9be54d4a154cf80490d69bda989cdb8328232e08fea9220420a26469706673582212208433ce73412cdd47522f0b53ef4591451befbcc3e4f6c57195bc7e5ed139c63d64736f6c63430008130033"; + +type NttManagerConstructorParams = + | [linkLibraryAddresses: NttManagerLibraryAddresses, signer?: Signer] + | ConstructorParameters; + +const isSuperArgs = ( + xs: NttManagerConstructorParams +): xs is ConstructorParameters => { + return ( + typeof xs[0] === "string" || + (Array.isArray as (arg: any) => arg is readonly any[])(xs[0]) || + "_isInterface" in xs[0] + ); +}; + +export class NttManager__factory extends ContractFactory { + constructor(...args: NttManagerConstructorParams) { + if (isSuperArgs(args)) { + super(...args); + } else { + const [linkLibraryAddresses, signer] = args; + super( + _abi, + NttManager__factory.linkBytecode(linkLibraryAddresses), + signer + ); + } + } + + static linkBytecode( + linkLibraryAddresses: NttManagerLibraryAddresses + ): string { + let linkedBytecode = _bytecode; + + linkedBytecode = linkedBytecode.replace( + new RegExp("__\\$93083e246e55d56d98f3df2872cd16bfd0\\$__", "g"), + linkLibraryAddresses[ + "src/libraries/TransceiverStructs.sol:TransceiverStructs" + ] + .replace(/^0x/, "") + .toLowerCase() + ); + + return linkedBytecode; + } + + override getDeployTransaction( + _endpoint: AddressLike, + _executor: AddressLike, + _token: AddressLike, + _mode: BigNumberish, + _chainId: BigNumberish, + _rateLimitDuration: BigNumberish, + _skipRateLimiting: boolean, + overrides?: NonPayableOverrides & { from?: string } + ): Promise { + return super.getDeployTransaction( + _endpoint, + _executor, + _token, + _mode, + _chainId, + _rateLimitDuration, + _skipRateLimiting, + overrides || {} + ); + } + override deploy( + _endpoint: AddressLike, + _executor: AddressLike, + _token: AddressLike, + _mode: BigNumberish, + _chainId: BigNumberish, + _rateLimitDuration: BigNumberish, + _skipRateLimiting: boolean, + overrides?: NonPayableOverrides & { from?: string } + ) { + return super.deploy( + _endpoint, + _executor, + _token, + _mode, + _chainId, + _rateLimitDuration, + _skipRateLimiting, + overrides || {} + ) as Promise< + NttManager & { + deploymentTransaction(): ContractTransactionResponse; + } + >; + } + override connect(runner: ContractRunner | null): NttManager__factory { + return super.connect(runner) as NttManager__factory; + } + + static readonly bytecode = _bytecode; + static readonly abi = _abi; + static createInterface(): NttManagerInterface { + return new Interface(_abi) as NttManagerInterface; + } + static connect(address: string, runner?: ContractRunner | null): NttManager { + return new Contract(address, _abi, runner) as unknown as NttManager; + } +} + +export interface NttManagerLibraryAddresses { + ["src/libraries/TransceiverStructs.sol:TransceiverStructs"]: string; +} diff --git a/evm/ts/src/ethers-contracts/2_0_0/factories/index.ts b/evm/ts/src/ethers-contracts/2_0_0/factories/index.ts new file mode 100644 index 000000000..844e137ef --- /dev/null +++ b/evm/ts/src/ethers-contracts/2_0_0/factories/index.ts @@ -0,0 +1,4 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +export { NttManager__factory } from "./NttManager__factory.js"; diff --git a/evm/ts/src/ethers-contracts/2_0_0/index.ts b/evm/ts/src/ethers-contracts/2_0_0/index.ts new file mode 100644 index 000000000..ad2af3ee6 --- /dev/null +++ b/evm/ts/src/ethers-contracts/2_0_0/index.ts @@ -0,0 +1,6 @@ +/* Autogenerated file. Do not edit manually. */ +/* tslint:disable */ +/* eslint-disable */ +export type { NttManager } from "./NttManager.js"; +export * as factories from "./factories/index.js"; +export { NttManager__factory } from "./factories/NttManager__factory.js"; diff --git a/evm/ts/src/ethers-contracts/index.ts b/evm/ts/src/ethers-contracts/index.ts index 2e32dc983..d9d465421 100644 --- a/evm/ts/src/ethers-contracts/index.ts +++ b/evm/ts/src/ethers-contracts/index.ts @@ -1,6 +1,7 @@ import * as __0_1_0 from "./0_1_0/index.js"; import * as __1_0_0 from "./1_0_0/index.js"; import * as __1_1_0 from "./1_1_0/index.js"; +import * as __2_0_0 from "./2_0_0/index.js"; const _0_1_0 = { NttManager: { @@ -29,4 +30,10 @@ const _1_1_0 = { }, }; -export { _0_1_0, _1_0_0, _1_1_0 }; +const _2_0_0 = { + NttManager: { + connect: __2_0_0.NttManager__factory.connect, + }, +}; + +export { _0_1_0, _1_0_0, _1_1_0, _2_0_0 }; diff --git a/evm/ts/src/executor/BinaryReader.ts b/evm/ts/src/executor/BinaryReader.ts new file mode 100644 index 000000000..ba387fb86 --- /dev/null +++ b/evm/ts/src/executor/BinaryReader.ts @@ -0,0 +1,106 @@ +import { Buffer } from "buffer"; + +export function isValidHexString(s: string): boolean { + return /^(0x)?[0-9a-fA-F]+$/.test(s); +} + +export function hexToBuffer(s: string): Buffer { + if (!isValidHexString(s)) { + throw new Error(`${s} is not hex`); + } + if (s.startsWith("0x")) { + s = s.slice(2); + } + s.padStart(s.length + (s.length % 2), "0"); + return Buffer.from(s, "hex"); +} + +export function hexToUint8Array(s: string): Uint8Array { + return new Uint8Array(hexToBuffer(s)); +} + +export function uint8ArrayToHex(b: Uint8Array): `0x${string}` { + return `0x${Buffer.from(b).toString("hex")}`; +} + +// BinaryReader provides the inverse of BinaryWriter +// Numbers are encoded as big endian +export class BinaryReader { + private _buffer: Buffer; + private _offset: number; + + constructor( + arrayBufferOrString: + | WithImplicitCoercion + | string + ) { + if (typeof arrayBufferOrString === "string") { + this._buffer = hexToBuffer(arrayBufferOrString); + } else { + this._buffer = Buffer.from(arrayBufferOrString); + } + this._offset = 0; + } + + length(): number { + return this._buffer.length; + } + + offset(): number { + return this._offset; + } + + readUint8(): number { + const tmp = this._buffer.readUint8(this._offset); + this._offset += 1; + return tmp; + } + + readUint16(): number { + const tmp = this._buffer.readUint16BE(this._offset); + this._offset += 2; + return tmp; + } + + readUint32(): number { + const tmp = this._buffer.readUint32BE(this._offset); + this._offset += 4; + return tmp; + } + + readUint64(): bigint { + const tmp = this._buffer.readBigUInt64BE(this._offset); + this._offset += 8; + return tmp; + } + + readUint128(): bigint { + const tmp = this._buffer.subarray(this._offset, this._offset + 16); + this._offset += 16; + return BigInt(`0x${tmp.toString("hex") || "0"}`); + } + + readUint256(): bigint { + const tmp = this._buffer.subarray(this._offset, this._offset + 32); + this._offset += 32; + return BigInt(`0x${tmp.toString("hex") || "0"}`); + } + + readUint8Array(length: number): Uint8Array { + const tmp = this._buffer.subarray(this._offset, this._offset + length); + this._offset += length; + return new Uint8Array(tmp); + } + + readHex(length: number): `0x${string}` { + return uint8ArrayToHex(this.readUint8Array(length)); + } + + readString(length: number): string { + const tmp = this._buffer + .subarray(this._offset, this._offset + length) + .toString(); + this._offset += length; + return tmp; + } +} diff --git a/evm/ts/src/executor/BinaryWriter.ts b/evm/ts/src/executor/BinaryWriter.ts new file mode 100644 index 000000000..61b05c497 --- /dev/null +++ b/evm/ts/src/executor/BinaryWriter.ts @@ -0,0 +1,91 @@ +import { Buffer } from "buffer"; +import { hexToUint8Array, uint8ArrayToHex } from "./BinaryReader.js"; + +export const MAX_U64 = 18446744073709551615n; +export const MAX_U128 = 340282366920938463463374607431768211455n; +export const MAX_U256 = + 115792089237316195423570985008687907853269984665640564039457584007913129639935n; + +// BinaryWriter appends data to the end of a buffer, resizing the buffer as needed +// Numbers are encoded as big endian +export class BinaryWriter { + private _buffer: Buffer; + private _offset: number; + + constructor(initialSize: number = 1024) { + if (initialSize < 0) throw new Error("Initial size must be non-negative"); + this._buffer = Buffer.alloc(initialSize); + this._offset = 0; + } + + // Ensure the buffer has the capacity to write `size` bytes, otherwise allocate more memory + _ensure(size: number) { + const remaining = this._buffer.length - this._offset; + if (remaining < size) { + const oldBuffer = this._buffer; + const newSize = this._buffer.length * 2 + size; + this._buffer = Buffer.alloc(newSize); + oldBuffer.copy(new Uint8Array(this._buffer)); + } + } + + writeUint8(value: number) { + if (value < 0 || value > 255) throw new Error("Invalid value"); + this._ensure(1); + this._buffer.writeUint8(value, this._offset); + this._offset += 1; + return this; + } + + writeUint16(value: number) { + if (value < 0 || value > 65535) throw new Error("Invalid value"); + this._ensure(2); + this._offset = this._buffer.writeUint16BE(value, this._offset); + return this; + } + + writeUint32(value: number) { + if (value < 0 || value > 4294967295) throw new Error("Invalid value"); + this._ensure(4); + this._offset = this._buffer.writeUint32BE(value, this._offset); + return this; + } + + writeUint64(value: bigint) { + if (value < 0n || value > MAX_U64) throw new Error("Invalid value"); + this._ensure(8); + this._offset = this._buffer.writeBigUInt64BE(value, this._offset); + return this; + } + + writeUint128(value: bigint) { + if (value < 0n || value > MAX_U128) throw new Error("Invalid value"); + return this.writeHex(value.toString(16).padStart(16 * 2, "0")); + } + + writeUint256(value: bigint) { + if (value < 0n || value > MAX_U256) throw new Error("Invalid value"); + return this.writeHex(value.toString(16).padStart(32 * 2, "0")); + } + + writeUint8Array(value: Uint8Array) { + this._ensure(value.length); + this._buffer.set(value, this._offset); + this._offset += value.length; + return this; + } + + writeHex(value: string) { + return this.writeUint8Array(hexToUint8Array(value)); + } + + data(): Uint8Array { + const copy = new Uint8Array(this._offset); + copy.set(this._buffer.subarray(0, this._offset)); + return copy; + } + + toHex(): `0x${string}` { + return uint8ArrayToHex(this.data()); + } +} diff --git a/evm/ts/src/executor/fetch.ts b/evm/ts/src/executor/fetch.ts new file mode 100644 index 000000000..b0e73d8fe --- /dev/null +++ b/evm/ts/src/executor/fetch.ts @@ -0,0 +1,23 @@ +// TODO: these functions belong in an sdk for the executor (where the types can be shared) +import axios from "axios"; +import { Chain, chainToChainId } from "@wormhole-foundation/sdk"; +export async function fetchQuote( + srcChain: Chain, + dstChain: Chain +): Promise<`0x${string}`> { + const ret = await axios.get( + `http://executor:3000/v0/quote/${chainToChainId(srcChain)}/${chainToChainId( + dstChain + )}` + ); + return ret.data.signedQuote; +} +export async function fetchEstimate( + quote: `0x${string}`, + relayInstructions: `0x${string}` +): Promise { + const ret = await axios.get( + `http://executor:3000/v0/estimate/${quote}/${relayInstructions}/` + ); + return BigInt(ret.data.estimate); +} diff --git a/evm/ts/src/executor/relayInstructions.ts b/evm/ts/src/executor/relayInstructions.ts new file mode 100644 index 000000000..a42fdde60 --- /dev/null +++ b/evm/ts/src/executor/relayInstructions.ts @@ -0,0 +1,92 @@ +import { BinaryReader } from "./BinaryReader.js"; +import { BinaryWriter } from "./BinaryWriter.js"; + +const RECV_INST_TYPE_GAS = 1; +const RECV_INST_TYPE_DROP_OFF = 2; + +interface RelayInstruction { + type: string; +} + +interface GasInstruction extends RelayInstruction { + type: "GasInstruction"; + gasLimit: bigint; + msgValue: bigint; +} + +interface GasDropOffInstruction extends RelayInstruction { + type: "GasDropOffInstruction"; + dropOff: bigint; + recipient: string; +} + +type RelayInstructions = (GasInstruction | GasDropOffInstruction)[]; + +export function decodeRelayInstructions( + relayInstructionsBytes: + | WithImplicitCoercion + | string +): RelayInstructions { + const relayInstructions: RelayInstructions = []; + const reader = new BinaryReader(relayInstructionsBytes); + while (reader.offset() < reader.length()) { + const type = reader.readUint8(); + if (type === RECV_INST_TYPE_GAS) { + relayInstructions.push({ + type: "GasInstruction", + gasLimit: reader.readUint128(), + msgValue: reader.readUint128(), + }); + } else if (type === RECV_INST_TYPE_DROP_OFF) { + relayInstructions.push({ + type: "GasDropOffInstruction", + dropOff: reader.readUint128(), + recipient: reader.readHex(32), + }); + } else { + throw new Error(`unsupported relay instruction type: ${type}`); + } + } + if (reader.offset() > reader.length()) { + throw new Error(`unable to decode relay instructions`); + } + return relayInstructions; +} + +export function encodeRelayInstructions( + relayInstructions: RelayInstructions +): `0x${string}` { + const writer = new BinaryWriter(); + for (const relayInstruction of relayInstructions) { + if (relayInstruction.type === "GasInstruction") { + writer + .writeUint8(RECV_INST_TYPE_GAS) + .writeUint128(relayInstruction.gasLimit) + .writeUint128(relayInstruction.msgValue); + } else if (relayInstruction.type === "GasDropOffInstruction") { + // TODO: enforce length on recipient + writer + .writeUint8(RECV_INST_TYPE_DROP_OFF) + .writeUint128(relayInstruction.dropOff) + .writeHex(relayInstruction.recipient); + } + } + return writer.toHex(); +} + +export function totalGasLimitAndMsgValue( + relayInstructions: RelayInstructions +): { + gasLimit: bigint; + msgValue: bigint; +} { + let gasLimit = 0n; + let msgValue = 0n; + for (const relayInstruction of relayInstructions) { + if (relayInstruction.type === "GasInstruction") { + gasLimit += relayInstruction.gasLimit; + msgValue += relayInstruction.msgValue; + } + } + return { gasLimit, msgValue }; +} diff --git a/evm/ts/src/ntt.ts b/evm/ts/src/ntt.ts index 07b4cb8cf..68d6d89ca 100644 --- a/evm/ts/src/ntt.ts +++ b/evm/ts/src/ntt.ts @@ -42,6 +42,9 @@ import { NttTransceiverBindings, loadAbiVersion, } from "./bindings.js"; +import { BinaryWriter } from "./executor/BinaryWriter.js"; +import { fetchEstimate, fetchQuote } from "./executor/fetch.js"; +import { encodeRelayInstructions } from "./executor/relayInstructions.js"; export class EvmNttWormholeTranceiver implements @@ -194,7 +197,7 @@ export class EvmNtt readonly chain: C, readonly provider: Provider, readonly contracts: Contracts & { ntt?: Ntt.Contracts }, - readonly version: string = "1.0.0" + readonly version: string = "2.0.0" ) { if (!contracts.ntt) throw new Error("No Ntt Contracts provided"); @@ -214,32 +217,6 @@ export class EvmNtt ); this.xcvrs = []; - if ( - "wormhole" in contracts.ntt.transceiver && - contracts.ntt.transceiver["wormhole"] - ) { - const transceiverTypes = [ - "wormhole", // wormhole xcvr should be ix 0 - ...Object.keys(contracts.ntt.transceiver).filter((transceiverType) => { - transceiverType !== "wormhole"; - }), - ]; - transceiverTypes.map((transceiverType) => { - // we currently only support wormhole transceivers - if (transceiverType !== "wormhole") { - throw new Error(`Unsupported transceiver type: ${transceiverType}`); - } - - // Enable more Transceivers here - this.xcvrs.push( - new EvmNttWormholeTranceiver( - this, - contracts.ntt!.transceiver[transceiverType]!, - abiBindings! - ) - ); - }); - } } async getTransceiver(ix: number): Promise | null> { @@ -290,8 +267,8 @@ export class EvmNtt yield this.createUnsignedTx(tx, "Ntt.setPauser"); } - async getThreshold(): Promise { - return Number(await this.manager.getThreshold()); + async getThreshold(chainId: number): Promise { + return Number(await this.manager.getThreshold(chainId)); } async isRelayingAvailable(destination: Chain): Promise { @@ -309,13 +286,15 @@ export class EvmNtt } async getIsExecuted(attestation: Ntt.Attestation): Promise { - const payload = - attestation.payloadName === "WormholeTransfer" - ? attestation.payload - : attestation.payload["payload"]; - const isExecuted = await this.manager.isMessageExecuted( - Ntt.messageDigest(attestation.emitterChain, payload["nttManagerPayload"]) - ); + // const payload = + // attestation.payloadName === "WormholeTransfer" + // ? attestation.payload + // : attestation.payload["payload"]; + const isExecuted = false; + // TODO: err... fix this + // await this.manager.isMessageExecuted( + // Ntt.messageDigest(attestation.emitterChain, payload["nttManagerPayload"]) + // ); if (!isExecuted) return false; // Also check that the transfer is not queued for it to be considered complete return !(await this.getIsTransferInboundQueued(attestation)); @@ -337,13 +316,15 @@ export class EvmNtt } getIsApproved(attestation: Ntt.Attestation): Promise { - const payload = - attestation.payloadName === "WormholeTransfer" - ? attestation.payload - : attestation.payload["payload"]; - return this.manager.isMessageApproved( - Ntt.messageDigest(attestation.emitterChain, payload["nttManagerPayload"]) - ); + // TODO: err... fix this + return Promise.resolve(true); + // const payload = + // attestation.payloadName === "WormholeTransfer" + // ? attestation.payload + // : attestation.payload["payload"]; + // return this.manager.isMessageApproved( + // Ntt.messageDigest(attestation.emitterChain, payload["nttManagerPayload"]) + // ); } async getTokenDecimals(): Promise { @@ -365,6 +346,7 @@ export class EvmNtt return { address: { chain: chain, address: toUniversal(chain, peerAddress) }, tokenDecimals: Number(peer.tokenDecimals), + gasLimit: peer.gasLimit, inboundLimit: await this.getInboundLimit(chain), }; } @@ -426,7 +408,7 @@ export class EvmNtt dstChain: Chain, options: Ntt.TransferOptions ): Promise { - const [, totalPrice] = await this.manager.quoteDeliveryPrice( + const totalPrice = await this.manager.quoteDeliveryPrice( toChainId(dstChain), Ntt.encodeTransceiverInstructions(this.encodeOptions(options)) ); @@ -436,12 +418,14 @@ export class EvmNtt async *setPeer( peer: ChainAddress, tokenDecimals: number, + gasLimit: bigint, inboundLimit: bigint ) { const tx = await this.manager.setPeer.populateTransaction( toChainId(peer.chain), universalAddress(peer), tokenDecimals, + gasLimit, inboundLimit ); yield this.createUnsignedTx(tx, "Ntt.setPeer"); @@ -466,11 +450,48 @@ export class EvmNtt ): AsyncGenerator> { const senderAddress = new EvmAddress(sender).toString(); + const peer = await this.getPeer(destination.chain); + if (!peer) { + throw new Error(`null peer for destination chain ${destination.chain}`); + } + const quote = await fetchQuote(this.chain, destination.chain); + const relayInstruction = encodeRelayInstructions([ + { type: "GasInstruction", gasLimit: peer.gasLimit, msgValue: 0n }, + ]); + const executorEstimate = await fetchEstimate(quote, relayInstruction); + + // assemble the transceiver instructions + // like https://github.com/wormholelabs-xyz/example-messaging-adapter-wormhole-guardians/blob/7deb17c52d95b252435740a1047902a857b5fea8/evm/src/WormholeGuardiansAdapterWithExecutor.sol#L132-L152 + // and https://github.com/wormholelabs-xyz/example-messaging-endpoint/blob/0f853ea0335937d611217f5048d677a4f46249fd/evm/src/libraries/AdapterInstructions.sol#L37-L66 + const wgaGasLimit = 1000000n; // determine some amount of gas based on destination chain + const wgaRelayInstruction = encodeRelayInstructions([ + { type: "GasInstruction", gasLimit: wgaGasLimit, msgValue: 0n }, + ]); + const wgaExecutorEstimate = await fetchEstimate(quote, wgaRelayInstruction); + const transceiversInstruction = new BinaryWriter() + .writeUint8(1) // version + .writeUint256(wgaExecutorEstimate) + .writeUint16((quote.length - 2) / 2) + .writeHex(quote) + .writeUint16((wgaRelayInstruction.length - 2) / 2) + .writeHex(wgaRelayInstruction) + .data(); + const transceiversInstructions = new BinaryWriter() + .writeUint8(1) // instructionsLength + .writeUint8(0) // instruction.index + .writeUint16(transceiversInstruction.length) + .writeUint8Array(transceiversInstruction) + .toHex(); + // Note: these flags are indexed by transceiver index - const totalPrice = await this.quoteDeliveryPrice( - destination.chain, - options - ); + const totalPrice = + executorEstimate + + (await this.manager.quoteDeliveryPrice( + toChainId(destination.chain), + transceiversInstructions + )); + // TODO: make quoteDeliveryPrice work again + // (await this.quoteDeliveryPrice(destination.chain, options)); //TODO check for ERC-2612 (permit) support on token? const tokenContract = EvmPlatform.getTokenImplementation( @@ -492,14 +513,18 @@ export class EvmNtt const receiver = universalAddress(destination); const txReq = await this.manager - .getFunction("transfer(uint256,uint16,bytes32,bytes32,bool,bytes)") + .getFunction( + "transfer(uint256,uint16,bytes32,bytes32,bool,bytes,bytes,bytes)" + ) .populateTransaction( amount, toChainId(destination.chain), receiver, receiver, options.queue, - Ntt.encodeTransceiverInstructions(this.encodeOptions(options)), + quote, + "0x", // TODO: optionally encode gas dropoff relay instruction + transceiversInstructions, { value: totalPrice } ); @@ -615,7 +640,8 @@ export class EvmNtt manager: this.managerAddress, token: await this.manager.token(), transceiver: { - wormhole: (await this.manager.getTransceivers())[0]!, // TODO: make this more generic + // TODO: idk how this should work now + // wormhole: (await this.manager.getTransceivers())[0]!, // TODO: make this more generic }, }; diff --git a/package-lock.json b/package-lock.json index 780760dcb..8093460a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,6 +23,7 @@ "@types/node": "^20.12.2", "@wormhole-foundation/sdk": "^1.0.0", "@wormhole-foundation/wormchain-sdk": "^0.0.1", + "axios": "^1.7.9", "ethers": "^6.5.1", "ts-jest": "^29.1.2", "tsx": "^4.7.2", @@ -5970,9 +5971,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.7", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", - "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", + "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", diff --git a/package.json b/package.json index 2f2293458..d3a88e7c2 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "@types/node": "^20.12.2", "@wormhole-foundation/sdk": "^1.0.0", "@wormhole-foundation/wormchain-sdk": "^0.0.1", + "axios": "^1.7.9", "ethers": "^6.5.1", "ts-jest": "^29.1.2", "tsx": "^4.7.2", diff --git a/sdk/Dockerfile b/sdk/Dockerfile index acfbbd44a..88ab8df85 100644 --- a/sdk/Dockerfile +++ b/sdk/Dockerfile @@ -15,10 +15,11 @@ COPY --from=ntt-evm-contract . evm/out RUN rm -rf evm/out/ts -COPY . ./ +COPY package.json package-lock.json ./ RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \ npm ci +COPY . ./ RUN npm run build RUN npm run generate:test diff --git a/sdk/__tests__/index.test.ts b/sdk/__tests__/index.test.ts index e510b7e18..31a6ff622 100644 --- a/sdk/__tests__/index.test.ts +++ b/sdk/__tests__/index.test.ts @@ -66,17 +66,17 @@ describe("Hub and Spoke Tests", function () { test("Test Solana and Ethereum Hubs", async () => { await Promise.all([ + // testHub( + // "Solana", + // "Ethereum", + // "Bsc", + // makeGetNativeSigner(ETH_PRIVATE_KEY, SOL_PRIVATE_KEY), + // ACCT_MNEMONIC + // ), testHub( - "Solana", "Ethereum", "Bsc", - makeGetNativeSigner(ETH_PRIVATE_KEY, SOL_PRIVATE_KEY), - ACCT_MNEMONIC - ), - testHub( - "Ethereum", - "Bsc", - "Solana", + // "Solana", makeGetNativeSigner(ETH_PRIVATE_KEY_2, SOL_PRIVATE_KEY_2), ACCT_MNEMONIC_2 ), diff --git a/sdk/__tests__/utils.ts b/sdk/__tests__/utils.ts index 990c0675a..2ea85e9d9 100644 --- a/sdk/__tests__/utils.ts +++ b/sdk/__tests__/utils.ts @@ -5,13 +5,16 @@ import { Chain, ChainAddress, ChainContext, + ChainId, NativeSigner, Platform, Signer, + TransactionId, VAA, Wormhole, WormholeMessageId, amount, + chainToChainId, chainToPlatform, encoding, keccak256, @@ -31,7 +34,6 @@ import { IWormholeRelayer__factory } from "../../evm/ts/ethers-ci-contracts/fact import { NttManager__factory } from "../../evm/ts/ethers-ci-contracts/factories/NttManager__factory.js"; import { TransceiverStructs__factory } from "../../evm/ts/ethers-ci-contracts/factories/TransceiverStructs__factory.js"; import { TrimmedAmountLib__factory } from "../../evm/ts/ethers-ci-contracts/factories/TrimmedAmount.sol/TrimmedAmountLib__factory.js"; -import { WormholeTransceiver__factory } from "../../evm/ts/ethers-ci-contracts/factories/WormholeTransceiver__factory.js"; import "../../evm/ts/src/index.js"; import "../../solana/ts/sdk/index.js"; @@ -39,11 +41,24 @@ import { NTT } from "../../solana/ts/lib/index.js"; import { SolanaNtt } from "../../solana/ts/sdk/index.js"; import { Ntt } from "../definitions/src/index.js"; import { submitAccountantVAAs } from "./accountant.js"; +import axios from "axios"; // Note: Currently, in order for this to run, the evm bindings with extra contracts must be build // To do that, at the root, run `npm run generate:test` export const NETWORK: "Devnet" = "Devnet"; +export const ENDPOINTS: { [chainId in ChainId]?: `0x${string}` } = { + 2: "0xF9f67913ba058BD29E699BfcD0eb0ec878555226", + 4: "0x86ee2Ad4bca65764928680812A6C5315eDA9EEfd", +}; +export const EXECUTORS: { [chainId in ChainId]?: `0x${string}` } = { + 2: "0x0a65677098872f870224F6E9533734F4a4B0eBAB", + 4: "0xB67841A38bF16EB9999dC7B6015746506e20F0aA", +}; +export const WHG_ADAPTERS: { [chainId in ChainId]?: `0x${string}` } = { + 2: "0x47A17F7E84Fb16c752352325F854A5358b4461d0", + 4: "0x9683b5Cb8F274510782183CB20E76c3F7C1c884b", +}; type NativeSdkSigner

= P extends "Evm" ? ethers.Wallet @@ -116,24 +131,24 @@ export async function link(chainInfos: Ctx[], accountantPrivateKey: string) { console.log("========================"); // first submit hub init to accountant - const hub = chainInfos[0]!; - const hubChain = hub.context.chain; - - const msgId: WormholeMessageId = { - chain: hubChain, - emitter: Wormhole.chainAddress( - hubChain, - hub.contracts!.transceiver["wormhole"]! - ).address.toUniversalAddress(), - sequence: 0n, - }; + // const hub = chainInfos[0]!; + // const hubChain = hub.context.chain; + + // const msgId: WormholeMessageId = { + // chain: hubChain, + // emitter: Wormhole.chainAddress( + // hubChain, + // hub.contracts!.transceiver["wormhole"]! + // ).address.toUniversalAddress(), + // sequence: 0n, + // }; - const vaa = await wh.getVaa(msgId, "Ntt:TransceiverInfo"); - const vaas: Uint8Array[] = [serialize(vaa!)]; + // const vaa = await wh.getVaa(msgId, "Ntt:TransceiverInfo"); + // const vaas: Uint8Array[] = [serialize(vaa!)]; // [target, peer, vaa] - const registrations: [string, string, VAA<"Ntt:TransceiverRegistration">][] = - []; + // const registrations: [string, string, VAA<"Ntt:TransceiverRegistration">][] = + // []; // register each chain in parallel await Promise.all( @@ -151,63 +166,64 @@ export async function link(chainInfos: Ctx[], accountantPrivateKey: string) { ); for (const peerInfo of toRegister) { - const vaa = await setupPeer(targetInfo, peerInfo); - if (!vaa) throw new Error("No VAA found"); + // const vaa = await setupPeer(targetInfo, peerInfo); + await setupPeer(targetInfo, peerInfo); + // if (!vaa) throw new Error("No VAA found"); // Add to registrations by PEER chain so we can register hub first - registrations.push([ - targetInfo.context.chain, - peerInfo.context.chain, - vaa, - ]); + // registrations.push([ + // targetInfo.context.chain, + // peerInfo.context.chain, + // vaa, + // ]); } })() ) ); // Push Hub to Spoke registrations - const hubToSpokeRegistrations = registrations.filter( - ([_, peer]) => peer === hubChain - ); - for (const [, , vaa] of hubToSpokeRegistrations) { - console.log( - "Pushing hub to spoke registrations: ", - vaa.emitterChain, - vaa.payload.chain, - vaa.payload.transceiver.toString() - ); - vaas.push(serialize(vaa)); - } + // const hubToSpokeRegistrations = registrations.filter( + // ([_, peer]) => peer === hubChain + // ); + // for (const [, , vaa] of hubToSpokeRegistrations) { + // console.log( + // "Pushing hub to spoke registrations: ", + // vaa.emitterChain, + // vaa.payload.chain, + // vaa.payload.transceiver.toString() + // ); + // vaas.push(serialize(vaa)); + // } // Push Spoke to Hub registrations - const spokeToHubRegistrations = registrations.filter( - ([target, _]) => target === hubChain - ); - for (const [, , vaa] of spokeToHubRegistrations) { - console.log( - "Pushing spoke to hub registrations: ", - vaa.emitterChain, - vaa.payload.chain, - vaa.payload.transceiver.toString() - ); - vaas.push(serialize(vaa)); - } + // const spokeToHubRegistrations = registrations.filter( + // ([target, _]) => target === hubChain + // ); + // for (const [, , vaa] of spokeToHubRegistrations) { + // console.log( + // "Pushing spoke to hub registrations: ", + // vaa.emitterChain, + // vaa.payload.chain, + // vaa.payload.transceiver.toString() + // ); + // vaas.push(serialize(vaa)); + // } // Push all other registrations - const spokeToSpokeRegistrations = registrations.filter( - ([target, peer]) => target !== hubChain && peer !== hubChain - ); - for (const [, , vaa] of spokeToSpokeRegistrations) { - console.log( - "Pushing spoke to spoke registrations: ", - vaa.emitterChain, - vaa.payload.chain, - vaa.payload.transceiver.toString() - ); - vaas.push(serialize(vaa)); - } + // const spokeToSpokeRegistrations = registrations.filter( + // ([target, peer]) => target !== hubChain && peer !== hubChain + // ); + // for (const [, , vaa] of spokeToSpokeRegistrations) { + // console.log( + // "Pushing spoke to spoke registrations: ", + // vaa.emitterChain, + // vaa.payload.chain, + // vaa.payload.transceiver.toString() + // ); + // vaas.push(serialize(vaa)); + // } // Submit all registrations at once - await submitAccountantVAAs(vaas, accountantPrivateKey); + // await submitAccountantVAAs(vaas, accountantPrivateKey); } export async function transferWithChecks(sourceCtx: Ctx, destinationCtx: Ctx) { @@ -245,15 +261,69 @@ export async function transferWithChecks(sourceCtx: Ctx, destinationCtx: Ctx) { automatic: useRelayer, gasDropoff: 0n, }); - const txids = await signSendWait(sourceCtx.context, transferTxs, srcSigner); + const txIds = await signSendWait(sourceCtx.context, transferTxs, srcSigner); + console.log(txIds); + + const sourceChainId = chainToChainId(sourceCtx.context.chain); + // TODO: executor sdk should get execution request IDs from a tx + const getExecutionIdsFromTx = async ( + txId: TransactionId + ): Promise => { + const executionIds: string[] = []; + const response = await axios.post(sourceCtx.context.config.rpc, { + jsonrpc: "2.0", + id: 1, + method: "eth_getTransactionReceipt", + params: [txId.txid], + }); + const logs = response?.data?.result?.logs; + if (logs) { + for (let logIndex = 0; logIndex < logs.length; logIndex++) { + const log = logs[logIndex]; + if ( + log && + log.removed === false && + log.address === EXECUTORS[sourceChainId]?.toLowerCase() && + log.topics.length === 2 && + log.topics[0] === + "0xd870d87e4a7c33d0943b0a3d2822b174e239cc55c169af14cc56467a4489e3b5" + ) { + executionIds.push( + `${sourceChainId + .toString(16) + .padStart(4, "0")}${txId.txid.substring(2)}${logIndex + .toString(16) + .padStart(64, "0")}` + ); + } + } + } + return executionIds; + }; + const executionIds = []; + for (const txId of txIds) { + executionIds.push(...(await getExecutionIdsFromTx(txId))); + } - const srcCore = await sourceCtx.context.getWormholeCore(); - const msgId = ( - await srcCore.parseTransaction(txids[txids.length - 1]!.txid) - )[0]!; + for (const executionId of executionIds) { + let isRelayed = false; + while (!isRelayed) { + const ret = await axios.get( + `http://executor:3000/v0/status/${executionId}` + ); + if (ret.data.status === "pending") { + await new Promise((resolve) => setTimeout(resolve, 1000)); + } else if (ret.data.status === "submitted") { + isRelayed = true; + } else { + throw new Error("error executing relay"); + } + } + } - if (!useRelayer) await receive(msgId, destinationCtx); - else await waitForRelay(msgId, destinationCtx); + // apparently we're checking before the transaction has settled + // TODO: wait for the tx to be included in a block + await new Promise((resolve) => setTimeout(resolve, 1000)); const [managerBalanceAfterSend, userBalanceAfterSend] = await getManagerAndUserBalance(sourceCtx); @@ -275,34 +345,6 @@ export async function transferWithChecks(sourceCtx: Ctx, destinationCtx: Ctx) { ); } -async function waitForRelay( - msgId: WormholeMessageId, - dst: Ctx, - retryTime: number = 2000 -) { - const vaa = await wh.getVaa(msgId, "Uint8Array"); - const deliveryHash = keccak256(vaa!.hash); - - const wormholeRelayer = IWormholeRelayer__factory.connect( - dst.context.config.contracts.relayer!, - await dst.context.getRpc() - ); - - let success = false; - while (!success) { - try { - const successBlock = await wormholeRelayer.deliverySuccessBlock( - deliveryHash - ); - if (successBlock > 0) success = true; - console.log("Relayer delivery: ", success); - } catch (e) { - console.error(e); - } - await new Promise((resolve) => setTimeout(resolve, retryTime)); - } -} - // Wrap signSendWait from sdk to provide full error message async function signSendWait( ctx: ChainContext, @@ -401,7 +443,17 @@ async function deployEvm(ctx: Ctx): Promise { console.log("Deploying manager implementation"); const wormholeManager = new NttManager__factory(myObj, wallet); + const endpoint = ENDPOINTS[chainId]; + if (!endpoint) { + throw new Error(`Endpoint is not specified for chain ${chainId}`); + } + const executor = EXECUTORS[chainId]; + if (!executor) { + throw new Error(`Executor is not specified for chain ${chainId}`); + } const managerAddress = await wormholeManager.deploy( + endpoint, + executor, ERC20NTTAddress, // Token address ctx.mode === "locking" ? 0 : 1, // Lock chainId, // chain id @@ -424,46 +476,23 @@ async function deployEvm(ctx: Ctx): Promise { wallet ); - console.log("Deploy transceiver implementation"); - const WormholeTransceiverFactory = new WormholeTransceiver__factory( - myObj, - wallet - ); - const WormholeTransceiverAddress = await WormholeTransceiverFactory.deploy( - // List of useful wormhole contracts - https://github.com/wormhole-foundation/wormhole/blob/00f504ef452ae2d94fa0024c026be2d8cf903ad5/ethereum/ts-scripts/relayer/config/ci/contracts.json - await manager.getAddress(), - ctx.context.config.contracts.coreBridge!, // Core wormhole contract - https://docs.wormhole.com/wormhole/blockchain-environments/evm#local-network-contract -- may need to be changed to support other chains - ctx.context.config.contracts.relayer!, // Relayer contract -- double check these...https://github.com/wormhole-foundation/wormhole/blob/main/sdk/js/src/relayer/__tests__/wormhole_relayer.ts - "0x0000000000000000000000000000000000000000", // TODO - Specialized relayer?????? - 200, // Consistency level - 500000n // Gas limit - ); - await WormholeTransceiverAddress.deploymentTransaction()?.wait(1); - - // Setup with the proxy - console.log("Deploy transceiver proxy"); - const transceiverProxyFactory = new ERC1967Proxy__factory(wallet); - const transceiverProxyDeployment = await transceiverProxyFactory.deploy( - await WormholeTransceiverAddress.getAddress(), - "0x" - ); - await transceiverProxyDeployment.deploymentTransaction()?.wait(1); - - const transceiverProxyAddress = await transceiverProxyDeployment.getAddress(); - const transceiver = WormholeTransceiver__factory.connect( - transceiverProxyAddress, - wallet - ); - // initialize() on both the manager and transceiver console.log("Initialize the manager"); await tryAndWaitThrice(() => manager.initialize()); - console.log("Initialize the transceiver"); - await tryAndWaitThrice(() => transceiver.initialize()); // Setup the initial calls, like transceivers for the manager + const adapter = WHG_ADAPTERS[chainId]; + if (!adapter) { + throw new Error(`Adapter is not specified for chain ${chainId}`); + } console.log("Set transceiver for manager"); - await tryAndWaitThrice(() => manager.setTransceiver(transceiverProxyAddress)); + await tryAndWaitThrice(() => manager.setTransceiver(adapter)); + await tryAndWaitThrice(() => + manager.enableSendTransceiver(chainId === 2 ? 4 : 2, adapter) + ); + await tryAndWaitThrice(() => + manager.enableRecvTransceiver(chainId === 2 ? 4 : 2, adapter) + ); console.log("Set outbound limit for manager"); await tryAndWaitThrice(() => @@ -474,7 +503,7 @@ async function deployEvm(ctx: Ctx): Promise { ...ctx, contracts: { transceiver: { - wormhole: transceiverProxyAddress, + wormhole: adapter, }, manager: await managerProxyAddress.getAddress(), token: ERC20NTTAddress, @@ -598,6 +627,7 @@ async function setupPeer(targetCtx: Ctx, peerCtx: Ctx) { const peerTransceiver = Wormhole.chainAddress(peer.chain, transceiver!); const tokenDecimals = target.config.nativeTokenDecimals; + const gasLimit = BigInt("200000"); const inboundLimit = amount.units(amount.parse("1000", tokenDecimals)); const { signer, address: sender } = targetCtx.signers; @@ -606,43 +636,23 @@ async function setupPeer(targetCtx: Ctx, peerCtx: Ctx) { const setPeerTxs = nttManager.setPeer( peerManager, tokenDecimals, + gasLimit, inboundLimit, sender.address ); await signSendWait(target, setPeerTxs, signer); - const setXcvrPeerTxs = nttManager.setTransceiverPeer( - 0, // 0 = Wormhole - peerTransceiver, - sender.address - ); - const xcvrPeerTxids = await signSendWait(target, setXcvrPeerTxs, signer); - const [whm] = await target.parseTransaction(xcvrPeerTxids[0]!.txid); + // const setXcvrPeerTxs = nttManager.setTransceiverPeer( + // 0, // 0 = Wormhole + // peerTransceiver, + // sender.address + // ); + // const xcvrPeerTxids = await signSendWait(target, setXcvrPeerTxs, signer); + // await signSendWait(target, setXcvrPeerTxs, signer); + // const [whm] = await target.parseTransaction(xcvrPeerTxids[0]!.txid); console.log("Set peers for: ", target.chain, peer.chain); - if ( - chainToPlatform(target.chain) === "Evm" && - chainToPlatform(peer.chain) === "Evm" - ) { - const nativeSigner = (signer as NativeSigner).unwrap(); - const xcvr = WormholeTransceiver__factory.connect( - targetCtx.contracts!.transceiver["wormhole"]!, - nativeSigner.signer - ); - const peerChainId = toChainId(peer.chain); - - console.log("Setting isEvmChain for: ", peer.chain); - await tryAndWaitThrice(() => - xcvr.setIsWormholeEvmChain.send(peerChainId, true) - ); - - console.log("Setting wormhole relaying for: ", peer.chain); - await tryAndWaitThrice(() => - xcvr.setIsWormholeRelayingEnabled.send(peerChainId, true) - ); - } - - return await wh.getVaa(whm!, "Ntt:TransceiverRegistration"); + // return await wh.getVaa(whm!, "Ntt:TransceiverRegistration"); } async function receive(msgId: WormholeMessageId, destination: Ctx) { @@ -731,7 +741,7 @@ async function tryAndWaitThrice( export async function testHub( source: Chain, destinationA: Chain, - destinationB: Chain, + // destinationB: Chain, getNativeSigner: (ctx: Partial) => any, accountantPrivateKey: string ) { @@ -739,35 +749,37 @@ export async function testHub( const hubChain = wh.getChain(source); const spokeChainA = wh.getChain(destinationA); - const spokeChainB = wh.getChain(destinationB); + // const spokeChainB = wh.getChain(destinationB); // Deploy contracts for hub chain console.log("Deploying contracts"); - const [hub, spokeA, spokeB] = await Promise.all([ + const [hub, spokeA] = await Promise.all([ deploy({ context: hubChain, mode: "locking" }, getNativeSigner), deploy({ context: spokeChainA, mode: "burning" }, getNativeSigner), - deploy({ context: spokeChainB, mode: "burning" }, getNativeSigner), + // deploy({ context: spokeChainB, mode: "burning" }, getNativeSigner), ]); console.log("Deployed: ", { [hub.context.chain]: hub.contracts, [spokeA.context.chain]: spokeA.contracts, - [spokeB.context.chain]: spokeB.contracts, + // [spokeB.context.chain]: spokeB.contracts, }); // Link contracts console.log("Linking Peers"); - await link([hub, spokeA, spokeB], accountantPrivateKey); + await link([hub, spokeA], accountantPrivateKey); // Transfer tokens from hub to spoke and check balances console.log("Transfer hub to spoke A"); await transferWithChecks(hub, spokeA); // Transfer between spokes and check balances - console.log("Transfer spoke A to spoke B"); - await transferWithChecks(spokeA, spokeB); + // console.log("Transfer spoke A to spoke B"); + // await transferWithChecks(spokeA, spokeB); // Transfer back to hub and check balances - console.log("Transfer spoke B to hub"); - await transferWithChecks(spokeB, hub); + // console.log("Transfer spoke B to hub"); + // await transferWithChecks(spokeB, hub); + console.log("Transfer spoke A to hub"); + await transferWithChecks(spokeA, hub); } diff --git a/sdk/definitions/src/ntt.ts b/sdk/definitions/src/ntt.ts index 485d1cd24..176990068 100644 --- a/sdk/definitions/src/ntt.ts +++ b/sdk/definitions/src/ntt.ts @@ -91,6 +91,7 @@ export namespace Ntt { export type Peer = { address: ChainAddress; tokenDecimals: number; + gasLimit: bigint; inboundLimit: bigint; }; @@ -182,11 +183,12 @@ export interface Ntt { payer?: AccountAddress ): AsyncGenerator>; - getThreshold(): Promise; + getThreshold(chainId: number): Promise; setPeer( peer: ChainAddress, tokenDecimals: number, + gasLimit: bigint, inboundLimit: bigint, payer?: AccountAddress ): AsyncGenerator>; diff --git a/solana/tests/anchor.test.ts b/solana/tests/anchor.test.ts index 16ba7319b..5b61831fb 100644 --- a/solana/tests/anchor.test.ts +++ b/solana/tests/anchor.test.ts @@ -266,7 +266,7 @@ describe("example-native-token-transfers", () => { await signSendWait(ctx, setXcvrPeerTxs, signer); // Set manager peer - const setPeerTxs = ntt.setPeer(remoteMgr, 18, 1000000n, sender); + const setPeerTxs = ntt.setPeer(remoteMgr, 18, 1n, 1000000n, sender); await signSendWait(ctx, setPeerTxs, signer); } catch (e) { console.error("Failed to setup peer: ", e); diff --git a/solana/ts/sdk/ntt.ts b/solana/ts/sdk/ntt.ts index 3e8982d2b..d70729634 100644 --- a/solana/ts/sdk/ntt.ts +++ b/solana/ts/sdk/ntt.ts @@ -598,6 +598,7 @@ export class SolanaNtt address: toUniversal(chain, new Uint8Array(peer.address)), }, tokenDecimals: peer.tokenDecimals, + gasLimit: BigInt(0), //TODO: read this from peer account inboundLimit: await this.getInboundLimit(chain), }; } @@ -767,6 +768,7 @@ export class SolanaNtt async *setPeer( peer: ChainAddress, tokenDecimals: number, + _gasLimit: bigint, inboundLimit: bigint, payer: AccountAddress ) { diff --git a/tilt/Dockerfile.deploy b/tilt/Dockerfile.deploy new file mode 100644 index 000000000..23cfc7134 --- /dev/null +++ b/tilt/Dockerfile.deploy @@ -0,0 +1,40 @@ +FROM --platform=linux/amd64 ghcr.io/foundry-rs/foundry@sha256:8b843eb65cc7b155303b316f65d27173c862b37719dc095ef3a2ef27ce8d3c00 as builder + +RUN apk add jq bash + +WORKDIR /app/example-messaging-endpoint + +RUN git init && \ + git remote add origin https://github.com/wormholelabs-xyz/example-messaging-endpoint && \ + git fetch --depth 1 origin 0f853ea0335937d611217f5048d677a4f46249fd && \ + git checkout FETCH_HEAD + +WORKDIR /app/example-messaging-endpoint/evm + +RUN forge build + +WORKDIR /app/example-messaging-executor + +RUN git init && \ + git remote add origin https://github.com/wormholelabs-xyz/example-messaging-executor && \ + git fetch --depth 1 origin 57c49d9ad7c410b9a7f938e07a5444f07872159d && \ + git checkout FETCH_HEAD + +WORKDIR /app/example-messaging-executor/evm + +RUN forge build + +WORKDIR /app/example-messaging-adapter-wormhole-guardians + +RUN git init && \ + git remote add origin https://github.com/wormholelabs-xyz/example-messaging-adapter-wormhole-guardians && \ + git fetch --depth 1 origin e2172453c64bc072d0aaa9e9c5d2057d6b56d741 && \ + git checkout FETCH_HEAD + +WORKDIR /app/example-messaging-adapter-wormhole-guardians/evm + +RUN forge build + +WORKDIR /app + +COPY deploy.sh deploy.sh diff --git a/tilt/Dockerfile.executor b/tilt/Dockerfile.executor new file mode 100644 index 000000000..193185dc2 --- /dev/null +++ b/tilt/Dockerfile.executor @@ -0,0 +1,13 @@ +FROM node:20.11.1-alpine@sha256:f4c96a28c0b2d8981664e03f461c2677152cd9a756012ffa8e2c6727427c2bda + +RUN apk add git + +WORKDIR /usr/src/ + +RUN git init && \ + git remote add origin https://github.com/wormholelabs-xyz/example-messaging-executor && \ + git fetch --depth 1 origin 0b0ae41d2e3c8dbda3107074080ad586021f75a9 && \ + git checkout FETCH_HEAD + +RUN --mount=type=cache,uid=1000,gid=1000,target=/home/node/.npm \ + npm ci diff --git a/tilt/deploy.sh b/tilt/deploy.sh new file mode 100644 index 000000000..e92a643a8 --- /dev/null +++ b/tilt/deploy.sh @@ -0,0 +1,29 @@ +#!/bin/sh + +cd /app/example-messaging-endpoint/evm +RPC_URL=http://eth-devnet:8545 bash ./sh/deployEndpoint.sh +OUR_CHAIN_ID=4 EVM_CHAIN_ID=1397 RPC_URL=http://eth-devnet2:8545 bash ./sh/deployEndpoint.sh + +cd /app/example-messaging-executor/evm +RPC_URL=http://eth-devnet:8545 bash ./sh/deployExecutor.sh +OUR_CHAIN_ID=4 EVM_CHAIN_ID=1397 RPC_URL=http://eth-devnet2:8545 bash ./sh/deployExecutor.sh + +cd /app/example-messaging-adapter-wormhole-guardians/evm + +export ENDPOINT=$(jq -r '.returns.deployedAddress.value' <<< cat /app/example-messaging-endpoint/evm/broadcast/DeployEndpoint.s.sol/1337/run-latest.json) +export EXECUTOR=$(jq -r '.returns.deployedAddress.value' <<< cat /app/example-messaging-executor/evm/broadcast/DeployExecutor.s.sol/1337/run-latest.json) +RPC_URL=http://eth-devnet:8545 bash ./sh/deployWormholeGuardiansAdapterWithExecutor.sh + +export ENDPOINT=$(jq -r '.returns.deployedAddress.value' <<< cat /app/example-messaging-endpoint/evm/broadcast/DeployEndpoint.s.sol/1397/run-latest.json) +export EXECUTOR=$(jq -r '.returns.deployedAddress.value' <<< cat /app/example-messaging-executor/evm/broadcast/DeployExecutor.s.sol/1397/run-latest.json) +OUR_CHAIN_ID=4 EVM_CHAIN_ID=1397 RPC_URL=http://eth-devnet2:8545 bash ./sh/deployWormholeGuardiansAdapterWithExecutor.sh + +export WGA_ADDR=$(jq -r '.returns.deployedAddress.value' <<< cat ./broadcast/DeployWormholeGuardiansAdapterWithExecutor.s.sol/1337/run-latest.json) +export PEER_CHAIN_ID=4 +export PEER_ADDR="0x000000000000000000000000$(jq -r '.returns.deployedAddress.value' <<< cat ./broadcast/DeployWormholeGuardiansAdapterWithExecutor.s.sol/1397/run-latest.json | cut -d 'x' -f2)" +RPC_URL=http://eth-devnet:8545 bash ./sh/setPeer.sh + +export WGA_ADDR=$(jq -r '.returns.deployedAddress.value' <<< cat ./broadcast/DeployWormholeGuardiansAdapterWithExecutor.s.sol/1397/run-latest.json) +export PEER_CHAIN_ID=2 +export PEER_ADDR="0x000000000000000000000000$(jq -r '.returns.deployedAddress.value' <<< cat ./broadcast/DeployWormholeGuardiansAdapterWithExecutor.s.sol/1337/run-latest.json | cut -d 'x' -f2)" +RPC_URL=http://eth-devnet2:8545 bash ./sh/setPeer.sh diff --git a/tilt/executor-deploy.yaml b/tilt/executor-deploy.yaml new file mode 100644 index 000000000..61dbb6b2d --- /dev/null +++ b/tilt/executor-deploy.yaml @@ -0,0 +1,24 @@ +kind: Job +apiVersion: batch/v1 +metadata: + name: executor-deploy +spec: + backoffLimit: 0 + template: + spec: + restartPolicy: Never + containers: + - name: deploy + image: executor-deploy + command: + - /bin/sh + - -c + - "bash ./deploy.sh && touch /success" + readinessProbe: + exec: + command: + - test + - -e + - "/success" + initialDelaySeconds: 5 + periodSeconds: 5 diff --git a/tilt/executor.yaml b/tilt/executor.yaml new file mode 100644 index 000000000..77844db40 --- /dev/null +++ b/tilt/executor.yaml @@ -0,0 +1,48 @@ +--- +apiVersion: v1 +kind: Service +metadata: + name: executor + labels: + app: executor +spec: + clusterIP: None + selector: + app: executor + ports: + - port: 3000 + name: express + protocol: TCP +--- +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: executor +spec: + selector: + matchLabels: + app: executor + serviceName: executor + template: + metadata: + labels: + app: executor + spec: + restartPolicy: Always + terminationGracePeriodSeconds: 0 + containers: + - name: executor + image: executor + command: + - /bin/sh + - -c + - "npm start" + readinessProbe: + tcpSocket: + port: 3000 + periodSeconds: 1 + failureThreshold: 300 + ports: + - containerPort: 3000 + name: executor + protocol: TCP From f7c7f72b3505826dce3e340b51a8dee8f0f9b44d Mon Sep 17 00:00:00 2001 From: Bruce Riley Date: Mon, 23 Dec 2024 10:58:53 -0600 Subject: [PATCH 4/4] evm: Fix minor issues from PR#4 --- evm/src/NttManager/ManagerBase.sol | 1 + evm/src/NttManager/NttManager.sol | 2 ++ 2 files changed, 3 insertions(+) diff --git a/evm/src/NttManager/ManagerBase.sol b/evm/src/NttManager/ManagerBase.sol index 3e6228df6..bb936e01a 100644 --- a/evm/src/NttManager/ManagerBase.sol +++ b/evm/src/NttManager/ManagerBase.sol @@ -354,6 +354,7 @@ abstract contract ManagerBase is assert(this.mode() == mode); assert(this.chainId() == chainId); assert(this.endpoint() == endpoint); + assert(this.executor() == executor); } function _checkThresholdInvariants() internal view { diff --git a/evm/src/NttManager/NttManager.sol b/evm/src/NttManager/NttManager.sol index 78f9f3fb8..70b011c35 100644 --- a/evm/src/NttManager/NttManager.sol +++ b/evm/src/NttManager/NttManager.sol @@ -662,6 +662,8 @@ contract NttManager is INttManager, RateLimiter, ManagerBase { uint128 gasLimit = peerData.gasLimit; if (gasLimit == 0) { + // The gas limit can only be zero when a contract has been migrated from an older version, + // where the gasLimit was not defined in the manager peer struct and was not set during the upgrade. revert InvalidGasLimitZero(args.recipientChain); }