Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion evm/echidna/FuzzNttManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import "./helpers/FuzzingHelpers.sol";
import "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import "solidity-bytes-utils/BytesLib.sol";
import "../test/mocks/DummyTransceiver.sol";
import "wormhole-solidity-sdk/Utils.sol";
import "wormhole-sdk/Utils.sol";
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify Utils.sol exists in the SDK submodule
fd -t f "Utils.sol" lib/wormhole-solidity-sdk/src/ 2>/dev/null

# Check what symbols are exported from Utils.sol
if [ -f "lib/wormhole-solidity-sdk/src/Utils.sol" ]; then
  echo "=== Utils.sol contents ==="
  head -50 lib/wormhole-solidity-sdk/src/Utils.sol
fi

Repository: wormhole-foundation/native-token-transfers

Length of output: 68


🏁 Script executed:

#!/bin/bash
# Check lib directory structure
ls -la lib/ 2>/dev/null || echo "lib directory not found or empty"

# Check remappings.txt for relevant entries
echo "=== Checking remappings.txt ==="
cat remappings.txt 2>/dev/null || echo "remappings.txt not found"

# Search for Utils.sol anywhere in the repo
echo "=== Searching for Utils.sol ==="
fd -t f "Utils.sol" . 2>/dev/null | head -20

Repository: wormhole-foundation/native-token-transfers

Length of output: 238


🏁 Script executed:

#!/bin/bash
# Find and show FuzzNttManager.sol
echo "=== FuzzNttManager.sol location and content ==="
find . -name "FuzzNttManager.sol" -type f -exec head -25 {} \;

# Check for remapping configurations in config files
echo -e "\n=== Checking config files for remappings ==="
ls -la | grep -E "foundry|hardhat|truffle|forge"

if [ -f "foundry.toml" ]; then
  echo "=== foundry.toml ==="
  cat foundry.toml
fi

if [ -f "hardhat.config.ts" ] || [ -f "hardhat.config.js" ]; then
  echo "=== hardhat config ==="
  cat hardhat.config.* 2>/dev/null | head -50
fi

Repository: wormhole-foundation/native-token-transfers

Length of output: 1240


🏁 Script executed:

#!/bin/bash
# Check for any config files that might contain remappings
echo "=== All config files in repo ==="
find . -maxdepth 2 -name "*.toml" -o -name "*.json" | grep -E "(forge|foundry|hardhat|package|tsconfig)" | head -20

# Check evm subdirectory specifically
echo -e "\n=== evm directory config files ==="
ls -la evm/ | grep -E "toml|json"

# Check package.json if it exists
if [ -f "package.json" ]; then
  echo -e "\n=== package.json (first 80 lines) ==="
  head -80 package.json
fi

if [ -f "evm/package.json" ]; then
  echo -e "\n=== evm/package.json ==="
  cat evm/package.json
fi

# Check for forge.toml
if [ -f "evm/forge.toml" ]; then
  echo -e "\n=== evm/forge.toml ==="
  cat evm/forge.toml
fi

Repository: wormhole-foundation/native-token-transfers

Length of output: 2426


🏁 Script executed:

#!/bin/bash
echo "=== evm/foundry.toml ==="
cat evm/foundry.toml

Repository: wormhole-foundation/native-token-transfers

Length of output: 1948


🏁 Script executed:

#!/bin/bash
# Check evm package.json
if [ -f "evm/package.json" ]; then
  echo "=== evm/package.json ==="
  cat evm/package.json
else
  echo "evm/package.json not found"
fi

# Check what's in evm/lib
echo -e "\n=== evm/lib directory ==="
ls -la evm/lib/ 2>/dev/null || echo "evm/lib directory not found"

# Search for any references to wormhole-sdk in the codebase
echo -e "\n=== References to wormhole-sdk ==="
grep -r "wormhole-sdk" evm/ --include="*.sol" --include="*.json" --include="*.toml" 2>/dev/null | head -20

Repository: wormhole-foundation/native-token-transfers

Length of output: 2602


🏁 Script executed:

#!/bin/bash
# Check for wormhole-sdk as a symlink or directory
echo "=== Checking for wormhole-sdk ==="
ls -la evm/lib/ | grep wormhole

# Check if wormhole-sdk is referenced elsewhere
echo -e "\n=== Check git status for lib changes ==="
cd evm && git status lib/ 2>/dev/null | head -30

# Check what Utils.sol exports in the actual library
echo -e "\n=== Contents of wormhole-solidity-sdk/Utils.sol ==="
head -30 evm/lib/wormhole-solidity-sdk/Utils.sol 2>/dev/null || echo "File not found"

# Check if the imports are using the correct path in other files
echo -e "\n=== Sample working import from another file ==="
grep -A2 "import.*Utils" evm/src/NttManager/ManagerBase.sol | head -10

Repository: wormhole-foundation/native-token-transfers

Length of output: 500


🏁 Script executed:

#!/bin/bash
# List contents of wormhole-solidity-sdk
echo "=== Contents of evm/lib/wormhole-solidity-sdk/ ==="
find evm/lib/wormhole-solidity-sdk -type f | head -20

# Check if it's actually a symlink
echo -e "\n=== Check symlinks in evm/lib ==="
ls -la evm/lib/

# Look for wormhole-sdk anywhere
echo -e "\n=== Search for wormhole-sdk anywhere ==="
find evm -name "wormhole-sdk" -o -name "wormhole-sdk*" 2>/dev/null

# Check actual location of Utils from test
echo -e "\n=== Utils.sol in test ==="
ls -la evm/test/libraries/ 2>/dev/null | grep Utils

Repository: wormhole-foundation/native-token-transfers

Length of output: 682


🏁 Script executed:

#!/bin/bash
# Verify wormhole-solidity-sdk is truly empty
echo "=== Checking if wormhole-solidity-sdk is a git submodule ==="
cd evm && git config --file .gitmodules --get-regexp path | grep wormhole
cd -

# Check the git history to see what changed
echo -e "\n=== Recent git log for FuzzNttManager.sol ==="
cd evm && git log --oneline -n 10 echidna/FuzzNttManager.sol 2>/dev/null || echo "No git history"
cd -

# Check if there's a remapping or import alias that might help
echo -e "\n=== Check for any remapping attempts in Solidity files ==="
grep -r "remapping\|import.*as" evm/src evm/echidna --include="*.sol" 2>/dev/null | head -5

# Verify: does wormhole-sdk exist as an npm package that would be installed?
echo -e "\n=== Check root package.json for wormhole-sdk ==="
grep -i "wormhole-sdk" package.json 2>/dev/null || echo "Not found in root package.json"

Repository: wormhole-foundation/native-token-transfers

Length of output: 879


🏁 Script executed:

#!/bin/bash
# Check .gitmodules
echo "=== .gitmodules content ==="
cat .gitmodules 2>/dev/null || cat evm/.gitmodules 2>/dev/null || echo "No .gitmodules found"

# Check git submodule status
echo -e "\n=== Git submodule status ==="
git config --file .gitmodules --get-regexp . 2>/dev/null || echo "No submodules configured"

# Look at the actual commit that changed the imports
echo -e "\n=== Show the commit that changed FuzzNttManager.sol ==="
cd evm && git show --stat 3fa3e22 2>/dev/null | head -50
cd -

Repository: wormhole-foundation/native-token-transfers

Length of output: 4278


🏁 Script executed:

#!/bin/bash
# Check the full diff of the commit to understand the remapping changes
echo "=== Full diff of the upgrade commit (first 200 lines) ==="
cd evm && git show 3fa3e22 -- foundry.toml 2>/dev/null | head -100
cd -

# Also check if remappings were added in this commit
echo -e "\n=== Check if remappings.txt was added ==="
cd evm && git show 3fa3e22 -- remappings.txt 2>/dev/null || echo "No remappings.txt in this commit"
cd -

# Check the actual current state - look at full diff for FuzzNttManager.sol
echo -e "\n=== Diff for FuzzNttManager.sol import change ==="
cd evm && git show 3fa3e22 -- echidna/FuzzNttManager.sol 2>/dev/null | grep -A5 -B5 "wormhole"
cd -

Repository: wormhole-foundation/native-token-transfers

Length of output: 5327


🏁 Script executed:

#!/bin/bash
# Verify remappings.txt actually exists in current state
echo "=== Check if evm/remappings.txt exists ==="
ls -la evm/remappings.txt 2>/dev/null || echo "File does not exist in current state"

# Check git status to see if it's tracked
echo -e "\n=== Git status of remappings.txt ==="
cd evm && git ls-files remappings.txt 2>/dev/null && echo "File is tracked" || echo "File not tracked"
cd -

# Try to view the file directly
echo -e "\n=== Content of evm/remappings.txt ==="
cat evm/remappings.txt 2>/dev/null || echo "Cannot read file"

# Check git for current tracked files in evm root
echo -e "\n=== Tracked files in evm root ==="
cd evm && git ls-files | grep "^[^/]*$" | head -20
cd -

Repository: wormhole-foundation/native-token-transfers

Length of output: 558


🏁 Script executed:

#!/bin/bash
# Verify Utils.sol exists at the remapped location
echo "=== Check if Utils.sol exists at remapped location ==="
ls -la evm/lib/wormhole-solidity-sdk/src/Utils.sol 2>/dev/null || echo "File not found"

# List what's in the src directory
echo -e "\n=== Contents of evm/lib/wormhole-solidity-sdk/src/ ==="
ls -la evm/lib/wormhole-solidity-sdk/src/ 2>/dev/null | head -20

# Verify the remapping resolves correctly
echo -e "\n=== Verify remapping resolution ==="
# Check if the path referenced in remappings exists
stat evm/lib/wormhole-solidity-sdk/src/ 2>/dev/null && echo "Remapped directory exists" || echo "Remapped directory not found"

Repository: wormhole-foundation/native-token-transfers

Length of output: 277


🏁 Script executed:

#!/bin/bash
# Check git submodule status
echo "=== Git submodule status ==="
cd evm && git submodule status 2>/dev/null
cd -

# Check if submodule initialization is needed
echo -e "\n=== Submodule update status ==="
cd evm && git config --file .gitmodules --get submodule.lib/wormhole-solidity-sdk.path
cd -

# Try to understand what's in the submodule directory
echo -e "\n=== Detailed check of wormhole-solidity-sdk ==="
find evm/lib/wormhole-solidity-sdk -type f 2>/dev/null | wc -l
echo "Number of files in wormhole-solidity-sdk:"
find evm/lib/wormhole-solidity-sdk -type d 2>/dev/null | wc -l
echo "Number of directories in wormhole-solidity-sdk (empty dirs count as 0 files)"

Repository: wormhole-foundation/native-token-transfers

Length of output: 617


Critical: Submodule not initialized—remappings will fail to resolve.

The import path change to wormhole-sdk/Utils.sol depends on remappings configured in evm/remappings.txt, which maps wormhole-sdk/ to lib/wormhole-solidity-sdk/src/. However, the wormhole-solidity-sdk git submodule is not initialized in the current state (indicated by the - prefix in submodule status), meaning the src/ directory does not exist. This will cause compilation failures when Foundry/Forge attempts to resolve the import.

The remappings configuration itself is correct, but this PR requires either:

  1. Ensuring git submodule update --init --recursive runs before compilation (CI/build scripts, or developer setup instructions)
  2. Or, if submodules are intentionally avoided, updating imports to use the old wormhole-solidity-sdk/ path which is already configured
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@evm/echidna/FuzzNttManager.sol` at line 17, The import in FuzzNttManager.sol
now references "wormhole-sdk/Utils.sol" but the wormhole-solidity-sdk submodule
is not initialized so remapped path (evm/remappings.txt mapping wormhole-sdk/ ->
lib/wormhole-solidity-sdk/src/) will fail; either ensure the submodule is
initialized in CI/setup by running git submodule update --init --recursive
before compilation, or revert the import to the previously working path (e.g.,
the original wormhole-solidity-sdk import) so builds work without submodule
changes; update CI/build scripts or developer setup docs accordingly and adjust
FuzzNttManager.sol import to match whichever approach you choose.


contract FuzzNttManager is FuzzingHelpers {
uint64[] queuedOutboundTransfersArray;
Expand Down
2 changes: 1 addition & 1 deletion evm/foundry.lock
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,6 @@
"rev": "e0115c4d231910df47ce3b60625ce562fe4af985"
},
"lib/wormhole-solidity-sdk": {
"rev": "b9e129e65d34827d92fceeed8c87d3ecdfc801d0"
"rev": "d80e0f6cd16f281f3f9df435e98e823b4212b2ad"
}
}
4 changes: 2 additions & 2 deletions evm/foundry.toml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
[profile.default]
solc_version = "0.8.19"
solc_version = "0.8.24"
optimizer = true
optimizer_runs = 200
via_ir = false
via_ir = true
evm_version = "london"
src = "src"
out = "out"
Expand Down
2 changes: 1 addition & 1 deletion evm/lib/wormhole-solidity-sdk
2 changes: 2 additions & 0 deletions evm/remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
wormhole-solidity-sdk/=lib/wormhole-solidity-sdk/src/
wormhole-sdk/=lib/wormhole-solidity-sdk/src/
7 changes: 2 additions & 5 deletions evm/script/DeployWormholeNtt.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,11 @@ 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 {ICoreBridge} from "wormhole-sdk/interfaces/ICoreBridge.sol";
import {NttManager} from "../src/NttManager/NttManager.sol";
import {NttManagerNoRateLimiting} from "../src/NttManager/NttManagerNoRateLimiting.sol";
import {NttManagerWethUnwrap} from "../src/NttManager/NttManagerWethUnwrap.sol";

interface IWormhole {
function chainId() external view returns (uint16);
}

contract DeployWormholeNtt is Script, DeployWormholeNttBase {
function run() public {
vm.startBroadcast();
Expand All @@ -32,7 +29,7 @@ contract DeployWormholeNtt is Script, DeployWormholeNttBase {
modeUint == 0 ? IManagerBase.Mode.LOCKING : IManagerBase.Mode.BURNING;
string memory managerVariant = vm.envOr("MANAGER_VARIANT", string("standard"));

IWormhole wh = IWormhole(wormhole);
ICoreBridge wh = ICoreBridge(wormhole);

// sanity check decimals
(bool success, bytes memory queriedDecimals) =
Expand Down
4 changes: 2 additions & 2 deletions evm/script/helpers/DeployWormholeNttBase.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
WormholeTransceiver
} from "../../src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol";
import {ERC1967Proxy} from "openzeppelin-contracts/contracts/proxy/ERC1967/ERC1967Proxy.sol";
import {IWormhole} from "wormhole-solidity-sdk/interfaces/IWormhole.sol";
import {ICoreBridge} from "wormhole-sdk/interfaces/ICoreBridge.sol";

contract DeployWormholeNttBase is ParseNttConfig {
/// @notice Parameters for deploying NTT contracts
Expand Down Expand Up @@ -106,7 +106,7 @@ contract DeployWormholeNttBase is ParseNttConfig {
WormholeTransceiver transceiverProxy =
WormholeTransceiver(address(new ERC1967Proxy(address(implementation), "")));

IWormhole wh = IWormhole(params.wormholeCoreBridge);
ICoreBridge wh = ICoreBridge(params.wormholeCoreBridge);
uint256 messageFee = wh.messageFee();
// wh transceiver sends a WH_TRANSCEIVER_INIT_PREFIX message
transceiverProxy.initialize{value: messageFee}();
Expand Down
19 changes: 1 addition & 18 deletions evm/script/helpers/ParseNttConfig.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {stdJson} from "forge-std/StdJson.sol";

import "../../src/interfaces/INttManager.sol";
import "../../src/interfaces/IWormholeTransceiver.sol";
import {toUniversalAddress, fromUniversalAddress} from "wormhole-sdk/Utils.sol";

contract ParseNttConfig is Script {
using stdJson for string;
Expand All @@ -25,24 +26,6 @@ contract ParseNttConfig is Script {

mapping(uint16 => bool) duplicateChainIds;

function toUniversalAddress(
address evmAddr
) internal pure returns (bytes32 converted) {
assembly ("memory-safe") {
converted := and(0xffffffffffffffffffffffffffffffffffffffff, evmAddr)
}
}

function fromUniversalAddress(
bytes32 universalAddr
) internal pure returns (address converted) {
require(bytes12(universalAddr) == 0, "Address overflow");

assembly ("memory-safe") {
converted := universalAddr
}
}

function _parseAndValidateConfigFile(
uint16 wormholeChainId
)
Expand Down
29 changes: 11 additions & 18 deletions evm/src/NttManager/ManagerBase.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.8 <0.9.0;

import "wormhole-solidity-sdk/Utils.sol";
import "wormhole-solidity-sdk/libraries/BytesParsing.sol";
import "wormhole-sdk/Utils.sol";
import "wormhole-sdk/libraries/BytesParsing.sol";

import "../libraries/external/OwnableUpgradeable.sol";
import "../libraries/external/ReentrancyGuardUpgradeable.sol";
Expand Down Expand Up @@ -108,7 +108,7 @@ abstract contract ManagerBase is

TransceiverStructs.TransceiverInstruction[] memory instructions =
TransceiverStructs.parseTransceiverInstructions(
transceiverInstructions, enabledTransceivers.length
transceiverInstructions, _getRegisteredTransceiversStorage().length
);

return _quoteDeliveryPrice(recipientChain, instructions, enabledTransceivers);
Expand Down Expand Up @@ -146,16 +146,20 @@ abstract contract ManagerBase is
bytes32 nttManagerMessageHash =
TransceiverStructs.nttManagerMessageDigest(sourceChainId, payload);

// Cache the transceiver index to avoid redundant storage reads.
uint8 index = _getTransceiverInfosStorage()[msg.sender].index;

// 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
)) {
if (transceiverAttestedToMessage(nttManagerMessageHash, index)) {
revert TransceiverAlreadyAttestedToMessage(nttManagerMessageHash);
}
_setTransceiverAttestedToMessage(nttManagerMessageHash, msg.sender);
_setTransceiverAttestedToMessage(nttManagerMessageHash, index);

// msg.sender is the transceiver address (guaranteed by onlyTransceiver modifier)
emit MessageAttestedTo(nttManagerMessageHash, msg.sender, index);

return nttManagerMessageHash;
}
Expand Down Expand Up @@ -430,17 +434,6 @@ abstract contract ManagerBase is
_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
Expand Down
12 changes: 6 additions & 6 deletions evm/src/NttManager/NttManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ pragma solidity >=0.8.8 <0.9.0;
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";
import "wormhole-solidity-sdk/Utils.sol";
import "wormhole-solidity-sdk/libraries/BytesParsing.sol";
import "wormhole-sdk/Utils.sol";
import "wormhole-sdk/libraries/BytesParsing.sol";

import "../libraries/RateLimiter.sol";

Expand Down Expand Up @@ -41,7 +41,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase {
using TrimmedAmountLib for uint256;
using TrimmedAmountLib for TrimmedAmount;

string public constant NTT_MANAGER_VERSION = "2.0.0";
string public constant NTT_MANAGER_VERSION = "2.1.0";

// =============== Setup =================================================================

Expand Down Expand Up @@ -245,7 +245,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase {
TrimmedAmount nativeTransferAmount =
(nativeTokenTransfer.amount.untrim(toDecimals)).trim(toDecimals, toDecimals);

address transferRecipient = fromWormholeFormat(nativeTokenTransfer.to);
address transferRecipient = fromUniversalAddress(nativeTokenTransfer.to);

bool enqueued = _enqueueOrConsumeInboundRateLimit(
digest, sourceChainId, nativeTransferAmount, transferRecipient
Expand Down Expand Up @@ -551,7 +551,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase {
bytes memory encodedNttManagerPayload = TransceiverStructs.encodeNttManagerMessage(
TransceiverStructs.NttManagerMessage(
bytes32(uint256(seq)),
toWormholeFormat(sender),
toUniversalAddress(sender),
TransceiverStructs.encodeNativeTokenTransfer(ntt)
)
);
Expand Down Expand Up @@ -609,7 +609,7 @@ contract NttManager is INttManager, RateLimiter, ManagerBase {
bytes32 // refundAddress
) internal virtual returns (TransceiverStructs.NativeTokenTransfer memory) {
return TransceiverStructs.NativeTokenTransfer(
amount, toWormholeFormat(token), recipient, recipientChain, ""
amount, toUniversalAddress(token), recipient, recipientChain, ""
);
}

Expand Down
6 changes: 3 additions & 3 deletions evm/src/Transceiver/Transceiver.sol
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.8 <0.9.0;

import "wormhole-solidity-sdk/Utils.sol";
import "wormhole-sdk/Utils.sol";

import "../libraries/TransceiverStructs.sol";
import "../libraries/PausableOwnable.sol";
Expand Down Expand Up @@ -148,9 +148,9 @@ abstract contract Transceiver is
bytes32 recipientNttManagerAddress,
TransceiverStructs.NttManagerMessage memory payload
) internal virtual {
if (recipientNttManagerAddress != toWormholeFormat(nttManager)) {
if (recipientNttManagerAddress != toUniversalAddress(nttManager)) {
revert UnexpectedRecipientNttManagerAddress(
toWormholeFormat(nttManager), recipientNttManagerAddress
toUniversalAddress(nttManager), recipientNttManagerAddress
);
}
INttManager(nttManager).attestationReceived(sourceChainId, sourceNttManagerAddress, payload);
Expand Down
73 changes: 34 additions & 39 deletions evm/src/Transceiver/WormholeTransceiver/WormholeTransceiver.sol
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
// SPDX-License-Identifier: Apache 2
pragma solidity >=0.8.8 <0.9.0;

import "wormhole-solidity-sdk/libraries/BytesParsing.sol";
import "wormhole-solidity-sdk/interfaces/IWormhole.sol";

import "wormhole-sdk/libraries/BytesParsing.sol";
import {ICoreBridge} from "wormhole-sdk/interfaces/ICoreBridge.sol";
import {CoreBridgeLib} from "wormhole-sdk/libraries/CoreBridge.sol";
import {VaaLib} from "wormhole-sdk/libraries/VaaLib.sol";
import "../../libraries/TransceiverHelpers.sol";
import "../../libraries/TransceiverStructs.sol";
import "../../libraries/ConfigMakers.sol";

import "../../interfaces/IWormholeTransceiver.sol";
import "../../interfaces/INttManager.sol";
import "../../interfaces/ICustomConsistencyLevel.sol";

import "./WormholeTransceiverState.sol";

Expand All @@ -29,7 +28,7 @@ import "./WormholeTransceiverState.sol";
contract WormholeTransceiver is IWormholeTransceiver, WormholeTransceiverState {
using BytesParsing for bytes;

string public constant WORMHOLE_TRANSCEIVER_VERSION = "2.0.0";
string public constant WORMHOLE_TRANSCEIVER_VERSION = "2.1.0";

constructor(
address nttManager,
Expand Down Expand Up @@ -57,11 +56,9 @@ contract WormholeTransceiver is IWormholeTransceiver, WormholeTransceiverState {

/// @inheritdoc IWormholeTransceiver
function receiveMessage(
bytes memory encodedMessage
bytes calldata encodedMessage
) external {
uint16 sourceChainId;
bytes memory payload;
(sourceChainId, payload) = _verifyMessage(encodedMessage);
(uint16 sourceChainId, bytes calldata payload) = _verifyMessage(encodedMessage);

// parse the encoded Transceiver payload
TransceiverStructs.TransceiverMessage memory parsedTransceiverMessage;
Expand Down Expand Up @@ -90,8 +87,8 @@ contract WormholeTransceiver is IWormholeTransceiver, WormholeTransceiverState {
}

uint256 offset = 0;
(instruction.shouldSkipRelayerSend, offset) = encoded.asBoolUnchecked(offset);
encoded.checkLength(offset);
(instruction.shouldSkipRelayerSend, offset) = encoded.asBoolMemUnchecked(offset);
BytesParsing.checkLength(encoded.length, offset);
}

/// @inheritdoc IWormholeTransceiver
Expand Down Expand Up @@ -124,7 +121,7 @@ contract WormholeTransceiver is IWormholeTransceiver, WormholeTransceiverState {
bytes memory encodedTransceiverPayload
) = TransceiverStructs.buildAndEncodeTransceiverMessage(
WH_TRANSCEIVER_PAYLOAD_PREFIX,
toWormholeFormat(caller),
toUniversalAddress(caller),
recipientNttManagerAddress,
nttManagerMessage,
new bytes(0)
Expand All @@ -141,38 +138,36 @@ contract WormholeTransceiver is IWormholeTransceiver, WormholeTransceiverState {
}

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);
}
bytes calldata encodedMessage
) internal returns (uint16, bytes calldata) {
checkFork(wormholeTransceiver_evmChainId);

// Verify VAA using client-side verification (gas-optimized vs CoreBridge external call).
// The calldata variant avoids copying the entire VAA into memory.
(
,,
uint16 emitterChainId,
bytes32 emitterAddress,
uint64 sequence,,
bytes calldata payload
) = CoreBridgeLib.decodeAndVerifyVaaCd(address(wormhole), encodedMessage);

// ensure that the message came from a registered peer contract
if (!_verifyBridgeVM(vm)) {
revert InvalidWormholePeer(vm.emitterChainId, vm.emitterAddress);
if (getWormholePeer(emitterChainId) != emitterAddress) {
revert InvalidWormholePeer(emitterChainId, emitterAddress);
}

// save the VAA hash in storage to protect against replay attacks.
if (isVAAConsumed(vm.hash)) {
revert TransferAlreadyCompleted(vm.hash);
// Replay protection using the same double-keccak digest as the old CoreBridge
// parseAndVerifyVM (backward compatible across upgrades). Computed directly from
// calldata using the SDK's optimized keccak utilities (no abi.encodePacked allocation).
bytes32 vaaDigest = VaaLib.calcVaaDoubleHashCd(encodedMessage);
if (isVAAConsumed(vaaDigest)) {
revert TransferAlreadyCompleted(vaaDigest);
}
_setVAAConsumed(vm.hash);

// emit `ReceivedMessage` event
emit ReceivedMessage(vm.hash, vm.emitterChainId, vm.emitterAddress, vm.sequence);
_setVAAConsumed(vaaDigest);

return (vm.emitterChainId, vm.payload);
}
emit ReceivedMessage(vaaDigest, emitterChainId, emitterAddress, sequence);

function _verifyBridgeVM(
IWormhole.VM memory vm
) internal view returns (bool) {
checkFork(wormholeTransceiver_evmChainId);
return getWormholePeer(vm.emitterChainId) == vm.emitterAddress;
return (emitterChainId, payload);
}
}
Loading
Loading