Latest 25 from a total of 375 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Receive Tokens | 38433700 | 39 hrs ago | IN | 0 ETH | 0.00000028 | ||||
| Receive Tokens | 38432890 | 39 hrs ago | IN | 0 ETH | 0.00000028 | ||||
| Receive Tokens | 38154857 | 4 days ago | IN | 0 ETH | 0.00000017 | ||||
| Receive Tokens | 38018146 | 6 days ago | IN | 0 ETH | 0.00000016 | ||||
| Receive Tokens | 37952664 | 7 days ago | IN | 0 ETH | 0.00000018 | ||||
| Swap And Bridge | 37918880 | 7 days ago | IN | 0.0001725 ETH | 0.00000002 | ||||
| Receive Tokens | 37918620 | 7 days ago | IN | 0 ETH | 0.00000017 | ||||
| Receive Tokens | 37456213 | 12 days ago | IN | 0 ETH | 0.00000017 | ||||
| Swap And Bridge | 37456116 | 12 days ago | IN | 0.0001725 ETH | 0.00000002 | ||||
| Receive Tokens | 37433253 | 13 days ago | IN | 0 ETH | 0.00000018 | ||||
| Receive Tokens | 37264899 | 15 days ago | IN | 0 ETH | 0.00000018 | ||||
| Swap And Bridge | 37262792 | 15 days ago | IN | 0.00001495 ETH | 0 | ||||
| Receive Tokens | 37262594 | 15 days ago | IN | 0 ETH | 0.00000016 | ||||
| Receive Tokens | 37261914 | 15 days ago | IN | 0 ETH | 0.00000017 | ||||
| Receive Tokens | 37260323 | 15 days ago | IN | 0 ETH | 0.00000016 | ||||
| Receive Tokens | 37260301 | 15 days ago | IN | 0 ETH | 0.00000017 | ||||
| Receive Tokens | 37260285 | 15 days ago | IN | 0 ETH | 0.00000016 | ||||
| Swap And Bridge | 37259893 | 15 days ago | IN | 0 ETH | 0 | ||||
| Swap And Bridge | 37259597 | 15 days ago | IN | 0.00027978 ETH | 0 | ||||
| Swap And Bridge | 37259564 | 15 days ago | IN | 0.0001725 ETH | 0.00000014 | ||||
| Swap And Bridge | 37259504 | 15 days ago | IN | 0.0000069 ETH | 0 | ||||
| Swap And Bridge | 37259449 | 15 days ago | IN | 0.000069 ETH | 0 | ||||
| Receive Tokens | 37258914 | 15 days ago | IN | 0 ETH | 0.00000017 | ||||
| Swap And Bridge | 37258881 | 15 days ago | IN | 0 ETH | 0 | ||||
| Swap And Bridge | 37258843 | 15 days ago | IN | 0 ETH | 0 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 38527757 | 13 hrs ago | 0.00000936 ETH | ||||
| 38372215 | 2 days ago | 0.00001275 ETH | ||||
| 38336843 | 2 days ago | 0.00006598 ETH | ||||
| 38235467 | 3 days ago | 0.00005 ETH | ||||
| 38007400 | 6 days ago | 0.00006598 ETH | ||||
| 37918880 | 7 days ago | 0.00005 ETH | ||||
| 37769899 | 9 days ago | 0.00002 ETH | ||||
| 37629224 | 10 days ago | 0.0000155 ETH | ||||
| 37555964 | 11 days ago | 0.00005 ETH | ||||
| 37533894 | 12 days ago | 0.00000329 ETH | ||||
| 37456754 | 12 days ago | 0.00005 ETH | ||||
| 37456116 | 12 days ago | 0.00005 ETH | ||||
| 37358376 | 14 days ago | 0.00000426 ETH | ||||
| 37341180 | 14 days ago | 0.00005 ETH | ||||
| 37291176 | 14 days ago | 0.00005 ETH | ||||
| 37288057 | 14 days ago | 0.00000512 ETH | ||||
| 37262792 | 15 days ago | 0.00000282 ETH | ||||
| 37259893 | 15 days ago | 0.0000041 ETH | ||||
| 37259597 | 15 days ago | 0.00012164 ETH | ||||
| 37259564 | 15 days ago | 0.00005 ETH | ||||
| 37259504 | 15 days ago | 0.00000077 ETH | ||||
| 37259449 | 15 days ago | 0.00002 ETH | ||||
| 37258881 | 15 days ago | 0.00000382 ETH | ||||
| 37258843 | 15 days ago | 0.00000382 ETH | ||||
| 37257550 | 15 days ago | 0.00000279 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Bridge
Compiler Version
v0.8.20+commit.a1b79de6
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IBridge, MessengerProtocol} from "./interfaces/IBridge.sol";
import {Router} from "./Router.sol";
import {Messenger} from "./Messenger.sol";
import {MessengerGateway} from "./MessengerGateway.sol";
import {IGasOracle} from "./interfaces/IGasOracle.sol";
import {GasUsage} from "./GasUsage.sol";
import {WormholeMessenger} from "./WormholeMessenger.sol";
import {HashUtils} from "./libraries/HashUtils.sol";
/**
* @title Bridge
* @dev A contract with functions to facilitate bridging tokens across different blockchains.
*/
contract Bridge is GasUsage, Router, MessengerGateway, IBridge {
using SafeERC20 for IERC20;
using HashUtils for bytes32;
uint public immutable override chainId;
mapping(bytes32 messageHash => uint isProcessed) public override processedMessages;
mapping(bytes32 messageHash => uint isSent) public override sentMessages;
// Info about bridges on other chains
mapping(uint chainId => bytes32 bridgeAddress) public override otherBridges;
// Info about tokens on other chains
mapping(uint chainId => mapping(bytes32 tokenAddress => bool isSupported)) public override otherBridgeTokens;
/**
* @dev Emitted when tokens are sent on the source blockchain.
*/
event TokensSent(
uint amount,
bytes32 recipient,
uint destinationChainId,
bytes32 receiveToken,
uint nonce,
MessengerProtocol messenger
);
/**
* @dev Emitted when the tokens are received on the destination blockchain.
*/
event TokensReceived(uint amount, bytes32 recipient, uint nonce, MessengerProtocol messenger, bytes32 message);
/**
* @dev Emitted when this contract receives the bridging fee.
*/
event ReceiveFee(uint bridgeTransactionCost, uint messageTransactionCost);
/**
* @dev Emitted when this contract charged the sender with the tokens for the bridging fee.
*/
event BridgingFeeFromTokens(uint gas);
/**
* @dev Emitted when the contract receives native tokens (e.g. Ether on the Ethereum network) from the admin to
* supply the gas for bridging.
*/
event Received(address sender, uint amount);
constructor(
uint chainId_,
uint chainPrecision_,
Messenger allbridgeMessenger_,
WormholeMessenger wormholeMessenger_,
IGasOracle gasOracle_
) Router(chainPrecision_) MessengerGateway(allbridgeMessenger_, wormholeMessenger_) GasUsage(gasOracle_) {
chainId = chainId_;
}
/**
* @notice Initiates a swap and bridge process of a given token for a token on another blockchain.
* @dev This function is used to initiate a cross-chain transfer. The specified amount of token is first transferred
* to the pool on the current chain, and then an event `TokensSent` is emitted to signal that tokens have been sent
* on the source chain. See the function `receiveTokens`.
* The bridging fee required for the cross-chain transfer can be paid in two ways:
* - by sending the required amount of native gas token along with the transaction
* (See `getTransactionCost` in the `GasUsage` contract and `getMessageCost` in the `MessengerGateway` contract).
* - by setting the parameter `feeTokenAmount` with the bridging fee amount in the source tokens
* (See the function `getBridgingCostInTokens`).
* @param token The token to be swapped.
* @param amount The amount of tokens to be swapped (including `feeTokenAmount`).
* @param destinationChainId The ID of the destination chain.
* @param receiveToken The token to receive in exchange for the swapped token.
* @param nonce An identifier that is used to ensure that each transfer is unique and can only be processed once.
* @param messenger The chosen way of delivering the message across chains.
* @param feeTokenAmount The amount of tokens to be deducted from the transferred amount as a bridging fee.
*
*/
function swapAndBridge(
bytes32 token,
uint amount,
bytes32 recipient,
uint destinationChainId,
bytes32 receiveToken,
uint nonce,
MessengerProtocol messenger,
uint feeTokenAmount
) external payable override whenCanSwap {
require(amount > feeTokenAmount, "Bridge: amount too low for fee");
require(recipient != 0, "Bridge: bridge to the zero address");
uint bridgingFee = msg.value + _convertBridgingFeeInTokensToNativeToken(msg.sender, token, feeTokenAmount);
uint amountAfterFee = amount - feeTokenAmount;
uint vUsdAmount = _sendAndSwapToVUsd(token, msg.sender, amountAfterFee);
_sendTokens(vUsdAmount, recipient, destinationChainId, receiveToken, nonce, messenger, bridgingFee);
}
/**
* @notice Completes the bridging process by sending the tokens on the destination chain to the recipient.
* @dev This function is called only after a bridging has been initiated by a user
* through the `swapAndBridge` function on the source chain.
* @param amount The amount of tokens being bridged.
* @param recipient The recipient address for the bridged tokens.
* @param sourceChainId The ID of the source chain.
* @param receiveToken The address of the token being received.
* @param nonce A unique nonce for the bridging transaction.
* @param messenger The protocol used to relay the message.
* @param receiveAmountMin The minimum amount of receiveToken required to be received.
*/
function receiveTokens(
uint amount,
bytes32 recipient,
uint sourceChainId,
bytes32 receiveToken,
uint nonce,
MessengerProtocol messenger,
uint receiveAmountMin
) external payable override whenCanSwap {
require(otherBridges[sourceChainId] != bytes32(0), "Bridge: source not registered");
bytes32 messageWithSender = this
.hashMessage(amount, recipient, sourceChainId, chainId, receiveToken, nonce, messenger)
.hashWithSender(otherBridges[sourceChainId]);
require(processedMessages[messageWithSender] == 0, "Bridge: message processed");
// mark the transfer as received on the destination chain
processedMessages[messageWithSender] = 1;
// check if tokens has been sent on the source chain
require(this.hasReceivedMessage(messageWithSender, messenger), "Bridge: no message");
uint receiveAmount = _receiveAndSwapFromVUsd(
receiveToken,
address(uint160(uint(recipient))),
amount,
receiveAmountMin
);
// pass extra gas to the recipient
if (msg.value > 0) {
// ignore if passing extra gas failed
// solc-ignore-next-line unused-call-retval
payable(address(uint160(uint(recipient)))).call{value: msg.value}("");
}
emit TokensReceived(receiveAmount, recipient, nonce, messenger, messageWithSender);
}
/**
* @notice Allows the admin to add new supported chain destination.
* @dev Registers the address of a bridge deployed on a different chain.
* @param chainId_ The chain ID of the bridge to register.
* @param bridgeAddress The address of the bridge contract to register.
*/
function registerBridge(uint chainId_, bytes32 bridgeAddress) external override onlyOwner {
otherBridges[chainId_] = bridgeAddress;
}
/**
* @notice Allows the admin to add a new supported destination token.
* @dev Adds the address of a token on another chain to the list of supported tokens for the specified chain.
* @param chainId_ The chain ID where the token is deployed.
* @param tokenAddress The address of the token to add as a supported token.
*/
function addBridgeToken(uint chainId_, bytes32 tokenAddress) external override onlyOwner {
otherBridgeTokens[chainId_][tokenAddress] = true;
}
/**
* @notice Allows the admin to remove support for a destination token.
* @dev Removes the address of a token on another chain from the list of supported tokens for the specified chain.
* @param chainId_ The chain ID where the token is deployed.
* @param tokenAddress The address of the token to remove from the list of supported tokens.
*/
function removeBridgeToken(uint chainId_, bytes32 tokenAddress) external override onlyOwner {
otherBridgeTokens[chainId_][tokenAddress] = false;
}
/**
* @notice Allows the admin to withdraw the bridging fee collected in native tokens.
*/
function withdrawGasTokens(uint amount) external override onlyOwner {
payable(msg.sender).transfer(amount);
}
/**
* @notice Allows the admin to withdraw the bridging fee collected in tokens.
* @param token The address of the token contract.
*/
function withdrawBridgingFeeInTokens(IERC20 token) external onlyOwner {
uint toWithdraw = token.balanceOf(address(this));
if (toWithdraw > 0) {
token.safeTransfer(msg.sender, toWithdraw);
}
}
/**
* @dev Calculates the amount of bridging fee nominated in a given token, which includes:
* - the gas cost of making the receive transaction on the destination chain,
* - the gas cost of sending the message to the destination chain using the specified messenger protocol.
* @param destinationChainId The ID of the destination chain.
* @param messenger The chosen way of delivering the message across chains.
* @param tokenAddress The address of the token contract on the source chain.
* @return The total price of bridging, with the precision according to the token's `decimals()` value.
*/
function getBridgingCostInTokens(
uint destinationChainId,
MessengerProtocol messenger,
address tokenAddress
) external view override returns (uint) {
return
gasOracle.getTransactionGasCostInUSD(
destinationChainId,
gasUsage[destinationChainId] + getMessageGasUsage(destinationChainId, messenger)
) / fromGasOracleScalingFactor[tokenAddress];
}
/**
* @dev Produces a hash of transfer parameters, which is used as a message to the bridge on the destination chain
* to notify that the tokens on the source chain has been sent.
* @param amount The amount of tokens being transferred.
* @param recipient The address of the recipient on the destination chain.
* @param sourceChainId The ID of the source chain.
* @param destinationChainId The ID of the destination chain.
* @param receiveToken The token being received on the destination chain.
* @param nonce The unique nonce.
* @param messenger The chosen way of delivering the message across chains.
*/
function hashMessage(
uint amount,
bytes32 recipient,
uint sourceChainId,
uint destinationChainId,
bytes32 receiveToken,
uint nonce,
MessengerProtocol messenger
) external pure override returns (bytes32) {
return
keccak256(abi.encodePacked(amount, recipient, sourceChainId, receiveToken, nonce, messenger))
.replaceChainBytes(uint8(sourceChainId), uint8(destinationChainId));
}
function _sendTokens(
uint amount,
bytes32 recipient,
uint destinationChainId,
bytes32 receiveToken,
uint nonce,
MessengerProtocol messenger,
uint bridgingFee
) internal {
require(destinationChainId != chainId, "Bridge: wrong destination chain");
require(otherBridgeTokens[destinationChainId][receiveToken], "Bridge: unknown chain or token");
bytes32 message = this.hashMessage(
amount,
recipient,
chainId,
destinationChainId,
receiveToken,
nonce,
messenger
);
require(sentMessages[message] == 0, "Bridge: tokens already sent");
// mark the transfer as sent on the source chain
sentMessages[message] = 1;
uint bridgeTransactionCost = this.getTransactionCost(destinationChainId);
uint messageTransactionCost = _sendMessage(message, messenger);
emit ReceiveFee(bridgeTransactionCost, messageTransactionCost);
unchecked {
require(bridgingFee >= bridgeTransactionCost + messageTransactionCost, "Bridge: not enough fee");
}
emit TokensSent(amount, recipient, destinationChainId, receiveToken, nonce, messenger);
}
/**
* @dev Charges the bridging fee in tokens and calculates the amount of native tokens that correspond
* to the charged fee using the current exchange rate.
* @param user The address of the user who is paying the bridging fee
* @param tokenAddress The address of the token used to pay the bridging fee
* @param feeTokenAmount The amount of tokens to pay as the bridging fee
* @return bridging fee amount in the native tokens (e.g. in wei for Ethereum)
*/
function _convertBridgingFeeInTokensToNativeToken(
address user,
bytes32 tokenAddress,
uint feeTokenAmount
) internal returns (uint) {
if (feeTokenAmount == 0) return 0;
address tokenAddress_ = address(uint160(uint(tokenAddress)));
IERC20 token = IERC20(tokenAddress_);
token.safeTransferFrom(user, address(this), feeTokenAmount);
uint fee = (bridgingFeeConversionScalingFactor[tokenAddress_] * feeTokenAmount) / gasOracle.price(chainId);
emit BridgingFeeFromTokens(fee);
return fee;
}
fallback() external payable {
revert("Unsupported");
}
receive() external payable {
emit Received(msg.sender, msg.value);
}
}// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
import "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol";
library ExecutorOptions {
using CalldataBytesLib for bytes;
uint8 internal constant WORKER_ID = 1;
uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;
uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;
uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;
uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;
uint8 internal constant OPTION_TYPE_LZREAD = 5;
error Executor_InvalidLzReceiveOption();
error Executor_InvalidNativeDropOption();
error Executor_InvalidLzComposeOption();
error Executor_InvalidLzReadOption();
/// @dev decode the next executor option from the options starting from the specified cursor
/// @param _options [executor_id][executor_option][executor_id][executor_option]...
/// executor_option = [option_size][option_type][option]
/// option_size = len(option_type) + len(option)
/// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes
/// @param _cursor the cursor to start decoding from
/// @return optionType the type of the option
/// @return option the option of the executor
/// @return cursor the cursor to start decoding the next executor option
function nextExecutorOption(
bytes calldata _options,
uint256 _cursor
) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {
unchecked {
// skip worker id
cursor = _cursor + 1;
// read option size
uint16 size = _options.toU16(cursor);
cursor += 2;
// read option type
optionType = _options.toU8(cursor);
// startCursor and endCursor are used to slice the option from _options
uint256 startCursor = cursor + 1; // skip option type
uint256 endCursor = cursor + size;
option = _options[startCursor:endCursor];
cursor += size;
}
}
function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {
if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();
gas = _option.toU128(0);
value = _option.length == 32 ? _option.toU128(16) : 0;
}
function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {
if (_option.length != 48) revert Executor_InvalidNativeDropOption();
amount = _option.toU128(0);
receiver = _option.toB32(16);
}
function decodeLzComposeOption(
bytes calldata _option
) internal pure returns (uint16 index, uint128 gas, uint128 value) {
if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();
index = _option.toU16(0);
gas = _option.toU128(2);
value = _option.length == 34 ? _option.toU128(18) : 0;
}
function decodeLzReadOption(
bytes calldata _option
) internal pure returns (uint128 gas, uint32 calldataSize, uint128 value) {
if (_option.length != 20 && _option.length != 36) revert Executor_InvalidLzReadOption();
gas = _option.toU128(0);
calldataSize = _option.toU32(16);
value = _option.length == 36 ? _option.toU128(20) : 0;
}
function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {
return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);
}
function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {
return abi.encodePacked(_amount, _receiver);
}
function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {
return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);
}
function encodeLzReadOption(
uint128 _gas,
uint32 _calldataSize,
uint128 _value
) internal pure returns (bytes memory) {
return _value == 0 ? abi.encodePacked(_gas, _calldataSize) : abi.encodePacked(_gas, _calldataSize, _value);
}
}// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
import { BytesLib } from "solidity-bytes-utils/contracts/BytesLib.sol";
import { BitMap256 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/BitMaps.sol";
import { CalldataBytesLib } from "@layerzerolabs/lz-evm-protocol-v2/contracts/libs/CalldataBytesLib.sol";
library DVNOptions {
using CalldataBytesLib for bytes;
using BytesLib for bytes;
uint8 internal constant WORKER_ID = 2;
uint8 internal constant OPTION_TYPE_PRECRIME = 1;
error DVN_InvalidDVNIdx();
error DVN_InvalidDVNOptions(uint256 cursor);
/// @dev group dvn options by its idx
/// @param _options [dvn_id][dvn_option][dvn_id][dvn_option]...
/// dvn_option = [option_size][dvn_idx][option_type][option]
/// option_size = len(dvn_idx) + len(option_type) + len(option)
/// dvn_id: uint8, dvn_idx: uint8, option_size: uint16, option_type: uint8, option: bytes
/// @return dvnOptions the grouped options, still share the same format of _options
/// @return dvnIndices the dvn indices
function groupDVNOptionsByIdx(
bytes memory _options
) internal pure returns (bytes[] memory dvnOptions, uint8[] memory dvnIndices) {
if (_options.length == 0) return (dvnOptions, dvnIndices);
uint8 numDVNs = getNumDVNs(_options);
// if there is only 1 dvn, we can just return the whole options
if (numDVNs == 1) {
dvnOptions = new bytes[](1);
dvnOptions[0] = _options;
dvnIndices = new uint8[](1);
dvnIndices[0] = _options.toUint8(3); // dvn idx
return (dvnOptions, dvnIndices);
}
// otherwise, we need to group the options by dvn_idx
dvnIndices = new uint8[](numDVNs);
dvnOptions = new bytes[](numDVNs);
unchecked {
uint256 cursor = 0;
uint256 start = 0;
uint8 lastDVNIdx = 255; // 255 is an invalid dvn_idx
while (cursor < _options.length) {
++cursor; // skip worker_id
// optionLength asserted in getNumDVNs (skip check)
uint16 optionLength = _options.toUint16(cursor);
cursor += 2;
// dvnIdx asserted in getNumDVNs (skip check)
uint8 dvnIdx = _options.toUint8(cursor);
// dvnIdx must equal to the lastDVNIdx for the first option
// so it is always skipped in the first option
// this operation slices out options whenever the scan finds a different lastDVNIdx
if (lastDVNIdx == 255) {
lastDVNIdx = dvnIdx;
} else if (dvnIdx != lastDVNIdx) {
uint256 len = cursor - start - 3; // 3 is for worker_id and option_length
bytes memory opt = _options.slice(start, len);
_insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, opt);
// reset the start and lastDVNIdx
start += len;
lastDVNIdx = dvnIdx;
}
cursor += optionLength;
}
// skip check the cursor here because the cursor is asserted in getNumDVNs
// if we have reached the end of the options, we need to process the last dvn
uint256 size = cursor - start;
bytes memory op = _options.slice(start, size);
_insertDVNOptions(dvnOptions, dvnIndices, lastDVNIdx, op);
// revert dvnIndices to start from 0
for (uint8 i = 0; i < numDVNs; ++i) {
--dvnIndices[i];
}
}
}
function _insertDVNOptions(
bytes[] memory _dvnOptions,
uint8[] memory _dvnIndices,
uint8 _dvnIdx,
bytes memory _newOptions
) internal pure {
// dvnIdx starts from 0 but default value of dvnIndices is 0,
// so we tell if the slot is empty by adding 1 to dvnIdx
if (_dvnIdx == 255) revert DVN_InvalidDVNIdx();
uint8 dvnIdxAdj = _dvnIdx + 1;
for (uint256 j = 0; j < _dvnIndices.length; ++j) {
uint8 index = _dvnIndices[j];
if (dvnIdxAdj == index) {
_dvnOptions[j] = abi.encodePacked(_dvnOptions[j], _newOptions);
break;
} else if (index == 0) {
// empty slot, that means it is the first time we see this dvn
_dvnIndices[j] = dvnIdxAdj;
_dvnOptions[j] = _newOptions;
break;
}
}
}
/// @dev get the number of unique dvns
/// @param _options the format is the same as groupDVNOptionsByIdx
function getNumDVNs(bytes memory _options) internal pure returns (uint8 numDVNs) {
uint256 cursor = 0;
BitMap256 bitmap;
// find number of unique dvn_idx
unchecked {
while (cursor < _options.length) {
++cursor; // skip worker_id
uint16 optionLength = _options.toUint16(cursor);
cursor += 2;
if (optionLength < 2) revert DVN_InvalidDVNOptions(cursor); // at least 1 byte for dvn_idx and 1 byte for option_type
uint8 dvnIdx = _options.toUint8(cursor);
// if dvnIdx is not set, increment numDVNs
// max num of dvns is 255, 255 is an invalid dvn_idx
// The order of the dvnIdx is not required to be sequential, as enforcing the order may weaken
// the composability of the options. e.g. if we refrain from enforcing the order, an OApp that has
// already enforced certain options can append additional options to the end of the enforced
// ones without restrictions.
if (dvnIdx == 255) revert DVN_InvalidDVNIdx();
if (!bitmap.get(dvnIdx)) {
++numDVNs;
bitmap = bitmap.set(dvnIdx);
}
cursor += optionLength;
}
}
if (cursor != _options.length) revert DVN_InvalidDVNOptions(cursor);
}
/// @dev decode the next dvn option from _options starting from the specified cursor
/// @param _options the format is the same as groupDVNOptionsByIdx
/// @param _cursor the cursor to start decoding
/// @return optionType the type of the option
/// @return option the option
/// @return cursor the cursor to start decoding the next option
function nextDVNOption(
bytes calldata _options,
uint256 _cursor
) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {
unchecked {
// skip worker id
cursor = _cursor + 1;
// read option size
uint16 size = _options.toU16(cursor);
cursor += 2;
// read option type
optionType = _options.toU8(cursor + 1); // skip dvn_idx
// startCursor and endCursor are used to slice the option from _options
uint256 startCursor = cursor + 2; // skip option type and dvn_idx
uint256 endCursor = cursor + size;
option = _options[startCursor:endCursor];
cursor += size;
}
}
}// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { ExecutorOptions } from "@layerzerolabs/lz-evm-protocol-v2/contracts/messagelib/libs/ExecutorOptions.sol";
import { DVNOptions } from "./DVNOptions.sol";
library UlnOptions {
using SafeCast for uint256;
uint16 internal constant TYPE_1 = 1; // legacy options type 1
uint16 internal constant TYPE_2 = 2; // legacy options type 2
uint16 internal constant TYPE_3 = 3;
error LZ_ULN_InvalidWorkerOptions(uint256 cursor);
error LZ_ULN_InvalidWorkerId(uint8 workerId);
error LZ_ULN_InvalidLegacyType1Option();
error LZ_ULN_InvalidLegacyType2Option();
error LZ_ULN_UnsupportedOptionType(uint16 optionType);
/// @dev decode the options into executorOptions and dvnOptions
/// @param _options the options can be either legacy options (type 1 or 2) or type 3 options
/// @return executorOptions the executor options, share the same format of type 3 options
/// @return dvnOptions the dvn options, share the same format of type 3 options
function decode(
bytes calldata _options
) internal pure returns (bytes memory executorOptions, bytes memory dvnOptions) {
// at least 2 bytes for the option type, but can have no options
if (_options.length < 2) revert LZ_ULN_InvalidWorkerOptions(0);
uint16 optionsType = uint16(bytes2(_options[0:2]));
uint256 cursor = 2;
// type3 options: [worker_option][worker_option]...
// worker_option: [worker_id][option_size][option]
// worker_id: uint8, option_size: uint16, option: bytes
if (optionsType == TYPE_3) {
unchecked {
uint256 start = cursor;
uint8 lastWorkerId; // worker_id starts from 1, so 0 is an invalid worker_id
// heuristic: we assume that the options are mostly EXECUTOR options only
// checking the workerID can reduce gas usage for most cases
while (cursor < _options.length) {
uint8 workerId = uint8(bytes1(_options[cursor:cursor + 1]));
if (workerId == 0) revert LZ_ULN_InvalidWorkerId(0);
// workerId must equal to the lastWorkerId for the first option
// so it is always skipped in the first option
// this operation slices out options whenever the the scan finds a different workerId
if (lastWorkerId == 0) {
lastWorkerId = workerId;
} else if (workerId != lastWorkerId) {
bytes calldata op = _options[start:cursor]; // slice out the last worker's options
(executorOptions, dvnOptions) = _insertWorkerOptions(
executorOptions,
dvnOptions,
lastWorkerId,
op
);
// reset the start cursor and lastWorkerId
start = cursor;
lastWorkerId = workerId;
}
++cursor; // for workerId
uint16 size = uint16(bytes2(_options[cursor:cursor + 2]));
if (size == 0) revert LZ_ULN_InvalidWorkerOptions(cursor);
cursor += size + 2;
}
// the options length must be the same as the cursor at the end
if (cursor != _options.length) revert LZ_ULN_InvalidWorkerOptions(cursor);
// if we have reached the end of the options and the options are not empty
// we need to process the last worker's options
if (_options.length > 2) {
bytes calldata op = _options[start:cursor];
(executorOptions, dvnOptions) = _insertWorkerOptions(executorOptions, dvnOptions, lastWorkerId, op);
}
}
} else {
executorOptions = decodeLegacyOptions(optionsType, _options);
}
}
function _insertWorkerOptions(
bytes memory _executorOptions,
bytes memory _dvnOptions,
uint8 _workerId,
bytes calldata _newOptions
) private pure returns (bytes memory, bytes memory) {
if (_workerId == ExecutorOptions.WORKER_ID) {
_executorOptions = _executorOptions.length == 0
? _newOptions
: abi.encodePacked(_executorOptions, _newOptions);
} else if (_workerId == DVNOptions.WORKER_ID) {
_dvnOptions = _dvnOptions.length == 0 ? _newOptions : abi.encodePacked(_dvnOptions, _newOptions);
} else {
revert LZ_ULN_InvalidWorkerId(_workerId);
}
return (_executorOptions, _dvnOptions);
}
/// @dev decode the legacy options (type 1 or 2) into executorOptions
/// @param _optionType the legacy option type
/// @param _options the legacy options, which still has the option type in the first 2 bytes
/// @return executorOptions the executor options, share the same format of type 3 options
/// Data format:
/// legacy type 1: [extraGas]
/// legacy type 2: [extraGas][dstNativeAmt][dstNativeAddress]
/// extraGas: uint256, dstNativeAmt: uint256, dstNativeAddress: bytes
function decodeLegacyOptions(
uint16 _optionType,
bytes calldata _options
) internal pure returns (bytes memory executorOptions) {
if (_optionType == TYPE_1) {
if (_options.length != 34) revert LZ_ULN_InvalidLegacyType1Option();
// execution gas
uint128 executionGas = uint256(bytes32(_options[2:2 + 32])).toUint128();
// dont use the encode function in the ExecutorOptions lib for saving gas by calling abi.encodePacked once
// the result is a lzReceive option: [executor_id][option_size][option_type][execution_gas]
// option_type: uint8, execution_gas: uint128
// option_size = len(option_type) + len(execution_gas) = 1 + 16 = 17
executorOptions = abi.encodePacked(
ExecutorOptions.WORKER_ID,
uint16(17), // 16 + 1, 16 for option_length, + 1 for option_type
ExecutorOptions.OPTION_TYPE_LZRECEIVE,
executionGas
);
} else if (_optionType == TYPE_2) {
// receiver size <= 32
if (_options.length <= 66 || _options.length > 98) revert LZ_ULN_InvalidLegacyType2Option();
// execution gas
uint128 executionGas = uint256(bytes32(_options[2:2 + 32])).toUint128();
// nativeDrop (amount + receiver)
uint128 amount = uint256(bytes32(_options[34:34 + 32])).toUint128(); // offset 2 + 32
bytes32 receiver;
unchecked {
uint256 receiverLen = _options.length - 66; // offset 2 + 32 + 32
receiver = bytes32(_options[66:]);
receiver = receiver >> (8 * (32 - receiverLen)); // padding 0 to the left
}
// dont use the encode function in the ExecutorOptions lib for saving gas by calling abi.encodePacked once
// the result has one lzReceive option and one nativeDrop option:
// [executor_id][lzReceive_option_size][option_type][execution_gas] +
// [executor_id][nativeDrop_option_size][option_type][nativeDrop_amount][receiver]
// option_type: uint8, execution_gas: uint128, nativeDrop_amount: uint128, receiver: bytes32
// lzReceive_option_size = len(option_type) + len(execution_gas) = 1 + 16 = 17
// nativeDrop_option_size = len(option_type) + len(nativeDrop_amount) + len(receiver) = 1 + 16 + 32 = 49
executorOptions = abi.encodePacked(
ExecutorOptions.WORKER_ID,
uint16(17), // 16 + 1, 16 for option_length, + 1 for option_type
ExecutorOptions.OPTION_TYPE_LZRECEIVE,
executionGas,
ExecutorOptions.WORKER_ID,
uint16(49), // 48 + 1, 32 + 16 for option_length, + 1 for option_type
ExecutorOptions.OPTION_TYPE_NATIVE_DROP,
amount,
receiver
);
} else {
revert LZ_ULN_UnsupportedOptionType(_optionType);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
import { IMessageLibManager } from "./IMessageLibManager.sol";
import { IMessagingComposer } from "./IMessagingComposer.sol";
import { IMessagingChannel } from "./IMessagingChannel.sol";
import { IMessagingContext } from "./IMessagingContext.sol";
struct MessagingParams {
uint32 dstEid;
bytes32 receiver;
bytes message;
bytes options;
bool payInLzToken;
}
struct MessagingReceipt {
bytes32 guid;
uint64 nonce;
MessagingFee fee;
}
struct MessagingFee {
uint256 nativeFee;
uint256 lzTokenFee;
}
struct Origin {
uint32 srcEid;
bytes32 sender;
uint64 nonce;
}
interface ILayerZeroEndpointV2 is IMessageLibManager, IMessagingComposer, IMessagingChannel, IMessagingContext {
event PacketSent(bytes encodedPayload, bytes options, address sendLibrary);
event PacketVerified(Origin origin, address receiver, bytes32 payloadHash);
event PacketDelivered(Origin origin, address receiver);
event LzReceiveAlert(
address indexed receiver,
address indexed executor,
Origin origin,
bytes32 guid,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
event LzTokenSet(address token);
event DelegateSet(address sender, address delegate);
function quote(MessagingParams calldata _params, address _sender) external view returns (MessagingFee memory);
function send(
MessagingParams calldata _params,
address _refundAddress
) external payable returns (MessagingReceipt memory);
function verify(Origin calldata _origin, address _receiver, bytes32 _payloadHash) external;
function verifiable(Origin calldata _origin, address _receiver) external view returns (bool);
function initializable(Origin calldata _origin, address _receiver) external view returns (bool);
function lzReceive(
Origin calldata _origin,
address _receiver,
bytes32 _guid,
bytes calldata _message,
bytes calldata _extraData
) external payable;
// oapp can burn messages partially by calling this function with its own business logic if messages are verified in order
function clear(address _oapp, Origin calldata _origin, bytes32 _guid, bytes calldata _message) external;
function setLzToken(address _lzToken) external;
function lzToken() external view returns (address);
function nativeToken() external view returns (address);
function setDelegate(address _delegate) external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
struct SetConfigParam {
uint32 eid;
uint32 configType;
bytes config;
}
interface IMessageLibManager {
struct Timeout {
address lib;
uint256 expiry;
}
event LibraryRegistered(address newLib);
event DefaultSendLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibrarySet(uint32 eid, address newLib);
event DefaultReceiveLibraryTimeoutSet(uint32 eid, address oldLib, uint256 expiry);
event SendLibrarySet(address sender, uint32 eid, address newLib);
event ReceiveLibrarySet(address receiver, uint32 eid, address newLib);
event ReceiveLibraryTimeoutSet(address receiver, uint32 eid, address oldLib, uint256 timeout);
function registerLibrary(address _lib) external;
function isRegisteredLibrary(address _lib) external view returns (bool);
function getRegisteredLibraries() external view returns (address[] memory);
function setDefaultSendLibrary(uint32 _eid, address _newLib) external;
function defaultSendLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibrary(uint32 _eid, address _newLib, uint256 _gracePeriod) external;
function defaultReceiveLibrary(uint32 _eid) external view returns (address);
function setDefaultReceiveLibraryTimeout(uint32 _eid, address _lib, uint256 _expiry) external;
function defaultReceiveLibraryTimeout(uint32 _eid) external view returns (address lib, uint256 expiry);
function isSupportedEid(uint32 _eid) external view returns (bool);
function isValidReceiveLibrary(address _receiver, uint32 _eid, address _lib) external view returns (bool);
/// ------------------- OApp interfaces -------------------
function setSendLibrary(address _oapp, uint32 _eid, address _newLib) external;
function getSendLibrary(address _sender, uint32 _eid) external view returns (address lib);
function isDefaultSendLibrary(address _sender, uint32 _eid) external view returns (bool);
function setReceiveLibrary(address _oapp, uint32 _eid, address _newLib, uint256 _gracePeriod) external;
function getReceiveLibrary(address _receiver, uint32 _eid) external view returns (address lib, bool isDefault);
function setReceiveLibraryTimeout(address _oapp, uint32 _eid, address _lib, uint256 _expiry) external;
function receiveLibraryTimeout(address _receiver, uint32 _eid) external view returns (address lib, uint256 expiry);
function setConfig(address _oapp, address _lib, SetConfigParam[] calldata _params) external;
function getConfig(
address _oapp,
address _lib,
uint32 _eid,
uint32 _configType
) external view returns (bytes memory config);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingChannel {
event InboundNonceSkipped(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce);
event PacketNilified(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
event PacketBurnt(uint32 srcEid, bytes32 sender, address receiver, uint64 nonce, bytes32 payloadHash);
function eid() external view returns (uint32);
// this is an emergency function if a message cannot be verified for some reasons
// required to provide _nextNonce to avoid race condition
function skip(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce) external;
function nilify(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function burn(address _oapp, uint32 _srcEid, bytes32 _sender, uint64 _nonce, bytes32 _payloadHash) external;
function nextGuid(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (bytes32);
function inboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
function outboundNonce(address _sender, uint32 _dstEid, bytes32 _receiver) external view returns (uint64);
function inboundPayloadHash(
address _receiver,
uint32 _srcEid,
bytes32 _sender,
uint64 _nonce
) external view returns (bytes32);
function lazyInboundNonce(address _receiver, uint32 _srcEid, bytes32 _sender) external view returns (uint64);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingComposer {
event ComposeSent(address from, address to, bytes32 guid, uint16 index, bytes message);
event ComposeDelivered(address from, address to, bytes32 guid, uint16 index);
event LzComposeAlert(
address indexed from,
address indexed to,
address indexed executor,
bytes32 guid,
uint16 index,
uint256 gas,
uint256 value,
bytes message,
bytes extraData,
bytes reason
);
function composeQueue(
address _from,
address _to,
bytes32 _guid,
uint16 _index
) external view returns (bytes32 messageHash);
function sendCompose(address _to, bytes32 _guid, uint16 _index, bytes calldata _message) external;
function lzCompose(
address _from,
address _to,
bytes32 _guid,
uint16 _index,
bytes calldata _message,
bytes calldata _extraData
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
interface IMessagingContext {
function isSendingMessage() external view returns (bool);
function getSendContext() external view returns (uint32 dstEid, address sender);
}// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
library CalldataBytesLib {
function toU8(bytes calldata _bytes, uint256 _start) internal pure returns (uint8) {
return uint8(_bytes[_start]);
}
function toU16(bytes calldata _bytes, uint256 _start) internal pure returns (uint16) {
unchecked {
uint256 end = _start + 2;
return uint16(bytes2(_bytes[_start:end]));
}
}
function toU32(bytes calldata _bytes, uint256 _start) internal pure returns (uint32) {
unchecked {
uint256 end = _start + 4;
return uint32(bytes4(_bytes[_start:end]));
}
}
function toU64(bytes calldata _bytes, uint256 _start) internal pure returns (uint64) {
unchecked {
uint256 end = _start + 8;
return uint64(bytes8(_bytes[_start:end]));
}
}
function toU128(bytes calldata _bytes, uint256 _start) internal pure returns (uint128) {
unchecked {
uint256 end = _start + 16;
return uint128(bytes16(_bytes[_start:end]));
}
}
function toU256(bytes calldata _bytes, uint256 _start) internal pure returns (uint256) {
unchecked {
uint256 end = _start + 32;
return uint256(bytes32(_bytes[_start:end]));
}
}
function toAddr(bytes calldata _bytes, uint256 _start) internal pure returns (address) {
unchecked {
uint256 end = _start + 20;
return address(bytes20(_bytes[_start:end]));
}
}
function toB32(bytes calldata _bytes, uint256 _start) internal pure returns (bytes32) {
unchecked {
uint256 end = _start + 32;
return bytes32(_bytes[_start:end]);
}
}
}// SPDX-License-Identifier: MIT
// modified from https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/BitMaps.sol
pragma solidity ^0.8.20;
type BitMap256 is uint256;
using BitMaps for BitMap256 global;
library BitMaps {
/**
* @dev Returns whether the bit at `index` is set.
*/
function get(BitMap256 bitmap, uint8 index) internal pure returns (bool) {
uint256 mask = 1 << index;
return BitMap256.unwrap(bitmap) & mask != 0;
}
/**
* @dev Sets the bit at `index`.
*/
function set(BitMap256 bitmap, uint8 index) internal pure returns (BitMap256) {
uint256 mask = 1 << index;
return BitMap256.wrap(BitMap256.unwrap(bitmap) | mask);
}
}// SPDX-License-Identifier: LZBL-1.2
pragma solidity ^0.8.20;
import { CalldataBytesLib } from "../../libs/CalldataBytesLib.sol";
library ExecutorOptions {
using CalldataBytesLib for bytes;
uint8 internal constant WORKER_ID = 1;
uint8 internal constant OPTION_TYPE_LZRECEIVE = 1;
uint8 internal constant OPTION_TYPE_NATIVE_DROP = 2;
uint8 internal constant OPTION_TYPE_LZCOMPOSE = 3;
uint8 internal constant OPTION_TYPE_ORDERED_EXECUTION = 4;
error Executor_InvalidLzReceiveOption();
error Executor_InvalidNativeDropOption();
error Executor_InvalidLzComposeOption();
/// @dev decode the next executor option from the options starting from the specified cursor
/// @param _options [executor_id][executor_option][executor_id][executor_option]...
/// executor_option = [option_size][option_type][option]
/// option_size = len(option_type) + len(option)
/// executor_id: uint8, option_size: uint16, option_type: uint8, option: bytes
/// @param _cursor the cursor to start decoding from
/// @return optionType the type of the option
/// @return option the option of the executor
/// @return cursor the cursor to start decoding the next executor option
function nextExecutorOption(
bytes calldata _options,
uint256 _cursor
) internal pure returns (uint8 optionType, bytes calldata option, uint256 cursor) {
unchecked {
// skip worker id
cursor = _cursor + 1;
// read option size
uint16 size = _options.toU16(cursor);
cursor += 2;
// read option type
optionType = _options.toU8(cursor);
// startCursor and endCursor are used to slice the option from _options
uint256 startCursor = cursor + 1; // skip option type
uint256 endCursor = cursor + size;
option = _options[startCursor:endCursor];
cursor += size;
}
}
function decodeLzReceiveOption(bytes calldata _option) internal pure returns (uint128 gas, uint128 value) {
if (_option.length != 16 && _option.length != 32) revert Executor_InvalidLzReceiveOption();
gas = _option.toU128(0);
value = _option.length == 32 ? _option.toU128(16) : 0;
}
function decodeNativeDropOption(bytes calldata _option) internal pure returns (uint128 amount, bytes32 receiver) {
if (_option.length != 48) revert Executor_InvalidNativeDropOption();
amount = _option.toU128(0);
receiver = _option.toB32(16);
}
function decodeLzComposeOption(
bytes calldata _option
) internal pure returns (uint16 index, uint128 gas, uint128 value) {
if (_option.length != 18 && _option.length != 34) revert Executor_InvalidLzComposeOption();
index = _option.toU16(0);
gas = _option.toU128(2);
value = _option.length == 34 ? _option.toU128(18) : 0;
}
function encodeLzReceiveOption(uint128 _gas, uint128 _value) internal pure returns (bytes memory) {
return _value == 0 ? abi.encodePacked(_gas) : abi.encodePacked(_gas, _value);
}
function encodeNativeDropOption(uint128 _amount, bytes32 _receiver) internal pure returns (bytes memory) {
return abi.encodePacked(_amount, _receiver);
}
function encodeLzComposeOption(uint16 _index, uint128 _gas, uint128 _value) internal pure returns (bytes memory) {
return _value == 0 ? abi.encodePacked(_index, _gas) : abi.encodePacked(_index, _gas, _value);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { ILayerZeroEndpointV2 } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
/**
* @title IOAppCore
*/
interface IOAppCore {
// Custom error messages
error OnlyPeer(uint32 eid, bytes32 sender);
error NoPeer(uint32 eid);
error InvalidEndpointCall();
error InvalidDelegate();
// Event emitted when a peer (OApp) is set for a corresponding endpoint
event PeerSet(uint32 eid, bytes32 peer);
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*/
function oAppVersion() external view returns (uint64 senderVersion, uint64 receiverVersion);
/**
* @notice Retrieves the LayerZero endpoint associated with the OApp.
* @return iEndpoint The LayerZero endpoint as an interface.
*/
function endpoint() external view returns (ILayerZeroEndpointV2 iEndpoint);
/**
* @notice Retrieves the peer (OApp) associated with a corresponding endpoint.
* @param _eid The endpoint ID.
* @return peer The peer address (OApp instance) associated with the corresponding endpoint.
*/
function peers(uint32 _eid) external view returns (bytes32 peer);
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*/
function setPeer(uint32 _eid, bytes32 _peer) external;
/**
* @notice Sets the delegate address for the OApp Core.
* @param _delegate The address of the delegate to be set.
*/
function setDelegate(address _delegate) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { BytesLib } from "solidity-bytes-utils/contracts/BytesLib.sol";
import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import { ExecutorOptions } from "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol";
import { DVNOptions } from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/DVNOptions.sol";
/**
* @title OptionsBuilder
* @dev Library for building and encoding various message options.
*/
library OptionsBuilder {
using SafeCast for uint256;
using BytesLib for bytes;
// Constants for options types
uint16 internal constant TYPE_1 = 1; // legacy options type 1
uint16 internal constant TYPE_2 = 2; // legacy options type 2
uint16 internal constant TYPE_3 = 3;
// Custom error message
error InvalidSize(uint256 max, uint256 actual);
error InvalidOptionType(uint16 optionType);
// Modifier to ensure only options of type 3 are used
modifier onlyType3(bytes memory _options) {
if (_options.toUint16(0) != TYPE_3) revert InvalidOptionType(_options.toUint16(0));
_;
}
/**
* @dev Creates a new options container with type 3.
* @return options The newly created options container.
*/
function newOptions() internal pure returns (bytes memory) {
return abi.encodePacked(TYPE_3);
}
/**
* @dev Adds an executor LZ receive option to the existing options.
* @param _options The existing options container.
* @param _gas The gasLimit used on the lzReceive() function in the OApp.
* @param _value The msg.value passed to the lzReceive() function in the OApp.
* @return options The updated options container.
*
* @dev When multiples of this option are added, they are summed by the executor
* eg. if (_gas: 200k, and _value: 1 ether) AND (_gas: 100k, _value: 0.5 ether) are sent in an option to the LayerZeroEndpoint,
* that becomes (300k, 1.5 ether) when the message is executed on the remote lzReceive() function.
*/
function addExecutorLzReceiveOption(
bytes memory _options,
uint128 _gas,
uint128 _value
) internal pure onlyType3(_options) returns (bytes memory) {
bytes memory option = ExecutorOptions.encodeLzReceiveOption(_gas, _value);
return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZRECEIVE, option);
}
/**
* @dev Adds an executor native drop option to the existing options.
* @param _options The existing options container.
* @param _amount The amount for the native value that is airdropped to the 'receiver'.
* @param _receiver The receiver address for the native drop option.
* @return options The updated options container.
*
* @dev When multiples of this option are added, they are summed by the executor on the remote chain.
*/
function addExecutorNativeDropOption(
bytes memory _options,
uint128 _amount,
bytes32 _receiver
) internal pure onlyType3(_options) returns (bytes memory) {
bytes memory option = ExecutorOptions.encodeNativeDropOption(_amount, _receiver);
return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_NATIVE_DROP, option);
}
// /**
// * @dev Adds an executor native drop option to the existing options.
// * @param _options The existing options container.
// * @param _amount The amount for the native value that is airdropped to the 'receiver'.
// * @param _receiver The receiver address for the native drop option.
// * @return options The updated options container.
// *
// * @dev When multiples of this option are added, they are summed by the executor on the remote chain.
// */
function addExecutorLzReadOption(
bytes memory _options,
uint128 _gas,
uint32 _size,
uint128 _value
) internal pure onlyType3(_options) returns (bytes memory) {
bytes memory option = ExecutorOptions.encodeLzReadOption(_gas, _size, _value);
return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZREAD, option);
}
/**
* @dev Adds an executor LZ compose option to the existing options.
* @param _options The existing options container.
* @param _index The index for the lzCompose() function call.
* @param _gas The gasLimit for the lzCompose() function call.
* @param _value The msg.value for the lzCompose() function call.
* @return options The updated options container.
*
* @dev When multiples of this option are added, they are summed PER index by the executor on the remote chain.
* @dev If the OApp sends N lzCompose calls on the remote, you must provide N incremented indexes starting with 0.
* ie. When your remote OApp composes (N = 3) messages, you must set this option for index 0,1,2
*/
function addExecutorLzComposeOption(
bytes memory _options,
uint16 _index,
uint128 _gas,
uint128 _value
) internal pure onlyType3(_options) returns (bytes memory) {
bytes memory option = ExecutorOptions.encodeLzComposeOption(_index, _gas, _value);
return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_LZCOMPOSE, option);
}
/**
* @dev Adds an executor ordered execution option to the existing options.
* @param _options The existing options container.
* @return options The updated options container.
*/
function addExecutorOrderedExecutionOption(
bytes memory _options
) internal pure onlyType3(_options) returns (bytes memory) {
return addExecutorOption(_options, ExecutorOptions.OPTION_TYPE_ORDERED_EXECUTION, bytes(""));
}
/**
* @dev Adds a DVN pre-crime option to the existing options.
* @param _options The existing options container.
* @param _dvnIdx The DVN index for the pre-crime option.
* @return options The updated options container.
*/
function addDVNPreCrimeOption(
bytes memory _options,
uint8 _dvnIdx
) internal pure onlyType3(_options) returns (bytes memory) {
return addDVNOption(_options, _dvnIdx, DVNOptions.OPTION_TYPE_PRECRIME, bytes(""));
}
/**
* @dev Adds an executor option to the existing options.
* @param _options The existing options container.
* @param _optionType The type of the executor option.
* @param _option The encoded data for the executor option.
* @return options The updated options container.
*/
function addExecutorOption(
bytes memory _options,
uint8 _optionType,
bytes memory _option
) internal pure onlyType3(_options) returns (bytes memory) {
return
abi.encodePacked(
_options,
ExecutorOptions.WORKER_ID,
_option.length.toUint16() + 1, // +1 for optionType
_optionType,
_option
);
}
/**
* @dev Adds a DVN option to the existing options.
* @param _options The existing options container.
* @param _dvnIdx The DVN index for the DVN option.
* @param _optionType The type of the DVN option.
* @param _option The encoded data for the DVN option.
* @return options The updated options container.
*/
function addDVNOption(
bytes memory _options,
uint8 _dvnIdx,
uint8 _optionType,
bytes memory _option
) internal pure onlyType3(_options) returns (bytes memory) {
return
abi.encodePacked(
_options,
DVNOptions.WORKER_ID,
_option.length.toUint16() + 2, // +2 for optionType and dvnIdx
_dvnIdx,
_optionType,
_option
);
}
/**
* @dev Encodes legacy options of type 1.
* @param _executionGas The gasLimit value passed to lzReceive().
* @return legacyOptions The encoded legacy options.
*/
function encodeLegacyOptionsType1(uint256 _executionGas) internal pure returns (bytes memory) {
if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);
return abi.encodePacked(TYPE_1, _executionGas);
}
/**
* @dev Encodes legacy options of type 2.
* @param _executionGas The gasLimit value passed to lzReceive().
* @param _nativeForDst The amount of native air dropped to the receiver.
* @param _receiver The _nativeForDst receiver address.
* @return legacyOptions The encoded legacy options of type 2.
*/
function encodeLegacyOptionsType2(
uint256 _executionGas,
uint256 _nativeForDst,
bytes memory _receiver // @dev Use bytes instead of bytes32 in legacy type 2 for _receiver.
) internal pure returns (bytes memory) {
if (_executionGas > type(uint128).max) revert InvalidSize(type(uint128).max, _executionGas);
if (_nativeForDst > type(uint128).max) revert InvalidSize(type(uint128).max, _nativeForDst);
if (_receiver.length > 32) revert InvalidSize(32, _receiver.length);
return abi.encodePacked(TYPE_2, _executionGas, _nativeForDst, _receiver);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { IOAppCore, ILayerZeroEndpointV2 } from "./interfaces/IOAppCore.sol";
/**
* @title OAppCore
* @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.
*/
abstract contract OAppCore is IOAppCore, Ownable {
// The LayerZero endpoint associated with the given OApp
ILayerZeroEndpointV2 public immutable endpoint;
// Mapping to store peers associated with corresponding endpoints
mapping(uint32 eid => bytes32 peer) public peers;
/**
* @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.
* @param _endpoint The address of the LOCAL Layer Zero endpoint.
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
*
* @dev The delegate typically should be set as the owner of the contract.
*/
constructor(address _endpoint, address _delegate) {
endpoint = ILayerZeroEndpointV2(_endpoint);
if (_delegate == address(0)) revert InvalidDelegate();
endpoint.setDelegate(_delegate);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/
function setPeer(uint32 _eid, bytes32 _peer) public virtual onlyOwner {
_setPeer(_eid, _peer);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/
function _setPeer(uint32 _eid, bytes32 _peer) internal virtual {
peers[_eid] = _peer;
emit PeerSet(_eid, _peer);
}
/**
* @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.
* ie. the peer is set to bytes32(0).
* @param _eid The endpoint ID.
* @return peer The address of the peer associated with the specified endpoint.
*/
function _getPeerOrRevert(uint32 _eid) internal view virtual returns (bytes32) {
bytes32 peer = peers[_eid];
if (peer == bytes32(0)) revert NoPeer(_eid);
return peer;
}
/**
* @notice Sets the delegate address for the OApp.
* @param _delegate The address of the delegate to be set.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.
*/
function setDelegate(address _delegate) public onlyOwner {
endpoint.setDelegate(_delegate);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { SafeERC20, IERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { MessagingParams, MessagingFee, MessagingReceipt } from "@layerzerolabs/lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { OAppCore } from "./OAppCore.sol";
/**
* @title OAppSender
* @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.
*/
abstract contract OAppSender is OAppCore {
using SafeERC20 for IERC20;
// Custom error messages
error NotEnoughNative(uint256 msgValue);
error LzTokenUnavailable();
// @dev The version of the OAppSender implementation.
// @dev Version is bumped when changes are made to this contract.
uint64 internal constant SENDER_VERSION = 1;
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*
* @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.
* ie. this is a SEND only OApp.
* @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions
*/
function oAppVersion() public view virtual returns (uint64 senderVersion, uint64 receiverVersion) {
return (SENDER_VERSION, 0);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.
* @return fee The calculated MessagingFee for the message.
* - nativeFee: The native fee for the message.
* - lzTokenFee: The LZ token fee for the message.
*/
function _quote(
uint32 _dstEid,
bytes memory _message,
bytes memory _options,
bool _payInLzToken
) internal view virtual returns (MessagingFee memory fee) {
return
endpoint.quote(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),
address(this)
);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _fee The calculated LayerZero fee for the message.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess fee values sent to the endpoint.
* @return receipt The receipt for the sent message.
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function _lzSend(
uint32 _dstEid,
bytes memory _message,
bytes memory _options,
MessagingFee memory _fee,
address _refundAddress
) internal virtual returns (MessagingReceipt memory receipt) {
// @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.
uint256 messageValue = _payNative(_fee.nativeFee);
if (_fee.lzTokenFee > 0) _payLzToken(_fee.lzTokenFee);
return
// solhint-disable-next-line check-send-result
endpoint.send{ value: messageValue }(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee > 0),
_refundAddress
);
}
/**
* @dev Internal function to pay the native fee associated with the message.
* @param _nativeFee The native fee to be paid.
* @return nativeFee The amount of native currency paid.
*
* @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,
* this will need to be overridden because msg.value would contain multiple lzFees.
* @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.
* @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.
* @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.
*/
function _payNative(uint256 _nativeFee) internal virtual returns (uint256 nativeFee) {
if (msg.value != _nativeFee) revert NotEnoughNative(msg.value);
return _nativeFee;
}
/**
* @dev Internal function to pay the LZ token fee associated with the message.
* @param _lzTokenFee The LZ token fee to be paid.
*
* @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.
* @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().
*/
function _payLzToken(uint256 _lzTokenFee) internal virtual {
// @dev Cannot cache the token because it is not immutable in the endpoint.
address lzToken = endpoint.lzToken();
if (lzToken == address(0)) revert LzTokenUnavailable();
// Pay LZ token fee by sending tokens to the endpoint.
IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { MessagingReceipt, MessagingFee } from "@layerzerolabs/oapp-evm/contracts/oapp/OAppSender.sol";
/**
* @dev Struct representing token parameters for the OFT send() operation.
*/
struct SendParam {
uint32 dstEid; // Destination endpoint ID.
bytes32 to; // Recipient address.
uint256 amountLD; // Amount to send in local decimals.
uint256 minAmountLD; // Minimum amount to send in local decimals.
bytes extraOptions; // Additional options supplied by the caller to be used in the LayerZero message.
bytes composeMsg; // The composed message for the send() operation.
bytes oftCmd; // The OFT command to be executed, unused in default OFT implementations.
}
/**
* @dev Struct representing OFT limit information.
* @dev These amounts can change dynamically and are up the specific oft implementation.
*/
struct OFTLimit {
uint256 minAmountLD; // Minimum amount in local decimals that can be sent to the recipient.
uint256 maxAmountLD; // Maximum amount in local decimals that can be sent to the recipient.
}
/**
* @dev Struct representing OFT receipt information.
*/
struct OFTReceipt {
uint256 amountSentLD; // Amount of tokens ACTUALLY debited from the sender in local decimals.
// @dev In non-default implementations, the amountReceivedLD COULD differ from this value.
uint256 amountReceivedLD; // Amount of tokens to be received on the remote side.
}
/**
* @dev Struct representing OFT fee details.
* @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.
*/
struct OFTFeeDetail {
int256 feeAmountLD; // Amount of the fee in local decimals.
string description; // Description of the fee.
}
/**
* @title IOFT
* @dev Interface for the OftChain (OFT) token.
* @dev Does not inherit ERC20 to accommodate usage by OFTAdapter as well.
* @dev This specific interface ID is '0x02e49c2c'.
*/
interface IOFT {
// Custom error messages
error InvalidLocalDecimals();
error SlippageExceeded(uint256 amountLD, uint256 minAmountLD);
// Events
event OFTSent(
bytes32 indexed guid, // GUID of the OFT message.
uint32 dstEid, // Destination Endpoint ID.
address indexed fromAddress, // Address of the sender on the src chain.
uint256 amountSentLD, // Amount of tokens sent in local decimals.
uint256 amountReceivedLD // Amount of tokens received in local decimals.
);
event OFTReceived(
bytes32 indexed guid, // GUID of the OFT message.
uint32 srcEid, // Source Endpoint ID.
address indexed toAddress, // Address of the recipient on the dst chain.
uint256 amountReceivedLD // Amount of tokens received in local decimals.
);
/**
* @notice Retrieves interfaceID and the version of the OFT.
* @return interfaceId The interface ID.
* @return version The version.
*
* @dev interfaceId: This specific interface ID is '0x02e49c2c'.
* @dev version: Indicates a cross-chain compatible msg encoding with other OFTs.
* @dev If a new feature is added to the OFT cross-chain msg encoding, the version will be incremented.
* ie. localOFT version(x,1) CAN send messages to remoteOFT version(x,1)
*/
function oftVersion() external view returns (bytes4 interfaceId, uint64 version);
/**
* @notice Retrieves the address of the token associated with the OFT.
* @return token The address of the ERC20 token implementation.
*/
function token() external view returns (address);
/**
* @notice Indicates whether the OFT contract requires approval of the 'token()' to send.
* @return requiresApproval Needs approval of the underlying token implementation.
*
* @dev Allows things like wallet implementers to determine integration requirements,
* without understanding the underlying token implementation.
*/
function approvalRequired() external view returns (bool);
/**
* @notice Retrieves the shared decimals of the OFT.
* @return sharedDecimals The shared decimals of the OFT.
*/
function sharedDecimals() external view returns (uint8);
/**
* @notice Provides the fee breakdown and settings data for an OFT. Unused in the default implementation.
* @param _sendParam The parameters for the send operation.
* @return limit The OFT limit information.
* @return oftFeeDetails The details of OFT fees.
* @return receipt The OFT receipt information.
*/
function quoteOFT(
SendParam calldata _sendParam
) external view returns (OFTLimit memory, OFTFeeDetail[] memory oftFeeDetails, OFTReceipt memory);
/**
* @notice Provides a quote for the send() operation.
* @param _sendParam The parameters for the send() operation.
* @param _payInLzToken Flag indicating whether the caller is paying in the LZ token.
* @return fee The calculated LayerZero messaging fee from the send() operation.
*
* @dev MessagingFee: LayerZero msg fee
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
*/
function quoteSend(SendParam calldata _sendParam, bool _payInLzToken) external view returns (MessagingFee memory);
/**
* @notice Executes the send() operation.
* @param _sendParam The parameters for the send operation.
* @param _fee The fee information supplied by the caller.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess funds from fees etc. on the src.
* @return receipt The LayerZero messaging receipt from the send() operation.
* @return oftReceipt The OFT receipt information.
*
* @dev MessagingReceipt: LayerZero msg receipt
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable returns (MessagingReceipt memory, OFTReceipt memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling 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.9.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].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* 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}.
*
* 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 default value returned by this function, unless
* it's 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 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 (last updated v4.9.4) (token/ERC20/extensions/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.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
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].
*
* CAUTION: See Security Considerations above.
*/
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.9.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 (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/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;
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
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));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
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");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
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");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// 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 cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return
success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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://consensys.net/diligence/blog/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.8.0/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 (last updated v4.9.4) (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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.0;
/**
* @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*
* Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
* all math on `uint256` and `int256` and then downcasting.
*/
library SafeCast {
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toUint248(uint256 value) internal pure returns (uint248) {
require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toUint240(uint256 value) internal pure returns (uint240) {
require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toUint232(uint256 value) internal pure returns (uint232) {
require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.2._
*/
function toUint224(uint256 value) internal pure returns (uint224) {
require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toUint216(uint256 value) internal pure returns (uint216) {
require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toUint208(uint256 value) internal pure returns (uint208) {
require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toUint200(uint256 value) internal pure returns (uint200) {
require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toUint192(uint256 value) internal pure returns (uint192) {
require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toUint184(uint256 value) internal pure returns (uint184) {
require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toUint176(uint256 value) internal pure returns (uint176) {
require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toUint168(uint256 value) internal pure returns (uint168) {
require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toUint160(uint256 value) internal pure returns (uint160) {
require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toUint152(uint256 value) internal pure returns (uint152) {
require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toUint144(uint256 value) internal pure returns (uint144) {
require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toUint136(uint256 value) internal pure returns (uint136) {
require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v2.5._
*/
function toUint128(uint256 value) internal pure returns (uint128) {
require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toUint120(uint256 value) internal pure returns (uint120) {
require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toUint112(uint256 value) internal pure returns (uint112) {
require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toUint104(uint256 value) internal pure returns (uint104) {
require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.2._
*/
function toUint96(uint256 value) internal pure returns (uint96) {
require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toUint88(uint256 value) internal pure returns (uint88) {
require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toUint80(uint256 value) internal pure returns (uint80) {
require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toUint72(uint256 value) internal pure returns (uint72) {
require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v2.5._
*/
function toUint64(uint256 value) internal pure returns (uint64) {
require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toUint56(uint256 value) internal pure returns (uint56) {
require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toUint48(uint256 value) internal pure returns (uint48) {
require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toUint40(uint256 value) internal pure returns (uint40) {
require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v2.5._
*/
function toUint32(uint256 value) internal pure returns (uint32) {
require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toUint24(uint256 value) internal pure returns (uint24) {
require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v2.5._
*/
function toUint16(uint256 value) internal pure returns (uint16) {
require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v2.5._
*/
function toUint8(uint256 value) internal pure returns (uint8) {
require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*
* _Available since v3.0._
*/
function toUint256(int256 value) internal pure returns (uint256) {
require(value >= 0, "SafeCast: value must be positive");
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*
* _Available since v4.7._
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*
* _Available since v4.7._
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*
* _Available since v4.7._
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*
* _Available since v4.7._
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*
* _Available since v4.7._
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*
* _Available since v4.7._
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*
* _Available since v4.7._
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*
* _Available since v4.7._
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*
* _Available since v4.7._
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*
* _Available since v4.7._
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*
* _Available since v4.7._
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*
* _Available since v4.7._
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*
* _Available since v4.7._
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*
* _Available since v4.7._
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*
* _Available since v4.7._
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*
* _Available since v3.1._
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*
* _Available since v4.7._
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*
* _Available since v4.7._
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*
* _Available since v4.7._
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*
* _Available since v4.7._
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*
* _Available since v4.7._
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*
* _Available since v4.7._
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*
* _Available since v4.7._
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*
* _Available since v3.1._
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*
* _Available since v4.7._
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*
* _Available since v4.7._
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*
* _Available since v4.7._
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*
* _Available since v3.1._
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*
* _Available since v4.7._
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*
* _Available since v3.1._
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*
* _Available since v3.1._
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*
* _Available since v3.0._
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
return int256(value);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IGasOracle} from "./interfaces/IGasOracle.sol";
import {ITokenMessenger} from "./interfaces/cctp/ITokenMessenger.sol";
import {IMessageTransmitter} from "./interfaces/cctp/IMessageTransmitter.sol";
import {GasUsage} from "./GasUsage.sol";
contract CctpBridge is GasUsage {
using SafeERC20 for IERC20Metadata;
uint internal constant ORACLE_PRECISION = 18;
uint internal constant BP = 1e4;
uint public immutable chainId;
// Admin fee share (in basis points)
uint public adminFeeShareBP;
IERC20Metadata private immutable token;
ITokenMessenger private immutable cctpMessenger;
IMessageTransmitter private immutable cctpTransmitter;
// precomputed value of the scaling factor required for converting the stable token to gas amount
uint private immutable stableTokensForGasScalingFactor;
// precomputed value to divide by to change the precision from the Gas Oracle precision to the stable token precision
uint private immutable fromGasOracleScalingFactor;
mapping(uint chainId => uint domainNumber) private chainIdDomainMap;
mapping(uint nonce => address sender) private senders;
/**
* @notice Emitted when the contract receives some gas directly.
*/
event ReceivedGas(address sender, uint amount);
/**
* @notice Emitted when the contract sends some extra gas to the recipient of tokens.
*/
event ReceivedExtraGas(address recipient, uint amount);
/**
* @notice Emitted when tokens are sent on the source blockchain.
*/
event TokensSent(
uint amount,
address sender,
bytes32 recipient,
uint destinationChainId,
uint nonce,
uint receivedRelayerFeeFromGas,
uint receivedRelayerFeeFromTokens,
uint relayerFee,
uint receivedRelayerFeeTokenAmount,
uint adminFeeTokenAmount
);
event TokensSentExtras(bytes32 recipientWalletAddress);
event RecipientReplaced(address sender, uint nonce, bytes32 newRecipient);
constructor(
uint chainId_,
uint chainPrecision_,
address tokenAddress,
address cctpMessenger_,
address cctpTransmitter_,
IGasOracle gasOracle_
) GasUsage(gasOracle_) {
chainId = chainId_;
token = IERC20Metadata(tokenAddress);
uint tokenDecimals = token.decimals();
cctpMessenger = ITokenMessenger(cctpMessenger_);
cctpTransmitter = IMessageTransmitter(cctpTransmitter_);
token.approve(cctpMessenger_, type(uint256).max);
stableTokensForGasScalingFactor = 10 ** (ORACLE_PRECISION - tokenDecimals + chainPrecision_);
fromGasOracleScalingFactor = 10 ** (ORACLE_PRECISION - tokenDecimals);
}
/**
* @notice Initiates a bridging process of the token to another blockchain.
* @dev This function is used to initiate a cross-chain transfer.
* The bridging fee required for the cross-chain transfer can be paid in two ways:
* - by sending the required amount of native gas token along with the transaction
* (See `getTransactionCost` in the `GasUsage` contract).
* - by setting the parameter `relayerFeeTokenAmount` with the amount of bridging fee in tokens
* (See the function `getBridgingCostInTokens`).
* @param amount The amount of tokens to send (including `relayerFeeTokenAmount`).
* @param recipient The recipient address.
* @param destinationChainId The ID of the destination chain.
* @param relayerFeeTokenAmount The amount of tokens to be deducted from the transferred amount as a bridging fee.
*/
function bridge(
uint amount,
bytes32 recipient,
uint destinationChainId,
uint relayerFeeTokenAmount
) public payable {
require(amount > relayerFeeTokenAmount, "CCTP: Amount <= relayer fee");
require(recipient != 0, "CCTP: Recipient must be nonzero");
token.safeTransferFrom(msg.sender, address(this), amount);
uint gasFromStables = _getStableTokensValueInGas(relayerFeeTokenAmount);
uint relayerFee = this.getTransactionCost(destinationChainId);
require(msg.value + gasFromStables >= relayerFee, "CCTP: Not enough fee");
uint amountToSend = amount - relayerFeeTokenAmount;
uint adminFee;
if (adminFeeShareBP != 0) {
adminFee = (amountToSend * adminFeeShareBP) / BP;
if (adminFee == 0) {
adminFee = 1;
}
amountToSend -= adminFee;
}
uint32 destinationDomain = getDomainByChainId(destinationChainId);
uint64 nonce = cctpMessenger.depositForBurn(amountToSend, destinationDomain, recipient, address(token));
senders[nonce] = msg.sender;
emit TokensSent(
amountToSend,
msg.sender,
recipient,
destinationChainId,
nonce,
msg.value,
gasFromStables,
relayerFee,
relayerFeeTokenAmount,
adminFee
);
}
/**
* @notice Public method to initiate a bridging process of the token to another blockchain. Used for recipients with different wallet address (Solana)
* @dev See full description in the bridge method
* @param recipientWalletAddress The recipient wallet address - used to track user for transfers to Solana.
**/
function bridgeWithWalletAddress(
uint amount,
bytes32 recipient,
bytes32 recipientWalletAddress,
uint destinationChainId,
uint relayerFeeTokenAmount
) external payable {
bridge(amount, recipient, destinationChainId, relayerFeeTokenAmount);
emit TokensSentExtras(recipientWalletAddress);
}
/**
* @notice Public method to replace recipient if it was accidentally incorrectly specified
* @param originalMessage original message bytes (to replace)
* @param originalAttestation original attestation bytes
* @param newRecipient the new mint recipient, which may be the same as the
* original mint recipient, or different.
**/
function changeRecipient(
bytes calldata originalMessage,
bytes calldata originalAttestation,
bytes32 newRecipient
) external {
uint64 nonce = uint64(bytes8(originalMessage[12:20]));
require(senders[nonce] == msg.sender, "CCTP: wrong sender");
cctpMessenger.replaceDepositForBurn(originalMessage, originalAttestation, bytes32(0), newRecipient);
emit RecipientReplaced(msg.sender, nonce, newRecipient);
}
/**
* @notice Completes the bridging process by sending the tokens on the destination blockchain to the recipient.
* @param recipient The recipient address.
* @param message The message information emitted by the CCTP contract `MessageTransmitter` on the source chain.
* @param signature Concatenated 65-byte signature(s) of `message`.
*/
function receiveTokens(address recipient, bytes calldata message, bytes calldata signature) external payable {
require(cctpTransmitter.receiveMessage(message, signature), "CCTP: Receive message failed");
// pass extra gas to the recipient
if (msg.value > 0) {
(bool sent, ) = payable(recipient).call{value: msg.value}("");
if (sent) {
emit ReceivedExtraGas(recipient, msg.value);
}
}
}
/**
* @notice Allows the admin to add new supported chain destination.
* @param chainId_ The chain ID of the destination to register.
* @param domain The domain of the destination to register.
*/
function registerBridgeDestination(uint chainId_, uint32 domain) external onlyOwner {
chainIdDomainMap[chainId_] = domain + 1;
}
/**
* @notice Allows the admin to remove a chain from the map of supported destinations.
* @param chainId_ The chain ID of the destination to unregister.
*/
function unregisterBridgeDestination(uint chainId_) external onlyOwner {
chainIdDomainMap[chainId_] = 0;
}
/**
* @notice Allows the admin to withdraw the relayer fee collected in gas tokens.
*/
function withdrawGas(uint amount) external onlyOwner {
payable(msg.sender).transfer(amount);
}
/**
* @notice Allows the admin to withdraw the admin fee and relayer fee collected in tokens.
*/
function withdrawFeeInTokens() external onlyOwner {
uint toWithdraw = token.balanceOf(address(this));
if (toWithdraw > 0) {
token.safeTransfer(msg.sender, toWithdraw);
}
}
/**
* @notice Sets the basis points of the admin fee share from each bridge.
*/
function setAdminFeeShare(uint adminFeeShareBP_) external onlyOwner {
require(adminFeeShareBP_ <= BP, "CCTP: Too high");
adminFeeShareBP = adminFeeShareBP_;
}
/**
* @notice Calculates the amount of bridging fee nominated in the stable token.
* @param destinationChainId The ID of the destination chain.
* @return The total price of bridging, with the precision according to the token's `decimals()` value.
*/
function getBridgingCostInTokens(uint destinationChainId) external view returns (uint) {
return
gasOracle.getTransactionGasCostInUSD(destinationChainId, gasUsage[destinationChainId]) /
fromGasOracleScalingFactor;
}
function isMessageProcessed(uint sourceChainId, uint64 nonce) external view returns (bool) {
return cctpTransmitter.usedNonces(_hashSourceAndNonce(getDomainByChainId(sourceChainId), nonce)) != 0;
}
function getDomainByChainId(uint chainId_) public view returns (uint32) {
uint domainNumber = chainIdDomainMap[chainId_];
require(domainNumber > 0, "CCTP: Unknown chain id");
return uint32(domainNumber - 1);
}
/**
* @notice Calculates the amount of gas equivalent in value to provided amount of tokens
* according to the current exchange rate.
* @param stableTokenAmount The amount of tokens.
* @return amount of gas tokens.
*/
function _getStableTokensValueInGas(uint stableTokenAmount) internal view returns (uint) {
if (stableTokenAmount == 0) return 0;
return (stableTokensForGasScalingFactor * stableTokenAmount) / gasOracle.price(chainId);
}
function _hashSourceAndNonce(uint32 sourceDomain, uint64 nonce) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(sourceDomain, nonce));
}
fallback() external payable {
revert("Unsupported");
}
receive() external payable {
emit ReceivedGas(msg.sender, msg.value);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IGasOracle} from "./interfaces/IGasOracle.sol";
import {ITokenMessengerV2} from "./interfaces/cctp/ITokenMessengerV2.sol";
import {IMessageTransmitter} from "./interfaces/cctp/IMessageTransmitter.sol";
import {GasUsage} from "./GasUsage.sol";
contract CctpV2Bridge is GasUsage {
using SafeERC20 for IERC20Metadata;
uint internal constant ORACLE_PRECISION = 18;
uint internal constant BP = 1e4;
uint internal constant MAX_FEE_SHARE_P = 1e9;
uint32 public minFinalityThreshold = 1000;
uint public immutable chainId;
// Admin fee share (in basis points)
uint public adminFeeShareBP = 10;
uint public maxFeeShare = 100000;
IERC20Metadata private immutable token;
ITokenMessengerV2 private immutable cctpMessenger;
IMessageTransmitter private immutable cctpTransmitter;
// precomputed value of the scaling factor required for converting the stable token to gas amount
uint private immutable stableTokensForGasScalingFactor;
// precomputed value to divide by to change the precision from the Gas Oracle precision to the stable token precision
uint private immutable fromGasOracleScalingFactor;
mapping(uint chainId => uint domainNumber) private chainIdDomainMap;
/**
* @notice Emitted when the contract receives some gas directly.
*/
event ReceivedGas(address sender, uint amount);
/**
* @notice Emitted when the contract receives message
*/
event ReceivedMessageId(bytes32 messageId);
/**
* @notice Emitted when the contract sends some extra gas to the recipient of tokens.
*/
event ReceivedExtraGas(address recipient, uint amount);
/**
* @notice Emitted when tokens are sent on the source blockchain.
*/
event TokensSent(
address sender,
bytes32 recipient,
uint amount,
uint destinationChainId,
uint receivedRelayerFeeFromGas,
uint receivedRelayerFeeFromTokens,
uint relayerFee,
uint receivedRelayerFeeTokenAmount,
uint adminFeeTokenAmount,
uint maxFee
);
event TokensSentExtras(bytes32 recipientWalletAddress);
constructor(
uint chainId_,
uint chainPrecision_,
address tokenAddress,
address cctpMessenger_,
address cctpTransmitter_,
IGasOracle gasOracle_
) GasUsage(gasOracle_) {
chainId = chainId_;
token = IERC20Metadata(tokenAddress);
uint tokenDecimals = token.decimals();
cctpMessenger = ITokenMessengerV2(cctpMessenger_);
cctpTransmitter = IMessageTransmitter(cctpTransmitter_);
token.approve(cctpMessenger_, type(uint256).max);
stableTokensForGasScalingFactor = 10 ** (ORACLE_PRECISION - tokenDecimals + chainPrecision_);
fromGasOracleScalingFactor = 10 ** (ORACLE_PRECISION - tokenDecimals);
}
/**
* @notice Initiates a bridging process of the token to another blockchain.
* @dev This function is used to initiate a cross-chain transfer.
* The bridging fee required for the cross-chain transfer can be paid in two ways:
* - by sending the required amount of native gas token along with the transaction
* (See `getTransactionCost` in the `GasUsage` contract).
* - by setting the parameter `relayerFeeTokenAmount` with the amount of bridging fee in tokens
* (See the function `getBridgingCostInTokens`).
* @param amount The amount of tokens to send (including `relayerFeeTokenAmount`).
* @param recipient The recipient address.
* @param destinationChainId The ID of the destination chain.
* @param relayerFeeTokenAmount The amount of tokens to be deducted from the transferred amount as a bridging fee.
*/
function bridge(
uint amount,
bytes32 recipient,
uint destinationChainId,
uint relayerFeeTokenAmount
) public payable {
require(amount > relayerFeeTokenAmount, "CCTP: Amount <= relayer fee");
require(recipient != 0, "CCTP: Recipient must be nonzero");
token.safeTransferFrom(msg.sender, address(this), amount);
uint gasFromStables = _getStableTokensValueInGas(relayerFeeTokenAmount);
uint relayerFee = this.getTransactionCost(destinationChainId);
require(msg.value + gasFromStables >= relayerFee, "CCTP: Not enough fee");
uint amountToSend = amount - relayerFeeTokenAmount;
uint adminFee;
if (adminFeeShareBP != 0) {
adminFee = (amountToSend * adminFeeShareBP) / BP;
if (adminFee == 0) {
adminFee = 1;
}
amountToSend -= adminFee;
}
uint maxFee = (amountToSend * maxFeeShare) / MAX_FEE_SHARE_P + 1;
uint32 destinationDomain = getDomainByChainId(destinationChainId);
cctpMessenger.depositForBurn(
amountToSend,
destinationDomain,
recipient,
address(token),
bytes32(0),
maxFee,
minFinalityThreshold
);
emit TokensSent(
msg.sender,
recipient,
amountToSend,
destinationChainId,
msg.value,
gasFromStables,
relayerFee,
relayerFeeTokenAmount,
adminFee,
maxFee
);
}
/**
* @notice Public method to initiate a bridging process of the token to another blockchain. Used for recipients with different wallet address (Solana)
* @dev See full description in the bridge method
* @param recipientWalletAddress The recipient wallet address - used to track user for transfers to Solana.
**/
function bridgeWithWalletAddress(
uint amount,
bytes32 recipient,
bytes32 recipientWalletAddress,
uint destinationChainId,
uint relayerFeeTokenAmount
) external payable {
bridge(amount, recipient, destinationChainId, relayerFeeTokenAmount);
emit TokensSentExtras(recipientWalletAddress);
}
/**
* @notice Completes the bridging process by sending the tokens on the destination blockchain to the recipient.
* @param recipient The recipient address.
* @param messageId The message id to connect sent and received transaction (Sent tx id)
* @param message The message information emitted by the CCTP contract `MessageTransmitter` on the source chain.
* @param signature Concatenated 65-byte signature(s) of `message`.
*/
function receiveTokens(
address recipient,
bytes32 messageId,
bytes calldata message,
bytes calldata signature
) external payable {
require(cctpTransmitter.receiveMessage(message, signature), "CCTP: Receive message failed");
// pass extra gas to the recipient
if (msg.value > 0) {
(bool sent, ) = payable(recipient).call{value: msg.value}("");
if (sent) {
emit ReceivedExtraGas(recipient, msg.value);
}
}
emit ReceivedMessageId(messageId);
}
/**
* @notice Allows the admin to add new supported chain destination.
* @param chainId_ The chain ID of the destination to register.
* @param domain The domain of the destination to register.
*/
function registerBridgeDestination(uint chainId_, uint32 domain) external onlyOwner {
chainIdDomainMap[chainId_] = domain + 1;
}
/**
* @notice Allows the admin to remove a chain from the map of supported destinations.
* @param chainId_ The chain ID of the destination to unregister.
*/
function unregisterBridgeDestination(uint chainId_) external onlyOwner {
chainIdDomainMap[chainId_] = 0;
}
/**
* @notice Allows the admin to withdraw the relayer fee collected in gas tokens.
*/
function withdrawGas(uint amount) external onlyOwner {
payable(msg.sender).transfer(amount);
}
/**
* @notice Allows the admin to withdraw the admin fee and relayer fee collected in tokens.
*/
function withdrawFeeInTokens() external onlyOwner {
uint toWithdraw = token.balanceOf(address(this));
if (toWithdraw > 0) {
token.safeTransfer(msg.sender, toWithdraw);
}
}
/**
* @notice Sets the basis points of the admin fee share from each bridge.
*/
function setAdminFeeShare(uint adminFeeShareBP_) external onlyOwner {
require(adminFeeShareBP_ <= BP, "CCTP: Too high");
adminFeeShareBP = adminFeeShareBP_;
}
/**
* @notice Sets the maximum fee share for the relayer.
*/
function setMaxFeeShare(uint maxFeeShare_) external onlyOwner {
require(maxFeeShare_ <= MAX_FEE_SHARE_P, "CCTP: Too high");
maxFeeShare = maxFeeShare_;
}
/**
* @notice Sets the minimum finality threshold for the relayer.
*/
function setMinFinalityThreshold(uint32 minFinalityThreshold_) external onlyOwner {
minFinalityThreshold = minFinalityThreshold_;
}
/**
* @notice Calculates the amount of bridging fee nominated in the stable token.
* @param destinationChainId The ID of the destination chain.
* @return The total price of bridging, with the precision according to the token's `decimals()` value.
*/
function getBridgingCostInTokens(uint destinationChainId) external view returns (uint) {
return
gasOracle.getTransactionGasCostInUSD(destinationChainId, gasUsage[destinationChainId]) /
fromGasOracleScalingFactor;
}
function isMessageProcessed(bytes32 nonce) external view returns (bool) {
return cctpTransmitter.usedNonces(nonce) != 0;
}
function getDomainByChainId(uint chainId_) public view returns (uint32) {
uint domainNumber = chainIdDomainMap[chainId_];
require(domainNumber > 0, "CCTP: Unknown chain id");
return uint32(domainNumber - 1);
}
/**
* @notice Calculates the amount of gas equivalent in value to provided amount of tokens
* according to the current exchange rate.
* @param stableTokenAmount The amount of tokens.
* @return amount of gas tokens.
*/
function _getStableTokensValueInGas(uint stableTokenAmount) internal view returns (uint) {
if (stableTokenAmount == 0) return 0;
return (stableTokensForGasScalingFactor * stableTokenAmount) / gasOracle.price(chainId);
}
fallback() external payable {
revert("Unsupported");
}
receive() external payable {
emit ReceivedGas(msg.sender, msg.value);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IGasOracle} from "./interfaces/IGasOracle.sol";
/**
* @title GasOracle
* @dev A contract that provides gas price and native token USD price data on other blockchains.
*/
contract GasOracle is Ownable, IGasOracle {
struct ChainData {
// price of the chain's native token in USD
uint128 price;
// price of a gas unit in the chain's native token with precision according to the const ORACLE_PRECISION
uint128 gasPrice;
}
uint private constant ORACLE_PRECISION = 18;
uint private constant ORACLE_SCALING_FACTOR = 10 ** ORACLE_PRECISION;
// number to divide by to change precision from gas oracle price precision to chain precision
uint private immutable fromOracleToChainScalingFactor;
mapping(uint chainId => ChainData) public override chainData;
// current chain ID
uint public immutable override chainId;
constructor(uint chainId_, uint chainPrecision) {
chainId = chainId_;
fromOracleToChainScalingFactor = 10 ** (ORACLE_PRECISION - chainPrecision);
}
/**
* @notice Sets the chain data for a given chain ID.
* @param chainId_ The ID of the given chain to set data for.
* @param price_ The price of the given chain's native token in USD.
* @param gasPrice The price of a gas unit in the given chain's native token (with precision according to the const
* `ORACLE_PRECISION`).
*/
function setChainData(uint chainId_, uint128 price_, uint128 gasPrice) external override onlyOwner {
chainData[chainId_].price = price_;
chainData[chainId_].gasPrice = gasPrice;
}
/**
* @notice Sets only the price for a given chain ID.
* @param chainId_ The ID of the given chain to set the price for.
* @param price_ The price of the given chain's native token in USD.
*/
function setPrice(uint chainId_, uint128 price_) external override onlyOwner {
chainData[chainId_].price = price_;
}
/**
* @notice Sets only the gas price for a given chain ID.
* @param chainId_ The ID of the given chain to set the gas price for.
* @param gasPrice The price of a gas unit in the given chain's native token (with precision according to the const
* `ORACLE_PRECISION`).
*/
function setGasPrice(uint chainId_, uint128 gasPrice) external override onlyOwner {
chainData[chainId_].gasPrice = gasPrice;
}
/**
* @notice Calculates the gas cost of a transaction on another chain in the current chain's native token.
* @param otherChainId The ID of the chain for which to get the gas cost.
* @param gasAmount The amount of gas used in a transaction.
* @return The gas cost of a transaction in the current chain's native token
*/
function getTransactionGasCostInNativeToken(
uint otherChainId,
uint gasAmount
) external view override returns (uint) {
return
(chainData[otherChainId].gasPrice * gasAmount * chainData[otherChainId].price) /
chainData[chainId].price /
fromOracleToChainScalingFactor;
}
/**
* @notice Calculates the gas cost of a transaction on another chain in USD.
* @param otherChainId The ID of the chain for which to get the gas cost.
* @param gasAmount The amount of gas used in a transaction.
* @return The gas cost of a transaction in USD with precision of `ORACLE_PRECISION`
*/
function getTransactionGasCostInUSD(uint otherChainId, uint gasAmount) external view override returns (uint) {
return (chainData[otherChainId].gasPrice * gasAmount * chainData[otherChainId].price) / ORACLE_SCALING_FACTOR;
}
/**
* @notice Get the cross-rate between the two chains' native tokens.
* @param otherChainId The ID of the other chain to get the cross-rate for.
*/
function crossRate(uint otherChainId) external view override returns (uint) {
return (chainData[otherChainId].price * ORACLE_SCALING_FACTOR) / chainData[chainId].price;
}
/**
* @notice Get the price of a given chain's native token in USD.
* @param chainId_ The ID of the given chain to get the price.
* @return the price of the given chain's native token in USD with precision of const ORACLE_PRECISION
*/
function price(uint chainId_) external view override returns (uint) {
return chainData[chainId_].price;
}
fallback() external payable {
revert("Unsupported");
}
receive() external payable {
revert("Unsupported");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IGasOracle} from "./interfaces/IGasOracle.sol";
/**
* @dev Contract module which allows children to store typical gas usage of a certain transaction on another chain.
*/
abstract contract GasUsage is Ownable {
IGasOracle internal gasOracle;
mapping(uint chainId => uint amount) public gasUsage;
constructor(IGasOracle gasOracle_) {
gasOracle = gasOracle_;
}
/**
* @dev Sets the amount of gas used for a transaction on a given chain.
* @param chainId The ID of the chain.
* @param gasAmount The amount of gas used on the chain.
*/
function setGasUsage(uint chainId, uint gasAmount) external onlyOwner {
gasUsage[chainId] = gasAmount;
}
/**
* @dev Sets the Gas Oracle contract address.
* @param gasOracle_ The address of the Gas Oracle contract.
*/
function setGasOracle(IGasOracle gasOracle_) external onlyOwner {
gasOracle = gasOracle_;
}
/**
* @notice Get the gas cost of a transaction on another chain in the current chain's native token.
* @param chainId The ID of the chain for which to get the gas cost.
* @return The calculated gas cost of the transaction in the current chain's native token
*/
function getTransactionCost(uint chainId) external view returns (uint) {
unchecked {
return gasOracle.getTransactionGasCostInNativeToken(chainId, gasUsage[chainId]);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {IReceiver} from "./IReceiver.sol";
interface IMessageTransmitter is IReceiver {
function usedNonces(bytes32 _sourceAndNonce) external view returns (uint256);
}// SPDX-License-Identifier: Apache-2.0
/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity ^0.8.18;
/**
* @title IReceiver
* @notice Receives messages on destination chain and forwards them to IMessageDestinationHandler
*/
interface IReceiver {
/**
* @notice Receives an incoming message, validating the header and passing
* the body to application-specific handler.
* @param message The message raw bytes
* @param signature The message signature
* @return success bool, true if successful
*/
function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
interface ITokenMessenger {
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - given burnToken is not supported
* - given destinationDomain has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - MessageTransmitter returns false or reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain
* @param mintRecipient address of mint recipient on destination domain
* @param burnToken address of contract to burn deposited tokens, on local domain
* @return _nonce unique nonce reserved by message
*/
function depositForBurn(
uint amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken
) external returns (uint64 _nonce);
/**
* @notice Replace a BurnMessage to change the mint recipient and/or
* destination caller. Allows the sender of a previous BurnMessage
* (created by depositForBurn or depositForBurnWithCaller)
* to send a new BurnMessage to replace the original.
* The new BurnMessage will reuse the amount and burn token of the original,
* without requiring a new deposit.
* @dev The new message will reuse the original message's nonce. For a
* given nonce, all replacement message(s) and the original message are
* valid to broadcast on the destination domain, until the first message
* at the nonce confirms, at which point all others are invalidated.
* Note: The msg.sender of the replaced message must be the same as the
* msg.sender of the original message.
* @param originalMessage original message bytes (to replace)
* @param originalAttestation original attestation bytes
* @param newDestinationCaller the new destination caller, which may be the
* same as the original destination caller, a new destination caller, or an empty
* destination caller (bytes32(0), indicating that any destination caller is valid.)
* @param newMintRecipient the new mint recipient, which may be the same as the
* original mint recipient, or different.
*/
function replaceDepositForBurn(
bytes calldata originalMessage,
bytes calldata originalAttestation,
bytes32 newDestinationCaller,
bytes32 newMintRecipient
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
interface ITokenMessengerV2 {
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - given burnToken is not supported
* - given destinationDomain has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - maxFee is greater than or equal to `amount`.
* - MessageTransmitterV2#sendMessage reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain to receive message on
* @param mintRecipient address of mint recipient on destination domain
* @param burnToken token to burn `amount` of, on local domain
* @param destinationCaller authorized caller on the destination domain, as bytes32. If equal to bytes32(0),
* any address can broadcast the message.
* @param maxFee maximum fee to pay on the destination domain, specified in units of burnToken
* @param minFinalityThreshold the minimum finality at which a burn message will be attested to.
*/
function depositForBurn(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
bytes32 destinationCaller,
uint256 maxFee,
uint32 minFinalityThreshold
) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
enum MessengerProtocol {
None,
Allbridge,
Wormhole,
LayerZero
}
interface IBridge {
function chainId() external view returns (uint);
function processedMessages(bytes32) external view returns (uint);
function sentMessages(bytes32) external view returns (uint);
function otherBridges(uint) external view returns (bytes32);
function otherBridgeTokens(uint, bytes32) external view returns (bool);
function getBridgingCostInTokens(
uint destinationChainId,
MessengerProtocol messenger,
address tokenAddress
) external view returns (uint);
function hashMessage(
uint amount,
bytes32 recipient,
uint sourceChainId,
uint destinationChainId,
bytes32 receiveToken,
uint nonce,
MessengerProtocol messenger
) external pure returns (bytes32);
function receiveTokens(
uint amount,
bytes32 recipient,
uint sourceChainId,
bytes32 receiveToken,
uint nonce,
MessengerProtocol messenger,
uint receiveAmountMin
) external payable;
function withdrawGasTokens(uint amount) external;
function registerBridge(uint chainId, bytes32 bridgeAddress) external;
function addBridgeToken(uint chainId, bytes32 tokenAddress) external;
function removeBridgeToken(uint chainId, bytes32 tokenAddress) external;
function swapAndBridge(
bytes32 token,
uint amount,
bytes32 recipient,
uint destinationChainId,
bytes32 receiveToken,
uint nonce,
MessengerProtocol messenger,
uint feeTokenAmount
) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
interface IGasOracle {
function chainData(uint chainId) external view returns (uint128 price, uint128 gasPrice);
function chainId() external view returns (uint);
function crossRate(uint otherChainId) external view returns (uint);
function getTransactionGasCostInNativeToken(uint otherChainId, uint256 gasAmount) external view returns (uint);
function getTransactionGasCostInUSD(uint otherChainId, uint256 gasAmount) external view returns (uint);
function price(uint chainId) external view returns (uint);
function setChainData(uint chainId, uint128 price, uint128 gasPrice) external;
function setGasPrice(uint chainId, uint128 gasPrice) external;
function setPrice(uint chainId, uint128 price) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
interface IMessenger {
function sentMessagesBlock(bytes32 message) external view returns (uint);
function receivedMessages(bytes32 message) external view returns (uint);
function sendMessage(bytes32 message) external payable;
function receiveMessage(bytes32 message, uint v1v2, bytes32 r1, bytes32 s1, bytes32 r2, bytes32 s2) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {MessengerProtocol} from "./IBridge.sol";
interface IRouter {
function canSwap() external view returns (uint8);
function swap(uint amount, bytes32 token, bytes32 receiveToken, address recipient, uint receiveAmountMin) external;
}// contracts/Messages.sol
// SPDX-License-Identifier: Apache 2
pragma solidity ^0.8.18;
interface Structs {
struct Provider {
uint16 chainId;
uint16 governanceChainId;
bytes32 governanceContract;
}
struct GuardianSet {
address[] keys;
uint32 expirationTime;
}
struct Signature {
bytes32 r;
bytes32 s;
uint8 v;
uint8 guardianIndex;
}
struct VM {
uint8 version;
uint32 timestamp;
uint32 nonce;
uint16 emitterChainId;
bytes32 emitterAddress;
uint64 sequence;
uint8 consistencyLevel;
bytes payload;
uint32 guardianSetIndex;
Signature[] signatures;
bytes32 hash;
}
}
interface IWormhole is Structs {
event LogMessagePublished(
address indexed sender,
uint64 sequence,
uint32 nonce,
bytes payload,
uint8 consistencyLevel
);
function publishMessage(
uint32 nonce,
bytes memory payload,
uint8 consistencyLevel
) external payable returns (uint64 sequence);
function parseAndVerifyVM(
bytes calldata encodedVM
) external view returns (Structs.VM memory vm, bool valid, string memory reason);
function verifyVM(Structs.VM memory vm) external view returns (bool valid, string memory reason);
function verifySignatures(
bytes32 hash,
Structs.Signature[] memory signatures,
Structs.GuardianSet memory guardianSet
) external pure returns (bool valid, string memory reason);
function parseVM(bytes memory encodedVM) external pure returns (Structs.VM memory vm);
function getGuardianSet(uint32 index) external view returns (Structs.GuardianSet memory);
function getCurrentGuardianSetIndex() external view returns (uint32);
function getGuardianSetExpiry() external view returns (uint32);
function governanceActionIsConsumed(bytes32 hash) external view returns (bool);
function isInitialized(address impl) external view returns (bool);
function chainId() external view returns (uint16);
function governanceChainId() external view returns (uint16);
function governanceContract() external view returns (bytes32);
function messageFee() external view returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
library HashUtils {
function replaceChainBytes(
bytes32 data,
uint8 sourceChainId,
uint8 destinationChainId
) internal pure returns (bytes32 result) {
assembly {
mstore(0x00, data)
mstore8(0x00, sourceChainId)
mstore8(0x01, destinationChainId)
result := mload(0x0)
}
}
function hashWithSender(bytes32 message, bytes32 sender) internal pure returns (bytes32 result) {
assembly {
mstore(0x00, message)
mstore(0x20, sender)
result := or(
and(
message,
0xffff000000000000000000000000000000000000000000000000000000000000 // First 2 bytes
),
and(
keccak256(0x00, 0x40),
0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // Last 30 bytes
)
)
}
}
function hashWithSenderAddress(bytes32 message, address sender) internal pure returns (bytes32 result) {
assembly {
mstore(0x00, message)
mstore(0x20, sender)
result := or(
and(
message,
0xffff000000000000000000000000000000000000000000000000000000000000 // First 2 bytes
),
and(
keccak256(0x00, 0x40),
0x0000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff // Last 30 bytes
)
)
}
}
function hashed(bytes32 message) internal pure returns (bytes32 result) {
assembly {
mstore(0x00, message)
result := keccak256(0x00, 0x20)
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IGasOracle} from "./interfaces/IGasOracle.sol";
import {IMessenger} from "./interfaces/IMessenger.sol";
import {GasUsage} from "./GasUsage.sol";
import {HashUtils} from "./libraries/HashUtils.sol";
/**
* @dev This contract implements the Allbridge messenger cross-chain communication protocol.
*/
contract Messenger is Ownable, GasUsage, IMessenger {
using HashUtils for bytes32;
// current chain ID
uint public immutable chainId;
// supported destination chain IDs
bytes32 public otherChainIds;
// the primary account that is responsible for validation that a message has been sent on the source chain
address private primaryValidator;
// the secondary accounts that are responsible for validation that a message has been sent on the source chain
mapping(address => bool) private secondaryValidators;
mapping(bytes32 messageHash => uint blockNumber) public override sentMessagesBlock;
mapping(bytes32 messageHash => uint isReceived) public override receivedMessages;
event MessageSent(bytes32 indexed message);
event MessageReceived(bytes32 indexed message);
/**
* @dev Emitted when the contract receives native gas tokens (e.g. Ether on the Ethereum network).
*/
event Received(address, uint);
/**
* @dev Emitted when the mapping of secondary validators is updated.
*/
event SecondaryValidatorsSet(address[] oldValidators, address[] newValidators);
constructor(
uint chainId_,
bytes32 otherChainIds_,
IGasOracle gasOracle_,
address primaryValidator_,
address[] memory validators
) GasUsage(gasOracle_) {
chainId = chainId_;
otherChainIds = otherChainIds_;
primaryValidator = primaryValidator_;
uint length = validators.length;
for (uint index; index < length; ) {
secondaryValidators[validators[index]] = true;
unchecked {
index++;
}
}
}
/**
* @notice Sends a message to another chain.
* @dev Emits a {MessageSent} event, which signals to the off-chain messaging service to invoke the `receiveMessage`
* function on the destination chain to deliver the message.
*
* Requirements:
*
* - the first byte of the message must be the current chain ID.
* - the second byte of the message must be the destination chain ID.
* - the same message cannot be sent second time.
* - messaging fee must be payed. (See `getTransactionCost` of the `GasUsage` contract).
* @param message The message to be sent to the destination chain.
*/
function sendMessage(bytes32 message) external payable override {
require(uint8(message[0]) == chainId, "Messenger: wrong chainId");
require(otherChainIds[uint8(message[1])] != 0, "Messenger: wrong destination");
bytes32 messageWithSender = message.hashWithSenderAddress(msg.sender);
require(sentMessagesBlock[messageWithSender] == 0, "Messenger: has message");
sentMessagesBlock[messageWithSender] = block.number;
require(msg.value >= this.getTransactionCost(uint8(message[1])), "Messenger: not enough fee");
emit MessageSent(messageWithSender);
}
/**
* @notice Delivers a message to the destination chain.
* @dev Emits an {MessageReceived} event indicating the message has been delivered.
*
* Requirements:
*
* - a valid signature of the primary validator.
* - a valid signature of one of the secondary validators.
* - the second byte of the message must be the current chain ID.
*/
function receiveMessage(
bytes32 message,
uint v1v2,
bytes32 r1,
bytes32 s1,
bytes32 r2,
bytes32 s2
) external override {
bytes32 hashedMessage = message.hashed();
require(ecrecover(hashedMessage, uint8(v1v2 >> 8), r1, s1) == primaryValidator, "Messenger: invalid primary");
require(secondaryValidators[ecrecover(hashedMessage, uint8(v1v2), r2, s2)], "Messenger: invalid secondary");
require(uint8(message[1]) == chainId, "Messenger: wrong chainId");
receivedMessages[message] = 1;
emit MessageReceived(message);
}
/**
* @dev Allows the admin to withdraw the messaging fee collected in native gas tokens.
*/
function withdrawGasTokens(uint amount) external onlyOwner {
payable(msg.sender).transfer(amount);
}
/**
* @dev Allows the admin to set the primary validator address.
*/
function setPrimaryValidator(address value) external onlyOwner {
primaryValidator = value;
}
/**
* @dev Allows the admin to set the addresses of secondary validators.
*/
function setSecondaryValidators(address[] memory oldValidators, address[] memory newValidators) external onlyOwner {
uint length = oldValidators.length;
uint index;
for (; index < length; ) {
secondaryValidators[oldValidators[index]] = false;
unchecked {
index++;
}
}
length = newValidators.length;
index = 0;
for (; index < length; ) {
secondaryValidators[newValidators[index]] = true;
unchecked {
index++;
}
}
emit SecondaryValidatorsSet(oldValidators, newValidators);
}
/**
* @dev Allows the admin to update a list of supported destination chain IDs
* @param value Each byte of the `value` parameter represents whether a chain ID with such index is supported
* as a valid message destination.
*/
function setOtherChainIds(bytes32 value) external onlyOwner {
otherChainIds = value;
}
fallback() external payable {
revert("Unsupported");
}
receive() external payable {
emit Received(msg.sender, msg.value);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IGasOracle} from "./interfaces/IGasOracle.sol";
import {Messenger} from "./Messenger.sol";
import {MessengerProtocol} from "./interfaces/IBridge.sol";
import {WormholeMessenger} from "./WormholeMessenger.sol";
/**
* @dev This abstract contract provides functions for cross-chain communication and supports different messaging
* protocols.
*/
abstract contract MessengerGateway is Ownable {
Messenger private allbridgeMessenger;
WormholeMessenger private wormholeMessenger;
constructor(Messenger allbridgeMessenger_, WormholeMessenger wormholeMessenger_) {
allbridgeMessenger = allbridgeMessenger_;
wormholeMessenger = wormholeMessenger_;
}
/**
* @dev Sets the Allbridge Messenger contract address.
* @param allbridgeMessenger_ The address of the Messenger contract.
*/
function setAllbridgeMessenger(Messenger allbridgeMessenger_) external onlyOwner {
allbridgeMessenger = allbridgeMessenger_;
}
/**
* @dev Sets the Wormhole Messenger contract address.
* @param wormholeMessenger_ The address of the WormholeMessenger contract.
*/
function setWormholeMessenger(WormholeMessenger wormholeMessenger_) external onlyOwner {
wormholeMessenger = wormholeMessenger_;
}
/**
* @notice Get the gas cost of a messaging transaction on another chain in the current chain's native token.
* @param chainId The ID of the chain where to send the message.
* @param protocol The messenger used to send the message.
* @return The calculated gas cost of the messaging transaction in the current chain's native token.
*/
function getMessageCost(uint chainId, MessengerProtocol protocol) external view returns (uint) {
if (protocol == MessengerProtocol.Allbridge) {
return allbridgeMessenger.getTransactionCost(chainId);
} else if (protocol == MessengerProtocol.Wormhole) {
return wormholeMessenger.getTransactionCost(chainId);
}
return 0;
}
/**
* @notice Get the amount of gas a messaging transaction uses on a given chain.
* @param chainId The ID of the chain where to send the message.
* @param protocol The messenger used to send the message.
* @return The amount of gas a messaging transaction uses.
*/
function getMessageGasUsage(uint chainId, MessengerProtocol protocol) public view returns (uint) {
if (protocol == MessengerProtocol.Allbridge) {
return allbridgeMessenger.gasUsage(chainId);
} else if (protocol == MessengerProtocol.Wormhole) {
return wormholeMessenger.gasUsage(chainId);
}
return 0;
}
/**
* @notice Checks whether a given message has been received via the specified messenger protocol.
* @param message The message to check.
* @param protocol The messenger used to send the message.
* @return A boolean indicating whether the message has been received.
*/
function hasReceivedMessage(bytes32 message, MessengerProtocol protocol) external view returns (bool) {
if (protocol == MessengerProtocol.Allbridge) {
return allbridgeMessenger.receivedMessages(message) != 0;
} else if (protocol == MessengerProtocol.Wormhole) {
return wormholeMessenger.receivedMessages(message) != 0;
} else {
revert("Not implemented");
}
}
/**
* @notice Checks whether a given message has been sent.
* @param message The message to check.
* @return A boolean indicating whether the message has been sent.
*/
function hasSentMessage(bytes32 message) external view returns (bool) {
return allbridgeMessenger.sentMessagesBlock(message) != 0 || wormholeMessenger.sentMessages(message) != 0;
}
function _sendMessage(bytes32 message, MessengerProtocol protocol) internal returns (uint messageCost) {
if (protocol == MessengerProtocol.Allbridge) {
messageCost = allbridgeMessenger.getTransactionCost(uint8(message[1]));
allbridgeMessenger.sendMessage{value: messageCost}(message);
} else if (protocol == MessengerProtocol.Wormhole) {
messageCost = wormholeMessenger.getTransactionCost(uint8(message[1]));
wormholeMessenger.sendMessage{value: messageCost}(message);
} else {
revert("Not implemented");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IGasOracle} from "./interfaces/IGasOracle.sol";
import {OptionsBuilder} from "@layerzerolabs/oapp-evm/contracts/oapp/libs/OptionsBuilder.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {SendParam, IOFT, MessagingFee} from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol";
/**
* @title OftBridge
* @notice A proxy contract for handling cross-chain token transfers using LayerZero's Omnichain Fungible Token (OFT) standard.
* @dev Incorporates OpenZeppelin and LayerZero libraries for safe token handling and cross-chain messaging.
*/
contract OftBridge is Ownable {
using SafeERC20 for IERC20;
using OptionsBuilder for bytes;
// Constants
uint internal constant ORACLE_PRECISION = 18; // Decimals for gas cost calculations
uint internal constant BP = 1e4; // Basis points denominator (1 basis point = 0.01%)
uint private immutable chainPrecision; // Current chain precision
// State variables
IGasOracle internal gasOracle; // Gas price oracle for pricing gas in token terms
uint public immutable chainId; // Chain ID of the current blockchain
mapping(address tokenAddress => uint feeShare) public adminFeeShareBP; // Admin's fee share per token (bps)
// Mappings for managing token addresses and chain configurations
mapping(address tokenAddress => uint scalingFactor) internal stableTokensForGasScalingFactor; // Scaling factor for token-to-gas conversion
mapping(uint chainId => uint32 eid) private chainIdEidMap; // Map from chain ID to LayerZero Endpoint ID
mapping(uint chainId => uint maxExtraGas) internal maxExtraGas; // Maximum allowed extra gas for each chain
mapping(uint chainId => uint128 gasLimit) internal lzGasLimit; // LayerZero-specific gas limit for cross-chain transactions
mapping(address tokenAddress => mapping(uint destinationChainId => address oftAddress)) public oftAddress; // Map from token to its OFT contract address depending on destination ChainId
/**
* @dev Event emitted when the contract receives some gas directly.
* @param sender The address of the sender.
* @param amount The amount of gas received.
*/
event ReceivedGas(address sender, uint amount);
/**
* @dev Event emitted when tokens are sent to a destination chain.
*/
event OftTokensSent(
address sender,
bytes32 recipient,
address tokenAddress,
uint amount,
uint destinationChainId,
uint receivedRelayerFeeFromGas,
uint receivedRelayerFeeFromTokens,
uint relayerFeeWithExtraGas,
uint receivedRelayerFeeTokenAmount,
uint adminFeeTokenAmount,
uint extraGasDestinationToken
);
/**
* @notice Constructor to initialize the OftBridge.
* @param chainId_ The chain ID of the current blockchain.
* @param gasOracle_ The address of the gas oracle contract.
*/
constructor(
uint chainId_,
uint chainPrecision_,
IGasOracle gasOracle_
) {
chainId = chainId_;
gasOracle = gasOracle_;
chainPrecision = chainPrecision_;
}
/**
* @notice Bridges tokens to a destination chain.
* @param tokenAddress The token address to send.
* @param amount The amount of tokens to bridge.
* @param recipient The recipient address on the destination chain (as bytes32).
* @param destinationChainId The ID of the destination chain.
* @param relayerFeeTokenAmount The portion of the fee in tokens.
* @param extraGasInDestinationToken Additional gas for execution on the destination chain.
* @param slippageBP The acceptable slippage in basis points.
*/
function bridge(
address tokenAddress,
uint amount,
bytes32 recipient,
uint destinationChainId,
uint relayerFeeTokenAmount,
uint extraGasInDestinationToken,
uint slippageBP
) public payable {
// Validate input parameters
require(amount > relayerFeeTokenAmount, "Amount <= relayer fee");
require(recipient != 0, "Recipient must be nonzero");
require(slippageBP <= BP, "Too high slippage");
// Retrieve the oft address associated with the token
address oft = oftAddress[tokenAddress][destinationChainId];
require(oft != address(0), "Token is not registered for the destination");
IERC20(tokenAddress).safeTransferFrom(msg.sender, address(this), amount);
// Ensure extra gas is within the allowed limit
require(maxExtraGas[destinationChainId] == 0 || extraGasInDestinationToken <= maxExtraGas[destinationChainId], "Extra gas too high");
// Initialize LayerZero options for the transaction
bytes memory options = OptionsBuilder.newOptions();
if (lzGasLimit[destinationChainId] > 0) {
options = options.addExecutorLzReceiveOption(lzGasLimit[destinationChainId], 0);
}
if (extraGasInDestinationToken > 0) {
options = options.addExecutorNativeDropOption(uint128(extraGasInDestinationToken), recipient);
}
// Calculate the amount to send after deducting fees
uint amountToSend = amount - relayerFeeTokenAmount;
uint adminFee;
if (adminFeeShareBP[tokenAddress] != 0) {
adminFee = (amountToSend * adminFeeShareBP[tokenAddress]) / BP;
if (adminFee == 0) {
adminFee = 1;
}
amountToSend -= adminFee;
}
// Prepare the LayerZero `SendParam` with messaging options and slippage
SendParam memory sendParam = SendParam(
getEidByChainId(destinationChainId),
recipient,
amountToSend,
amountToSend - (amountToSend * slippageBP / BP), // Minimum amount (after slippage)
options,
"",
""
);
// Retrieve the messaging fee from the LayerZero protocol
MessagingFee memory messagingFee = IOFT(oft).quoteSend(sendParam, false);
// Calculate the gas equivalent of stable tokens for the relayer fee
uint gasFromStables = _getStableTokensValueInGas(tokenAddress, relayerFeeTokenAmount);
require(msg.value + gasFromStables >= messagingFee.nativeFee, "Not enough fee");
// Send the transaction through the OFT protocol
require(address(this).balance >= messagingFee.nativeFee, "Insufficient contract balance");
IOFT(oft).send{value: messagingFee.nativeFee}(sendParam, messagingFee, msg.sender);
// Emit an event for tracking the token bridge
emit OftTokensSent(
msg.sender,
recipient,
tokenAddress,
amountToSend,
destinationChainId,
msg.value,
gasFromStables,
messagingFee.nativeFee,
relayerFeeTokenAmount,
adminFee,
extraGasInDestinationToken
);
}
/**
* @notice Registers a new destination chain for bridging operations.
* @param chainId_ The chain ID of the destination chain.
* @param eid_ The LayerZero endpoint ID for the destination chain.
* @param lzGasLimit_ The gas limit for LayerZero operations on the destination chain.
*/
function registerBridgeDestination(uint chainId_, uint32 eid_, uint128 lzGasLimit_) external onlyOwner {
chainIdEidMap[chainId_] = eid_;
lzGasLimit[chainId_] = lzGasLimit_;
}
/**
* @notice Allows the admin to remove a chain from the map of supported destinations.
* @param chainId_ The chain ID of the destination to unregister.
*/
function unregisterBridgeDestination(uint chainId_) external onlyOwner {
chainIdEidMap[chainId_] = 0;
lzGasLimit[chainId_] = 0;
}
/**
* @notice Adds a new token to the bridge with its corresponding OFT contract for specific destination
* @param oft_ The address of the OFT contract for the token.
* @param destinationChainId_ The ID of the destination chain.
*/
function addToken(address oft_, uint destinationChainId_) external onlyOwner {
address tokenAddress = IOFT(oft_).token();
uint tokenDecimals = IERC20Metadata(tokenAddress).decimals();
IERC20(tokenAddress).forceApprove(oft_, type(uint256).max);
stableTokensForGasScalingFactor[tokenAddress] = 10 ** (ORACLE_PRECISION - tokenDecimals + chainPrecision);
oftAddress[tokenAddress][destinationChainId_] = oft_;
}
/**
* @notice Removes a token from the bridge for specific destination.
* @param oft_ The address of the OFT contract to remove.
* @param destinationChainId_ The ID of the destination chain.
*/
function removeToken(address oft_, uint destinationChainId_) external onlyOwner {
address tokenAddress = IOFT(oft_).token();
oftAddress[tokenAddress][destinationChainId_] = address(0);
}
/**
* @notice Removes a token allowance from oft contract and scaling factor
* @param oft_ The address of the OFT contract to remove.
*/
function removeTokenAllowanceAndScalingFactor(address oft_) external onlyOwner {
address tokenAddress = IOFT(oft_).token();
stableTokensForGasScalingFactor[tokenAddress] = 0;
IERC20(tokenAddress).forceApprove(oft_, 0);
}
/**
* @notice Retrieves the relayer fee required for a specific chain.
* @param tokenAddress_ The address of the token.
* @param destinationChainId_ The ID of the destination chain.
* @return The required fee in native gas tokens.
*/
function relayerFee(address tokenAddress_, uint destinationChainId_) external view returns (uint) {
address oft = oftAddress[tokenAddress_][destinationChainId_];
require(oft != address(0), "Token is not registered for the destination");
MessagingFee memory messagingFee = IOFT(oft).quoteSend(_createEmptySendParam(destinationChainId_, 0), false);
return messagingFee.nativeFee;
}
/**
* @notice Calculates the price of additional gas on the destination chain.
* @param tokenAddress_ The address of the token.
* @param destinationChainId_ The ID of the destination chain.
* @param extraGasAmount_ The extra gas amount for the destination chain.
* @return The price of the additional gas in native tokens.
*/
function extraGasPrice(address tokenAddress_, uint destinationChainId_, uint128 extraGasAmount_) external view returns (uint) {
address oft = oftAddress[tokenAddress_][destinationChainId_];
require(oft != address(0), "Token is not registered for the destination");
MessagingFee memory messagingFee1 = IOFT(oft).quoteSend(_createEmptySendParam(destinationChainId_, extraGasAmount_), false);
MessagingFee memory messagingFee2 = IOFT(oft).quoteSend(_createEmptySendParam(destinationChainId_, extraGasAmount_ * 2), false);
return messagingFee2.nativeFee - messagingFee1.nativeFee;
}
/**
* @notice Allows the admin to withdraw the relayer fee collected in gas tokens.
* @param amount_ The amount of gas tokens to withdraw.
* @dev Only callable by the contract owner. Transfers the specified amount of native tokens
* from the contract to the owner's address.
*/
function withdrawGas(uint amount_) external onlyOwner {
payable(msg.sender).transfer(amount_);
}
/**
* @notice Allows the admin to withdraw the admin fee and relayer fee collected in tokens.
* @param token_ The ERC20 token contract address from which to withdraw fees.
* @dev Only callable by the contract owner. Transfers all available tokens
* of the specified type from the contract to the owner's address.
*/
function withdrawFeeInTokens(IERC20 token_) external onlyOwner {
uint toWithdraw = token_.balanceOf(address(this));
if (toWithdraw > 0) {
token_.safeTransfer(msg.sender, toWithdraw);
}
}
/**
* @notice Sets the basis points of the admin fee share from each bridge.
* @param tokenAddress_ The address of the token for which to set the admin fee share.
* @param adminFeeShareBP_ The percentage of the fee in basis points (1 BP = 0.01%) to be allocated to admin.
* @dev Only callable by the contract owner.
* The value must be less than or equal to the BP constant (10000).
* This fee is deducted from the token amount before bridging.
*/
function setAdminFeeShare(address tokenAddress_, uint adminFeeShareBP_) external onlyOwner {
require(adminFeeShareBP_ <= BP, "Too high");
adminFeeShareBP[tokenAddress_] = adminFeeShareBP_;
}
/**
* @notice Sets the maximum extra gas allowed for a specific chain.
* @param chainId_ The chain ID to set the limit for.
* @param maxExtraGas_ The maximum amount of extra gas allowed.
*/
function setMaxExtraGas(uint chainId_, uint maxExtraGas_) external onlyOwner {
maxExtraGas[chainId_] = maxExtraGas_;
}
/**
* @notice Sets the LayerZero gas limit for a specific chain.
* @param chainId_ The chain ID to set the limit for.
* @param lzGasLimit_ The gas limit for LayerZero operations.
*/
function setLzGasLimit(uint chainId_, uint128 lzGasLimit_) external onlyOwner {
lzGasLimit[chainId_] = lzGasLimit_;
}
/**
* @notice Gets the LayerZero endpoint ID for a given chain ID.
* @param chainId_ The chain ID to lookup.
* @return The corresponding LayerZero endpoint ID.
*/
function getEidByChainId(uint chainId_) public view returns (uint32) {
uint32 domainNumber = chainIdEidMap[chainId_];
require(domainNumber > 0, "Unknown chain id");
return domainNumber;
}
/**
* @notice Creates an empty SendParam structure for fee estimation.
* @dev Used internally to create a basic SendParam with minimal values for gas calculations.
* @param destinationChainId_ The ID of the destination chain.
* @param extraGasDestinationToken_ Additional gas amount for the destination chain execution.
* @return SendParam A LayerZero SendParam structure with basic configuration.
*/
function _createEmptySendParam(
uint destinationChainId_, uint128 extraGasDestinationToken_
) private view returns (SendParam memory) {
bytes memory options = OptionsBuilder.newOptions();
if (lzGasLimit[destinationChainId_] > 0) {
options = options.addExecutorLzReceiveOption(lzGasLimit[destinationChainId_], 0);
}
if (extraGasDestinationToken_ > 0) {
options = options.addExecutorNativeDropOption(extraGasDestinationToken_, bytes32(0));
}
return SendParam(
getEidByChainId(destinationChainId_),
bytes32(0),
0,
0,
options,
"",
""
);
}
/**
* @notice Calculates the amount of gas equivalent in value to provided amount of tokens
* according to the current exchange rate.
* @param tokenAddress_ The address of the token.
* @param stableTokenAmount_ The amount of tokens.
* @return amount of gas tokens.
*/
function _getStableTokensValueInGas(address tokenAddress_, uint stableTokenAmount_) internal view returns (uint) {
require(stableTokensForGasScalingFactor[tokenAddress_] > 0, "Token is not set");
if (stableTokenAmount_ == 0) return 0;
return (stableTokensForGasScalingFactor[tokenAddress_] * stableTokenAmount_) / gasOracle.price(chainId);
}
fallback() external payable {
revert("Unsupported");
}
receive() external payable {
emit ReceivedGas(msg.sender, msg.value);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {RewardManager} from "./RewardManager.sol";
/**
* 4AD - D = 4A(x + y) - (D³ / 4xy)
* X - is value of real stable token
* Y - is value of virtual usd
*/
contract Pool is RewardManager {
using SafeERC20 for ERC20;
uint private constant SYSTEM_PRECISION = 3;
int private constant PP = 1e4; // Price Precision
uint private constant MAX_TOKEN_BALANCE = 2 ** 40; // Max possible token balance
/**
* @dev Gas optimization: both the 'feeShareBP' and 'router' fields are used during the 'swapFromVUsd', 'swapToVUsd'
* operations and can occupy the same slot.
*/
uint16 public feeShareBP;
address public router;
uint public tokenBalance;
uint public vUsdBalance;
uint public balanceRatioMinBP;
uint public reserves;
uint public immutable a;
uint public d;
uint private immutable tokenAmountReduce;
uint private immutable tokenAmountIncrease;
// can restrict deposit or withdraw operations
address private stopAuthority;
// is deposit operation allowed
uint public canDeposit = 1;
// is withdraw operation allowed
uint public canWithdraw = 1;
event SwappedToVUsd(address sender, address token, uint amount, uint vUsdAmount, uint fee);
event SwappedFromVUsd(address recipient, address token, uint vUsdAmount, uint amount, uint fee);
constructor(
address router_,
uint a_,
ERC20 token_,
uint16 feeShareBP_,
uint balanceRatioMinBP_,
string memory lpName,
string memory lpSymbol
) RewardManager(token_, lpName, lpSymbol) {
a = a_;
router = router_;
stopAuthority = owner();
feeShareBP = feeShareBP_;
balanceRatioMinBP = balanceRatioMinBP_;
uint decimals = token_.decimals();
tokenAmountReduce = decimals > SYSTEM_PRECISION ? 10 ** (decimals - SYSTEM_PRECISION) : 0;
tokenAmountIncrease = decimals < SYSTEM_PRECISION ? 10 ** (SYSTEM_PRECISION - decimals) : 0;
}
/**
* @dev Throws if called by any account other than the router.
*/
modifier onlyRouter() {
require(router == msg.sender, "Pool: is not router");
_;
}
/**
* @dev Throws if called by any account other than the stopAuthority.
*/
modifier onlyStopAuthority() {
require(stopAuthority == msg.sender, "Pool: is not stopAuthority");
_;
}
/**
* @dev Modifier to prevent function from disbalancing the pool over a threshold defined by `balanceRatioMinBP`
*/
modifier validateBalanceRatio() {
_;
if (tokenBalance > vUsdBalance) {
require((vUsdBalance * BP) / tokenBalance >= balanceRatioMinBP, "Pool: low vUSD balance");
} else if (tokenBalance < vUsdBalance) {
require((tokenBalance * BP) / vUsdBalance >= balanceRatioMinBP, "Pool: low token balance");
}
}
/**
* @dev Modifier to make a function callable only when the deposit is allowed.
*/
modifier whenCanDeposit() {
require(canDeposit == 1, "Pool: deposit prohibited");
_;
}
/**
* @dev Modifier to make a function callable only when the withdraw is allowed.
*/
modifier whenCanWithdraw() {
require(canWithdraw == 1, "Pool: withdraw prohibited");
_;
}
/**
* @dev Calculates the price and deposit token according to the amount and price, then adds the same amount to the X
* and to the Y
* @param amount The deposited amount
*/
function deposit(uint amount) external whenCanDeposit {
uint oldD = d;
uint amountSP = _toSystemPrecision(amount);
require(amountSP > 0, "Pool: too little");
token.safeTransferFrom(msg.sender, address(this), amount);
// Add deposited amount to reserves
reserves += amountSP;
uint oldBalance = (tokenBalance + vUsdBalance);
if (oldD == 0 || oldBalance == 0) {
// Split balance equally on the first deposit
uint halfAmount = amountSP >> 1;
tokenBalance += halfAmount;
vUsdBalance += halfAmount;
} else {
// Add amount proportionally to each pool
tokenBalance += (amountSP * tokenBalance) / oldBalance;
vUsdBalance += (amountSP * vUsdBalance) / oldBalance;
}
_updateD();
// Deposit as many LP tokens as the D increase
_depositLp(msg.sender, d - oldD);
require(tokenBalance < MAX_TOKEN_BALANCE, "Pool: too much");
}
/*
* @dev Subtracts X and Y for that amount, calculates current price and withdraw the token to the user according to
* the price
* @param amount The deposited amount
*/
function withdraw(uint amountLp) external whenCanWithdraw {
uint oldD = d;
_withdrawLp(msg.sender, amountLp);
// Always withdraw tokens in amount equal to amountLp
// Withdraw proportionally from token and vUsd balance
uint oldBalance = (tokenBalance + vUsdBalance);
tokenBalance -= (amountLp * tokenBalance) / oldBalance;
vUsdBalance -= (amountLp * vUsdBalance) / oldBalance;
require(tokenBalance + vUsdBalance < oldBalance, "Pool: zero changes");
// Check if there is enough funds in reserve to withdraw
require(amountLp <= reserves, "Pool: reserves");
// Adjust reserves by withdraw amount
reserves -= amountLp;
// Update D and transfer tokens to the sender
_updateD();
require(d < oldD, "Pool: zero D changes");
token.safeTransfer(msg.sender, _fromSystemPrecision(amountLp));
}
/**
* @notice Calculates new virtual USD value from the given amount of tokens.
* @dev Calculates new Y according to new X.
* NOTICE: Prior to calling this the router must transfer tokens from the user to the pool.
* @param amount The amount of tokens to swap.
* @param zeroFee When true it allows to swap without incurring any fees. It is intended for use with service
* accounts.
* @return returns the difference between the old and the new value of vUsdBalance
*/
function swapToVUsd(
address user,
uint amount,
bool zeroFee
) external onlyRouter validateBalanceRatio returns (uint) {
uint result; // 0 by default
uint fee;
if (amount > 0) {
if (!zeroFee) {
fee = (amount * feeShareBP) / BP;
}
uint amountIn = _toSystemPrecision(amount - fee);
// Incorporate rounding dust into the fee
fee = amount - _fromSystemPrecision(amountIn);
// Adjust token and reserve balances after the fee is applied
tokenBalance += amountIn;
reserves += amountIn;
uint vUsdNewAmount = this.getY(tokenBalance);
if (vUsdBalance > vUsdNewAmount) {
result = vUsdBalance - vUsdNewAmount;
}
vUsdBalance = vUsdNewAmount;
_addRewards(fee);
}
emit SwappedToVUsd(user, address(token), amount, result, fee);
return result;
}
/**
* @notice Calculates the amount of tokens from the given virtual USD value, and transfers it to the user.
* @dev Calculates new X according to new Y.
* @param user The address of the recipient.
* @param amount The amount of vUSD to swap.
* @param receiveAmountMin The minimum amount of tokens required to be received during the swap, otherwise the
* transaction reverts.
* @param zeroFee When true it allows to swap without incurring any fees. It is intended for use with service
* accounts.
* @return returns the difference between the old and the new value of vUsdBalance
*/
function swapFromVUsd(
address user,
uint amount,
uint receiveAmountMin,
bool zeroFee
) external onlyRouter validateBalanceRatio returns (uint) {
uint resultSP; // 0 by default
uint result; // 0 by default
uint fee;
if (amount > 0) {
vUsdBalance += amount;
uint newAmount = this.getY(vUsdBalance);
if (tokenBalance > newAmount) {
resultSP = tokenBalance - newAmount;
result = _fromSystemPrecision(resultSP);
} // Otherwise result/resultSP stay 0
// Check if there is enough funds in reserve to pay
require(resultSP <= reserves, "Pool: reserves");
// Remove from reserves including fee, apply fee later
reserves -= resultSP;
if (!zeroFee) {
fee = (result * feeShareBP) / BP;
}
// We can use unchecked here because feeShareBP <= BP
unchecked {
result -= fee;
}
tokenBalance = newAmount;
require(result >= receiveAmountMin, "Pool: slippage");
token.safeTransfer(user, result);
_addRewards(fee);
}
emit SwappedFromVUsd(user, address(token), amount, result, fee);
return result;
}
/**
* @dev Sets admin fee share.
*/
function setFeeShare(uint16 feeShareBP_) external onlyOwner {
require(feeShareBP_ <= BP, "Pool: too large");
feeShareBP = feeShareBP_;
}
function adjustTotalLpAmount() external onlyOwner {
if (d > totalSupply()) {
_depositLp(owner(), d - totalSupply());
}
}
/**
* @dev Sets the threshold over which the pool can't be disbalanced.
*/
function setBalanceRatioMinBP(uint balanceRatioMinBP_) external onlyOwner {
require(balanceRatioMinBP_ <= BP, "Pool: too large");
balanceRatioMinBP = balanceRatioMinBP_;
}
/**
* @dev Switches off the possibility to make deposits.
*/
function stopDeposit() external onlyStopAuthority {
canDeposit = 0;
}
/**
* @dev Switches on the possibility to make deposits.
*/
function startDeposit() external onlyOwner {
canDeposit = 1;
}
/**
* @dev Switches off the possibility to make withdrawals.
*/
function stopWithdraw() external onlyStopAuthority {
canWithdraw = 0;
}
/**
* @dev Switches on the possibility to make withdrawals.
*/
function startWithdraw() external onlyOwner {
canWithdraw = 1;
}
/**
* @dev Sets the address of the stopAuthority account.
*/
function setStopAuthority(address stopAuthority_) external onlyOwner {
stopAuthority = stopAuthority_;
}
/**
* @dev Sets the address of the Router contract.
*/
function setRouter(address router_) external onlyOwner {
router = router_;
}
/**
* @dev y = (sqrt(x(4AD³ + x (4A(D - x) - D )²)) + x (4A(D - x) - D ))/8Ax.
*/
function getY(uint x) external view returns (uint) {
uint d_ = d; // Gas optimization
uint a4 = a << 2;
uint a8 = a4 << 1;
// 4A(D - x) - D
int part1 = int(a4) * (int(d_) - int(x)) - int(d_);
// x * (4AD³ + x(part1²))
uint part2 = x * (a4 * d_ * d_ * d_ + x * uint(part1 * part1));
// (sqrt(part2) + x(part1)) / 8Ax)
return SafeCast.toUint256(int(_sqrt(part2)) + int(x) * part1) / (a8 * x) + 1; // +1 to offset rounding errors
}
/**
* @dev price = (1/2) * ((D³ + 8ADx² - 8Ax³ - 2Dx²) / (4x * sqrt(x(4AD³ + x (4A(D - x) - D )²))))
*/
function getPrice() external view returns (uint) {
uint x = tokenBalance;
uint a8 = a << 3;
uint dCubed = d * d * d;
// 4A(D - x) - D
int p1 = int(a << 2) * (int(d) - int(x)) - int(d);
// x * 4AD³ + x(p1²)
uint p2 = x * ((a << 2) * dCubed + x * uint(p1 * p1));
// D³ + 8ADx² - 8Ax³ - 2Dx²
int p3 = int(dCubed) + int((a << 3) * d * x * x) - int(a8 * x * x * x) - int((d << 1) * x * x);
// 1/2 * p3 / (4x * sqrt(p2))
return SafeCast.toUint256((PP >> 1) + ((PP * p3) / int((x << 2) * _sqrt(p2))));
}
function _updateD() internal {
uint x = tokenBalance;
uint y = vUsdBalance;
// a = 8 * Axy(x+y)
// b = 4 * xy(4A - 1) / 3
// c = sqrt(a² + b³)
// D = cbrt(a + c) + cbrt(a - c)
uint xy = x * y;
uint a_ = a;
// Axy(x+y)
uint p1 = a_ * xy * (x + y);
// xy(4A - 1) / 3
uint p2 = (xy * ((a_ << 2) - 1)) / 3;
// p1² + p2³
uint p3 = _sqrt((p1 * p1) + (p2 * p2 * p2));
unchecked {
uint d_ = _cbrt(p1 + p3);
if (p3 > p1) {
d_ -= _cbrt(p3 - p1);
} else {
d_ += _cbrt(p1 - p3);
}
d = (d_ << 1);
}
}
function _toSystemPrecision(uint amount) internal view returns (uint) {
if (tokenAmountReduce > 0) {
return amount / tokenAmountReduce;
} else if (tokenAmountIncrease > 0) {
return amount * tokenAmountIncrease;
}
return amount;
}
function _fromSystemPrecision(uint amount) internal view returns (uint) {
if (tokenAmountReduce > 0) {
return amount * tokenAmountReduce;
} else if (tokenAmountIncrease > 0) {
return amount / tokenAmountIncrease;
}
return amount;
}
function _sqrt(uint n) internal pure returns (uint) {
unchecked {
if (n > 0) {
uint x = (n >> 1) + 1;
uint y = (x + n / x) >> 1;
while (x > y) {
x = y;
y = (x + n / x) >> 1;
}
return x;
}
return 0;
}
}
function _cbrt(uint n) internal pure returns (uint) {
unchecked {
uint x = 0;
for (uint y = 1 << 255; y > 0; y >>= 3) {
x <<= 1;
uint z = 3 * x * (x + 1) + 1;
if (n / y >= z) {
n -= y * z;
x += 1;
}
}
return x;
}
}
fallback() external payable {
revert("Unsupported");
}
receive() external payable {
revert("Unsupported");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
contract RewardManager is Ownable, ERC20 {
using SafeERC20 for ERC20;
uint private constant P = 52;
uint internal constant BP = 1e4;
// Accumulated rewards per share, shifted left by P bits
uint public accRewardPerShareP;
// Reward token
ERC20 public immutable token;
// Info of each user reward debt
mapping(address user => uint amount) public userRewardDebt;
// Admin fee share (in basis points)
uint public adminFeeShareBP;
// Unclaimed admin fee amount
uint public adminFeeAmount;
event Deposit(address indexed user, uint amount);
event Withdraw(address indexed user, uint amount);
event RewardsClaimed(address indexed user, uint amount);
constructor(ERC20 token_, string memory lpName, string memory lpSymbol) ERC20(lpName, lpSymbol) {
token = token_;
// Default admin fee is 20%
adminFeeShareBP = BP / 5;
}
/**
* @notice Claims pending rewards for the current staker without updating the stake balance.
*/
function claimRewards() external {
uint userLpAmount = balanceOf(msg.sender);
if (userLpAmount > 0) {
uint rewards = (userLpAmount * accRewardPerShareP) >> P;
uint pending = rewards - userRewardDebt[msg.sender];
if (pending > 0) {
userRewardDebt[msg.sender] = rewards;
token.safeTransfer(msg.sender, pending);
emit RewardsClaimed(msg.sender, pending);
}
}
}
/**
* @notice Sets the basis points of the admin fee share from rewards.
*/
function setAdminFeeShare(uint adminFeeShareBP_) external onlyOwner {
require(adminFeeShareBP_ <= BP, "RewardManager: too high");
adminFeeShareBP = adminFeeShareBP_;
}
/**
* @notice Allows the admin to claim the collected admin fee.
*/
function claimAdminFee() external onlyOwner {
if (adminFeeAmount > 0) {
token.safeTransfer(msg.sender, adminFeeAmount);
adminFeeAmount = 0;
}
}
/**
* @notice Returns pending rewards for the staker.
* @param user The address of the staker.
*/
function pendingReward(address user) external view returns (uint) {
return ((balanceOf(user) * accRewardPerShareP) >> P) - userRewardDebt[user];
}
/**
* @dev Returns the number of decimals used to get user representation of LP tokens.
*/
function decimals() public pure override returns (uint8) {
return 3;
}
/**
* @dev Adds reward to the pool, splits admin fee share and updates the accumulated rewards per share.
*/
function _addRewards(uint rewardAmount) internal {
if (totalSupply() > 0) {
uint adminFeeRewards = (rewardAmount * adminFeeShareBP) / BP;
unchecked {
rewardAmount -= adminFeeRewards;
}
accRewardPerShareP += (rewardAmount << P) / totalSupply();
adminFeeAmount += adminFeeRewards;
}
}
/**
* @dev Deposits LP amount for the user, updates user reward debt and pays pending rewards.
*/
function _depositLp(address to, uint lpAmount) internal {
uint pending;
uint userLpAmount = balanceOf(to); // Gas optimization
if (userLpAmount > 0) {
pending = ((userLpAmount * accRewardPerShareP) >> P) - userRewardDebt[to];
}
userLpAmount += lpAmount;
_mint(to, lpAmount);
userRewardDebt[to] = (userLpAmount * accRewardPerShareP) >> P;
if (pending > 0) {
token.safeTransfer(to, pending);
emit RewardsClaimed(to, pending);
}
emit Deposit(to, lpAmount);
}
/**
* @dev Withdraws LP amount for the user, updates user reward debt and pays out pending rewards.
*/
function _withdrawLp(address from, uint lpAmount) internal {
uint userLpAmount = balanceOf(from); // Gas optimization
require(userLpAmount >= lpAmount, "RewardManager: not enough amount");
uint pending;
if (userLpAmount > 0) {
pending = ((userLpAmount * accRewardPerShareP) >> P) - userRewardDebt[from];
}
userLpAmount -= lpAmount;
_burn(from, lpAmount);
userRewardDebt[from] = (userLpAmount * accRewardPerShareP) >> P;
if (pending > 0) {
token.safeTransfer(from, pending);
emit RewardsClaimed(from, pending);
}
emit Withdraw(from, lpAmount);
}
function _transfer(address, address, uint) internal pure override {
revert("Unsupported");
}
function _approve(address, address, uint) internal pure override {
revert("Unsupported");
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IRouter} from "./interfaces/IRouter.sol";
import {MessengerProtocol} from "./interfaces/IBridge.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Pool} from "./Pool.sol";
abstract contract Router is Ownable, IRouter {
using SafeERC20 for ERC20;
uint private immutable chainPrecision;
uint internal constant ORACLE_PRECISION = 18;
mapping(bytes32 tokenId => Pool) public pools;
// precomputed values to divide by to change the precision from the Gas Oracle precision to the token precision
mapping(address tokenAddress => uint scalingFactor) internal fromGasOracleScalingFactor;
// precomputed values of the scaling factor required for paying the bridging fee with stable tokens
mapping(address tokenAddress => uint scalingFactor) internal bridgingFeeConversionScalingFactor;
// can restrict swap operations
address private stopAuthority;
/**
* @dev The rebalancer is an account responsible for balancing the liquidity pools. It ensures that the pool is
* balanced by executing zero-fee swaps when the pool is imbalanced.
*
* Gas optimization: both the 'rebalancer' and 'canSwap' fields are used in the 'swap' and 'swapAndBridge'
* functions and can occupy the same slot.
*/
address private rebalancer;
uint8 public override canSwap = 1;
/**
* @dev Emitted during the on-chain swap of tokens.
*/
event Swapped(
address sender,
address recipient,
bytes32 sendToken,
bytes32 receiveToken,
uint sendAmount,
uint receiveAmount
);
constructor(uint chainPrecision_) {
chainPrecision = chainPrecision_;
stopAuthority = owner();
}
/**
* @dev Modifier to make a function callable only when the swap is allowed.
*/
modifier whenCanSwap() {
require(canSwap == 1, "Router: swap prohibited");
_;
}
/**
* @dev Throws if called by any account other than the stopAuthority.
*/
modifier onlyStopAuthority() {
require(stopAuthority == msg.sender, "Router: is not stopAuthority");
_;
}
/**
* @notice Swaps a given pair of tokens on the same blockchain.
* @param amount The amount of tokens to be swapped.
* @param token The token to be swapped.
* @param receiveToken The token to receive in exchange for the swapped token.
* @param recipient The address to receive the tokens.
* @param receiveAmountMin The minimum amount of tokens required to receive during the swap.
*/
function swap(
uint amount,
bytes32 token,
bytes32 receiveToken,
address recipient,
uint receiveAmountMin
) external override whenCanSwap {
uint vUsdAmount = _sendAndSwapToVUsd(token, msg.sender, amount);
uint receivedAmount = _receiveAndSwapFromVUsd(receiveToken, recipient, vUsdAmount, receiveAmountMin);
emit Swapped(msg.sender, recipient, token, receiveToken, amount, receivedAmount);
}
/**
* @notice Allows the admin to add new supported liquidity pools.
* @dev Adds the address of the `Pool` contract to the list of supported liquidity pools.
* @param pool The address of the `Pool` contract.
* @param token The address of the token in the liquidity pool.
*/
function addPool(Pool pool, bytes32 token) external onlyOwner {
pools[token] = pool;
address tokenAddress = address(uint160(uint(token)));
uint tokenDecimals = ERC20(tokenAddress).decimals();
bridgingFeeConversionScalingFactor[tokenAddress] = 10 ** (ORACLE_PRECISION - tokenDecimals + chainPrecision);
fromGasOracleScalingFactor[tokenAddress] = 10 ** (ORACLE_PRECISION - tokenDecimals);
}
/**
* @dev Switches off the possibility to make swaps.
*/
function stopSwap() external onlyStopAuthority {
canSwap = 0;
}
/**
* @dev Switches on the possibility to make swaps.
*/
function startSwap() external onlyOwner {
canSwap = 1;
}
/**
* @dev Allows the admin to set the address of the stopAuthority.
*/
function setStopAuthority(address stopAuthority_) external onlyOwner {
stopAuthority = stopAuthority_;
}
/**
* @dev Allows the admin to set the address of the rebalancer.
*/
function setRebalancer(address rebalancer_) external onlyOwner {
rebalancer = rebalancer_;
}
function _receiveAndSwapFromVUsd(
bytes32 token,
address recipient,
uint vUsdAmount,
uint receiveAmountMin
) internal returns (uint) {
Pool tokenPool = pools[token];
require(address(tokenPool) != address(0), "Router: no receive pool");
return tokenPool.swapFromVUsd(recipient, vUsdAmount, receiveAmountMin, recipient == rebalancer);
}
function _sendAndSwapToVUsd(bytes32 token, address user, uint amount) internal virtual returns (uint) {
Pool pool = pools[token];
require(address(pool) != address(0), "Router: no pool");
ERC20(address(uint160(uint(token)))).safeTransferFrom(user, address(pool), amount);
return pool.swapToVUsd(user, amount, user == rebalancer);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
library console {
address constant CONSOLE_ADDRESS = address(0x000000000000000000636F6e736F6c652e6c6f67);
function _sendLogPayload(bytes memory payload) private view {
uint payloadLength = payload.length;
address consoleAddress = CONSOLE_ADDRESS;
assembly {
let payloadStart := add(payload, 32)
let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
}
}
function log() internal view {
_sendLogPayload(abi.encodeWithSignature("log()"));
}
function logInt(int p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(int)", p0));
}
function logUint(uint p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
}
function logString(string memory p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function logBool(bool p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
}
function logAddress(address p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
}
function logBytes(bytes memory p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes)", p0));
}
function logBytes1(bytes1 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes1)", p0));
}
function logBytes2(bytes2 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes2)", p0));
}
function logBytes3(bytes3 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes3)", p0));
}
function logBytes4(bytes4 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes4)", p0));
}
function logBytes5(bytes5 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes5)", p0));
}
function logBytes6(bytes6 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes6)", p0));
}
function logBytes7(bytes7 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes7)", p0));
}
function logBytes8(bytes8 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes8)", p0));
}
function logBytes9(bytes9 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes9)", p0));
}
function logBytes10(bytes10 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes10)", p0));
}
function logBytes11(bytes11 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes11)", p0));
}
function logBytes12(bytes12 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes12)", p0));
}
function logBytes13(bytes13 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes13)", p0));
}
function logBytes14(bytes14 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes14)", p0));
}
function logBytes15(bytes15 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes15)", p0));
}
function logBytes16(bytes16 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes16)", p0));
}
function logBytes17(bytes17 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes17)", p0));
}
function logBytes18(bytes18 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes18)", p0));
}
function logBytes19(bytes19 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes19)", p0));
}
function logBytes20(bytes20 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes20)", p0));
}
function logBytes21(bytes21 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes21)", p0));
}
function logBytes22(bytes22 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes22)", p0));
}
function logBytes23(bytes23 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes23)", p0));
}
function logBytes24(bytes24 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes24)", p0));
}
function logBytes25(bytes25 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes25)", p0));
}
function logBytes26(bytes26 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes26)", p0));
}
function logBytes27(bytes27 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes27)", p0));
}
function logBytes28(bytes28 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes28)", p0));
}
function logBytes29(bytes29 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes29)", p0));
}
function logBytes30(bytes30 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes30)", p0));
}
function logBytes31(bytes31 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes31)", p0));
}
function logBytes32(bytes32 p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bytes32)", p0));
}
function log(uint p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint)", p0));
}
function log(string memory p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function log(bool p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool)", p0));
}
function log(address p0) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address)", p0));
}
function log(uint p0, uint p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint)", p0, p1));
}
function log(uint p0, string memory p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string)", p0, p1));
}
function log(uint p0, bool p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool)", p0, p1));
}
function log(uint p0, address p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address)", p0, p1));
}
function log(string memory p0, uint p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint)", p0, p1));
}
function log(string memory p0, string memory p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
}
function log(string memory p0, bool p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool)", p0, p1));
}
function log(string memory p0, address p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address)", p0, p1));
}
function log(bool p0, uint p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint)", p0, p1));
}
function log(bool p0, string memory p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string)", p0, p1));
}
function log(bool p0, bool p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool)", p0, p1));
}
function log(bool p0, address p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address)", p0, p1));
}
function log(address p0, uint p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint)", p0, p1));
}
function log(address p0, string memory p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string)", p0, p1));
}
function log(address p0, bool p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool)", p0, p1));
}
function log(address p0, address p1) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address)", p0, p1));
}
function log(uint p0, uint p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint)", p0, p1, p2));
}
function log(uint p0, uint p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string)", p0, p1, p2));
}
function log(uint p0, uint p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool)", p0, p1, p2));
}
function log(uint p0, uint p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address)", p0, p1, p2));
}
function log(uint p0, string memory p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint)", p0, p1, p2));
}
function log(uint p0, string memory p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string)", p0, p1, p2));
}
function log(uint p0, string memory p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool)", p0, p1, p2));
}
function log(uint p0, string memory p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address)", p0, p1, p2));
}
function log(uint p0, bool p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint)", p0, p1, p2));
}
function log(uint p0, bool p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string)", p0, p1, p2));
}
function log(uint p0, bool p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool)", p0, p1, p2));
}
function log(uint p0, bool p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address)", p0, p1, p2));
}
function log(uint p0, address p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint)", p0, p1, p2));
}
function log(uint p0, address p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string)", p0, p1, p2));
}
function log(uint p0, address p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool)", p0, p1, p2));
}
function log(uint p0, address p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address)", p0, p1, p2));
}
function log(string memory p0, uint p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint)", p0, p1, p2));
}
function log(string memory p0, uint p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string)", p0, p1, p2));
}
function log(string memory p0, uint p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool)", p0, p1, p2));
}
function log(string memory p0, uint p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address)", p0, p1, p2));
}
function log(string memory p0, string memory p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint)", p0, p1, p2));
}
function log(string memory p0, string memory p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string)", p0, p1, p2));
}
function log(string memory p0, string memory p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool)", p0, p1, p2));
}
function log(string memory p0, string memory p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address)", p0, p1, p2));
}
function log(string memory p0, bool p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint)", p0, p1, p2));
}
function log(string memory p0, bool p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string)", p0, p1, p2));
}
function log(string memory p0, bool p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool)", p0, p1, p2));
}
function log(string memory p0, bool p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address)", p0, p1, p2));
}
function log(string memory p0, address p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint)", p0, p1, p2));
}
function log(string memory p0, address p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string)", p0, p1, p2));
}
function log(string memory p0, address p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool)", p0, p1, p2));
}
function log(string memory p0, address p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address)", p0, p1, p2));
}
function log(bool p0, uint p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint)", p0, p1, p2));
}
function log(bool p0, uint p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string)", p0, p1, p2));
}
function log(bool p0, uint p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool)", p0, p1, p2));
}
function log(bool p0, uint p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address)", p0, p1, p2));
}
function log(bool p0, string memory p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint)", p0, p1, p2));
}
function log(bool p0, string memory p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string)", p0, p1, p2));
}
function log(bool p0, string memory p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool)", p0, p1, p2));
}
function log(bool p0, string memory p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address)", p0, p1, p2));
}
function log(bool p0, bool p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint)", p0, p1, p2));
}
function log(bool p0, bool p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string)", p0, p1, p2));
}
function log(bool p0, bool p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool)", p0, p1, p2));
}
function log(bool p0, bool p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address)", p0, p1, p2));
}
function log(bool p0, address p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint)", p0, p1, p2));
}
function log(bool p0, address p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string)", p0, p1, p2));
}
function log(bool p0, address p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool)", p0, p1, p2));
}
function log(bool p0, address p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address)", p0, p1, p2));
}
function log(address p0, uint p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint)", p0, p1, p2));
}
function log(address p0, uint p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string)", p0, p1, p2));
}
function log(address p0, uint p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool)", p0, p1, p2));
}
function log(address p0, uint p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address)", p0, p1, p2));
}
function log(address p0, string memory p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint)", p0, p1, p2));
}
function log(address p0, string memory p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string)", p0, p1, p2));
}
function log(address p0, string memory p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool)", p0, p1, p2));
}
function log(address p0, string memory p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address)", p0, p1, p2));
}
function log(address p0, bool p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint)", p0, p1, p2));
}
function log(address p0, bool p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string)", p0, p1, p2));
}
function log(address p0, bool p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool)", p0, p1, p2));
}
function log(address p0, bool p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address)", p0, p1, p2));
}
function log(address p0, address p1, uint p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint)", p0, p1, p2));
}
function log(address p0, address p1, string memory p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string)", p0, p1, p2));
}
function log(address p0, address p1, bool p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool)", p0, p1, p2));
}
function log(address p0, address p1, address p2) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address)", p0, p1, p2));
}
function log(uint p0, uint p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,uint)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,string)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,bool)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,uint,address)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,uint)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,string)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,bool)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,string,address)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,uint)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,string)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,bool)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,bool,address)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,uint)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,string)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,bool)", p0, p1, p2, p3));
}
function log(uint p0, uint p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,uint,address,address)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,uint)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,string)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,bool)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,uint,address)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,uint)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,string)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,bool)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,string,address)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,uint)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,string)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,bool)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,bool,address)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,uint)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,string)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,bool)", p0, p1, p2, p3));
}
function log(uint p0, string memory p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,string,address,address)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,uint)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,string)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,bool)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,uint,address)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,uint)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,string)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,bool)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,string,address)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,uint)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,string)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,bool)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,bool,address)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,uint)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,string)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,bool)", p0, p1, p2, p3));
}
function log(uint p0, bool p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,bool,address,address)", p0, p1, p2, p3));
}
function log(uint p0, address p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,uint)", p0, p1, p2, p3));
}
function log(uint p0, address p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,string)", p0, p1, p2, p3));
}
function log(uint p0, address p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,bool)", p0, p1, p2, p3));
}
function log(uint p0, address p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,uint,address)", p0, p1, p2, p3));
}
function log(uint p0, address p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,uint)", p0, p1, p2, p3));
}
function log(uint p0, address p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,string)", p0, p1, p2, p3));
}
function log(uint p0, address p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,bool)", p0, p1, p2, p3));
}
function log(uint p0, address p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,string,address)", p0, p1, p2, p3));
}
function log(uint p0, address p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,uint)", p0, p1, p2, p3));
}
function log(uint p0, address p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,string)", p0, p1, p2, p3));
}
function log(uint p0, address p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,bool)", p0, p1, p2, p3));
}
function log(uint p0, address p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,bool,address)", p0, p1, p2, p3));
}
function log(uint p0, address p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,uint)", p0, p1, p2, p3));
}
function log(uint p0, address p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,string)", p0, p1, p2, p3));
}
function log(uint p0, address p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,bool)", p0, p1, p2, p3));
}
function log(uint p0, address p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(uint,address,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,uint)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,uint,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,uint)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,uint)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,uint)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, uint p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,uint,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,uint)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,uint,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,uint)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,uint)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,uint)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, string memory p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,string,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,uint)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,uint,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,uint)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,uint)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,uint)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, bool p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,bool,address,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,uint)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,uint,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,uint)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,string,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,uint)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,bool,address)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,uint)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,string)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,bool)", p0, p1, p2, p3));
}
function log(string memory p0, address p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(string,address,address,address)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,uint)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,string)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,uint,address)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,uint)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,string)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,string,address)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,uint)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,uint)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,string)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, uint p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,uint,address,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,uint)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,uint,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,uint)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,string,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,uint)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,uint)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,string)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, string memory p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,string,address,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,uint)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,uint,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,uint)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,string,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,uint)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,uint)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,string)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, bool p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,bool,address,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,uint)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,uint,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,uint)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,string,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,uint)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,bool,address)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,uint)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,string)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,bool)", p0, p1, p2, p3));
}
function log(bool p0, address p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(bool,address,address,address)", p0, p1, p2, p3));
}
function log(address p0, uint p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,uint)", p0, p1, p2, p3));
}
function log(address p0, uint p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,string)", p0, p1, p2, p3));
}
function log(address p0, uint p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,bool)", p0, p1, p2, p3));
}
function log(address p0, uint p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,uint,address)", p0, p1, p2, p3));
}
function log(address p0, uint p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,uint)", p0, p1, p2, p3));
}
function log(address p0, uint p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,string)", p0, p1, p2, p3));
}
function log(address p0, uint p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,bool)", p0, p1, p2, p3));
}
function log(address p0, uint p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,string,address)", p0, p1, p2, p3));
}
function log(address p0, uint p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,uint)", p0, p1, p2, p3));
}
function log(address p0, uint p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,string)", p0, p1, p2, p3));
}
function log(address p0, uint p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, uint p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,bool,address)", p0, p1, p2, p3));
}
function log(address p0, uint p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,uint)", p0, p1, p2, p3));
}
function log(address p0, uint p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,string)", p0, p1, p2, p3));
}
function log(address p0, uint p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,bool)", p0, p1, p2, p3));
}
function log(address p0, uint p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,uint,address,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,uint)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,uint,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,uint)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,string,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,uint)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,bool,address)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,uint)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,string)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,bool)", p0, p1, p2, p3));
}
function log(address p0, string memory p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,string,address,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,uint)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,uint,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,uint)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,string,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,uint)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,bool,address)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,uint)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,string)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,bool)", p0, p1, p2, p3));
}
function log(address p0, bool p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,bool,address,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,uint)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, uint p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,uint,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,uint)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, string memory p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,string,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,uint)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, bool p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,bool,address)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, uint p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,uint)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, string memory p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,string)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, bool p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,bool)", p0, p1, p2, p3));
}
function log(address p0, address p1, address p2, address p3) internal view {
_sendLogPayload(abi.encodeWithSignature("log(address,address,address,address)", p0, p1, p2, p3));
}
}// SPDX-License-Identifier: MIT
contract MockERC20 {
string public name;
string public symbol;
uint8 public decimals;
uint public totalSupply;
mapping(address => uint) public balanceOf;
mapping(address => mapping(address => uint)) public allowance;
constructor(string memory _name, string memory _symbol, uint8 _decimals) {
name = _name;
symbol = _symbol;
decimals = _decimals;
}
function mint(address to, uint amount) external {
balanceOf[to] += amount;
totalSupply += amount;
}
function approve(address spender, uint amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
return true;
}
function transfer(address to, uint amount) external returns (bool) {
require(balanceOf[msg.sender] >= amount, "Insufficient balance");
balanceOf[msg.sender] -= amount;
balanceOf[to] += amount;
return true;
}
function transferFrom(address from, address to, uint amount) external returns (bool) {
require(balanceOf[from] >= amount, "Insufficient balance");
require(allowance[from][msg.sender] >= amount, "Insufficient allowance");
balanceOf[from] -= amount;
balanceOf[to] += amount;
allowance[from][msg.sender] -= amount;
return true;
}
}// SPDX-License-Identifier: MIT
contract MockGasOracle {
mapping(uint => uint) public prices;
function price(uint chainId) external view returns (uint) {
return prices[chainId];
}
function setPrice(uint chainId, uint _price) external {
prices[chainId] = _price;
}
}import {UlnOptions} from "@layerzerolabs/lz-evm-messagelib-v2/contracts/uln/libs/UlnOptions.sol";
import {ExecutorOptions} from "@layerzerolabs/lz-evm-messagelib-v2/contracts/libs/ExecutorOptions.sol";
import {MockERC20} from "./MockERC20.sol";
import {SendParam, MessagingFee, MessagingReceipt, OFTReceipt} from "@layerzerolabs/oft-evm/contracts/interfaces/IOFT.sol";
// SPDX-License-Identifier: MIT
contract MockOFT {
using ExecutorOptions for bytes;
address public token;
uint public defaultNativeFee = 10000000000000000;
constructor(address _token) {
token = _token;
}
function setDefaultNativeFee(uint fee) external {
defaultNativeFee = fee;
}
function quoteSend(SendParam calldata sendParam, bool payInLzToken) external view returns (MessagingFee memory) {
// Calculate fee based on options - in real implementation this would parse the options
uint fee = defaultNativeFee;
bytes calldata _options = sendParam.extraOptions[2:];
uint256 cursor = 0;
while (cursor < _options.length) {
(uint8 optionType, bytes calldata option, uint256 newCursor) = _options.nextExecutorOption(cursor);
cursor = newCursor;
if (optionType == ExecutorOptions.OPTION_TYPE_NATIVE_DROP) {
(uint128 nativeDropAmount, ) = ExecutorOptions.decodeNativeDropOption(option);
fee += nativeDropAmount;
}
}
return MessagingFee({
nativeFee: fee,
lzTokenFee: 0
});
}
function send(
SendParam calldata sendParam,
MessagingFee calldata fee,
address refundAddress
) external payable returns (MessagingReceipt memory, OFTReceipt memory) {
require(msg.value >= fee.nativeFee, "Not enough native token for fee");
// Simulate the token transfer
MockERC20(token).transferFrom(msg.sender, address(this), sendParam.amountLD);
return (MessagingReceipt(bytes32(0), 0, MessagingFee(0, 0)), OFTReceipt(0, 0));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {IBridge, MessengerProtocol} from "../interfaces/IBridge.sol";
import {Router} from "../Router.sol";
contract TestBridgeForSwap is IBridge, Router {
uint public chainId;
mapping(bytes32 messageHash => uint isProcessed) public override processedMessages;
mapping(bytes32 messageHash => uint isSent) public override sentMessages;
// Info about bridges on other chains
mapping(uint chainId => bytes32 bridgeAddress) public override otherBridges;
// Info about tokens on other chains
mapping(uint chainId => mapping(bytes32 tokenAddress => bool isSupported)) public override otherBridgeTokens;
event vUsdSent(uint amount);
constructor() Router(18) {}
function swapAndBridge(
bytes32 token,
uint amount,
bytes32 recipient,
uint destinationChainId,
bytes32 receiveToken,
uint nonce,
MessengerProtocol messenger,
uint feeTokenAmount
) external payable override {}
function receiveTokens(
uint amount,
bytes32,
uint,
bytes32 receiveToken,
uint,
MessengerProtocol,
uint receiveAmountMin
) external payable override {}
function withdrawGasTokens(uint amount) external override onlyOwner {}
function registerBridge(uint chainId_, bytes32 bridgeAddress_) external override onlyOwner {}
function addBridgeToken(uint chainId_, bytes32 tokenAddress_) external override onlyOwner {}
function removeBridgeToken(uint chainId_, bytes32 tokenAddress_) external override onlyOwner {}
function getBridgingCostInTokens(uint, MessengerProtocol, address) external pure override returns (uint) {
return 0;
}
function hashMessage(
uint,
bytes32,
uint,
uint,
bytes32,
uint,
MessengerProtocol
) external pure override returns (bytes32) {
return 0;
}
function _sendAndSwapToVUsd(bytes32 token, address user, uint amount) internal override returns (uint) {
uint vUsdAmount = super._sendAndSwapToVUsd(token, user, amount);
emit vUsdSent(vUsdAmount);
return vUsdAmount;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {IMessenger} from "../interfaces/IMessenger.sol";
contract TestMessenger is IMessenger {
bool public isHasMessage = false;
event Sent();
function sentMessagesBlock(bytes32) external view override returns (uint) {
return isHasMessage ? 1 : 0;
}
function receivedMessages(bytes32) external view override returns (uint) {
return isHasMessage ? 1 : 0;
}
function sendMessage(bytes32) external payable override {
emit Sent();
}
function receiveMessage(
bytes32 message,
uint v1v2,
bytes32 r1,
bytes32 s1,
bytes32 r2,
bytes32 s2
) external override {
// Do nothing
}
function getTransactionCost(uint) external pure returns (uint) {
return 1000;
}
function setIsHasMessage(bool isHasMessage_) external {
isHasMessage = isHasMessage_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {Pool} from "../Pool.sol";
import {RewardManager} from "../RewardManager.sol";
contract TestPool is Pool {
constructor(
address router_,
uint a_,
ERC20 token_,
uint16 feeShareBP_,
uint balanceRatioMinBP_
) Pool(router_, a_, token_, feeShareBP_, balanceRatioMinBP_, "LP", "LP") {}
function setVUsdBalance(uint vUsdBalance_) public {
vUsdBalance = vUsdBalance_;
}
function setTokenBalance(uint tokenBalance_) public {
tokenBalance = tokenBalance_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {RewardManager} from "../RewardManager.sol";
contract TestPoolForRewards is RewardManager {
// solhint-disable-next-line no-empty-blocks
constructor(ERC20 token) RewardManager(token, "LP", "LP") {}
function deposit(uint amount) external {
_depositLp(msg.sender, amount);
}
function withdraw(uint amount) external {
_withdrawLp(msg.sender, amount);
}
function addRewards(uint amount) external {
_addRewards(amount);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract Token is ERC20 {
uint8 internal _decimals;
constructor(string memory _name, string memory _symbol, uint _amount, uint8 __decimals) ERC20(_name, _symbol) {
_mint(msg.sender, _amount);
_decimals = __decimals;
}
function decimals() public view virtual override returns (uint8) {
return _decimals;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.18;
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IGasOracle} from "./interfaces/IGasOracle.sol";
import {IMessenger} from "./interfaces/IMessenger.sol";
import {IWormhole} from "./interfaces/IWormhole.sol";
import {GasUsage} from "./GasUsage.sol";
import {GasOracle} from "./GasOracle.sol";
import {HashUtils} from "./libraries/HashUtils.sol";
contract WormholeMessenger is Ownable, GasUsage {
using HashUtils for bytes32;
IWormhole private immutable wormhole;
uint public immutable chainId;
bytes32 public otherChainIds;
uint32 private nonce;
uint8 private commitmentLevel;
mapping(uint16 chainId => bytes32 wormholeMessengerAddress) private otherWormholeMessengers;
mapping(bytes32 messageHash => uint isReceived) public receivedMessages;
mapping(bytes32 messageHash => uint isSent) public sentMessages;
event MessageSent(bytes32 indexed message, uint64 sequence);
event MessageReceived(bytes32 indexed message, uint64 sequence);
event Received(address, uint);
constructor(
uint chainId_,
bytes32 otherChainIds_,
IWormhole wormhole_,
uint8 commitmentLevel_,
IGasOracle gasOracle_
) GasUsage(gasOracle_) {
chainId = chainId_;
otherChainIds = otherChainIds_;
wormhole = wormhole_;
commitmentLevel = commitmentLevel_;
}
function sendMessage(bytes32 message) external payable {
require(uint8(message[0]) == chainId, "WormholeMessenger: wrong chainId");
require(otherChainIds[uint8(message[1])] != 0, "Messenger: wrong destination");
bytes32 messageWithSender = message.hashWithSenderAddress(msg.sender);
uint32 nonce_ = nonce;
uint256 wormholeFee = wormhole.messageFee();
uint64 sequence = wormhole.publishMessage{value: wormholeFee}(
nonce_,
abi.encodePacked(messageWithSender),
commitmentLevel
);
unchecked {
nonce = nonce_ + 1;
}
require(sentMessages[messageWithSender] == 0, "WormholeMessenger: has message");
sentMessages[messageWithSender] = 1;
emit MessageSent(messageWithSender, sequence);
}
function receiveMessage(bytes memory encodedMsg) external {
(IWormhole.VM memory vm, bool valid, string memory reason) = wormhole.parseAndVerifyVM(encodedMsg);
require(valid, reason);
require(vm.payload.length == 32, "WormholeMessenger: wrong length");
bytes32 messageWithSender = bytes32(vm.payload);
require(uint8(messageWithSender[1]) == chainId, "WormholeMessenger: wrong chainId");
require(otherWormholeMessengers[vm.emitterChainId] == vm.emitterAddress, "WormholeMessenger: wrong emitter");
receivedMessages[messageWithSender] = 1;
emit MessageReceived(messageWithSender, vm.sequence);
}
function setCommitmentLevel(uint8 value) external onlyOwner {
commitmentLevel = value;
}
function setOtherChainIds(bytes32 value) external onlyOwner {
otherChainIds = value;
}
function registerWormholeMessenger(uint16 chainId_, bytes32 address_) external onlyOwner {
otherWormholeMessengers[chainId_] = address_;
}
function withdrawGasTokens(uint amount) external onlyOwner {
payable(msg.sender).transfer(amount);
}
fallback() external payable {
revert("Unsupported");
}
receive() external payable {
emit Received(msg.sender, msg.value);
}
}// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity >=0.8.0 <0.9.0; library BytesLib { function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore(0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. )) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes.slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes.slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and( fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 ), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= _start + 1 , "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equalStorage( bytes storage _preBytes, bytes memory _postBytes ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for {} eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
{
"viaIR": true,
"optimizer": {
"enabled": true,
"runs": 1000,
"details": {
"yulDetails": {
"optimizerSteps": "u"
}
}
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"uint256","name":"chainPrecision_","type":"uint256"},{"internalType":"contract Messenger","name":"allbridgeMessenger_","type":"address"},{"internalType":"contract WormholeMessenger","name":"wormholeMessenger_","type":"address"},{"internalType":"contract IGasOracle","name":"gasOracle_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"gas","type":"uint256"}],"name":"BridgingFeeFromTokens","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":false,"internalType":"uint256","name":"bridgeTransactionCost","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"messageTransactionCost","type":"uint256"}],"name":"ReceiveFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"bytes32","name":"sendToken","type":"bytes32"},{"indexed":false,"internalType":"bytes32","name":"receiveToken","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"sendAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"receiveAmount","type":"uint256"}],"name":"Swapped","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"recipient","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"enum MessengerProtocol","name":"messenger","type":"uint8"},{"indexed":false,"internalType":"bytes32","name":"message","type":"bytes32"}],"name":"TokensReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"recipient","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"indexed":false,"internalType":"bytes32","name":"receiveToken","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"nonce","type":"uint256"},{"indexed":false,"internalType":"enum MessengerProtocol","name":"messenger","type":"uint8"}],"name":"TokensSent","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"bytes32","name":"tokenAddress","type":"bytes32"}],"name":"addBridgeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Pool","name":"pool","type":"address"},{"internalType":"bytes32","name":"token","type":"bytes32"}],"name":"addPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"canSwap","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chainId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"gasUsage","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"enum MessengerProtocol","name":"messenger","type":"uint8"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"getBridgingCostInTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"enum MessengerProtocol","name":"protocol","type":"uint8"}],"name":"getMessageCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"enum MessengerProtocol","name":"protocol","type":"uint8"}],"name":"getMessageGasUsage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"getTransactionCost","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"message","type":"bytes32"},{"internalType":"enum MessengerProtocol","name":"protocol","type":"uint8"}],"name":"hasReceivedMessage","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"message","type":"bytes32"}],"name":"hasSentMessage","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"uint256","name":"sourceChainId","type":"uint256"},{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"bytes32","name":"receiveToken","type":"bytes32"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"enum MessengerProtocol","name":"messenger","type":"uint8"}],"name":"hashMessage","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"bytes32","name":"tokenAddress","type":"bytes32"}],"name":"otherBridgeTokens","outputs":[{"internalType":"bool","name":"isSupported","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"}],"name":"otherBridges","outputs":[{"internalType":"bytes32","name":"bridgeAddress","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"tokenId","type":"bytes32"}],"name":"pools","outputs":[{"internalType":"contract Pool","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"processedMessages","outputs":[{"internalType":"uint256","name":"isProcessed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"uint256","name":"sourceChainId","type":"uint256"},{"internalType":"bytes32","name":"receiveToken","type":"bytes32"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"enum MessengerProtocol","name":"messenger","type":"uint8"},{"internalType":"uint256","name":"receiveAmountMin","type":"uint256"}],"name":"receiveTokens","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"bytes32","name":"bridgeAddress","type":"bytes32"}],"name":"registerBridge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId_","type":"uint256"},{"internalType":"bytes32","name":"tokenAddress","type":"bytes32"}],"name":"removeBridgeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"messageHash","type":"bytes32"}],"name":"sentMessages","outputs":[{"internalType":"uint256","name":"isSent","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Messenger","name":"allbridgeMessenger_","type":"address"}],"name":"setAllbridgeMessenger","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IGasOracle","name":"gasOracle_","type":"address"}],"name":"setGasOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"uint256","name":"gasAmount","type":"uint256"}],"name":"setGasUsage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rebalancer_","type":"address"}],"name":"setRebalancer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"stopAuthority_","type":"address"}],"name":"setStopAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract WormholeMessenger","name":"wormholeMessenger_","type":"address"}],"name":"setWormholeMessenger","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"startSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stopSwap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"token","type":"bytes32"},{"internalType":"bytes32","name":"receiveToken","type":"bytes32"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"receiveAmountMin","type":"uint256"}],"name":"swap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"token","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32","name":"recipient","type":"bytes32"},{"internalType":"uint256","name":"destinationChainId","type":"uint256"},{"internalType":"bytes32","name":"receiveToken","type":"bytes32"},{"internalType":"uint256","name":"nonce","type":"uint256"},{"internalType":"enum MessengerProtocol","name":"messenger","type":"uint8"},{"internalType":"uint256","name":"feeTokenAmount","type":"uint256"}],"name":"swapAndBridge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"withdrawBridgingFeeInTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawGasTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60c0604052346200005b57620000226200001862000152565b939290926200017c565b604051612cbb62000337823960805181612889015260a0518181816107fc015281816111c801528181611ccd0152611f490152612cbb90f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b90601f01601f191681019081106001600160401b038211176200009557604052565b6200005f565b90620000b2620000aa60405190565b928362000073565b565b805b036200005b57565b90505190620000b282620000b4565b6001600160a01b031690565b90565b6001600160a01b038116620000b6565b90505190620000b282620000dc565b919060a0838203126200005b57620001148184620000be565b92620001248260208301620000be565b92620000d9620001388460408501620000ec565b93620001488160608601620000ec565b93608001620000ec565b6200017562002ff28038038062000169816200009b565b928339810190620000fb565b9091929394565b9391926200018a936200018f565b60a052565b90620000b2939291620001fe565b906001600160a01b03905b9181191691161790565b620000d990620000cd906001600160a01b031682565b620000d990620001b2565b620000d990620001c8565b90620001f2620000d9620001fa92620001d3565b82546200019d565b9055565b62000213620000b2946200021b939462000265565b6008620001de565b6009620001de565b9060ff60a01b9060a01b620001a8565b62000243620000d9620000d99290565b60ff1690565b906200025d620000d9620001fa9262000233565b825462000223565b90620002719062000298565b6200027f6001600762000249565b608052620000b262000290620002cc565b6006620001de565b620000b290620000b290620002ac620002b4565b6001620001de565b620000b233620002d7565b546001600160a01b031690565b620000d95f620002bf565b620002e25f620002bf565b90620002ef815f620001de565b620003266200031f7f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e093620001d3565b91620001d3565b916200033160405190565b5f90a356fe6080604052600436101561001d575b366120495761001b61208e565b005b5f3560e01c806310ce05161461024c5780631268cee81461024757806318b51308146102425780632c48bb8d1461023d5780632d4b01b5146102385780632ed1084b14610233578063331838b21461022e57806336d6825a146102295780633ee2594e146102245780634927b44c1461021f5780634cd480bd1461021a5780635fa1ebd91461021557806368593337146102105780636cfd15531461020b578063715018a614610206578063737dae1d146102015780637d18330b146101fc57806382e3702d146101f757806388ba16ab146101f25780638b9369e2146101ed5780638da5cb5b146101e85780639a8a0592146101e35780639bb55742146101de578063a1f5e9cd146101d9578063a87b8152146101d4578063aab65192146101cf578063b5217bb4146101ca578063b633b364146101c5578063c7c56d7e146101c0578063c992b5b2146101bb578063e3f04fa4146101b6578063e43bfe5e146101b1578063e4ce6160146101ac578063eb588675146101a75763f2fde38b0361000e57610a4e565b610a35565b6109ff565b6109e5565b610967565b61094f565b610936565b61091e565b6108f7565b610868565b610850565b610838565b610820565b6107e7565b6107a9565b61078e565b610773565b610747565b61071a565b6106ce565b6106b6565b61069e565b610682565b610646565b610629565b61059a565b61056d565b610552565b610525565b61048f565b6103f7565b61039d565b610308565b6102eb565b61028e565b5f91031261025b57565b5f80fd5b61026e916008021c5b60ff1690565b90565b9061026e915461025f565b61026e60146007610271565b9052565b565b3461025b5761029e366004610251565b6102bd6102a961027c565b6040519182918260ff909116815260200190565b0390f35b805b0361025b57565b9050359061028c826102c1565b9060208282031261025b5761026e916102ca565b3461025b576103036102fe3660046102d7565b611523565b604051005b3461025b576102bd61032361031e3660046102d7565b61210d565b6040519182918290815260200190565b919060408382031261025b5761026e9061034d81856102ca565b936020016102ca565b61026e61026e61026e9290565b9061036d90610356565b5f5260205260405f2090565b9061036d565b61039861026e92610393600d935f94610363565b610379565b610271565b3461025b576102bd6103b96103b3366004610333565b9061037f565b60405191829182901515815260200190565b61026e916008021c81565b9061026e91546103cb565b61026e906103f2600c915f92610363565b6103d6565b3461025b576102bd61032361040d3660046102d7565b6103e1565b6004111561025b57565b9050359061028c82610412565b60e08183031261025b5761043d82826102ca565b9261044b83602084016102ca565b9261045981604085016102ca565b9261046782606083016102ca565b9261026e61047884608085016102ca565b936104868160a086016102ca565b9360c00161041c565b3461025b576102bd6103236104a5366004610429565b95949094939193611aa7565b6001600160a01b031690565b6001600160a01b0381166102c3565b9050359061028c826104bd565b919060a08382031261025b576104ef81846102ca565b926104fd82602083016102ca565b9261026e61050e84604085016102ca565b9361051c81606086016104cc565b936080016102ca565b3461025b576103036105383660046104d9565b93929092612792565b61026e906103f26002915f92610363565b3461025b576102bd6103236105683660046102d7565b610541565b3461025b57610303610580366004610333565b90611464565b9060208282031261025b5761026e916104cc565b3461025b576103036105ad366004610586565b612a04565b91906101008382031261025b576105c981846102ca565b926105d782602083016102ca565b926105e583604084016102ca565b926105f381606085016102ca565b9261060182608083016102ca565b9261026e6106128460a085016102ca565b936106208160c0860161041c565b9360e0016102ca565b6103036106373660046105b2565b96959095949194939293610e5f565b3461025b57610303610659366004610333565b906120e3565b919060408382031261025b5761026e9061067981856102ca565b9360200161041c565b3461025b576102bd61032361069836600461065f565b906121c3565b3461025b576103036106b1366004610586565b612a24565b3461025b576106c6366004610251565b610303610a9d565b3461025b576102bd6103236106e436600461065f565b90612246565b909160608284031261025b5761026e61070384846102ca565b93610711816020860161041c565b936040016104cc565b3461025b576102bd6103236107303660046106ea565b916119d0565b61026e906103f2600b915f92610379565b3461025b576102bd61032361075d3660046102d7565b610736565b61026e906103f2600a915f92610379565b3461025b576102bd6103236107893660046102d7565b610762565b3461025b576102bd6103b96107a43660046102d7565b6123bd565b3461025b576107b9366004610251565b6102bd6107cd5f546001600160a01b031690565b604051918291826001600160a01b03909116815260200190565b3461025b576107f7366004610251565b6102bd7f0000000000000000000000000000000000000000000000000000000000000000610323565b3461025b57610303610833366004610586565b6121ba565b3461025b5761030361084b366004610586565b61219a565b3461025b57610303610863366004610586565b612104565b3461025b5761030361087b366004610333565b906114e3565b61026e916008021c6001600160a01b031690565b9061026e9154610881565b61026e906108b16003915f92610379565b610895565b6104b161026e61026e926001600160a01b031690565b61026e906108b6565b61026e906108cc565b610288906108d5565b60208101929161028c91906108de565b3461025b576102bd61091261090d3660046102d7565b6108a0565b604051918291826108e7565b3461025b5761092e366004610251565b6103036129c5565b3461025b57610303610949366004610333565b906114b9565b3461025b5761095f366004610251565b6103036129e5565b3461025b5761030361097a366004610586565b6115d5565b60e08183031261025b5761099382826102ca565b926109a183602084016102ca565b926109af81604085016102ca565b926109bd82606083016102ca565b9261026e6109ce84608085016102ca565b936109dc8160a0860161041c565b9360c0016102ca565b6103036109f336600461097f565b95949094939193611421565b3461025b576102bd6103b9610a1536600461065f565b906122c3565b919060408382031261025b5761026e9061034d81856104cc565b3461025b57610303610a48366004610a1b565b906128f9565b3461025b57610303610a61366004610586565b610bd7565b610a6e610afd565b61028c610a8c565b6104b161026e61026e9290565b61026e90610a76565b61028c610a985f610a83565b610c10565b61028c610a66565b0190565b15610ab057565b60405162461bcd60e51b815280610af9600482016020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b0390fd5b61028c610b115f546001600160a01b031690565b610b2a610b1d336104b1565b916001600160a01b031690565b14610aa9565b61028c90610b3c610afd565b610bb3565b15610b4857565b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608490fd5b61028c90610a98610bc66104b15f610a83565b6001600160a01b0383161415610b41565b61028c90610b30565b906001600160a01b03905b9181191691161790565b90610c0561026e610c0c926108d5565b8254610be0565b9055565b5f546001600160a01b031690610c26815f610bf5565b610c59610c537f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0936108d5565b916108d5565b91610c6360405190565b5f90a3565b61026e9060a01c610268565b61026e9054610c68565b61026861026e61026e9290565b15610c9257565b60405162461bcd60e51b815260206004820152601760248201527f526f757465723a20737761702070726f686962697465640000000000000000006044820152606490fd5b9061028c97969594939291610d09610cef6007610c74565b610d03610cfc6001610c7e565b9160ff1690565b14610c8b565b610dff565b15610d1557565b60405162461bcd60e51b815260206004820152601e60248201527f4272696467653a20616d6f756e7420746f6f206c6f7720666f722066656500006044820152606490fd5b15610d6157565b60405162461bcd60e51b815260206004820152602260248201527f4272696467653a2062726964676520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b91908201809211610ded57565b610dcc565b91908203918211610ded57565b610e5a9061028c9897969594939297610e1f610e188290565b8a11610d0e565b610e33610e2b5f610356565b851415610d5a565b610e543491610e4e3393610e48838787611ece565b90610de0565b9a610df2565b91612bdb565b611cb0565b9061028c97969594939291610cd7565b9061028c969594939291610e86610cef6007610c74565b61116c565b61026e9081565b61026e9054610e8b565b15610ea357565b60405162461bcd60e51b815260206004820152601d60248201527f4272696467653a20736f75726365206e6f7420726567697374657265640000006044820152606490fd5b634e487b7160e01b5f52604160045260245ffd5b90601f01601f1916810190811067ffffffffffffffff821117610f1e57604052565b610ee8565b9050519061028c826102c1565b9060208282031261025b5761026e91610f23565b634e487b7160e01b5f52602160045260245ffd5b60041115610f6257565b610f44565b9061028c82610f58565b61026e90610f67565b61028890610f71565b90815260e081019796959094909390929091602086015260408501526060840152608083015260a082015260c00161028c91610f7a565b6040513d5f823e3d90fd5b15610fcc57565b60405162461bcd60e51b815260206004820152601960248201527f4272696467653a206d6573736167652070726f636573736564000000000000006044820152606490fd5b905f1990610beb565b9061102a61026e610c0c92610356565b8254611011565b8015156102c3565b9050519061028c82611031565b9060208282031261025b5761026e91611039565b90815260408101929161028c916020905b0190610f7a565b1561107957565b60405162461bcd60e51b815260206004820152601260248201527f4272696467653a206e6f206d65737361676500000000000000000000000000006044820152606490fd5b61026e90610356565b9061028c6110d460405190565b9283610efc565b67ffffffffffffffff8111610f1e57602090601f01601f19160190565b9061110a611105836110db565b6110c7565b918252565b3d156111285761111e3d6110f8565b903d5f602084013e565b606090565b9095949261028c9461115e6111689261115760809661115060a088019c5f890152565b6020870152565b6040850152565b6060830190610f7a565b0152565b61026e90849597969296949394888860206111fc8961120889600c966111b661119d611198868b610363565b610e92565b6111af6111ab5f9e8f610356565b9190565b1415610e9c565b6111bf306108d5565b9c632ed1084b947f0000000000000000000000000000000000000000000000000000000000000000906111f160405190565b998a98899860e01b90565b88528d60048901610f83565b03818a5afa80156113e3576112dd98602093611284935f936113e8575b506112339161119891610363565b815f526020527fffff0000000000000000000000000000000000000000000000000000000000007dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60405f201691161790565b956112c5600a6112ac61129a6111988b84610379565b6112a66111ab8a610356565b14610fc5565b6112c0896112ba6001610356565b92610379565b61101a565b63e4ce6160906112d460405190565b98899260e01b90565b825281806112ef898b6004840161105a565b03915afa9182156113e3576113126113329361137b985f916113b5575b50611072565b61132b6113266113218b6110be565b610a76565b6108cc565b8098612ab8565b943461133d83610356565b8111611380575b5050507fe9d840d27ab4032a839c20760fb995af8e3ad1980b9428980ca1c7e072acd87a9561137260405190565b9586958661112d565b0390a1565b61139261138d84936108d5565b6108d5565b9061139c60405190565b90818003925af1506113ac61110f565b505f8080611344565b6113d6915060203d81116113dc575b6113ce8183610efc565b810190611046565b5f61130c565b503d6113c4565b610fba565b6111989193509161141161123393873d811161141a575b6114098183610efc565b810190610f30565b93915091611225565b503d6113ff565b9061028c969594939291610e6f565b9061028c9161143d610afd565b611450565b9061102a61026e610c0c9290565b9061145f61028c92600c610363565b611442565b9061028c91611430565b9061028c9161147b610afd565b6114a0565b9060ff90610beb565b9061149961026e610c0c92151590565b8254611480565b6114b461028c92610393600193600d610363565b611489565b9061028c9161146e565b9061028c916114d0610afd565b6114b461028c926103935f93600d610363565b9061028c916114c3565b61028c906114f9610afd565b5f8080809361150a61138d336108d5565b82821561151a575bf1156113e357565b506108fc611512565b61028c906114ed565b61028c90611538610afd565b6115866020611546836108d5565b6370a082319061156f611558306108d5565b9261156260405190565b9586948593849360e01b90565b83526001600160a01b031660048301526024820190565b03915afa9081156113e3575f916115b7575b506115a25f610356565b81116115ac575050565b61028c913390611639565b6115cf915060203d811161141a576114098183610efc565b5f611598565b61028c9061152c565b6115f76115f161026e9263ffffffff1690565b60e01b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b6001600160a01b03909116815260408101929161028c9160200152565b61167c60049261166d61028c9561165363a9059cbb6115de565b9261165d60405190565b968794602086019081520161161c565b60208201810382520383610efc565b61172e565b61168b60206110f8565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602082015290565b61026e611681565b156116c357565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608490fd5b61028c9161173e61174d926108d5565b906117476116b4565b9161178a565b805161175b6111ab5f610356565b1490811561176a575b506116bc565b61178491506020611779825190565b818301019101611046565b5f611764565b61026e92916117985f610356565b91611810565b156117a557565b60405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608490fd5b905f61026e94938192611821606090565b5061183861182e306108d5565b839031101561179e565b60208101905191855af161184a61110f565b9161189c565b1561185757565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b919290156118cd575081516118b36111ab5f610356565b146118bc575090565b6118c861026e916118d3565b611850565b8261194a565b3b6118e06111ab5f610356565b1190565b5f5b8381106118f55750505f910152565b81810151838201526020016118e6565b61192661192f602093610aa59361191a815190565b80835293849260200190565b958691016118e4565b601f01601f191690565b602080825261026e92910190611905565b90611953825190565b61195f6111ab5f610356565b111561196e5750805190602001fd5b610af99061197b60405190565b91829162461bcd60e51b835260048301611939565b90815260408101929161028c9160200152565b9061036d906108d5565b634e487b7160e01b5f52601260045260245ffd5b81156119cb570490565b6119ad565b9091906020906119eb61138d6001546001600160a01b031690565b611a29611a0f6358c09b8f96610e48611a08611198876002610363565b9186612246565b95611a34611a1c60405190565b9788958694859460e01b90565b845260048401611990565b03915afa9081156113e35761026e925f92611a5f575b50611198611a599160046119a3565b906119c1565b611a59919250611a7f6111989160203d811161141a576114098183610efc565b929150611a4a565b611a9361028891610f71565b60f81b90565b01918252565b018092611a87565b829591611b0591611af961026e99611b1e9695611b249899611ac65f90565b50604051978896602088019687610aa594611a9960208096611a998260019c9b98611a998289611a99829b611a9f9d9052565b90810382520382610efc565b611b17611b10825190565b9160200190565b2093610c7e565b91610c7e565b91612c79565b15611b3157565b60405162461bcd60e51b815260206004820152601f60248201527f4272696467653a2077726f6e672064657374696e6174696f6e20636861696e006044820152606490fd5b61026e90610268565b61026e9054611b76565b15611b9057565b60405162461bcd60e51b815260206004820152601e60248201527f4272696467653a20756e6b6e6f776e20636861696e206f7220746f6b656e00006044820152606490fd5b15611bdc57565b60405162461bcd60e51b815260206004820152601b60248201527f4272696467653a20746f6b656e7320616c72656164792073656e7400000000006044820152606490fd5b15611c2857565b60405162461bcd60e51b815260206004820152601660248201527f4272696467653a206e6f7420656e6f75676820666565000000000000000000006044820152606490fd5b9194611ca961106b92989795611ca260a096611c9b61028c9a611c9460c08a019e5f8b0152565b6020890152565b6040870152565b6060850152565b6080830152565b939094919592611cf193856020611d3c89611d478888888f611cf97f00000000000000000000000000000000000000000000000000000000000000009e8f90565b821415611b2a565b611d18611d13611d0e8461039385600d610363565b611b7f565b611b89565b611d21306108d5565b9d632ed1084b96611d3160405190565b9a8b998a9960e01b90565b895260048901610f83565b0381895afa9586156113e35789915f97611e91575b50611dc69291602091611d9a600b611d8c611d7a6111988d84610379565b611d866111ab5f610356565b14611bd5565b6112c08b6112ba6001610356565b611db86318b51308611dab60405190565b9687948593849360e01b90565b835260048301526024820190565b03915afa9586156113e3576111ab61026e611e349261137b9989611e3b975f92611e6f575b50611df69192612516565b907f706abb59b8db30e9e92cb7e272fbe6682712ec97e7b117d7ee3389b24bb3de21611e2160405190565b80611e2d858583611990565b0390a10190565b1015611c21565b7f9cd6008e8d4ebd34fd9d022278fec7f95d133780ecc1a0dea459fae3e967539096611e6660405190565b96879687611c6d565b611df69250611e8b9060203d811161141a576114098183610efc565b91611deb565b602091975091611eb1611dc69493833d811161141a576114098183610efc565b9791509192611d5c565b81810292918115918404141715610ded57565b905f611ed981610356565b8414611fe2575091611f22611198611f2793611f1b84611f01611326611321611f729a6110be565b92611f0b846108d5565b90611f15306108d5565b91612013565b60056119a3565b611ebb565b6020611f3e61138d6001546001600160a01b031690565b6326a49e3790611db87f000000000000000000000000000000000000000000000000000000000000000092611dab60405190565b03915afa9081156113e357611f8e925f92611fc2575b506119c1565b7f02a1962fff2ae0f895c1fcd8481ce39cce1e8083e752ce6f5e80d2b5366382c8611fb860405190565b828152602090a190565b611fdb91925060203d811161141a576114098183610efc565b905f611f88565b91505061026e9150610356565b6001600160a01b0391821681529116602082015260608101929161028c9160400152565b9061167c9061166d61028c9560049561202f6323b872dd6115de565b9361203960405190565b9788956020870190815201611fef565b60405162461bcd60e51b815260206004820152600b60248201527f556e737570706f727465640000000000000000000000000000000000000000006044820152606490fd5b33347f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f885258749161137b6120be60405190565b9283928361161c565b9061028c916120d4610afd565b906112c061028c926002610363565b9061028c916120c7565b61028c906120f9610afd565b61028c906001610bf5565b61028c906120ed565b602061212461138d6001546001600160a01b031690565b9163ca24994c92611a2961213c611198846002610363565b9461215661214960405190565b9687958694859460e01b90565b03915afa9081156113e3575f9161216b575090565b61026e915060203d811161141a576114098183610efc565b61028c9061218f610afd565b61028c906008610bf5565b61028c90612183565b61028c906121af610afd565b61028c906009610bf5565b61028c906121a3565b906121ce6001610f67565b6121d782610f67565b0361220957506020612156916121f861138d6008546001600160a01b031690565b611db86318b5130861156260405190565b61221c6122166002610f67565b91610f67565b1461222b575061026e5f610356565b6020612156916121f861138d6009546001600160a01b031690565b906122516001610f67565b61225a82610f67565b0361228c575060206121569161227b61138d6008546001600160a01b031690565b611db86336d6825a61156260405190565b6122996122166002610f67565b146122a8575061026e5f610356565b60206121569161227b61138d6009546001600160a01b031690565b906122ce6001610f67565b6122d782610f67565b0361234b57506020612309916122f861138d6008546001600160a01b031690565b611db863f69f815161156260405190565b03915afa9081156113e3575f9161232d575b506123286111ab5f610356565b141590565b612345915060203d811161141a576114098183610efc565b5f61231b565b6123586122166002610f67565b03612378576020612309916122f861138d6009546001600160a01b031690565b60405162461bcd60e51b815260206004820152600f60248201527f4e6f7420696d706c656d656e74656400000000000000000000000000000000006044820152606490fd5b6123ef60206123d761138d6008546001600160a01b031690565b6395b70182906123e660405190565b93849260e01b90565b82526004820185905260249082905afa9081156113e3575f9161249f575b505f9061241c6111ab83610356565b141591821561242a57505090565b612458925060209061244761138d6009546001600160a01b031690565b611db86382e3702d611dab60405190565b03915afa9182156113e3575f92612477575b506111ab61232891610356565b6123289192506124976111ab9160203d811161141a576114098183610efc565b92915061246a565b6124b7915060203d811161141a576114098183610efc565b5f61240d565b634e487b7160e01b5f52603260045260245ffd5b61026861026e61026e9260ff1690565b61026e9060f81c6124d1565b61026e61026e61026e9260ff1690565b610288906124ed565b60208101929161028c91906124fd565b6001929161252384610f67565b61252c82610f67565b03612642575061254761138d6008546001600160a01b031690565b926125566318b5130891610356565b90602082101561263d5761259f9461259461257d612578866020961a60f81b90565b6124e1565b9261258760405190565b9788948593849360e01b90565b835260048301612506565b03915afa9283156113e3575f9361261d575b50826125c861138d6008546001600160a01b031690565b9163e12c9ca892803b1561025b57611db85f936125f5956125e860405190565b9687958694859360e01b90565b03925af180156113e3576126065750565b61028c905f6126158183610efc565b810190610251565b61263691935060203d811161141a576114098183610efc565b915f6125b1565b6124bd565b61264f6122166002610f67565b036123785761266961138d6009546001600160a01b031690565b926126786318b5130891610356565b90602082101561263d5761269a9461259461257d612578866020961a60f81b90565b03915afa9283156113e3575f936126c3575b50826125c861138d6009546001600160a01b031690565b6126dc91935060203d811161141a576114098183610efc565b915f6126ac565b9061028c949392916126f8610cef6007610c74565b61273f565b9194611ca961116892989795611ca260a096611c9b61028c9a61272f60c08a019e5f8b01906001600160a01b03169052565b6001600160a01b03166020890152565b9161137b91949261275d339461275683878a612bdb565b8785612ab8565b917fe85f63622be58135a84c6e9de632115a3c471b0540a04d37a7c53a0647cd0c399661278960405190565b968796876126fd565b9061028c949392916126e3565b9061028c916127ac610afd565b6127f3565b60ff81166102c3565b9050519061028c826127b1565b9060208282031261025b5761026e916127ba565b61026e6012610356565b604d8111610ded57600a0a90565b61132661132161283f936128146128199461280f836003610379565b610bf5565b6110be565b602061282761138d836108d5565b63313ce5679061283660405190565b94859260e01b90565b825260049082905afa80156113e3576128c36128ae61286c6112c09361028c965f916128cb575b506124ed565b6128746127db565b6128be6128b36128ae6128878585610df2565b7f000000000000000000000000000000000000000000000000000000000000000090610de0565b6127e5565b6112c08860056119a3565b610df2565b9160046119a3565b6128ec915060203d81116128f2575b6128e48183610efc565b8101906127c7565b5f612866565b503d6128da565b9061028c9161279f565b1561290a57565b60405162461bcd60e51b815260206004820152601c60248201527f526f757465723a206973206e6f742073746f70417574686f72697479000000006044820152606490fd5b6129736129646006546001600160a01b031690565b61296d33610b1d565b14612903565b61028c6129b2565b9074ff00000000000000000000000000000000000000009060a01b610beb565b906129ab61026e610c0c926124d1565b825461297b565b61028c6129be5f610c7e565b600761299b565b61028c61294f565b6129d5610afd565b61028c61028c6129be6001610c7e565b61028c6129cd565b61028c906129f9610afd565b61028c906006610bf5565b61028c906129ed565b61028c90612a19610afd565b61028c906007610bf5565b61028c90612a0d565b15612a3457565b60405162461bcd60e51b815260206004820152601760248201527f526f757465723a206e6f207265636569766520706f6f6c0000000000000000006044820152606490fd5b612ab061028c94612aa9606094989795612aa2608086019a5f8701906001600160a01b03169052565b6020850152565b6040830152565b019015159052565b92612b48612ae561138d612ad860209697612ad05f90565b506003610379565b546001600160a01b031690565b915f90612b08612af76104b184610a83565b6001600160a01b0386161415612a2d565b612b53632d46f63e91612b266104b16007546001600160a01b031690565b6001600160a01b0388161490612b3b60405190565b998a988997889660e01b90565b865260048601612a79565b03925af19081156113e3575f9161216b575090565b15612b6f57565b60405162461bcd60e51b815260206004820152600f60248201527f526f757465723a206e6f20706f6f6c00000000000000000000000000000000006044820152606490fd5b6001600160a01b03909116815260608101939261028c929091604091612ab0906020830152565b60209190612c6e612bf361138d612ad8846003610379565b91612c30868486612c2b61138d61138d6113266113215f99612814612c1a6104b18d610a83565b6001600160a01b038a161415612b68565b612013565b6328fdb481612b53612c4d6104b16007546001600160a01b031690565b6001600160a01b03871614612c6160405190565b9889978896879560e01b90565b855260048501612bb4565b5f525f536001535f519056fea2646970667358221220e1780f47783b02b3cb746afb003b635f7646ee9a1a3ec2bf33647c98a0a2e41364736f6c63430008140033000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000013313c46533ca196577edd74c578c09d95524c4f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044abfa4c87ec30a7c49063b7610cab73a5f62cc0
Deployed Bytecode
0x6080604052600436101561001d575b366120495761001b61208e565b005b5f3560e01c806310ce05161461024c5780631268cee81461024757806318b51308146102425780632c48bb8d1461023d5780632d4b01b5146102385780632ed1084b14610233578063331838b21461022e57806336d6825a146102295780633ee2594e146102245780634927b44c1461021f5780634cd480bd1461021a5780635fa1ebd91461021557806368593337146102105780636cfd15531461020b578063715018a614610206578063737dae1d146102015780637d18330b146101fc57806382e3702d146101f757806388ba16ab146101f25780638b9369e2146101ed5780638da5cb5b146101e85780639a8a0592146101e35780639bb55742146101de578063a1f5e9cd146101d9578063a87b8152146101d4578063aab65192146101cf578063b5217bb4146101ca578063b633b364146101c5578063c7c56d7e146101c0578063c992b5b2146101bb578063e3f04fa4146101b6578063e43bfe5e146101b1578063e4ce6160146101ac578063eb588675146101a75763f2fde38b0361000e57610a4e565b610a35565b6109ff565b6109e5565b610967565b61094f565b610936565b61091e565b6108f7565b610868565b610850565b610838565b610820565b6107e7565b6107a9565b61078e565b610773565b610747565b61071a565b6106ce565b6106b6565b61069e565b610682565b610646565b610629565b61059a565b61056d565b610552565b610525565b61048f565b6103f7565b61039d565b610308565b6102eb565b61028e565b5f91031261025b57565b5f80fd5b61026e916008021c5b60ff1690565b90565b9061026e915461025f565b61026e60146007610271565b9052565b565b3461025b5761029e366004610251565b6102bd6102a961027c565b6040519182918260ff909116815260200190565b0390f35b805b0361025b57565b9050359061028c826102c1565b9060208282031261025b5761026e916102ca565b3461025b576103036102fe3660046102d7565b611523565b604051005b3461025b576102bd61032361031e3660046102d7565b61210d565b6040519182918290815260200190565b919060408382031261025b5761026e9061034d81856102ca565b936020016102ca565b61026e61026e61026e9290565b9061036d90610356565b5f5260205260405f2090565b9061036d565b61039861026e92610393600d935f94610363565b610379565b610271565b3461025b576102bd6103b96103b3366004610333565b9061037f565b60405191829182901515815260200190565b61026e916008021c81565b9061026e91546103cb565b61026e906103f2600c915f92610363565b6103d6565b3461025b576102bd61032361040d3660046102d7565b6103e1565b6004111561025b57565b9050359061028c82610412565b60e08183031261025b5761043d82826102ca565b9261044b83602084016102ca565b9261045981604085016102ca565b9261046782606083016102ca565b9261026e61047884608085016102ca565b936104868160a086016102ca565b9360c00161041c565b3461025b576102bd6103236104a5366004610429565b95949094939193611aa7565b6001600160a01b031690565b6001600160a01b0381166102c3565b9050359061028c826104bd565b919060a08382031261025b576104ef81846102ca565b926104fd82602083016102ca565b9261026e61050e84604085016102ca565b9361051c81606086016104cc565b936080016102ca565b3461025b576103036105383660046104d9565b93929092612792565b61026e906103f26002915f92610363565b3461025b576102bd6103236105683660046102d7565b610541565b3461025b57610303610580366004610333565b90611464565b9060208282031261025b5761026e916104cc565b3461025b576103036105ad366004610586565b612a04565b91906101008382031261025b576105c981846102ca565b926105d782602083016102ca565b926105e583604084016102ca565b926105f381606085016102ca565b9261060182608083016102ca565b9261026e6106128460a085016102ca565b936106208160c0860161041c565b9360e0016102ca565b6103036106373660046105b2565b96959095949194939293610e5f565b3461025b57610303610659366004610333565b906120e3565b919060408382031261025b5761026e9061067981856102ca565b9360200161041c565b3461025b576102bd61032361069836600461065f565b906121c3565b3461025b576103036106b1366004610586565b612a24565b3461025b576106c6366004610251565b610303610a9d565b3461025b576102bd6103236106e436600461065f565b90612246565b909160608284031261025b5761026e61070384846102ca565b93610711816020860161041c565b936040016104cc565b3461025b576102bd6103236107303660046106ea565b916119d0565b61026e906103f2600b915f92610379565b3461025b576102bd61032361075d3660046102d7565b610736565b61026e906103f2600a915f92610379565b3461025b576102bd6103236107893660046102d7565b610762565b3461025b576102bd6103b96107a43660046102d7565b6123bd565b3461025b576107b9366004610251565b6102bd6107cd5f546001600160a01b031690565b604051918291826001600160a01b03909116815260200190565b3461025b576107f7366004610251565b6102bd7f000000000000000000000000000000000000000000000000000000000000000e610323565b3461025b57610303610833366004610586565b6121ba565b3461025b5761030361084b366004610586565b61219a565b3461025b57610303610863366004610586565b612104565b3461025b5761030361087b366004610333565b906114e3565b61026e916008021c6001600160a01b031690565b9061026e9154610881565b61026e906108b16003915f92610379565b610895565b6104b161026e61026e926001600160a01b031690565b61026e906108b6565b61026e906108cc565b610288906108d5565b60208101929161028c91906108de565b3461025b576102bd61091261090d3660046102d7565b6108a0565b604051918291826108e7565b3461025b5761092e366004610251565b6103036129c5565b3461025b57610303610949366004610333565b906114b9565b3461025b5761095f366004610251565b6103036129e5565b3461025b5761030361097a366004610586565b6115d5565b60e08183031261025b5761099382826102ca565b926109a183602084016102ca565b926109af81604085016102ca565b926109bd82606083016102ca565b9261026e6109ce84608085016102ca565b936109dc8160a0860161041c565b9360c0016102ca565b6103036109f336600461097f565b95949094939193611421565b3461025b576102bd6103b9610a1536600461065f565b906122c3565b919060408382031261025b5761026e9061034d81856104cc565b3461025b57610303610a48366004610a1b565b906128f9565b3461025b57610303610a61366004610586565b610bd7565b610a6e610afd565b61028c610a8c565b6104b161026e61026e9290565b61026e90610a76565b61028c610a985f610a83565b610c10565b61028c610a66565b0190565b15610ab057565b60405162461bcd60e51b815280610af9600482016020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b0390fd5b61028c610b115f546001600160a01b031690565b610b2a610b1d336104b1565b916001600160a01b031690565b14610aa9565b61028c90610b3c610afd565b610bb3565b15610b4857565b60405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608490fd5b61028c90610a98610bc66104b15f610a83565b6001600160a01b0383161415610b41565b61028c90610b30565b906001600160a01b03905b9181191691161790565b90610c0561026e610c0c926108d5565b8254610be0565b9055565b5f546001600160a01b031690610c26815f610bf5565b610c59610c537f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0936108d5565b916108d5565b91610c6360405190565b5f90a3565b61026e9060a01c610268565b61026e9054610c68565b61026861026e61026e9290565b15610c9257565b60405162461bcd60e51b815260206004820152601760248201527f526f757465723a20737761702070726f686962697465640000000000000000006044820152606490fd5b9061028c97969594939291610d09610cef6007610c74565b610d03610cfc6001610c7e565b9160ff1690565b14610c8b565b610dff565b15610d1557565b60405162461bcd60e51b815260206004820152601e60248201527f4272696467653a20616d6f756e7420746f6f206c6f7720666f722066656500006044820152606490fd5b15610d6157565b60405162461bcd60e51b815260206004820152602260248201527f4272696467653a2062726964676520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608490fd5b634e487b7160e01b5f52601160045260245ffd5b91908201809211610ded57565b610dcc565b91908203918211610ded57565b610e5a9061028c9897969594939297610e1f610e188290565b8a11610d0e565b610e33610e2b5f610356565b851415610d5a565b610e543491610e4e3393610e48838787611ece565b90610de0565b9a610df2565b91612bdb565b611cb0565b9061028c97969594939291610cd7565b9061028c969594939291610e86610cef6007610c74565b61116c565b61026e9081565b61026e9054610e8b565b15610ea357565b60405162461bcd60e51b815260206004820152601d60248201527f4272696467653a20736f75726365206e6f7420726567697374657265640000006044820152606490fd5b634e487b7160e01b5f52604160045260245ffd5b90601f01601f1916810190811067ffffffffffffffff821117610f1e57604052565b610ee8565b9050519061028c826102c1565b9060208282031261025b5761026e91610f23565b634e487b7160e01b5f52602160045260245ffd5b60041115610f6257565b610f44565b9061028c82610f58565b61026e90610f67565b61028890610f71565b90815260e081019796959094909390929091602086015260408501526060840152608083015260a082015260c00161028c91610f7a565b6040513d5f823e3d90fd5b15610fcc57565b60405162461bcd60e51b815260206004820152601960248201527f4272696467653a206d6573736167652070726f636573736564000000000000006044820152606490fd5b905f1990610beb565b9061102a61026e610c0c92610356565b8254611011565b8015156102c3565b9050519061028c82611031565b9060208282031261025b5761026e91611039565b90815260408101929161028c916020905b0190610f7a565b1561107957565b60405162461bcd60e51b815260206004820152601260248201527f4272696467653a206e6f206d65737361676500000000000000000000000000006044820152606490fd5b61026e90610356565b9061028c6110d460405190565b9283610efc565b67ffffffffffffffff8111610f1e57602090601f01601f19160190565b9061110a611105836110db565b6110c7565b918252565b3d156111285761111e3d6110f8565b903d5f602084013e565b606090565b9095949261028c9461115e6111689261115760809661115060a088019c5f890152565b6020870152565b6040850152565b6060830190610f7a565b0152565b61026e90849597969296949394888860206111fc8961120889600c966111b661119d611198868b610363565b610e92565b6111af6111ab5f9e8f610356565b9190565b1415610e9c565b6111bf306108d5565b9c632ed1084b947f000000000000000000000000000000000000000000000000000000000000000e906111f160405190565b998a98899860e01b90565b88528d60048901610f83565b03818a5afa80156113e3576112dd98602093611284935f936113e8575b506112339161119891610363565b815f526020527fffff0000000000000000000000000000000000000000000000000000000000007dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60405f201691161790565b956112c5600a6112ac61129a6111988b84610379565b6112a66111ab8a610356565b14610fc5565b6112c0896112ba6001610356565b92610379565b61101a565b63e4ce6160906112d460405190565b98899260e01b90565b825281806112ef898b6004840161105a565b03915afa9182156113e3576113126113329361137b985f916113b5575b50611072565b61132b6113266113218b6110be565b610a76565b6108cc565b8098612ab8565b943461133d83610356565b8111611380575b5050507fe9d840d27ab4032a839c20760fb995af8e3ad1980b9428980ca1c7e072acd87a9561137260405190565b9586958661112d565b0390a1565b61139261138d84936108d5565b6108d5565b9061139c60405190565b90818003925af1506113ac61110f565b505f8080611344565b6113d6915060203d81116113dc575b6113ce8183610efc565b810190611046565b5f61130c565b503d6113c4565b610fba565b6111989193509161141161123393873d811161141a575b6114098183610efc565b810190610f30565b93915091611225565b503d6113ff565b9061028c969594939291610e6f565b9061028c9161143d610afd565b611450565b9061102a61026e610c0c9290565b9061145f61028c92600c610363565b611442565b9061028c91611430565b9061028c9161147b610afd565b6114a0565b9060ff90610beb565b9061149961026e610c0c92151590565b8254611480565b6114b461028c92610393600193600d610363565b611489565b9061028c9161146e565b9061028c916114d0610afd565b6114b461028c926103935f93600d610363565b9061028c916114c3565b61028c906114f9610afd565b5f8080809361150a61138d336108d5565b82821561151a575bf1156113e357565b506108fc611512565b61028c906114ed565b61028c90611538610afd565b6115866020611546836108d5565b6370a082319061156f611558306108d5565b9261156260405190565b9586948593849360e01b90565b83526001600160a01b031660048301526024820190565b03915afa9081156113e3575f916115b7575b506115a25f610356565b81116115ac575050565b61028c913390611639565b6115cf915060203d811161141a576114098183610efc565b5f611598565b61028c9061152c565b6115f76115f161026e9263ffffffff1690565b60e01b90565b7fffffffff000000000000000000000000000000000000000000000000000000001690565b6001600160a01b03909116815260408101929161028c9160200152565b61167c60049261166d61028c9561165363a9059cbb6115de565b9261165d60405190565b968794602086019081520161161c565b60208201810382520383610efc565b61172e565b61168b60206110f8565b7f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564602082015290565b61026e611681565b156116c357565b60405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608490fd5b61028c9161173e61174d926108d5565b906117476116b4565b9161178a565b805161175b6111ab5f610356565b1490811561176a575b506116bc565b61178491506020611779825190565b818301019101611046565b5f611764565b61026e92916117985f610356565b91611810565b156117a557565b60405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c00000000000000000000000000000000000000000000000000006064820152608490fd5b905f61026e94938192611821606090565b5061183861182e306108d5565b839031101561179e565b60208101905191855af161184a61110f565b9161189c565b1561185757565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b919290156118cd575081516118b36111ab5f610356565b146118bc575090565b6118c861026e916118d3565b611850565b8261194a565b3b6118e06111ab5f610356565b1190565b5f5b8381106118f55750505f910152565b81810151838201526020016118e6565b61192661192f602093610aa59361191a815190565b80835293849260200190565b958691016118e4565b601f01601f191690565b602080825261026e92910190611905565b90611953825190565b61195f6111ab5f610356565b111561196e5750805190602001fd5b610af99061197b60405190565b91829162461bcd60e51b835260048301611939565b90815260408101929161028c9160200152565b9061036d906108d5565b634e487b7160e01b5f52601260045260245ffd5b81156119cb570490565b6119ad565b9091906020906119eb61138d6001546001600160a01b031690565b611a29611a0f6358c09b8f96610e48611a08611198876002610363565b9186612246565b95611a34611a1c60405190565b9788958694859460e01b90565b845260048401611990565b03915afa9081156113e35761026e925f92611a5f575b50611198611a599160046119a3565b906119c1565b611a59919250611a7f6111989160203d811161141a576114098183610efc565b929150611a4a565b611a9361028891610f71565b60f81b90565b01918252565b018092611a87565b829591611b0591611af961026e99611b1e9695611b249899611ac65f90565b50604051978896602088019687610aa594611a9960208096611a998260019c9b98611a998289611a99829b611a9f9d9052565b90810382520382610efc565b611b17611b10825190565b9160200190565b2093610c7e565b91610c7e565b91612c79565b15611b3157565b60405162461bcd60e51b815260206004820152601f60248201527f4272696467653a2077726f6e672064657374696e6174696f6e20636861696e006044820152606490fd5b61026e90610268565b61026e9054611b76565b15611b9057565b60405162461bcd60e51b815260206004820152601e60248201527f4272696467653a20756e6b6e6f776e20636861696e206f7220746f6b656e00006044820152606490fd5b15611bdc57565b60405162461bcd60e51b815260206004820152601b60248201527f4272696467653a20746f6b656e7320616c72656164792073656e7400000000006044820152606490fd5b15611c2857565b60405162461bcd60e51b815260206004820152601660248201527f4272696467653a206e6f7420656e6f75676820666565000000000000000000006044820152606490fd5b9194611ca961106b92989795611ca260a096611c9b61028c9a611c9460c08a019e5f8b0152565b6020890152565b6040870152565b6060850152565b6080830152565b939094919592611cf193856020611d3c89611d478888888f611cf97f000000000000000000000000000000000000000000000000000000000000000e9e8f90565b821415611b2a565b611d18611d13611d0e8461039385600d610363565b611b7f565b611b89565b611d21306108d5565b9d632ed1084b96611d3160405190565b9a8b998a9960e01b90565b895260048901610f83565b0381895afa9586156113e35789915f97611e91575b50611dc69291602091611d9a600b611d8c611d7a6111988d84610379565b611d866111ab5f610356565b14611bd5565b6112c08b6112ba6001610356565b611db86318b51308611dab60405190565b9687948593849360e01b90565b835260048301526024820190565b03915afa9586156113e3576111ab61026e611e349261137b9989611e3b975f92611e6f575b50611df69192612516565b907f706abb59b8db30e9e92cb7e272fbe6682712ec97e7b117d7ee3389b24bb3de21611e2160405190565b80611e2d858583611990565b0390a10190565b1015611c21565b7f9cd6008e8d4ebd34fd9d022278fec7f95d133780ecc1a0dea459fae3e967539096611e6660405190565b96879687611c6d565b611df69250611e8b9060203d811161141a576114098183610efc565b91611deb565b602091975091611eb1611dc69493833d811161141a576114098183610efc565b9791509192611d5c565b81810292918115918404141715610ded57565b905f611ed981610356565b8414611fe2575091611f22611198611f2793611f1b84611f01611326611321611f729a6110be565b92611f0b846108d5565b90611f15306108d5565b91612013565b60056119a3565b611ebb565b6020611f3e61138d6001546001600160a01b031690565b6326a49e3790611db87f000000000000000000000000000000000000000000000000000000000000000e92611dab60405190565b03915afa9081156113e357611f8e925f92611fc2575b506119c1565b7f02a1962fff2ae0f895c1fcd8481ce39cce1e8083e752ce6f5e80d2b5366382c8611fb860405190565b828152602090a190565b611fdb91925060203d811161141a576114098183610efc565b905f611f88565b91505061026e9150610356565b6001600160a01b0391821681529116602082015260608101929161028c9160400152565b9061167c9061166d61028c9560049561202f6323b872dd6115de565b9361203960405190565b9788956020870190815201611fef565b60405162461bcd60e51b815260206004820152600b60248201527f556e737570706f727465640000000000000000000000000000000000000000006044820152606490fd5b33347f88a5966d370b9919b20f3e2c13ff65706f196a4e32cc2c12bf57088f885258749161137b6120be60405190565b9283928361161c565b9061028c916120d4610afd565b906112c061028c926002610363565b9061028c916120c7565b61028c906120f9610afd565b61028c906001610bf5565b61028c906120ed565b602061212461138d6001546001600160a01b031690565b9163ca24994c92611a2961213c611198846002610363565b9461215661214960405190565b9687958694859460e01b90565b03915afa9081156113e3575f9161216b575090565b61026e915060203d811161141a576114098183610efc565b61028c9061218f610afd565b61028c906008610bf5565b61028c90612183565b61028c906121af610afd565b61028c906009610bf5565b61028c906121a3565b906121ce6001610f67565b6121d782610f67565b0361220957506020612156916121f861138d6008546001600160a01b031690565b611db86318b5130861156260405190565b61221c6122166002610f67565b91610f67565b1461222b575061026e5f610356565b6020612156916121f861138d6009546001600160a01b031690565b906122516001610f67565b61225a82610f67565b0361228c575060206121569161227b61138d6008546001600160a01b031690565b611db86336d6825a61156260405190565b6122996122166002610f67565b146122a8575061026e5f610356565b60206121569161227b61138d6009546001600160a01b031690565b906122ce6001610f67565b6122d782610f67565b0361234b57506020612309916122f861138d6008546001600160a01b031690565b611db863f69f815161156260405190565b03915afa9081156113e3575f9161232d575b506123286111ab5f610356565b141590565b612345915060203d811161141a576114098183610efc565b5f61231b565b6123586122166002610f67565b03612378576020612309916122f861138d6009546001600160a01b031690565b60405162461bcd60e51b815260206004820152600f60248201527f4e6f7420696d706c656d656e74656400000000000000000000000000000000006044820152606490fd5b6123ef60206123d761138d6008546001600160a01b031690565b6395b70182906123e660405190565b93849260e01b90565b82526004820185905260249082905afa9081156113e3575f9161249f575b505f9061241c6111ab83610356565b141591821561242a57505090565b612458925060209061244761138d6009546001600160a01b031690565b611db86382e3702d611dab60405190565b03915afa9182156113e3575f92612477575b506111ab61232891610356565b6123289192506124976111ab9160203d811161141a576114098183610efc565b92915061246a565b6124b7915060203d811161141a576114098183610efc565b5f61240d565b634e487b7160e01b5f52603260045260245ffd5b61026861026e61026e9260ff1690565b61026e9060f81c6124d1565b61026e61026e61026e9260ff1690565b610288906124ed565b60208101929161028c91906124fd565b6001929161252384610f67565b61252c82610f67565b03612642575061254761138d6008546001600160a01b031690565b926125566318b5130891610356565b90602082101561263d5761259f9461259461257d612578866020961a60f81b90565b6124e1565b9261258760405190565b9788948593849360e01b90565b835260048301612506565b03915afa9283156113e3575f9361261d575b50826125c861138d6008546001600160a01b031690565b9163e12c9ca892803b1561025b57611db85f936125f5956125e860405190565b9687958694859360e01b90565b03925af180156113e3576126065750565b61028c905f6126158183610efc565b810190610251565b61263691935060203d811161141a576114098183610efc565b915f6125b1565b6124bd565b61264f6122166002610f67565b036123785761266961138d6009546001600160a01b031690565b926126786318b5130891610356565b90602082101561263d5761269a9461259461257d612578866020961a60f81b90565b03915afa9283156113e3575f936126c3575b50826125c861138d6009546001600160a01b031690565b6126dc91935060203d811161141a576114098183610efc565b915f6126ac565b9061028c949392916126f8610cef6007610c74565b61273f565b9194611ca961116892989795611ca260a096611c9b61028c9a61272f60c08a019e5f8b01906001600160a01b03169052565b6001600160a01b03166020890152565b9161137b91949261275d339461275683878a612bdb565b8785612ab8565b917fe85f63622be58135a84c6e9de632115a3c471b0540a04d37a7c53a0647cd0c399661278960405190565b968796876126fd565b9061028c949392916126e3565b9061028c916127ac610afd565b6127f3565b60ff81166102c3565b9050519061028c826127b1565b9060208282031261025b5761026e916127ba565b61026e6012610356565b604d8111610ded57600a0a90565b61132661132161283f936128146128199461280f836003610379565b610bf5565b6110be565b602061282761138d836108d5565b63313ce5679061283660405190565b94859260e01b90565b825260049082905afa80156113e3576128c36128ae61286c6112c09361028c965f916128cb575b506124ed565b6128746127db565b6128be6128b36128ae6128878585610df2565b7f000000000000000000000000000000000000000000000000000000000000001290610de0565b6127e5565b6112c08860056119a3565b610df2565b9160046119a3565b6128ec915060203d81116128f2575b6128e48183610efc565b8101906127c7565b5f612866565b503d6128da565b9061028c9161279f565b1561290a57565b60405162461bcd60e51b815260206004820152601c60248201527f526f757465723a206973206e6f742073746f70417574686f72697479000000006044820152606490fd5b6129736129646006546001600160a01b031690565b61296d33610b1d565b14612903565b61028c6129b2565b9074ff00000000000000000000000000000000000000009060a01b610beb565b906129ab61026e610c0c926124d1565b825461297b565b61028c6129be5f610c7e565b600761299b565b61028c61294f565b6129d5610afd565b61028c61028c6129be6001610c7e565b61028c6129cd565b61028c906129f9610afd565b61028c906006610bf5565b61028c906129ed565b61028c90612a19610afd565b61028c906007610bf5565b61028c90612a0d565b15612a3457565b60405162461bcd60e51b815260206004820152601760248201527f526f757465723a206e6f207265636569766520706f6f6c0000000000000000006044820152606490fd5b612ab061028c94612aa9606094989795612aa2608086019a5f8701906001600160a01b03169052565b6020850152565b6040830152565b019015159052565b92612b48612ae561138d612ad860209697612ad05f90565b506003610379565b546001600160a01b031690565b915f90612b08612af76104b184610a83565b6001600160a01b0386161415612a2d565b612b53632d46f63e91612b266104b16007546001600160a01b031690565b6001600160a01b0388161490612b3b60405190565b998a988997889660e01b90565b865260048601612a79565b03925af19081156113e3575f9161216b575090565b15612b6f57565b60405162461bcd60e51b815260206004820152600f60248201527f526f757465723a206e6f20706f6f6c00000000000000000000000000000000006044820152606490fd5b6001600160a01b03909116815260608101939261028c929091604091612ab0906020830152565b60209190612c6e612bf361138d612ad8846003610379565b91612c30868486612c2b61138d61138d6113266113215f99612814612c1a6104b18d610a83565b6001600160a01b038a161415612b68565b612013565b6328fdb481612b53612c4d6104b16007546001600160a01b031690565b6001600160a01b03871614612c6160405190565b9889978896879560e01b90565b855260048501612bb4565b5f525f536001535f519056fea2646970667358221220e1780f47783b02b3cb746afb003b635f7646ee9a1a3ec2bf33647c98a0a2e41364736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000013313c46533ca196577edd74c578c09d95524c4f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000044abfa4c87ec30a7c49063b7610cab73a5f62cc0
-----Decoded View---------------
Arg [0] : chainId_ (uint256): 14
Arg [1] : chainPrecision_ (uint256): 18
Arg [2] : allbridgeMessenger_ (address): 0x13313C46533cA196577EdD74C578c09D95524C4F
Arg [3] : wormholeMessenger_ (address): 0x0000000000000000000000000000000000000000
Arg [4] : gasOracle_ (address): 0x44ABfA4C87Ec30A7c49063B7610caB73a5f62cC0
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [2] : 00000000000000000000000013313c46533ca196577edd74c578c09d95524c4f
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 00000000000000000000000044abfa4c87ec30a7c49063b7610cab73a5f62cc0
Deployed Bytecode Sourcemap
747:13368:26:-:0;;;;;;;;;-1:-1:-1;747:13368:26;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;:::i;1538:33:36:-;;;;;:::i;747:13368:26:-;;;:::o;:::-;:::o;:::-;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;:::i;:::-;;;-1:-1:-1;747:13368:26;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::o;:::-;;;;1256:108;;;;;;;;;;:::i;:::-;;:::i;:::-;;:::i;747:13368::-;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;1134:75::-;;;;;;;;;:::i;:::-;;:::i;747:13368::-;;;;;;;;;;:::i;:::-;;:::i;:::-;;-1:-1:-1;747:13368:26;;;:::o;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;747:13368:26;;:::o;:::-;-1:-1:-1;;;;;747:13368:26;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;:::i;386:52:30:-;;;;;;;;;:::i;747:13368:26:-;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;:::i;1014:72::-;;;;;;;;;:::i;747:13368::-;;;;;;;;;;:::i;:::-;;:::i;926:82::-;;;;;;;;;:::i;747:13368::-;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;:::-;;;1273:6:16;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;;;;;;;;-1:-1:-1;;;;;747:13368:26;;;;;;;;;;;;;;;;;:::i;:::-;;882:38;747:13368;882:38;747:13368;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;-1:-1:-1;;;;;747:13368:26;;;;;;;;;:::i;598:45:36:-;;;;;;;;;:::i;:::-;;:::i;747:13368:26:-;;;;;-1:-1:-1;;;;;747:13368:26;;;;;;;:::i;:::-;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;;;:::i;:::-;;:::i;1063:62:16:-;;;:::i;:::-;1117:1;;:::i;747:13368:26:-;;;;;;;;;;;:::i;1824:101:16:-;1907:10;;1915:1;1907:10;:::i;:::-;;:::i;1824:101::-;;;:::i;747:13368:26:-;;;:::o;:::-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;1359:130:16;1414:68;1422:7;1273:6;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;1422:7:16;:23;;734:10:23;1433:12:16;655:96:23;1422:23:16;;-1:-1:-1;;;;;747:13368:26;;;1422:23:16;;1414:68;:::i;1063:62::-;1117:1;1063:62;;;:::i;:::-;1117:1;:::i;747:13368:26:-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;;2074:198:16;2256:8;2074:198;2154:73;2162:22;2174:10;2182:1;2174:10;:::i;2162:22::-;-1:-1:-1;;;;;747:13368:26;;2162:22:16;;2154:73;:::i;2074:198::-;;;;:::i;747:13368:26:-;;-1:-1:-1;;;;;747:13368:26;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;:::i;:::-;;;:::o;2426:187:16:-;2518:6;747:13368:26;-1:-1:-1;;;;;747:13368:26;2534:17:16;;;;;:::i;:::-;2566:40;;;;;:::i;:::-;;;:::i;:::-;;;747:13368:26;;;;2566:40:16;;;;2426:187::o;747:13368:26:-;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;2055:99:36;;2146:1;2055:99;;;;;;;2088:48;2096:7;;;:::i;:::-;:12;;2107:1;2096:12;:::i;:::-;;747:13368:26;;;;2096:12:36;;2088:48;:::i;:::-;2146:1;:::i;747:13368:26:-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;:::o;:::-;;:::i;:::-;;;;;;;;;;:::o;4200:803::-;4834:53;4200:803;4984:11;4200:803;;;;;;;;4497:66;4505:23;;747:13368;;4505:23;;;4497:66;:::i;:::-;4573:61;4581:14;4594:1;4581:14;:::i;:::-;;;;4573:61;:::i;:::-;4782:23;4663:9;4716:10;4663:87;4716:10;4675:75;;;;;;:::i;:::-;4663:87;;:::i;:::-;4782:23;;:::i;:::-;4834:53;;:::i;:::-;4984:11;:::i;4200:803::-;;;;;;;;;;;:::i;2055:99:36:-;;2146:1;2055:99;;;;;;2088:48;2096:7;;;:::i;2088:48::-;2146:1;:::i;747:13368:26:-;;;;;;;;;;:::i;:::-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;-1:-1:-1;;747:13368:26;;;;;;;;;;;;;;:::o;:::-;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;747:13368:26;;;;;;;;;;-1:-1:-1;747:13368:26;;;:::o;:::-;;:::i;:::-;;;;;:::i;:::-;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;747:13368:26;;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;;;;-1:-1:-1;;747:13368:26;;;:::o;:::-;;;;;;:::i;:::-;;:::i;:::-;;;;:::o;:::-;;;;;;;;:::i;:::-;;;;;;;;:::o;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;5764:1473;6074:10;5764:1473;;;;;;;;;;6043:12;;6156:104;;6043:12;6156:104;6043:12;;:27;6035:83;6043:27;;;;;:::i;:::-;;:::i;:::-;:41;;6082:1;6074:10;;;:::i;6043:41::-;;747:13368;;6043:41;;;6035:83;:::i;:::-;6156:29;:4;:29;:::i;:::-;;;6220:7;;6156:104;;747:13368;;;;6156:104;;;;;;747:13368;;;;6156:104;;;;;;;;:::i;:::-;;;;;;;;;;6603:53;6156:104;6603:53;6156:104;:161;:104;;;;;5764:1473;6289:27;;;;;;:::i;:::-;538:486:47;;;;;;;;;;;;;;432:598;;6156:161:26;6336:17;6483:40;6336:17;6328:79;6336:36;;;;;:::i;:::-;:41;;;;:::i;:::-;;6328:79;:::i;:::-;6483:36;6522:1;6483:40;6522:1;6483:40;:::i;:::-;:36;;:::i;:::-;:40;:::i;:::-;6603:23;:53;;747:13368;;;;6603:53;;;;747:13368;;;;6603:53;;;;;;;;;;;;:::i;:::-;;;;;;;;;;6595:84;6711:156;6603:53;7153:77;6603:53;;;;;5764:1473;6595:84;;:::i;:::-;6774:33;6782:24;6790:15;;;:::i;:::-;6782:24;:::i;:::-;6774:33;:::i;:::-;6711:156;;;:::i;:::-;6924:9;;:13;;;:::i;:::-;;;6920:219;;5764:1473;7153:77;;;;;;747:13368;;;;7153:77;;;;;;:::i;:::-;;;;5764:1473::o;6920:219::-;7059:47;:42;;;;:::i;:::-;:47;:::i;:::-;:69;;747:13368;;;;7059:69;;;;;;;;;;;:::i;:::-;;6920:219;;;;;6603:53;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;:::i;6156:104::-;6289:27;6156:104;;;;;6289:27;6156:104;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;;5764:1473;;;;;;;;;;:::i;1063:62:16:-;;1117:1;1063:62;;;:::i;:::-;1117:1;:::i;747:13368:26:-;;;;;;;;7547:145;;7647:22;:38;7547:145;7647:12;:22;:::i;:::-;:38;:::i;7547:145::-;;;;;:::i;1063:62:16:-;;1117:1;1063:62;;;:::i;:::-;1117:1;:::i;747:13368:26:-;;;;;;;;;;;;;;;;;;;;:::i;8048:154::-;8147:41;:48;8048:154;8147:27;8191:4;8147:17;;:27;:::i;:41::-;:48;:::i;8048:154::-;;;;;:::i;1063:62:16:-;;1117:1;1063:62;;;:::i;:::-;8682:41:26;:49;8580:158;8682:27;8726:5;8682:17;;:27;:::i;8580:158::-;;;;;:::i;1063:62:16:-;1117:1;1063:62;;;:::i;:::-;8927:36:26;8849:121;;;;8927:28;:19;8935:10;8927:19;:::i;:28::-;:36;;;;;8849:121;8927:36;;;;8849:121::o;8927:36::-;;;;;8849:121;;;;:::i;1063:62:16:-;1117:1;1063:62;;;:::i;:::-;9227:30:26;;:15;;;:::i;:::-;;9251:4;9227:30;9243:13;9251:4;9243:13;:::i;:::-;9227:30;;747:13368;;;;9227:30;;;;;;;;747:13368;;;;9227:30;;;-1:-1:-1;;;;;747:13368:26;9227:30;;;747:13368;;;;;;9227:30;;;;;;;;;;;;;;9129:231;9284:1;9271:14;9284:1;9271:14;:::i;:::-;;;9267:87;;9129:231;;:::o;9267:87::-;9332:10;9320;;9332;;:::i;9227:30::-;;;;;;;;;;;;;;:::i;:::-;;;;9129:231;;;;:::i;747:13368::-;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;747:13368:26;;;;;;;;;;;;;;;;941:175:21;1050:58;;941:175;1050:58;;941:175;1073:23;:14;:23;:::i;:::-;1050:58;;747:13368:26;;;;1050:58:21;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;:::i;:::-;;:::i;747:13368:26:-;;;;:::i;:::-;;;;;;;:::o;:::-;;;:::i;:::-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;;5196:642:21;5720:111;5196:642;5641:14;:69;5196:642;5641:14;:::i;:::-;:69;;;:::i;:::-;;;:::i;:::-;747:13368:26;;5728:22:21;;5749:1;5728:22;:::i;:::-;;:56;;;;;5196:642;5720:111;;:::i;5728:56::-;5754:30;;;;;;747:13368:26;;;5754:30:21;;;;;;;;:::i;:::-;5728:56;;;4108:223:22;4272:52;;4108:223;4272:52;4308:1;4272:52;:::i;:::-;;;:::i;747:13368:26:-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;;5165:446:22;;5487:31;5535:69;5165:446;;;;5330:12;747:13368:26;;;5330:12:22;5370:4;5354:81;5362:13;5370:4;5362:13;:::i;:::-;:30;;:21;:30;;5354:81;:::i;:::-;5487:31;;;;;;;;;;;:::i;:::-;5535:69;;:::i;747:13368:26:-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;7671:628:22;;;;7875:418;;;-1:-1:-1;747:13368:26;;7906:22:22;;7927:1;7906:22;:::i;:::-;;7902:286;;8201:17;;:::o;7902:286::-;8121:18;8113:60;8121:18;;:::i;:::-;8113:60;:::i;7875:418::-;8269:12;;:::i;1412:320::-;1702:19;:23;;1724:1;1702:23;:::i;:::-;;1412:320;:::o;747:13368:26:-;;;;;;;;-1:-1:-1;;747:13368:26;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;;747:13368:26;;;;;;;;;;;;;;:::i;8821:540:22:-;;8980:17;;747:13368:26;;;8980:17:22;:21;;9000:1;8980:21;:::i;:::-;;8976:379;;;9152:142;;;;;;;8976:379;9324:20;;;747:13368:26;;;;9324:20:22;;;;-1:-1:-1;;;9324:20:22;;;;;;:::i;747:13368:26:-;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;:::o;:::-;;:::i;10004:440::-;;;;10210:184;;:36;:9;;747:13368;-1:-1:-1;;;;;747:13368:26;;;10210:36;:184;10300:80;10210:36;10300:8;10331:49;10300:28;;:8;;:28;:::i;:::-;10331:49;;;:::i;10300:80::-;10210:184;;;747:13368;;;;10210:184;;;;;;;;747:13368;;;;10210:184;;;;;;;:::i;:::-;;;;;;;;;;:227;:184;;;;;10004:440;10397:26;:40;;:26;;:40;:::i;:::-;10210:227;;:::i;:184::-;10397:40;10210:184;;;;10397:40;10210:184;;;;;;;;;;;:::i;:::-;;;;;;747:13368;;;;;:::i;:::-;;;;;;;;;;:::o;:::-;;;;;:::i;11115:477::-;;;;11418:82;11115:477;11418:82;11408:177;11115:477;11537:20;11115:477;;11559:25;11115:477;;11370:7;747:13368;;;11370:7;-1:-1:-1;747:13368:26;;11418:82;;;;;;;;747:13368;;;;;;;;;;;;;;;;;;;;;;;11418:82;;;;;;;;;:::i;:::-;11408:93;;;747:13368;;;11408:93;;747:13368;;;;11408:93;;11537:20;;:::i;:::-;11559:25;;:::i;:::-;11408:177;;:::i;747:13368::-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11598:1274;;;;;;;11846:29;11598:1274;;12043:190;;11598:1274;12043:190;11598:1274;;;;11838:73;11868:7;11846:29;;747:13368;;11846:29;;;;11838:73;:::i;:::-;11921:94;11929:51;;:17;:37;:17;;:37;:::i;:51::-;;:::i;:::-;11921:94;:::i;:::-;12043:16;:4;:16;:::i;:::-;;;:190;;747:13368;;;;12043:190;;;;;;747:13368;;;;12043:190;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;11598:1274;12252:12;12442:43;12252:12;;12442:43;12252:12;12377:25;12252:12;12244:66;12252:21;;;;;:::i;:::-;:26;;12277:1;12252:26;:::i;:::-;;12244:66;:::i;:::-;12377:21;12401:1;12377:25;12401:1;12377:25;:::i;:::-;12442:43;:23;:43;747:13368;;;;12442:43;;;;;;;;747:13368;;;;12442:43;;;;;;747:13368;;;;;;12442:43;;;;;;;;;;12671:61;12686:46;12671:61;12442:43;12784:81;12442:43;;12663:96;12442:43;;;;;11598:1274;12525:32;;;;;:::i;:::-;12572:57;;;747:13368;;;;12572:57;;;;;;;:::i;:::-;;;;747:13368;;;12671:61;;;12663:96;:::i;:::-;12784:81;;;747:13368;;;;12784:81;;;;;;:::i;12442:43::-;12525:32;12442:43;;;;;;;;;;;;;;:::i;:::-;;;;12043:190;12442:43;12043:190;;;;;12442:43;12043:190;;;;;;;;;;;;:::i;:::-;;;;;;;;747:13368;;;;;;;;;;;;;;;;:::o;13379:576::-;;13572:1;13554:19;;;:::i;:::-;;;13550:33;;13633:18;;13792:49;;:66;13633:18;13754:14;13633:18;13617:36;13625:27;13633:18;13862:24;13633:18;;:::i;13617:36::-;13679:21;;;;:::i;:::-;13747:4;13739:13;13747:4;13739:13;:::i;:::-;13754:14;;:::i;:::-;13792:34;:49;:::i;:::-;:66;:::i;:::-;13862:24;:15;:9;;747:13368;-1:-1:-1;;;;;747:13368:26;;;13862:15;;13878:7;13862:24;13878:7;13862:24;;747:13368;;;;13862:24;;;;;;;;;;13791:95;13862:24;;;;;13379:576;13791:95;;:::i;:::-;13902:26;;747:13368;;;;13902:26;747:13368;;;;;13902:26;13379:576;:::o;13862:24::-;;;;;;;;;;;;;;;:::i;:::-;;;;;13550:33;13575:8;;;;;;;:::i;747:13368::-;-1:-1:-1;;;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;1355:203:21;;1482:68;1355:203;1482:68;;1355:203;1482:68;1355:203;1505:27;:18;:27;:::i;:::-;1482:68;;747:13368:26;;;;1482:68:21;;;;;;;;;;;;:::i;13961:66:26:-;747:13368;;-1:-1:-1;;;13999:21:26;;747:13368;13999:21;;;747:13368;;;;;;;;;;;;;;14033:80;14084:10;14096:9;14075:31;;;;747:13368;;;;14075:31;;;;;;:::i;1063:62:16:-;;1117:1;1063:62;;;:::i;:::-;721:116:30;801:17;:29;721:116;801:8;:17;:::i;721:116::-;;;;;:::i;1063:62:16:-;1117:1;1063:62;;;:::i;:::-;1048:22:30;974:103;1048:22;;:::i;974:103::-;;;;:::i;1369:201::-;1481:72;:44;:9;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;1481:44:30;;;1535:8;1481:72;1535:17;;:8;;:17;:::i;:::-;1481:72;;;747:13368:26;;;;1481:72:30;;;;;;;;747:13368:26;;;;1481:72:30;;;;;;;;;;;;;;1474:79;1369:201;:::o;1481:72::-;;;;;;;;;;;;;;:::i;1063:62:16:-;1117:1;1063:62;;;:::i;:::-;1060:40:32;969:138;1060:40;;:::i;969:138::-;;;;:::i;1063:62:16:-;1117:1;1063:62;;;:::i;:::-;1364:38:32;1267:142;1364:38;;:::i;1267:142::-;;;;:::i;1781:379::-;;1890:39;1902:27;1890:39;:::i;:::-;;;;:::i;:::-;;1886:250;;1952:18;:46;;:18;:37;:18;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;1952:37:32;:46;:37;:46;747:13368:26;;;;1886:250:32;2019:38;;2031:26;2019:38;:::i;:::-;;;:::i;:::-;;2015:121;;1886:250;2145:8;2152:1;2145:8;:::i;2015:121::-;2080:45;;:17;:36;:17;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;2461:361:32;;2572:39;2584:27;2572:39;:::i;:::-;;;;:::i;:::-;;2568:230;;2634:18;:36;;:18;:27;:18;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;2634:27:32;:36;:27;:36;747:13368:26;;;;2568:230:32;2691:38;;2703:26;2691:38;:::i;:::-;;2687:111;;2568:230;2807:8;2814:1;2807:8;:::i;2687:111::-;2752:35;;:17;:26;:17;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;3128:430:32;;3244:39;3256:27;3244:39;:::i;:::-;;;;:::i;:::-;;3240:312;;3306:18;:44;;:18;:35;:18;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;3306:35:32;:44;:35;:44;747:13368:26;;;;3306:44:32;;;;;;;;;;;;;;3240:312;3354:1;3306:49;;3354:1;3306:49;:::i;:::-;;;3299:56;:::o;3306:44::-;;;;;;;;;;;;;;:::i;:::-;;;;3240:312;3376:38;;3388:26;3376:38;:::i;:::-;;3372:180;;3437:43;;:17;:34;:17;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;3372:180:32;747:13368:26;;-1:-1:-1;;;3516:25:32;;747:13368:26;3516:25:32;;;747:13368:26;;;;;;;;;;;;;;3756:192:32;3843:45;;:36;:18;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;3843:36:32;;:45;;747:13368:26;;;;3843:45:32;;;;747:13368:26;;;;3843:45:32;;;;;;747:13368:26;;;;;3843:45:32;;;;;;;;;;;;;3756:192;3892:1;;3843:50;;;;;:::i;:::-;;;:98;;;;;3836:105;;3756:192;:::o;3843:98::-;3897:39;:17;;:39;:17;:30;:17;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;3897:30:32;:39;:30;:39;747:13368:26;;;;3897:39:32;;;;;;;;;;;;;;3843:98;3897:44;;;;;:::i;:39::-;:44;:39;;;;:44;:39;;;;;;;;;;;:::i;:::-;;;;;;3843:45;;;;;;;;;;;;;;:::i;:::-;;;;747:13368:26;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;3954:604:32:-;4083:27;;3954:604;4071:39;4083:27;4071:39;:::i;:::-;;;;:::i;:::-;;4067:485;;4140:18;:37;:18;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;4140:37:32;;4184:10;4140:37;4184:10;;:::i;:::-;;;;;;;;4140:56;4184:10;4140:56;4178:17;4184:10;;4140:56;4184:10;;747:13368:26;;;;4184:10:32;4178:17;:::i;:::-;4140:56;;747:13368:26;;;;4140:56:32;;;;;;;;747:13368:26;;;;4140:56:32;;;;;;;:::i;:::-;;;;;;;;;;;;;;4067:485;4126:70;;4210:30;:18;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;4210:30:32;;;:59;;;;;;;;;;;;747:13368:26;;;;4210:59:32;;;;;;;;747:13368:26;;;;4210:59:32;;;;;;;;;;;4067:485;3954:604::o;4210:59::-;;;;;;;;:::i;:::-;;;;;:::i;4140:56::-;;;;;;;;;;;;;;;:::i;:::-;;;;;4184:10;;:::i;4067:485::-;4290:38;;4302:26;4290:38;:::i;:::-;;4286:266;;4358:36;:17;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;4358:36:32;;4401:10;4358:36;4401:10;;:::i;:::-;;;;;;;;4358:55;4401:10;4358:55;4395:17;4401:10;;4358:55;4401:10;;747:13368:26;;;;4358:55:32;;;;;;;;;;;;;;4286:266;4344:69;;4427:29;:17;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;4358:55:32;;;;;;;;;;;;;;;:::i;:::-;;;;;2055:99:36;;2146:1;2055:99;;;;2088:48;2096:7;;;:::i;2088:48::-;2146:1;:::i;747:13368:26:-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;747:13368:26;;;;;-1:-1:-1;;;;;747:13368:26;;;;;;2806:462:36;;3186:75;2806:462;;;3093:78;3042:10;3016:45;;;;;;:::i;:::-;3093:78;;;:::i;:::-;3186:75;;;;747:13368:26;;;;3186:75:36;;;;;;:::i;2806:462::-;;;;;;;;:::i;1063:62:16:-;;1117:1;1063:62;;;:::i;:::-;1117:1;:::i;747:13368:26:-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;547:44:36:-;589:2;;;:::i;:::-;;;;;;;;;:::o;3577:432::-;3709:20;3717:11;3761:30;3577:432;3649:19;3701:29;3577:432;3649:12;:5;;:12;:::i;:::-;:19;:::i;:::-;3717:11;:::i;3701:29::-;3761:30;:28;:19;;;:::i;:28::-;;:30;;747:13368:26;;;;3761:30:36;;;;747:13368:26;;;;3761:30:36;;;;;;;;;;;;;3962:40;3969:32;3740:51;3919:40;3761:30;3919:83;3761:30;;;;;3577:432;3740:51;;:::i;:::-;3859:16;;:::i;:::-;3801:108;3852:57;3859:49;:32;;;;:::i;:::-;3894:14;3859:49;;:::i;:::-;3852:57;:::i;:::-;3801:48;:34;;:48;:::i;:108::-;3969:32;:::i;3962:40::-;3919:26;;:40;:::i;3761:30::-;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;3577:432;;;;;:::i;747:13368:26:-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;2250:125:36;2289:68;2297:13;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;2297:13:36;:27;2314:10;2297:27;747:13368:26;2297:27:36;;2289:68;:::i;:::-;2367:1;;:::i;747:13368:26:-;;;;;;;;;;;;;;;:::i;:::-;;;;:::i;4087:75:36:-;4144:11;;4154:1;4144:11;:::i;:::-;;;:::i;4087:75::-;;;:::i;1063:62:16:-;;;:::i;:::-;1117:1;4289:11:36;;4299:1;4289:11;:::i;4239:68::-;;;:::i;1063:62:16:-;1117:1;1063:62;;;:::i;:::-;4478:30:36;4399:116;4478:30;;:::i;4399:116::-;;;;:::i;1063:62:16:-;1117:1;1063:62;;;:::i;:::-;4677:24:36;4604:104;4677:24;;:::i;4604:104::-;;;;:::i;747:13368:26:-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;;4714:398:36;;5017:88;4940:18;4910:12;;5017:88;4714:398;;4877:4;747:13368:26;;;4877:4:36;4910:5;;:12;:::i;:::-;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;4940:18:36;4970:1;;4962:10;4932:68;4940:32;4962:10;;;:::i;4940:32::-;-1:-1:-1;;;;;747:13368:26;;4940:32:36;;4932:68;:::i;:::-;5017:88;:22;5094:10;5081:23;5094:10;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;5081:23:36;-1:-1:-1;;;;;747:13368:26;;5081:23:36;5017:88;;747:13368:26;;;;5017:88:36;;;;;;;;747:13368:26;;;;5017:88:36;;;;;;;:::i;:::-;;;;;;;;;;;;;;5010:95;4714:398;:::o;747:13368:26:-;;;;:::o;:::-;;;-1:-1:-1;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;747:13368:26;;;;;;;;;;;;;;;;;;;;;;;5118:366:36;5428:49;;5118:366;5428:49;5272:13;5242:12;;:5;;:12;:::i;5272:13::-;5297:1;5404:6;5297:1;;;5329:53;:36;5335:29;5343:20;5351:11;5297:1;5289:10;5264:55;5272:27;5289:10;;;:::i;5272:27::-;-1:-1:-1;;;;;747:13368:26;;5272:27:36;;5264:55;:::i;5329:53::-;5404:6;:::i;:::-;5428:15;:49;5458:18;5466:10;;747:13368:26;-1:-1:-1;;;;;747:13368:26;;;5458:18:36;-1:-1:-1;;;;;747:13368:26;;5458:18:36;5428:49;747:13368:26;;;;5428:49:36;;;;;;;;747:13368:26;;;;5428:49:36;;;;;;;:::i;82:344:47:-;249:171;;;;;;;;82:344;:::o
Swarm Source
ipfs://e1780f47783b02b3cb746afb003b635f7646ee9a1a3ec2bf33647c98a0a2e413
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$192.92
Net Worth in ETH
0.065632
Token Allocations
ETH
94.48%
USD₮0
3.19%
USDC
2.33%
Multichain Portfolio | 35 Chains
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.