Source Code
Overview
AVAX Balance
Token Holdings
More Info
ContractCreator
Multichain Info
N/A
Latest 25 from a total of 25 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
Amount
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Send | 51530089 | 4 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51486484 | 5 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51484931 | 5 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51484748 | 5 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51481574 | 5 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51465024 | 6 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51464973 | 6 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51464949 | 6 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51449497 | 6 days ago | IN | 0 AVAX | 0.00015264 | ||||
| Send | 51449069 | 6 days ago | IN | 0 AVAX | 0.0001272 | ||||
| Send | 51448954 | 6 days ago | IN | 0 AVAX | 0.00012748 | ||||
| Send | 51417051 | 7 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51411740 | 8 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51302088 | 10 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51301688 | 10 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51268022 | 11 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51240225 | 12 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51226687 | 12 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51221093 | 12 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51220571 | 12 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51178421 | 13 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51177855 | 13 days ago | IN | 0 AVAX | 0 | ||||
| Send | 51119529 | 15 days ago | IN | 0 AVAX | 0 | ||||
| Send | 50980999 | 17 days ago | IN | 0 AVAX | 0 | ||||
| Send | 50786231 | 21 days ago | IN | 0 AVAX | 0 |
Latest 25 internal transactions (View All)
| Parent Transaction Hash | Block | From | To | Amount | ||
|---|---|---|---|---|---|---|
| 51667148 | 20 hrs ago | 0 AVAX | ||||
| 51667148 | 20 hrs ago | 0 AVAX | ||||
| 51667148 | 20 hrs ago | 0 AVAX | ||||
| 51667148 | 20 hrs ago | 0 AVAX | ||||
| 51667148 | 20 hrs ago | 0 AVAX | ||||
| 51667148 | 20 hrs ago | 0 AVAX | ||||
| 51667133 | 20 hrs ago | 0 AVAX | ||||
| 51667133 | 20 hrs ago | 0 AVAX | ||||
| 51667133 | 20 hrs ago | 0 AVAX | ||||
| 51667133 | 20 hrs ago | 0 AVAX | ||||
| 51667133 | 20 hrs ago | 0 AVAX | ||||
| 51667133 | 20 hrs ago | 0 AVAX | ||||
| 51666827 | 20 hrs ago | 0 AVAX | ||||
| 51666827 | 20 hrs ago | 0 AVAX | ||||
| 51666827 | 20 hrs ago | 0 AVAX | ||||
| 51666827 | 20 hrs ago | 0 AVAX | ||||
| 51666827 | 20 hrs ago | 0 AVAX | ||||
| 51666827 | 20 hrs ago | 0 AVAX | ||||
| 51666811 | 20 hrs ago | 0 AVAX | ||||
| 51666811 | 20 hrs ago | 0 AVAX | ||||
| 51666811 | 20 hrs ago | 0 AVAX | ||||
| 51666811 | 20 hrs ago | 0 AVAX | ||||
| 51666811 | 20 hrs ago | 0 AVAX | ||||
| 51666811 | 20 hrs ago | 0 AVAX | ||||
| 51658745 | 27 hrs ago | 0 AVAX |
Loading...
Loading
Contract Name:
ERC20TokenHome
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {TokenHome} from "./TokenHome.sol";
import {IERC20TokenHome} from "./interfaces/IERC20TokenHome.sol";
import {IERC20SendAndCallReceiver} from "../interfaces/IERC20SendAndCallReceiver.sol";
import {
SendTokensInput,
SendAndCallInput,
SingleHopCallMessage
} from "../interfaces/ITokenTransferrer.sol";
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";
import {SafeERC20TransferFrom} from "../utils/SafeERC20TransferFrom.sol";
import {CallUtils} from "../utils/CallUtils.sol";
/**
* @title ERC20TokenHome
* @notice An {IERC20TokenHome} implementation that locks a specified ERC20 token to be sent to
* TokenRemote instances on other chains.
* @custom:security-contact https://github.com/ava-labs/avalanche-interchain-token-transfer/blob/main/SECURITY.md
*/
contract ERC20TokenHome is IERC20TokenHome, TokenHome {
using SafeERC20 for IERC20;
/// @notice The ERC20 token this home contract transfers to TokenRemote instances.
IERC20 public immutable token;
/**
* @notice Initializes the token TokenHome instance to send ERC20 tokens to TokenRemote instances on other chains.
* @param teleporterRegistryAddress The current blockchain ID's Teleporter registry
* address. See here for details: https://github.com/ava-labs/teleporter/tree/main/contracts/src/Teleporter/upgrades
* @param teleporterManager Address that manages this contract's integration with the
* Teleporter registry and Teleporter versions.
* @param tokenAddress_ The ERC20 token contract address to be transferred by the home.
* @param tokenDecimals_ The number of decimals for the ERC20 token
*/
constructor(
address teleporterRegistryAddress,
address teleporterManager,
address tokenAddress_,
uint8 tokenDecimals_
) TokenHome(teleporterRegistryAddress, teleporterManager, tokenAddress_, tokenDecimals_) {
token = IERC20(tokenAddress);
}
/**
* @dev See {IERC20TokenTransferrer-send}
*/
function send(SendTokensInput calldata input, uint256 amount) external {
_send(input, amount);
}
/**
* @dev See {IERC20TokenTransferrer-sendAndCall}
*/
function sendAndCall(SendAndCallInput calldata input, uint256 amount) external {
_sendAndCall({
sourceBlockchainID: blockchainID,
originTokenTransferrerAddress: address(this),
originSenderAddress: _msgSender(),
input: input,
amount: amount
});
}
/**
* @dev See {IERC20TokenHome-addCollateral}
*/
function addCollateral(
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
uint256 amount
) external {
_addCollateral(remoteBlockchainID, remoteTokenTransferrerAddress, amount);
}
/**
* @dev See {TokenHome-_deposit}
*/
function _deposit(uint256 amount) internal virtual override returns (uint256) {
return SafeERC20TransferFrom.safeTransferFrom(token, _msgSender(), amount);
}
/**
* @dev See {TokenHome-_withdraw}
*/
function _withdraw(address recipient, uint256 amount) internal virtual override {
emit TokensWithdrawn(recipient, amount);
token.safeTransfer(recipient, amount);
}
/**
* @dev See {TokenHome-_handleSendAndCall}
*
* Approves the recipient contract to spend the amount of tokens from this contract,
* and calls {IERC20SendAndCallReceiver-receiveTokens} on the recipient contract.
* If the call fails or doesn't spend all of the tokens, the remaining amount is
* sent to the fallback recipient.
*/
function _handleSendAndCall(
SingleHopCallMessage memory message,
uint256 amount
) internal virtual override {
// Approve the recipient contract to spend the amount from the collateral.
SafeERC20.safeIncreaseAllowance(token, message.recipientContract, amount);
// Encode the call to {IERC20SendAndCallReceiver-receiveTokens}
bytes memory payload = abi.encodeCall(
IERC20SendAndCallReceiver.receiveTokens,
(
message.sourceBlockchainID,
message.originTokenTransferrerAddress,
message.originSenderAddress,
address(token),
amount,
message.recipientPayload
)
);
// Call the recipient contract with the given payload and gas amount.
bool success = CallUtils._callWithExactGas(
message.recipientGasLimit, message.recipientContract, payload
);
uint256 remainingAllowance = token.allowance(address(this), message.recipientContract);
// Reset the recipient contract allowance to 0.
// Use of {safeApprove} is okay to reset the allowance to 0.
SafeERC20.safeApprove(token, message.recipientContract, 0);
if (success) {
emit CallSucceeded(message.recipientContract, amount);
} else {
emit CallFailed(message.recipientContract, amount);
}
// Transfer any remaining allowance to the fallback recipient. This will be the
// full amount if the call failed.
if (remainingAllowance > 0) {
token.safeTransfer(message.fallbackRecipient, remainingAllowance);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)
pragma solidity ^0.8.0;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be _NOT_ENTERED
require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* The default value of {decimals} is 18. To select a different value for
* {decimals} you should overload it.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless this function is
* overridden;
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(
address from,
address to,
uint256 amount
) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(
address from,
address to,
uint256 amount
) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(
address owner,
address spender,
uint256 amount
) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// (c) 2022-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
struct WarpMessage {
bytes32 sourceChainID;
address originSenderAddress;
bytes payload;
}
struct WarpBlockHash {
bytes32 sourceChainID;
bytes32 blockHash;
}
interface IWarpMessenger {
event SendWarpMessage(address indexed sender, bytes32 indexed messageID, bytes message);
// sendWarpMessage emits a request for the subnet to send a warp message from [msg.sender]
// with the specified parameters.
// This emits a SendWarpMessage log from the precompile. When the corresponding block is accepted
// the Accept hook of the Warp precompile is invoked with all accepted logs emitted by the Warp
// precompile.
// Each validator then adds the UnsignedWarpMessage encoded in the log to the set of messages
// it is willing to sign for an off-chain relayer to aggregate Warp signatures.
function sendWarpMessage(bytes calldata payload) external returns (bytes32 messageID);
// getVerifiedWarpMessage parses the pre-verified warp message in the
// predicate storage slots as a WarpMessage and returns it to the caller.
// If the message exists and passes verification, returns the verified message
// and true.
// Otherwise, returns false and the empty value for the message.
function getVerifiedWarpMessage(uint32 index) external view returns (WarpMessage calldata message, bool valid);
// getVerifiedWarpBlockHash parses the pre-verified WarpBlockHash message in the
// predicate storage slots as a WarpBlockHash message and returns it to the caller.
// If the message exists and passes verification, returns the verified message
// and true.
// Otherwise, returns false and the empty value for the message.
function getVerifiedWarpBlockHash(
uint32 index
) external view returns (WarpBlockHash calldata warpBlockHash, bool valid);
// getBlockchainID returns the snow.Context BlockchainID of this chain.
// This blockchainID is the hash of the transaction that created this blockchain on the P-Chain
// and is not related to the Ethereum ChainID.
function getBlockchainID() external view returns (bytes32 blockchainID);
}// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
// A message receipt identifies the message that was delivered by its nonce,
// and the address that can redeem the reward for that message.
struct TeleporterMessageReceipt {
uint256 receivedMessageNonce;
address relayerRewardAddress;
}
// Represents all of the information required for submitting a Teleporter message
// to be sent to the given destination chain ID and address. Includes the fee
// information for the message, the amount of gas the relayer must provide to execute
// the message on the destination chain, the relayer accounts allowed to deliver the
// message, and the message data itself.
struct TeleporterMessageInput {
bytes32 destinationBlockchainID;
address destinationAddress;
TeleporterFeeInfo feeInfo;
uint256 requiredGasLimit;
address[] allowedRelayerAddresses;
bytes message;
}
// Represents a message sent or received by an implementation of {ITeleporterMessenger}.
struct TeleporterMessage {
uint256 messageNonce;
address originSenderAddress;
bytes32 destinationBlockchainID;
address destinationAddress;
uint256 requiredGasLimit;
address[] allowedRelayerAddresses;
TeleporterMessageReceipt[] receipts;
bytes message;
}
// Represents the fee information associated to a given Teleporter message.
// The contract address is the asset contract the fee will be paid in, and
// the amount is the amount of that specified asset.
struct TeleporterFeeInfo {
address feeTokenAddress;
uint256 amount;
}
/**
* @dev Interface that describes functionalities for a cross-chain messenger implementing the Teleporter protcol.
*
* @custom:security-contact https://github.com/ava-labs/teleporter/blob/main/SECURITY.md
*/
interface ITeleporterMessenger {
/**
* @notice Emitted when the blockchain ID of the contract instance is initialized using the Warp precompile.
*/
event BlockchainIDInitialized(bytes32 indexed blockchainID);
/**
* @notice Emitted when sending a Teleporter message cross-chain.
*/
event SendCrossChainMessage(
bytes32 indexed messageID,
bytes32 indexed destinationBlockchainID,
TeleporterMessage message,
TeleporterFeeInfo feeInfo
);
/**
* @notice Emitted when an additional fee amount is added to a Teleporter message that had previously
* been sent, but not yet delivered to the destination chain.
*/
event AddFeeAmount(bytes32 indexed messageID, TeleporterFeeInfo updatedFeeInfo);
/**
* @notice Emitted when a Teleporter message is being delivered on the destination chain to an address,
* but message execution fails. Failed messages can then be retried with `retryMessageExecution`
*/
event MessageExecutionFailed(
bytes32 indexed messageID, bytes32 indexed sourceBlockchainID, TeleporterMessage message
);
/**
* @notice Emitted when a Teleporter message is successfully executed with the
* specified destination address and message call data. This can occur either when
* the message is initially received, or on a retry attempt.
*
* Each message received can be executed successfully at most once.
*/
event MessageExecuted(bytes32 indexed messageID, bytes32 indexed sourceBlockchainID);
/**
* @notice Emitted when a TeleporterMessage is successfully received.
*/
event ReceiveCrossChainMessage(
bytes32 indexed messageID,
bytes32 indexed sourceBlockchainID,
address indexed deliverer,
address rewardRedeemer,
TeleporterMessage message
);
/**
* @notice Emitted when a receipt is marked as received on the source chain that sent the
* corresponding Teleporter message.
*/
event ReceiptReceived(
bytes32 indexed messageID,
bytes32 indexed destinationBlockchainID,
address indexed relayerRewardAddress,
TeleporterFeeInfo feeInfo
);
/**
* @notice Emitted when an account redeems accumulated relayer rewards.
*/
event RelayerRewardsRedeemed(address indexed redeemer, address indexed asset, uint256 amount);
/**
* @notice Called by transactions to initiate the sending of a cross-chain message.
* @return The message ID of the newly sent message.
*/
function sendCrossChainMessage(TeleporterMessageInput calldata messageInput)
external
returns (bytes32);
/**
* @notice Called by transactions to retry the sending of a cross-chain message.
*
* @dev Retriggers the sending of a message previously emitted by sendCrossChainMessage that has not yet been acknowledged
* with a receipt from the destination chain. This may be necessary in the unlikely event that less than the required
* threshold of stake weight successfully inserted the message in their messages DB at the time of the first submission.
* The message is checked to have already been previously submitted by comparing its message hash against those kept in
* state until a receipt is received for the message.
*/
function retrySendCrossChainMessage(TeleporterMessage calldata message) external;
/**
* @notice Adds the additional fee amount to the amount to be paid to the relayer that delivers
* the given message ID to the destination chain.
*
* @dev The fee token address must be the same asset type as the fee asset specified in the original
* call to sendCrossChainMessage. Reverts if the message doesn't exist or there is already
* receipt of delivery of the message.
*/
function addFeeAmount(
bytes32 messageID,
address feeTokenAddress,
uint256 additionalFeeAmount
) external;
/**
* @notice Receives a cross-chain message, and marks the `relayerRewardAddress` for fee reward for a successful delivery.
*
* @dev The message specified by `messageIndex` must be provided at that index in the access list storage slots of the transaction,
* and is verified in the precompile predicate.
*/
function receiveCrossChainMessage(uint32 messageIndex, 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.
*
* @dev Intended to be used if message excution failed on initial delivery of the Teleporter message.
* For example, this may occur if the original required gas limit was not sufficient for the message
* execution, or if the destination address did not contain a contract, but a compatible contract
* was later deployed to that address. Messages are ensured to be successfully executed at most once.
*/
function retryMessageExecution(
bytes32 sourceBlockchainID,
TeleporterMessage calldata message
) external;
/**
* @notice Sends the receipts for the given `messageIDs`.
*
* @dev Sends the specified message receipts in a new message (with an empty payload) back to the source chain.
* This is intended for use in sending receipts that have not been sent in a timely manner by the standard
* receipt delivery mechanism.
* @return The message ID of the newly sent message.
*/
function sendSpecifiedReceipts(
bytes32 sourceBlockchainID,
bytes32[] calldata messageIDs,
TeleporterFeeInfo calldata feeInfo,
address[] calldata allowedRelayerAddresses
) external returns (bytes32);
/**
* @notice Sends any fee amount rewards for the given fee asset out to the caller.
*/
function redeemRelayerRewards(address feeTokenAddress) external;
/**
* @notice Gets the hash of a given message stored in the EVM state, if the message exists.
* @return The message hash
*/
function getMessageHash(bytes32 messageID) external view returns (bytes32);
/**
* @notice Checks whether or not the given message has been received by this chain.
* @return Boolean representing if the given message has been received.
*/
function messageReceived(bytes32 messageID) external view returns (bool);
/**
* @notice Returns the address the relayer reward should be sent to on the source chain
* for a given message, assuming that the message has already been delivered.
* @return The relayer reward address for the given message.
*/
function getRelayerRewardAddress(bytes32 messageID) external view returns (address);
/**
* @notice Gets the current reward amount of a given fee asset that is redeemable by the given relayer.
* @return The amount of the fee asset redeemable by the specified relayer.
*/
function checkRelayerRewardAmount(
address relayer,
address feeTokenAddress
) external view returns (uint256);
/**
* @notice Gets the fee token address and amount for a given sent message.
* @return The fee token address and fee amount for a the given sent message ID.
* If the message ID is not found, zero address and amount values are returned.
*/
function getFeeInfo(bytes32 messageID) external view returns (address, uint256);
/**
* @notice Gets the message ID that would currently be used for the next message sent from the contract
* instance to the given destination blockchain.
*
* @dev This message ID may never be used in the event that the next call to sendCrossChainMessage in a
* transaction uses a different destination blockchain. The current value as returned by this function will
* change with each successful call to sendCrossChainMessage.
* @return The specified message ID.
*/
function getNextMessageID(bytes32 destinationBlockchainID) external view returns (bytes32);
/**
* @notice Gets the number of receipts that are waiting to be sent to the given source chain ID.
* @return Size of the given queue.
*/
function getReceiptQueueSize(bytes32 sourceBlockchainID) external view returns (uint256);
/**
* @notice Gets the receipt at the given index in the queue for the given source chain ID.
* @return The receipt requested.
*/
function getReceiptAtIndex(
bytes32 sourceBlockchainID,
uint256 index
) external view returns (TeleporterMessageReceipt memory);
}// (c) 2022-2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
/**
* @dev Interface that cross-chain applications must implement to receive messages from Teleporter.
*
* @custom:security-contact https://github.com/ava-labs/teleporter/blob/main/SECURITY.md
*/
interface ITeleporterReceiver {
/**
* @dev Called by TeleporterMessenger on the receiving chain.
*
* @param sourceBlockchainID is provided by the TeleporterMessenger contract.
* @param originSenderAddress is provided by the TeleporterMessenger contract.
* @param message is the TeleporterMessage payload set by the sender.
*/
function receiveTeleporterMessage(
bytes32 sourceBlockchainID,
address originSenderAddress,
bytes calldata message
) external;
}// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {TeleporterUpgradeable} from "./TeleporterUpgradeable.sol";
import {Ownable} from "@openzeppelin/[email protected]/access/Ownable.sol";
/**
* @dev Contract that inherits {TeleporterUpgradeable} and allows
* only owners of the contract to update the minimum Teleporter version.
*
* @custom:security-contact https://github.com/ava-labs/teleporter/blob/main/SECURITY.md
*/
abstract contract TeleporterOwnerUpgradeable is TeleporterUpgradeable, Ownable {
constructor(
address teleporterRegistryAddress,
address initialOwner
) TeleporterUpgradeable(teleporterRegistryAddress) {
transferOwnership(initialOwner);
}
/**
* @dev See {TeleporterUpgradeable-_checkTeleporterUpgradeAccess}
*
* Checks that the caller is the owner of the contract for upgrade access.
*/
function _checkTeleporterUpgradeAccess() internal view virtual override {
_checkOwner();
}
}// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {ITeleporterMessenger} from "../ITeleporterMessenger.sol";
import {
IWarpMessenger,
WarpMessage
} from "@avalabs/[email protected]/contracts/interfaces/IWarpMessenger.sol";
// Registry entry that represents a mapping between protocolAddress and Teleporter version.
struct ProtocolRegistryEntry {
uint256 version;
address protocolAddress;
}
/**
* @dev TeleporterRegistry contract provides an upgrade mechanism for {ITeleporterMessenger} contracts
* through Warp off-chain messages
*
* @custom:security-contact https://github.com/ava-labs/teleporter/blob/main/SECURITY.md
*/
contract TeleporterRegistry {
/**
* @notice Address that the off-chain Warp message sets as the "source" address.
* @dev The address is not owned by any EOA or smart contract account, so it
* cannot possibly be the source address of any other Warp message emitted by the VM.
*/
address public constant VALIDATORS_SOURCE_ADDRESS = address(0);
/**
* @notice Warp precompile used for sending and receiving Warp messages.
*/
IWarpMessenger public constant WARP_MESSENGER =
IWarpMessenger(0x0200000000000000000000000000000000000005);
/**
* @notice The blockchain ID of the chain the contract is deployed on.
*/
bytes32 public immutable blockchainID;
/**
* @notice The maximum version increment allowed when adding a new protocol version.
*/
uint256 public constant MAX_VERSION_INCREMENT = 500;
/**
* @notice The latest protocol version.
* @dev 0 means no protocol version has been added, and isn't a valid version.
*/
uint256 public latestVersion;
/**
* @notice Mappings that keep track of the protocol version and corresponding contract address.
*/
mapping(uint256 version => address protocolAddress) private _versionToAddress;
mapping(address protocolAddress => uint256 version) private _addressToVersion;
/**
* @notice Emitted when a new protocol version is added to the registry.
*/
event AddProtocolVersion(uint256 indexed version, address indexed protocolAddress);
/**
* @notice Emitted when the latest version is updated.
*/
event LatestVersionUpdated(uint256 indexed oldVersion, uint256 indexed newVersion);
/**
* @dev Initializes the contract by setting `blockchainID` and `latestVersion`.
* Also adds the initial protocol versions to the registry.
*/
constructor(ProtocolRegistryEntry[] memory initialEntries) {
blockchainID = WARP_MESSENGER.getBlockchainID();
uint256 length = initialEntries.length;
for (uint256 i; i < length; ++i) {
_addToRegistry(initialEntries[i]);
}
}
/**
* @dev Receives a Warp off-chain message containing a new protocol version and address to be registered,
* and adds the new values to the respective mappings.
* If a version is greater than the current latest version, it will be set as the latest version.
* If a version is less than the current latest version, it is added to the registry, but
* doesn't change the latest version.
*
* Emits a {AddProtocolVersion} event when successful.
* Emits a {LatestVersionUpdated} event when a new protocol version greater than the current latest version is added.
* Requirements:
*
* - a valid Warp off-chain message must be provided.
* - source chain ID must be the same as the blockchain ID of the registry.
* - origin sender address must be the same as the `VALIDATORS_SOURCE_ADDRESS`.
* - destination address must be the same as the address of this registry.
*/
function addProtocolVersion(uint32 messageIndex) external {
// Get the verified Warp message, and check that it was sent to this registry via a Warp off-chain message.
(WarpMessage memory message, bool success) =
WARP_MESSENGER.getVerifiedWarpMessage(messageIndex);
require(success, "TeleporterRegistry: invalid warp message");
require(
message.sourceChainID == blockchainID, "TeleporterRegistry: invalid source chain ID"
);
// Check that the message is sent through a Warp off-chain message.
require(
message.originSenderAddress == VALIDATORS_SOURCE_ADDRESS,
"TeleporterRegistry: invalid origin sender address"
);
(ProtocolRegistryEntry memory entry, address destinationAddress) =
abi.decode(message.payload, (ProtocolRegistryEntry, address));
// Check that the message is sent to the registry.
require(
destinationAddress == address(this), "TeleporterRegistry: invalid destination address"
);
_addToRegistry(entry);
}
/**
* @dev Gets the latest {ITeleporterMessenger} contract.
*/
function getLatestTeleporter() external view returns (ITeleporterMessenger) {
return ITeleporterMessenger(getAddressFromVersion(latestVersion));
}
/**
* @dev Gets the {ITeleporterMessenger} contract of the given `version`.
* Requirements:
*
* - `version` must be a valid registered version.
*/
function getTeleporterFromVersion(uint256 version)
external
view
returns (ITeleporterMessenger)
{
return ITeleporterMessenger(getAddressFromVersion(version));
}
/**
* @dev Gets the address of a protocol version.
* Requirements:
*
* - `version` must be a valid registered version.
*/
function getAddressFromVersion(uint256 version) public view returns (address) {
require(version != 0, "TeleporterRegistry: zero version");
address protocolAddress = _versionToAddress[version];
require(protocolAddress != address(0), "TeleporterRegistry: version not found");
return protocolAddress;
}
/**
* @dev Gets the version of the given `protocolAddress`.
* Requirements:
*
* - `protocolAddress` must be a valid registered protocol address.
*/
function getVersionFromAddress(address protocolAddress) public view returns (uint256) {
require(protocolAddress != address(0), "TeleporterRegistry: zero protocol address");
uint256 version = _addressToVersion[protocolAddress];
require(version != 0, "TeleporterRegistry: protocol address not found");
return version;
}
/**
* @dev Adds the new protocol version address to the registry.
* Updates latest version if the version is greater than the current latest version.
*
* Emits a {AddProtocolVersion} event when successful.
* Emits a {LatestVersionUpdated} event when a new protocol version greater than the current latest version is added.
* Note: `entry.protocolAddress` doesn't have to be a contract address, allowing a new protocol address to be registered before the contract is deployed.
* Requirements:
*
* - `entry.version` is not zero
* - `entry.version` is not already registered
* - `entry.protocolAddress` is not zero address
*/
function _addToRegistry(ProtocolRegistryEntry memory entry) private {
require(entry.version != 0, "TeleporterRegistry: zero version");
// Check that the version has not previously been registered.
require(
_versionToAddress[entry.version] == address(0),
"TeleporterRegistry: version already exists"
);
require(entry.protocolAddress != address(0), "TeleporterRegistry: zero protocol address");
uint256 latestVersion_ = latestVersion;
require(
entry.version <= latestVersion_ + MAX_VERSION_INCREMENT,
"TeleporterRegistry: version increment too high"
);
_versionToAddress[entry.version] = entry.protocolAddress;
// Since a protocol address can be registered multiple times,
// only update the version if the new version is greater than the current version.
if (entry.version > _addressToVersion[entry.protocolAddress]) {
_addressToVersion[entry.protocolAddress] = entry.version;
}
emit AddProtocolVersion(entry.version, entry.protocolAddress);
// Set latest version if the version is greater than the current latest version.
if (entry.version > latestVersion_) {
latestVersion = entry.version;
emit LatestVersionUpdated(latestVersion_, entry.version);
}
}
}// (c) 2023, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {TeleporterRegistry} from "./TeleporterRegistry.sol";
import {ITeleporterReceiver} from "../ITeleporterReceiver.sol";
import {ITeleporterMessenger, TeleporterMessageInput} from "../ITeleporterMessenger.sol";
import {Context} from "@openzeppelin/[email protected]/utils/Context.sol";
import {ReentrancyGuard} from "@openzeppelin/[email protected]/security/ReentrancyGuard.sol";
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
/**
* @dev TeleporterUpgradeable provides upgrade utility for applications built on top
* of the Teleporter protocol by integrating with the {TeleporterRegistry}.
*
* This contract is intended to be inherited by other contracts that wish to use the
* upgrade mechanism. It provides an interface that restricts access to only Teleporter
* versions that are greater than or equal to `minTeleporterVersion`.
*
* @custom:security-contact https://github.com/ava-labs/teleporter/blob/main/SECURITY.md
*/
abstract contract TeleporterUpgradeable is Context, ITeleporterReceiver, ReentrancyGuard {
using SafeERC20 for IERC20;
// The Teleporter registry contract manages different Teleporter contract versions.
TeleporterRegistry public immutable teleporterRegistry;
/**
* @dev A mapping that keeps track of paused Teleporter addresses.
*/
mapping(address teleporterAddress => bool paused) private _pausedTeleporterAddresses;
/**
* @dev The minimum required Teleporter version that the contract is allowed
* to receive messages from. Should only be updated by `_setMinTeleporterVersion`
*/
uint256 private _minTeleporterVersion;
/**
* @dev Emitted when `minTeleporterVersion` is updated.
*/
event MinTeleporterVersionUpdated(
uint256 indexed oldMinTeleporterVersion, uint256 indexed newMinTeleporterVersion
);
/**
* @dev Emitted when a Teleporter address is paused.
*/
event TeleporterAddressPaused(address indexed teleporterAddress);
/**
* @dev Emitted when a Teleporter address is unpaused.
*/
event TeleporterAddressUnpaused(address indexed teleporterAddress);
/**
* @dev Initializes the {TeleporterUpgradeable} contract by getting `teleporterRegistry`
* instance and setting `_minTeleporterVersion`.
*/
constructor(address teleporterRegistryAddress) {
require(
teleporterRegistryAddress != address(0),
"TeleporterUpgradeable: zero teleporter registry address"
);
teleporterRegistry = TeleporterRegistry(teleporterRegistryAddress);
_minTeleporterVersion = teleporterRegistry.latestVersion();
}
/**
* @dev See {ITeleporterReceiver-receiveTeleporterMessage}
* `nonReentrant` is a reentrancy guard that protects again multiple versions of the
* TeleporterMessengerContract delivering a message in the same call. Any internal calls
* will not be able to call functions also marked with `nonReentrant`.
*
* Requirements:
*
* - `_msgSender()` must be a Teleporter version greater than or equal to `minTeleporterVersion`.
*/
function receiveTeleporterMessage(
bytes32 sourceBlockchainID,
address originSenderAddress,
bytes calldata message
) external nonReentrant {
// Checks that `_msgSender()` matches a Teleporter version greater than or equal to `minTeleporterVersion`.
require(
teleporterRegistry.getVersionFromAddress(_msgSender()) >= _minTeleporterVersion,
"TeleporterUpgradeable: invalid Teleporter sender"
);
// Check against the paused Teleporter addresses.
require(
!isTeleporterAddressPaused(_msgSender()),
"TeleporterUpgradeable: Teleporter address paused"
);
_receiveTeleporterMessage(sourceBlockchainID, originSenderAddress, message);
}
/**
* @dev Updates the minimum Teleporter version allowed for delivering Teleporer messages
* to this contract.
*
* To prevent anyone from being able to call this function, which would disallow messages
* from old Teleporter versions from being received, this function should be safeguarded with access
* controls. This is done by overriding the implementation of {_checkTeleporterUpgradeAccess}.
*/
function updateMinTeleporterVersion(uint256 version) public virtual {
_checkTeleporterUpgradeAccess();
_setMinTeleporterVersion(version);
}
/**
* @dev Pauses a Teleporter address from interacting with this contract.
* After pausing a Teleporter address, it will no longer be able to deliver messages
* to this contract, and this contract will not send messages through that Teleporter address.
* The address does not need to be registered with the Teleporter registry.
* Emits a {TeleporterAddressPaused} event if successfully paused.
* Requirements:
*
* - `_msgSender()` must have Teleporter upgrade access.
* - `teleporterAddress` is not the zero address.
* - `teleporterAddress` is not already paused.
*/
function pauseTeleporterAddress(address teleporterAddress) public virtual {
_checkTeleporterUpgradeAccess();
require(teleporterAddress != address(0), "TeleporterUpgradeable: zero Teleporter address");
require(
!isTeleporterAddressPaused(teleporterAddress),
"TeleporterUpgradeable: address already paused"
);
_pausedTeleporterAddresses[teleporterAddress] = true;
emit TeleporterAddressPaused(teleporterAddress);
}
/**
* @dev Unpauses a Teleporter address from interacting with this contract.
* After unpausing a Teleporter address, it will again be able to deliver messages
* to this contract, and this contract can send messages through that Teleporter address.
* The address does not need to be registered with the Teleporter registry.
* Emits a {TeleporterAddressUnpaused} event if successfully unpaused.
* Requirements:
*
* - `_msgSender()` must have Teleporter upgrade access.
* - `teleporterAddress` is not the zero address.
* - `teleporterAddress` is already paused.
*/
function unpauseTeleporterAddress(address teleporterAddress) public virtual {
_checkTeleporterUpgradeAccess();
require(teleporterAddress != address(0), "TeleporterUpgradeable: zero Teleporter address");
require(
isTeleporterAddressPaused(teleporterAddress),
"TeleporterUpgradeable: address not paused"
);
emit TeleporterAddressUnpaused(teleporterAddress);
_pausedTeleporterAddresses[teleporterAddress] = false;
}
/**
* @dev Public getter for `_minTeleporterVersion`.
*/
function getMinTeleporterVersion() public view returns (uint256) {
return _minTeleporterVersion;
}
/**
* @dev Checks if a Teleporter address is paused.
*/
function isTeleporterAddressPaused(address teleporterAddress)
public
view
virtual
returns (bool)
{
return _pausedTeleporterAddresses[teleporterAddress];
}
/**
* @dev Sets the minimum Teleporter version allowed for delivering Teleporter messages.
* Emits a {MinTeleporterVersionUpdated} event if the minimum Teleporter version was updated.
* Requirements:
*
* - `version` must be less than or equal to the latest Teleporter version.
* - `version` must be greater than the current minimum Teleporter version.
*
*/
function _setMinTeleporterVersion(uint256 version) internal virtual {
uint256 latestTeleporterVersion = teleporterRegistry.latestVersion();
uint256 oldMinTeleporterVersion = _minTeleporterVersion;
require(
version <= latestTeleporterVersion, "TeleporterUpgradeable: invalid Teleporter version"
);
require(
version > oldMinTeleporterVersion,
"TeleporterUpgradeable: not greater than current minimum version"
);
_minTeleporterVersion = version;
emit MinTeleporterVersionUpdated(oldMinTeleporterVersion, version);
}
/**
* @dev Receives Teleporter messages and handles accordingly.
* This function should be overridden by contracts that inherit from this contract.
*/
function _receiveTeleporterMessage(
bytes32 sourceBlockchainID,
address originSenderAddress,
bytes memory message
) internal virtual;
/**
* @dev Checks that the caller has access to update the minimum Teleporter version
* allowed for delivering Teleporter messages to this contract.
*
* This function should be overridden by contracts that inherit from this contract.
*/
function _checkTeleporterUpgradeAccess() internal virtual;
/**
* @dev Sends a cross chain message using the TeleporterMessenger contract.
*
* The fee amount should be transferred to this contract prior to calling this function.
* The fee amount is then allocated from this contract's token balance to
* Teleporter's allowance to pay for sending the message.
*
* @return `messageID` The unique identifier for the Teleporter message.
*/
function _sendTeleporterMessage(TeleporterMessageInput memory messageInput)
internal
virtual
returns (bytes32)
{
ITeleporterMessenger teleporterMessenger = _getTeleporterMessenger();
// For non-zero fee amounts increase the Teleporter contract's allowance.
if (messageInput.feeInfo.amount > 0) {
require(
messageInput.feeInfo.feeTokenAddress != address(0),
"TeleporterUpgradeable: zero fee token address"
);
IERC20(messageInput.feeInfo.feeTokenAddress).safeIncreaseAllowance(
address(teleporterMessenger), messageInput.feeInfo.amount
);
}
return teleporterMessenger.sendCrossChainMessage(messageInput);
}
/**
* @dev Returns the Teleporter messenger used to send Teleporter messages,
* and checks that the Teleporter messenger is not paused.
*
* By default returns the latest Teleporter messenger, but can be overriden to
* return a Teleporter messenger of a specific version.
*/
function _getTeleporterMessenger() internal view virtual returns (ITeleporterMessenger) {
ITeleporterMessenger teleporter = teleporterRegistry.getLatestTeleporter();
require(
!isTeleporterAddressPaused(address(teleporter)),
"TeleporterUpgradeable: Teleporter sending paused"
);
return teleporter;
}
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {TeleporterMessageInput, TeleporterFeeInfo} from "@teleporter/ITeleporterMessenger.sol";
import {TeleporterOwnerUpgradeable} from "@teleporter/upgrades/TeleporterOwnerUpgradeable.sol";
import {ITokenHome} from "./interfaces/ITokenHome.sol";
import {
SendTokensInput,
SendAndCallInput,
TransferrerMessageType,
TransferrerMessage,
SingleHopSendMessage,
SingleHopCallMessage,
MultiHopSendMessage,
MultiHopCallMessage,
RegisterRemoteMessage
} from "../interfaces/ITokenTransferrer.sol";
import {SendReentrancyGuard} from "../utils/SendReentrancyGuard.sol";
import {TokenScalingUtils} from "../utils/TokenScalingUtils.sol";
import {SafeERC20TransferFrom} from "../utils/SafeERC20TransferFrom.sol";
import {IWarpMessenger} from
"@avalabs/[email protected]/contracts/interfaces/IWarpMessenger.sol";
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/ERC20.sol";
/**
* @notice Each TokenRemote instance registers with the home contract, and provides settings for transferring
* to the remote token transfer contract.
* @param registered Whether the remote token transferrer is registered
* @param collateralNeeded The amount of tokens that must be first added as collateral,
* through {addCollateral} calls, before tokens can be transferred to the remote token transferrer.
* @param tokenMultiplier The scaling factor for the amount of tokens to be transferred to the remote.
* @param multiplyOnRemote Whether the {tokenMultiplier} should be applied when transferring tokens to
* the remote (multiplyOnRemote=true), or when transferring tokens back to the home (multiplyOnRemote=false).
*/
struct RemoteTokenTransferrerSettings {
bool registered;
uint256 collateralNeeded;
uint256 tokenMultiplier;
bool multiplyOnRemote;
}
/**
* @title TokenHome
* @dev Abstract contract for a token transferrer home that sends its specified token to {TokenRemote} instances.
*
* This contract also handles multi-hop transfers, where tokens sent from a {TokenRemote}
* instance are forwarded to another {TokenRemote} instance.
*
* @custom:security-contact https://github.com/ava-labs/avalanche-interchain-token-transfer/blob/main/SECURITY.md
*/
abstract contract TokenHome is ITokenHome, TeleporterOwnerUpgradeable, SendReentrancyGuard {
/// @notice The blockchain ID of the chain this contract is deployed on.
bytes32 public immutable blockchainID;
/**
* @notice The token address this home contract transfers to TokenRemote instances.
* For multi-hop transfers, this {tokenAddress} is always used to pay for the secondary message fees.
* If the token is an ERC20 token, the contract address is directly passed in.
* If the token is a native asset, the contract address is the wrapped token contract.
*/
address public immutable tokenAddress;
uint8 public immutable tokenDecimals;
/**
* @notice Tracks the settings for each remote token transferrer instance. TokenRemote instances
* must register with their {TokenHome} contracts via Teleporter message to be able to
* receive tokens from this contract.
*/
mapping(
bytes32 remoteBlockchainID
=> mapping(
address remoteTokenTransferrerAddress
=> RemoteTokenTransferrerSettings remoteSettings
)
) public registeredRemotes;
/**
* @notice Tracks the balances of tokens sent to TokenRemote instances.
* Balances are represented in the remote token's denomination,
* and token transferrers are not allowed to unwrap more than has been sent to them.
* @dev (remoteBlockchainID, remoteTokenTransferrerAddress) -> balance
*/
mapping(
bytes32 remoteBlockchainID
=> mapping(address remoteTokenTransferrerAddress => uint256 balance)
) public transferredBalances;
/**
* @notice Initializes this home token transferrer instance to send tokens to TokenRemote instances on other chains.
*/
constructor(
address teleporterRegistryAddress,
address teleporterManager,
address tokenAddress_,
uint8 tokenDecimals_
) TeleporterOwnerUpgradeable(teleporterRegistryAddress, teleporterManager) {
blockchainID = IWarpMessenger(0x0200000000000000000000000000000000000005).getBlockchainID();
require(tokenAddress_ != address(0), "TokenHome: zero token address");
require(
tokenDecimals_ <= TokenScalingUtils.MAX_TOKEN_DECIMALS,
"TokenHome: token decimals too high"
);
tokenAddress = tokenAddress_;
tokenDecimals = tokenDecimals_;
}
function _registerRemote(
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
RegisterRemoteMessage memory message
) internal {
require(remoteBlockchainID != bytes32(0), "TokenHome: zero remote blockchain ID");
require(
remoteBlockchainID != blockchainID, "TokenHome: cannot register remote on same chain"
);
require(
remoteTokenTransferrerAddress != address(0),
"TokenHome: zero remote token transferrer address"
);
require(
!registeredRemotes[remoteBlockchainID][remoteTokenTransferrerAddress].registered,
"TokenHome: remote already registered"
);
require(
message.remoteTokenDecimals <= TokenScalingUtils.MAX_TOKEN_DECIMALS,
"TokenHome: remote token decimals too high"
);
require(
message.homeTokenDecimals == tokenDecimals, "TokenHome: invalid home token decimals"
);
(uint256 tokenMultiplier, bool multiplyOnRemote) = TokenScalingUtils
.deriveTokenMultiplierValues(tokenDecimals, message.remoteTokenDecimals);
// Calculate the collateral needed in home token denomination.
uint256 collateralNeeded = TokenScalingUtils.removeTokenScale(
tokenMultiplier, multiplyOnRemote, message.initialReserveImbalance
);
// Round up the collateral needed by 1 in the case that {multiplyOnRemote} is true and
// {initialReserveImbalance} is not divisible by the {tokenMultiplier} to
// ensure that the full amount is accounted for.
if (multiplyOnRemote && message.initialReserveImbalance % tokenMultiplier != 0) {
collateralNeeded += 1;
}
registeredRemotes[remoteBlockchainID][remoteTokenTransferrerAddress] =
RemoteTokenTransferrerSettings({
registered: true,
collateralNeeded: collateralNeeded,
tokenMultiplier: tokenMultiplier,
multiplyOnRemote: multiplyOnRemote
});
emit RemoteRegistered(
remoteBlockchainID,
remoteTokenTransferrerAddress,
collateralNeeded,
message.remoteTokenDecimals
);
}
/**
* @notice Sends tokens to the specified remote token transferrer instance.
*
* @dev Increases the balance sent to the remote token transferrer instance,
* and uses Teleporter to send a cross chain message. The amount passed is assumed to
* be already scaled to the local denomination for this token home.
* Requirements:
*
* - The TokenRemote instance specified by {input.destinationBlockchainID} and {input.destinationTokenTransferrerAddress} must
* be registered with this contract.
* - {input.recipient} cannot be the zero address
* - {amount} must be greater than 0
*/
function _send(SendTokensInput memory input, uint256 amount) internal sendNonReentrant {
_validateSendTokensInput(input);
// Require that a single hop transfer does not have a multi-hop fallback recipient.
require(input.multiHopFallback == address(0), "TokenHome: non-zero multi-hop fallback");
(uint256 adjustedAmount, uint256 feeAmount) = _prepareSend({
remoteBlockchainID: input.destinationBlockchainID,
remoteTokenTransferrerAddress: input.destinationTokenTransferrerAddress,
amount: amount,
primaryFeeTokenAddress: input.primaryFeeTokenAddress,
feeAmount: input.primaryFee
});
TransferrerMessage memory message = TransferrerMessage({
messageType: TransferrerMessageType.SINGLE_HOP_SEND,
payload: abi.encode(
SingleHopSendMessage({recipient: input.recipient, amount: adjustedAmount})
)
});
// Send message to the TokenRemote instance
bytes32 messageID = _sendTeleporterMessage(
TeleporterMessageInput({
destinationBlockchainID: input.destinationBlockchainID,
destinationAddress: input.destinationTokenTransferrerAddress,
feeInfo: TeleporterFeeInfo({
feeTokenAddress: input.primaryFeeTokenAddress,
amount: feeAmount
}),
requiredGasLimit: input.requiredGasLimit,
allowedRelayerAddresses: new address[](0),
message: abi.encode(message)
})
);
emit TokensSent(messageID, _msgSender(), input, adjustedAmount);
}
/**
* @notice Routes tokens from a multi-hop message to the specified remote token transferrer instance.
*
* @dev Increases the balance sent to the remote token transferrer instance,
* and uses Teleporter to send a cross chain message. The amount passed is assumed to
* be already scaled to the local denomination for this token home.
* Requirements:
*
* - The TokenRemote instance specified by {input.destinationBlockchainID} and {input.destinationTokenTransferrerAddress} must
* be registered with this contract.
* - {input.recipient} cannot be the zero address
* - {amount} must be greater than 0
*/
function _routeMultiHop(
SendTokensInput memory input,
uint256 amount
) internal sendNonReentrant {
_validateSendTokensInput(input);
uint256 adjustedAmount = _prepareMultiHopRouting(
input.destinationBlockchainID,
input.destinationTokenTransferrerAddress,
amount,
input.primaryFee
);
if (adjustedAmount == 0) {
// If the adjusted amount is zero for any reason (i.e. unregistered remote,
// being scaled down to zero, etc.), send the tokens to the multi-hop fallback.
_withdraw(input.multiHopFallback, amount);
return;
}
TransferrerMessage memory message = TransferrerMessage({
messageType: TransferrerMessageType.SINGLE_HOP_SEND,
payload: abi.encode(
SingleHopSendMessage({recipient: input.recipient, amount: adjustedAmount})
)
});
// Send message to the TokenRemote instance.
bytes32 messageID = _sendTeleporterMessage(
TeleporterMessageInput({
destinationBlockchainID: input.destinationBlockchainID,
destinationAddress: input.destinationTokenTransferrerAddress,
feeInfo: TeleporterFeeInfo({
feeTokenAddress: input.primaryFeeTokenAddress,
amount: input.primaryFee
}),
requiredGasLimit: input.requiredGasLimit,
allowedRelayerAddresses: new address[](0),
message: abi.encode(message)
})
);
emit TokensRouted(messageID, input, adjustedAmount);
}
function _sendAndCall(
bytes32 sourceBlockchainID,
address originTokenTransferrerAddress,
address originSenderAddress,
SendAndCallInput memory input,
uint256 amount
) internal sendNonReentrant {
_validateSendAndCallInput(input);
// Require that a single hop transfer does not have a multi-hop fallback recipient.
require(input.multiHopFallback == address(0), "TokenHome: non-zero multi-hop fallback");
(uint256 adjustedAmount, uint256 feeAmount) = _prepareSend({
remoteBlockchainID: input.destinationBlockchainID,
remoteTokenTransferrerAddress: input.destinationTokenTransferrerAddress,
amount: amount,
primaryFeeTokenAddress: input.primaryFeeTokenAddress,
feeAmount: input.primaryFee
});
TransferrerMessage memory message = TransferrerMessage({
messageType: TransferrerMessageType.SINGLE_HOP_CALL,
payload: abi.encode(
SingleHopCallMessage({
sourceBlockchainID: sourceBlockchainID,
originTokenTransferrerAddress: originTokenTransferrerAddress,
originSenderAddress: originSenderAddress,
recipientContract: input.recipientContract,
amount: adjustedAmount,
recipientPayload: input.recipientPayload,
recipientGasLimit: input.recipientGasLimit,
fallbackRecipient: input.fallbackRecipient
})
)
});
// Send message to the TokenRemote instance.
bytes32 messageID = _sendTeleporterMessage(
TeleporterMessageInput({
destinationBlockchainID: input.destinationBlockchainID,
destinationAddress: input.destinationTokenTransferrerAddress,
feeInfo: TeleporterFeeInfo({
feeTokenAddress: input.primaryFeeTokenAddress,
amount: feeAmount
}),
requiredGasLimit: input.requiredGasLimit,
allowedRelayerAddresses: new address[](0),
message: abi.encode(message)
})
);
emit TokensAndCallSent(messageID, originSenderAddress, input, adjustedAmount);
}
function _routeMultiHopSendAndCall(
bytes32 sourceBlockchainID,
address originTokenTransferrerAddress,
address originSenderAddress,
SendAndCallInput memory input,
uint256 amount
) internal sendNonReentrant {
_validateSendAndCallInput(input);
uint256 adjustedAmount = _prepareMultiHopRouting(
input.destinationBlockchainID,
input.destinationTokenTransferrerAddress,
amount,
input.primaryFee
);
if (adjustedAmount == 0) {
// If the adjusted amount is zero for any reason (i.e. unregistered remote,
// being scaled down to zero, etc.), send the tokens to the multi-hop fallback recipient.
_withdraw(input.multiHopFallback, amount);
return;
}
TransferrerMessage memory message = TransferrerMessage({
messageType: TransferrerMessageType.SINGLE_HOP_CALL,
payload: abi.encode(
SingleHopCallMessage({
sourceBlockchainID: sourceBlockchainID,
originTokenTransferrerAddress: originTokenTransferrerAddress,
originSenderAddress: originSenderAddress,
recipientContract: input.recipientContract,
amount: adjustedAmount,
recipientPayload: input.recipientPayload,
recipientGasLimit: input.recipientGasLimit,
fallbackRecipient: input.fallbackRecipient
})
)
});
// Send message to the TokenRemote instance.
bytes32 messageID = _sendTeleporterMessage(
TeleporterMessageInput({
destinationBlockchainID: input.destinationBlockchainID,
destinationAddress: input.destinationTokenTransferrerAddress,
feeInfo: TeleporterFeeInfo({
feeTokenAddress: input.primaryFeeTokenAddress,
amount: input.primaryFee
}),
requiredGasLimit: input.requiredGasLimit,
allowedRelayerAddresses: new address[](0),
message: abi.encode(message)
})
);
emit TokensAndCallRouted(messageID, input, adjustedAmount);
}
/**
* @dev See {INativeTokenHome-addCollateral}
*/
function _addCollateral(
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
uint256 amount
) internal sendNonReentrant {
RemoteTokenTransferrerSettings memory remoteSettings =
registeredRemotes[remoteBlockchainID][remoteTokenTransferrerAddress];
require(remoteSettings.registered, "TokenHome: remote not registered");
require(remoteSettings.collateralNeeded > 0, "TokenHome: zero collateral needed");
// Deposit the full amount, and withdraw back to the sender if there is excess.
amount = _deposit(amount);
// Calculate the remaining collateral needed, any excess amount, and adjust
// {amount} to represent the amount of tokens added as collateral.
uint256 remainingCollateralNeeded;
uint256 excessAmount;
if (amount >= remoteSettings.collateralNeeded) {
remainingCollateralNeeded = 0;
excessAmount = amount - remoteSettings.collateralNeeded;
amount = remoteSettings.collateralNeeded;
} else {
remainingCollateralNeeded = remoteSettings.collateralNeeded - amount;
}
// Update the remaining collateral needed.
registeredRemotes[remoteBlockchainID][remoteTokenTransferrerAddress].collateralNeeded =
remainingCollateralNeeded;
emit CollateralAdded(
remoteBlockchainID, remoteTokenTransferrerAddress, amount, remainingCollateralNeeded
);
// If there is excess amount, send it back to the sender.
if (excessAmount > 0) {
_withdraw(_msgSender(), excessAmount);
}
}
/**
* @dev See {ITeleporterUpgradeable-_receiveTeleporterMessage}
*
* Handles the processing of Teleporter messages sent to this contract.
* Supported message types include registering a TokenRemote instance, single-hop sends,
* single-hop calls, multi-hop sends, and multi-hop calls.
*/
function _receiveTeleporterMessage(
bytes32 sourceBlockchainID,
address originSenderAddress,
bytes memory message
) internal override {
TransferrerMessage memory transferrerMessage = abi.decode(message, (TransferrerMessage));
if (transferrerMessage.messageType == TransferrerMessageType.SINGLE_HOP_SEND) {
SingleHopSendMessage memory payload =
abi.decode(transferrerMessage.payload, (SingleHopSendMessage));
uint256 homeAmount =
_processSingleHopTransfer(sourceBlockchainID, originSenderAddress, payload.amount);
// Send the tokens to the recipient.
_withdraw(payload.recipient, homeAmount);
return;
} else if (transferrerMessage.messageType == TransferrerMessageType.SINGLE_HOP_CALL) {
SingleHopCallMessage memory payload =
abi.decode(transferrerMessage.payload, (SingleHopCallMessage));
uint256 homeAmount =
_processSingleHopTransfer(sourceBlockchainID, originSenderAddress, payload.amount);
// Verify that the payload's source blockchain ID and origin token transferrer address matches the source blockchain ID
// and origin sender address passed from Teleporter.
require(
payload.sourceBlockchainID == sourceBlockchainID,
"TokenHome: mismatched source blockchain ID"
);
require(
payload.originTokenTransferrerAddress == originSenderAddress,
"TokenHome: mismatched origin sender address"
);
_handleSendAndCall(payload, homeAmount);
return;
} else if (transferrerMessage.messageType == TransferrerMessageType.MULTI_HOP_SEND) {
MultiHopSendMessage memory payload =
abi.decode(transferrerMessage.payload, (MultiHopSendMessage));
(uint256 homeAmount, uint256 fee) = _processMultiHopTransfer(
sourceBlockchainID, originSenderAddress, payload.amount, payload.secondaryFee
);
// For a multi-hop send, the fee token address has to be {tokenAddress},
// because the fee is taken from the amount that has already been deposited.
// For ERC20 tokens, the token address of the contract is directly passed.
// For native assets, the contract address is the wrapped token contract.
_routeMultiHop(
SendTokensInput({
destinationBlockchainID: payload.destinationBlockchainID,
destinationTokenTransferrerAddress: payload.destinationTokenTransferrerAddress,
recipient: payload.recipient,
primaryFeeTokenAddress: tokenAddress,
primaryFee: fee,
secondaryFee: 0,
requiredGasLimit: payload.secondaryGasLimit,
multiHopFallback: payload.multiHopFallback
}),
homeAmount
);
return;
} else if (transferrerMessage.messageType == TransferrerMessageType.MULTI_HOP_CALL) {
MultiHopCallMessage memory payload =
abi.decode(transferrerMessage.payload, (MultiHopCallMessage));
(uint256 homeAmount, uint256 fee) = _processMultiHopTransfer(
sourceBlockchainID, originSenderAddress, payload.amount, payload.secondaryFee
);
// For a multi-hop send, the fee token address has to be {tokenAddress},
// because the fee is taken from the amount that has already been deposited.
// For ERC20 tokens, the token address of the contract is directly passed.
// For native assets, the contract address is the wrapped token contract.
_routeMultiHopSendAndCall({
sourceBlockchainID: sourceBlockchainID,
originTokenTransferrerAddress: originSenderAddress,
originSenderAddress: payload.originSenderAddress,
input: SendAndCallInput({
destinationBlockchainID: payload.destinationBlockchainID,
destinationTokenTransferrerAddress: payload.destinationTokenTransferrerAddress,
recipientContract: payload.recipientContract,
recipientPayload: payload.recipientPayload,
requiredGasLimit: payload.secondaryRequiredGasLimit,
recipientGasLimit: payload.recipientGasLimit,
multiHopFallback: payload.multiHopFallback,
fallbackRecipient: payload.fallbackRecipient,
primaryFeeTokenAddress: tokenAddress,
primaryFee: fee,
secondaryFee: 0
}),
amount: homeAmount
});
return;
} else if (transferrerMessage.messageType == TransferrerMessageType.REGISTER_REMOTE) {
RegisterRemoteMessage memory payload =
abi.decode(transferrerMessage.payload, (RegisterRemoteMessage));
_registerRemote(sourceBlockchainID, originSenderAddress, payload);
}
}
/**
* @notice Deposits tokens from the sender to this contract,
* and returns the adjusted amount of tokens deposited.
* @param amount The initial amount sent to this contract.
* @return The actual amount deposited to this contract.
*/
function _deposit(uint256 amount) internal virtual returns (uint256);
/**
* @notice Withdraws tokens to the recipient address.
* @param recipient The address to withdraw tokens to
* @param amount The amount of tokens to withdraw
*/
function _withdraw(address recipient, uint256 amount) internal virtual;
/**
* @notice Processes a send and call message by calling the recipient contract.
* @param message The send and call message include recipient calldata
* @param amount The amount of tokens to be sent to the recipient. This amount is assumed to be
* already scaled to the local denomination for this token home.
*/
function _handleSendAndCall(
SingleHopCallMessage memory message,
uint256 amount
) internal virtual;
/**
* @notice Processes a received single hop transfer from a TokenRemote instance.
* Validates that the message is sent from a registered TokenRemote instance,
* and is already collateralized.
* @param remoteBlockchainID The blockchain ID of the TokenRemote instance.
* @param remoteTokenTransferrerAddress The address of the TokenRemote instance.
* @param amount The amount of tokens sent back from remote, denominated by the
* remote's token scale amount.
*/
function _processSingleHopTransfer(
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
uint256 amount
) private returns (uint256) {
RemoteTokenTransferrerSettings memory remoteSettings =
registeredRemotes[remoteBlockchainID][remoteTokenTransferrerAddress];
return _processReceivedTransfer(
remoteSettings, remoteBlockchainID, remoteTokenTransferrerAddress, amount
);
}
/**
* @notice Processes a received multi-hop transfer from a TokenRemote instance.
* Validates that the message is sent from a registered TokenRemote instance,
* and is already collateralized.
* @param remoteBlockchainID The blockchain ID of the TokenRemote instance.
* @param remoteTokenTransferrerAddress The address of the TokenRemote instance.
* @param amount The amount of tokens sent back from remote, denominated by the
* remote's token scale amount.
* @param secondaryFee The Teleporter fee for the second hop of the mutihop transfer
*/
function _processMultiHopTransfer(
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
uint256 amount,
uint256 secondaryFee
) private returns (uint256, uint256) {
RemoteTokenTransferrerSettings memory remoteSettings =
registeredRemotes[remoteBlockchainID][remoteTokenTransferrerAddress];
uint256 transferAmount = _processReceivedTransfer(
remoteSettings, remoteBlockchainID, remoteTokenTransferrerAddress, amount
);
uint256 fee = TokenScalingUtils.removeTokenScale(
remoteSettings.tokenMultiplier, remoteSettings.multiplyOnRemote, secondaryFee
);
return (transferAmount, fee);
}
/**
* @notice Processes a received transfer from a TokenRemote instance.
* Deducts the balance transferred to the given TokenRemote instance.
* Removes the token scaling of the remote, checks the associated home token
* amount is greater than zero, and returns the home token amount.
* @param remoteSettings The token transferrer settings for the TokenRemote instance we received the transfer from.
* @param remoteBlockchainID The blockchain ID of the TokenRemote instance.
* @param remoteTokenTransferrerAddress The address of the TokenRemote instance.
* @param amount The amount of tokens sent back from remote, denominated by the
* remote's token scale amount.
*/
function _processReceivedTransfer(
RemoteTokenTransferrerSettings memory remoteSettings,
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
uint256 amount
) private returns (uint256) {
// Require that the remote is registered and has no collateral needed.
require(remoteSettings.registered, "TokenHome: remote not registered");
require(remoteSettings.collateralNeeded == 0, "TokenHome: remote not collateralized");
// Deduct the balance transferred to the given TokenRemote instance prior to scaling the amount.
_deductSenderBalance(remoteBlockchainID, remoteTokenTransferrerAddress, amount);
// Remove the token scaling of the remote and get home token amount.
uint256 homeAmount = TokenScalingUtils.removeTokenScale(
remoteSettings.tokenMultiplier, remoteSettings.multiplyOnRemote, amount
);
// Require that the home token amount is greater than zero after removed scaling.
require(homeAmount > 0, "TokenHome: zero token amount");
return homeAmount;
}
/**
* @notice Prepares a multi-hop send by checking the TokenRemote instance settings
* and adjusting the amount to be sent.
* @return The scaled amount to be sent to the TokenRemote instance. Zero can be returned if the
* TokenRemote instance is not registered, needs collateral, or the scaled amount is zero.
*/
function _prepareMultiHopRouting(
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
uint256 amount,
uint256 fee
) private returns (uint256) {
RemoteTokenTransferrerSettings memory remoteSettings =
registeredRemotes[remoteBlockchainID][remoteTokenTransferrerAddress];
if (!remoteSettings.registered || remoteSettings.collateralNeeded > 0) {
return 0;
}
// Subtract fee amount from amount prior to scaling.
require(amount > fee, "TokenHome: insufficient amount to cover fees");
amount -= fee;
// Scale the amount based on the token multiplier for the given TokenRemote instance.
uint256 scaledAmount = TokenScalingUtils.applyTokenScale(
remoteSettings.tokenMultiplier, remoteSettings.multiplyOnRemote, amount
);
if (scaledAmount == 0) {
return 0;
}
// Increase the balance of the TokenRemote instance by the scaled amount.
transferredBalances[remoteBlockchainID][remoteTokenTransferrerAddress] += scaledAmount;
return scaledAmount;
}
/**
* @dev Prepares tokens to be sent to another chain by handling the
* locking of the token amount in this contract and updating the accounting
* balances.
*/
function _prepareSend(
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
uint256 amount,
address primaryFeeTokenAddress,
uint256 feeAmount
) private returns (uint256, uint256) {
RemoteTokenTransferrerSettings memory remoteSettings =
registeredRemotes[remoteBlockchainID][remoteTokenTransferrerAddress];
require(remoteSettings.registered, "TokenHome: remote not registered");
require(remoteSettings.collateralNeeded == 0, "TokenHome: collateral needed for remote");
// Deposit the funds sent from the user to the token transferrer,
// and set to adjusted amount after deposit.
amount = _deposit(amount);
if (feeAmount > 0) {
feeAmount = SafeERC20TransferFrom.safeTransferFrom(
IERC20(primaryFeeTokenAddress), _msgSender(), feeAmount
);
}
// Scale the amount based on the token multiplier for the given TokenRemote instance.
uint256 scaledAmount = TokenScalingUtils.applyTokenScale(
remoteSettings.tokenMultiplier, remoteSettings.multiplyOnRemote, amount
);
require(scaledAmount > 0, "TokenHome: zero scaled amount");
// Increase the balance of the TokenRemote instance by the scaled amount.
transferredBalances[remoteBlockchainID][remoteTokenTransferrerAddress] += scaledAmount;
return (scaledAmount, feeAmount);
}
function _deductSenderBalance(
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
uint256 amount
) private {
uint256 senderBalance =
transferredBalances[remoteBlockchainID][remoteTokenTransferrerAddress];
require(senderBalance >= amount, "TokenHome: insufficient token transfer balance");
transferredBalances[remoteBlockchainID][remoteTokenTransferrerAddress] =
senderBalance - amount;
}
function _validateSendAndCallInput(SendAndCallInput memory input) private pure {
require(input.recipientContract != address(0), "TokenHome: zero recipient contract address");
require(input.requiredGasLimit > 0, "TokenHome: zero required gas limit");
require(input.recipientGasLimit > 0, "TokenHome: zero recipient gas limit");
require(
input.recipientGasLimit < input.requiredGasLimit,
"TokenHome: invalid recipient gas limit"
);
require(input.fallbackRecipient != address(0), "TokenHome: zero fallback recipient address");
require(input.secondaryFee == 0, "TokenHome: non-zero secondary fee");
}
function _validateSendTokensInput(SendTokensInput memory input) private pure {
require(input.recipient != address(0), "TokenHome: zero recipient address");
require(input.requiredGasLimit > 0, "TokenHome: zero required gas limit");
require(input.secondaryFee == 0, "TokenHome: non-zero secondary fee");
}
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {IERC20TokenTransferrer} from "../../interfaces/IERC20TokenTransferrer.sol";
import {ITokenHome} from "./ITokenHome.sol";
/**
* @notice Interface for a ERC20 token "home" contract that locks its specified ERC20
* token on its chain to be transferred to supported remote token transfer contracts on other chains.
*
* @custom:security-contact https://github.com/ava-labs/avalanche-interchain-token-transfer/blob/main/SECURITY.md
*/
interface IERC20TokenHome is IERC20TokenTransferrer, ITokenHome {
/**
* @notice Adds collateral to the home token transfer contract for the specified TokenRemote instance. If more value is provided
* than the amount of collateral needed, the excess amount is returned to the caller.
* @param remoteBlockchainID The blockchain ID of the TokenRemote instance to add collateral for.
* @param remoteTokenTransferrerAddress The address of the TokenRemote instance to add collateral for on the {remoteBlockchainID}.
* @param amount Amount of tokens to add as collateral.
*/
function addCollateral(
bytes32 remoteBlockchainID,
address remoteTokenTransferrerAddress,
uint256 amount
) external;
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {
ITokenTransferrer,
SendTokensInput,
SendAndCallInput
} from "../../interfaces/ITokenTransferrer.sol";
/**
* @dev Interface for a "home" token transferrer contract that locks a specific token
* on its chain to be transferred to supported "remote" token transferrer contracts on other chains.
*/
interface ITokenHome is ITokenTransferrer {
/**
* @dev Emitted when tokens are added as collateral for a given TokenRemote instance.
* The event emits a {remaining} value of 0 when the TokenRemote instance is fully collateralized.
*/
event CollateralAdded(
bytes32 indexed remoteBlockchainID,
address indexed remoteTokenTransferrerAddress,
uint256 amount,
uint256 remaining
);
/**
* @notice Emitted when a new TokenRemote instance is registered with the token transferrer.
*/
event RemoteRegistered(
bytes32 indexed remoteBlockchainID,
address indexed remoteTokenTransferrerAddress,
uint256 initialCollateralNeeded,
uint8 tokenDecimals
);
/**
* @notice Emitted when tokens are routed from a multi-hop send message to another chain.
*/
event TokensRouted(bytes32 indexed teleporterMessageID, SendTokensInput input, uint256 amount);
/**
* @notice Emitted when tokens are routed from a mulit-hop send message,
* with calldata for a contract recipient, to another chain.
*/
event TokensAndCallRouted(
bytes32 indexed teleporterMessageID, SendAndCallInput input, uint256 amount
);
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
/**
* @notice Interface for contracts that are called to receive token transfers.
*/
interface IERC20SendAndCallReceiver {
/**
* @notice Called to receive the amount of the given token
* @param sourceBlockchainID Blockchain ID that the transfer originated from
* @param originTokenTransferrerAddress Address of the token transferrer that initiated the Teleporter message
* @param originSenderAddress Address of the sender that sent the transfer. This value
* should only be trusted if {originTokenTransferrerAddress} is verified and known.
* @param token Address of the token to be received
* @param amount Amount of the token to be received
* @param payload Arbitrary data provided by the caller
*/
function receiveTokens(
bytes32 sourceBlockchainID,
address originTokenTransferrerAddress,
address originSenderAddress,
address token,
uint256 amount,
bytes calldata payload
) external;
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {ITokenTransferrer, SendTokensInput, SendAndCallInput} from "./ITokenTransferrer.sol";
/**
* @notice Interface for an Avalanche interchain token transferrer that sends ERC20 tokens to another chain.
*
* @custom:security-contact https://github.com/ava-labs/avalanche-interchain-token-transfer/blob/main/SECURITY.md
*/
interface IERC20TokenTransferrer is ITokenTransferrer {
/**
* @notice Sends ERC20 tokens to the specified destination.
* @param input Specifies information for delivery of the tokens
* @param amount Amount of tokens to send
*/
function send(SendTokensInput calldata input, uint256 amount) external;
/**
* @notice Sends ERC20 tokens to the specified destination to be used in a smart contract interaction.
* @param input Specifies information for delivery of the tokens
* @param amount Amount of tokens to send
*/
function sendAndCall(SendAndCallInput calldata input, uint256 amount) external;
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {ITeleporterReceiver} from "@teleporter/ITeleporterReceiver.sol";
/**
* @notice Input parameters for transferring tokens to another chain as part of a simple transfer.
* @param destinationBlockchainID Blockchain ID of the destination
* @param destinationTokenTransferrerAddress Address of the destination token transferrer instance
* @param recipient Address of the recipient on the destination chain
* @param primaryFeeTokenAddress Address of the ERC20 contract to optionally pay a Teleporter message fee
* @param primaryFee Amount of tokens to pay as the optional Teleporter message fee
* @param secondaryFee Amount of tokens to pay for Teleporter fee if a multi-hop is needed
* @param requiredGasLimit Gas limit requirement for sending to a token transferrer.
* This is required because the gas requirement varies based on the token transferrer instance
* specified by {destinationBlockchainID} and {destinationTokenTransferrerAddress}.
* @param multiHopFallback In the case of a multi-hop transfer, the address where the tokens
* are sent on the home chain if the transfer is unable to be routed to its final destination.
* Note that this address must be able to receive the tokens held as collateral in the home contract.
*/
struct SendTokensInput {
bytes32 destinationBlockchainID;
address destinationTokenTransferrerAddress;
address recipient;
address primaryFeeTokenAddress;
uint256 primaryFee;
uint256 secondaryFee;
uint256 requiredGasLimit;
address multiHopFallback;
}
/**
* @notice Input parameters for transferring tokens to another chain as part of a transfer with a contract call.
* @param destinationBlockchainID BlockchainID of the destination
* @param destinationTokenTransferrerAddress Address of the destination token transferrer instance
* @param recipientContract The contract on the destination chain that will be called
* @param recipientPayload The payload that will be provided to the recipient contract on the destination chain
* @param requiredGasLimit The required amount of gas needed to deliver the message on its destination chain,
* including token operations and the call to the recipient contract.
* @param recipientGasLimit The amount of gas that will provided to the recipient contract on the destination chain,
* which must be less than the requiredGasLimit of the message as a whole.
* @param multiHopFallback In the case of a multi-hop transfer, the address where the tokens
* are sent on the home chain if the transfer is unable to be routed to its final destination.
* Note that this address must be able to receive the tokens held as collateral in the home contract.
* @param fallbackRecipient Address on the {destinationBlockchainID} where the transferred tokens are sent to if the call
* to the recipient contract fails. Note that this address must be able to receive the tokens on the destination
* chain of the transfer.
* @param primaryFeeTokenAddress Address of the ERC20 contract to optionally pay a Teleporter message fee
* @param primaryFee Amount of tokens to pay for Teleporter fee on the chain that iniiated the transfer
* @param secondaryFee Amount of tokens to pay for Teleporter fee if a multi-hop is needed
*/
struct SendAndCallInput {
bytes32 destinationBlockchainID;
address destinationTokenTransferrerAddress;
address recipientContract;
bytes recipientPayload;
uint256 requiredGasLimit;
uint256 recipientGasLimit;
address multiHopFallback;
address fallbackRecipient;
address primaryFeeTokenAddress;
uint256 primaryFee;
uint256 secondaryFee;
}
enum TransferrerMessageType {
REGISTER_REMOTE,
SINGLE_HOP_SEND,
SINGLE_HOP_CALL,
MULTI_HOP_SEND,
MULTI_HOP_CALL
}
/**
* @dev The TransferrerMessage struct is used to wrap messages between two token transferrer contracts
* with their message type so that the receiving token transferrer can decode the payload.
*/
struct TransferrerMessage {
TransferrerMessageType messageType;
bytes payload;
}
/**
* @dev Register remote message payloads are sent to the home token transferrer contract to register a new remote contract
* instance on another chain.
* @param initialReserveImbalance The initial reserve imbalance of the remote contract to calculate
* associated collateral needed on home contract.
* @param homeTokenDecimals The number of decimals that the home token has.
* @param remoteTokenDecimals The number of decimals that the remote token has.
*/
struct RegisterRemoteMessage {
uint256 initialReserveImbalance;
uint8 homeTokenDecimals;
uint8 remoteTokenDecimals;
}
/**
* @dev Single hop send message payloads include the recipient address and transferred amount.
* The destination chain and token transferrer address for the transfer are defined by the Teleporter message.
*/
struct SingleHopSendMessage {
address recipient;
uint256 amount;
}
/**
* @dev Single hop call message payloads include the required information to call
* the target contract on the destination chain. The destination chain and token transferrer
* address are defined by the Teleporter message. The message also includes the
* blockchain ID and address of the original sender.
*/
struct SingleHopCallMessage {
bytes32 sourceBlockchainID;
address originTokenTransferrerAddress;
address originSenderAddress;
address recipientContract;
uint256 amount;
bytes recipientPayload;
uint256 recipientGasLimit;
address fallbackRecipient;
}
/**
* @dev Multi hop send message payloads include the recipient address as well as all
* the information the intermediate (home) chain token transferrer contract needs to route
* the send message on to its final destination.
*/
struct MultiHopSendMessage {
bytes32 destinationBlockchainID;
address destinationTokenTransferrerAddress;
address recipient;
uint256 amount;
uint256 secondaryFee;
uint256 secondaryGasLimit;
address multiHopFallback;
}
/**
* @dev Multi hop call message payloads include the required information to call the target contract on the
* destination chain, as well as the information the intermediate (home) chain token transferrer contract needs to route
* the call message on to its final destination. This includes the {secondaryRequiredGasLimit}, which is the
* required gas limit set for the second Teleporter message. The {secondaryRequiredGasLimit} should be sufficient
* to cover the destination token operations as well as the call to the recipient contract, and will always be
* greater than the recipientGasLimit. The multi-hop message also includes the address of the original sender.
* The source blockchain ID of the sender is known from the Teleporter message.
*/
struct MultiHopCallMessage {
address originSenderAddress;
bytes32 destinationBlockchainID;
address destinationTokenTransferrerAddress;
address recipientContract;
uint256 amount;
bytes recipientPayload;
uint256 recipientGasLimit;
address fallbackRecipient;
uint256 secondaryRequiredGasLimit;
address multiHopFallback;
uint256 secondaryFee;
}
/**
* @notice Interface for an Avalanche interchain token transferrer that sends tokens to another chain.
*
* @custom:security-contact https://github.com/ava-labs/avalanche-interchain-token-transfer/blob/main/SECURITY.md
*/
interface ITokenTransferrer is ITeleporterReceiver {
/**
* @notice Emitted when tokens are sent to another chain.
*/
event TokensSent(
bytes32 indexed teleporterMessageID,
address indexed sender,
SendTokensInput input,
uint256 amount
);
/**
* @notice Emitted when tokens are sent to another chain with calldata for a contract recipient.
*/
event TokensAndCallSent(
bytes32 indexed teleporterMessageID,
address indexed sender,
SendAndCallInput input,
uint256 amount
);
/**
* @notice Emitted when tokens are withdrawn from the token transferrer contract.
*/
event TokensWithdrawn(address indexed recipient, uint256 amount);
/**
* @notice Emitted when a call to a recipient contract to receive token succeeds.
*/
event CallSucceeded(address indexed recipientContract, uint256 amount);
/**
* @notice Emitted when a call to a recipient contract to receive token fails, and the tokens are sent
* to a fallback recipient.
*/
event CallFailed(address indexed recipientContract, uint256 amount);
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
library CallUtils {
/**
* @dev calls target address with exactly gasAmount gas and data as calldata
* or reverts if at least gasAmount gas is not available.
*/
function _callWithExactGas(
uint256 gasAmount,
address target,
bytes memory data
) internal returns (bool) {
return _callWithExactGasAndValue(gasAmount, 0, target, data);
}
/**
* @dev calls target address with exactly gasAmount gas and data as calldata
* or reverts if at least gasAmount gas is not available.
*/
function _callWithExactGasAndValue(
uint256 gasAmount,
uint256 value,
address target,
bytes memory data
) internal returns (bool) {
require(gasleft() >= gasAmount, "CallUtils: insufficient gas");
require(address(this).balance >= value, "CallUtils: insufficient value");
// If there is no code at the target, automatically consider the call to have failed since it
// doesn't have any effect on state.
if (target.code.length == 0) {
return false;
}
// Call the target address of the message with the provided data and amount of gas.
//
// Assembly is used for the low-level call to avoid unnecessary expansion of the return data in memory.
// This prevents possible "return bomb" vectors where the external contract could force the caller
// to use an arbitrary amount of gas. See Solidity issue here: https://github.com/ethereum/solidity/issues/12306
bool success;
// solhint-disable-next-line no-inline-assembly
assembly {
success :=
call(
gasAmount, // gas provided to the call
target, // call target
value, // value transferred
add(data, 0x20), // input data - 0x20 needs to be added to an array because the first 32-byte slot contains the array length (0x20 in hex is 32 in decimal).
mload(data), // input data size - mload returns mem[p..(p+32)], which is the first 32-byte slot of the array. In this case, the array length.
0, // output
0 // output size
)
}
return success;
}
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
import {IERC20} from "@openzeppelin/[email protected]/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/[email protected]/token/ERC20/utils/SafeERC20.sol";
/**
* @dev Provides a wrapper used for calling an ERC20 transferFrom method to receive tokens to a contract
* from a specified sender. Differs from the "SafeERC20TransferFrom" implementation found here
* https://github.com/ava-labs/teleporter/blob/main/contracts/src/Teleporter/SafeERC20TransferFrom.sol in that
* it supports passing arbitrary sender address values, allowing its use in ERC-2771 compliant meta-transactions.
*
* Checks the balance of the contract using the library before and after the call to safeTransferFrom, and
* returns balance increase. Designed for safely handling ERC20 "fee on transfer" and "burn on transfer"
* implementations.
*
* Note: Contracts that use this library must ensure that users cannot pass arbitrary addresses as
* the {from} address for the {transferFrom} call. Proper authorization (such as msg.sender) must
* be required to ensure no one can improperly transfer tokens from any address.
*
* Note: A reentrancy guard must always be used when calling token.safeTransferFrom in order to
* prevent against possible "before-after" pattern vulnerabilities.
*
* @custom:security-contact https://github.com/ava-labs/avalanche-interchain-token-transfer/blob/main/SECURITY.md
*/
library SafeERC20TransferFrom {
using SafeERC20 for IERC20;
/**
* @dev Checks the balance of the contract before and after the call to safeTransferFrom, and returns the balance
* increase. Designed for safely handling ERC20 "fee on transfer" and "burn on transfer" implementations.
*/
// solhint-disable private-vars-leading-underscore
function safeTransferFrom(
IERC20 erc20,
address from,
uint256 amount
) internal returns (uint256) {
uint256 balanceBefore = erc20.balanceOf(address(this));
erc20.safeTransferFrom(from, address(this), amount);
uint256 balanceAfter = erc20.balanceOf(address(this));
require(balanceAfter > balanceBefore, "SafeERC20TransferFrom: balance not increased");
return balanceAfter - balanceBefore;
}
// solhint-enable private-vars-leading-underscore
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
/**
* @dev Abstract contract that helps implement reentrancy guards for Avalanche interchain token transfer {_send} and {_sendAndCall}
* functions.
*
* The send methods must not allow reentry given that can make calls to external contracts such as {safeTransferFrom}
* and {safeDeposit}. However, the send methods should be allowed to be called from {receiveTeleporterMessage}, either
* as a part of processing a multi-hop transfer, or as a part of an external call made to process a "sendAndCall"
* message.
*
* @custom:security-contact https://github.com/ava-labs/avalanche-interchain-token-transfer/blob/main/SECURITY.md
*/
abstract contract SendReentrancyGuard {
uint256 internal constant _NOT_ENTERED = 1;
uint256 internal constant _ENTERED = 2;
uint256 private _sendEntered;
// sendNonReentrant modifier makes sure there is not reentry between {_send} or {_sendAndCall} calls.
modifier sendNonReentrant() {
require(_sendEntered == _NOT_ENTERED, "SendReentrancyGuard: send reentrancy");
_sendEntered = _ENTERED;
_;
_sendEntered = _NOT_ENTERED;
}
constructor() {
_sendEntered = _NOT_ENTERED;
}
}// (c) 2024, Ava Labs, Inc. All rights reserved.
// See the file LICENSE for licensing terms.
// SPDX-License-Identifier: Ecosystem
pragma solidity 0.8.18;
library TokenScalingUtils {
uint256 public constant MAX_TOKEN_DECIMALS = 18;
/**
* @notice Scales the {amount} of home tokens to a TokenRemote instance's token scale.
* @param tokenMultiplier The token multiplier of the TokenRemote instance.
* @param multiplyOnRemote Whether the amount of home tokens will be multiplied on the remote, or divided.
* @param homeTokenAmount The amount of home tokens to scale.
*/
function applyTokenScale(
uint256 tokenMultiplier,
bool multiplyOnRemote,
uint256 homeTokenAmount
) internal pure returns (uint256) {
return _scaleTokens(tokenMultiplier, multiplyOnRemote, homeTokenAmount, true);
}
/**
* @notice Removes the TokenRemote instance's token scaling, and returns the corresponding
* amount of home tokens.
* @param tokenMultiplier The token multiplier of the TokenRemote instance.
* @param multiplyOnRemote Whether the amount of home tokens will be multiplied on the remote, or divided.
* @param remoteTokenAmount The amount of remote tokens to remove scaling from.
*/
function removeTokenScale(
uint256 tokenMultiplier,
bool multiplyOnRemote,
uint256 remoteTokenAmount
) internal pure returns (uint256) {
return _scaleTokens(tokenMultiplier, multiplyOnRemote, remoteTokenAmount, false);
}
/**
* @notice Takes both the home and remote token denominations and uses
* them to derive the token transferrer scaling multiplier values
* @param homeTokenDecimals The number of decimals of the home token.
* @param remoteTokenDecimals The number of decimals of the remote token.
*/
function deriveTokenMultiplierValues(
uint8 homeTokenDecimals,
uint8 remoteTokenDecimals
) internal pure returns (uint256, bool) {
bool multiplyOnRemote = remoteTokenDecimals > homeTokenDecimals;
uint256 tokenMultiplier = 10
** (
multiplyOnRemote
? uint256(remoteTokenDecimals - homeTokenDecimals)
: uint256(homeTokenDecimals - remoteTokenDecimals)
);
return (tokenMultiplier, multiplyOnRemote);
}
/**
* @dev Scales {value} based on {tokenMultiplier} and if the amount is applying or
* removing the TokenRemote instance's token scale.
* Should be used for all tokens and fees being transferred to/from other subnets.
* @param tokenMultiplier The token multiplier of the TokenRemote instance.
* @param multiplyOnRemote Whether the amount of home tokens will be multiplied on the remote, or divided.
* @param amount The amount of tokens to scale.
* @param isSendToRemote If true, indicates the amount is being sent to the
* TokenRemote instance, so applies token scale. If false, indicates the amount is being
* sent back to the TokenHome instance, so removes token scale.
*/
function _scaleTokens(
uint256 tokenMultiplier,
bool multiplyOnRemote,
uint256 amount,
bool isSendToRemote
) private pure returns (uint256) {
// Multiply when multiplyOnRemote and isSendToRemote are
// both true or both false.
if (multiplyOnRemote == isSendToRemote) {
return amount * tokenMultiplier;
}
// Otherwise divide.
return amount / tokenMultiplier;
}
}{
"evmVersion": "paris",
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@avalabs/[email protected]/=lib/teleporter/contracts/lib/subnet-evm/contracts/",
":@openzeppelin/[email protected]/=lib/teleporter/contracts/lib/openzeppelin-contracts/contracts/",
":@teleporter-mocks/=lib/teleporter/contracts/src/Mocks/",
":@teleporter/=lib/teleporter/contracts/src/Teleporter/",
":ds-test/=lib/forge-std/lib/ds-test/src/",
":forge-std/=lib/forge-std/src/",
":openzeppelin-contracts/=lib/teleporter/contracts/lib/openzeppelin-contracts/",
":subnet-evm/=lib/teleporter/contracts/lib/subnet-evm/",
":teleporter/=lib/teleporter/"
],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"teleporterRegistryAddress","type":"address"},{"internalType":"address","name":"teleporterManager","type":"address"},{"internalType":"address","name":"tokenAddress_","type":"address"},{"internalType":"uint8","name":"tokenDecimals_","type":"uint8"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipientContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CallFailed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipientContract","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CallSucceeded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"remoteBlockchainID","type":"bytes32"},{"indexed":true,"internalType":"address","name":"remoteTokenTransferrerAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remaining","type":"uint256"}],"name":"CollateralAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"oldMinTeleporterVersion","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"newMinTeleporterVersion","type":"uint256"}],"name":"MinTeleporterVersionUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"remoteBlockchainID","type":"bytes32"},{"indexed":true,"internalType":"address","name":"remoteTokenTransferrerAddress","type":"address"},{"indexed":false,"internalType":"uint256","name":"initialCollateralNeeded","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"tokenDecimals","type":"uint8"}],"name":"RemoteRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"teleporterAddress","type":"address"}],"name":"TeleporterAddressPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"teleporterAddress","type":"address"}],"name":"TeleporterAddressUnpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"teleporterMessageID","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"destinationBlockchainID","type":"bytes32"},{"internalType":"address","name":"destinationTokenTransferrerAddress","type":"address"},{"internalType":"address","name":"recipientContract","type":"address"},{"internalType":"bytes","name":"recipientPayload","type":"bytes"},{"internalType":"uint256","name":"requiredGasLimit","type":"uint256"},{"internalType":"uint256","name":"recipientGasLimit","type":"uint256"},{"internalType":"address","name":"multiHopFallback","type":"address"},{"internalType":"address","name":"fallbackRecipient","type":"address"},{"internalType":"address","name":"primaryFeeTokenAddress","type":"address"},{"internalType":"uint256","name":"primaryFee","type":"uint256"},{"internalType":"uint256","name":"secondaryFee","type":"uint256"}],"indexed":false,"internalType":"struct SendAndCallInput","name":"input","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensAndCallRouted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"teleporterMessageID","type":"bytes32"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"components":[{"internalType":"bytes32","name":"destinationBlockchainID","type":"bytes32"},{"internalType":"address","name":"destinationTokenTransferrerAddress","type":"address"},{"internalType":"address","name":"recipientContract","type":"address"},{"internalType":"bytes","name":"recipientPayload","type":"bytes"},{"internalType":"uint256","name":"requiredGasLimit","type":"uint256"},{"internalType":"uint256","name":"recipientGasLimit","type":"uint256"},{"internalType":"address","name":"multiHopFallback","type":"address"},{"internalType":"address","name":"fallbackRecipient","type":"address"},{"internalType":"address","name":"primaryFeeTokenAddress","type":"address"},{"internalType":"uint256","name":"primaryFee","type":"uint256"},{"internalType":"uint256","name":"secondaryFee","type":"uint256"}],"indexed":false,"internalType":"struct SendAndCallInput","name":"input","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensAndCallSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"teleporterMessageID","type":"bytes32"},{"components":[{"internalType":"bytes32","name":"destinationBlockchainID","type":"bytes32"},{"internalType":"address","name":"destinationTokenTransferrerAddress","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"primaryFeeTokenAddress","type":"address"},{"internalType":"uint256","name":"primaryFee","type":"uint256"},{"internalType":"uint256","name":"secondaryFee","type":"uint256"},{"internalType":"uint256","name":"requiredGasLimit","type":"uint256"},{"internalType":"address","name":"multiHopFallback","type":"address"}],"indexed":false,"internalType":"struct SendTokensInput","name":"input","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensRouted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"teleporterMessageID","type":"bytes32"},{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"components":[{"internalType":"bytes32","name":"destinationBlockchainID","type":"bytes32"},{"internalType":"address","name":"destinationTokenTransferrerAddress","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"primaryFeeTokenAddress","type":"address"},{"internalType":"uint256","name":"primaryFee","type":"uint256"},{"internalType":"uint256","name":"secondaryFee","type":"uint256"},{"internalType":"uint256","name":"requiredGasLimit","type":"uint256"},{"internalType":"address","name":"multiHopFallback","type":"address"}],"indexed":false,"internalType":"struct SendTokensInput","name":"input","type":"tuple"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensSent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensWithdrawn","type":"event"},{"inputs":[{"internalType":"bytes32","name":"remoteBlockchainID","type":"bytes32"},{"internalType":"address","name":"remoteTokenTransferrerAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"addCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"blockchainID","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMinTeleporterVersion","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"teleporterAddress","type":"address"}],"name":"isTeleporterAddressPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"teleporterAddress","type":"address"}],"name":"pauseTeleporterAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"sourceBlockchainID","type":"bytes32"},{"internalType":"address","name":"originSenderAddress","type":"address"},{"internalType":"bytes","name":"message","type":"bytes"}],"name":"receiveTeleporterMessage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"remoteBlockchainID","type":"bytes32"},{"internalType":"address","name":"remoteTokenTransferrerAddress","type":"address"}],"name":"registeredRemotes","outputs":[{"internalType":"bool","name":"registered","type":"bool"},{"internalType":"uint256","name":"collateralNeeded","type":"uint256"},{"internalType":"uint256","name":"tokenMultiplier","type":"uint256"},{"internalType":"bool","name":"multiplyOnRemote","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"destinationBlockchainID","type":"bytes32"},{"internalType":"address","name":"destinationTokenTransferrerAddress","type":"address"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"primaryFeeTokenAddress","type":"address"},{"internalType":"uint256","name":"primaryFee","type":"uint256"},{"internalType":"uint256","name":"secondaryFee","type":"uint256"},{"internalType":"uint256","name":"requiredGasLimit","type":"uint256"},{"internalType":"address","name":"multiHopFallback","type":"address"}],"internalType":"struct SendTokensInput","name":"input","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"send","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"destinationBlockchainID","type":"bytes32"},{"internalType":"address","name":"destinationTokenTransferrerAddress","type":"address"},{"internalType":"address","name":"recipientContract","type":"address"},{"internalType":"bytes","name":"recipientPayload","type":"bytes"},{"internalType":"uint256","name":"requiredGasLimit","type":"uint256"},{"internalType":"uint256","name":"recipientGasLimit","type":"uint256"},{"internalType":"address","name":"multiHopFallback","type":"address"},{"internalType":"address","name":"fallbackRecipient","type":"address"},{"internalType":"address","name":"primaryFeeTokenAddress","type":"address"},{"internalType":"uint256","name":"primaryFee","type":"uint256"},{"internalType":"uint256","name":"secondaryFee","type":"uint256"}],"internalType":"struct SendAndCallInput","name":"input","type":"tuple"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"sendAndCall","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"teleporterRegistry","outputs":[{"internalType":"contract TeleporterRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenDecimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"remoteBlockchainID","type":"bytes32"},{"internalType":"address","name":"remoteTokenTransferrerAddress","type":"address"}],"name":"transferredBalances","outputs":[{"internalType":"uint256","name":"balance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"teleporterAddress","type":"address"}],"name":"unpauseTeleporterAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"version","type":"uint256"}],"name":"updateMinTeleporterVersion","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101206040523480156200001257600080fd5b506040516200465938038062004659833981016040819052620000359162000402565b6001600055838383838383816001600160a01b038116620000c35760405162461bcd60e51b815260206004820152603760248201527f54656c65706f727465725570677261646561626c653a207a65726f2074656c6560448201527f706f72746572207265676973747279206164647265737300000000000000000060648201526084015b60405180910390fd5b6001600160a01b03811660808190526040805163301fd1f560e21b8152905163c07f47d4916004808201926020929091908290030181865afa1580156200010e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000134919062000466565b600255506200014333620002b6565b6200014e8162000308565b505060016004819055507302000000000000000000000000000000000000056001600160a01b0316634213cf786040518163ffffffff1660e01b8152600401602060405180830381865afa158015620001ab573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620001d1919062000466565b60a0526001600160a01b0382166200022c5760405162461bcd60e51b815260206004820152601d60248201527f546f6b656e486f6d653a207a65726f20746f6b656e20616464726573730000006044820152606401620000ba565b60128160ff1611156200028d5760405162461bcd60e51b815260206004820152602260248201527f546f6b656e486f6d653a20746f6b656e20646563696d616c7320746f6f2068696044820152610ced60f31b6064820152608401620000ba565b6001600160a01b0390911660c081905260ff90911660e052610100525062000480945050505050565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6200031262000387565b6001600160a01b038116620003795760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401620000ba565b6200038481620002b6565b50565b6003546001600160a01b03163314620003e35760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401620000ba565b565b80516001600160a01b0381168114620003fd57600080fd5b919050565b600080600080608085870312156200041957600080fd5b6200042485620003e5565b93506200043460208601620003e5565b92506200044460408601620003e5565b9150606085015160ff811681146200045b57600080fd5b939692955090935050565b6000602082840312156200047957600080fd5b5051919050565b60805160a05160c05160e05161010051614124620005356000396000818161037c01528181611b2b01528181611b5701528181611b9401528181611c4301528181611cb701528181611d9501526125b5015260008181610184015281816123e701526124750152600081816102970152818161117301526112b70152600081816102d1015281816105e8015261220801526000818161012b0152818161064101528181610a93015261276601526141246000f3fe608060405234801561001057600080fd5b50600436106101215760003560e01c80638da5cb5b116100ad578063d2cc7a7011610071578063d2cc7a70146102f3578063d536ec33146102fb578063f2fde38b14610364578063fc0c546a14610377578063fd6582681461039e57600080fd5b80638da5cb5b1461024557806397314297146102565780639d76ea5814610292578063c868efaa146102b9578063d127dc9b146102cc57600080fd5b80634511243e116100f45780634511243e146101f15780635d16225d146102045780635eb9951414610217578063656900381461022a578063715018a61461023d57600080fd5b80631a7f5bec146101265780632b0d8f181461016a5780633b97e8561461017f5780633ca563e3146101b8575b600080fd5b61014d7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61017d61017836600461315b565b6103b1565b005b6101a67f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610161565b6101e36101c6366004613178565b600660209081526000928352604080842090915290825290205481565b604051908152602001610161565b61017d6101ff36600461315b565b6104b6565b61017d6102123660046131a8565b6105b3565b61017d6102253660046131da565b6105cf565b61017d6102383660046131f3565b6105e3565b61017d610618565b6003546001600160a01b031661014d565b61028261026436600461315b565b6001600160a01b031660009081526001602052604090205460ff1690565b6040519015158152602001610161565b61014d7f000000000000000000000000000000000000000000000000000000000000000081565b61017d6102c736600461323d565b61062c565b6101e37f000000000000000000000000000000000000000000000000000000000000000081565b6002546101e3565b610340610309366004613178565b6005602090815260009283526040808420909152908252902080546001820154600283015460039093015460ff9283169391921684565b60408051941515855260208501939093529183015215156060820152608001610161565b61017d61037236600461315b565b6107f6565b61014d7f000000000000000000000000000000000000000000000000000000000000000081565b61017d6103ac3660046132c5565b61086c565b6103b961087c565b6001600160a01b0381166103e85760405162461bcd60e51b81526004016103df906132fd565b60405180910390fd5b6001600160a01b03811660009081526001602052604090205460ff16156104675760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a2061646472657373206160448201526c1b1c9958591e481c185d5cd959609a1b60648201526084016103df565b6001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f933f93e57a222e6330362af8b376d0a8725b6901e9a2fb86d00f169702b28a4c9190a250565b6104be61087c565b6001600160a01b0381166104e45760405162461bcd60e51b81526004016103df906132fd565b6001600160a01b03811660009081526001602052604090205460ff1661055e5760405162461bcd60e51b815260206004820152602960248201527f54656c65706f727465725570677261646561626c653a2061646472657373206e6044820152681bdd081c185d5cd95960ba1b60648201526084016103df565b6040516001600160a01b038216907f844e2f3154214672229235858fd029d1dfd543901c6d05931f0bc2480a2d72c390600090a26001600160a01b03166000908152600160205260409020805460ff19169055565b6105cb6105c5368490038401846133ff565b82610884565b5050565b6105d761087c565b6105e081610a8f565b50565b6105cb7f0000000000000000000000000000000000000000000000000000000000000000303361061286613508565b85610c2f565b610620610e35565b61062a6000610e8f565b565b610634610ee1565b6002546001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016634c1f08ce336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156106ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106cf91906135d9565b10156107365760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201526f32b632b837b93a32b91039b2b73232b960811b60648201526084016103df565b61073f33610264565b156107a55760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881859191c995cdcc81c185d5cd95960821b60648201526084016103df565b6107e6848484848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610f3a92505050565b6107f06001600055565b50505050565b6107fe610e35565b6001600160a01b0381166108635760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103df565b6105e081610e8f565b61087783838361133d565b505050565b61062a610e35565b6001600454146108a65760405162461bcd60e51b81526004016103df906135f2565b60026004556108b48261150a565b60e08201516001600160a01b0316156108df5760405162461bcd60e51b81526004016103df90613636565b6000806108ff8460000151856020015185876060015188608001516115b4565b9150915060006040518060400160405280600160048111156109235761092361367c565b8152602001604051806040016040528088604001516001600160a01b031681526020018681525060405160200161095a9190613692565b60405160208183030381529060405281525090506000610a3c6040518060c001604052808860000151815260200188602001516001600160a01b0316815260200160405180604001604052808a606001516001600160a01b031681526020018781525081526020018860c00151815260200160006001600160401b038111156109e5576109e561334b565b604051908082528060200260200182016040528015610a0e578160200160208202803683370190505b50815260200184604051602001610a259190613702565b60405160208183030381529060405281525061175e565b9050336001600160a01b0316817f93f19bf1ec58a15dc643b37e7e18a1c13e85e06cd11929e283154691ace9fb528887604051610a7a929190613747565b60405180910390a35050600160045550505050565b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610aef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1391906135d9565b60025490915081831115610b835760405162461bcd60e51b815260206004820152603160248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201527032b632b837b93a32b9103b32b939b4b7b760791b60648201526084016103df565b808311610bf85760405162461bcd60e51b815260206004820152603f60248201527f54656c65706f727465725570677261646561626c653a206e6f7420677265617460448201527f6572207468616e2063757272656e74206d696e696d756d2076657273696f6e0060648201526084016103df565b6002839055604051839082907fa9a7ef57e41f05b4c15480842f5f0c27edfcbb553fed281f7c4068452cc1c02d90600090a3505050565b600160045414610c515760405162461bcd60e51b81526004016103df906135f2565b6002600455610c5f82611884565b60c08201516001600160a01b031615610c8a5760405162461bcd60e51b81526004016103df90613636565b600080610cac84600001518560200151858761010001518861012001516115b4565b915091506000604051806040016040528060026004811115610cd057610cd061367c565b81526020016040518061010001604052808b81526020018a6001600160a01b03168152602001896001600160a01b0316815260200188604001516001600160a01b03168152602001868152602001886060015181526020018860a0015181526020018860e001516001600160a01b0316815250604051602001610d5391906137c9565b60405160208183030381529060405281525090506000610ddf6040518060c001604052808860000151815260200188602001516001600160a01b0316815260200160405180604001604052808a61010001516001600160a01b031681526020018781525081526020018860800151815260200160006001600160401b038111156109e5576109e561334b565b9050866001600160a01b0316817f5d76dff81bf773b908b050fa113d39f7d8135bb4175398f313ea19cd3a1a0b168887604051610e1d929190613868565b60405180910390a35050600160045550505050505050565b6003546001600160a01b0316331461062a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016103df565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600260005403610f335760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016103df565b6002600055565b600081806020019051810190610f509190613990565b9050600181516004811115610f6757610f6761367c565b03610fb15760008160200151806020019051810190610f869190613a1e565b90506000610f9986868460200151611a6b565b9050610fa9826000015182611adb565b505050505050565b600281516004811115610fc657610fc661367c565b036110e15760008160200151806020019051810190610fe59190613a58565b90506000610ff886868460800151611a6b565b8251909150861461105e5760405162461bcd60e51b815260206004820152602a60248201527f546f6b656e486f6d653a206d69736d61746368656420736f7572636520626c6f60448201526918dad8da185a5b88125160b21b60648201526084016103df565b846001600160a01b031682602001516001600160a01b0316146110d75760405162461bcd60e51b815260206004820152602b60248201527f546f6b656e486f6d653a206d69736d617463686564206f726967696e2073656e60448201526a646572206164647265737360a81b60648201526084016103df565b610fa98282611b52565b6003815160048111156110f6576110f661367c565b036111d757600081602001518060200190518101906111159190613b27565b905060008061112e878785606001518660800151611dbc565b915091506111ce6040518061010001604052808560000151815260200185602001516001600160a01b0316815260200185604001516001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602001838152602001600081526020018560a0015181526020018560c001516001600160a01b031681525083611e4d565b50505050505050565b6004815160048111156111ec576111ec61367c565b036112f5576000816020015180602001905181019061120b9190613bc2565b905060008061122587878560800151866101400151611dbc565b915091506111ce878785600001516040518061016001604052808860200151815260200188604001516001600160a01b0316815260200188606001516001600160a01b031681526020018860a00151815260200188610100015181526020018860c0015181526020018861012001516001600160a01b031681526020018860e001516001600160a01b031681526020017f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602001868152602001600081525086611fd4565b60008151600481111561130a5761130a61367c565b036107f057600081602001518060200190518101906113299190613cd0565b90506113368585836121ad565b5050505050565b60016004541461135f5760405162461bcd60e51b81526004016103df906135f2565b6002600481905560008481526005602090815260408083206001600160a01b03871684528252918290208251608081018452815460ff908116151580835260018401549483019490945294820154938101939093526003015490921615156060820152906113df5760405162461bcd60e51b81526004016103df90613d34565b600081602001511161143d5760405162461bcd60e51b815260206004820152602160248201527f546f6b656e486f6d653a207a65726f20636f6c6c61746572616c206e656564656044820152601960fa1b60648201526084016103df565b611446826125ae565b915060008082602001518410611477576020830151600092506114699085613d7f565b90508260200151935061148a565b8383602001516114879190613d7f565b91505b60008681526005602090815260408083206001600160a01b03891680855290835292819020600101859055805187815291820185905288917f6769a5f9bfc8b6e0db839ab981cbf9239274ae72d2d035081a9157d43bd33cb6910160405180910390a380156114fd576114fd3382611adb565b5050600160045550505050565b60408101516001600160a01b031661156e5760405162461bcd60e51b815260206004820152602160248201527f546f6b656e486f6d653a207a65726f20726563697069656e74206164647265736044820152607360f81b60648201526084016103df565b60008160c00151116115925760405162461bcd60e51b81526004016103df90613d92565b60a0810151156105e05760405162461bcd60e51b81526004016103df90613dd4565b60008581526005602090815260408083206001600160a01b038816845282528083208151608081018352815460ff90811615158083526001840154958301959095526002830154938201939093526003909101549091161515606082015282916116305760405162461bcd60e51b81526004016103df90613d34565b6020810151156116925760405162461bcd60e51b815260206004820152602760248201527f546f6b656e486f6d653a20636f6c6c61746572616c206e656564656420666f726044820152662072656d6f746560c81b60648201526084016103df565b61169b866125ae565b955083156116b1576116ae8533866125e1565b93505b60006116c6826040015183606001518961274a565b9050600081116117185760405162461bcd60e51b815260206004820152601d60248201527f546f6b656e486f6d653a207a65726f207363616c656420616d6f756e7400000060448201526064016103df565b60008981526006602090815260408083206001600160a01b038c1684529091528120805483929061174a908490613e15565b909155509099949850939650505050505050565b600080611769612761565b6040840151602001519091501561180e576040830151516001600160a01b03166117eb5760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a207a65726f206665652060448201526c746f6b656e206164647265737360981b60648201526084016103df565b60408301516020810151905161180e916001600160a01b03909116908390612875565b604051630624488560e41b81526001600160a01b0382169063624488509061183a908690600401613e28565b6020604051808303816000875af1158015611859573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187d91906135d9565b9392505050565b60408101516001600160a01b03166118f15760405162461bcd60e51b815260206004820152602a60248201527f546f6b656e486f6d653a207a65726f20726563697069656e7420636f6e7472616044820152696374206164647265737360b01b60648201526084016103df565b60008160800151116119155760405162461bcd60e51b81526004016103df90613d92565b60008160a00151116119755760405162461bcd60e51b815260206004820152602360248201527f546f6b656e486f6d653a207a65726f20726563697069656e7420676173206c696044820152621b5a5d60ea1b60648201526084016103df565b80608001518160a00151106119db5760405162461bcd60e51b815260206004820152602660248201527f546f6b656e486f6d653a20696e76616c696420726563697069656e7420676173604482015265081b1a5b5a5d60d21b60648201526084016103df565b60e08101516001600160a01b0316611a485760405162461bcd60e51b815260206004820152602a60248201527f546f6b656e486f6d653a207a65726f2066616c6c6261636b20726563697069656044820152696e74206164647265737360b01b60648201526084016103df565b610140810151156105e05760405162461bcd60e51b81526004016103df90613dd4565b60008381526005602090815260408083206001600160a01b038616845282528083208151608081018352815460ff908116151582526001830154948201949094526002820154928101929092526003015490911615156060820152611ad28186868661295a565b95945050505050565b816001600160a01b03167f6352c5382c4a4578e712449ca65e83cdb392d045dfcf1cad9615189db2da244b82604051611b1691815260200190565b60405180910390a26105cb6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168383612a4b565b611b817f0000000000000000000000000000000000000000000000000000000000000000836060015183612875565b60008260000151836020015184604001517f0000000000000000000000000000000000000000000000000000000000000000858760a00151604051602401611bce96959493929190613ee1565b60408051601f198184030181529190526020810180516001600160e01b03166394395edd60e01b17905260c08401516060850151919250600091611c13919084612a7b565b6060850151604051636eb1769f60e11b81523060048201526001600160a01b0391821660248201529192506000917f00000000000000000000000000000000000000000000000000000000000000009091169063dd62ed3e90604401602060405180830381865afa158015611c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb091906135d9565b9050611ce27f000000000000000000000000000000000000000000000000000000000000000086606001516000612a8a565b8115611d345784606001516001600160a01b03167f104deb555f67e63782bb817bc26c39050894645f9b9f29c4be8ae68d0e8b7ff485604051611d2791815260200190565b60405180910390a2611d7c565b84606001516001600160a01b03167fb9eaeae386d339f8115782f297a9e5f0e13fb587cd6b0d502f113cb8dd4d6cb085604051611d7391815260200190565b60405180910390a25b80156113365760e0850151611336906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169083612a4b565b60008481526005602090815260408083206001600160a01b038716845282528083208151608081018352815460ff908116151582526001830154948201949094526002820154928101929092526003015490911615156060820152819081611e268289898961295a565b90506000611e3d8360400151846060015188612b9f565b9199919850909650505050505050565b600160045414611e6f5760405162461bcd60e51b81526004016103df906135f2565b6002600455611e7d8261150a565b6000611e9783600001518460200151848660800151612bae565b905080600003611eb557611eaf8360e0015183611adb565b50611fcb565b604080518082019091526000908060018152602001604051806040016040528087604001516001600160a01b0316815260200185815250604051602001611efc9190613692565b60405160208183030381529060405281525090506000611f8b6040518060c001604052808760000151815260200187602001516001600160a01b03168152602001604051806040016040528089606001516001600160a01b03168152602001896080015181525081526020018760c00151815260200160006001600160401b038111156109e5576109e561334b565b9050807f825080857c76cef4a1629c0705a7f8b4ef0282ddcafde0b6715c4fb34b68aaf08685604051611fbf929190613747565b60405180910390a25050505b50506001600455565b600160045414611ff65760405162461bcd60e51b81526004016103df906135f2565b600260045561200482611884565b600061201f8360000151846020015184866101200151612bae565b90508060000361203d576120378360c0015183611adb565b506121a1565b6040805180820190915260009080600281526020016040518061010001604052808a8152602001896001600160a01b03168152602001886001600160a01b0316815260200187604001516001600160a01b03168152602001858152602001876060015181526020018760a0015181526020018760e001516001600160a01b03168152506040516020016120d091906137c9565b604051602081830303815290604052815250905060006121616040518060c001604052808760000151815260200187602001516001600160a01b0316815260200160405180604001604052808961010001516001600160a01b0316815260200189610120015181525081526020018760800151815260200160006001600160401b038111156109e5576109e561334b565b9050807f42eff9005856e3c586b096d67211a566dc926052119fd7cc08023c70937ecb308685604051612195929190613868565b60405180910390a25050505b50506001600455505050565b826122065760405162461bcd60e51b8152602060048201526024808201527f546f6b656e486f6d653a207a65726f2072656d6f746520626c6f636b636861696044820152631b88125160e21b60648201526084016103df565b7f0000000000000000000000000000000000000000000000000000000000000000830361228d5760405162461bcd60e51b815260206004820152602f60248201527f546f6b656e486f6d653a2063616e6e6f742072656769737465722072656d6f7460448201526e329037b71039b0b6b29031b430b4b760891b60648201526084016103df565b6001600160a01b0382166122fc5760405162461bcd60e51b815260206004820152603060248201527f546f6b656e486f6d653a207a65726f2072656d6f746520746f6b656e2074726160448201526f6e73666572726572206164647265737360801b60648201526084016103df565b60008381526005602090815260408083206001600160a01b038616845290915290205460ff161561237b5760405162461bcd60e51b8152602060048201526024808201527f546f6b656e486f6d653a2072656d6f746520616c726561647920726567697374604482015263195c995960e21b60648201526084016103df565b6012816040015160ff1611156123e55760405162461bcd60e51b815260206004820152602960248201527f546f6b656e486f6d653a2072656d6f746520746f6b656e20646563696d616c73604482015268040e8dede40d0d2ced60bb1b60648201526084016103df565b7f000000000000000000000000000000000000000000000000000000000000000060ff16816020015160ff161461246d5760405162461bcd60e51b815260206004820152602660248201527f546f6b656e486f6d653a20696e76616c696420686f6d6520746f6b656e20646560448201526563696d616c7360d01b60648201526084016103df565b60008061249e7f00000000000000000000000000000000000000000000000000000000000000008460400151612d07565b9150915060006124b383838660000151612b9f565b90508180156124cd575083516124ca908490613f38565b15155b156124e0576124dd600182613e15565b90505b60408051608081018252600180825260208083018581528385018881528715156060860190815260008d8152600585528781206001600160a01b038e1680835295528790209551865490151560ff1991821617875592519486019490945551600285015591516003909301805493151593909216929092179055858201519151909188917ff229b02a51a4c8d5ef03a096ae0dd727d7b48b710d21b50ebebb560eef739b909161259e9186919091825260ff16602082015260400190565b60405180910390a3505050505050565b60006125db7f000000000000000000000000000000000000000000000000000000000000000033846125e1565b92915050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa15801561262a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264e91906135d9565b90506126656001600160a01b038616853086612d50565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa1580156126ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d091906135d9565b90508181116127365760405162461bcd60e51b815260206004820152602c60248201527f5361666545524332305472616e7366657246726f6d3a2062616c616e6365206e60448201526b1bdd081a5b98dc99585cd95960a21b60648201526084016103df565b6127408282613d7f565b9695505050505050565b60006127598484846001612d88565b949350505050565b6000807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663d820e64f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127e69190613f4c565b905061280a816001600160a01b031660009081526001602052604090205460ff1690565b156128705760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881cd95b991a5b99c81c185d5cd95960821b60648201526084016103df565b919050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa1580156128c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ea91906135d9565b6128f49190613e15565b6040516001600160a01b0385166024820152604481018290529091506107f090859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612db0565b835160009061297b5760405162461bcd60e51b81526004016103df90613d34565b6020850151156129d95760405162461bcd60e51b8152602060048201526024808201527f546f6b656e486f6d653a2072656d6f7465206e6f7420636f6c6c61746572616c6044820152631a5e995960e21b60648201526084016103df565b6129e4848484612e82565b60006129f98660400151876060015185612b9f565b905060008111611ad25760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e486f6d653a207a65726f20746f6b656e20616d6f756e740000000060448201526064016103df565b6040516001600160a01b03831660248201526044810182905261087790849063a9059cbb60e01b90606401612923565b60006127598460008585612f43565b801580612b045750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b0291906135d9565b155b612b6f5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016103df565b6040516001600160a01b03831660248201526044810182905261087790849063095ea7b360e01b90606401612923565b60006127598484846000612d88565b60008481526005602090815260408083206001600160a01b038716845282528083208151608081018352815460ff9081161580158352600184015495830195909552600283015493820193909352600390910154909116151560608201529080612c1c575060008160200151115b15612c2b576000915050612759565b828411612c8f5760405162461bcd60e51b815260206004820152602c60248201527f546f6b656e486f6d653a20696e73756666696369656e7420616d6f756e74207460448201526b6f20636f766572206665657360a01b60648201526084016103df565b612c998385613d7f565b93506000612cb0826040015183606001518761274a565b905080600003612cc557600092505050612759565b60008781526006602090815260408083206001600160a01b038a16845290915281208054839290612cf7908490613e15565b9091555090979650505050505050565b60008060ff808516908416118181612d2b57612d238587613f69565b60ff16612d39565b612d358686613f69565b60ff165b612d4490600a614066565b96919550909350505050565b6040516001600160a01b03808516602483015283166044820152606481018290526107f09085906323b872dd60e01b90608401612923565b600081151584151503612da657612d9f8584614072565b9050612759565b611ad28584614089565b6000612e05826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166130189092919063ffffffff16565b8051909150156108775780806020019051810190612e23919061409d565b6108775760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016103df565b60008381526006602090815260408083206001600160a01b038616845290915290205481811015612f0c5760405162461bcd60e51b815260206004820152602e60248201527f546f6b656e486f6d653a20696e73756666696369656e7420746f6b656e20747260448201526d616e736665722062616c616e636560901b60648201526084016103df565b612f168282613d7f565b60009485526006602090815260408087206001600160a01b03909616875294905292909320919091555050565b6000845a1015612f955760405162461bcd60e51b815260206004820152601b60248201527f43616c6c5574696c733a20696e73756666696369656e7420676173000000000060448201526064016103df565b83471015612fe55760405162461bcd60e51b815260206004820152601d60248201527f43616c6c5574696c733a20696e73756666696369656e742076616c756500000060448201526064016103df565b826001600160a01b03163b600003612fff57506000612759565b600080600084516020860188888bf19695505050505050565b6060612759848460008585600080866001600160a01b0316858760405161303f91906140bf565b60006040518083038185875af1925050503d806000811461307c576040519150601f19603f3d011682016040523d82523d6000602084013e613081565b606091505b50915091506130928783838761309d565b979650505050505050565b6060831561310c578251600003613105576001600160a01b0385163b6131055760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016103df565b5081612759565b61275983838151156131215781518083602001fd5b8060405162461bcd60e51b81526004016103df91906140db565b6001600160a01b03811681146105e057600080fd5b80356128708161313b565b60006020828403121561316d57600080fd5b813561187d8161313b565b6000806040838503121561318b57600080fd5b82359150602083013561319d8161313b565b809150509250929050565b6000808284036101208112156131bd57600080fd5b610100808212156131cd57600080fd5b9395938601359450505050565b6000602082840312156131ec57600080fd5b5035919050565b6000806040838503121561320657600080fd5b82356001600160401b0381111561321c57600080fd5b8301610160818603121561322f57600080fd5b946020939093013593505050565b6000806000806060858703121561325357600080fd5b8435935060208501356132658161313b565b925060408501356001600160401b038082111561328157600080fd5b818701915087601f83011261329557600080fd5b8135818111156132a457600080fd5b8860208285010111156132b657600080fd5b95989497505060200194505050565b6000806000606084860312156132da57600080fd5b8335925060208401356132ec8161313b565b929592945050506040919091013590565b6020808252602e908201527f54656c65706f727465725570677261646561626c653a207a65726f2054656c6560408201526d706f72746572206164647265737360901b606082015260800190565b634e487b7160e01b600052604160045260246000fd5b60405161010081016001600160401b03811182821017156133845761338461334b565b60405290565b60405161016081016001600160401b03811182821017156133845761338461334b565b604080519081016001600160401b03811182821017156133845761338461334b565b604051601f8201601f191681016001600160401b03811182821017156133f7576133f761334b565b604052919050565b6000610100828403121561341257600080fd5b61341a613361565b82358152602083013561342c8161313b565b6020820152604083013561343f8161313b565b604082015261345060608401613150565b60608201526080830135608082015260a083013560a082015260c083013560c082015261347f60e08401613150565b60e08201529392505050565b60006001600160401b038211156134a4576134a461334b565b50601f01601f191660200190565b600082601f8301126134c357600080fd5b81356134d66134d18261348b565b6133cf565b8181528460208386010111156134eb57600080fd5b816020850160208301376000918101602001919091529392505050565b6000610160823603121561351b57600080fd5b61352361338a565b8235815261353360208401613150565b602082015261354460408401613150565b604082015260608301356001600160401b0381111561356257600080fd5b61356e368286016134b2565b6060830152506080830135608082015260a083013560a082015261359460c08401613150565b60c08201526135a560e08401613150565b60e08201526101006135b8818501613150565b90820152610120838101359082015261014092830135928101929092525090565b6000602082840312156135eb57600080fd5b5051919050565b60208082526024908201527f53656e645265656e7472616e637947756172643a2073656e64207265656e7472604082015263616e637960e01b606082015260800190565b60208082526026908201527f546f6b656e486f6d653a206e6f6e2d7a65726f206d756c74692d686f702066616040820152656c6c6261636b60d01b606082015260800190565b634e487b7160e01b600052602160045260246000fd5b81516001600160a01b0316815260208083015190820152604081016125db565b60005b838110156136cd5781810151838201526020016136b5565b50506000910152565b600081518084526136ee8160208601602086016136b2565b601f01601f19169290920160200192915050565b60208152600082516005811061372857634e487b7160e01b600052602160045260246000fd5b80602084015250602083015160408084015261275960608401826136d6565b60006101208201905083518252602084015160018060a01b03808216602085015280604087015116604085015280606087015116606085015250506080840151608083015260a084015160a083015260c084015160c083015260e08401516137ba60e08401826001600160a01b03169052565b50826101008301529392505050565b60208152815160208201526000602083015160018060a01b0380821660408501528060408601511660608501525050606083015161381260808401826001600160a01b03169052565b50608083015160a083015260a08301516101008060c08501526138396101208501836136d6565b915060c085015160e085015260e085015161385e828601826001600160a01b03169052565b5090949350505050565b60408152825160408201526000602084015161388f60608401826001600160a01b03169052565b5060408401516001600160a01b03166080830152606084015161016060a084018190526138c06101a08501836136d6565b9150608086015160c085015260a086015160e085015260c08601516101006138f2818701836001600160a01b03169052565b60e08801519150610120613910818801846001600160a01b03169052565b9088015191506101409061392e878301846001600160a01b03169052565b880151928601929092525090940151610180830152506020015290565b600082601f83011261395c57600080fd5b815161396a6134d18261348b565b81815284602083860101111561397f57600080fd5b6127598260208301602087016136b2565b6000602082840312156139a257600080fd5b81516001600160401b03808211156139b957600080fd5b90830190604082860312156139cd57600080fd5b6139d56133ad565b8251600581106139e457600080fd5b81526020830151828111156139f857600080fd5b613a048782860161394b565b60208301525095945050505050565b80516128708161313b565b600060408284031215613a3057600080fd5b613a386133ad565b8251613a438161313b565b81526020928301519281019290925250919050565b600060208284031215613a6a57600080fd5b81516001600160401b0380821115613a8157600080fd5b908301906101008286031215613a9657600080fd5b613a9e613361565b82518152613aae60208401613a13565b6020820152613abf60408401613a13565b6040820152613ad060608401613a13565b60608201526080830151608082015260a083015182811115613af157600080fd5b613afd8782860161394b565b60a08301525060c083015160c0820152613b1960e08401613a13565b60e082015295945050505050565b600060e08284031215613b3957600080fd5b60405160e081018181106001600160401b0382111715613b5b57613b5b61334b565b604052825181526020830151613b708161313b565b60208201526040830151613b838161313b565b80604083015250606083015160608201526080830151608082015260a083015160a082015260c0830151613bb68161313b565b60c08201529392505050565b600060208284031215613bd457600080fd5b81516001600160401b0380821115613beb57600080fd5b908301906101608286031215613c0057600080fd5b613c0861338a565b613c1183613a13565b815260208301516020820152613c2960408401613a13565b6040820152613c3a60608401613a13565b60608201526080830151608082015260a083015182811115613c5b57600080fd5b613c678782860161394b565b60a08301525060c083015160c0820152613c8360e08401613a13565b60e082015261010083810151908201526101209150613ca3828401613a13565b9181019190915261014091820151918101919091529392505050565b805160ff8116811461287057600080fd5b600060608284031215613ce257600080fd5b604051606081018181106001600160401b0382111715613d0457613d0461334b565b60405282518152613d1760208401613cbf565b6020820152613d2860408401613cbf565b60408201529392505050565b6020808252818101527f546f6b656e486f6d653a2072656d6f7465206e6f742072656769737465726564604082015260600190565b634e487b7160e01b600052601160045260246000fd5b818103818111156125db576125db613d69565b60208082526022908201527f546f6b656e486f6d653a207a65726f20726571756972656420676173206c696d6040820152611a5d60f21b606082015260800190565b60208082526021908201527f546f6b656e486f6d653a206e6f6e2d7a65726f207365636f6e646172792066656040820152606560f81b606082015260800190565b808201808211156125db576125db613d69565b6020808252825182820152828101516001600160a01b039081166040808501919091528401518051821660608501528083015160808501526000929161010085019190606087015160a0870152608087015160e060c0880152805193849052840192600092506101208701905b80841015613eb757845183168252938501936001939093019290850190613e95565b5060a0880151878203601f190160e08901529450613ed581866136d6565b98975050505050505050565b8681526001600160a01b0386811660208301528581166040830152841660608201526080810183905260c060a08201819052600090613ed5908301846136d6565b634e487b7160e01b600052601260045260246000fd5b600082613f4757613f47613f22565b500690565b600060208284031215613f5e57600080fd5b815161187d8161313b565b60ff82811682821603908111156125db576125db613d69565b600181815b80851115613fbd578160001904821115613fa357613fa3613d69565b80851615613fb057918102915b93841c9390800290613f87565b509250929050565b600082613fd4575060016125db565b81613fe1575060006125db565b8160018114613ff757600281146140015761401d565b60019150506125db565b60ff84111561401257614012613d69565b50506001821b6125db565b5060208310610133831016604e8410600b8410161715614040575081810a6125db565b61404a8383613f82565b806000190482111561405e5761405e613d69565b029392505050565b600061187d8383613fc5565b80820281158282048414176125db576125db613d69565b60008261409857614098613f22565b500490565b6000602082840312156140af57600080fd5b8151801515811461187d57600080fd5b600082516140d18184602087016136b2565b9190910192915050565b60208152600061187d60208301846136d656fea264697066735822122027ee2c8edf5a85411d98a486f46b1b0d4271954ef0a1055ad3cd05958c4db41f64736f6c63430008120033000000000000000000000000f86cb19ad8405aefa7d09c778215d2cb6ebfb228000000000000000000000000efe9e34988a5054907fc125aef5e85aaaf7f928c0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad180000000000000000000000000000000000000000000000000000000000000012
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101215760003560e01c80638da5cb5b116100ad578063d2cc7a7011610071578063d2cc7a70146102f3578063d536ec33146102fb578063f2fde38b14610364578063fc0c546a14610377578063fd6582681461039e57600080fd5b80638da5cb5b1461024557806397314297146102565780639d76ea5814610292578063c868efaa146102b9578063d127dc9b146102cc57600080fd5b80634511243e116100f45780634511243e146101f15780635d16225d146102045780635eb9951414610217578063656900381461022a578063715018a61461023d57600080fd5b80631a7f5bec146101265780632b0d8f181461016a5780633b97e8561461017f5780633ca563e3146101b8575b600080fd5b61014d7f000000000000000000000000f86cb19ad8405aefa7d09c778215d2cb6ebfb22881565b6040516001600160a01b0390911681526020015b60405180910390f35b61017d61017836600461315b565b6103b1565b005b6101a67f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610161565b6101e36101c6366004613178565b600660209081526000928352604080842090915290825290205481565b604051908152602001610161565b61017d6101ff36600461315b565b6104b6565b61017d6102123660046131a8565b6105b3565b61017d6102253660046131da565b6105cf565b61017d6102383660046131f3565b6105e3565b61017d610618565b6003546001600160a01b031661014d565b61028261026436600461315b565b6001600160a01b031660009081526001602052604090205460ff1690565b6040519015158152602001610161565b61014d7f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad1881565b61017d6102c736600461323d565b61062c565b6101e37f7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d581565b6002546101e3565b610340610309366004613178565b6005602090815260009283526040808420909152908252902080546001820154600283015460039093015460ff9283169391921684565b60408051941515855260208501939093529183015215156060820152608001610161565b61017d61037236600461315b565b6107f6565b61014d7f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad1881565b61017d6103ac3660046132c5565b61086c565b6103b961087c565b6001600160a01b0381166103e85760405162461bcd60e51b81526004016103df906132fd565b60405180910390fd5b6001600160a01b03811660009081526001602052604090205460ff16156104675760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a2061646472657373206160448201526c1b1c9958591e481c185d5cd959609a1b60648201526084016103df565b6001600160a01b0381166000818152600160208190526040808320805460ff1916909217909155517f933f93e57a222e6330362af8b376d0a8725b6901e9a2fb86d00f169702b28a4c9190a250565b6104be61087c565b6001600160a01b0381166104e45760405162461bcd60e51b81526004016103df906132fd565b6001600160a01b03811660009081526001602052604090205460ff1661055e5760405162461bcd60e51b815260206004820152602960248201527f54656c65706f727465725570677261646561626c653a2061646472657373206e6044820152681bdd081c185d5cd95960ba1b60648201526084016103df565b6040516001600160a01b038216907f844e2f3154214672229235858fd029d1dfd543901c6d05931f0bc2480a2d72c390600090a26001600160a01b03166000908152600160205260409020805460ff19169055565b6105cb6105c5368490038401846133ff565b82610884565b5050565b6105d761087c565b6105e081610a8f565b50565b6105cb7f7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5303361061286613508565b85610c2f565b610620610e35565b61062a6000610e8f565b565b610634610ee1565b6002546001600160a01b037f000000000000000000000000f86cb19ad8405aefa7d09c778215d2cb6ebfb22816634c1f08ce336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602401602060405180830381865afa1580156106ab573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106cf91906135d9565b10156107365760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201526f32b632b837b93a32b91039b2b73232b960811b60648201526084016103df565b61073f33610264565b156107a55760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881859191c995cdcc81c185d5cd95960821b60648201526084016103df565b6107e6848484848080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250610f3a92505050565b6107f06001600055565b50505050565b6107fe610e35565b6001600160a01b0381166108635760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016103df565b6105e081610e8f565b61087783838361133d565b505050565b61062a610e35565b6001600454146108a65760405162461bcd60e51b81526004016103df906135f2565b60026004556108b48261150a565b60e08201516001600160a01b0316156108df5760405162461bcd60e51b81526004016103df90613636565b6000806108ff8460000151856020015185876060015188608001516115b4565b9150915060006040518060400160405280600160048111156109235761092361367c565b8152602001604051806040016040528088604001516001600160a01b031681526020018681525060405160200161095a9190613692565b60405160208183030381529060405281525090506000610a3c6040518060c001604052808860000151815260200188602001516001600160a01b0316815260200160405180604001604052808a606001516001600160a01b031681526020018781525081526020018860c00151815260200160006001600160401b038111156109e5576109e561334b565b604051908082528060200260200182016040528015610a0e578160200160208202803683370190505b50815260200184604051602001610a259190613702565b60405160208183030381529060405281525061175e565b9050336001600160a01b0316817f93f19bf1ec58a15dc643b37e7e18a1c13e85e06cd11929e283154691ace9fb528887604051610a7a929190613747565b60405180910390a35050600160045550505050565b60007f000000000000000000000000f86cb19ad8405aefa7d09c778215d2cb6ebfb2286001600160a01b031663c07f47d46040518163ffffffff1660e01b8152600401602060405180830381865afa158015610aef573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b1391906135d9565b60025490915081831115610b835760405162461bcd60e51b815260206004820152603160248201527f54656c65706f727465725570677261646561626c653a20696e76616c6964205460448201527032b632b837b93a32b9103b32b939b4b7b760791b60648201526084016103df565b808311610bf85760405162461bcd60e51b815260206004820152603f60248201527f54656c65706f727465725570677261646561626c653a206e6f7420677265617460448201527f6572207468616e2063757272656e74206d696e696d756d2076657273696f6e0060648201526084016103df565b6002839055604051839082907fa9a7ef57e41f05b4c15480842f5f0c27edfcbb553fed281f7c4068452cc1c02d90600090a3505050565b600160045414610c515760405162461bcd60e51b81526004016103df906135f2565b6002600455610c5f82611884565b60c08201516001600160a01b031615610c8a5760405162461bcd60e51b81526004016103df90613636565b600080610cac84600001518560200151858761010001518861012001516115b4565b915091506000604051806040016040528060026004811115610cd057610cd061367c565b81526020016040518061010001604052808b81526020018a6001600160a01b03168152602001896001600160a01b0316815260200188604001516001600160a01b03168152602001868152602001886060015181526020018860a0015181526020018860e001516001600160a01b0316815250604051602001610d5391906137c9565b60405160208183030381529060405281525090506000610ddf6040518060c001604052808860000151815260200188602001516001600160a01b0316815260200160405180604001604052808a61010001516001600160a01b031681526020018781525081526020018860800151815260200160006001600160401b038111156109e5576109e561334b565b9050866001600160a01b0316817f5d76dff81bf773b908b050fa113d39f7d8135bb4175398f313ea19cd3a1a0b168887604051610e1d929190613868565b60405180910390a35050600160045550505050505050565b6003546001600160a01b0316331461062a5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016103df565b600380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600260005403610f335760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064016103df565b6002600055565b600081806020019051810190610f509190613990565b9050600181516004811115610f6757610f6761367c565b03610fb15760008160200151806020019051810190610f869190613a1e565b90506000610f9986868460200151611a6b565b9050610fa9826000015182611adb565b505050505050565b600281516004811115610fc657610fc661367c565b036110e15760008160200151806020019051810190610fe59190613a58565b90506000610ff886868460800151611a6b565b8251909150861461105e5760405162461bcd60e51b815260206004820152602a60248201527f546f6b656e486f6d653a206d69736d61746368656420736f7572636520626c6f60448201526918dad8da185a5b88125160b21b60648201526084016103df565b846001600160a01b031682602001516001600160a01b0316146110d75760405162461bcd60e51b815260206004820152602b60248201527f546f6b656e486f6d653a206d69736d617463686564206f726967696e2073656e60448201526a646572206164647265737360a81b60648201526084016103df565b610fa98282611b52565b6003815160048111156110f6576110f661367c565b036111d757600081602001518060200190518101906111159190613b27565b905060008061112e878785606001518660800151611dbc565b915091506111ce6040518061010001604052808560000151815260200185602001516001600160a01b0316815260200185604001516001600160a01b031681526020017f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad186001600160a01b03168152602001838152602001600081526020018560a0015181526020018560c001516001600160a01b031681525083611e4d565b50505050505050565b6004815160048111156111ec576111ec61367c565b036112f5576000816020015180602001905181019061120b9190613bc2565b905060008061122587878560800151866101400151611dbc565b915091506111ce878785600001516040518061016001604052808860200151815260200188604001516001600160a01b0316815260200188606001516001600160a01b031681526020018860a00151815260200188610100015181526020018860c0015181526020018861012001516001600160a01b031681526020018860e001516001600160a01b031681526020017f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad186001600160a01b03168152602001868152602001600081525086611fd4565b60008151600481111561130a5761130a61367c565b036107f057600081602001518060200190518101906113299190613cd0565b90506113368585836121ad565b5050505050565b60016004541461135f5760405162461bcd60e51b81526004016103df906135f2565b6002600481905560008481526005602090815260408083206001600160a01b03871684528252918290208251608081018452815460ff908116151580835260018401549483019490945294820154938101939093526003015490921615156060820152906113df5760405162461bcd60e51b81526004016103df90613d34565b600081602001511161143d5760405162461bcd60e51b815260206004820152602160248201527f546f6b656e486f6d653a207a65726f20636f6c6c61746572616c206e656564656044820152601960fa1b60648201526084016103df565b611446826125ae565b915060008082602001518410611477576020830151600092506114699085613d7f565b90508260200151935061148a565b8383602001516114879190613d7f565b91505b60008681526005602090815260408083206001600160a01b03891680855290835292819020600101859055805187815291820185905288917f6769a5f9bfc8b6e0db839ab981cbf9239274ae72d2d035081a9157d43bd33cb6910160405180910390a380156114fd576114fd3382611adb565b5050600160045550505050565b60408101516001600160a01b031661156e5760405162461bcd60e51b815260206004820152602160248201527f546f6b656e486f6d653a207a65726f20726563697069656e74206164647265736044820152607360f81b60648201526084016103df565b60008160c00151116115925760405162461bcd60e51b81526004016103df90613d92565b60a0810151156105e05760405162461bcd60e51b81526004016103df90613dd4565b60008581526005602090815260408083206001600160a01b038816845282528083208151608081018352815460ff90811615158083526001840154958301959095526002830154938201939093526003909101549091161515606082015282916116305760405162461bcd60e51b81526004016103df90613d34565b6020810151156116925760405162461bcd60e51b815260206004820152602760248201527f546f6b656e486f6d653a20636f6c6c61746572616c206e656564656420666f726044820152662072656d6f746560c81b60648201526084016103df565b61169b866125ae565b955083156116b1576116ae8533866125e1565b93505b60006116c6826040015183606001518961274a565b9050600081116117185760405162461bcd60e51b815260206004820152601d60248201527f546f6b656e486f6d653a207a65726f207363616c656420616d6f756e7400000060448201526064016103df565b60008981526006602090815260408083206001600160a01b038c1684529091528120805483929061174a908490613e15565b909155509099949850939650505050505050565b600080611769612761565b6040840151602001519091501561180e576040830151516001600160a01b03166117eb5760405162461bcd60e51b815260206004820152602d60248201527f54656c65706f727465725570677261646561626c653a207a65726f206665652060448201526c746f6b656e206164647265737360981b60648201526084016103df565b60408301516020810151905161180e916001600160a01b03909116908390612875565b604051630624488560e41b81526001600160a01b0382169063624488509061183a908690600401613e28565b6020604051808303816000875af1158015611859573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061187d91906135d9565b9392505050565b60408101516001600160a01b03166118f15760405162461bcd60e51b815260206004820152602a60248201527f546f6b656e486f6d653a207a65726f20726563697069656e7420636f6e7472616044820152696374206164647265737360b01b60648201526084016103df565b60008160800151116119155760405162461bcd60e51b81526004016103df90613d92565b60008160a00151116119755760405162461bcd60e51b815260206004820152602360248201527f546f6b656e486f6d653a207a65726f20726563697069656e7420676173206c696044820152621b5a5d60ea1b60648201526084016103df565b80608001518160a00151106119db5760405162461bcd60e51b815260206004820152602660248201527f546f6b656e486f6d653a20696e76616c696420726563697069656e7420676173604482015265081b1a5b5a5d60d21b60648201526084016103df565b60e08101516001600160a01b0316611a485760405162461bcd60e51b815260206004820152602a60248201527f546f6b656e486f6d653a207a65726f2066616c6c6261636b20726563697069656044820152696e74206164647265737360b01b60648201526084016103df565b610140810151156105e05760405162461bcd60e51b81526004016103df90613dd4565b60008381526005602090815260408083206001600160a01b038616845282528083208151608081018352815460ff908116151582526001830154948201949094526002820154928101929092526003015490911615156060820152611ad28186868661295a565b95945050505050565b816001600160a01b03167f6352c5382c4a4578e712449ca65e83cdb392d045dfcf1cad9615189db2da244b82604051611b1691815260200190565b60405180910390a26105cb6001600160a01b037f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad18168383612a4b565b611b817f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad18836060015183612875565b60008260000151836020015184604001517f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad18858760a00151604051602401611bce96959493929190613ee1565b60408051601f198184030181529190526020810180516001600160e01b03166394395edd60e01b17905260c08401516060850151919250600091611c13919084612a7b565b6060850151604051636eb1769f60e11b81523060048201526001600160a01b0391821660248201529192506000917f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad189091169063dd62ed3e90604401602060405180830381865afa158015611c8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cb091906135d9565b9050611ce27f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad1886606001516000612a8a565b8115611d345784606001516001600160a01b03167f104deb555f67e63782bb817bc26c39050894645f9b9f29c4be8ae68d0e8b7ff485604051611d2791815260200190565b60405180910390a2611d7c565b84606001516001600160a01b03167fb9eaeae386d339f8115782f297a9e5f0e13fb587cd6b0d502f113cb8dd4d6cb085604051611d7391815260200190565b60405180910390a25b80156113365760e0850151611336906001600160a01b037f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad18169083612a4b565b60008481526005602090815260408083206001600160a01b038716845282528083208151608081018352815460ff908116151582526001830154948201949094526002820154928101929092526003015490911615156060820152819081611e268289898961295a565b90506000611e3d8360400151846060015188612b9f565b9199919850909650505050505050565b600160045414611e6f5760405162461bcd60e51b81526004016103df906135f2565b6002600455611e7d8261150a565b6000611e9783600001518460200151848660800151612bae565b905080600003611eb557611eaf8360e0015183611adb565b50611fcb565b604080518082019091526000908060018152602001604051806040016040528087604001516001600160a01b0316815260200185815250604051602001611efc9190613692565b60405160208183030381529060405281525090506000611f8b6040518060c001604052808760000151815260200187602001516001600160a01b03168152602001604051806040016040528089606001516001600160a01b03168152602001896080015181525081526020018760c00151815260200160006001600160401b038111156109e5576109e561334b565b9050807f825080857c76cef4a1629c0705a7f8b4ef0282ddcafde0b6715c4fb34b68aaf08685604051611fbf929190613747565b60405180910390a25050505b50506001600455565b600160045414611ff65760405162461bcd60e51b81526004016103df906135f2565b600260045561200482611884565b600061201f8360000151846020015184866101200151612bae565b90508060000361203d576120378360c0015183611adb565b506121a1565b6040805180820190915260009080600281526020016040518061010001604052808a8152602001896001600160a01b03168152602001886001600160a01b0316815260200187604001516001600160a01b03168152602001858152602001876060015181526020018760a0015181526020018760e001516001600160a01b03168152506040516020016120d091906137c9565b604051602081830303815290604052815250905060006121616040518060c001604052808760000151815260200187602001516001600160a01b0316815260200160405180604001604052808961010001516001600160a01b0316815260200189610120015181525081526020018760800151815260200160006001600160401b038111156109e5576109e561334b565b9050807f42eff9005856e3c586b096d67211a566dc926052119fd7cc08023c70937ecb308685604051612195929190613868565b60405180910390a25050505b50506001600455505050565b826122065760405162461bcd60e51b8152602060048201526024808201527f546f6b656e486f6d653a207a65726f2072656d6f746520626c6f636b636861696044820152631b88125160e21b60648201526084016103df565b7f7fc93d85c6d62c5b2ac0b519c87010ea5294012d1e407030d6acd0021cac10d5830361228d5760405162461bcd60e51b815260206004820152602f60248201527f546f6b656e486f6d653a2063616e6e6f742072656769737465722072656d6f7460448201526e329037b71039b0b6b29031b430b4b760891b60648201526084016103df565b6001600160a01b0382166122fc5760405162461bcd60e51b815260206004820152603060248201527f546f6b656e486f6d653a207a65726f2072656d6f746520746f6b656e2074726160448201526f6e73666572726572206164647265737360801b60648201526084016103df565b60008381526005602090815260408083206001600160a01b038616845290915290205460ff161561237b5760405162461bcd60e51b8152602060048201526024808201527f546f6b656e486f6d653a2072656d6f746520616c726561647920726567697374604482015263195c995960e21b60648201526084016103df565b6012816040015160ff1611156123e55760405162461bcd60e51b815260206004820152602960248201527f546f6b656e486f6d653a2072656d6f746520746f6b656e20646563696d616c73604482015268040e8dede40d0d2ced60bb1b60648201526084016103df565b7f000000000000000000000000000000000000000000000000000000000000001260ff16816020015160ff161461246d5760405162461bcd60e51b815260206004820152602660248201527f546f6b656e486f6d653a20696e76616c696420686f6d6520746f6b656e20646560448201526563696d616c7360d01b60648201526084016103df565b60008061249e7f00000000000000000000000000000000000000000000000000000000000000128460400151612d07565b9150915060006124b383838660000151612b9f565b90508180156124cd575083516124ca908490613f38565b15155b156124e0576124dd600182613e15565b90505b60408051608081018252600180825260208083018581528385018881528715156060860190815260008d8152600585528781206001600160a01b038e1680835295528790209551865490151560ff1991821617875592519486019490945551600285015591516003909301805493151593909216929092179055858201519151909188917ff229b02a51a4c8d5ef03a096ae0dd727d7b48b710d21b50ebebb560eef739b909161259e9186919091825260ff16602082015260400190565b60405180910390a3505050505050565b60006125db7f0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad1833846125e1565b92915050565b6040516370a0823160e01b815230600482015260009081906001600160a01b038616906370a0823190602401602060405180830381865afa15801561262a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061264e91906135d9565b90506126656001600160a01b038616853086612d50565b6040516370a0823160e01b81523060048201526000906001600160a01b038716906370a0823190602401602060405180830381865afa1580156126ac573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906126d091906135d9565b90508181116127365760405162461bcd60e51b815260206004820152602c60248201527f5361666545524332305472616e7366657246726f6d3a2062616c616e6365206e60448201526b1bdd081a5b98dc99585cd95960a21b60648201526084016103df565b6127408282613d7f565b9695505050505050565b60006127598484846001612d88565b949350505050565b6000807f000000000000000000000000f86cb19ad8405aefa7d09c778215d2cb6ebfb2286001600160a01b031663d820e64f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156127c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906127e69190613f4c565b905061280a816001600160a01b031660009081526001602052604090205460ff1690565b156128705760405162461bcd60e51b815260206004820152603060248201527f54656c65706f727465725570677261646561626c653a2054656c65706f72746560448201526f1c881cd95b991a5b99c81c185d5cd95960821b60648201526084016103df565b919050565b604051636eb1769f60e11b81523060048201526001600160a01b038381166024830152600091839186169063dd62ed3e90604401602060405180830381865afa1580156128c6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906128ea91906135d9565b6128f49190613e15565b6040516001600160a01b0385166024820152604481018290529091506107f090859063095ea7b360e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152612db0565b835160009061297b5760405162461bcd60e51b81526004016103df90613d34565b6020850151156129d95760405162461bcd60e51b8152602060048201526024808201527f546f6b656e486f6d653a2072656d6f7465206e6f7420636f6c6c61746572616c6044820152631a5e995960e21b60648201526084016103df565b6129e4848484612e82565b60006129f98660400151876060015185612b9f565b905060008111611ad25760405162461bcd60e51b815260206004820152601c60248201527f546f6b656e486f6d653a207a65726f20746f6b656e20616d6f756e740000000060448201526064016103df565b6040516001600160a01b03831660248201526044810182905261087790849063a9059cbb60e01b90606401612923565b60006127598460008585612f43565b801580612b045750604051636eb1769f60e11b81523060048201526001600160a01b03838116602483015284169063dd62ed3e90604401602060405180830381865afa158015612ade573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612b0291906135d9565b155b612b6f5760405162461bcd60e51b815260206004820152603660248201527f5361666545524332303a20617070726f76652066726f6d206e6f6e2d7a65726f60448201527520746f206e6f6e2d7a65726f20616c6c6f77616e636560501b60648201526084016103df565b6040516001600160a01b03831660248201526044810182905261087790849063095ea7b360e01b90606401612923565b60006127598484846000612d88565b60008481526005602090815260408083206001600160a01b038716845282528083208151608081018352815460ff9081161580158352600184015495830195909552600283015493820193909352600390910154909116151560608201529080612c1c575060008160200151115b15612c2b576000915050612759565b828411612c8f5760405162461bcd60e51b815260206004820152602c60248201527f546f6b656e486f6d653a20696e73756666696369656e7420616d6f756e74207460448201526b6f20636f766572206665657360a01b60648201526084016103df565b612c998385613d7f565b93506000612cb0826040015183606001518761274a565b905080600003612cc557600092505050612759565b60008781526006602090815260408083206001600160a01b038a16845290915281208054839290612cf7908490613e15565b9091555090979650505050505050565b60008060ff808516908416118181612d2b57612d238587613f69565b60ff16612d39565b612d358686613f69565b60ff165b612d4490600a614066565b96919550909350505050565b6040516001600160a01b03808516602483015283166044820152606481018290526107f09085906323b872dd60e01b90608401612923565b600081151584151503612da657612d9f8584614072565b9050612759565b611ad28584614089565b6000612e05826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166130189092919063ffffffff16565b8051909150156108775780806020019051810190612e23919061409d565b6108775760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016103df565b60008381526006602090815260408083206001600160a01b038616845290915290205481811015612f0c5760405162461bcd60e51b815260206004820152602e60248201527f546f6b656e486f6d653a20696e73756666696369656e7420746f6b656e20747260448201526d616e736665722062616c616e636560901b60648201526084016103df565b612f168282613d7f565b60009485526006602090815260408087206001600160a01b03909616875294905292909320919091555050565b6000845a1015612f955760405162461bcd60e51b815260206004820152601b60248201527f43616c6c5574696c733a20696e73756666696369656e7420676173000000000060448201526064016103df565b83471015612fe55760405162461bcd60e51b815260206004820152601d60248201527f43616c6c5574696c733a20696e73756666696369656e742076616c756500000060448201526064016103df565b826001600160a01b03163b600003612fff57506000612759565b600080600084516020860188888bf19695505050505050565b6060612759848460008585600080866001600160a01b0316858760405161303f91906140bf565b60006040518083038185875af1925050503d806000811461307c576040519150601f19603f3d011682016040523d82523d6000602084013e613081565b606091505b50915091506130928783838761309d565b979650505050505050565b6060831561310c578251600003613105576001600160a01b0385163b6131055760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016103df565b5081612759565b61275983838151156131215781518083602001fd5b8060405162461bcd60e51b81526004016103df91906140db565b6001600160a01b03811681146105e057600080fd5b80356128708161313b565b60006020828403121561316d57600080fd5b813561187d8161313b565b6000806040838503121561318b57600080fd5b82359150602083013561319d8161313b565b809150509250929050565b6000808284036101208112156131bd57600080fd5b610100808212156131cd57600080fd5b9395938601359450505050565b6000602082840312156131ec57600080fd5b5035919050565b6000806040838503121561320657600080fd5b82356001600160401b0381111561321c57600080fd5b8301610160818603121561322f57600080fd5b946020939093013593505050565b6000806000806060858703121561325357600080fd5b8435935060208501356132658161313b565b925060408501356001600160401b038082111561328157600080fd5b818701915087601f83011261329557600080fd5b8135818111156132a457600080fd5b8860208285010111156132b657600080fd5b95989497505060200194505050565b6000806000606084860312156132da57600080fd5b8335925060208401356132ec8161313b565b929592945050506040919091013590565b6020808252602e908201527f54656c65706f727465725570677261646561626c653a207a65726f2054656c6560408201526d706f72746572206164647265737360901b606082015260800190565b634e487b7160e01b600052604160045260246000fd5b60405161010081016001600160401b03811182821017156133845761338461334b565b60405290565b60405161016081016001600160401b03811182821017156133845761338461334b565b604080519081016001600160401b03811182821017156133845761338461334b565b604051601f8201601f191681016001600160401b03811182821017156133f7576133f761334b565b604052919050565b6000610100828403121561341257600080fd5b61341a613361565b82358152602083013561342c8161313b565b6020820152604083013561343f8161313b565b604082015261345060608401613150565b60608201526080830135608082015260a083013560a082015260c083013560c082015261347f60e08401613150565b60e08201529392505050565b60006001600160401b038211156134a4576134a461334b565b50601f01601f191660200190565b600082601f8301126134c357600080fd5b81356134d66134d18261348b565b6133cf565b8181528460208386010111156134eb57600080fd5b816020850160208301376000918101602001919091529392505050565b6000610160823603121561351b57600080fd5b61352361338a565b8235815261353360208401613150565b602082015261354460408401613150565b604082015260608301356001600160401b0381111561356257600080fd5b61356e368286016134b2565b6060830152506080830135608082015260a083013560a082015261359460c08401613150565b60c08201526135a560e08401613150565b60e08201526101006135b8818501613150565b90820152610120838101359082015261014092830135928101929092525090565b6000602082840312156135eb57600080fd5b5051919050565b60208082526024908201527f53656e645265656e7472616e637947756172643a2073656e64207265656e7472604082015263616e637960e01b606082015260800190565b60208082526026908201527f546f6b656e486f6d653a206e6f6e2d7a65726f206d756c74692d686f702066616040820152656c6c6261636b60d01b606082015260800190565b634e487b7160e01b600052602160045260246000fd5b81516001600160a01b0316815260208083015190820152604081016125db565b60005b838110156136cd5781810151838201526020016136b5565b50506000910152565b600081518084526136ee8160208601602086016136b2565b601f01601f19169290920160200192915050565b60208152600082516005811061372857634e487b7160e01b600052602160045260246000fd5b80602084015250602083015160408084015261275960608401826136d6565b60006101208201905083518252602084015160018060a01b03808216602085015280604087015116604085015280606087015116606085015250506080840151608083015260a084015160a083015260c084015160c083015260e08401516137ba60e08401826001600160a01b03169052565b50826101008301529392505050565b60208152815160208201526000602083015160018060a01b0380821660408501528060408601511660608501525050606083015161381260808401826001600160a01b03169052565b50608083015160a083015260a08301516101008060c08501526138396101208501836136d6565b915060c085015160e085015260e085015161385e828601826001600160a01b03169052565b5090949350505050565b60408152825160408201526000602084015161388f60608401826001600160a01b03169052565b5060408401516001600160a01b03166080830152606084015161016060a084018190526138c06101a08501836136d6565b9150608086015160c085015260a086015160e085015260c08601516101006138f2818701836001600160a01b03169052565b60e08801519150610120613910818801846001600160a01b03169052565b9088015191506101409061392e878301846001600160a01b03169052565b880151928601929092525090940151610180830152506020015290565b600082601f83011261395c57600080fd5b815161396a6134d18261348b565b81815284602083860101111561397f57600080fd5b6127598260208301602087016136b2565b6000602082840312156139a257600080fd5b81516001600160401b03808211156139b957600080fd5b90830190604082860312156139cd57600080fd5b6139d56133ad565b8251600581106139e457600080fd5b81526020830151828111156139f857600080fd5b613a048782860161394b565b60208301525095945050505050565b80516128708161313b565b600060408284031215613a3057600080fd5b613a386133ad565b8251613a438161313b565b81526020928301519281019290925250919050565b600060208284031215613a6a57600080fd5b81516001600160401b0380821115613a8157600080fd5b908301906101008286031215613a9657600080fd5b613a9e613361565b82518152613aae60208401613a13565b6020820152613abf60408401613a13565b6040820152613ad060608401613a13565b60608201526080830151608082015260a083015182811115613af157600080fd5b613afd8782860161394b565b60a08301525060c083015160c0820152613b1960e08401613a13565b60e082015295945050505050565b600060e08284031215613b3957600080fd5b60405160e081018181106001600160401b0382111715613b5b57613b5b61334b565b604052825181526020830151613b708161313b565b60208201526040830151613b838161313b565b80604083015250606083015160608201526080830151608082015260a083015160a082015260c0830151613bb68161313b565b60c08201529392505050565b600060208284031215613bd457600080fd5b81516001600160401b0380821115613beb57600080fd5b908301906101608286031215613c0057600080fd5b613c0861338a565b613c1183613a13565b815260208301516020820152613c2960408401613a13565b6040820152613c3a60608401613a13565b60608201526080830151608082015260a083015182811115613c5b57600080fd5b613c678782860161394b565b60a08301525060c083015160c0820152613c8360e08401613a13565b60e082015261010083810151908201526101209150613ca3828401613a13565b9181019190915261014091820151918101919091529392505050565b805160ff8116811461287057600080fd5b600060608284031215613ce257600080fd5b604051606081018181106001600160401b0382111715613d0457613d0461334b565b60405282518152613d1760208401613cbf565b6020820152613d2860408401613cbf565b60408201529392505050565b6020808252818101527f546f6b656e486f6d653a2072656d6f7465206e6f742072656769737465726564604082015260600190565b634e487b7160e01b600052601160045260246000fd5b818103818111156125db576125db613d69565b60208082526022908201527f546f6b656e486f6d653a207a65726f20726571756972656420676173206c696d6040820152611a5d60f21b606082015260800190565b60208082526021908201527f546f6b656e486f6d653a206e6f6e2d7a65726f207365636f6e646172792066656040820152606560f81b606082015260800190565b808201808211156125db576125db613d69565b6020808252825182820152828101516001600160a01b039081166040808501919091528401518051821660608501528083015160808501526000929161010085019190606087015160a0870152608087015160e060c0880152805193849052840192600092506101208701905b80841015613eb757845183168252938501936001939093019290850190613e95565b5060a0880151878203601f190160e08901529450613ed581866136d6565b98975050505050505050565b8681526001600160a01b0386811660208301528581166040830152841660608201526080810183905260c060a08201819052600090613ed5908301846136d6565b634e487b7160e01b600052601260045260246000fd5b600082613f4757613f47613f22565b500690565b600060208284031215613f5e57600080fd5b815161187d8161313b565b60ff82811682821603908111156125db576125db613d69565b600181815b80851115613fbd578160001904821115613fa357613fa3613d69565b80851615613fb057918102915b93841c9390800290613f87565b509250929050565b600082613fd4575060016125db565b81613fe1575060006125db565b8160018114613ff757600281146140015761401d565b60019150506125db565b60ff84111561401257614012613d69565b50506001821b6125db565b5060208310610133831016604e8410600b8410161715614040575081810a6125db565b61404a8383613f82565b806000190482111561405e5761405e613d69565b029392505050565b600061187d8383613fc5565b80820281158282048414176125db576125db613d69565b60008261409857614098613f22565b500490565b6000602082840312156140af57600080fd5b8151801515811461187d57600080fd5b600082516140d18184602087016136b2565b9190910192915050565b60208152600061187d60208301846136d656fea264697066735822122027ee2c8edf5a85411d98a486f46b1b0d4271954ef0a1055ad3cd05958c4db41f64736f6c63430008120033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f86cb19ad8405aefa7d09c778215d2cb6ebfb228000000000000000000000000efe9e34988a5054907fc125aef5e85aaaf7f928c0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad180000000000000000000000000000000000000000000000000000000000000012
-----Decoded View---------------
Arg [0] : teleporterRegistryAddress (address): 0xF86Cb19Ad8405AEFa7d09C778215D2Cb6eBfB228
Arg [1] : teleporterManager (address): 0xEfe9e34988A5054907fc125AEf5e85Aaaf7f928C
Arg [2] : tokenAddress_ (address): 0x3F964A0630b429FFB1797f2CE9fd8e5a45C5AD18
Arg [3] : tokenDecimals_ (uint8): 18
-----Encoded View---------------
4 Constructor Arguments found :
Arg [0] : 000000000000000000000000f86cb19ad8405aefa7d09c778215d2cb6ebfb228
Arg [1] : 000000000000000000000000efe9e34988a5054907fc125aef5e85aaaf7f928c
Arg [2] : 0000000000000000000000003f964a0630b429ffb1797f2ce9fd8e5a45c5ad18
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000012
Loading...
Loading
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.