Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native Token Bridges uses Teleporter Registry #197

Merged
merged 16 commits into from
Jan 2, 2024
Merged

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
TeleporterMessageInput,
TeleporterFeeInfo
} from "@teleporter/ITeleporterMessenger.sol";
import {ITeleporterReceiver} from "@teleporter/ITeleporterReceiver.sol";
import {TeleporterOwnerUpgradeable} from "@teleporter/upgrades/TeleporterOwnerUpgradeable.sol";
import {SafeERC20TransferFrom} from "@teleporter/SafeERC20TransferFrom.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
Expand All @@ -25,7 +25,7 @@ import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol
*/

contract ERC20TokenSource is
ITeleporterReceiver,
TeleporterOwnerUpgradeable,
IERC20TokenSource,
ITokenSource,
ReentrancyGuard
Expand All @@ -42,21 +42,12 @@ contract ERC20TokenSource is
address public immutable nativeTokenDestinationAddress;
address public immutable erc20ContractAddress;

// Used for sending and receiving Teleporter messages.
ITeleporterMessenger public immutable teleporterMessenger;

constructor(
address teleporterMessengerAddress,
address teleporterRegistryAddress,
bytes32 destinationBlockchainID_,
address nativeTokenDestinationAddress_,
address erc20ContractAddress_
) {
require(
teleporterMessengerAddress != address(0),
"ERC20TokenSource: zero TeleporterMessenger address"
);
teleporterMessenger = ITeleporterMessenger(teleporterMessengerAddress);

) TeleporterOwnerUpgradeable(teleporterRegistryAddress) {
require(
destinationBlockchainID_ != bytes32(0),
"ERC20TokenSource: zero destination blockchain ID"
Expand All @@ -80,48 +71,6 @@ contract ERC20TokenSource is
erc20ContractAddress = erc20ContractAddress_;
}

/**
* @dev See {ITeleporterReceiver-receiveTeleporterMessage}.
*
* Receives a Teleporter message and routes to the appropriate internal function call.
*/
function receiveTeleporterMessage(
bytes32 senderBlockchainID,
address senderAddress,
bytes calldata message
) external nonReentrant {
// Only allow the Teleporter messenger to deliver messages.
require(
msg.sender == address(teleporterMessenger),
"ERC20TokenSource: unauthorized TeleporterMessenger contract"
);

// Only allow messages from the destination chain.
require(
senderBlockchainID == destinationBlockchainID,
"ERC20TokenSource: invalid destination chain"
);

// Only allow the partner contract to send messages.
require(
senderAddress == nativeTokenDestinationAddress, "ERC20TokenSource: unauthorized sender"
);

// Decode the payload to recover the action and corresponding function parameters
(SourceAction action, bytes memory actionData) = abi.decode(message, (SourceAction, bytes));

// Route to the appropriate function.
if (action == SourceAction.Unlock) {
(address recipient, uint256 amount) = abi.decode(actionData, (address, uint256));
_unlockTokens(recipient, amount);
} else if (action == SourceAction.Burn) {
uint256 newBurnTotal = abi.decode(actionData, (uint256));
_handleBurnTokens(newBurnTotal);
} else {
revert("ERC20TokenSource: invalid action");
}
}

/**
* @dev See {IERC20TokenSource-transferToDestination}.
*/
Expand All @@ -131,6 +80,8 @@ contract ERC20TokenSource is
uint256 feeAmount,
address[] calldata allowedRelayerAddresses
) external nonReentrant {
ITeleporterMessenger teleporterMessenger = _getTeleporterMessenger();

// The recipient cannot be the zero address.
require(recipient != address(0), "ERC20TokenSource: zero recipient address");

Expand Down Expand Up @@ -171,6 +122,42 @@ contract ERC20TokenSource is
});
}

/**
* @dev See {TeleporterUpgradeable-receiveTeleporterMessage}.
*
* Receives a Teleporter message and routes to the appropriate internal function call.
*/
function _receiveTeleporterMessage(
bytes32 senderBlockchainID,
address senderAddress,
bytes memory message
) internal override {
// Only allow messages from the destination chain.
require(
senderBlockchainID == destinationBlockchainID,
"ERC20TokenSource: invalid destination chain"
);

// Only allow the partner contract to send messages.
require(
senderAddress == nativeTokenDestinationAddress, "ERC20TokenSource: unauthorized sender"
);

// Decode the payload to recover the action and corresponding function parameters
(SourceAction action, bytes memory actionData) = abi.decode(message, (SourceAction, bytes));

// Route to the appropriate function.
if (action == SourceAction.Unlock) {
(address recipient, uint256 amount) = abi.decode(actionData, (address, uint256));
_unlockTokens(recipient, amount);
} else if (action == SourceAction.Burn) {
uint256 newBurnTotal = abi.decode(actionData, (uint256));
_handleBurnTokens(newBurnTotal);
} else {
revert("ERC20TokenSource: invalid action");
}
}

/**
* @dev Unlocks tokens to recipient.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import {
TeleporterFeeInfo,
TeleporterMessageInput
} from "@teleporter/ITeleporterMessenger.sol";
import {ITeleporterReceiver} from "@teleporter/ITeleporterReceiver.sol";
import {TeleporterOwnerUpgradeable} from "@teleporter/upgrades/TeleporterOwnerUpgradeable.sol";
import {SafeERC20TransferFrom} from "@teleporter/SafeERC20TransferFrom.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
Expand All @@ -29,7 +29,11 @@ import {IAllowList} from "@subnet-evm-contracts/interfaces/IAllowList.sol";
* DO NOT USE THIS CODE IN PRODUCTION.
*/

contract NativeTokenDestination is ITeleporterReceiver, INativeTokenDestination, ReentrancyGuard {
contract NativeTokenDestination is
TeleporterOwnerUpgradeable,
INativeTokenDestination,
ReentrancyGuard
{
// The address where the burned transaction fees are credited.
// Defined as BLACKHOLE_ADDRESS at
// https://github.com/ava-labs/subnet-evm/blob/e23ab058d039ff9c8469c89b139d21d52c4bd283/constants/constants.go
Expand All @@ -53,21 +57,12 @@ contract NativeTokenDestination is ITeleporterReceiver, INativeTokenDestination,
uint256 public currentReserveImbalance;
uint256 public totalMinted;

// Used for sending and receiving Teleporter messages.
ITeleporterMessenger public immutable teleporterMessenger;

constructor(
address teleporterMessengerAddress,
address teleporterRegistryAddress,
bytes32 sourceBlockchainID_,
address nativeTokenSourceAddress_,
uint256 initialReserveImbalance_
) {
require(
teleporterMessengerAddress != address(0),
"NativeTokenDestination: zero TeleporterMessenger address"
);
teleporterMessenger = ITeleporterMessenger(teleporterMessengerAddress);

) TeleporterOwnerUpgradeable(teleporterRegistryAddress) {
require(
sourceBlockchainID_ != bytes32(0), "NativeTokenDestination: zero source blockchain ID"
);
Expand All @@ -92,58 +87,6 @@ contract NativeTokenDestination is ITeleporterReceiver, INativeTokenDestination,
currentReserveImbalance = initialReserveImbalance_;
}

/**
* @dev See {ITeleporterReceiver-receiveTeleporterMessage}.
*
* Receives a Teleporter message.
*/
function receiveTeleporterMessage(
bytes32 senderBlockchainID,
address senderAddress,
bytes calldata message
) external nonReentrant {
// Only allow the Teleporter messenger to deliver messages.
require(
msg.sender == address(teleporterMessenger),
"NativeTokenDestination: unauthorized TeleporterMessenger contract"
);

// Only allow messages from the source chain.
require(
senderBlockchainID == sourceBlockchainID, "NativeTokenDestination: invalid source chain"
);

// Only allow the partner contract to send messages.
require(
senderAddress == nativeTokenSourceAddress, "NativeTokenDestination: unauthorized sender"
);

(address recipient, uint256 amount) = abi.decode(message, (address, uint256));
require(recipient != address(0), "NativeTokenDestination: zero recipient address");
require(amount != 0, "NativeTokenDestination: zero transfer value");

// If the contract has not yet been collateralized, we will deduct as many tokens
// as needed from the transfer as needed. If there are any excess tokens, they will
// be minted and sent to the recipient.
uint256 adjustedAmount = amount;
if (currentReserveImbalance > 0) {
if (amount > currentReserveImbalance) {
emit CollateralAdded({amount: currentReserveImbalance, remaining: 0});
adjustedAmount = amount - currentReserveImbalance;
currentReserveImbalance = 0;
} else {
currentReserveImbalance -= amount;
emit CollateralAdded({amount: amount, remaining: currentReserveImbalance});
return;
}
}

totalMinted += adjustedAmount;
emit NativeTokensMinted(recipient, adjustedAmount);
// Calls NativeMinter precompile through INativeMinter interface.
_nativeMinter.mintNativeCoin(recipient, adjustedAmount);
}

/**
* @dev See {INativeTokenDestination-transferToSource}.
*/
Expand All @@ -152,6 +95,7 @@ contract NativeTokenDestination is ITeleporterReceiver, INativeTokenDestination,
TeleporterFeeInfo calldata feeInfo,
address[] calldata allowedRelayerAddresses
) external payable nonReentrant {
ITeleporterMessenger teleporterMessenger = teleporterRegistry.getLatestTeleporter();
// The recipient cannot be the zero address.
require(recipient != address(0), "NativeTokenDestination: zero recipient address");

Expand Down Expand Up @@ -201,6 +145,8 @@ contract NativeTokenDestination is ITeleporterReceiver, INativeTokenDestination,
TeleporterFeeInfo calldata feeInfo,
address[] calldata allowedRelayerAddresses
) external {
ITeleporterMessenger teleporterMessenger = _getTeleporterMessenger();

uint256 totalBurnedTxFees = address(BURNED_TX_FEES_ADDRESS).balance;
uint256 messageID = teleporterMessenger.sendCrossChainMessage(
TeleporterMessageInput({
Expand Down Expand Up @@ -236,4 +182,50 @@ contract NativeTokenDestination is ITeleporterReceiver, INativeTokenDestination,

return created - burned;
}

/**
* @dev See {TeleporterUpgradeable-receiveTeleporterMessage}.
*
* Receives a Teleporter message.
*/
function _receiveTeleporterMessage(
bytes32 senderBlockchainID,
address senderAddress,
bytes memory message
) internal override {
// Only allow messages from the source chain.
require(
senderBlockchainID == sourceBlockchainID, "NativeTokenDestination: invalid source chain"
);

// Only allow the partner contract to send messages.
require(
senderAddress == nativeTokenSourceAddress, "NativeTokenDestination: unauthorized sender"
);

(address recipient, uint256 amount) = abi.decode(message, (address, uint256));
require(recipient != address(0), "NativeTokenDestination: zero recipient address");
require(amount != 0, "NativeTokenDestination: zero transfer value");

// If the contract has not yet been collateralized, we will deduct as many tokens
// as needed from the transfer as needed. If there are any excess tokens, they will
// be minted and sent to the recipient.
uint256 adjustedAmount = amount;
if (currentReserveImbalance > 0) {
if (amount > currentReserveImbalance) {
emit CollateralAdded({amount: currentReserveImbalance, remaining: 0});
adjustedAmount = amount - currentReserveImbalance;
currentReserveImbalance = 0;
} else {
currentReserveImbalance -= amount;
emit CollateralAdded({amount: amount, remaining: currentReserveImbalance});
return;
}
}

totalMinted += adjustedAmount;
emit NativeTokensMinted(recipient, adjustedAmount);
// Calls NativeMinter precompile through INativeMinter interface.
_nativeMinter.mintNativeCoin(recipient, adjustedAmount);
}
}
Loading