Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00
Cross-Chain Transactions
Loading...
Loading
Contract Name:
LockReleaseTokenPoolAndProxy
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 26000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;
import {ILiquidityContainer} from "../../liquiditymanager/interfaces/ILiquidityContainer.sol";
import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol";
import {Pool} from "../libraries/Pool.sol";
import {LegacyPoolWrapper} from "./LegacyPoolWrapper.sol";
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
/// @notice Token pool used for tokens on their native chain. This uses a lock and release mechanism.
/// Because of lock/unlock requiring liquidity, this pool contract also has function to add and remove
/// liquidity. This allows for proper bookkeeping for both user and liquidity provider balances.
/// @dev One token per LockReleaseTokenPool.
contract LockReleaseTokenPoolAndProxy is LegacyPoolWrapper, ILiquidityContainer, ITypeAndVersion {
using SafeERC20 for IERC20;
error InsufficientLiquidity();
error LiquidityNotAccepted();
string public constant override typeAndVersion = "LockReleaseTokenPoolAndProxy 1.5.0";
/// @dev Whether or not the pool accepts liquidity.
/// External liquidity is not required when there is one canonical token deployed to a chain,
/// and CCIP is facilitating mint/burn on all the other chains, in which case the invariant
/// balanceOf(pool) on home chain >= sum(totalSupply(mint/burn "wrapped" token) on all remote chains) should always hold
bool internal immutable i_acceptLiquidity;
/// @notice The address of the rebalancer.
address internal s_rebalancer;
constructor(
IERC20 token,
address[] memory allowlist,
address rmnProxy,
bool acceptLiquidity,
address router
) LegacyPoolWrapper(token, allowlist, rmnProxy, router) {
i_acceptLiquidity = acceptLiquidity;
}
/// @notice Locks the token in the pool
/// @dev The _validateLockOrBurn check is an essential security check
function lockOrBurn(Pool.LockOrBurnInV1 calldata lockOrBurnIn)
external
virtual
override
returns (Pool.LockOrBurnOutV1 memory)
{
_validateLockOrBurn(lockOrBurnIn);
if (_hasLegacyPool()) {
_lockOrBurnLegacy(lockOrBurnIn);
}
emit Locked(msg.sender, lockOrBurnIn.amount);
return Pool.LockOrBurnOutV1({destTokenAddress: getRemoteToken(lockOrBurnIn.remoteChainSelector), destPoolData: ""});
}
/// @notice Release tokens from the pool to the recipient
/// @dev The _validateReleaseOrMint check is an essential security check
function releaseOrMint(Pool.ReleaseOrMintInV1 calldata releaseOrMintIn)
external
virtual
override
returns (Pool.ReleaseOrMintOutV1 memory)
{
_validateReleaseOrMint(releaseOrMintIn);
if (!_hasLegacyPool()) {
// Release to the recipient
getToken().safeTransfer(releaseOrMintIn.receiver, releaseOrMintIn.amount);
} else {
_releaseOrMintLegacy(releaseOrMintIn);
}
emit Released(msg.sender, releaseOrMintIn.receiver, releaseOrMintIn.amount);
return Pool.ReleaseOrMintOutV1({destinationAmount: releaseOrMintIn.amount});
}
// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
return interfaceId == type(ILiquidityContainer).interfaceId || super.supportsInterface(interfaceId);
}
/// @notice Gets LiquidityManager, can be address(0) if none is configured.
/// @return The current liquidity manager.
function getRebalancer() external view returns (address) {
return s_rebalancer;
}
/// @notice Sets the LiquidityManager address.
/// @dev Only callable by the owner.
function setRebalancer(address rebalancer) external onlyOwner {
s_rebalancer = rebalancer;
}
/// @notice Checks if the pool can accept liquidity.
/// @return true if the pool can accept liquidity, false otherwise.
function canAcceptLiquidity() external view returns (bool) {
return i_acceptLiquidity;
}
/// @notice Adds liquidity to the pool. The tokens should be approved first.
/// @param amount The amount of liquidity to provide.
function provideLiquidity(uint256 amount) external {
if (!i_acceptLiquidity) revert LiquidityNotAccepted();
if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender);
i_token.safeTransferFrom(msg.sender, address(this), amount);
emit LiquidityAdded(msg.sender, amount);
}
/// @notice Removed liquidity to the pool. The tokens will be sent to msg.sender.
/// @param amount The amount of liquidity to remove.
function withdrawLiquidity(uint256 amount) external {
if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender);
if (i_token.balanceOf(address(this)) < amount) revert InsufficientLiquidity();
i_token.safeTransfer(msg.sender, amount);
emit LiquidityRemoved(msg.sender, amount);
}
/// @notice This function can be used to transfer liquidity from an older version of the pool to this pool. To do so
/// this pool will have to be set as the rebalancer in the older version of the pool. This allows it to transfer the
/// funds in the old pool to the new pool.
/// @dev When upgrading a LockRelease pool, this function can be called at the same time as the pool is changed in the
/// TokenAdminRegistry. This allows for a smooth transition of both liquidity and transactions to the new pool.
/// Alternatively, when no multicall is available, a portion of the funds can be transferred to the new pool before
/// changing which pool CCIP uses, to ensure both pools can operate. Then the pool should be changed in the
/// TokenAdminRegistry, which will activate the new pool. All new transactions will use the new pool and its
/// liquidity. Finally, the remaining liquidity can be transferred to the new pool using this function one more time.
/// @param from The address of the old pool.
/// @param amount The amount of liquidity to transfer.
function transferLiquidity(address from, uint256 amount) external onlyOwner {
LockReleaseTokenPoolAndProxy(from).withdrawLiquidity(amount);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
/// @notice Interface for a liquidity container, this can be a CCIP token pool.
interface ILiquidityContainer {
event LiquidityAdded(address indexed provider, uint256 indexed amount);
event LiquidityRemoved(address indexed provider, uint256 indexed amount);
/// @notice Provide additional liquidity to the container.
/// @dev Should emit LiquidityAdded
function provideLiquidity(uint256 amount) external;
/// @notice Withdraws liquidity from the container to the msg sender
/// @dev Should emit LiquidityRemoved
function withdrawLiquidity(uint256 amount) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface ITypeAndVersion {
function typeAndVersion() external pure returns (string memory);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice This library contains various token pool functions to aid constructing the return data.
library Pool {
// The tag used to signal support for the pool v1 standard
// bytes4(keccak256("CCIP_POOL_V1"))
bytes4 public constant CCIP_POOL_V1 = 0xaff2afbf;
// The number of bytes in the return data for a pool v1 releaseOrMint call.
// This should match the size of the ReleaseOrMintOutV1 struct.
uint16 public constant CCIP_POOL_V1_RET_BYTES = 32;
// The default max number of bytes in the return data for a pool v1 lockOrBurn call.
// This data can be used to send information to the destination chain token pool. Can be overwritten
// in the TokenTransferFeeConfig.destBytesOverhead if more data is required.
uint32 public constant CCIP_LOCK_OR_BURN_V1_RET_BYTES = 32;
struct LockOrBurnInV1 {
bytes receiver; // The recipient of the tokens on the destination chain, abi encoded
uint64 remoteChainSelector; // ─╮ The chain ID of the destination chain
address originalSender; // ─────╯ The original sender of the tx on the source chain
uint256 amount; // The amount of tokens to lock or burn, denominated in the source token's decimals
address localToken; // The address on this chain of the token to lock or burn
}
struct LockOrBurnOutV1 {
// The address of the destination token, abi encoded in the case of EVM chains
// This value is UNTRUSTED as any pool owner can return whatever value they want.
bytes destTokenAddress;
// Optional pool data to be transferred to the destination chain. Be default this is capped at
// CCIP_LOCK_OR_BURN_V1_RET_BYTES bytes. If more data is required, the TokenTransferFeeConfig.destBytesOverhead
// has to be set for the specific token.
bytes destPoolData;
}
struct ReleaseOrMintInV1 {
bytes originalSender; // The original sender of the tx on the source chain
uint64 remoteChainSelector; // ─╮ The chain ID of the source chain
address receiver; // ───────────╯ The recipient of the tokens on the destination chain.
uint256 amount; // The amount of tokens to release or mint, denominated in the source token's decimals
address localToken; // The address on this chain of the token to release or mint
/// @dev WARNING: sourcePoolAddress should be checked prior to any processing of funds. Make sure it matches the
/// expected pool address for the given remoteChainSelector.
bytes sourcePoolAddress; // The address of the source pool, abi encoded in the case of EVM chains
bytes sourcePoolData; // The data received from the source pool to process the release or mint
/// @dev WARNING: offchainTokenData is untrusted data.
bytes offchainTokenData; // The offchain data to process the release or mint
}
struct ReleaseOrMintOutV1 {
// The number of tokens released or minted on the destination chain, denominated in the local token's decimals.
// This value is expected to be equal to the ReleaseOrMintInV1.amount in the case where the source and destination
// chain have the same number of decimals.
uint256 destinationAmount;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
import {IPoolPriorTo1_5} from "../interfaces/IPoolPriorTo1_5.sol";
import {Pool} from "../libraries/Pool.sol";
import {TokenPool} from "./TokenPool.sol";
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
abstract contract LegacyPoolWrapper is TokenPool {
using SafeERC20 for IERC20;
event LegacyPoolChanged(IPoolPriorTo1_5 oldPool, IPoolPriorTo1_5 newPool);
/// @dev The previous pool, if there is any. This is a property to make the older 1.0-1.4 pools
/// compatible with the current 1.5 pool. To achieve this, we set the previous pool address to the
/// currently deployed legacy pool. Then we configure this new pool as onRamp and offRamp on the legacy pools.
/// In the case of a 1.4 pool, this new pool contract has to be set to the Router as well, as it validates
/// who can call it through the router calls. This contract will always return itself as the only allowed ramp.
/// @dev Can be address(0), this would indicate that this pool is operating as a normal pool as opposed to
/// a proxy pool.
IPoolPriorTo1_5 internal s_previousPool;
constructor(
IERC20 token,
address[] memory allowlist,
address rmnProxy,
address router
) TokenPool(token, allowlist, rmnProxy, router) {}
// ================================================================
// │ Legacy Fallbacks │
// ================================================================
// Legacy fallbacks for older token pools that do not implement the new interface.
/// @notice Legacy fallback for the 1.4 token pools.
function getOnRamp(uint64) external view returns (address onRampAddress) {
return address(this);
}
/// @notice Return true if the given offRamp is a configured offRamp for the given source chain.
function isOffRamp(uint64 sourceChainSelector, address offRamp) external view returns (bool) {
return offRamp == address(this) || s_router.isOffRamp(sourceChainSelector, offRamp);
}
/// @notice Configures the legacy fallback option. If the previous pool is set, this pool will act as a proxy for
/// the legacy pool.
/// @param prevPool The address of the previous pool.
function setPreviousPool(IPoolPriorTo1_5 prevPool) external onlyOwner {
IPoolPriorTo1_5 oldPrevPool = s_previousPool;
s_previousPool = prevPool;
emit LegacyPoolChanged(oldPrevPool, prevPool);
}
/// @notice Returns the address of the previous pool.
function getPreviousPool() external view returns (address) {
return address(s_previousPool);
}
function _hasLegacyPool() internal view returns (bool) {
return address(s_previousPool) != address(0);
}
function _lockOrBurnLegacy(Pool.LockOrBurnInV1 memory lockOrBurnIn) internal {
i_token.safeTransfer(address(s_previousPool), lockOrBurnIn.amount);
s_previousPool.lockOrBurn(
lockOrBurnIn.originalSender, lockOrBurnIn.receiver, lockOrBurnIn.amount, lockOrBurnIn.remoteChainSelector, ""
);
}
/// @notice This call converts the arguments from a >=1.5 pool call to those of a <1.5 pool call, and uses these
/// to call the previous pool.
/// @param releaseOrMintIn The 1.5 style release or mint arguments.
/// @dev Overwrites the receiver so the previous pool sends the tokens to the sender of this call, which is the
/// offRamp. This is due to the older pools sending funds directly to the receiver, while the new pools do a hop
/// through the offRamp to ensure the correct tokens are sent.
/// @dev Since extraData has never been used in LockRelease or MintBurn token pools, we can safely ignore it.
function _releaseOrMintLegacy(Pool.ReleaseOrMintInV1 memory releaseOrMintIn) internal {
s_previousPool.releaseOrMint(
releaseOrMintIn.originalSender,
releaseOrMintIn.receiver,
releaseOrMintIn.amount,
releaseOrMintIn.remoteChainSelector,
""
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
// Shared public interface for multiple pool types.
// Each pool type handles a different child token model (lock/unlock, mint/burn.)
interface IPoolPriorTo1_5 {
/// @notice Lock tokens into the pool or burn the tokens.
/// @param originalSender Original sender of the tokens.
/// @param receiver Receiver of the tokens on destination chain.
/// @param amount Amount to lock or burn.
/// @param remoteChainSelector Destination chain Id.
/// @param extraArgs Additional data passed in by sender for lockOrBurn processing
/// in custom pools on source chain.
/// @return retData Optional field that contains bytes. Unused for now but already
/// implemented to allow future upgrades while preserving the interface.
function lockOrBurn(
address originalSender,
bytes calldata receiver,
uint256 amount,
uint64 remoteChainSelector,
bytes calldata extraArgs
) external returns (bytes memory);
/// @notice Releases or mints tokens to the receiver address.
/// @param originalSender Original sender of the tokens.
/// @param receiver Receiver of the tokens.
/// @param amount Amount to release or mint.
/// @param remoteChainSelector Source chain Id.
/// @param extraData Additional data supplied offchain for releaseOrMint processing in
/// custom pools on dest chain. This could be an attestation that was retrieved through a
/// third party API.
/// @dev offchainData can come from any untrusted source.
function releaseOrMint(
bytes memory originalSender,
address receiver,
uint256 amount,
uint64 remoteChainSelector,
bytes memory extraData
) external;
/// @notice Gets the IERC20 token that this pool can lock or burn.
/// @return token The IERC20 token representation.
function getToken() external view returns (IERC20 token);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.24;
import {IPoolV1} from "../interfaces/IPool.sol";
import {IRMN} from "../interfaces/IRMN.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol";
import {Pool} from "../libraries/Pool.sol";
import {RateLimiter} from "../libraries/RateLimiter.sol";
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol";
import {EnumerableSet} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/structs/EnumerableSet.sol";
/// @notice Base abstract class with common functions for all token pools.
/// A token pool serves as isolated place for holding tokens and token specific logic
/// that may execute as tokens move across the bridge.
abstract contract TokenPool is IPoolV1, OwnerIsCreator {
using EnumerableSet for EnumerableSet.AddressSet;
using EnumerableSet for EnumerableSet.UintSet;
using RateLimiter for RateLimiter.TokenBucket;
error CallerIsNotARampOnRouter(address caller);
error ZeroAddressNotAllowed();
error SenderNotAllowed(address sender);
error AllowListNotEnabled();
error NonExistentChain(uint64 remoteChainSelector);
error ChainNotAllowed(uint64 remoteChainSelector);
error CursedByRMN();
error ChainAlreadyExists(uint64 chainSelector);
error InvalidSourcePoolAddress(bytes sourcePoolAddress);
error InvalidToken(address token);
error Unauthorized(address caller);
event Locked(address indexed sender, uint256 amount);
event Burned(address indexed sender, uint256 amount);
event Released(address indexed sender, address indexed recipient, uint256 amount);
event Minted(address indexed sender, address indexed recipient, uint256 amount);
event ChainAdded(
uint64 remoteChainSelector,
bytes remoteToken,
RateLimiter.Config outboundRateLimiterConfig,
RateLimiter.Config inboundRateLimiterConfig
);
event ChainConfigured(
uint64 remoteChainSelector,
RateLimiter.Config outboundRateLimiterConfig,
RateLimiter.Config inboundRateLimiterConfig
);
event ChainRemoved(uint64 remoteChainSelector);
event RemotePoolSet(uint64 indexed remoteChainSelector, bytes previousPoolAddress, bytes remotePoolAddress);
event AllowListAdd(address sender);
event AllowListRemove(address sender);
event RouterUpdated(address oldRouter, address newRouter);
struct ChainUpdate {
uint64 remoteChainSelector; // ──╮ Remote chain selector
bool allowed; // ────────────────╯ Whether the chain should be enabled
bytes remotePoolAddress; // Address of the remote pool, ABI encoded in the case of a remove EVM chain.
bytes remoteTokenAddress; // Address of the remote token, ABI encoded in the case of a remote EVM chain.
RateLimiter.Config outboundRateLimiterConfig; // Outbound rate limited config, meaning the rate limits for all of the onRamps for the given chain
RateLimiter.Config inboundRateLimiterConfig; // Inbound rate limited config, meaning the rate limits for all of the offRamps for the given chain
}
struct RemoteChainConfig {
RateLimiter.TokenBucket outboundRateLimiterConfig; // Outbound rate limited config, meaning the rate limits for all of the onRamps for the given chain
RateLimiter.TokenBucket inboundRateLimiterConfig; // Inbound rate limited config, meaning the rate limits for all of the offRamps for the given chain
bytes remotePoolAddress; // Address of the remote pool, ABI encoded in the case of a remote EVM chain.
bytes remoteTokenAddress; // Address of the remote token, ABI encoded in the case of a remote EVM chain.
}
/// @dev The bridgeable token that is managed by this pool.
IERC20 internal immutable i_token;
/// @dev The address of the RMN proxy
address internal immutable i_rmnProxy;
/// @dev The immutable flag that indicates if the pool is access-controlled.
bool internal immutable i_allowlistEnabled;
/// @dev A set of addresses allowed to trigger lockOrBurn as original senders.
/// Only takes effect if i_allowlistEnabled is true.
/// This can be used to ensure only token-issuer specified addresses can
/// move tokens.
EnumerableSet.AddressSet internal s_allowList;
/// @dev The address of the router
IRouter internal s_router;
/// @dev A set of allowed chain selectors. We want the allowlist to be enumerable to
/// be able to quickly determine (without parsing logs) who can access the pool.
/// @dev The chain selectors are in uint256 format because of the EnumerableSet implementation.
EnumerableSet.UintSet internal s_remoteChainSelectors;
mapping(uint64 remoteChainSelector => RemoteChainConfig) internal s_remoteChainConfigs;
/// @notice The address of the rate limiter admin.
/// @dev Can be address(0) if none is configured.
address internal s_rateLimitAdmin;
constructor(IERC20 token, address[] memory allowlist, address rmnProxy, address router) {
if (address(token) == address(0) || router == address(0) || rmnProxy == address(0)) revert ZeroAddressNotAllowed();
i_token = token;
i_rmnProxy = rmnProxy;
s_router = IRouter(router);
// Pool can be set as permissioned or permissionless at deployment time only to save hot-path gas.
i_allowlistEnabled = allowlist.length > 0;
if (i_allowlistEnabled) {
_applyAllowListUpdates(new address[](0), allowlist);
}
}
/// @notice Get RMN proxy address
/// @return rmnProxy Address of RMN proxy
function getRmnProxy() public view returns (address rmnProxy) {
return i_rmnProxy;
}
/// @inheritdoc IPoolV1
function isSupportedToken(address token) public view virtual returns (bool) {
return token == address(i_token);
}
/// @notice Gets the IERC20 token that this pool can lock or burn.
/// @return token The IERC20 token representation.
function getToken() public view returns (IERC20 token) {
return i_token;
}
/// @notice Gets the pool's Router
/// @return router The pool's Router
function getRouter() public view returns (address router) {
return address(s_router);
}
/// @notice Sets the pool's Router
/// @param newRouter The new Router
function setRouter(address newRouter) public onlyOwner {
if (newRouter == address(0)) revert ZeroAddressNotAllowed();
address oldRouter = address(s_router);
s_router = IRouter(newRouter);
emit RouterUpdated(oldRouter, newRouter);
}
/// @notice Signals which version of the pool interface is supported
function supportsInterface(bytes4 interfaceId) public pure virtual override returns (bool) {
return interfaceId == Pool.CCIP_POOL_V1 || interfaceId == type(IPoolV1).interfaceId
|| interfaceId == type(IERC165).interfaceId;
}
// ================================================================
// │ Validation │
// ================================================================
/// @notice Validates the lock or burn input for correctness on
/// - token to be locked or burned
/// - RMN curse status
/// - allowlist status
/// - if the sender is a valid onRamp
/// - rate limit status
/// @param lockOrBurnIn The input to validate.
/// @dev This function should always be called before executing a lock or burn. Not doing so would allow
/// for various exploits.
function _validateLockOrBurn(Pool.LockOrBurnInV1 memory lockOrBurnIn) internal {
if (!isSupportedToken(lockOrBurnIn.localToken)) revert InvalidToken(lockOrBurnIn.localToken);
if (IRMN(i_rmnProxy).isCursed(bytes16(uint128(lockOrBurnIn.remoteChainSelector)))) revert CursedByRMN();
_checkAllowList(lockOrBurnIn.originalSender);
_onlyOnRamp(lockOrBurnIn.remoteChainSelector);
_consumeOutboundRateLimit(lockOrBurnIn.remoteChainSelector, lockOrBurnIn.amount);
}
/// @notice Validates the release or mint input for correctness on
/// - token to be released or minted
/// - RMN curse status
/// - if the sender is a valid offRamp
/// - if the source pool is valid
/// - rate limit status
/// @param releaseOrMintIn The input to validate.
/// @dev This function should always be called before executing a release or mint. Not doing so would allow
/// for various exploits.
function _validateReleaseOrMint(Pool.ReleaseOrMintInV1 memory releaseOrMintIn) internal {
if (!isSupportedToken(releaseOrMintIn.localToken)) revert InvalidToken(releaseOrMintIn.localToken);
if (IRMN(i_rmnProxy).isCursed(bytes16(uint128(releaseOrMintIn.remoteChainSelector)))) revert CursedByRMN();
_onlyOffRamp(releaseOrMintIn.remoteChainSelector);
// Validates that the source pool address is configured on this pool.
bytes memory configuredRemotePool = getRemotePool(releaseOrMintIn.remoteChainSelector);
if (
configuredRemotePool.length == 0
|| keccak256(releaseOrMintIn.sourcePoolAddress) != keccak256(configuredRemotePool)
) {
revert InvalidSourcePoolAddress(releaseOrMintIn.sourcePoolAddress);
}
_consumeInboundRateLimit(releaseOrMintIn.remoteChainSelector, releaseOrMintIn.amount);
}
// ================================================================
// │ Chain permissions │
// ================================================================
/// @notice Gets the pool address on the remote chain.
/// @param remoteChainSelector Remote chain selector.
/// @dev To support non-evm chains, this value is encoded into bytes
function getRemotePool(uint64 remoteChainSelector) public view returns (bytes memory) {
return s_remoteChainConfigs[remoteChainSelector].remotePoolAddress;
}
/// @notice Gets the token address on the remote chain.
/// @param remoteChainSelector Remote chain selector.
/// @dev To support non-evm chains, this value is encoded into bytes
function getRemoteToken(uint64 remoteChainSelector) public view returns (bytes memory) {
return s_remoteChainConfigs[remoteChainSelector].remoteTokenAddress;
}
/// @notice Sets the remote pool address for a given chain selector.
/// @param remoteChainSelector The remote chain selector for which the remote pool address is being set.
/// @param remotePoolAddress The address of the remote pool.
function setRemotePool(uint64 remoteChainSelector, bytes calldata remotePoolAddress) external onlyOwner {
if (!isSupportedChain(remoteChainSelector)) revert NonExistentChain(remoteChainSelector);
bytes memory prevAddress = s_remoteChainConfigs[remoteChainSelector].remotePoolAddress;
s_remoteChainConfigs[remoteChainSelector].remotePoolAddress = remotePoolAddress;
emit RemotePoolSet(remoteChainSelector, prevAddress, remotePoolAddress);
}
/// @inheritdoc IPoolV1
function isSupportedChain(uint64 remoteChainSelector) public view returns (bool) {
return s_remoteChainSelectors.contains(remoteChainSelector);
}
/// @notice Get list of allowed chains
/// @return list of chains.
function getSupportedChains() public view returns (uint64[] memory) {
uint256[] memory uint256ChainSelectors = s_remoteChainSelectors.values();
uint64[] memory chainSelectors = new uint64[](uint256ChainSelectors.length);
for (uint256 i = 0; i < uint256ChainSelectors.length; ++i) {
chainSelectors[i] = uint64(uint256ChainSelectors[i]);
}
return chainSelectors;
}
/// @notice Sets the permissions for a list of chains selectors. Actual senders for these chains
/// need to be allowed on the Router to interact with this pool.
/// @dev Only callable by the owner
/// @param chains A list of chains and their new permission status & rate limits. Rate limits
/// are only used when the chain is being added through `allowed` being true.
function applyChainUpdates(ChainUpdate[] calldata chains) external virtual onlyOwner {
for (uint256 i = 0; i < chains.length; ++i) {
ChainUpdate memory update = chains[i];
RateLimiter._validateTokenBucketConfig(update.outboundRateLimiterConfig, !update.allowed);
RateLimiter._validateTokenBucketConfig(update.inboundRateLimiterConfig, !update.allowed);
if (update.allowed) {
// If the chain already exists, revert
if (!s_remoteChainSelectors.add(update.remoteChainSelector)) {
revert ChainAlreadyExists(update.remoteChainSelector);
}
if (update.remotePoolAddress.length == 0 || update.remoteTokenAddress.length == 0) {
revert ZeroAddressNotAllowed();
}
s_remoteChainConfigs[update.remoteChainSelector] = RemoteChainConfig({
outboundRateLimiterConfig: RateLimiter.TokenBucket({
rate: update.outboundRateLimiterConfig.rate,
capacity: update.outboundRateLimiterConfig.capacity,
tokens: update.outboundRateLimiterConfig.capacity,
lastUpdated: uint32(block.timestamp),
isEnabled: update.outboundRateLimiterConfig.isEnabled
}),
inboundRateLimiterConfig: RateLimiter.TokenBucket({
rate: update.inboundRateLimiterConfig.rate,
capacity: update.inboundRateLimiterConfig.capacity,
tokens: update.inboundRateLimiterConfig.capacity,
lastUpdated: uint32(block.timestamp),
isEnabled: update.inboundRateLimiterConfig.isEnabled
}),
remotePoolAddress: update.remotePoolAddress,
remoteTokenAddress: update.remoteTokenAddress
});
emit ChainAdded(
update.remoteChainSelector,
update.remoteTokenAddress,
update.outboundRateLimiterConfig,
update.inboundRateLimiterConfig
);
} else {
// If the chain doesn't exist, revert
if (!s_remoteChainSelectors.remove(update.remoteChainSelector)) {
revert NonExistentChain(update.remoteChainSelector);
}
delete s_remoteChainConfigs[update.remoteChainSelector];
emit ChainRemoved(update.remoteChainSelector);
}
}
}
// ================================================================
// │ Rate limiting │
// ================================================================
/// @notice Sets the rate limiter admin address.
/// @dev Only callable by the owner.
/// @param rateLimitAdmin The new rate limiter admin address.
function setRateLimitAdmin(address rateLimitAdmin) external onlyOwner {
s_rateLimitAdmin = rateLimitAdmin;
}
/// @notice Gets the rate limiter admin address.
function getRateLimitAdmin() external view returns (address) {
return s_rateLimitAdmin;
}
/// @notice Consumes outbound rate limiting capacity in this pool
function _consumeOutboundRateLimit(uint64 remoteChainSelector, uint256 amount) internal {
s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._consume(amount, address(i_token));
}
/// @notice Consumes inbound rate limiting capacity in this pool
function _consumeInboundRateLimit(uint64 remoteChainSelector, uint256 amount) internal {
s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._consume(amount, address(i_token));
}
/// @notice Gets the token bucket with its values for the block it was requested at.
/// @return The token bucket.
function getCurrentOutboundRateLimiterState(uint64 remoteChainSelector)
external
view
returns (RateLimiter.TokenBucket memory)
{
return s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._currentTokenBucketState();
}
/// @notice Gets the token bucket with its values for the block it was requested at.
/// @return The token bucket.
function getCurrentInboundRateLimiterState(uint64 remoteChainSelector)
external
view
returns (RateLimiter.TokenBucket memory)
{
return s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._currentTokenBucketState();
}
/// @notice Sets the chain rate limiter config.
/// @param remoteChainSelector The remote chain selector for which the rate limits apply.
/// @param outboundConfig The new outbound rate limiter config, meaning the onRamp rate limits for the given chain.
/// @param inboundConfig The new inbound rate limiter config, meaning the offRamp rate limits for the given chain.
function setChainRateLimiterConfig(
uint64 remoteChainSelector,
RateLimiter.Config memory outboundConfig,
RateLimiter.Config memory inboundConfig
) external {
if (msg.sender != s_rateLimitAdmin && msg.sender != owner()) revert Unauthorized(msg.sender);
_setRateLimitConfig(remoteChainSelector, outboundConfig, inboundConfig);
}
function _setRateLimitConfig(
uint64 remoteChainSelector,
RateLimiter.Config memory outboundConfig,
RateLimiter.Config memory inboundConfig
) internal {
if (!isSupportedChain(remoteChainSelector)) revert NonExistentChain(remoteChainSelector);
RateLimiter._validateTokenBucketConfig(outboundConfig, false);
s_remoteChainConfigs[remoteChainSelector].outboundRateLimiterConfig._setTokenBucketConfig(outboundConfig);
RateLimiter._validateTokenBucketConfig(inboundConfig, false);
s_remoteChainConfigs[remoteChainSelector].inboundRateLimiterConfig._setTokenBucketConfig(inboundConfig);
emit ChainConfigured(remoteChainSelector, outboundConfig, inboundConfig);
}
// ================================================================
// │ Access │
// ================================================================
/// @notice Checks whether remote chain selector is configured on this contract, and if the msg.sender
/// is a permissioned onRamp for the given chain on the Router.
function _onlyOnRamp(uint64 remoteChainSelector) internal view {
if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector);
if (!(msg.sender == s_router.getOnRamp(remoteChainSelector))) revert CallerIsNotARampOnRouter(msg.sender);
}
/// @notice Checks whether remote chain selector is configured on this contract, and if the msg.sender
/// is a permissioned offRamp for the given chain on the Router.
function _onlyOffRamp(uint64 remoteChainSelector) internal view {
if (!isSupportedChain(remoteChainSelector)) revert ChainNotAllowed(remoteChainSelector);
if (!s_router.isOffRamp(remoteChainSelector, msg.sender)) revert CallerIsNotARampOnRouter(msg.sender);
}
// ================================================================
// │ Allowlist │
// ================================================================
function _checkAllowList(address sender) internal view {
if (i_allowlistEnabled) {
if (!s_allowList.contains(sender)) {
revert SenderNotAllowed(sender);
}
}
}
/// @notice Gets whether the allowList functionality is enabled.
/// @return true is enabled, false if not.
function getAllowListEnabled() external view returns (bool) {
return i_allowlistEnabled;
}
/// @notice Gets the allowed addresses.
/// @return The allowed addresses.
function getAllowList() external view returns (address[] memory) {
return s_allowList.values();
}
/// @notice Apply updates to the allow list.
/// @param removes The addresses to be removed.
/// @param adds The addresses to be added.
function applyAllowListUpdates(address[] calldata removes, address[] calldata adds) external onlyOwner {
_applyAllowListUpdates(removes, adds);
}
/// @notice Internal version of applyAllowListUpdates to allow for reuse in the constructor.
function _applyAllowListUpdates(address[] memory removes, address[] memory adds) internal {
if (!i_allowlistEnabled) revert AllowListNotEnabled();
for (uint256 i = 0; i < removes.length; ++i) {
address toRemove = removes[i];
if (s_allowList.remove(toRemove)) {
emit AllowListRemove(toRemove);
}
}
for (uint256 i = 0; i < adds.length; ++i) {
address toAdd = adds[i];
if (toAdd == address(0)) {
continue;
}
if (s_allowList.add(toAdd)) {
emit AllowListAdd(toAdd);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Pool} from "../libraries/Pool.sol";
import {IERC165} from "../../vendor/openzeppelin-solidity/v5.0.2/contracts/utils/introspection/IERC165.sol";
/// @notice Shared public interface for multiple V1 pool types.
/// Each pool type handles a different child token model (lock/unlock, mint/burn.)
interface IPoolV1 is IERC165 {
/// @notice Lock tokens into the pool or burn the tokens.
/// @param lockOrBurnIn Encoded data fields for the processing of tokens on the source chain.
/// @return lockOrBurnOut Encoded data fields for the processing of tokens on the destination chain.
function lockOrBurn(Pool.LockOrBurnInV1 calldata lockOrBurnIn)
external
returns (Pool.LockOrBurnOutV1 memory lockOrBurnOut);
/// @notice Releases or mints tokens to the receiver address.
/// @param releaseOrMintIn All data required to release or mint tokens.
/// @return releaseOrMintOut The amount of tokens released or minted on the local chain, denominated
/// in the local token's decimals.
/// @dev The offramp asserts that the balanceOf of the receiver has been incremented by exactly the number
/// of tokens that is returned in ReleaseOrMintOutV1.destinationAmount. If the amounts do not match, the tx reverts.
function releaseOrMint(Pool.ReleaseOrMintInV1 calldata releaseOrMintIn)
external
returns (Pool.ReleaseOrMintOutV1 memory);
/// @notice Checks whether a remote chain is supported in the token pool.
/// @param remoteChainSelector The selector of the remote chain.
/// @return true if the given chain is a permissioned remote chain.
function isSupportedChain(uint64 remoteChainSelector) external view returns (bool);
/// @notice Returns if the token pool supports the given token.
/// @param token The address of the token.
/// @return true if the token is supported by the pool.
function isSupportedToken(address token) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/// @notice This interface contains the only RMN-related functions that might be used on-chain by other CCIP contracts.
interface IRMN {
/// @notice A Merkle root tagged with the address of the commit store contract it is destined for.
struct TaggedRoot {
address commitStore;
bytes32 root;
}
/// @notice Callers MUST NOT cache the return value as a blessed tagged root could become unblessed.
function isBlessed(TaggedRoot calldata taggedRoot) external view returns (bool);
/// @notice Iff there is an active global or legacy curse, this function returns true.
function isCursed() external view returns (bool);
/// @notice Iff there is an active global curse, or an active curse for `subject`, this function returns true.
/// @param subject To check whether a particular chain is cursed, set to bytes16(uint128(chainSelector)).
function isCursed(bytes16 subject) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {Client} from "../libraries/Client.sol";
interface IRouter {
error OnlyOffRamp();
/// @notice Route the message to its intended receiver contract.
/// @param message Client.Any2EVMMessage struct.
/// @param gasForCallExactCheck of params for exec
/// @param gasLimit set of params for exec
/// @param receiver set of params for exec
/// @dev if the receiver is a contracts that signals support for CCIP execution through EIP-165.
/// the contract is called. If not, only tokens are transferred.
/// @return success A boolean value indicating whether the ccip message was received without errors.
/// @return retBytes A bytes array containing return data form CCIP receiver.
/// @return gasUsed the gas used by the external customer call. Does not include any overhead.
function routeMessage(
Client.Any2EVMMessage calldata message,
uint16 gasForCallExactCheck,
uint256 gasLimit,
address receiver
) external returns (bool success, bytes memory retBytes, uint256 gasUsed);
/// @notice Returns the configured onramp for a specific destination chain.
/// @param destChainSelector The destination chain Id to get the onRamp for.
/// @return onRampAddress The address of the onRamp.
function getOnRamp(uint64 destChainSelector) external view returns (address onRampAddress);
/// @notice Return true if the given offRamp is a configured offRamp for the given source chain.
/// @param sourceChainSelector The source chain selector to check.
/// @param offRamp The address of the offRamp to check.
function isOffRamp(uint64 sourceChainSelector, address offRamp) external view returns (bool isOffRamp);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwner} from "./ConfirmedOwner.sol";
/// @title The OwnerIsCreator contract
/// @notice A contract with helpers for basic contract ownership.
contract OwnerIsCreator is ConfirmedOwner {
constructor() ConfirmedOwner(msg.sender) {}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.0;
/// @notice Implements Token Bucket rate limiting.
/// @dev uint128 is safe for rate limiter state.
/// For USD value rate limiting, it can adequately store USD value in 18 decimals.
/// For ERC20 token amount rate limiting, all tokens that will be listed will have at most
/// a supply of uint128.max tokens, and it will therefore not overflow the bucket.
/// In exceptional scenarios where tokens consumed may be larger than uint128,
/// e.g. compromised issuer, an enabled RateLimiter will check and revert.
library RateLimiter {
error BucketOverfilled();
error OnlyCallableByAdminOrOwner();
error TokenMaxCapacityExceeded(uint256 capacity, uint256 requested, address tokenAddress);
error TokenRateLimitReached(uint256 minWaitInSeconds, uint256 available, address tokenAddress);
error AggregateValueMaxCapacityExceeded(uint256 capacity, uint256 requested);
error AggregateValueRateLimitReached(uint256 minWaitInSeconds, uint256 available);
error InvalidRateLimitRate(Config rateLimiterConfig);
error DisabledNonZeroRateLimit(Config config);
error RateLimitMustBeDisabled();
event TokensConsumed(uint256 tokens);
event ConfigChanged(Config config);
struct TokenBucket {
uint128 tokens; // ──────╮ Current number of tokens that are in the bucket.
uint32 lastUpdated; // │ Timestamp in seconds of the last token refill, good for 100+ years.
bool isEnabled; // ──────╯ Indication whether the rate limiting is enabled or not
uint128 capacity; // ────╮ Maximum number of tokens that can be in the bucket.
uint128 rate; // ────────╯ Number of tokens per second that the bucket is refilled.
}
struct Config {
bool isEnabled; // Indication whether the rate limiting should be enabled
uint128 capacity; // ────╮ Specifies the capacity of the rate limiter
uint128 rate; // ───────╯ Specifies the rate of the rate limiter
}
/// @notice _consume removes the given tokens from the pool, lowering the
/// rate tokens allowed to be consumed for subsequent calls.
/// @param requestTokens The total tokens to be consumed from the bucket.
/// @param tokenAddress The token to consume capacity for, use 0x0 to indicate aggregate value capacity.
/// @dev Reverts when requestTokens exceeds bucket capacity or available tokens in the bucket
/// @dev emits removal of requestTokens if requestTokens is > 0
function _consume(TokenBucket storage s_bucket, uint256 requestTokens, address tokenAddress) internal {
// If there is no value to remove or rate limiting is turned off, skip this step to reduce gas usage
if (!s_bucket.isEnabled || requestTokens == 0) {
return;
}
uint256 tokens = s_bucket.tokens;
uint256 capacity = s_bucket.capacity;
uint256 timeDiff = block.timestamp - s_bucket.lastUpdated;
if (timeDiff != 0) {
if (tokens > capacity) revert BucketOverfilled();
// Refill tokens when arriving at a new block time
tokens = _calculateRefill(capacity, tokens, timeDiff, s_bucket.rate);
s_bucket.lastUpdated = uint32(block.timestamp);
}
if (capacity < requestTokens) {
// Token address 0 indicates consuming aggregate value rate limit capacity.
if (tokenAddress == address(0)) revert AggregateValueMaxCapacityExceeded(capacity, requestTokens);
revert TokenMaxCapacityExceeded(capacity, requestTokens, tokenAddress);
}
if (tokens < requestTokens) {
uint256 rate = s_bucket.rate;
// Wait required until the bucket is refilled enough to accept this value, round up to next higher second
// Consume is not guaranteed to succeed after wait time passes if there is competing traffic.
// This acts as a lower bound of wait time.
uint256 minWaitInSeconds = ((requestTokens - tokens) + (rate - 1)) / rate;
if (tokenAddress == address(0)) revert AggregateValueRateLimitReached(minWaitInSeconds, tokens);
revert TokenRateLimitReached(minWaitInSeconds, tokens, tokenAddress);
}
tokens -= requestTokens;
// Downcast is safe here, as tokens is not larger than capacity
s_bucket.tokens = uint128(tokens);
emit TokensConsumed(requestTokens);
}
/// @notice Gets the token bucket with its values for the block it was requested at.
/// @return The token bucket.
function _currentTokenBucketState(TokenBucket memory bucket) internal view returns (TokenBucket memory) {
// We update the bucket to reflect the status at the exact time of the
// call. This means we might need to refill a part of the bucket based
// on the time that has passed since the last update.
bucket.tokens =
uint128(_calculateRefill(bucket.capacity, bucket.tokens, block.timestamp - bucket.lastUpdated, bucket.rate));
bucket.lastUpdated = uint32(block.timestamp);
return bucket;
}
/// @notice Sets the rate limited config.
/// @param s_bucket The token bucket
/// @param config The new config
function _setTokenBucketConfig(TokenBucket storage s_bucket, Config memory config) internal {
// First update the bucket to make sure the proper rate is used for all the time
// up until the config change.
uint256 timeDiff = block.timestamp - s_bucket.lastUpdated;
if (timeDiff != 0) {
s_bucket.tokens = uint128(_calculateRefill(s_bucket.capacity, s_bucket.tokens, timeDiff, s_bucket.rate));
s_bucket.lastUpdated = uint32(block.timestamp);
}
s_bucket.tokens = uint128(_min(config.capacity, s_bucket.tokens));
s_bucket.isEnabled = config.isEnabled;
s_bucket.capacity = config.capacity;
s_bucket.rate = config.rate;
emit ConfigChanged(config);
}
/// @notice Validates the token bucket config
function _validateTokenBucketConfig(Config memory config, bool mustBeDisabled) internal pure {
if (config.isEnabled) {
if (config.rate >= config.capacity || config.rate == 0) {
revert InvalidRateLimitRate(config);
}
if (mustBeDisabled) {
revert RateLimitMustBeDisabled();
}
} else {
if (config.rate != 0 || config.capacity != 0) {
revert DisabledNonZeroRateLimit(config);
}
}
}
/// @notice Calculate refilled tokens
/// @param capacity bucket capacity
/// @param tokens current bucket tokens
/// @param timeDiff block time difference since last refill
/// @param rate bucket refill rate
/// @return the value of tokens after refill
function _calculateRefill(
uint256 capacity,
uint256 tokens,
uint256 timeDiff,
uint256 rate
) private pure returns (uint256) {
return _min(capacity, tokens + timeDiff * rate);
}
/// @notice Return the smallest of two integers
/// @param a first int
/// @param b second int
/// @return smallest
function _min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
// End consumer library.
library Client {
/// @dev RMN depends on this struct, if changing, please notify the RMN maintainers.
struct EVMTokenAmount {
address token; // token address on the local chain.
uint256 amount; // Amount of tokens.
}
struct Any2EVMMessage {
bytes32 messageId; // MessageId corresponding to ccipSend on source.
uint64 sourceChainSelector; // Source chain selector.
bytes sender; // abi.decode(sender) if coming from an EVM chain.
bytes data; // payload sent in original message.
EVMTokenAmount[] destTokenAmounts; // Tokens and their amounts in their destination chain representation.
}
// If extraArgs is empty bytes, the default is 200k gas limit.
struct EVM2AnyMessage {
bytes receiver; // abi.encode(receiver address) for dest EVM chains
bytes data; // Data payload
EVMTokenAmount[] tokenAmounts; // Token transfers
address feeToken; // Address of feeToken. address(0) means you will send msg.value.
bytes extraArgs; // Populate this with _argsToBytes(EVMExtraArgsV2)
}
// bytes4(keccak256("CCIP EVMExtraArgsV1"));
bytes4 public constant EVM_EXTRA_ARGS_V1_TAG = 0x97a657c9;
struct EVMExtraArgsV1 {
uint256 gasLimit;
}
function _argsToBytes(EVMExtraArgsV1 memory extraArgs) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EVM_EXTRA_ARGS_V1_TAG, extraArgs);
}
// bytes4(keccak256("CCIP EVMExtraArgsV2"));
bytes4 public constant EVM_EXTRA_ARGS_V2_TAG = 0x181dcf10;
/// @param gasLimit: gas limit for the callback on the destination chain.
/// @param allowOutOfOrderExecution: if true, it indicates that the message can be executed in any order relative to other messages from the same sender.
/// This value's default varies by chain. On some chains, a particular value is enforced, meaning if the expected value
/// is not set, the message request will revert.
struct EVMExtraArgsV2 {
uint256 gasLimit;
bool allowOutOfOrderExecution;
}
function _argsToBytes(EVMExtraArgsV2 memory extraArgs) internal pure returns (bytes memory bts) {
return abi.encodeWithSelector(EVM_EXTRA_ARGS_V2_TAG, extraArgs);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ConfirmedOwnerWithProposal} from "./ConfirmedOwnerWithProposal.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwner is ConfirmedOwnerWithProposal {
constructor(address newOwner) ConfirmedOwnerWithProposal(newOwner, address(0)) {}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IOwnable} from "../interfaces/IOwnable.sol";
/// @title The ConfirmedOwner contract
/// @notice A contract with helpers for basic contract ownership.
contract ConfirmedOwnerWithProposal is IOwnable {
address private s_owner;
address private s_pendingOwner;
event OwnershipTransferRequested(address indexed from, address indexed to);
event OwnershipTransferred(address indexed from, address indexed to);
constructor(address newOwner, address pendingOwner) {
// solhint-disable-next-line gas-custom-errors
require(newOwner != address(0), "Cannot set owner to zero");
s_owner = newOwner;
if (pendingOwner != address(0)) {
_transferOwnership(pendingOwner);
}
}
/// @notice Allows an owner to begin transferring ownership to a new address.
function transferOwnership(address to) public override onlyOwner {
_transferOwnership(to);
}
/// @notice Allows an ownership transfer to be completed by the recipient.
function acceptOwnership() external override {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_pendingOwner, "Must be proposed owner");
address oldOwner = s_owner;
s_owner = msg.sender;
s_pendingOwner = address(0);
emit OwnershipTransferred(oldOwner, msg.sender);
}
/// @notice Get the current owner
function owner() public view override returns (address) {
return s_owner;
}
/// @notice validate, transfer ownership, and emit relevant events
function _transferOwnership(address to) private {
// solhint-disable-next-line gas-custom-errors
require(to != msg.sender, "Cannot transfer to self");
s_pendingOwner = to;
emit OwnershipTransferRequested(s_owner, to);
}
/// @notice validate access
function _validateOwnership() internal view {
// solhint-disable-next-line gas-custom-errors
require(msg.sender == s_owner, "Only callable by owner");
}
/// @notice Reverts if called by anyone other than the contract owner.
modifier onlyOwner() {
_validateOwnership();
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IOwnable {
function owner() external returns (address);
function transferOwnership(address recipient) external;
function acceptOwnership() external;
}{
"remappings": [
"forge-std/=src/v0.8/vendor/forge-std/src/",
"@openzeppelin/=node_modules/@openzeppelin/",
"@arbitrum/=node_modules/@arbitrum/",
"hardhat/=node_modules/hardhat/",
"@eth-optimism/=node_modules/@eth-optimism/",
"@scroll-tech/=node_modules/@scroll-tech/",
"@offchainlabs/=node_modules/@offchainlabs/"
],
"optimizer": {
"enabled": true,
"runs": 26000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"viaIR": false,
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"address[]","name":"allowlist","type":"address[]"},{"internalType":"address","name":"rmnProxy","type":"address"},{"internalType":"bool","name":"acceptLiquidity","type":"bool"},{"internalType":"address","name":"router","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"}],"name":"AggregateValueMaxCapacityExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"minWaitInSeconds","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"}],"name":"AggregateValueRateLimitReached","type":"error"},{"inputs":[],"name":"AllowListNotEnabled","type":"error"},{"inputs":[],"name":"BucketOverfilled","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"CallerIsNotARampOnRouter","type":"error"},{"inputs":[{"internalType":"uint64","name":"chainSelector","type":"uint64"}],"name":"ChainAlreadyExists","type":"error"},{"inputs":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"}],"name":"ChainNotAllowed","type":"error"},{"inputs":[],"name":"CursedByRMN","type":"error"},{"inputs":[{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"config","type":"tuple"}],"name":"DisabledNonZeroRateLimit","type":"error"},{"inputs":[],"name":"InsufficientLiquidity","type":"error"},{"inputs":[{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"rateLimiterConfig","type":"tuple"}],"name":"InvalidRateLimitRate","type":"error"},{"inputs":[{"internalType":"bytes","name":"sourcePoolAddress","type":"bytes"}],"name":"InvalidSourcePoolAddress","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"LiquidityNotAccepted","type":"error"},{"inputs":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"}],"name":"NonExistentChain","type":"error"},{"inputs":[],"name":"RateLimitMustBeDisabled","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"SenderNotAllowed","type":"error"},{"inputs":[{"internalType":"uint256","name":"capacity","type":"uint256"},{"internalType":"uint256","name":"requested","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"TokenMaxCapacityExceeded","type":"error"},{"inputs":[{"internalType":"uint256","name":"minWaitInSeconds","type":"uint256"},{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"address","name":"tokenAddress","type":"address"}],"name":"TokenRateLimitReached","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAddressNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"AllowListAdd","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"AllowListRemove","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Burned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"remoteChainSelector","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"remoteToken","type":"bytes"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"indexed":false,"internalType":"struct RateLimiter.Config","name":"outboundRateLimiterConfig","type":"tuple"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"indexed":false,"internalType":"struct RateLimiter.Config","name":"inboundRateLimiterConfig","type":"tuple"}],"name":"ChainAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"remoteChainSelector","type":"uint64"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"indexed":false,"internalType":"struct RateLimiter.Config","name":"outboundRateLimiterConfig","type":"tuple"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"indexed":false,"internalType":"struct RateLimiter.Config","name":"inboundRateLimiterConfig","type":"tuple"}],"name":"ChainConfigured","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"remoteChainSelector","type":"uint64"}],"name":"ChainRemoved","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"indexed":false,"internalType":"struct RateLimiter.Config","name":"config","type":"tuple"}],"name":"ConfigChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IPoolPriorTo1_5","name":"oldPool","type":"address"},{"indexed":false,"internalType":"contract IPoolPriorTo1_5","name":"newPool","type":"address"}],"name":"LegacyPoolChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LiquidityAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provider","type":"address"},{"indexed":true,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LiquidityRemoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Locked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Minted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Released","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint64","name":"remoteChainSelector","type":"uint64"},{"indexed":false,"internalType":"bytes","name":"previousPoolAddress","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"remotePoolAddress","type":"bytes"}],"name":"RemotePoolSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRouter","type":"address"},{"indexed":false,"internalType":"address","name":"newRouter","type":"address"}],"name":"RouterUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"tokens","type":"uint256"}],"name":"TokensConsumed","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"removes","type":"address[]"},{"internalType":"address[]","name":"adds","type":"address[]"}],"name":"applyAllowListUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"},{"internalType":"bool","name":"allowed","type":"bool"},{"internalType":"bytes","name":"remotePoolAddress","type":"bytes"},{"internalType":"bytes","name":"remoteTokenAddress","type":"bytes"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"outboundRateLimiterConfig","type":"tuple"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"inboundRateLimiterConfig","type":"tuple"}],"internalType":"struct TokenPool.ChainUpdate[]","name":"chains","type":"tuple[]"}],"name":"applyChainUpdates","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"canAcceptLiquidity","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllowList","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllowListEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"}],"name":"getCurrentInboundRateLimiterState","outputs":[{"components":[{"internalType":"uint128","name":"tokens","type":"uint128"},{"internalType":"uint32","name":"lastUpdated","type":"uint32"},{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.TokenBucket","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"}],"name":"getCurrentOutboundRateLimiterState","outputs":[{"components":[{"internalType":"uint128","name":"tokens","type":"uint128"},{"internalType":"uint32","name":"lastUpdated","type":"uint32"},{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.TokenBucket","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"","type":"uint64"}],"name":"getOnRamp","outputs":[{"internalType":"address","name":"onRampAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPreviousPool","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRateLimitAdmin","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRebalancer","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"}],"name":"getRemotePool","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"}],"name":"getRemoteToken","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRmnProxy","outputs":[{"internalType":"address","name":"rmnProxy","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRouter","outputs":[{"internalType":"address","name":"router","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSupportedChains","outputs":[{"internalType":"uint64[]","name":"","type":"uint64[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getToken","outputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"sourceChainSelector","type":"uint64"},{"internalType":"address","name":"offRamp","type":"address"}],"name":"isOffRamp","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"}],"name":"isSupportedChain","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"isSupportedToken","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"receiver","type":"bytes"},{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"},{"internalType":"address","name":"originalSender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"localToken","type":"address"}],"internalType":"struct Pool.LockOrBurnInV1","name":"lockOrBurnIn","type":"tuple"}],"name":"lockOrBurn","outputs":[{"components":[{"internalType":"bytes","name":"destTokenAddress","type":"bytes"},{"internalType":"bytes","name":"destPoolData","type":"bytes"}],"internalType":"struct Pool.LockOrBurnOutV1","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"provideLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes","name":"originalSender","type":"bytes"},{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"localToken","type":"address"},{"internalType":"bytes","name":"sourcePoolAddress","type":"bytes"},{"internalType":"bytes","name":"sourcePoolData","type":"bytes"},{"internalType":"bytes","name":"offchainTokenData","type":"bytes"}],"internalType":"struct Pool.ReleaseOrMintInV1","name":"releaseOrMintIn","type":"tuple"}],"name":"releaseOrMint","outputs":[{"components":[{"internalType":"uint256","name":"destinationAmount","type":"uint256"}],"internalType":"struct Pool.ReleaseOrMintOutV1","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"outboundConfig","type":"tuple"},{"components":[{"internalType":"bool","name":"isEnabled","type":"bool"},{"internalType":"uint128","name":"capacity","type":"uint128"},{"internalType":"uint128","name":"rate","type":"uint128"}],"internalType":"struct RateLimiter.Config","name":"inboundConfig","type":"tuple"}],"name":"setChainRateLimiterConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IPoolPriorTo1_5","name":"prevPool","type":"address"}],"name":"setPreviousPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rateLimitAdmin","type":"address"}],"name":"setRateLimitAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"rebalancer","type":"address"}],"name":"setRebalancer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"remoteChainSelector","type":"uint64"},{"internalType":"bytes","name":"remotePoolAddress","type":"bytes"}],"name":"setRemotePool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newRouter","type":"address"}],"name":"setRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"typeAndVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101006040523480156200001257600080fd5b5060405162004f4338038062004f4383398101604081905262000035916200056d565b84848483838383833380600081620000945760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000c757620000c78162000186565b5050506001600160a01b0384161580620000e857506001600160a01b038116155b80620000fb57506001600160a01b038216155b156200011a576040516342bcdf7f60e11b815260040160405180910390fd5b6001600160a01b0384811660805282811660a052600480546001600160a01b031916918316919091179055825115801560c0526200016d576040805160008152602081019091526200016d908462000231565b5050505094151560e05250620006de9650505050505050565b336001600160a01b03821603620001e05760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016200008b565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60c05162000252576040516335f4a7b360e01b815260040160405180910390fd5b60005b8251811015620002dd57600083828151811062000276576200027662000690565b60209081029190910101519050620002906002826200038e565b15620002d3576040516001600160a01b03821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b5060010162000255565b5060005b81518110156200038957600082828151811062000302576200030262000690565b6020026020010151905060006001600160a01b0316816001600160a01b0316036200032e575062000380565b6200033b600282620003ae565b156200037e576040516001600160a01b03821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b600101620002e1565b505050565b6000620003a5836001600160a01b038416620003c5565b90505b92915050565b6000620003a5836001600160a01b038416620004c9565b60008181526001830160205260408120548015620004be576000620003ec600183620006a6565b85549091506000906200040290600190620006a6565b90508082146200046e57600086600001828154811062000426576200042662000690565b90600052602060002001549050808760000184815481106200044c576200044c62000690565b6000918252602080832090910192909255918252600188019052604090208390555b8554869080620004825762000482620006c8565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050620003a8565b6000915050620003a8565b60008181526001830160205260408120546200051257508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155620003a8565b506000620003a8565b6001600160a01b03811681146200053157600080fd5b50565b634e487b7160e01b600052604160045260246000fd5b805162000557816200051b565b919050565b805180151581146200055757600080fd5b600080600080600060a086880312156200058657600080fd5b855162000593816200051b565b602087810151919650906001600160401b0380821115620005b357600080fd5b818901915089601f830112620005c857600080fd5b815181811115620005dd57620005dd62000534565b8060051b604051601f19603f8301168101818110858211171562000605576200060562000534565b60405291825284820192508381018501918c8311156200062457600080fd5b938501935b828510156200064d576200063d856200054a565b8452938501939285019262000629565b80995050505050505062000664604087016200054a565b925062000674606087016200055c565b915062000684608087016200054a565b90509295509295909350565b634e487b7160e01b600052603260045260246000fd5b81810381811115620003a857634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052603160045260246000fd5b60805160a05160c05160e0516147c2620007816000396000818161057e015261195401526000818161062b01528181611f8e0152612b4a01526000818161060501528181611d2101526122410152600081816102c80152818161031d01528181610809015281816108db015281816109b501528181611a1601528181611c41015281816121610152818161234701528181612ae00152612d3501526147c26000f3fe608060405234801561001057600080fd5b506004361061025c5760003560e01c80639766b93211610145578063c0d78655116100bd578063db6327dc1161008c578063e0351e1311610071578063e0351e1314610629578063eb521a4c1461064f578063f2fde38b1461066257600080fd5b8063db6327dc146105f0578063dc0bd9711461060357600080fd5b8063c0d78655146105a2578063c4bffe2b146105b5578063c75eea9c146105ca578063cf7401f3146105dd57600080fd5b8063a8d87a3b11610114578063b0f479a1116100f9578063b0f479a11461054b578063b794658014610569578063bb98546b1461057c57600080fd5b8063a8d87a3b146104c9578063af58d59f146104dc57600080fd5b80639766b932146104635780639a4575b914610476578063a2b261d814610496578063a7cd63b7146104b457600080fd5b806366320087116101d857806379ba5097116101a757806383826b2b1161018c57806383826b2b1461041f5780638926f54f146104325780638da5cb5b1461044557600080fd5b806379ba5097146104045780637d54534e1461040c57600080fd5b806366320087146103ad5780636cfd1553146103c05780636d3d1a58146103d357806378a010b2146103f157600080fd5b806321df0da71161022f5780633907753711610214578063390775371461035a578063432a6ba31461037c57806354c8a4f31461039a57600080fd5b806321df0da7146102c6578063240028e81461030d57600080fd5b806301ffc9a7146102615780630a2fd493146102895780630a861f2a146102a9578063181f5a77146102be575b600080fd5b61027461026f3660046136df565b610675565b60405190151581526020015b60405180910390f35b61029c61029736600461373e565b6106d1565b60405161028091906137c7565b6102bc6102b73660046137da565b610781565b005b61029c610932565b7f00000000000000000000000000000000000000000000000000000000000000005b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610280565b61027461031b366004613820565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff90811691161490565b61036d61036836600461383d565b61094e565b60405190518152602001610280565b600a5473ffffffffffffffffffffffffffffffffffffffff166102e8565b6102bc6103a83660046138c5565b610a87565b6102bc6103bb366004613931565b610b02565b6102bc6103ce366004613820565b610b8e565b60085473ffffffffffffffffffffffffffffffffffffffff166102e8565b6102bc6103ff36600461395d565b610bdd565b6102bc610d4c565b6102bc61041a366004613820565b610e49565b61027461042d3660046139e0565b610e98565b61027461044036600461373e565b610f65565b60005473ffffffffffffffffffffffffffffffffffffffff166102e8565b6102bc610471366004613820565b610f7c565b610489610484366004613a17565b61100b565b6040516102809190613a52565b60095473ffffffffffffffffffffffffffffffffffffffff166102e8565b6104bc6110d4565b6040516102809190613ab2565b6102e86104d736600461373e565b503090565b6104ef6104ea36600461373e565b6110e5565b604051610280919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff166102e8565b61029c61057736600461373e565b6111ba565b7f0000000000000000000000000000000000000000000000000000000000000000610274565b6102bc6105b0366004613820565b6111e5565b6105bd6112b9565b6040516102809190613b0c565b6104ef6105d836600461373e565b611371565b6102bc6105eb366004613cc3565b611443565b6102bc6105fe366004613d08565b6114cc565b7f00000000000000000000000000000000000000000000000000000000000000006102e8565b7f0000000000000000000000000000000000000000000000000000000000000000610274565b6102bc61065d3660046137da565b611952565b6102bc610670366004613820565b611a6e565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fe1d405660000000000000000000000000000000000000000000000000000000014806106cb57506106cb82611a82565b92915050565b67ffffffffffffffff811660009081526007602052604090206004018054606091906106fc90613d4a565b80601f016020809104026020016040519081016040528092919081815260200182805461072890613d4a565b80156107755780601f1061074a57610100808354040283529160200191610775565b820191906000526020600020905b81548152906001019060200180831161075857829003601f168201915b50505050509050919050565b600a5473ffffffffffffffffffffffffffffffffffffffff1633146107d9576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610865573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108899190613d9d565b10156108c1576040517fbb55fd2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61090273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163383611b66565b604051819033907fc2c3f06e49b9f15e7b4af9055e183b0d73362e033ad82a07dec9bf984017171990600090a350565b6040518060600160405280602281526020016147946022913981565b60408051602081019091526000815261096e61096983613e52565b611c3a565b60095473ffffffffffffffffffffffffffffffffffffffff166109e5576109e061099e6060840160408501613820565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906060850135611b66565b6109f6565b6109f66109f183613e52565b611e6b565b610a066060830160408401613820565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f528460600135604051610a6891815260200190565b60405180910390a3506040805160208101909152606090910135815290565b610a8f611f09565b610afc84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808802828101820190935287825290935087925086918291850190849080828437600092019190915250611f8c92505050565b50505050565b610b0a611f09565b6040517f0a861f2a0000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff831690630a861f2a90602401600060405180830381600087803b158015610b7257600080fd5b505af1158015610b86573d6000803e3d6000fd5b505050505050565b610b96611f09565b600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b610be5611f09565b610bee83610f65565b610c30576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016107d0565b67ffffffffffffffff831660009081526007602052604081206004018054610c5790613d4a565b80601f0160208091040260200160405190810160405280929190818152602001828054610c8390613d4a565b8015610cd05780601f10610ca557610100808354040283529160200191610cd0565b820191906000526020600020905b815481529060010190602001808311610cb357829003601f168201915b5050505067ffffffffffffffff8616600090815260076020526040902091925050600401610cff838583613f8f565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610d3e939291906140a9565b60405180910390a250505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610dcd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107d0565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610e51611f09565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600073ffffffffffffffffffffffffffffffffffffffff8216301480610f5e5750600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86169281019290925273ffffffffffffffffffffffffffffffffffffffff848116602484015216906383826b2b90604401602060405180830381865afa158015610f3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5e919061410d565b9392505050565b60006106cb600567ffffffffffffffff8416612142565b610f84611f09565b6009805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f81accd0a7023865eaa51b3399dd0eafc488bf3ba238402911e1659cfe860f22891015b60405180910390a15050565b604080518082019091526060808252602082015261103061102b8361412a565b61215a565b60095473ffffffffffffffffffffffffffffffffffffffff161561105f5761105f61105a8361412a565b612324565b6040516060830135815233907f9f1ec8c880f76798e7b793325d625e9b60e4082a553c98f42b6cda368dd600089060200160405180910390a260405180604001604052806110b9846020016020810190610577919061373e565b81526040805160208181019092526000815291015292915050565b60606110e0600261243e565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260039091015480841660608301529190910490911660808201526106cb9061244b565b67ffffffffffffffff811660009081526007602052604090206005018054606091906106fc90613d4a565b6111ed611f09565b73ffffffffffffffffffffffffffffffffffffffff811661123a576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f16849101610fff565b606060006112c7600561243e565b90506000815167ffffffffffffffff8111156112e5576112e5613b4e565b60405190808252806020026020018201604052801561130e578160200160208202803683370190505b50905060005b825181101561136a5782818151811061132f5761132f6141cc565b6020026020010151828281518110611349576113496141cc565b67ffffffffffffffff90921660209283029190910190910152600101611314565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260019091015480841660608301529190910490911660808201526106cb9061244b565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590611483575060005473ffffffffffffffffffffffffffffffffffffffff163314155b156114bc576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016107d0565b6114c78383836124fd565b505050565b6114d4611f09565b60005b818110156114c75760008383838181106114f3576114f36141cc565b905060200281019061150591906141fb565b61150e90614239565b905061152381608001518260200151156125e7565b6115368160a001518260200151156125e7565b8060200151156118325780516115589060059067ffffffffffffffff16612720565b61159d5780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107d0565b60408101515115806115b25750606081015151155b156115e9576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c179091169615150295909517909855908101519401519381169316909102919091176003820155915190919060048201906117ca90826142ed565b50606082015160058201906117df90826142ed565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c295506118259493929190614407565b60405180910390a1611949565b805161184a9060059067ffffffffffffffff1661272c565b61188f5780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107d0565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906118f86004830182613691565b611906600583016000613691565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b506001016114d7565b7f00000000000000000000000000000000000000000000000000000000000000006119a9576040517fe93f8fa400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a5473ffffffffffffffffffffffffffffffffffffffff1633146119fc576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016107d0565b611a3e73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016333084612738565b604051819033907fc17cea59c2955cb181b03393209566960365771dbba9dc3d510180e7cb31208890600090a350565b611a76611f09565b611a7f81612796565b50565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf000000000000000000000000000000000000000000000000000000001480611b1557507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b806106cb57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001492915050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526114c79084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261288b565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff908116911614611ccf5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016107d0565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da1919061410d565b15611dd8576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611de58160200151612997565b6000611df482602001516106d1565b9050805160001480611e18575080805190602001208260a001518051906020012014155b15611e55578160a001516040517f24eb47e50000000000000000000000000000000000000000000000000000000081526004016107d091906137c7565b611e6782602001518360600151612abd565b5050565b60095481516040808401516060850151602086015192517f8627fad600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90951694638627fad694611ed494909392916004016144a0565b600060405180830381600087803b158015611eee57600080fd5b505af1158015611f02573d6000803e3d6000fd5b5050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611f8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107d0565b565b7f0000000000000000000000000000000000000000000000000000000000000000611fe3576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8251811015612079576000838281518110612003576120036141cc565b60200260200101519050612021816002612b0490919063ffffffff16565b156120705760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101611fe6565b5060005b81518110156114c757600082828151811061209a5761209a6141cc565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036120de575061213a565b6120e9600282612b26565b156121385760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010161207d565b60008181526001830160205260408120541515610f5e565b60808101517f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff9081169116146121ef5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016107d0565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa15801561229d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c1919061410d565b156122f8576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123058160400151612b48565b6123128160200151612bc7565b611a7f81602001518260600151612d15565b60095460608201516123719173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811692911690611b66565b60095460408083015183516060850151602086015193517f9687544500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909516946396875445946123d994939291600401614501565b6000604051808303816000875af11580156123f8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611e679190810190614561565b60606000610f5e83612d59565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526124d982606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff16426124bd91906145fe565b85608001516fffffffffffffffffffffffffffffffff16612db4565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b61250683610f65565b612548576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016107d0565b6125538260006125e7565b67ffffffffffffffff831660009081526007602052604090206125769083612dde565b6125818160006125e7565b67ffffffffffffffff831660009081526007602052604090206125a79060020182612dde565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b8383836040516125da93929190614611565b60405180910390a1505050565b8151156126ae5781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff1610158061263d575060408201516fffffffffffffffffffffffffffffffff16155b1561267657816040517f8020d1240000000000000000000000000000000000000000000000000000000081526004016107d09190614694565b8015611e67576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff161515806126e7575060208201516fffffffffffffffffffffffffffffffff1615155b15611e6757816040517fd68af9cc0000000000000000000000000000000000000000000000000000000081526004016107d09190614694565b6000610f5e8383612f80565b6000610f5e8383612fcf565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610afc9085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611bb8565b3373ffffffffffffffffffffffffffffffffffffffff821603612815576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107d0565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006128ed826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166130c29092919063ffffffff16565b8051909150156114c7578080602001905181019061290b919061410d565b6114c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016107d0565b6129a081610f65565b6129e2576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016107d0565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa158015612a61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a85919061410d565b611a7f576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016107d0565b67ffffffffffffffff82166000908152600760205260409020611e6790600201827f00000000000000000000000000000000000000000000000000000000000000006130d1565b6000610f5e8373ffffffffffffffffffffffffffffffffffffffff8416612fcf565b6000610f5e8373ffffffffffffffffffffffffffffffffffffffff8416612f80565b7f000000000000000000000000000000000000000000000000000000000000000015611a7f57612b79600282613454565b611a7f576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016107d0565b612bd081610f65565b612c12576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016107d0565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa158015612c8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612caf91906146d0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611a7f576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016107d0565b67ffffffffffffffff82166000908152600760205260409020611e6790827f00000000000000000000000000000000000000000000000000000000000000006130d1565b60608160000180548060200260200160405190810160405280929190818152602001828054801561077557602002820191906000526020600020905b815481526020019060010190808311612d955750505050509050919050565b6000612dd385612dc484866146ed565b612dce9087614704565b613483565b90505b949350505050565b8154600090612e0790700100000000000000000000000000000000900463ffffffff16426145fe565b90508015612ea95760018301548354612e4f916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612db4565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612ecf916fffffffffffffffffffffffffffffffff9081169116613483565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c19906125da908490614694565b6000818152600183016020526040812054612fc7575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556106cb565b5060006106cb565b600081815260018301602052604081205480156130b8576000612ff36001836145fe565b8554909150600090613007906001906145fe565b905080821461306c576000866000018281548110613027576130276141cc565b906000526020600020015490508087600001848154811061304a5761304a6141cc565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061307d5761307d614717565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506106cb565b60009150506106cb565b6060612dd68484600085613499565b825474010000000000000000000000000000000000000000900460ff1615806130f8575081155b1561310257505050565b825460018401546fffffffffffffffffffffffffffffffff8083169291169060009061314890700100000000000000000000000000000000900463ffffffff16426145fe565b90508015613208578183111561318a576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546131c49083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612db4565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b848210156132bf5773ffffffffffffffffffffffffffffffffffffffff8416613267576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016107d0565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff851660448201526064016107d0565b848310156133d25760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690600090829061330390826145fe565b61330d878a6145fe565b6133179190614704565b6133219190614746565b905073ffffffffffffffffffffffffffffffffffffffff861661337a576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016107d0565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff871660448201526064016107d0565b6133dc85846145fe565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515610f5e565b60008183106134925781610f5e565b5090919050565b60608247101561352b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016107d0565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516135549190614781565b60006040518083038185875af1925050503d8060008114613591576040519150601f19603f3d011682016040523d82523d6000602084013e613596565b606091505b50915091506135a7878383876135b2565b979650505050505050565b606083156136485782516000036136415773ffffffffffffffffffffffffffffffffffffffff85163b613641576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107d0565b5081612dd6565b612dd6838381511561365d5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107d091906137c7565b50805461369d90613d4a565b6000825580601f106136ad575050565b601f016020900490600052602060002090810190611a7f91905b808211156136db57600081556001016136c7565b5090565b6000602082840312156136f157600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610f5e57600080fd5b803567ffffffffffffffff8116811461373957600080fd5b919050565b60006020828403121561375057600080fd5b610f5e82613721565b60005b8381101561377457818101518382015260200161375c565b50506000910152565b60008151808452613795816020860160208601613759565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610f5e602083018461377d565b6000602082840312156137ec57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114611a7f57600080fd5b8035613739816137f3565b60006020828403121561383257600080fd5b8135610f5e816137f3565b60006020828403121561384f57600080fd5b813567ffffffffffffffff81111561386657600080fd5b82016101008185031215610f5e57600080fd5b60008083601f84011261388b57600080fd5b50813567ffffffffffffffff8111156138a357600080fd5b6020830191508360208260051b85010111156138be57600080fd5b9250929050565b600080600080604085870312156138db57600080fd5b843567ffffffffffffffff808211156138f357600080fd5b6138ff88838901613879565b9096509450602087013591508082111561391857600080fd5b5061392587828801613879565b95989497509550505050565b6000806040838503121561394457600080fd5b823561394f816137f3565b946020939093013593505050565b60008060006040848603121561397257600080fd5b61397b84613721565b9250602084013567ffffffffffffffff8082111561399857600080fd5b818601915086601f8301126139ac57600080fd5b8135818111156139bb57600080fd5b8760208285010111156139cd57600080fd5b6020830194508093505050509250925092565b600080604083850312156139f357600080fd5b6139fc83613721565b91506020830135613a0c816137f3565b809150509250929050565b600060208284031215613a2957600080fd5b813567ffffffffffffffff811115613a4057600080fd5b820160a08185031215610f5e57600080fd5b602081526000825160406020840152613a6e606084018261377d565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016040850152613aa9828261377d565b95945050505050565b6020808252825182820181905260009190848201906040850190845b81811015613b0057835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613ace565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015613b0057835167ffffffffffffffff1683529284019291840191600101613b28565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715613ba157613ba1613b4e565b60405290565b60405160c0810167ffffffffffffffff81118282101715613ba157613ba1613b4e565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613c1157613c11613b4e565b604052919050565b8015158114611a7f57600080fd5b803561373981613c19565b80356fffffffffffffffffffffffffffffffff8116811461373957600080fd5b600060608284031215613c6457600080fd5b6040516060810181811067ffffffffffffffff82111715613c8757613c87613b4e565b6040529050808235613c9881613c19565b8152613ca660208401613c32565b6020820152613cb760408401613c32565b60408201525092915050565b600080600060e08486031215613cd857600080fd5b613ce184613721565b9250613cf08560208601613c52565b9150613cff8560808601613c52565b90509250925092565b60008060208385031215613d1b57600080fd5b823567ffffffffffffffff811115613d3257600080fd5b613d3e85828601613879565b90969095509350505050565b600181811c90821680613d5e57607f821691505b602082108103613d97577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600060208284031215613daf57600080fd5b5051919050565b600067ffffffffffffffff821115613dd057613dd0613b4e565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112613e0d57600080fd5b8135613e20613e1b82613db6565b613bca565b818152846020838601011115613e3557600080fd5b816020850160208301376000918101602001919091529392505050565b60006101008236031215613e6557600080fd5b613e6d613b7d565b823567ffffffffffffffff80821115613e8557600080fd5b613e9136838701613dfc565b8352613e9f60208601613721565b6020840152613eb060408601613815565b604084015260608501356060840152613ecb60808601613815565b608084015260a0850135915080821115613ee457600080fd5b613ef036838701613dfc565b60a084015260c0850135915080821115613f0957600080fd5b613f1536838701613dfc565b60c084015260e0850135915080821115613f2e57600080fd5b50613f3b36828601613dfc565b60e08301525092915050565b601f8211156114c7576000816000526020600020601f850160051c81016020861015613f705750805b601f850160051c820191505b81811015610b8657828155600101613f7c565b67ffffffffffffffff831115613fa757613fa7613b4e565b613fbb83613fb58354613d4a565b83613f47565b6000601f84116001811461400d5760008515613fd75750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611f02565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561405c578685013582556020948501946001909201910161403c565b5086821015614097577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b6040815260006140bc604083018661377d565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b60006020828403121561411f57600080fd5b8151610f5e81613c19565b600060a0823603121561413c57600080fd5b60405160a0810167ffffffffffffffff828210818311171561416057614160613b4e565b81604052843591508082111561417557600080fd5b5061418236828601613dfc565b82525061419160208401613721565b602082015260408301356141a4816137f3565b60408201526060838101359082015260808301356141c1816137f3565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec183360301811261422f57600080fd5b9190910192915050565b6000610140823603121561424c57600080fd5b614254613ba7565b61425d83613721565b815261426b60208401613c27565b6020820152604083013567ffffffffffffffff8082111561428b57600080fd5b61429736838701613dfc565b604084015260608501359150808211156142b057600080fd5b506142bd36828601613dfc565b6060830152506142d03660808501613c52565b60808201526142e23660e08501613c52565b60a082015292915050565b815167ffffffffffffffff81111561430757614307613b4e565b61431b816143158454613d4a565b84613f47565b602080601f83116001811461436e57600084156143385750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610b86565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156143bb5788860151825594840194600190910190840161439c565b50858210156143f757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff8716835280602084015261442b8184018761377d565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff90811660608701529087015116608085015291506144699050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152613aa9565b60a0815260006144b360a083018761377d565b73ffffffffffffffffffffffffffffffffffffffff8616602084015284604084015267ffffffffffffffff841660608401528281036080840152600081526020810191505095945050505050565b73ffffffffffffffffffffffffffffffffffffffff8516815260a06020820152600061453060a083018661377d565b60408301949094525067ffffffffffffffff9190911660608201528082036080909101526000815260200192915050565b60006020828403121561457357600080fd5b815167ffffffffffffffff81111561458a57600080fd5b8201601f8101841361459b57600080fd5b80516145a9613e1b82613db6565b8181528560208385010111156145be57600080fd5b613aa9826020830160208601613759565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156106cb576106cb6145cf565b67ffffffffffffffff8416815260e0810161465d60208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612dd6565b606081016106cb82848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b6000602082840312156146e257600080fd5b8151610f5e816137f3565b80820281158282048414176106cb576106cb6145cf565b808201808211156106cb576106cb6145cf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008261477c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000825161422f81846020870161375956fe4c6f636b52656c65617365546f6b656e506f6f6c416e6450726f787920312e352e30a164736f6c6343000818000a0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c311a21e6fef769344eb1515588b9d535662a1450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000141fa059441e0ca23ce184b6a78bafd2a517dde80000000000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b506004361061025c5760003560e01c80639766b93211610145578063c0d78655116100bd578063db6327dc1161008c578063e0351e1311610071578063e0351e1314610629578063eb521a4c1461064f578063f2fde38b1461066257600080fd5b8063db6327dc146105f0578063dc0bd9711461060357600080fd5b8063c0d78655146105a2578063c4bffe2b146105b5578063c75eea9c146105ca578063cf7401f3146105dd57600080fd5b8063a8d87a3b11610114578063b0f479a1116100f9578063b0f479a11461054b578063b794658014610569578063bb98546b1461057c57600080fd5b8063a8d87a3b146104c9578063af58d59f146104dc57600080fd5b80639766b932146104635780639a4575b914610476578063a2b261d814610496578063a7cd63b7146104b457600080fd5b806366320087116101d857806379ba5097116101a757806383826b2b1161018c57806383826b2b1461041f5780638926f54f146104325780638da5cb5b1461044557600080fd5b806379ba5097146104045780637d54534e1461040c57600080fd5b806366320087146103ad5780636cfd1553146103c05780636d3d1a58146103d357806378a010b2146103f157600080fd5b806321df0da71161022f5780633907753711610214578063390775371461035a578063432a6ba31461037c57806354c8a4f31461039a57600080fd5b806321df0da7146102c6578063240028e81461030d57600080fd5b806301ffc9a7146102615780630a2fd493146102895780630a861f2a146102a9578063181f5a77146102be575b600080fd5b61027461026f3660046136df565b610675565b60405190151581526020015b60405180910390f35b61029c61029736600461373e565b6106d1565b60405161028091906137c7565b6102bc6102b73660046137da565b610781565b005b61029c610932565b7f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab15b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610280565b61027461031b366004613820565b7f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab173ffffffffffffffffffffffffffffffffffffffff90811691161490565b61036d61036836600461383d565b61094e565b60405190518152602001610280565b600a5473ffffffffffffffffffffffffffffffffffffffff166102e8565b6102bc6103a83660046138c5565b610a87565b6102bc6103bb366004613931565b610b02565b6102bc6103ce366004613820565b610b8e565b60085473ffffffffffffffffffffffffffffffffffffffff166102e8565b6102bc6103ff36600461395d565b610bdd565b6102bc610d4c565b6102bc61041a366004613820565b610e49565b61027461042d3660046139e0565b610e98565b61027461044036600461373e565b610f65565b60005473ffffffffffffffffffffffffffffffffffffffff166102e8565b6102bc610471366004613820565b610f7c565b610489610484366004613a17565b61100b565b6040516102809190613a52565b60095473ffffffffffffffffffffffffffffffffffffffff166102e8565b6104bc6110d4565b6040516102809190613ab2565b6102e86104d736600461373e565b503090565b6104ef6104ea36600461373e565b6110e5565b604051610280919081516fffffffffffffffffffffffffffffffff908116825260208084015163ffffffff1690830152604080840151151590830152606080840151821690830152608092830151169181019190915260a00190565b60045473ffffffffffffffffffffffffffffffffffffffff166102e8565b61029c61057736600461373e565b6111ba565b7f0000000000000000000000000000000000000000000000000000000000000000610274565b6102bc6105b0366004613820565b6111e5565b6105bd6112b9565b6040516102809190613b0c565b6104ef6105d836600461373e565b611371565b6102bc6105eb366004613cc3565b611443565b6102bc6105fe366004613d08565b6114cc565b7f000000000000000000000000c311a21e6fef769344eb1515588b9d535662a1456102e8565b7f0000000000000000000000000000000000000000000000000000000000000000610274565b6102bc61065d3660046137da565b611952565b6102bc610670366004613820565b611a6e565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167fe1d405660000000000000000000000000000000000000000000000000000000014806106cb57506106cb82611a82565b92915050565b67ffffffffffffffff811660009081526007602052604090206004018054606091906106fc90613d4a565b80601f016020809104026020016040519081016040528092919081815260200182805461072890613d4a565b80156107755780601f1061074a57610100808354040283529160200191610775565b820191906000526020600020905b81548152906001019060200180831161075857829003601f168201915b50505050509050919050565b600a5473ffffffffffffffffffffffffffffffffffffffff1633146107d9576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281907f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab173ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610865573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108899190613d9d565b10156108c1576040517fbb55fd2700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61090273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab1163383611b66565b604051819033907fc2c3f06e49b9f15e7b4af9055e183b0d73362e033ad82a07dec9bf984017171990600090a350565b6040518060600160405280602281526020016147946022913981565b60408051602081019091526000815261096e61096983613e52565b611c3a565b60095473ffffffffffffffffffffffffffffffffffffffff166109e5576109e061099e6060840160408501613820565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab116906060850135611b66565b6109f6565b6109f66109f183613e52565b611e6b565b610a066060830160408401613820565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f2d87480f50083e2b2759522a8fdda59802650a8055e609a7772cf70c07748f528460600135604051610a6891815260200190565b60405180910390a3506040805160208101909152606090910135815290565b610a8f611f09565b610afc84848080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050604080516020808802828101820190935287825290935087925086918291850190849080828437600092019190915250611f8c92505050565b50505050565b610b0a611f09565b6040517f0a861f2a0000000000000000000000000000000000000000000000000000000081526004810182905273ffffffffffffffffffffffffffffffffffffffff831690630a861f2a90602401600060405180830381600087803b158015610b7257600080fd5b505af1158015610b86573d6000803e3d6000fd5b505050505050565b610b96611f09565b600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b610be5611f09565b610bee83610f65565b610c30576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016107d0565b67ffffffffffffffff831660009081526007602052604081206004018054610c5790613d4a565b80601f0160208091040260200160405190810160405280929190818152602001828054610c8390613d4a565b8015610cd05780601f10610ca557610100808354040283529160200191610cd0565b820191906000526020600020905b815481529060010190602001808311610cb357829003601f168201915b5050505067ffffffffffffffff8616600090815260076020526040902091925050600401610cff838583613f8f565b508367ffffffffffffffff167fdb4d6220746a38cbc5335f7e108f7de80f482f4d23350253dfd0917df75a14bf828585604051610d3e939291906140a9565b60405180910390a250505050565b60015473ffffffffffffffffffffffffffffffffffffffff163314610dcd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064016107d0565b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b610e51611f09565b600880547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b600073ffffffffffffffffffffffffffffffffffffffff8216301480610f5e5750600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff86169281019290925273ffffffffffffffffffffffffffffffffffffffff848116602484015216906383826b2b90604401602060405180830381865afa158015610f3a573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f5e919061410d565b9392505050565b60006106cb600567ffffffffffffffff8416612142565b610f84611f09565b6009805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f81accd0a7023865eaa51b3399dd0eafc488bf3ba238402911e1659cfe860f22891015b60405180910390a15050565b604080518082019091526060808252602082015261103061102b8361412a565b61215a565b60095473ffffffffffffffffffffffffffffffffffffffff161561105f5761105f61105a8361412a565b612324565b6040516060830135815233907f9f1ec8c880f76798e7b793325d625e9b60e4082a553c98f42b6cda368dd600089060200160405180910390a260405180604001604052806110b9846020016020810190610577919061373e565b81526040805160208181019092526000815291015292915050565b60606110e0600261243e565b905090565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845260028201546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260039091015480841660608301529190910490911660808201526106cb9061244b565b67ffffffffffffffff811660009081526007602052604090206005018054606091906106fc90613d4a565b6111ed611f09565b73ffffffffffffffffffffffffffffffffffffffff811661123a576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff000000000000000000000000000000000000000083168117909355604080519190921680825260208201939093527f02dc5c233404867c793b749c6d644beb2277536d18a7e7974d3f238e4c6f16849101610fff565b606060006112c7600561243e565b90506000815167ffffffffffffffff8111156112e5576112e5613b4e565b60405190808252806020026020018201604052801561130e578160200160208202803683370190505b50905060005b825181101561136a5782818151811061132f5761132f6141cc565b6020026020010151828281518110611349576113496141cc565b67ffffffffffffffff90921660209283029190910190910152600101611314565b5092915050565b6040805160a08101825260008082526020820181905291810182905260608101829052608081019190915267ffffffffffffffff8216600090815260076020908152604091829020825160a08101845281546fffffffffffffffffffffffffffffffff808216835270010000000000000000000000000000000080830463ffffffff16958401959095527401000000000000000000000000000000000000000090910460ff1615159482019490945260019091015480841660608301529190910490911660808201526106cb9061244b565b60085473ffffffffffffffffffffffffffffffffffffffff163314801590611483575060005473ffffffffffffffffffffffffffffffffffffffff163314155b156114bc576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016107d0565b6114c78383836124fd565b505050565b6114d4611f09565b60005b818110156114c75760008383838181106114f3576114f36141cc565b905060200281019061150591906141fb565b61150e90614239565b905061152381608001518260200151156125e7565b6115368160a001518260200151156125e7565b8060200151156118325780516115589060059067ffffffffffffffff16612720565b61159d5780516040517f1d5ad3c500000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107d0565b60408101515115806115b25750606081015151155b156115e9576040517f8579befe00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040805161012081018252608083810180516020908101516fffffffffffffffffffffffffffffffff9081168486019081524263ffffffff90811660a0808901829052865151151560c08a01528651860151851660e08a015295518901518416610100890152918752875180860189529489018051850151841686528585019290925281515115158589015281518401518316606080870191909152915188015183168587015283870194855288880151878901908152828a015183890152895167ffffffffffffffff1660009081526007865289902088518051825482890151838e01519289167fffffffffffffffffffffffff0000000000000000000000000000000000000000928316177001000000000000000000000000000000009188168202177fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff90811674010000000000000000000000000000000000000000941515850217865584890151948d0151948a16948a168202949094176001860155995180516002860180549b8301519f830151918b169b9093169a909a179d9096168a029c909c179091169615150295909517909855908101519401519381169316909102919091176003820155915190919060048201906117ca90826142ed565b50606082015160058201906117df90826142ed565b505081516060830151608084015160a08501516040517f8d340f17e19058004c20453540862a9c62778504476f6756755cb33bcd6c38c295506118259493929190614407565b60405180910390a1611949565b805161184a9060059067ffffffffffffffff1661272c565b61188f5780516040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff90911660048201526024016107d0565b805167ffffffffffffffff16600090815260076020526040812080547fffffffffffffffffffffff000000000000000000000000000000000000000000908116825560018201839055600282018054909116905560038101829055906118f86004830182613691565b611906600583016000613691565b5050805160405167ffffffffffffffff90911681527f5204aec90a3c794d8e90fded8b46ae9c7c552803e7e832e0c1d358396d8599169060200160405180910390a15b506001016114d7565b7f00000000000000000000000000000000000000000000000000000000000000006119a9576040517fe93f8fa400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a5473ffffffffffffffffffffffffffffffffffffffff1633146119fc576040517f8e4a23d60000000000000000000000000000000000000000000000000000000081523360048201526024016107d0565b611a3e73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab116333084612738565b604051819033907fc17cea59c2955cb181b03393209566960365771dbba9dc3d510180e7cb31208890600090a350565b611a76611f09565b611a7f81612796565b50565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167faff2afbf000000000000000000000000000000000000000000000000000000001480611b1557507fffffffff0000000000000000000000000000000000000000000000000000000082167f0e64dd2900000000000000000000000000000000000000000000000000000000145b806106cb57507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a7000000000000000000000000000000000000000000000000000000001492915050565b60405173ffffffffffffffffffffffffffffffffffffffff83166024820152604481018290526114c79084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff000000000000000000000000000000000000000000000000000000009093169290921790915261288b565b60808101517f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab173ffffffffffffffffffffffffffffffffffffffff908116911614611ccf5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016107d0565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000c311a21e6fef769344eb1515588b9d535662a14573ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa158015611d7d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611da1919061410d565b15611dd8576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611de58160200151612997565b6000611df482602001516106d1565b9050805160001480611e18575080805190602001208260a001518051906020012014155b15611e55578160a001516040517f24eb47e50000000000000000000000000000000000000000000000000000000081526004016107d091906137c7565b611e6782602001518360600151612abd565b5050565b60095481516040808401516060850151602086015192517f8627fad600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90951694638627fad694611ed494909392916004016144a0565b600060405180830381600087803b158015611eee57600080fd5b505af1158015611f02573d6000803e3d6000fd5b5050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611f8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107d0565b565b7f0000000000000000000000000000000000000000000000000000000000000000611fe3576040517f35f4a7b300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60005b8251811015612079576000838281518110612003576120036141cc565b60200260200101519050612021816002612b0490919063ffffffff16565b156120705760405173ffffffffffffffffffffffffffffffffffffffff821681527f800671136ab6cfee9fbe5ed1fb7ca417811aca3cf864800d127b927adedf75669060200160405180910390a15b50600101611fe6565b5060005b81518110156114c757600082828151811061209a5761209a6141cc565b60200260200101519050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff16036120de575061213a565b6120e9600282612b26565b156121385760405173ffffffffffffffffffffffffffffffffffffffff821681527f2640d4d76caf8bf478aabfa982fa4e1c4eb71a37f93cd15e80dbc657911546d89060200160405180910390a15b505b60010161207d565b60008181526001830160205260408120541515610f5e565b60808101517f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab173ffffffffffffffffffffffffffffffffffffffff9081169116146121ef5760808101516040517f961c9a4f00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016107d0565b60208101516040517f2cbc26bb00000000000000000000000000000000000000000000000000000000815260809190911b77ffffffffffffffff000000000000000000000000000000001660048201527f000000000000000000000000c311a21e6fef769344eb1515588b9d535662a14573ffffffffffffffffffffffffffffffffffffffff1690632cbc26bb90602401602060405180830381865afa15801561229d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122c1919061410d565b156122f8576040517f53ad11d800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6123058160400151612b48565b6123128160200151612bc7565b611a7f81602001518260600151612d15565b60095460608201516123719173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab1811692911690611b66565b60095460408083015183516060850151602086015193517f9687544500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909516946396875445946123d994939291600401614501565b6000604051808303816000875af11580156123f8573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052611e679190810190614561565b60606000610f5e83612d59565b6040805160a0810182526000808252602082018190529181018290526060810182905260808101919091526124d982606001516fffffffffffffffffffffffffffffffff1683600001516fffffffffffffffffffffffffffffffff16846020015163ffffffff16426124bd91906145fe565b85608001516fffffffffffffffffffffffffffffffff16612db4565b6fffffffffffffffffffffffffffffffff1682525063ffffffff4216602082015290565b61250683610f65565b612548576040517f1e670e4b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff841660048201526024016107d0565b6125538260006125e7565b67ffffffffffffffff831660009081526007602052604090206125769083612dde565b6125818160006125e7565b67ffffffffffffffff831660009081526007602052604090206125a79060020182612dde565b7f0350d63aa5f270e01729d00d627eeb8f3429772b1818c016c66a588a864f912b8383836040516125da93929190614611565b60405180910390a1505050565b8151156126ae5781602001516fffffffffffffffffffffffffffffffff1682604001516fffffffffffffffffffffffffffffffff1610158061263d575060408201516fffffffffffffffffffffffffffffffff16155b1561267657816040517f8020d1240000000000000000000000000000000000000000000000000000000081526004016107d09190614694565b8015611e67576040517f433fc33d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408201516fffffffffffffffffffffffffffffffff161515806126e7575060208201516fffffffffffffffffffffffffffffffff1615155b15611e6757816040517fd68af9cc0000000000000000000000000000000000000000000000000000000081526004016107d09190614694565b6000610f5e8383612f80565b6000610f5e8383612fcf565b60405173ffffffffffffffffffffffffffffffffffffffff80851660248301528316604482015260648101829052610afc9085907f23b872dd0000000000000000000000000000000000000000000000000000000090608401611bb8565b3373ffffffffffffffffffffffffffffffffffffffff821603612815576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107d0565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b60006128ed826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648152508573ffffffffffffffffffffffffffffffffffffffff166130c29092919063ffffffff16565b8051909150156114c7578080602001905181019061290b919061410d565b6114c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f7420737563636565640000000000000000000000000000000000000000000060648201526084016107d0565b6129a081610f65565b6129e2576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016107d0565b600480546040517f83826b2b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925233602483015273ffffffffffffffffffffffffffffffffffffffff16906383826b2b90604401602060405180830381865afa158015612a61573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612a85919061410d565b611a7f576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016107d0565b67ffffffffffffffff82166000908152600760205260409020611e6790600201827f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab16130d1565b6000610f5e8373ffffffffffffffffffffffffffffffffffffffff8416612fcf565b6000610f5e8373ffffffffffffffffffffffffffffffffffffffff8416612f80565b7f000000000000000000000000000000000000000000000000000000000000000015611a7f57612b79600282613454565b611a7f576040517fd0d2597600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff821660048201526024016107d0565b612bd081610f65565b612c12576040517fa9902c7e00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff821660048201526024016107d0565b600480546040517fa8d87a3b00000000000000000000000000000000000000000000000000000000815267ffffffffffffffff84169281019290925273ffffffffffffffffffffffffffffffffffffffff169063a8d87a3b90602401602060405180830381865afa158015612c8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612caf91906146d0565b73ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611a7f576040517f728fe07b0000000000000000000000000000000000000000000000000000000081523360048201526024016107d0565b67ffffffffffffffff82166000908152600760205260409020611e6790827f0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab16130d1565b60608160000180548060200260200160405190810160405280929190818152602001828054801561077557602002820191906000526020600020905b815481526020019060010190808311612d955750505050509050919050565b6000612dd385612dc484866146ed565b612dce9087614704565b613483565b90505b949350505050565b8154600090612e0790700100000000000000000000000000000000900463ffffffff16426145fe565b90508015612ea95760018301548354612e4f916fffffffffffffffffffffffffffffffff80821692811691859170010000000000000000000000000000000090910416612db4565b83546fffffffffffffffffffffffffffffffff919091167fffffffffffffffffffffffff0000000000000000000000000000000000000000909116177001000000000000000000000000000000004263ffffffff16021783555b60208201518354612ecf916fffffffffffffffffffffffffffffffff9081169116613483565b83548351151574010000000000000000000000000000000000000000027fffffffffffffffffffffff00ffffffff000000000000000000000000000000009091166fffffffffffffffffffffffffffffffff92831617178455602083015160408085015183167001000000000000000000000000000000000291909216176001850155517f9ea3374b67bf275e6bb9c8ae68f9cae023e1c528b4b27e092f0bb209d3531c19906125da908490614694565b6000818152600183016020526040812054612fc7575081546001818101845560008481526020808220909301849055845484825282860190935260409020919091556106cb565b5060006106cb565b600081815260018301602052604081205480156130b8576000612ff36001836145fe565b8554909150600090613007906001906145fe565b905080821461306c576000866000018281548110613027576130276141cc565b906000526020600020015490508087600001848154811061304a5761304a6141cc565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061307d5761307d614717565b6001900381819060005260206000200160009055905585600101600086815260200190815260200160002060009055600193505050506106cb565b60009150506106cb565b6060612dd68484600085613499565b825474010000000000000000000000000000000000000000900460ff1615806130f8575081155b1561310257505050565b825460018401546fffffffffffffffffffffffffffffffff8083169291169060009061314890700100000000000000000000000000000000900463ffffffff16426145fe565b90508015613208578183111561318a576040517f9725942a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018601546131c49083908590849070010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff16612db4565b86547fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff167001000000000000000000000000000000004263ffffffff160217875592505b848210156132bf5773ffffffffffffffffffffffffffffffffffffffff8416613267576040517ff94ebcd100000000000000000000000000000000000000000000000000000000815260048101839052602481018690526044016107d0565b6040517f1a76572a000000000000000000000000000000000000000000000000000000008152600481018390526024810186905273ffffffffffffffffffffffffffffffffffffffff851660448201526064016107d0565b848310156133d25760018681015470010000000000000000000000000000000090046fffffffffffffffffffffffffffffffff1690600090829061330390826145fe565b61330d878a6145fe565b6133179190614704565b6133219190614746565b905073ffffffffffffffffffffffffffffffffffffffff861661337a576040517f15279c0800000000000000000000000000000000000000000000000000000000815260048101829052602481018690526044016107d0565b6040517fd0c8d23a000000000000000000000000000000000000000000000000000000008152600481018290526024810186905273ffffffffffffffffffffffffffffffffffffffff871660448201526064016107d0565b6133dc85846145fe565b86547fffffffffffffffffffffffffffffffff00000000000000000000000000000000166fffffffffffffffffffffffffffffffff82161787556040518681529093507f1871cdf8010e63f2eb8384381a68dfa7416dc571a5517e66e88b2d2d0c0a690a9060200160405180910390a1505050505050565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515610f5e565b60008183106134925781610f5e565b5090919050565b60608247101561352b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f60448201527f722063616c6c000000000000000000000000000000000000000000000000000060648201526084016107d0565b6000808673ffffffffffffffffffffffffffffffffffffffff1685876040516135549190614781565b60006040518083038185875af1925050503d8060008114613591576040519150601f19603f3d011682016040523d82523d6000602084013e613596565b606091505b50915091506135a7878383876135b2565b979650505050505050565b606083156136485782516000036136415773ffffffffffffffffffffffffffffffffffffffff85163b613641576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016107d0565b5081612dd6565b612dd6838381511561365d5781518083602001fd5b806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016107d091906137c7565b50805461369d90613d4a565b6000825580601f106136ad575050565b601f016020900490600052602060002090810190611a7f91905b808211156136db57600081556001016136c7565b5090565b6000602082840312156136f157600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610f5e57600080fd5b803567ffffffffffffffff8116811461373957600080fd5b919050565b60006020828403121561375057600080fd5b610f5e82613721565b60005b8381101561377457818101518382015260200161375c565b50506000910152565b60008151808452613795816020860160208601613759565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610f5e602083018461377d565b6000602082840312156137ec57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff81168114611a7f57600080fd5b8035613739816137f3565b60006020828403121561383257600080fd5b8135610f5e816137f3565b60006020828403121561384f57600080fd5b813567ffffffffffffffff81111561386657600080fd5b82016101008185031215610f5e57600080fd5b60008083601f84011261388b57600080fd5b50813567ffffffffffffffff8111156138a357600080fd5b6020830191508360208260051b85010111156138be57600080fd5b9250929050565b600080600080604085870312156138db57600080fd5b843567ffffffffffffffff808211156138f357600080fd5b6138ff88838901613879565b9096509450602087013591508082111561391857600080fd5b5061392587828801613879565b95989497509550505050565b6000806040838503121561394457600080fd5b823561394f816137f3565b946020939093013593505050565b60008060006040848603121561397257600080fd5b61397b84613721565b9250602084013567ffffffffffffffff8082111561399857600080fd5b818601915086601f8301126139ac57600080fd5b8135818111156139bb57600080fd5b8760208285010111156139cd57600080fd5b6020830194508093505050509250925092565b600080604083850312156139f357600080fd5b6139fc83613721565b91506020830135613a0c816137f3565b809150509250929050565b600060208284031215613a2957600080fd5b813567ffffffffffffffff811115613a4057600080fd5b820160a08185031215610f5e57600080fd5b602081526000825160406020840152613a6e606084018261377d565b905060208401517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0848303016040850152613aa9828261377d565b95945050505050565b6020808252825182820181905260009190848201906040850190845b81811015613b0057835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101613ace565b50909695505050505050565b6020808252825182820181905260009190848201906040850190845b81811015613b0057835167ffffffffffffffff1683529284019291840191600101613b28565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610100810167ffffffffffffffff81118282101715613ba157613ba1613b4e565b60405290565b60405160c0810167ffffffffffffffff81118282101715613ba157613ba1613b4e565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715613c1157613c11613b4e565b604052919050565b8015158114611a7f57600080fd5b803561373981613c19565b80356fffffffffffffffffffffffffffffffff8116811461373957600080fd5b600060608284031215613c6457600080fd5b6040516060810181811067ffffffffffffffff82111715613c8757613c87613b4e565b6040529050808235613c9881613c19565b8152613ca660208401613c32565b6020820152613cb760408401613c32565b60408201525092915050565b600080600060e08486031215613cd857600080fd5b613ce184613721565b9250613cf08560208601613c52565b9150613cff8560808601613c52565b90509250925092565b60008060208385031215613d1b57600080fd5b823567ffffffffffffffff811115613d3257600080fd5b613d3e85828601613879565b90969095509350505050565b600181811c90821680613d5e57607f821691505b602082108103613d97577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b600060208284031215613daf57600080fd5b5051919050565b600067ffffffffffffffff821115613dd057613dd0613b4e565b50601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b600082601f830112613e0d57600080fd5b8135613e20613e1b82613db6565b613bca565b818152846020838601011115613e3557600080fd5b816020850160208301376000918101602001919091529392505050565b60006101008236031215613e6557600080fd5b613e6d613b7d565b823567ffffffffffffffff80821115613e8557600080fd5b613e9136838701613dfc565b8352613e9f60208601613721565b6020840152613eb060408601613815565b604084015260608501356060840152613ecb60808601613815565b608084015260a0850135915080821115613ee457600080fd5b613ef036838701613dfc565b60a084015260c0850135915080821115613f0957600080fd5b613f1536838701613dfc565b60c084015260e0850135915080821115613f2e57600080fd5b50613f3b36828601613dfc565b60e08301525092915050565b601f8211156114c7576000816000526020600020601f850160051c81016020861015613f705750805b601f850160051c820191505b81811015610b8657828155600101613f7c565b67ffffffffffffffff831115613fa757613fa7613b4e565b613fbb83613fb58354613d4a565b83613f47565b6000601f84116001811461400d5760008515613fd75750838201355b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600387901b1c1916600186901b178355611f02565b6000838152602090207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0861690835b8281101561405c578685013582556020948501946001909201910161403c565b5086821015614097577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60f88860031b161c19848701351681555b505060018560011b0183555050505050565b6040815260006140bc604083018661377d565b82810360208401528381528385602083013760006020858301015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f860116820101915050949350505050565b60006020828403121561411f57600080fd5b8151610f5e81613c19565b600060a0823603121561413c57600080fd5b60405160a0810167ffffffffffffffff828210818311171561416057614160613b4e565b81604052843591508082111561417557600080fd5b5061418236828601613dfc565b82525061419160208401613721565b602082015260408301356141a4816137f3565b60408201526060838101359082015260808301356141c1816137f3565b608082015292915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec183360301811261422f57600080fd5b9190910192915050565b6000610140823603121561424c57600080fd5b614254613ba7565b61425d83613721565b815261426b60208401613c27565b6020820152604083013567ffffffffffffffff8082111561428b57600080fd5b61429736838701613dfc565b604084015260608501359150808211156142b057600080fd5b506142bd36828601613dfc565b6060830152506142d03660808501613c52565b60808201526142e23660e08501613c52565b60a082015292915050565b815167ffffffffffffffff81111561430757614307613b4e565b61431b816143158454613d4a565b84613f47565b602080601f83116001811461436e57600084156143385750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b178555610b86565b6000858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b828110156143bb5788860151825594840194600190910190840161439c565b50858210156143f757878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b5050505050600190811b01905550565b600061010067ffffffffffffffff8716835280602084015261442b8184018761377d565b8551151560408581019190915260208701516fffffffffffffffffffffffffffffffff90811660608701529087015116608085015291506144699050565b8251151560a083015260208301516fffffffffffffffffffffffffffffffff90811660c084015260408401511660e0830152613aa9565b60a0815260006144b360a083018761377d565b73ffffffffffffffffffffffffffffffffffffffff8616602084015284604084015267ffffffffffffffff841660608401528281036080840152600081526020810191505095945050505050565b73ffffffffffffffffffffffffffffffffffffffff8516815260a06020820152600061453060a083018661377d565b60408301949094525067ffffffffffffffff9190911660608201528082036080909101526000815260200192915050565b60006020828403121561457357600080fd5b815167ffffffffffffffff81111561458a57600080fd5b8201601f8101841361459b57600080fd5b80516145a9613e1b82613db6565b8181528560208385010111156145be57600080fd5b613aa9826020830160208601613759565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b818103818111156106cb576106cb6145cf565b67ffffffffffffffff8416815260e0810161465d60208301858051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b82511515608083015260208301516fffffffffffffffffffffffffffffffff90811660a084015260408401511660c0830152612dd6565b606081016106cb82848051151582526020808201516fffffffffffffffffffffffffffffffff9081169184019190915260409182015116910152565b6000602082840312156146e257600080fd5b8151610f5e816137f3565b80820281158282048414176106cb576106cb6145cf565b808201808211156106cb576106cb6145cf565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fd5b60008261477c577f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b500490565b6000825161422f81846020870161375956fe4c6f636b52656c65617365546f6b656e506f6f6c416e6450726f787920312e352e30a164736f6c6343000818000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab100000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000c311a21e6fef769344eb1515588b9d535662a1450000000000000000000000000000000000000000000000000000000000000000000000000000000000000000141fa059441e0ca23ce184b6a78bafd2a517dde80000000000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : token (address): 0x8FfC46A1b7a3b12F4A11Db8877d302876DCA7Ab1
Arg [1] : allowlist (address[]):
Arg [2] : rmnProxy (address): 0xC311a21e6fEf769344EB1515588B9d535662a145
Arg [3] : acceptLiquidity (bool): False
Arg [4] : router (address): 0x141fa059441E0ca23ce184B6A78bafD2A517DdE8
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000008ffc46a1b7a3b12f4a11db8877d302876dca7ab1
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000a0
Arg [2] : 000000000000000000000000c311a21e6fef769344eb1515588b9d535662a145
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [4] : 000000000000000000000000141fa059441e0ca23ce184b6a78bafd2a517dde8
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ 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.