Skip to content
Draft
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 .github/workflows/abi_bindings_checker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
- name: Install solc
run: |
source ./scripts/versions.sh
wget https://github.com/ethereum/solidity/releases/download/v$SOLIDITY_VERSION/solc-static-linux
wget https://github.com/ethereum/solidity/releases/download/v$AVALANCHE_SOLIDITY_VERSION/solc-static-linux
chmod +x solc-static-linux
sudo mv solc-static-linux /usr/local/bin/solc

Expand Down
15 changes: 9 additions & 6 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
[submodule "lib/forge-std"]
[submodule "avalanche/lib/forge-std"]
branch = v1
path = lib/forge-std
path = avalanche/lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/openzeppelin-contracts-upgradeable"]
path = lib/openzeppelin-contracts-upgradeable
[submodule "avalanche/lib/openzeppelin-contracts-upgradeable"]
path = avalanche/lib/openzeppelin-contracts-upgradeable
url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable
[submodule "lib/subnet-evm"]
path = lib/subnet-evm
[submodule "avalanche/lib/subnet-evm"]
path = avalanche/lib/subnet-evm
url = https://github.com/ava-labs/subnet-evm
[submodule "ethereum/lib/forge-std"]
path = ethereum/lib/forge-std
url = https://github.com/foundry-rs/forge-std

Large diffs are not rendered by default.

Large diffs are not rendered by default.

File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ interface ITeleporterMessenger {
*/
function receiveCrossChainMessage(uint32 messageIndex, address relayerRewardAddress) external;

/**
* @notice Receives an inter-chain message, and marks the `relayerRewardAddress` for fee reward for a successful delivery.
*
*/
function receiveInterChainMessage(bytes calldata messagePayload, address relayerRewardAddress) external;

/**
* @notice Retries the execution of a previously delivered message by verifying the payload matches
* the hash of the payload originally delivered, and calling the destination address again.
Expand Down
26 changes: 26 additions & 0 deletions avalanche/contracts/teleporter/IWarpExt.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// (c) 2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// SPDX-License-Identifier: LicenseRef-Ecosystem

pragma solidity ^0.8.25;

import {
WarpMessage,
IWarpMessenger
} from "@avalabs/[email protected]/contracts/interfaces/IWarpMessenger.sol";

/**
* @dev Interface that allows adapting the Warp interface. This is necessary for external interoperability
* since external chains do not receive Warp messages in their access lists.
*/
interface IWarpExt is IWarpMessenger {
/**
* @notice Depending on the chain this contract is deployed on, dispatch logic for
* getting the actual verified Warp message.
* @return message A verified Warp message.
*/
function getVerifiedMessageFromPayload(
bytes calldata payload
) external view returns (WarpMessage memory message);
}
46 changes: 46 additions & 0 deletions avalanche/contracts/teleporter/NativeWarp.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// (c) 2025, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.

// SPDX-License-Identifier: LicenseRef-Ecosystem

pragma solidity 0.8.25;

import {
WarpMessage,
WarpBlockHash,
IWarpMessenger
} from "@avalabs/[email protected]/contracts/interfaces/IWarpMessenger.sol";
import {IWarpExt} from "./IWarpExt.sol";

contract WarpNative is IWarpExt {
/**
* @notice Warp precompile used for sending and receiving Warp messages.
*/
IWarpMessenger public constant WARP_MESSENGER =
IWarpMessenger(0x0200000000000000000000000000000000000005);

function getVerifiedMessageFromPayload(
bytes calldata payload
) external view returns (WarpMessage memory message) {
revert("Todo");

Check notice

Code scanning / Semgrep PRO

Semgrep Finding: solidity.performance.use-custom-error-not-require.use-custom-error-not-require Note

Consider using custom errors as they are more gas efficient while allowing developers to describe the error in detail using NatSpec.
}

function sendWarpMessage(bytes calldata payload) external returns (bytes32 messageID) {
return WARP_MESSENGER.sendWarpMessage(payload);
}

function getVerifiedWarpMessage(uint32 index) external view returns (WarpMessage memory message, bool valid) {
return WARP_MESSENGER.getVerifiedWarpMessage(index);
}

function getVerifiedWarpBlockHash(
uint32 index
) external view returns (WarpBlockHash memory warpBlockHash, bool valid) {
return WARP_MESSENGER.getVerifiedWarpBlockHash(index);
}

function getBlockchainID() external view returns (bytes32 blockchainID) {
return WARP_MESSENGER.getBlockchainID();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import {SafeERC20TransferFrom} from "@utilities/SafeERC20TransferFrom.sol";
import {ITeleporterReceiver} from "./ITeleporterReceiver.sol";
import {ReentrancyGuards} from "@utilities/ReentrancyGuards.sol";
import {IWarpExt} from "./IWarpExt.sol";
import {IWarpMessenger} from "../../../lib/subnet-evm/contracts/contracts/interfaces/IWarpMessenger.sol";

/**
* @dev Implementation of the {ITeleporterMessenger} interface.
Expand All @@ -46,10 +48,9 @@
}

/**
* @notice Warp precompile used for sending and receiving Warp messages.
* @notice The contract for verifying Warp messages
*/
IWarpMessenger public constant WARP_MESSENGER =
IWarpMessenger(0x0200000000000000000000000000000000000005);
IWarpExt public immutable WARP_MESSENGER;

/**
* @notice The blockchain ID of the chain the contract is deployed on.
Expand Down Expand Up @@ -111,6 +112,12 @@
=> mapping(address feeTokenContract => uint256 redeemableRewardAmount)
) internal _relayerRewardAmounts;

// @notice Register the address of the Warp contract to be used by
// this teleporter.
constructor (address warpContract) {
WARP_MESSENGER = IWarpExt(warpContract);
}

Check notice

Code scanning / Semgrep PRO

Semgrep Finding: solidity.performance.non-payable-constructor.non-payable-constructor Note

Consider making costructor payable to save gas.

/**
* @dev See {ITeleporterMessenger-sendCrossChainMessage}
*
Expand Down Expand Up @@ -230,15 +237,12 @@

/**
* @dev Emits a {ReceiveCrossChainMessage} event.
* Receives a Warp message via storage slots and processes it.
* Re-entrancy is explicitly disallowed between receiving functions. One message is not able to receive another message.
* Requirements:
*
* - `relayerRewardAddress` must not be the zero address.
* - `messageIndex` must specify a valid warp message in the transaction's storage slots.
* - Valid warp message provided in storage slots, and sender address matches the address of this contract.
* - Teleporter message `destinationBlockchainID` must match the `blockchainID` of this contract.
* - Teleporter message was not previously received.
* - Transaction was sent by an allowed relayer for corresponding teleporter message.
* - Valid warp message provided in storage slots
* - sender address matches the address of this contract
*
* @inheritdoc ITeleporterMessenger
*/
Expand All @@ -251,15 +255,51 @@
(WarpMessage memory warpMessage, bool success) =
WARP_MESSENGER.getVerifiedWarpMessage(messageIndex);
require(success, "TeleporterMessenger: invalid warp message");

// Only allow for messages to be received from the same address as this teleporter contract.
// The contract should be deployed using the universal deployer pattern, such that it knows messages
// received from the same address on other chains were constructed using the same bytecode of this contract.
// This allows for trusting the message format and uniqueness as specified by sendCrossChainMessage.
// TODO: This check may not be sufficient if this contract is receiving message from non-avalanche L1s
require(
warpMessage.originSenderAddress == address(this),
"TeleporterMessenger: invalid origin sender address"
);
_processWarpMessage(warpMessage, relayerRewardAddress);
}

/**
* @dev Emits a {ReceiveCrossChainMessage} event.
* Receives a Warp message as a byte payload and processes it.
* Re-entrancy is explicitly disallowed between receiving functions. One message is not able to receive another message.
* Requirements:
*
* - Valid warp message is decoded from the payload
*
* @inheritdoc ITeleporterMessenger
*/
function receiveInterChainMessage(
bytes calldata messagePayload,
address relayerRewardAddress
) external receiverNonReentrant {
(WarpMessage memory warpMessage, bool success) =
WARP_MESSENGER.getVerifiedMessageFromPayload(messagePayload);
require(success, "TeleporterMessenger: invalid warp message");

Check notice

Code scanning / Semgrep PRO

Semgrep Finding: solidity.performance.use-custom-error-not-require.use-custom-error-not-require Note

Consider using custom errors as they are more gas efficient while allowing developers to describe the error in detail using NatSpec.

Check notice

Code scanning / Semgrep PRO

Semgrep Finding: solidity.performance.use-short-revert-string.use-short-revert-string Note

Shortening revert strings to fit in 32 bytes will decrease gas costs for deployment and gas costs when the revert condition has been met.
_processWarpMessage(warpMessage, relayerRewardAddress);
}

/**
* @dev Emits a {ReceiveCrossChainMessage} event.
*
* - `relayerRewardAddress` must not be the zero address.
* - Teleporter message `destinationBlockchainID` must match the `blockchainID` of this contract.
* - Teleporter message was not previously received.
* - Transaction was sent by an allowed relayer for corresponding teleporter message.
*
*/
function _processWarpMessage(
WarpMessage memory warpMessage,
address relayerRewardAddress
) internal {

// Parse the payload of the message.
TeleporterMessage memory teleporterMessage =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ abstract contract BaseTeleporterRegistryAppTest is TeleporterRegistryTest {
app.receiveTeleporterMessage(DEFAULT_SOURCE_BLOCKCHAIN_ID, DEFAULT_ORIGIN_ADDRESS, "");

// Now add new protocol version to registry and update the app's min version
address newTeleporterAddress = address(new TeleporterMessenger());
address newTeleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));
_addProtocolVersion(teleporterRegistry, newTeleporterAddress);

_updateMinTeleporterVersionSuccess(app, teleporterRegistry.latestVersion());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ abstract contract GetTeleporterMessengerTest is BaseTeleporterRegistryAppTest {

// Add a new version of Teleporter, and make sure we can get
// the new Teleporter successfully.
address newTeleporterAddress = address(new TeleporterMessenger());
address newTeleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));
_addProtocolVersion(teleporterRegistry, newTeleporterAddress);
ITeleporterMessenger messenger = app.getTeleporterMessenger();
assertEq(address(messenger), newTeleporterAddress);
Expand All @@ -46,7 +46,7 @@ abstract contract GetTeleporterMessengerTest is BaseTeleporterRegistryAppTest {
}

function testPauseNonLatestTeleporter() public {
address newTeleporterAddress = address(new TeleporterMessenger());
address newTeleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));
_addProtocolVersion(teleporterRegistry, newTeleporterAddress);

// Pause a non-latest version of Teleporter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ abstract contract NonReentrantTest is BaseTeleporterRegistryAppTest {
}

function testNonReentrantDifferentTeleporter() public {
TeleporterMessenger teleporterV2 = new TeleporterMessenger();
TeleporterMessenger teleporterV2 = new TeleporterMessenger(0x0200000000000000000000000000000000000005);
teleporterV2.initializeBlockchainID();
_addProtocolVersion(teleporterRegistry, address(teleporterV2));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ abstract contract PauseTeleporterAddressTest is BaseTeleporterRegistryAppTest {
// Check that a Teleporter address can be paused before it is registered with the registry

// Create a new Teleporter address that is not registered with the registry
address newTeleporterAddress = address(new TeleporterMessenger());
address newTeleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));

// Pause the new Teleporter address before it is registered
_pauseTeleporterAddressSuccess(app, newTeleporterAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ contract TeleporterRegistryTest is Test {
teleporterRegistry = new TeleporterRegistry(new ProtocolRegistryEntry[](0));
assertEq(0, teleporterRegistry.latestVersion());

teleporterAddress = address(new TeleporterMessenger());
teleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));
}

function testAddProtocolVersionBasic() public {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ abstract contract UnpauseTeleporterAddressTest is BaseTeleporterRegistryAppTest
app.receiveTeleporterMessage(DEFAULT_SOURCE_BLOCKCHAIN_ID, DEFAULT_ORIGIN_ADDRESS, "");

// Add a new Teleporter address to the registry and update minimum version
address newTeleporterAddress = address(new TeleporterMessenger());

address newTeleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));
_addProtocolVersion(teleporterRegistry, newTeleporterAddress);
_updateMinTeleporterVersionSuccess(app, teleporterRegistry.latestVersion());

Expand Down Expand Up @@ -77,7 +78,7 @@ abstract contract UnpauseTeleporterAddressTest is BaseTeleporterRegistryAppTest
// Check that a Teleporter address can be paused before it is registered with the registry

// Create a new Teleporter address that is not registered with the registry
address newTeleporterAddress = address(new TeleporterMessenger());
address newTeleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));

// Pause the new Teleporter address before it is registered
_pauseTeleporterAddressSuccess(app, newTeleporterAddress);
Expand All @@ -97,7 +98,7 @@ abstract contract UnpauseTeleporterAddressTest is BaseTeleporterRegistryAppTest
// Check that a Teleporter address can be paused before it is registered with the registry

// Create a new Teleporter address that is not registered with the registry
address newTeleporterAddress = address(new TeleporterMessenger());
address newTeleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));

// Pause the new Teleporter address before it is registered
_pauseTeleporterAddressSuccess(app, newTeleporterAddress);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ abstract contract UpdateMinTeleporterVersionTest is BaseTeleporterRegistryAppTes
app.receiveTeleporterMessage(DEFAULT_SOURCE_BLOCKCHAIN_ID, DEFAULT_ORIGIN_ADDRESS, "");

// Now add new protocol version to registry and update the app's min version
address newTeleporterAddress = address(new TeleporterMessenger());
address newTeleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));
_addProtocolVersion(teleporterRegistry, newTeleporterAddress);

_updateMinTeleporterVersionSuccess(app, teleporterRegistry.latestVersion());
Expand All @@ -38,7 +38,7 @@ abstract contract UpdateMinTeleporterVersionTest is BaseTeleporterRegistryAppTes
// that is not registered in the registry.

// First add to the registry by skipping a version
address newTeleporterAddress = address(new TeleporterMessenger());
address newTeleporterAddress = address(new TeleporterMessenger(0x0200000000000000000000000000000000000005));
uint256 latestVersion = teleporterRegistry.latestVersion();
uint32 messageIndex = 0;
WarpMessage memory warpMessage = _createWarpOffChainMessage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,14 @@
pragma solidity 0.8.25;

import {TeleporterMessenger} from "../TeleporterMessenger.sol";
import {IWarpMessenger} from "@avalabs/[email protected]/contracts/interfaces/IWarpMessenger.sol";
import {Test} from "@forge-std/Test.sol";

contract CheckIsAllowedRelayerTest is TeleporterMessenger, Test {
contract CheckIsAllowedRelayerTest is
TeleporterMessenger(0x0200000000000000000000000000000000000005),
Test
{

function testIsSpecifiedAllowedRelayer() public pure {
address relayerAddress = 0x6288dAdf62B57dd9A4ddcd02F88A98d0eb6c2598;
address[] memory allowedRelayers = new address[](3);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ contract InitializeBlockchainIDTest is Test {
abi.encode(DEFAULT_DESTINATION_BLOCKCHAIN_ID)
);

teleporterMessenger = new TeleporterMessenger();
teleporterMessenger = new TeleporterMessenger(0x0200000000000000000000000000000000000005);
}

function testUninitialized() public {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ contract TeleporterMessengerTest is Test {
abi.encode(DEFAULT_DESTINATION_BLOCKCHAIN_ID)
);

teleporterMessenger = new TeleporterMessenger();
teleporterMessenger = new TeleporterMessenger(0x0200000000000000000000000000000000000005);

// Blockchain ID should be 0 before it is initialized.
assertEq(teleporterMessenger.blockchainID(), bytes32(0));
Expand Down
File renamed without changes.
File renamed without changes.
Loading