Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Portal
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 10000 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
import "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./interfaces/IReceiveApproval.sol";
import "./interfaces/IReceiptToken.sol";
import "./interfaces/IERC20WithPermit.sol";
import "./interfaces/IERC20WithDecimals.sol";
contract Portal is Ownable2StepUpgradeable, IReceiveApproval {
using SafeERC20 for IERC20;
/// @notice Supported token ability defines what can be done with
/// a given token. Some tokens can be deposited but can not be locked.
enum TokenAbility {
None,
Deposit,
DepositAndLock
}
/// @notice Represents the to-tBTC migration state of a deposit. Note that
/// to-tBTC migration is only available for selected assets, as set
/// by the governance.
enum TbtcMigrationState {
NotRequested,
Requested,
InProgress,
Completed
}
/// @notice Supported token struct to pair a token address with its ability
/// @dev This is an auxiliary struct used to initialize the contract with
/// the supported tokens and for governance to add new supported tokens.
/// This struct is not used in the storage.
struct SupportedToken {
address token;
TokenAbility tokenAbility;
}
/// @notice Groups depositor address to their deposit ID to pass in an array
/// to trigger and complete the to-tBTC migration by the tBTC
/// migration treasury.
/// @dev This is an auxiliary struct used as a parameter in the
/// `withdrawForTbtcMigration` and `finalizeTbtcMigration` functions.
/// This struct is not used in the storage.
struct DepositToMigrate {
address depositor;
uint256 depositId;
}
/// @notice DepositInfo keeps track of the deposit balance and unlock time.
/// Each deposit is tracked separately and associated with a specific
/// token. Some tokens can be deposited but can not be locked - in
/// that case the unlockAt is the block timestamp of when the deposit
/// was created. The same is true for tokens that can be locked but
/// the depositor decided not to lock them. Some deposits can mint
/// a receipt tokens against them: receiptMinted is the amount of
/// receipt tokens minted against a deposit, while feeOwed is the
/// fee owed by the deposit to Portal, and the lastFeeIntegral is
/// the last updated value of the fee integral.
struct DepositInfo {
uint96 balance;
uint32 unlockAt;
// The amount of the receipt token minted for the deposit. Zero if no
// receipt token was minted.
uint96 receiptMinted;
// The fee owed if the receipt token was minted and the fee is non-zero.
// Fee owed is calculated based on the receipt tokens minted and is stored
// using receipt token decimals. The fee is collected when the deposit is
// withdrawn, adjusted to the deposit token decimals.
uint96 feeOwed;
// The last value of the receipt token fee integral. Zero if no receipt
// token was minted, if the fee is zero, or if the integral was not yet
// calculated.
uint88 lastFeeIntegral;
// The state of tBTC migration. If the to-tBTC migration is not allowed
// for the asset or if it was not requested by the depositor, the state
// is NotRequested.
TbtcMigrationState tbtcMigrationState;
}
/// @notice Keeps information about to-tBTC migration status for the asset:
/// whether the migration is allowed and what is the total amount
/// of the asset being currently in-progress for the migration.
struct TbtcMigrationInfo {
// Indicates whether the migration to tBTC was enabled by the governance
// for the asset.
bool isAllowed;
// The total amount that is currently migrated to tBTC. The value uses
// the same number of decimals as the asset being migrated.
uint96 totalMigrating;
}
/// @notice FeeInfo keeps track of the receipt token minting situation per
/// the supported deposit token.
///
/// The current fee rate for minting against deposits is
/// calculated separately for each token, and tracked as integral.
///
/// The fee rate has a single component: a governable base annual
/// fee, capped at 100%.
///
/// The annual fee is converted to a non-compounding fee per second,
/// R. This rate is then multiplied by time in seconds to get the
/// accumulated fee integral. The difference between the integral
/// in time T_1 and T_2 tracks how much fee is accrued per minted
/// token unit during that time. The minted amount A is multiplied
/// by R, and divided by a scalar constant to get the amount of fee
/// in the same units as A.
struct FeeInfo {
// XXX: not tracking total deposited - use token balance
// someone could send token to the contract outside locked deposits
// The amount of receipt tokens minted across all deposits for the given
// deposit token.
uint96 totalMinted;
// The timestamp of the block of the last fee update.
uint32 lastFeeUpdateAt;
// The integral tracks how much fee is accrued over time.
uint88 feeIntegral;
// Fee per annum in percentage.
uint8 annualFee;
// Receipt token minting cap in percentage, per deposit.
uint8 mintCap;
// A token supporting the IReceiptToken interface and used as a receipt
// token. Zero address if the given deposit token does not allow minting
// receipt tokens.
address receiptToken;
// Counts fees collected across all withdrawn deposits for the given
// deposit token. Fees are collected based on the receipt tokens earlier
// minted and the annual fee.
uint96 feeCollected;
}
/// @notice Mapping of the details of all the deposited funds:
/// depositor address -> token address -> deposit id -> DepositInfo
mapping(address => mapping(address => mapping(uint256 => DepositInfo)))
public deposits;
/// @notice The number of deposits created. Includes the deposits that
/// were fully withdrawn. This is also the identifier of the most
/// recently created deposit.
uint256 public depositCount;
/// @notice List of tokens enabled for deposit or for deposit and lock.
mapping(address => TokenAbility) public tokenAbility;
/// @notice Minimum time a deposit can be locked.
uint32 public minLockPeriod;
/// @notice Maximum time a deposit can be locked.
uint32 public maxLockPeriod;
/// @notice Address of the liquidity treasury multisig.
address public liquidityTreasury;
/// @notice Mapping to store whether an asset is managed by the liquidity
/// treasury multisig.
mapping(address => bool) public liquidityTreasuryManaged;
/// @notice Mapping of depositable tokens to their receipt token fee
/// parameters.
mapping(address => FeeInfo) public feeInfo;
/// @notice Address of the tBTC token. Used for the to-tBTC migration of
/// deposited assets for which the migration was enabled by the
/// governance.
address public tbtcToken;
/// @notice Address of the tBTC migration treasury multisig. The tBTC
/// migration treasury is responsible for conducting the
/// to-tBTC migration of deposited assets for which the migration
/// was enabled by the governance.
address public tbtcMigrationTreasury;
/// @notice Mapping indicating whether the given asset can be migrated to
/// tBTC by the tBTC migration treasury and if so, what is the
/// global status of migrations. The key to the mapping is the
/// address of the asset being migrated.
mapping(address => TbtcMigrationInfo) public tbtcMigrations;
event Deposited(
address indexed depositor,
address indexed token,
uint256 indexed depositId,
uint256 amount
);
event Withdrawn(
address indexed depositor,
address indexed token,
uint256 indexed depositId,
uint256 amount
);
event Locked(
address indexed depositor,
address indexed token,
uint256 indexed depositId,
uint32 unlockAt,
uint32 lockPeriod
);
event SupportedTokenAdded(address indexed token, TokenAbility tokenAbility);
event MaxLockPeriodUpdated(uint32 maxLockPeriod);
event MinLockPeriodUpdated(uint32 minLockPeriod);
/// @notice Event emitted when the liquidity treasury address is updated.
event LiquidityTreasuryUpdated(
address indexed previousLiquidityTreasury,
address indexed newLiquidityTreasury
);
/// @notice Event emitted when an asset is withdrawn by the liquidity
/// treasury multisig.
event WithdrawnByLiquidityTreasury(address indexed token, uint256 amount);
/// @notice Event emitted when an asset is set/unset as a liquidity
/// treasury multisig managed asset.
event LiquidityTreasuryManagedAssetUpdated(
address indexed asset,
bool isManaged
);
/// @notice Event emitted when the receipt parameters for a deposit token
/// are updated.
event ReceiptParamsUpdated(
address indexed token,
uint8 annualFee,
uint8 mintCap,
address receiptToken
);
/// @notice Event emitted when a receipt token is minted for a deposit.
event ReceiptMinted(
address indexed depositor,
address indexed token,
uint256 indexed depositId,
uint256 amount
);
/// @notice Event emitted when a receipt token is repaid for a deposit.
event ReceiptRepaid(
address indexed depositor,
address indexed token,
uint256 indexed depositId,
uint256 amount
);
/// @notice Event emitted when a fee is collected for a deposit.
event FeeCollected(
address indexed depositor,
address indexed token,
uint256 indexed depositId,
uint256 fee
);
/// @notice Event emitted when the tBTC token address used for the
/// to-tBTC migration was set by the governance.
/// @dev This event can be emitted only one time.
event TbtcTokenAddressSet(address tbtc);
/// @notice Event emitted when the to-tBTC migration treasury address was
/// updated.
event TbtcMigrationTreasuryUpdated(
address indexed previousMigrationTreasury,
address indexed newMigrationTreasury
);
/// @notice Event emitted when the eligibility for the to-tBTC migration
/// for the asset was updated.
event TbtcMigrationAllowedUpdated(address indexed token, bool isAllowed);
/// @notice Event emitted when the to-tBTC migration was requested for the
/// deposit.
event TbtcMigrationRequested(
address indexed depositor,
address indexed token,
uint256 indexed depositId
);
/// @notice Event emitted when the to-tBTC migration was started for the
/// deposit.
event TbtcMigrationStarted(
address indexed depositor,
address indexed token,
uint256 indexed depositId
);
/// @notice Event emitted when the tBTC migration treasury started to-tBTC
/// migration for a set of deposits withdrawing the given amount of
/// the token from the Portal contract.
event WithdrawnForTbtcMigration(address indexed token, uint256 amount);
/// @notice Event emitted when the to-tBTC migration was completed for the
/// deposit.
event TbtcMigrationCompleted(
address indexed depositor,
address indexed token,
uint256 indexed depositId
);
/// @notice Event emitted when the to-tBTC migration was completed for one
/// or multiple deposits and the Portal contract was funded with
/// tBTC coming from the migrated assets.
/// @dev The `amount` in the event is the amount in tBTC, using tBTC token's
/// precision.
event FundedFromTbtcMigration(uint256 amount);
/// @notice Event emitted in similar circumstances as `Withdrawn` except
/// that it represents a to-tBTC migrated deposit where the
/// withdrawal is performed in tBTC and not in the original deposit
/// token.
event WithdrawnTbtcMigrated(
address indexed depositor,
address indexed token,
address tbtcToken,
uint256 indexed depositId,
uint256 amountInTbtc
);
/// @notice Event emitted in similar circumstances as `FeeCollected`
/// except that it represents a to-tBTC migrated deposit where the
/// fee is collected in tBTC and not in the original deposit token.
event FeeCollectedTbtcMigrated(
address indexed depositor,
address indexed token,
address tbtcToken,
uint256 indexed depositId,
uint256 feeInTbtc
);
error IncorrectTokenAddress(address token);
error IncorrectTokenAbility(TokenAbility ability);
error IncorrectDepositor(address depositor);
error IncorrectAmount(uint256 amount);
error TokenNotSupported(address token);
error TokenAlreadySupported(address token, TokenAbility tokenAbility);
error InsufficientTokenAbility(address token, TokenAbility tokenAbility);
error LockPeriodOutOfRange(uint32 lockPeriod);
error IncorrectLockPeriod(uint256 lockPeriod);
error LockPeriodTooShort(
uint32 lockPeriod,
uint32 newUnlockAt,
uint32 existingUnlockAt
);
error DepositLocked(uint32 unlockAt);
error DepositNotFound();
/// @notice Error when the partial withdrawal amount is too high. Either it
/// equals the deposit amount (not a partial withdrawal) or it is
/// higher than the deposit amount.
error PartialWithdrawalAmountTooHigh(uint256 depositAmount);
/// @notice Error when the sender is not the liquidity treasury.
error SenderNotLiquidityTreasury(address sender);
/// @notice Error when the asset is not managed by the liquidity treasury.
error AssetNotManagedByLiquidityTreasury(address asset);
/// @notice Error when trying to withdraw without repaying the minted
/// receipt tokens back to Portal.
error ReceiptNotRepaid(uint256 receiptMinted);
/// @notice Error when the user tries to partially withdraw but the fee for
/// minting the receipt token is non-zero. In this case only a full
/// deposit withdrawal is possible.
error ReceiptFeeOwed(uint256 feeOwed);
/// @notice Error when the user tries to mint more than the mint limit
/// allows.
error ReceiptMintLimitExceeded(
uint256 mintLimit,
uint96 currentlyMinted,
uint256 feeOwed,
uint256 amount
);
/// @notice Error when the user tries to repay more than the minted debt.
error RepayAmountExceededDebt(uint96 mintedDebt, uint256 amount);
/// @notice Error when the annual fee proposed exceeds 100%.
error MaxAnnualFeeExceeded(uint8 annualFee);
/// @notice Error when the mint cap proposed exceeds 100%.
error MaxReceiptMintCapExceeded(uint8 mintCap);
/// @notice Error when there is no receipt token set for the asset.
error ReceiptMintingDisabled();
/// @notice Error when the receipt token is already initialized.
error ReceiptTokenAlreadyInitialized();
/// @notice Error when the receipt token decimals are not 18.
error IncorrectReceiptTokenDecimals(address receiptToken);
/// @notice Error when token being accepted as deposit token does not
/// support decimals() function or the function is malfunctioning.
error UnknownTokenDecimals(address token);
/// @notice The given asset can be marked as eligible for tBTC migration if
/// it is not managed by the liquidity treasury. The given asset
/// can be managed by the liquidity treasury if it is not marked as
/// eligible for tBTC migration. The transaction reverts with this
/// error when the governance changes would conflict with this
/// limitation.
error TbtcMigrationAndLiquidityManagementConflict();
/// @notice Error when the governance tries to add tBTC as an asset eligible
/// for to-tBTC migration.
error TbtcCanNotBeMigrated();
/// @notice Error when the tBTC token address used for to-tBTC migrations
/// was already set by the governance.
error TbtcTokenAddressAlreadySet();
/// @notice Error when the tBTC token address used for to-tBTC migrations
/// was not set by the governance but there is an attempt to enable
/// to-tBTC migration for some asset.
error TbtcTokenAddressNotSet();
/// @notice Error when someone else but the tBTC migration treasury tried
/// to withdraw tokens for tBTC migration or to complete the
/// migration process.
error SenderNotTbtcMigrationTreasury();
/// @notice Error when the given to-tBTC transition state change is
/// requested from an unexpected state. For example, it is not
/// allowed to complete the migration of a deposit for which the
/// migration was not requested.
error UnexpectedTbtcMigrationState(
uint256 depositId,
TbtcMigrationState currentState,
TbtcMigrationState expectedState
);
/// @notice Error when the to-tBTC migration for the asset was not allowed
/// by the governance but the depositor tried to request a migration.
error TbtcMigrationNotAllowed();
/// @notice Error when the to-tBTC migration has been requested but not yet
/// completed and the depositor tries to withdraw the deposit.
error TbtcMigrationNotCompleted();
/// @notice Error when the to-tBTC migration was requested and the depositor
/// tries to partially withdraw the deposit.
/// @dev The `Err` suffix is to distinguish this error from the
/// `TbtcMigrationRequested` event.
error TbtcMigrationRequestedErr();
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/// @notice Initialize the contract with the supported tokens and their
/// abilities.
/// @dev We are using OpenZeppelin's initializer pattern to initialize
/// the contract. This function is called only once during the contract
/// deployment.
/// @param supportedTokens List of supported tokens and their abilities
function initialize(
SupportedToken[] memory supportedTokens
) external initializer {
__Ownable_init(msg.sender);
minLockPeriod = 4 weeks;
maxLockPeriod = 39 weeks;
depositCount = 0;
for (uint256 i = 0; i < supportedTokens.length; i++) {
address token = supportedTokens[i].token;
TokenAbility ability = supportedTokens[i].tokenAbility;
if (token == address(0)) {
revert IncorrectTokenAddress(token);
}
tokenAbility[token] = ability;
}
}
/// @notice Add a new supported token and define its ability.
/// @dev Only the owner can add a new supported token. Supported token must
/// implement the decimals() method so Portal is able to perform
/// correct calculations.
/// @param supportedToken Supported token and its ability
function addSupportedToken(
SupportedToken calldata supportedToken
) external onlyOwner {
address token = supportedToken.token;
TokenAbility ability = supportedToken.tokenAbility;
if (token == address(0)) {
revert IncorrectTokenAddress(token);
}
if (ability == TokenAbility.None) {
revert IncorrectTokenAbility(ability);
}
if (tokenAbility[token] != TokenAbility.None) {
revert TokenAlreadySupported(token, tokenAbility[token]);
}
// Attempt to call decimals() on the token to verify it exists
// slither-disable-next-line unused-return
try IERC20WithDecimals(token).decimals() {} catch {
revert UnknownTokenDecimals(token);
}
tokenAbility[token] = ability;
emit SupportedTokenAdded(token, ability);
}
/// @notice Set the liquidity treasury multisig address.
/// @dev Only the owner can set the liquidity treasury multisig address. It
/// is possible to set the liquidity treasury multisig address to zero
/// in case the liquidity mining should be disabled.
/// @param _liquidityTreasury address of the liquidity treasury multisig
function setLiquidityTreasury(
address _liquidityTreasury
) external onlyOwner {
emit LiquidityTreasuryUpdated(liquidityTreasury, _liquidityTreasury);
// slither-disable-next-line missing-zero-check
liquidityTreasury = _liquidityTreasury;
}
/// @notice Set whether an asset is managed by the liquidity treasury multisig.
/// @dev Only the owner can set whether an asset is managed by the liquidity
/// treasury multisig. Only assets which can be locked in the Portal can
/// be managed by the liquidity treasury multisig.
/// @param asset address of the asset to be managed by the liquidity treasury
/// @param isManaged boolean value to set whether the asset is managed by the
/// liquidity treasury multisig
function setAssetAsLiquidityTreasuryManaged(
address asset,
bool isManaged
) external onlyOwner {
TokenAbility ability = tokenAbility[asset];
if (ability == TokenAbility.None) {
revert TokenNotSupported(asset);
}
if (ability != TokenAbility.DepositAndLock) {
revert InsufficientTokenAbility(asset, tokenAbility[asset]);
}
if (tbtcMigrations[asset].isAllowed) {
revert TbtcMigrationAndLiquidityManagementConflict();
}
liquidityTreasuryManaged[asset] = isManaged;
emit LiquidityTreasuryManagedAssetUpdated(asset, isManaged);
}
/// @notice Set the tBTC token address. Setting the tBTC token address
/// allows the governance to enable to-tBTC migrations of selected
/// deposited assets.
/// @dev Can only be executed one time. Can only be executed by the
/// governance.
/// @param _tbtcToken address of the tBTC token
function setTbtcTokenAddress(address _tbtcToken) external onlyOwner {
if (_tbtcToken == address(0)) {
revert IncorrectTokenAddress(_tbtcToken);
}
if (tbtcToken != address(0)) {
revert TbtcTokenAddressAlreadySet();
}
tbtcToken = _tbtcToken;
emit TbtcTokenAddressSet(_tbtcToken);
}
/// @notice Set the tBTC migration treasury multisig address.
/// @dev Only the owner can set the tBTC migraton treasury multisig address.
/// It is possible to set the migration treasury address to zero in
/// case the migration should be disabled.
/// @param _tbtcMigrationTreasury the new address of the tBTC migration
/// treasury multisig
function setTbtcMigrationTreasury(
address _tbtcMigrationTreasury
) external onlyOwner {
emit TbtcMigrationTreasuryUpdated(
tbtcMigrationTreasury,
_tbtcMigrationTreasury
);
// slither-disable-next-line missing-zero-check
tbtcMigrationTreasury = _tbtcMigrationTreasury;
}
/// @notice Set whether an asset can be migrated to tBTC by the migration
/// treasury multisig. Only assets that can be deposited in the
/// Portal can be marked as eligible for migration. Only assets that
/// represent Bitcoin and can be mapped 1:1 to tBTC can be marked
/// as eligible for migration.
/// @param asset address of the asset to upgrade the to-tBTC migration
/// settings for
/// @param isAllowed boolean value indicating whether to-tBTC migration
/// is allowed
function setAssetTbtcMigrationAllowed(
address asset,
bool isAllowed
) external onlyOwner {
if (tbtcToken == address(0)) {
revert TbtcTokenAddressNotSet();
}
if (asset == tbtcToken) {
revert TbtcCanNotBeMigrated();
}
TokenAbility ability = tokenAbility[asset];
if (ability == TokenAbility.None) {
revert TokenNotSupported(asset);
}
if (liquidityTreasuryManaged[asset]) {
revert TbtcMigrationAndLiquidityManagementConflict();
}
tbtcMigrations[asset].isAllowed = isAllowed;
emit TbtcMigrationAllowedUpdated(asset, isAllowed);
}
/// @notice Set the minimum lock period for deposits.
/// @dev Only the owner can update the minimum lock period. The new value
/// must be normalized to weeks, non-zero, and not higher than the
/// maximum lock period.
/// @param _minLockPeriod new minimum lock period
function setMinLockPeriod(uint32 _minLockPeriod) external onlyOwner {
uint32 normalizedLockPeriod = _normalizeLockPeriod(_minLockPeriod);
if (
_minLockPeriod != normalizedLockPeriod ||
normalizedLockPeriod == 0 ||
_minLockPeriod > maxLockPeriod
) {
revert IncorrectLockPeriod(_minLockPeriod);
}
minLockPeriod = _minLockPeriod;
emit MinLockPeriodUpdated(_minLockPeriod);
}
/// @notice Set the maximum lock period for deposits. Maximum lock
/// period is used as a limit to prevent users from accidentally
/// locking their deposits for too long.
/// @dev Only the owner can update the maximum lock period. The new value
/// must be normalized to weeks and not lower than the minimum lock
/// period.
/// @param _maxLockPeriod new maximum lock period
function setMaxLockPeriod(uint32 _maxLockPeriod) external onlyOwner {
if (
_maxLockPeriod != _normalizeLockPeriod(_maxLockPeriod) ||
_maxLockPeriod < minLockPeriod
) {
revert IncorrectLockPeriod(_maxLockPeriod);
}
maxLockPeriod = _maxLockPeriod;
emit MaxLockPeriodUpdated(_maxLockPeriod);
}
/// @notice Set the receipt parameters for a supported deposit token. The
/// receipt token address can be set only one time. All following
/// calls to this function for the same deposit token needs to pass
/// the same address of the receipt token as set initially.
/// @dev Only the owner can set the receipt parameters. Receipt token must
/// implement the decimals() method so Portal is able to perform correct
/// calculations for minting receipt tokens.
/// @param token deposit token address to set the parameters
/// @param annualFee annual fee in percentage for minting the receipt token
/// @param mintCap mint cap in percentage for minting the receipt token
/// @param receiptToken receipt token address
function setReceiptParams(
address token,
uint8 annualFee,
uint8 mintCap,
address receiptToken
) external onlyOwner {
if (tokenAbility[token] == TokenAbility.None) {
revert TokenNotSupported(token);
}
if (annualFee > 100) {
revert MaxAnnualFeeExceeded(annualFee);
}
if (mintCap > 100) {
revert MaxReceiptMintCapExceeded(mintCap);
}
if (receiptToken == address(0)) {
revert IncorrectTokenAddress(receiptToken);
}
FeeInfo storage i = feeInfo[token];
if (i.receiptToken != address(0) && i.receiptToken != receiptToken) {
revert ReceiptTokenAlreadyInitialized();
}
uint8 decimals = IERC20WithDecimals(receiptToken).decimals();
if (decimals != 18) {
revert IncorrectReceiptTokenDecimals(receiptToken);
}
_updateFeeIntegral(token);
i.annualFee = annualFee;
i.mintCap = mintCap;
i.receiptToken = receiptToken;
// solhint-disable-next-line not-rely-on-time
i.lastFeeUpdateAt = uint32(block.timestamp);
emit ReceiptParamsUpdated(token, annualFee, mintCap, receiptToken);
}
/// @notice Withdraws all deposited tokens.
///
/// Deposited lockable tokens can be withdrawn at any time if
/// there is no lock set on the deposit or the lock period has passed.
/// There is no way to withdraw locked deposit. Tokens that are not
/// lockable can be withdrawn at any time.
///
/// Deposits for which receipt tokens were minted and not fully
/// repaid can not be withdrawn even if the lock expired. Repaying
/// all receipt tokens is a must to withdraw the deposit. Upon
/// withdrawing a deposit for which the receipt tokens were minted,
/// the fee is collected based on the annual fee and the amount
/// of minted receipt tokens.
///
/// Deposits for which to-tBTC migration was requested but not yet
/// completed yet can not be withdrawn. The deposits for which the
/// to-tBTC migration was completed are withdrawn in tBTC.
///
/// This function withdraws all deposited tokens. For partial
/// withdrawals, use `withdrawPartially`.
/// @param token deposited token address
/// @param depositId id of the deposit
function withdraw(address token, uint256 depositId) external {
TokenAbility ability = tokenAbility[token];
if (ability == TokenAbility.None) {
revert TokenNotSupported(token);
}
DepositInfo storage selectedDeposit = deposits[msg.sender][token][
depositId
];
if (
selectedDeposit.tbtcMigrationState ==
TbtcMigrationState.Requested ||
selectedDeposit.tbtcMigrationState == TbtcMigrationState.InProgress
) {
revert TbtcMigrationNotCompleted();
}
if (
ability == TokenAbility.DepositAndLock &&
// solhint-disable-next-line not-rely-on-time
block.timestamp < selectedDeposit.unlockAt
) {
revert DepositLocked(selectedDeposit.unlockAt);
}
if (selectedDeposit.receiptMinted > 0) {
revert ReceiptNotRepaid(selectedDeposit.receiptMinted);
}
uint96 depositedAmount = selectedDeposit.balance;
if (depositedAmount == 0) {
revert DepositNotFound();
}
if (
selectedDeposit.tbtcMigrationState == TbtcMigrationState.Completed
) {
uint96 feeInTbtc = 0;
// The fee is collected based on the pre-migration rules, so based
// on the annual fee as set by the governance for the original token.
// The fee is collected in tBTC as the entire deposit was migrated
// to tBTC.
if (selectedDeposit.feeOwed > 0) {
FeeInfo storage tokenFeeInfo = feeInfo[token];
feeInTbtc = _adjustTokenDecimals(
tokenFeeInfo.receiptToken,
tbtcToken,
selectedDeposit.feeOwed
);
}
uint96 depositedAmountInTbtc = _adjustTokenDecimals(
token,
tbtcToken,
depositedAmount
);
uint96 withdrawableInTbtc = depositedAmountInTbtc - feeInTbtc;
emit WithdrawnTbtcMigrated(
msg.sender,
token,
tbtcToken,
depositId,
withdrawableInTbtc
);
emit FeeCollectedTbtcMigrated(
msg.sender,
token,
tbtcToken,
depositId,
feeInTbtc
);
feeInfo[tbtcToken].feeCollected += feeInTbtc;
delete deposits[msg.sender][token][depositId];
IERC20(tbtcToken).safeTransfer(msg.sender, withdrawableInTbtc);
} else {
uint96 fee = 0;
if (selectedDeposit.feeOwed > 0) {
FeeInfo storage tokenFeeInfo = feeInfo[token];
fee = _adjustTokenDecimals(
tokenFeeInfo.receiptToken,
token,
selectedDeposit.feeOwed
);
}
uint96 withdrawable = depositedAmount - fee;
emit Withdrawn(msg.sender, token, depositId, withdrawable);
emit FeeCollected(msg.sender, token, depositId, fee);
feeInfo[token].feeCollected += fee;
delete deposits[msg.sender][token][depositId];
IERC20(token).safeTransfer(msg.sender, withdrawable);
}
}
/// @notice Withdraws part of the deposited tokens.
///
/// Deposited lockable tokens can be withdrawn at any time if
/// there is no lock set on the deposit or the lock period has passed.
/// There is no way to withdraw locked deposit. Tokens that are not
/// lockable can be withdrawn at any time.
///
/// Deposits for which receipt tokens were minted and fully repaid
/// can not be partially withdrawn even if the lock expired.
/// Repaying all receipt tokens is a must to partially withdraw the
/// deposit. If the fee for receipt tokens minted is non-zero, the
/// deposit can not be partially withdrawn and only a full
/// withdrawal is possible.
///
/// Deposits for which the to-tBTC migration was requested can not
/// be withdrawn partially.
///
/// This function allows only for partial withdrawals. For full
/// withdrawals, use `withdraw`.
/// @param token deposited token address
/// @param depositId id of the deposit
/// @param amount the amount to be withdrawn
function withdrawPartially(
address token,
uint256 depositId,
uint96 amount
) external {
TokenAbility ability = tokenAbility[token];
if (ability == TokenAbility.None) {
revert TokenNotSupported(token);
}
if (amount == 0) {
revert IncorrectAmount(amount);
}
DepositInfo storage selectedDeposit = deposits[msg.sender][token][
depositId
];
if (selectedDeposit.balance == 0) {
revert DepositNotFound();
}
if (amount >= selectedDeposit.balance) {
revert PartialWithdrawalAmountTooHigh(selectedDeposit.balance);
}
if (
ability == TokenAbility.DepositAndLock &&
// solhint-disable-next-line not-rely-on-time
block.timestamp < selectedDeposit.unlockAt
) {
revert DepositLocked(selectedDeposit.unlockAt);
}
if (selectedDeposit.receiptMinted > 0) {
revert ReceiptNotRepaid(selectedDeposit.receiptMinted);
}
if (selectedDeposit.feeOwed > 0) {
revert ReceiptFeeOwed(selectedDeposit.feeOwed);
}
if (
selectedDeposit.tbtcMigrationState !=
TbtcMigrationState.NotRequested
) {
revert TbtcMigrationRequestedErr();
}
emit Withdrawn(msg.sender, token, depositId, amount);
selectedDeposit.balance -= amount;
IERC20(token).safeTransfer(msg.sender, amount);
}
/// @notice Receive approval from a token contract and deposit the tokens in
/// one transaction. If the the token is lockable and lock period is
/// greater than 0, the deposit will be locked for the given period.
/// @dev This function is called by the token following the `approveAndCall`
/// pattern.
// Encoded lock period is counted in seconds, will be normalized to
/// weeks. If non-zero, it must not be shorter than the minimum lock
/// period and must not be longer than the maximum lock period. To not
/// lock the deposit, the lock period must be 0.
/// @param from address which approved and sent the tokens
/// @param amount amount of tokens sent
/// @param token address of the token contract
/// @param data encoded lock period
function receiveApproval(
address from,
uint256 amount,
address token,
bytes calldata data
) external override {
if (token != msg.sender) {
revert IncorrectTokenAddress(token);
}
if (amount > type(uint96).max) {
revert IncorrectAmount(amount);
}
uint32 lockPeriod = abi.decode(data, (uint32));
_depositFor(from, from, token, uint96(amount), lockPeriod);
}
/// @notice Deposit and optionally lock tokens for the given period.
/// @dev Lock period will be normalized to weeks. If non-zero, it must not
/// be shorter than the minimum lock period and must not be longer than
/// the maximum lock period.
/// @param token token address to deposit
/// @param amount amount of tokens to deposit
/// @param lockPeriod lock period in seconds, 0 to not lock the deposit
function deposit(address token, uint96 amount, uint32 lockPeriod) external {
_depositFor(msg.sender, msg.sender, token, amount, lockPeriod);
}
/// @notice Deposit and optionally lock tokens to the contract on behalf of
/// someone else.
/// @dev Lock period will be normalized to weeks. If non-zero, it must not
/// be shorter than the minimum lock period and must not be longer than
/// the maximum lock period.
/// @param depositOwner address that will be able to withdraw the deposit
/// @param token token address to deposit
/// @param amount amount of tokens to deposit
/// @param lockPeriod lock period in seconds, 0 to not lock the deposit
function depositFor(
address depositOwner,
address token,
uint96 amount,
uint32 lockPeriod
) external {
_depositFor(depositOwner, msg.sender, token, amount, lockPeriod);
}
/// @notice Lock existing deposit for a given period. This function can be
/// used to lock a deposit that was not locked when it was created or
/// to extend the lock period of an already locked deposit.
/// @param token address of the deposited token
/// @param depositId id of the deposit
/// @param lockPeriod new lock period in seconds
function lock(
address token,
uint256 depositId,
uint32 lockPeriod
) external {
DepositInfo storage depositInfo = deposits[msg.sender][token][
depositId
];
if (depositInfo.balance == 0) {
revert DepositNotFound();
}
TokenAbility ability = tokenAbility[token];
if (ability != TokenAbility.DepositAndLock) {
revert InsufficientTokenAbility(token, ability);
}
uint32 existingUnlockAt = depositInfo.unlockAt;
uint32 newUnlockAt = _calculateUnlockTime(
msg.sender,
token,
depositId,
lockPeriod
);
if (newUnlockAt <= existingUnlockAt) {
revert LockPeriodTooShort(
lockPeriod,
newUnlockAt,
existingUnlockAt
);
}
depositInfo.unlockAt = newUnlockAt;
}
/// @notice External withdraw function to enable the liquidity treasury to
/// withdraw tokens from the contract. Only the liquidity treasury
/// can withdraw tokens from the contract using this function.
/// @param token token address to withdraw
/// @param amount amount of respective token to withdraw
function withdrawAsLiquidityTreasury(
address token,
uint256 amount
) external {
if (msg.sender != liquidityTreasury) {
revert SenderNotLiquidityTreasury(msg.sender);
}
if (amount == 0) {
revert IncorrectAmount(amount);
}
if (!liquidityTreasuryManaged[token]) {
revert AssetNotManagedByLiquidityTreasury(token);
}
emit WithdrawnByLiquidityTreasury(token, amount);
IERC20(token).safeTransfer(msg.sender, amount);
}
/// @notice Mint a deposit receipt token against an existing deposit.
/// @param token the token address related with the deposit
/// @param depositId the ID of the deposit
/// @param amount amount of the receipt token to mint
function mintReceipt(
address token,
uint256 depositId,
uint256 amount
) external {
FeeInfo storage fee = feeInfo[token];
if (fee.receiptToken == address(0)) {
revert ReceiptMintingDisabled();
}
if (amount == 0) {
revert IncorrectAmount(amount);
}
DepositInfo storage depositInfo = deposits[msg.sender][token][
depositId
];
if (depositInfo.balance == 0) {
revert DepositNotFound();
}
_updateFee(depositInfo, token);
// Normalize deposit balance to match receipt token decimals
uint96 normalizedBalance = _adjustTokenDecimals(
token,
fee.receiptToken,
depositInfo.balance
);
uint96 mintLimit = (normalizedBalance * fee.mintCap) / 100;
if (
amount + depositInfo.receiptMinted + depositInfo.feeOwed > mintLimit
) {
revert ReceiptMintLimitExceeded(
mintLimit,
depositInfo.receiptMinted,
depositInfo.feeOwed,
amount
);
}
depositInfo.receiptMinted += uint96(amount);
fee.totalMinted += uint96(amount);
emit ReceiptMinted(msg.sender, token, depositId, amount);
IReceiptToken(fee.receiptToken).mintReceipt(msg.sender, amount);
}
/// @notice Repay the deposit receipt token of a particular deposit.
/// @param token the token address related with the deposit
/// @param depositId the ID of the deposit
/// @param amount how much of the token to repay, up to outstanding balance
function repayReceipt(
address token,
uint256 depositId,
uint256 amount
) public {
FeeInfo storage fee = feeInfo[token];
if (fee.receiptToken == address(0)) {
revert ReceiptMintingDisabled();
}
if (amount == 0) {
revert IncorrectAmount(amount);
}
DepositInfo storage depositInfo = deposits[msg.sender][token][
depositId
];
if (depositInfo.balance == 0) {
revert DepositNotFound();
}
if (depositInfo.receiptMinted < amount) {
revert RepayAmountExceededDebt(depositInfo.receiptMinted, amount);
}
_updateFee(depositInfo, token);
depositInfo.receiptMinted -= uint96(amount);
fee.totalMinted -= uint96(amount);
emit ReceiptRepaid(msg.sender, token, depositId, amount);
// transfer the repaid receipt tokens and burn them
IERC20(fee.receiptToken).safeTransferFrom(
msg.sender,
address(this),
amount
);
IReceiptToken(fee.receiptToken).burnReceipt(amount);
}
/// @notice Request the to-tBTC migration for the given deposit. The
/// migration happens asynchronously and is managed by the tBTC
/// migration treasury. The contract gives no guarantees when the
/// migration will be completed.
/// @param token deposited token address
/// @param depositId id of the deposit
function requestTbtcMigration(address token, uint256 depositId) external {
DepositInfo storage depositInfo = deposits[msg.sender][token][
depositId
];
TbtcMigrationInfo storage migrationInfo = tbtcMigrations[token];
if (!migrationInfo.isAllowed) {
revert TbtcMigrationNotAllowed();
}
if (depositInfo.tbtcMigrationState != TbtcMigrationState.NotRequested) {
revert UnexpectedTbtcMigrationState(
depositId,
depositInfo.tbtcMigrationState,
TbtcMigrationState.NotRequested
);
}
depositInfo.tbtcMigrationState = TbtcMigrationState.Requested;
emit TbtcMigrationRequested(msg.sender, token, depositId);
}
/// @notice Used by the tBTC migration treasury to withdraw tokens from the
/// deposits marked by their owners as requested for to-tBTC
/// migration. The caller specifies which deposits are to begin the
/// migration. Calling this function for the deposit marks it
/// as to-tBTC migration started but gives no promise as when the
/// to-tBTC migration will be completed. For all deposits for which
/// the to-tBTC migration was started, the tBTC migration multisig
/// must ensure to call the `finalizeTbtcMigration` function once
/// the migration is completed.
/// @param token deposited token address
/// @param depositsToMigrate an array of deposits for which the to-tBTC
/// migration was requested and from which the assets should be
/// withdrawn for the to-tBTC migration
function withdrawForTbtcMigration(
address token,
DepositToMigrate[] memory depositsToMigrate
) external {
if (msg.sender != tbtcMigrationTreasury) {
revert SenderNotTbtcMigrationTreasury();
}
uint96 totalToMigrate = 0;
for (uint256 i = 0; i < depositsToMigrate.length; i++) {
address depositOwner = depositsToMigrate[i].depositor;
uint256 depositId = depositsToMigrate[i].depositId;
DepositInfo storage depositInfo = deposits[depositOwner][token][
depositId
];
if (depositInfo.balance == 0) {
revert DepositNotFound();
}
if (
depositInfo.tbtcMigrationState != TbtcMigrationState.Requested
) {
revert UnexpectedTbtcMigrationState(
depositId,
depositInfo.tbtcMigrationState,
TbtcMigrationState.Requested
);
}
depositInfo.tbtcMigrationState = TbtcMigrationState.InProgress;
totalToMigrate += depositInfo.balance;
emit TbtcMigrationStarted(depositOwner, token, depositId);
}
TbtcMigrationInfo storage migrationInfo = tbtcMigrations[token];
migrationInfo.totalMigrating += totalToMigrate;
emit WithdrawnForTbtcMigration(token, totalToMigrate);
IERC20(token).safeTransfer(tbtcMigrationTreasury, totalToMigrate);
}
/// @notice Used by the tBTC migration treasury to complete the to-tBTC
/// migration for the selected set of deposits. For all of those
/// deposits the migration should be started by calling
/// `withdrawForTbtcMigration` function before. The treasury has to
/// approve the amount of tBTC to the Portal contract equal to the
/// total migrated amount of the migrated deposits, adjusted to tBTC
/// token decimals.
/// @param token pre-migration deposited token address
/// @param migratedDeposits an array of deposits for which the to-tBTC
/// migration was completed
function completeTbtcMigration(
address token,
DepositToMigrate[] memory migratedDeposits
) external {
if (msg.sender != tbtcMigrationTreasury) {
revert SenderNotTbtcMigrationTreasury();
}
uint96 totalMigrated = 0;
for (uint256 i = 0; i < migratedDeposits.length; i++) {
address depositOwner = migratedDeposits[i].depositor;
uint256 depositId = migratedDeposits[i].depositId;
DepositInfo storage depositInfo = deposits[depositOwner][token][
depositId
];
if (
depositInfo.tbtcMigrationState != TbtcMigrationState.InProgress
) {
revert UnexpectedTbtcMigrationState(
depositId,
depositInfo.tbtcMigrationState,
TbtcMigrationState.InProgress
);
}
totalMigrated += depositInfo.balance;
depositInfo.tbtcMigrationState = TbtcMigrationState.Completed;
emit TbtcMigrationCompleted(depositOwner, token, depositId);
}
TbtcMigrationInfo storage migrationInfo = tbtcMigrations[token];
migrationInfo.totalMigrating -= totalMigrated;
uint96 totalMigratedInTbtc = _adjustTokenDecimals(
token,
tbtcToken,
totalMigrated
);
emit FundedFromTbtcMigration(totalMigratedInTbtc);
IERC20(tbtcToken).safeTransferFrom(
msg.sender,
address(this),
totalMigratedInTbtc
);
}
/// @notice Get the balance and unlock time of a given deposit. Note that
/// the deposit could be migrated to tBTC so even though the `token`
/// key under which it is available could still point to the
/// pre-migration version, the witdrawal - when requested - will
/// happen in tBTC.
/// @param depositor depositor address
/// @param token token address to get the balance
/// @param depositId id of the deposit
function getDeposit(
address depositor,
address token,
uint256 depositId
) external view returns (DepositInfo memory) {
return deposits[depositor][token][depositId];
}
/// @notice Internal deposit function to deposit tokens on behalf of a given
/// address - sender or someone else. If the lock period passed as
/// a parameter is non-zero, the function will lock the deposit.
/// @param depositOwner address that will be able to withdraw the deposit
/// @param token token address to deposit
/// @param amount amount of tokens to deposit
/// @param lockPeriod lock period in seconds, 0 to not lock the deposit
function _depositFor(
address depositOwner,
address depositFunder,
address token,
uint96 amount,
uint32 lockPeriod
) internal {
TokenAbility ability = tokenAbility[token];
if (ability == TokenAbility.None) {
revert TokenNotSupported(token);
}
if (lockPeriod > 0 && ability == TokenAbility.Deposit) {
revert InsufficientTokenAbility(token, ability);
}
if (depositOwner == address(0)) {
revert IncorrectDepositor(depositOwner);
}
if (amount == 0) {
revert IncorrectAmount(amount);
}
depositCount++;
uint256 depositId = depositCount;
emit Deposited(depositOwner, token, depositId, amount);
deposits[depositOwner][token][depositId] = DepositInfo({
balance: amount,
// solhint-disable-next-line not-rely-on-time
unlockAt: _calculateUnlockTime(
depositOwner,
token,
depositId,
lockPeriod
),
receiptMinted: 0,
feeOwed: 0,
lastFeeIntegral: 0,
tbtcMigrationState: TbtcMigrationState.NotRequested
});
IERC20(token).safeTransferFrom(depositFunder, address(this), amount);
}
/// @notice Calculates the unlock time normalizing the lock time value and
/// making sure it is in the allowed range. Returns the timestamp at
/// which the deposit can be unlocked.
/// @dev This function DOES NOT check if the deposit exists and if the
/// existing deposit already has a lock time. Please validate it before
/// calling this function.
/// @param depositor depositor address
/// @param token token of the deposit that will be locked
/// @param depositId id of the deposit that will be locked
/// @param lockPeriod lock period in seconds
function _calculateUnlockTime(
address depositor,
address token,
uint depositId,
uint32 lockPeriod
) internal returns (uint32) {
// Short-circuit if there is no intention to lock. There is no need to
// check the minimum/maximum lock time or normalize the lock period.
if (lockPeriod == 0) {
// solhint-disable-next-line not-rely-on-time
return uint32(block.timestamp);
}
uint32 normalizedLockPeriod = _normalizeLockPeriod(lockPeriod);
if (
normalizedLockPeriod == 0 ||
normalizedLockPeriod < minLockPeriod ||
normalizedLockPeriod > maxLockPeriod
) {
revert LockPeriodOutOfRange(lockPeriod);
}
// solhint-disable-next-line not-rely-on-time
uint32 unlockAt = uint32(block.timestamp) + normalizedLockPeriod;
// It is gas-cheaper to pass the required parameters and emit the event
// here instead of checking the result and emit the event in the calling
// function. Keep in mind internal functions are inlined.
emit Locked(
depositor,
token,
depositId,
unlockAt,
normalizedLockPeriod
);
return unlockAt;
}
/// @notice Internal function to update the fee information of a given
/// deposit.
/// @param depositInfo Storage reference to the deposit info to be updated.
/// @param token Address of the token related to the deposit.
function _updateFee(
DepositInfo storage depositInfo,
address token
) internal {
_updateFeeIntegral(token);
FeeInfo memory fee = feeInfo[token];
depositInfo.feeOwed += _calculateFeeAccrued(
fee.feeIntegral - depositInfo.lastFeeIntegral,
depositInfo.receiptMinted
);
depositInfo.lastFeeIntegral = fee.feeIntegral;
}
/// @notice Internal function to update the fee integral for a given token.
/// @param token Address of the token to update the fee integral.
function _updateFeeIntegral(address token) internal {
FeeInfo storage i = feeInfo[token];
uint96 feePerSecond = _calculateFeeRate(token);
// solhint-disable-next-line not-rely-on-time
uint32 timeInterval = uint32(block.timestamp) - i.lastFeeUpdateAt;
uint96 accumulated = timeInterval * feePerSecond;
i.feeIntegral += uint88(accumulated);
// solhint-disable-next-line not-rely-on-time
i.lastFeeUpdateAt = uint32(block.timestamp);
}
/// @notice Internal function to calculate the fee rate per second for a
/// given token.
/// @param token Address of the token.
function _calculateFeeRate(address token) internal view returns (uint96) {
FeeInfo memory i = feeInfo[token];
// The i.annualFee is expressed in % so we divide by 100 to get the
// fee rate in token units: 10^18 / 10^2 = 10^16
return (uint96(i.annualFee) * (10 ** 16)) / (365 days);
}
/// @notice Calculates the fee accrued based on the given integral
/// difference and minted amount.
/// @dev Multiplies the integral difference by the minted amount and scales
/// it down to fit the desired precision.
/// @param integralDiff The difference in integral values.
/// @param mintedAmount The amount of tokens minted.
function _calculateFeeAccrued(
uint88 integralDiff,
uint96 mintedAmount
) internal pure returns (uint96) {
return
uint96((uint256(integralDiff) * uint256(mintedAmount)) / 10 ** 18);
}
/// @notice Normalizes the lock period to weeks. Will round down if not
/// normalized.
/// @param lockPeriod the lock period to be normalized
function _normalizeLockPeriod(
uint32 lockPeriod
) internal pure returns (uint32) {
// slither-disable-next-line divide-before-multiply
return (lockPeriod / 1 weeks) * 1 weeks;
}
/// @notice Adjusts the decimals amount to desired precision for the two
/// token addresses and amount expressed in the precision used by
/// one of them.
/// @param sourceToken the token address of the source amount
/// @param targetToken the token address of the target amount
/// @param amount the source amount to be adjusted
function _adjustTokenDecimals(
address sourceToken,
address targetToken,
uint96 amount
) internal view returns (uint96) {
uint8 sourceDecimals = IERC20WithDecimals(sourceToken).decimals();
uint8 targetDecimals = IERC20WithDecimals(targetToken).decimals();
if (sourceDecimals < targetDecimals) {
return uint96(amount * (10 ** (targetDecimals - sourceDecimals)));
} else if (sourceDecimals > targetDecimals) {
return uint96(amount / (10 ** (sourceDecimals - targetDecimals)));
}
return amount;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {OwnableUpgradeable} from "./OwnableUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable2Step
struct Ownable2StepStorage {
address _pendingOwner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable2Step")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant Ownable2StepStorageLocation = 0x237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c00;
function _getOwnable2StepStorage() private pure returns (Ownable2StepStorage storage $) {
assembly {
$.slot := Ownable2StepStorageLocation
}
}
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
function __Ownable2Step_init() internal onlyInitializing {
}
function __Ownable2Step_init_unchained() internal onlyInitializing {
}
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
return $._pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
$._pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
Ownable2StepStorage storage $ = _getOwnable2StepStorage();
delete $._pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @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 value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of 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 value) 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 a `value` amount of tokens 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 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` 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 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @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);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @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 or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* 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.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @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`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) 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 FailedInnerCall();
}
}
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
/// @notice An interface to be optionally implemented by ERC20 tokens to retrieve
/// the decimals places of the token. We need this interface because to
/// properly calculate amounts when 2 tokens are involved we need to know
/// their decimals places.
/// As decimals() is optional in the ERC20 standard, additional interface
/// is needed to retrieve the decimals places of the token.
/// Portal contract assumes that only tokens supporting this interface
/// are accepted as supported tokens or receipt tokens.
interface IERC20WithDecimals {
/// @notice Returns the decimals places of the token
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
/// @notice An interface to be optionally implemented by ERC20 tokens to enable
/// gasless approvals.
interface IERC20WithPermit {
/// @notice EIP2612 approval made with secp256k1 signature.
/// Users can authorize a transfer of their tokens with a signature
/// conforming EIP712 standard, rather than an on-chain transaction
/// from their address. Anyone can submit this signature on the
/// user's behalf by calling the permit function, paying gas fees,
/// and possibly performing other actions in the same transaction.
function permit(
address owner,
address spender,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
/// @notice IReceiptToken is an interface that should be implemented by ERC20
/// receipt tokens used in the Portal contract.
interface IReceiptToken {
/// @notice Mints `amount` receipt tokens and transfers them to the `to`
/// address.
function mintReceipt(address to, uint256 amount) external;
/// @notice Burns `amount` of receipt tokens owned by `msg.sender`.
function burnReceipt(uint256 amount) external;
}// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.24;
/// @notice An interface that should be implemented by contracts supporting
/// `approveAndCall`/`receiveApproval` pattern.
interface IReceiveApproval {
/// @notice Receives approval to spend tokens. Called as a result of
/// `approveAndCall` call on the token.
function receiveApproval(
address from,
uint256 amount,
address token,
bytes calldata extraData
) external;
}{
"optimizer": {
"enabled": true,
"runs": 10000
},
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"AssetNotManagedByLiquidityTreasury","type":"error"},{"inputs":[{"internalType":"uint32","name":"unlockAt","type":"uint32"}],"name":"DepositLocked","type":"error"},{"inputs":[],"name":"DepositNotFound","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"IncorrectAmount","type":"error"},{"inputs":[{"internalType":"address","name":"depositor","type":"address"}],"name":"IncorrectDepositor","type":"error"},{"inputs":[{"internalType":"uint256","name":"lockPeriod","type":"uint256"}],"name":"IncorrectLockPeriod","type":"error"},{"inputs":[{"internalType":"address","name":"receiptToken","type":"address"}],"name":"IncorrectReceiptTokenDecimals","type":"error"},{"inputs":[{"internalType":"enum Portal.TokenAbility","name":"ability","type":"uint8"}],"name":"IncorrectTokenAbility","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"IncorrectTokenAddress","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum Portal.TokenAbility","name":"tokenAbility","type":"uint8"}],"name":"InsufficientTokenAbility","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[{"internalType":"uint32","name":"lockPeriod","type":"uint32"}],"name":"LockPeriodOutOfRange","type":"error"},{"inputs":[{"internalType":"uint32","name":"lockPeriod","type":"uint32"},{"internalType":"uint32","name":"newUnlockAt","type":"uint32"},{"internalType":"uint32","name":"existingUnlockAt","type":"uint32"}],"name":"LockPeriodTooShort","type":"error"},{"inputs":[{"internalType":"uint8","name":"annualFee","type":"uint8"}],"name":"MaxAnnualFeeExceeded","type":"error"},{"inputs":[{"internalType":"uint8","name":"mintCap","type":"uint8"}],"name":"MaxReceiptMintCapExceeded","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint256","name":"depositAmount","type":"uint256"}],"name":"PartialWithdrawalAmountTooHigh","type":"error"},{"inputs":[{"internalType":"uint256","name":"feeOwed","type":"uint256"}],"name":"ReceiptFeeOwed","type":"error"},{"inputs":[{"internalType":"uint256","name":"mintLimit","type":"uint256"},{"internalType":"uint96","name":"currentlyMinted","type":"uint96"},{"internalType":"uint256","name":"feeOwed","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReceiptMintLimitExceeded","type":"error"},{"inputs":[],"name":"ReceiptMintingDisabled","type":"error"},{"inputs":[{"internalType":"uint256","name":"receiptMinted","type":"uint256"}],"name":"ReceiptNotRepaid","type":"error"},{"inputs":[],"name":"ReceiptTokenAlreadyInitialized","type":"error"},{"inputs":[{"internalType":"uint96","name":"mintedDebt","type":"uint96"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RepayAmountExceededDebt","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"SenderNotLiquidityTreasury","type":"error"},{"inputs":[],"name":"SenderNotTbtcMigrationTreasury","type":"error"},{"inputs":[],"name":"TbtcCanNotBeMigrated","type":"error"},{"inputs":[],"name":"TbtcMigrationAndLiquidityManagementConflict","type":"error"},{"inputs":[],"name":"TbtcMigrationNotAllowed","type":"error"},{"inputs":[],"name":"TbtcMigrationNotCompleted","type":"error"},{"inputs":[],"name":"TbtcMigrationRequestedErr","type":"error"},{"inputs":[],"name":"TbtcTokenAddressAlreadySet","type":"error"},{"inputs":[],"name":"TbtcTokenAddressNotSet","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum Portal.TokenAbility","name":"tokenAbility","type":"uint8"}],"name":"TokenAlreadySupported","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"TokenNotSupported","type":"error"},{"inputs":[{"internalType":"uint256","name":"depositId","type":"uint256"},{"internalType":"enum Portal.TbtcMigrationState","name":"currentState","type":"uint8"},{"internalType":"enum Portal.TbtcMigrationState","name":"expectedState","type":"uint8"}],"name":"UnexpectedTbtcMigrationState","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"UnknownTokenDecimals","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Deposited","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FeeCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"tbtcToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeInTbtc","type":"uint256"}],"name":"FeeCollectedTbtcMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FundedFromTbtcMigration","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"bool","name":"isManaged","type":"bool"}],"name":"LiquidityTreasuryManagedAssetUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousLiquidityTreasury","type":"address"},{"indexed":true,"internalType":"address","name":"newLiquidityTreasury","type":"address"}],"name":"LiquidityTreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"unlockAt","type":"uint32"},{"indexed":false,"internalType":"uint32","name":"lockPeriod","type":"uint32"}],"name":"Locked","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"maxLockPeriod","type":"uint32"}],"name":"MaxLockPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"minLockPeriod","type":"uint32"}],"name":"MinLockPeriodUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReceiptMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint8","name":"annualFee","type":"uint8"},{"indexed":false,"internalType":"uint8","name":"mintCap","type":"uint8"},{"indexed":false,"internalType":"address","name":"receiptToken","type":"address"}],"name":"ReceiptParamsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReceiptRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"enum Portal.TokenAbility","name":"tokenAbility","type":"uint8"}],"name":"SupportedTokenAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"bool","name":"isAllowed","type":"bool"}],"name":"TbtcMigrationAllowedUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"TbtcMigrationCompleted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"TbtcMigrationRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"TbtcMigrationStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousMigrationTreasury","type":"address"},{"indexed":true,"internalType":"address","name":"newMigrationTreasury","type":"address"}],"name":"TbtcMigrationTreasuryUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"tbtc","type":"address"}],"name":"TbtcTokenAddressSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Withdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawnByLiquidityTreasury","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"WithdrawnForTbtcMigration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"tbtcToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"depositId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountInTbtc","type":"uint256"}],"name":"WithdrawnTbtcMigrated","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum Portal.TokenAbility","name":"tokenAbility","type":"uint8"}],"internalType":"struct Portal.SupportedToken","name":"supportedToken","type":"tuple"}],"name":"addSupportedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"address","name":"depositor","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"internalType":"struct Portal.DepositToMigrate[]","name":"migratedDeposits","type":"tuple[]"}],"name":"completeTbtcMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint32","name":"lockPeriod","type":"uint32"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"depositOwner","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint96","name":"amount","type":"uint96"},{"internalType":"uint32","name":"lockPeriod","type":"uint32"}],"name":"depositFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"deposits","outputs":[{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint32","name":"unlockAt","type":"uint32"},{"internalType":"uint96","name":"receiptMinted","type":"uint96"},{"internalType":"uint96","name":"feeOwed","type":"uint96"},{"internalType":"uint88","name":"lastFeeIntegral","type":"uint88"},{"internalType":"enum Portal.TbtcMigrationState","name":"tbtcMigrationState","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"feeInfo","outputs":[{"internalType":"uint96","name":"totalMinted","type":"uint96"},{"internalType":"uint32","name":"lastFeeUpdateAt","type":"uint32"},{"internalType":"uint88","name":"feeIntegral","type":"uint88"},{"internalType":"uint8","name":"annualFee","type":"uint8"},{"internalType":"uint8","name":"mintCap","type":"uint8"},{"internalType":"address","name":"receiptToken","type":"address"},{"internalType":"uint96","name":"feeCollected","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"depositor","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"getDeposit","outputs":[{"components":[{"internalType":"uint96","name":"balance","type":"uint96"},{"internalType":"uint32","name":"unlockAt","type":"uint32"},{"internalType":"uint96","name":"receiptMinted","type":"uint96"},{"internalType":"uint96","name":"feeOwed","type":"uint96"},{"internalType":"uint88","name":"lastFeeIntegral","type":"uint88"},{"internalType":"enum Portal.TbtcMigrationState","name":"tbtcMigrationState","type":"uint8"}],"internalType":"struct Portal.DepositInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"enum Portal.TokenAbility","name":"tokenAbility","type":"uint8"}],"internalType":"struct Portal.SupportedToken[]","name":"supportedTokens","type":"tuple[]"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidityTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"liquidityTreasuryManaged","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"},{"internalType":"uint32","name":"lockPeriod","type":"uint32"}],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maxLockPeriod","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minLockPeriod","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mintReceipt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"receiveApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"repayReceipt","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"requestTbtcMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"bool","name":"isManaged","type":"bool"}],"name":"setAssetAsLiquidityTreasuryManaged","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"},{"internalType":"bool","name":"isAllowed","type":"bool"}],"name":"setAssetTbtcMigrationAllowed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidityTreasury","type":"address"}],"name":"setLiquidityTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_maxLockPeriod","type":"uint32"}],"name":"setMaxLockPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_minLockPeriod","type":"uint32"}],"name":"setMinLockPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint8","name":"annualFee","type":"uint8"},{"internalType":"uint8","name":"mintCap","type":"uint8"},{"internalType":"address","name":"receiptToken","type":"address"}],"name":"setReceiptParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tbtcMigrationTreasury","type":"address"}],"name":"setTbtcMigrationTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tbtcToken","type":"address"}],"name":"setTbtcTokenAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tbtcMigrationTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tbtcMigrations","outputs":[{"internalType":"bool","name":"isAllowed","type":"bool"},{"internalType":"uint96","name":"totalMigrating","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tbtcToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"tokenAbility","outputs":[{"internalType":"enum Portal.TokenAbility","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdrawAsLiquidityTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"components":[{"internalType":"address","name":"depositor","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"}],"internalType":"struct Portal.DepositToMigrate[]","name":"depositsToMigrate","type":"tuple[]"}],"name":"withdrawForTbtcMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"depositId","type":"uint256"},{"internalType":"uint96","name":"amount","type":"uint96"}],"name":"withdrawPartially","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60806040523480156200001157600080fd5b506200001c62000022565b620000d6565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000735760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d35780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6153fc80620000e66000396000f3fe608060405234801561001057600080fd5b50600436106102ad5760003560e01c806373ae54b51161017b578063c74b552e116100d8578063f3fef3a31161008c578063f64a6c9011610071578063f64a6c90146107e6578063fa4e00c0146107f9578063fe10b9fb1461080c57600080fd5b8063f3fef3a3146107c0578063f5e8d327146107d357600080fd5b8063e30c3978116100bd578063e30c397814610792578063e5d3d7141461079a578063f2fde38b146107ad57600080fd5b8063c74b552e1461076c578063dfb6c2d21461077f57600080fd5b806392673f551161012f578063b918e5f911610114578063b918e5f9146105f5578063bc06b96514610614578063bfcfa66b1461062757600080fd5b806392673f55146105cf578063a2b7e2dd146105e257600080fd5b80637b498fc1116101605780637b498fc1146105895780638da5cb5b1461059c5780638f4ffcb1146105bc57600080fd5b806373ae54b51461057157806379ba50971461058157600080fd5b80634b1d29b4116102295780635fc7f7c5116101dd5780636572c5dd116101c25780636572c5dd146104dd5780636889d5d01461050d578063715018a61461056957600080fd5b80635fc7f7c5146104b7578063639fcbc2146104ca57600080fd5b8063563170e31161020e578063563170e3146103c8578063584c970b146103e85780635d93a3fc146103fb57600080fd5b80634b1d29b414610388578063554e6e21146103b557600080fd5b806325e102a9116102805780632dfdf0b5116102655780632dfdf0b51461034b57806331645d4e146103625780633c2b87451461037557600080fd5b806325e102a9146103255780632c4b24ae1461033857600080fd5b8063012f180e146102b25780630908c7dc146102c757806319808171146102da5780631ae41f8414610312575b600080fd5b6102c56102c0366004614881565b61081f565b005b6102c56102d53660046148d9565b610bf2565b6102fd6102e83660046148f1565b60046020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6102c5610320366004614928565b610e9b565b6102c56103333660046148f1565b6112e1565b6102c5610346366004614a3e565b611375565b61035460015481565b604051908152602001610309565b6102c5610370366004614b1c565b61163b565b6102c56103833660046148f1565b61164d565b6003546103a090640100000000900463ffffffff1681565b60405163ffffffff9091168152602001610309565b6102c56103c3366004614b56565b6116c9565b6103db6103d6366004614b80565b611810565b6040516103099190614bff565b6102c56103f6366004614c81565b611942565b6104a5610409366004614b80565b60006020818152938152604080822085529281528281209093528252902080546001909101546bffffffffffffffffffffffff808316926c0100000000000000000000000080820463ffffffff16937001000000000000000000000000000000009092048316928216919081046affffffffffffffffffffff169077010000000000000000000000000000000000000000000000900460ff1686565b60405161030996959493929190614cb8565b6102c56104c5366004614d09565b611af1565b6102c56104d8366004614c81565b611ec9565b6105006104eb3660046148f1565b60026020526000908152604090205460ff1681565b6040516103099190614d4c565b61054561051b3660046148f1565b60086020526000908152604090205460ff81169061010090046bffffffffffffffffffffffff1682565b6040805192151583526bffffffffffffffffffffffff909116602083015201610309565b6102c5612095565b6003546103a09063ffffffff1681565b6102c56120a9565b6102c5610597366004614b56565b61210a565b6105a4612282565b6040516001600160a01b039091168152602001610309565b6102c56105ca366004614d5a565b6122b7565b6102c56105dd366004614df5565b61236a565b6007546105a4906001600160a01b031681565b6003546105a4906801000000000000000090046001600160a01b031681565b6102c56106223660046148f1565b612465565b6107036106353660046148f1565b600560205260009081526040902080546001909101546bffffffffffffffffffffffff8083169263ffffffff6c01000000000000000000000000820416926affffffffffffffffffffff7001000000000000000000000000000000008304169260ff7b0100000000000000000000000000000000000000000000000000000084048116937c0100000000000000000000000000000000000000000000000000000000900416916001600160a01b03811691740100000000000000000000000000000000000000009091041687565b604080516bffffffffffffffffffffffff988916815263ffffffff90971660208801526affffffffffffffffffffff9095169486019490945260ff9283166060860152911660808401526001600160a01b031660a08301529190911660c082015260e001610309565b6102c561077a366004614e10565b612568565b6102c561078d366004614edf565b612853565b6105a4612866565b6006546105a4906001600160a01b031681565b6102c56107bb3660046148f1565b61288f565b6102c56107ce366004614b56565b61292c565b6102c56107e1366004614f28565b61306d565b6102c56107f4366004614df5565b61321c565b6102c5610807366004614d09565b6132fe565b6102c561081a366004614e10565b613640565b610827613979565b6001600160a01b038416600090815260026020819052604082205460ff169081111561085557610855614bbc565b0361089c576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024015b60405180910390fd5b60648360ff1611156108df576040517f68704cc400000000000000000000000000000000000000000000000000000000815260ff84166004820152602401610893565b60648260ff161115610922576040517fc51c004900000000000000000000000000000000000000000000000000000000815260ff83166004820152602401610893565b6001600160a01b03811661096d576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610893565b6001600160a01b0380851660009081526005602052604090206001810154909116158015906109ac575060018101546001600160a01b03838116911614155b156109e3576040517fd3d9f5c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a479190614f5b565b90508060ff16601214610a91576040517fcd5cf2c50000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b610a9a866139c4565b81546001830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038681169182179092557fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff9092167b0100000000000000000000000000000000000000000000000000000060ff8981169182027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff16929092177c0100000000000000000000000000000000000000000000000000000000928916928302177fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff166c010000000000000000000000004263ffffffff1602178655604080519182526020820192909252908101929092528716907fcc06fae89af7176b522e80e0f792b25901e66006b16c4ccac33cc75324a16dd39060600160405180910390a2505050505050565b610bfa613979565b6000610c0960208301836148f1565b90506000610c1d6040840160208501614f78565b90506001600160a01b038216610c6a576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610893565b6000816002811115610c7e57610c7e614bbc565b03610cb757806040517fd6a8a3da0000000000000000000000000000000000000000000000000000000081526004016108939190614d4c565b6001600160a01b038216600090815260026020819052604082205460ff1690811115610ce557610ce5614bbc565b14610d3e576001600160a01b038216600090815260026020526040908190205490517f71265fe200000000000000000000000000000000000000000000000000000000815261089391849160ff90911690600401614f93565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610db6575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610db391810190614f5b565b60015b610df7576040517fbcf463bf0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610893565b506001600160a01b0382166000908152600260208190526040909120805483927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116906001908490811115610e5057610e50614bbc565b0217905550816001600160a01b03167f29dd5553eda23e846a442697aeea6662a9699d1a79bb82afba4ba8994898b92c82604051610e8e9190614d4c565b60405180910390a2505050565b6001600160a01b03831660009081526002602052604081205460ff1690816002811115610eca57610eca614bbc565b03610f0c576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610893565b816bffffffffffffffffffffffff16600003610f64576040517f88967d2f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff83166004820152602401610893565b336000908152602081815260408083206001600160a01b038816845282528083208684529091528120805490916bffffffffffffffffffffffff9091169003610fd9576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80546bffffffffffffffffffffffff908116908416106110385780546040517f9dd2b6810000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff9091166004820152602401610893565b600282600281111561104c5761104c614bbc565b14801561106f575080546c01000000000000000000000000900463ffffffff1642105b156110c05780546040517fced227db0000000000000000000000000000000000000000000000000000000081526c0100000000000000000000000090910463ffffffff166004820152602401610893565b805470010000000000000000000000000000000090046bffffffffffffffffffffffff16156111415780546040517f226503810000000000000000000000000000000000000000000000000000000081527001000000000000000000000000000000009091046bffffffffffffffffffffffff166004820152602401610893565b60018101546bffffffffffffffffffffffff16156111a15760018101546040517f01e9e74a0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff9091166004820152602401610893565b6000600182015477010000000000000000000000000000000000000000000000900460ff1660038111156111d7576111d7614bbc565b1461120e576040517faf343ffd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516bffffffffffffffffffffffff8416815284906001600160a01b0387169033907f91fb9d98b786c57d74c099ccd2beca1739e9f6a81fb49001ca465c4b7591bbe29060200160405180910390a48054839082906000906112809084906bffffffffffffffffffffffff16614fdf565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506112da33846bffffffffffffffffffffffff16876001600160a01b0316613ac69092919063ffffffff16565b5050505050565b6112e9613979565b6003546040516001600160a01b038084169268010000000000000000900416907f087168495b2024a05f1e51c26b5abadc7eaa5984c24a419d3563f092693ca1d590600090a3600380546001600160a01b0390921668010000000000000000027fffffffff0000000000000000000000000000000000000000ffffffffffffffff909216919091179055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff166000811580156113c05750825b905060008267ffffffffffffffff1660011480156113dd5750303b155b9050811580156113eb575080155b15611422576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016600117855583156114835784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b61148c33613b3a565b600380547fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016670167e9800024ea00179055600060018190555b86518110156115d15760008782815181106114e3576114e3615004565b6020026020010151600001519050600088838151811061150557611505615004565b602002602001015160200151905060006001600160a01b0316826001600160a01b03160361156a576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610893565b6001600160a01b0382166000908152600260208190526040909120805483927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009091169060019084908111156115c2576115c2614bbc565b021790555050506001016114c6565b5083156116335784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b6116483333858585613b4b565b505050565b611655613979565b6007546040516001600160a01b038084169216907f2393a3a0213901ea187a0528e61d30bfd31577cb6efa698270cc0757e82cc28e90600090a3600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6003546801000000000000000090046001600160a01b0316331461171b576040517fd4bcb487000000000000000000000000000000000000000000000000000000008152336004820152602401610893565b80600003611758576040517f88967d2f00000000000000000000000000000000000000000000000000000000815260048101829052602401610893565b6001600160a01b03821660009081526004602052604090205460ff166117b5576040517f1cc7e5cf0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610893565b816001600160a01b03167f9ab9b817afca6d91dd7d523c53a3d2af8939f0a0805d85d0f67b07585fed524b826040516117f091815260200190565b60405180910390a261180c6001600160a01b0383163383613ac6565b5050565b6118466040805160c08101825260008082526020820181905291810182905260608101829052608081018290529060a082015290565b6001600160a01b038481166000908152602081815260408083209387168352928152828220858352815290829020825160c08101845281546bffffffffffffffffffffffff80821683526c0100000000000000000000000080830463ffffffff1695840195909552700100000000000000000000000000000000909104811694820194909452600182015493841660608201529183046affffffffffffffffffffff166080830152909160a083019077010000000000000000000000000000000000000000000000900460ff16600381111561192457611924614bbc565b600381111561193557611935614bbc565b90525090505b9392505050565b61194a613979565b6001600160a01b03821660009081526002602052604081205460ff169081600281111561197957611979614bbc565b036119bb576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b60028160028111156119cf576119cf614bbc565b14611a28576001600160a01b038316600090815260026020526040908190205490517ff8d3a93600000000000000000000000000000000000000000000000000000000815261089391859160ff90911690600401614f93565b6001600160a01b03831660009081526008602052604090205460ff1615611a7b576040517f0d232cb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f37757c6e0f561c1754a2bc68c5299e01bc49b31193e7928f6a6809920e6811e09101610e8e565b6001600160a01b0380841660009081526005602052604090206001810154909116611b48576040517fce16ea8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600003611b85576040517f88967d2f00000000000000000000000000000000000000000000000000000000815260048101839052602401610893565b336000908152602081815260408083206001600160a01b038816845282528083208684529091528120805490916bffffffffffffffffffffffff9091169003611bfa576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c048186613f1c565b60018201548154600091611c319188916001600160a01b0316906bffffffffffffffffffffffff166140f1565b8354909150600090606490611c69907c0100000000000000000000000000000000000000000000000000000000900460ff1684615033565b611c739190615092565b600184015484549192506bffffffffffffffffffffffff8084169291811691611cb29170010000000000000000000000000000000090910416886150bd565b611cbc91906150bd565b1115611d3657825460018401546040517fffdacd140000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff8085166004830152700100000000000000000000000000000000909304831660248201529116604482015260648101869052608401610893565b825485908490601090611d6c90849070010000000000000000000000000000000090046bffffffffffffffffffffffff166150d0565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550848460000160008282829054906101000a90046bffffffffffffffffffffffff16611dc591906150d0565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555085876001600160a01b0316336001600160a01b03167f862f9b789dcac5ebaaeece5aa03067d5588c6d3f84c140d527894495a028b17388604051611e3991815260200190565b60405180910390a460018401546040517f2389128a000000000000000000000000000000000000000000000000000000008152336004820152602481018790526001600160a01b0390911690632389128a90604401600060405180830381600087803b158015611ea857600080fd5b505af1158015611ebc573d6000803e3d6000fd5b5050505050505050505050565b611ed1613979565b6006546001600160a01b0316611f13576040517f8142dcf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546001600160a01b0390811690831603611f5b576040517fa10c8e1f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660009081526002602052604081205460ff1690816002811115611f8a57611f8a614bbc565b03611fcc576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b6001600160a01b03831660009081526004602052604090205460ff161561201f576040517f0d232cb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660008181526008602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f58282641aa313d24bee632c3ec1cdcbc8b924460dbda396d88cfc2a579446ecf9101610e8e565b61209d613979565b6120a76000614247565b565b33806120b3612866565b6001600160a01b0316146120fe576040517f118cdaa70000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610893565b61210781614247565b50565b336000908152602081815260408083206001600160a01b03861680855290835281842085855283528184209084526008909252909120805460ff1661217b576040517fa487737000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600183015477010000000000000000000000000000000000000000000000900460ff1660038111156121b1576121b1614bbc565b1461220057828260010160179054906101000a900460ff1660006040517f0c278ce1000000000000000000000000000000000000000000000000000000008152600401610893939291906150f5565b6001820180547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff167701000000000000000000000000000000000000000000000017905560405183906001600160a01b0386169033907f4713d6a3ccd421deeb6fb632d8c97878f2e4ae58ac48de1e520b362040b4abf990600090a450505050565b6000807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005b546001600160a01b031692915050565b6001600160a01b0383163314612304576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b6bffffffffffffffffffffffff84111561234d576040517f88967d2f00000000000000000000000000000000000000000000000000000000815260048101859052602401610893565b600061235b82840184614df5565b90506116338687868885613b4b565b612372613979565b600061237d82614297565b90508063ffffffff168263ffffffff1614158061239e575063ffffffff8116155b806123bc575060035463ffffffff6401000000009091048116908316115b156123fb576040517f74af145000000000000000000000000000000000000000000000000000000000815263ffffffff83166004820152602401610893565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff84169081179091556040519081527f4c35d0e4acd88f9d47ba71b6a74a890a34499d0af9d7536e5b46c2b190ea18be9060200160405180910390a15050565b61246d613979565b6001600160a01b0381166124b8576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610893565b6006546001600160a01b0316156124fb576040517f4bc00c4c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f3d8b27d0955baa4924ce9638e61ff44b8fca3c80475d3dfc8fd6582c5df016cf906020015b60405180910390a150565b6007546001600160a01b031633146125ac576040517fc670608600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b82518110156127595760008382815181106125cd576125cd615004565b602002602001015160000151905060008483815181106125ef576125ef615004565b6020908102919091018101518101516001600160a01b0380851660009081528084526040808220928b1682529184528181208382529093529091209091506002600182015477010000000000000000000000000000000000000000000000900460ff16600381111561266357612663614bbc565b146126b257818160010160179054906101000a900460ff1660026040517f0c278ce1000000000000000000000000000000000000000000000000000000008152600401610893939291906150f5565b80546126cc906bffffffffffffffffffffffff16866150d0565b6001820180547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff167703000000000000000000000000000000000000000000000017905560405190955082906001600160a01b0389811691908616907f68d9ffd354ad98f5572d5a19eb60d1be4e8cb57a2d8337d10a3ecfca40b1ebe990600090a45050506001016125b0565b506001600160a01b038316600090815260086020526040902080548290829060019061279990849061010090046bffffffffffffffffffffffff16614fdf565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555060006127e985600660009054906101000a90046001600160a01b0316856140f1565b6040516bffffffffffffffffffffffff821681529091507f4c0f021c587c95b1c98d00bd52fef4dc732158bc51f121461f3dc4e41990c5639060200160405180910390a16006546112da906001600160a01b031633306bffffffffffffffffffffffff85166142b9565b6128608433858585613b4b565b50505050565b6000807f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c006122a7565b612897613979565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03831690811782556128f3612282565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b6001600160a01b03821660009081526002602052604081205460ff169081600281111561295b5761295b614bbc565b0361299d576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b336000908152602081815260408083206001600160a01b03871684528252808320858452909152902060018082015477010000000000000000000000000000000000000000000000900460ff1660038111156129fb576129fb614bbc565b1480612a3a57506002600182015477010000000000000000000000000000000000000000000000900460ff166003811115612a3857612a38614bbc565b145b15612a71576040517fe966904900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002826002811115612a8557612a85614bbc565b148015612aa8575080546c01000000000000000000000000900463ffffffff1642105b15612af95780546040517fced227db0000000000000000000000000000000000000000000000000000000081526c0100000000000000000000000090910463ffffffff166004820152602401610893565b805470010000000000000000000000000000000090046bffffffffffffffffffffffff1615612b7a5780546040517f226503810000000000000000000000000000000000000000000000000000000081527001000000000000000000000000000000009091046bffffffffffffffffffffffff166004820152602401610893565b80546bffffffffffffffffffffffff166000819003612bc5576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003600183015477010000000000000000000000000000000000000000000000900460ff166003811115612bfb57612bfb614bbc565b03612e545760018201546000906bffffffffffffffffffffffff1615612c64576001600160a01b038087166000908152600560205260409020600180820154600654918701549293612c60939181169216906bffffffffffffffffffffffff166140f1565b9150505b600654600090612c7f9088906001600160a01b0316856140f1565b90506000612c8d8383614fdf565b600654604080516001600160a01b0392831681526bffffffffffffffffffffffff841660208201529293508992918b169133917faabf355ccacfa8b7366b9f6a14af62036d7dd401797d7591faae42a5bbbc3db9910160405180910390a4600654604080516001600160a01b0392831681526bffffffffffffffffffffffff8616602082015289928b169133917f5f82682eb95ce785b4c40b5c57de2b7ae2ca818ac5f1e7ab89300e6142215d8f910160405180910390a46006546001600160a01b031660009081526005602052604090206001018054849190601490612d9b9084907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166150d0565b82546101009290920a6bffffffffffffffffffffffff818102199093169183160217909155336000818152602081815260408083206001600160a01b038f811685529083528184208e855290925290912080547fffffffff0000000000000000000000000000000000000000000000000000000016815560010180547fffffffffffffffff000000000000000000000000000000000000000000000000169055600654612e4c945016918416613ac6565b5050506112da565b60018201546000906bffffffffffffffffffffffff1615612eb4576001600160a01b038087166000908152600560205260409020600180820154908601549192612eb09291169089906bffffffffffffffffffffffff166140f1565b9150505b6000612ec08284614fdf565b6040516bffffffffffffffffffffffff8216815290915086906001600160a01b0389169033907f91fb9d98b786c57d74c099ccd2beca1739e9f6a81fb49001ca465c4b7591bbe29060200160405180910390a46040516bffffffffffffffffffffffff8316815286906001600160a01b0389169033907f205442d60b70af1203d43cab62352c3b69b94f091be32fe683198057282b5c929060200160405180910390a46001600160a01b03871660009081526005602052604090206001018054839190601490612fb79084907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166150d0565b82546101009290920a6bffffffffffffffffffffffff818102199093169183160217909155336000818152602081815260408083206001600160a01b038e168085529083528184208d855290925290912080547fffffffff0000000000000000000000000000000000000000000000000000000016815560010180547fffffffffffffffff0000000000000000000000000000000000000000000000001690556130649350918416613ac6565b50505050505050565b336000908152602081815260408083206001600160a01b038716845282528083208584529091528120805490916bffffffffffffffffffffffff90911690036130e2576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03841660009081526002602081905260409091205460ff169081600281111561311457613114614bbc565b1461314f5784816040517ff8d3a936000000000000000000000000000000000000000000000000000000008152600401610893929190614f93565b81546c01000000000000000000000000900463ffffffff166000613175338888886142f2565b90508163ffffffff168163ffffffff16116131d4576040517f9e03653a00000000000000000000000000000000000000000000000000000000815263ffffffff8087166004830152808316602483015283166044820152606401610893565b835463ffffffff9091166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff909116179092555050505050565b613224613979565b61322d81614297565b63ffffffff168163ffffffff16141580613252575060035463ffffffff908116908216105b15613291576040517f74af145000000000000000000000000000000000000000000000000000000000815263ffffffff82166004820152602401610893565b600380547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1664010000000063ffffffff8416908102919091179091556040519081527fe02644567ab9266166c374f84f05396b070729fc139339e70d0237bb37e59dc59060200161255d565b6001600160a01b0380841660009081526005602052604090206001810154909116613355576040517fce16ea8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600003613392576040517f88967d2f00000000000000000000000000000000000000000000000000000000815260048101839052602401610893565b336000908152602081815260408083206001600160a01b038816845282528083208684529091528120805490916bffffffffffffffffffffffff9091169003613407576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805470010000000000000000000000000000000090046bffffffffffffffffffffffff168311156134915780546040517f5f6faf860000000000000000000000000000000000000000000000000000000081527001000000000000000000000000000000009091046bffffffffffffffffffffffff16600482015260248101849052604401610893565b61349b8186613f1c565b8054839082906010906134d190849070010000000000000000000000000000000090046bffffffffffffffffffffffff16614fdf565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550828260000160008282829054906101000a90046bffffffffffffffffffffffff1661352a9190614fdf565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555083856001600160a01b0316336001600160a01b03167f6043289a72dfdddcba5a5eebd82a24572023a2344a1292dfcf3b56c1a142f6068660405161359e91815260200190565b60405180910390a460018201546135c0906001600160a01b03163330866142b9565b60018201546040517f942c5875000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b039091169063942c587590602401600060405180830381600087803b15801561362157600080fd5b505af1158015613635573d6000803e3d6000fd5b505050505050505050565b6007546001600160a01b03163314613684576040517fc670608600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b825181101561388e5760008382815181106136a5576136a5615004565b602002602001015160000151905060008483815181106136c7576136c7615004565b6020908102919091018101518101516001600160a01b0380851660009081528084526040808220928b16825291845281812083825290935282208054919350916bffffffffffffffffffffffff909116900361374f576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018082015477010000000000000000000000000000000000000000000000900460ff16600381111561378457613784614bbc565b146137e2576001818101546040517f0c278ce100000000000000000000000000000000000000000000000000000000815261089392859277010000000000000000000000000000000000000000000000900460ff16916004016150f5565b6001810180547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16770200000000000000000000000000000000000000000000001790558054613840906bffffffffffffffffffffffff16866150d0565b945081876001600160a01b0316846001600160a01b03167f41f6c6b107a872f7e7a62127f1104669af1b4b25a8eba2a4207a8266bd2b2c6460405160405180910390a4505050600101613688565b506001600160a01b03831660009081526008602052604090208054829082906001906138ce90849061010090046bffffffffffffffffffffffff166150d0565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550836001600160a01b03167fd9953834583f8ccc107d531dd2133b07f00bf5c8cebe8f594486930986996c988360405161394991906bffffffffffffffffffffffff91909116815260200190565b60405180910390a2600754612860906001600160a01b0386811691166bffffffffffffffffffffffff8516613ac6565b33613982612282565b6001600160a01b0316146120a7576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610893565b6001600160a01b0381166000908152600560205260408120906139e68361440b565b8254909150600090613a0e906c01000000000000000000000000900463ffffffff1642615116565b90506000613a228363ffffffff8416615033565b845490915081908590601090613a5a90849070010000000000000000000000000000000090046affffffffffffffffffffff16615133565b82546101009290920a6affffffffffffffffffffff818102199093169190921691909102179055505082547fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff166c010000000000000000000000004263ffffffff160217909255505050565b6040516001600160a01b0383811660248301526044820183905261164891859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061452c565b613b426145a8565b6121078161460f565b6001600160a01b03831660009081526002602052604081205460ff1690816002811115613b7a57613b7a614bbc565b03613bbc576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610893565b60008263ffffffff16118015613be357506001816002811115613be157613be1614bbc565b145b15613c1e5783816040517ff8d3a936000000000000000000000000000000000000000000000000000000008152600401610893929190614f93565b6001600160a01b038616613c69576040517f9c64f6a10000000000000000000000000000000000000000000000000000000081526001600160a01b0387166004820152602401610893565b826bffffffffffffffffffffffff16600003613cc1576040517f88967d2f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff84166004820152602401610893565b60018054906000613cd183615157565b90915550506001546040516bffffffffffffffffffffffff8516815281906001600160a01b0380881691908a16907ff5681f9d0db1b911ac18ee83d515a1cf1051853a9eae418316a2fdf7dea427c59060200160405180910390a46040518060c00160405280856bffffffffffffffffffffffff168152602001613d57898885886142f2565b63ffffffff16815260006020820181905260408201819052606082018190526080909101526001600160a01b03808916600090815260208181526040808320938a1683529281528282208583528152908290208351815492850151938501516bffffffffffffffffffffffff908116700100000000000000000000000000000000027fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9096166c010000000000000000000000009081027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909616938316939093179490941794909416929092178155606084015160018201805460808701516affffffffffffffffffffff169094027fffffffffffffffffff000000000000000000000000000000000000000000000090941691909416179190911780835560a08401519192907fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff1677010000000000000000000000000000000000000000000000836003811115613ef157613ef1614bbc565b02179055506130649150506001600160a01b03861687306bffffffffffffffffffffffff88166142b9565b613f25816139c4565b6001600160a01b03818116600090815260056020908152604091829020825160e08101845281546bffffffffffffffffffffffff80821683526c0100000000000000000000000080830463ffffffff169584019590955270010000000000000000000000000000000082046affffffffffffffffffffff9081169684018790527b01000000000000000000000000000000000000000000000000000000830460ff90811660608601527c0100000000000000000000000000000000000000000000000000000000909304909216608084015260019384015496871660a08401527401000000000000000000000000000000000000000090960490951660c08201529086015490936140679361403e93909204169061518f565b845470010000000000000000000000000000000090046bffffffffffffffffffffffff1661465a565b6001840180546000906140899084906bffffffffffffffffffffffff166150d0565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550806040015183600101600c6101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff160217905550505050565b600080846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614132573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141569190614f5b565b90506000846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614198573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141bc9190614f5b565b90508060ff168260ff161015614202576141d682826151b3565b6141e190600a6152ec565b6141f9906bffffffffffffffffffffffff86166152fb565b9250505061193b565b8060ff168260ff16111561423d5761421a81836151b3565b61422590600a6152ec565b6141f9906bffffffffffffffffffffffff8616615312565b5091949350505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815561180c82614694565b60006142a662093a8083615326565b6142b39062093a8061533d565b92915050565b6040516001600160a01b0384811660248301528381166044830152606482018390526128609186918216906323b872dd90608401613af3565b60008163ffffffff16600003614309575042614403565b600061431483614297565b905063ffffffff81161580614334575060035463ffffffff908116908216105b80614352575060035463ffffffff6401000000009091048116908216115b15614391576040517f8dbf963f00000000000000000000000000000000000000000000000000000000815263ffffffff84166004820152602401610893565b600061439d824261535d565b905084866001600160a01b0316886001600160a01b03167f8b65b80ac62fde507cb8196bad6c93c114c2babc6ac846aae39ed6943ad36c4984866040516143f792919063ffffffff92831681529116602082015260400190565b60405180910390a49150505b949350505050565b6001600160a01b038082166000908152600560209081526040808320815160e08101835281546bffffffffffffffffffffffff808216835263ffffffff6c01000000000000000000000000830416958301959095526affffffffffffffffffffff7001000000000000000000000000000000008204169382019390935260ff7b0100000000000000000000000000000000000000000000000000000084048116606083018190527c010000000000000000000000000000000000000000000000000000000090940416608082015260019091015494851660a08201527401000000000000000000000000000000000000000090940490911660c08401529091906301e133809061452290662386f26fc10000615033565b61193b9190615092565b60006145416001600160a01b0384168361471d565b90508051600014158015614566575080806020019051810190614564919061537a565b155b15611648576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff166120a7576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6146176145a8565b6001600160a01b0381166120fe576040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260006004820152602401610893565b6000670de0b6b3a764000061468a6bffffffffffffffffffffffff84166affffffffffffffffffffff86166152fb565b61193b9190615312565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b606061193b8383600084600080856001600160a01b031684866040516147439190615397565b60006040518083038185875af1925050503d8060008114614780576040519150601f19603f3d011682016040523d82523d6000602084013e614785565b606091505b509150915061479586838361479f565b9695505050505050565b6060826147b4576147af82614814565b61193b565b81511580156147cb57506001600160a01b0384163b155b1561480d576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610893565b508061193b565b8051156148245780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80356001600160a01b038116811461486d57600080fd5b919050565b60ff8116811461210757600080fd5b6000806000806080858703121561489757600080fd5b6148a085614856565b935060208501356148b081614872565b925060408501356148c081614872565b91506148ce60608601614856565b905092959194509250565b6000604082840312156148eb57600080fd5b50919050565b60006020828403121561490357600080fd5b61193b82614856565b80356bffffffffffffffffffffffff8116811461486d57600080fd5b60008060006060848603121561493d57600080fd5b61494684614856565b92506020840135915061495b6040850161490c565b90509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156149b6576149b6614964565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614a0357614a03614964565b604052919050565b600067ffffffffffffffff821115614a2557614a25614964565b5060051b60200190565b80356003811061486d57600080fd5b60006020808385031215614a5157600080fd5b823567ffffffffffffffff811115614a6857600080fd5b8301601f81018513614a7957600080fd5b8035614a8c614a8782614a0b565b6149bc565b81815260069190911b82018301908381019087831115614aab57600080fd5b928401925b82841015614afd5760408489031215614ac95760008081fd5b614ad1614993565b614ada85614856565b8152614ae7868601614a2f565b8187015282526040939093019290840190614ab0565b979650505050505050565b803563ffffffff8116811461486d57600080fd5b600080600060608486031215614b3157600080fd5b614b3a84614856565b9250614b486020850161490c565b915061495b60408501614b08565b60008060408385031215614b6957600080fd5b614b7283614856565b946020939093013593505050565b600080600060608486031215614b9557600080fd5b614b9e84614856565b9250614bac60208501614856565b9150604084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110614bfb57614bfb614bbc565b9052565b600060c0820190506bffffffffffffffffffffffff80845116835263ffffffff6020850151166020840152806040850151166040840152806060850151166060840152506affffffffffffffffffffff608084015116608083015260a0830151614c6c60a0840182614beb565b5092915050565b801515811461210757600080fd5b60008060408385031215614c9457600080fd5b614c9d83614856565b91506020830135614cad81614c73565b809150509250929050565b6bffffffffffffffffffffffff878116825263ffffffff871660208301528581166040830152841660608201526affffffffffffffffffffff8316608082015260c08101614afd60a0830184614beb565b600080600060608486031215614d1e57600080fd5b614d2784614856565b95602085013595506040909401359392505050565b60038110614bfb57614bfb614bbc565b602081016142b38284614d3c565b600080600080600060808688031215614d7257600080fd5b614d7b86614856565b945060208601359350614d9060408701614856565b9250606086013567ffffffffffffffff80821115614dad57600080fd5b818801915088601f830112614dc157600080fd5b813581811115614dd057600080fd5b896020828501011115614de257600080fd5b9699959850939650602001949392505050565b600060208284031215614e0757600080fd5b61193b82614b08565b6000806040808486031215614e2457600080fd5b614e2d84614856565b925060208085013567ffffffffffffffff811115614e4a57600080fd5b8501601f81018713614e5b57600080fd5b8035614e69614a8782614a0b565b81815260069190911b82018301908381019089831115614e8857600080fd5b928401925b82841015614ecf5785848b031215614ea55760008081fd5b614ead614993565b614eb685614856565b8152848601358682015282529285019290840190614e8d565b8096505050505050509250929050565b60008060008060808587031215614ef557600080fd5b614efe85614856565b9350614f0c60208601614856565b9250614f1a6040860161490c565b91506148ce60608601614b08565b600080600060608486031215614f3d57600080fd5b614f4684614856565b92506020840135915061495b60408501614b08565b600060208284031215614f6d57600080fd5b815161193b81614872565b600060208284031215614f8a57600080fd5b61193b82614a2f565b6001600160a01b03831681526040810161193b6020830184614d3c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff828116828216039080821115614c6c57614c6c614fb0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6bffffffffffffffffffffffff81811683821602808216919082811461505b5761505b614fb0565b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006bffffffffffffffffffffffff808416806150b1576150b1615063565b92169190910492915050565b808201808211156142b3576142b3614fb0565b6bffffffffffffffffffffffff818116838216019080821115614c6c57614c6c614fb0565b838152606081016151096020830185614beb565b6144036040830184614beb565b63ffffffff828116828216039080821115614c6c57614c6c614fb0565b6affffffffffffffffffffff818116838216019080821115614c6c57614c6c614fb0565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361518857615188614fb0565b5060010190565b6affffffffffffffffffffff828116828216039080821115614c6c57614c6c614fb0565b60ff82811682821603908111156142b3576142b3614fb0565b600181815b8085111561522557817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561520b5761520b614fb0565b8085161561521857918102915b93841c93908002906151d1565b509250929050565b60008261523c575060016142b3565b81615249575060006142b3565b816001811461525f576002811461526957615285565b60019150506142b3565b60ff84111561527a5761527a614fb0565b50506001821b6142b3565b5060208310610133831016604e8410600b84101617156152a8575081810a6142b3565b6152b283836151cc565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156152e4576152e4614fb0565b029392505050565b600061193b60ff84168361522d565b80820281158282048414176142b3576142b3614fb0565b60008261532157615321615063565b500490565b600063ffffffff808416806150b1576150b1615063565b63ffffffff81811683821602808216919082811461505b5761505b614fb0565b63ffffffff818116838216019080821115614c6c57614c6c614fb0565b60006020828403121561538c57600080fd5b815161193b81614c73565b6000825160005b818110156153b8576020818601810151858301520161539e565b50600092019182525091905056fea26469706673582212204f4c8738f189afb9d4789abd80b92b01657882f015929fba454de37d775515b164736f6c63430008180033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102ad5760003560e01c806373ae54b51161017b578063c74b552e116100d8578063f3fef3a31161008c578063f64a6c9011610071578063f64a6c90146107e6578063fa4e00c0146107f9578063fe10b9fb1461080c57600080fd5b8063f3fef3a3146107c0578063f5e8d327146107d357600080fd5b8063e30c3978116100bd578063e30c397814610792578063e5d3d7141461079a578063f2fde38b146107ad57600080fd5b8063c74b552e1461076c578063dfb6c2d21461077f57600080fd5b806392673f551161012f578063b918e5f911610114578063b918e5f9146105f5578063bc06b96514610614578063bfcfa66b1461062757600080fd5b806392673f55146105cf578063a2b7e2dd146105e257600080fd5b80637b498fc1116101605780637b498fc1146105895780638da5cb5b1461059c5780638f4ffcb1146105bc57600080fd5b806373ae54b51461057157806379ba50971461058157600080fd5b80634b1d29b4116102295780635fc7f7c5116101dd5780636572c5dd116101c25780636572c5dd146104dd5780636889d5d01461050d578063715018a61461056957600080fd5b80635fc7f7c5146104b7578063639fcbc2146104ca57600080fd5b8063563170e31161020e578063563170e3146103c8578063584c970b146103e85780635d93a3fc146103fb57600080fd5b80634b1d29b414610388578063554e6e21146103b557600080fd5b806325e102a9116102805780632dfdf0b5116102655780632dfdf0b51461034b57806331645d4e146103625780633c2b87451461037557600080fd5b806325e102a9146103255780632c4b24ae1461033857600080fd5b8063012f180e146102b25780630908c7dc146102c757806319808171146102da5780631ae41f8414610312575b600080fd5b6102c56102c0366004614881565b61081f565b005b6102c56102d53660046148d9565b610bf2565b6102fd6102e83660046148f1565b60046020526000908152604090205460ff1681565b60405190151581526020015b60405180910390f35b6102c5610320366004614928565b610e9b565b6102c56103333660046148f1565b6112e1565b6102c5610346366004614a3e565b611375565b61035460015481565b604051908152602001610309565b6102c5610370366004614b1c565b61163b565b6102c56103833660046148f1565b61164d565b6003546103a090640100000000900463ffffffff1681565b60405163ffffffff9091168152602001610309565b6102c56103c3366004614b56565b6116c9565b6103db6103d6366004614b80565b611810565b6040516103099190614bff565b6102c56103f6366004614c81565b611942565b6104a5610409366004614b80565b60006020818152938152604080822085529281528281209093528252902080546001909101546bffffffffffffffffffffffff808316926c0100000000000000000000000080820463ffffffff16937001000000000000000000000000000000009092048316928216919081046affffffffffffffffffffff169077010000000000000000000000000000000000000000000000900460ff1686565b60405161030996959493929190614cb8565b6102c56104c5366004614d09565b611af1565b6102c56104d8366004614c81565b611ec9565b6105006104eb3660046148f1565b60026020526000908152604090205460ff1681565b6040516103099190614d4c565b61054561051b3660046148f1565b60086020526000908152604090205460ff81169061010090046bffffffffffffffffffffffff1682565b6040805192151583526bffffffffffffffffffffffff909116602083015201610309565b6102c5612095565b6003546103a09063ffffffff1681565b6102c56120a9565b6102c5610597366004614b56565b61210a565b6105a4612282565b6040516001600160a01b039091168152602001610309565b6102c56105ca366004614d5a565b6122b7565b6102c56105dd366004614df5565b61236a565b6007546105a4906001600160a01b031681565b6003546105a4906801000000000000000090046001600160a01b031681565b6102c56106223660046148f1565b612465565b6107036106353660046148f1565b600560205260009081526040902080546001909101546bffffffffffffffffffffffff8083169263ffffffff6c01000000000000000000000000820416926affffffffffffffffffffff7001000000000000000000000000000000008304169260ff7b0100000000000000000000000000000000000000000000000000000084048116937c0100000000000000000000000000000000000000000000000000000000900416916001600160a01b03811691740100000000000000000000000000000000000000009091041687565b604080516bffffffffffffffffffffffff988916815263ffffffff90971660208801526affffffffffffffffffffff9095169486019490945260ff9283166060860152911660808401526001600160a01b031660a08301529190911660c082015260e001610309565b6102c561077a366004614e10565b612568565b6102c561078d366004614edf565b612853565b6105a4612866565b6006546105a4906001600160a01b031681565b6102c56107bb3660046148f1565b61288f565b6102c56107ce366004614b56565b61292c565b6102c56107e1366004614f28565b61306d565b6102c56107f4366004614df5565b61321c565b6102c5610807366004614d09565b6132fe565b6102c561081a366004614e10565b613640565b610827613979565b6001600160a01b038416600090815260026020819052604082205460ff169081111561085557610855614bbc565b0361089c576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b03851660048201526024015b60405180910390fd5b60648360ff1611156108df576040517f68704cc400000000000000000000000000000000000000000000000000000000815260ff84166004820152602401610893565b60648260ff161115610922576040517fc51c004900000000000000000000000000000000000000000000000000000000815260ff83166004820152602401610893565b6001600160a01b03811661096d576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610893565b6001600160a01b0380851660009081526005602052604090206001810154909116158015906109ac575060018101546001600160a01b03838116911614155b156109e3576040517fd3d9f5c500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000826001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610a23573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a479190614f5b565b90508060ff16601214610a91576040517fcd5cf2c50000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b610a9a866139c4565b81546001830180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038681169182179092557fffffff0000ffffffffffffffffffffffffffffffffffffffffffffffffffffff9092167b0100000000000000000000000000000000000000000000000000000060ff8981169182027fffffff00ffffffffffffffffffffffffffffffffffffffffffffffffffffffff16929092177c0100000000000000000000000000000000000000000000000000000000928916928302177fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff166c010000000000000000000000004263ffffffff1602178655604080519182526020820192909252908101929092528716907fcc06fae89af7176b522e80e0f792b25901e66006b16c4ccac33cc75324a16dd39060600160405180910390a2505050505050565b610bfa613979565b6000610c0960208301836148f1565b90506000610c1d6040840160208501614f78565b90506001600160a01b038216610c6a576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610893565b6000816002811115610c7e57610c7e614bbc565b03610cb757806040517fd6a8a3da0000000000000000000000000000000000000000000000000000000081526004016108939190614d4c565b6001600160a01b038216600090815260026020819052604082205460ff1690811115610ce557610ce5614bbc565b14610d3e576001600160a01b038216600090815260026020526040908190205490517f71265fe200000000000000000000000000000000000000000000000000000000815261089391849160ff90911690600401614f93565b816001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa925050508015610db6575060408051601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201909252610db391810190614f5b565b60015b610df7576040517fbcf463bf0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610893565b506001600160a01b0382166000908152600260208190526040909120805483927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00909116906001908490811115610e5057610e50614bbc565b0217905550816001600160a01b03167f29dd5553eda23e846a442697aeea6662a9699d1a79bb82afba4ba8994898b92c82604051610e8e9190614d4c565b60405180910390a2505050565b6001600160a01b03831660009081526002602052604081205460ff1690816002811115610eca57610eca614bbc565b03610f0c576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610893565b816bffffffffffffffffffffffff16600003610f64576040517f88967d2f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff83166004820152602401610893565b336000908152602081815260408083206001600160a01b038816845282528083208684529091528120805490916bffffffffffffffffffffffff9091169003610fd9576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80546bffffffffffffffffffffffff908116908416106110385780546040517f9dd2b6810000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff9091166004820152602401610893565b600282600281111561104c5761104c614bbc565b14801561106f575080546c01000000000000000000000000900463ffffffff1642105b156110c05780546040517fced227db0000000000000000000000000000000000000000000000000000000081526c0100000000000000000000000090910463ffffffff166004820152602401610893565b805470010000000000000000000000000000000090046bffffffffffffffffffffffff16156111415780546040517f226503810000000000000000000000000000000000000000000000000000000081527001000000000000000000000000000000009091046bffffffffffffffffffffffff166004820152602401610893565b60018101546bffffffffffffffffffffffff16156111a15760018101546040517f01e9e74a0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff9091166004820152602401610893565b6000600182015477010000000000000000000000000000000000000000000000900460ff1660038111156111d7576111d7614bbc565b1461120e576040517faf343ffd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516bffffffffffffffffffffffff8416815284906001600160a01b0387169033907f91fb9d98b786c57d74c099ccd2beca1739e9f6a81fb49001ca465c4b7591bbe29060200160405180910390a48054839082906000906112809084906bffffffffffffffffffffffff16614fdf565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff1602179055506112da33846bffffffffffffffffffffffff16876001600160a01b0316613ac69092919063ffffffff16565b5050505050565b6112e9613979565b6003546040516001600160a01b038084169268010000000000000000900416907f087168495b2024a05f1e51c26b5abadc7eaa5984c24a419d3563f092693ca1d590600090a3600380546001600160a01b0390921668010000000000000000027fffffffff0000000000000000000000000000000000000000ffffffffffffffff909216919091179055565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff166000811580156113c05750825b905060008267ffffffffffffffff1660011480156113dd5750303b155b9050811580156113eb575080155b15611422576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016600117855583156114835784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b61148c33613b3a565b600380547fffffffffffffffffffffffffffffffffffffffffffffffff000000000000000016670167e9800024ea00179055600060018190555b86518110156115d15760008782815181106114e3576114e3615004565b6020026020010151600001519050600088838151811061150557611505615004565b602002602001015160200151905060006001600160a01b0316826001600160a01b03160361156a576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610893565b6001600160a01b0382166000908152600260208190526040909120805483927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009091169060019084908111156115c2576115c2614bbc565b021790555050506001016114c6565b5083156116335784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050565b6116483333858585613b4b565b505050565b611655613979565b6007546040516001600160a01b038084169216907f2393a3a0213901ea187a0528e61d30bfd31577cb6efa698270cc0757e82cc28e90600090a3600780547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0392909216919091179055565b6003546801000000000000000090046001600160a01b0316331461171b576040517fd4bcb487000000000000000000000000000000000000000000000000000000008152336004820152602401610893565b80600003611758576040517f88967d2f00000000000000000000000000000000000000000000000000000000815260048101829052602401610893565b6001600160a01b03821660009081526004602052604090205460ff166117b5576040517f1cc7e5cf0000000000000000000000000000000000000000000000000000000081526001600160a01b0383166004820152602401610893565b816001600160a01b03167f9ab9b817afca6d91dd7d523c53a3d2af8939f0a0805d85d0f67b07585fed524b826040516117f091815260200190565b60405180910390a261180c6001600160a01b0383163383613ac6565b5050565b6118466040805160c08101825260008082526020820181905291810182905260608101829052608081018290529060a082015290565b6001600160a01b038481166000908152602081815260408083209387168352928152828220858352815290829020825160c08101845281546bffffffffffffffffffffffff80821683526c0100000000000000000000000080830463ffffffff1695840195909552700100000000000000000000000000000000909104811694820194909452600182015493841660608201529183046affffffffffffffffffffff166080830152909160a083019077010000000000000000000000000000000000000000000000900460ff16600381111561192457611924614bbc565b600381111561193557611935614bbc565b90525090505b9392505050565b61194a613979565b6001600160a01b03821660009081526002602052604081205460ff169081600281111561197957611979614bbc565b036119bb576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b60028160028111156119cf576119cf614bbc565b14611a28576001600160a01b038316600090815260026020526040908190205490517ff8d3a93600000000000000000000000000000000000000000000000000000000815261089391859160ff90911690600401614f93565b6001600160a01b03831660009081526008602052604090205460ff1615611a7b576040517f0d232cb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660008181526004602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f37757c6e0f561c1754a2bc68c5299e01bc49b31193e7928f6a6809920e6811e09101610e8e565b6001600160a01b0380841660009081526005602052604090206001810154909116611b48576040517fce16ea8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600003611b85576040517f88967d2f00000000000000000000000000000000000000000000000000000000815260048101839052602401610893565b336000908152602081815260408083206001600160a01b038816845282528083208684529091528120805490916bffffffffffffffffffffffff9091169003611bfa576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611c048186613f1c565b60018201548154600091611c319188916001600160a01b0316906bffffffffffffffffffffffff166140f1565b8354909150600090606490611c69907c0100000000000000000000000000000000000000000000000000000000900460ff1684615033565b611c739190615092565b600184015484549192506bffffffffffffffffffffffff8084169291811691611cb29170010000000000000000000000000000000090910416886150bd565b611cbc91906150bd565b1115611d3657825460018401546040517fffdacd140000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff8085166004830152700100000000000000000000000000000000909304831660248201529116604482015260648101869052608401610893565b825485908490601090611d6c90849070010000000000000000000000000000000090046bffffffffffffffffffffffff166150d0565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550848460000160008282829054906101000a90046bffffffffffffffffffffffff16611dc591906150d0565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555085876001600160a01b0316336001600160a01b03167f862f9b789dcac5ebaaeece5aa03067d5588c6d3f84c140d527894495a028b17388604051611e3991815260200190565b60405180910390a460018401546040517f2389128a000000000000000000000000000000000000000000000000000000008152336004820152602481018790526001600160a01b0390911690632389128a90604401600060405180830381600087803b158015611ea857600080fd5b505af1158015611ebc573d6000803e3d6000fd5b5050505050505050505050565b611ed1613979565b6006546001600160a01b0316611f13576040517f8142dcf600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6006546001600160a01b0390811690831603611f5b576040517fa10c8e1f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03821660009081526002602052604081205460ff1690816002811115611f8a57611f8a614bbc565b03611fcc576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b6001600160a01b03831660009081526004602052604090205460ff161561201f576040517f0d232cb900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03831660008181526008602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001686151590811790915591519182527f58282641aa313d24bee632c3ec1cdcbc8b924460dbda396d88cfc2a579446ecf9101610e8e565b61209d613979565b6120a76000614247565b565b33806120b3612866565b6001600160a01b0316146120fe576040517f118cdaa70000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610893565b61210781614247565b50565b336000908152602081815260408083206001600160a01b03861680855290835281842085855283528184209084526008909252909120805460ff1661217b576040517fa487737000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000600183015477010000000000000000000000000000000000000000000000900460ff1660038111156121b1576121b1614bbc565b1461220057828260010160179054906101000a900460ff1660006040517f0c278ce1000000000000000000000000000000000000000000000000000000008152600401610893939291906150f5565b6001820180547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff167701000000000000000000000000000000000000000000000017905560405183906001600160a01b0386169033907f4713d6a3ccd421deeb6fb632d8c97878f2e4ae58ac48de1e520b362040b4abf990600090a450505050565b6000807f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c1993005b546001600160a01b031692915050565b6001600160a01b0383163314612304576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b6bffffffffffffffffffffffff84111561234d576040517f88967d2f00000000000000000000000000000000000000000000000000000000815260048101859052602401610893565b600061235b82840184614df5565b90506116338687868885613b4b565b612372613979565b600061237d82614297565b90508063ffffffff168263ffffffff1614158061239e575063ffffffff8116155b806123bc575060035463ffffffff6401000000009091048116908316115b156123fb576040517f74af145000000000000000000000000000000000000000000000000000000000815263ffffffff83166004820152602401610893565b600380547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001663ffffffff84169081179091556040519081527f4c35d0e4acd88f9d47ba71b6a74a890a34499d0af9d7536e5b46c2b190ea18be9060200160405180910390a15050565b61246d613979565b6001600160a01b0381166124b8576040517f6a2e6fbf0000000000000000000000000000000000000000000000000000000081526001600160a01b0382166004820152602401610893565b6006546001600160a01b0316156124fb576040517f4bc00c4c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600680547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f3d8b27d0955baa4924ce9638e61ff44b8fca3c80475d3dfc8fd6582c5df016cf906020015b60405180910390a150565b6007546001600160a01b031633146125ac576040517fc670608600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b82518110156127595760008382815181106125cd576125cd615004565b602002602001015160000151905060008483815181106125ef576125ef615004565b6020908102919091018101518101516001600160a01b0380851660009081528084526040808220928b1682529184528181208382529093529091209091506002600182015477010000000000000000000000000000000000000000000000900460ff16600381111561266357612663614bbc565b146126b257818160010160179054906101000a900460ff1660026040517f0c278ce1000000000000000000000000000000000000000000000000000000008152600401610893939291906150f5565b80546126cc906bffffffffffffffffffffffff16866150d0565b6001820180547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff167703000000000000000000000000000000000000000000000017905560405190955082906001600160a01b0389811691908616907f68d9ffd354ad98f5572d5a19eb60d1be4e8cb57a2d8337d10a3ecfca40b1ebe990600090a45050506001016125b0565b506001600160a01b038316600090815260086020526040902080548290829060019061279990849061010090046bffffffffffffffffffffffff16614fdf565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555060006127e985600660009054906101000a90046001600160a01b0316856140f1565b6040516bffffffffffffffffffffffff821681529091507f4c0f021c587c95b1c98d00bd52fef4dc732158bc51f121461f3dc4e41990c5639060200160405180910390a16006546112da906001600160a01b031633306bffffffffffffffffffffffff85166142b9565b6128608433858585613b4b565b50505050565b6000807f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c006122a7565b612897613979565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03831690811782556128f3612282565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a35050565b6001600160a01b03821660009081526002602052604081205460ff169081600281111561295b5761295b614bbc565b0361299d576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b336000908152602081815260408083206001600160a01b03871684528252808320858452909152902060018082015477010000000000000000000000000000000000000000000000900460ff1660038111156129fb576129fb614bbc565b1480612a3a57506002600182015477010000000000000000000000000000000000000000000000900460ff166003811115612a3857612a38614bbc565b145b15612a71576040517fe966904900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6002826002811115612a8557612a85614bbc565b148015612aa8575080546c01000000000000000000000000900463ffffffff1642105b15612af95780546040517fced227db0000000000000000000000000000000000000000000000000000000081526c0100000000000000000000000090910463ffffffff166004820152602401610893565b805470010000000000000000000000000000000090046bffffffffffffffffffffffff1615612b7a5780546040517f226503810000000000000000000000000000000000000000000000000000000081527001000000000000000000000000000000009091046bffffffffffffffffffffffff166004820152602401610893565b80546bffffffffffffffffffffffff166000819003612bc5576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003600183015477010000000000000000000000000000000000000000000000900460ff166003811115612bfb57612bfb614bbc565b03612e545760018201546000906bffffffffffffffffffffffff1615612c64576001600160a01b038087166000908152600560205260409020600180820154600654918701549293612c60939181169216906bffffffffffffffffffffffff166140f1565b9150505b600654600090612c7f9088906001600160a01b0316856140f1565b90506000612c8d8383614fdf565b600654604080516001600160a01b0392831681526bffffffffffffffffffffffff841660208201529293508992918b169133917faabf355ccacfa8b7366b9f6a14af62036d7dd401797d7591faae42a5bbbc3db9910160405180910390a4600654604080516001600160a01b0392831681526bffffffffffffffffffffffff8616602082015289928b169133917f5f82682eb95ce785b4c40b5c57de2b7ae2ca818ac5f1e7ab89300e6142215d8f910160405180910390a46006546001600160a01b031660009081526005602052604090206001018054849190601490612d9b9084907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166150d0565b82546101009290920a6bffffffffffffffffffffffff818102199093169183160217909155336000818152602081815260408083206001600160a01b038f811685529083528184208e855290925290912080547fffffffff0000000000000000000000000000000000000000000000000000000016815560010180547fffffffffffffffff000000000000000000000000000000000000000000000000169055600654612e4c945016918416613ac6565b5050506112da565b60018201546000906bffffffffffffffffffffffff1615612eb4576001600160a01b038087166000908152600560205260409020600180820154908601549192612eb09291169089906bffffffffffffffffffffffff166140f1565b9150505b6000612ec08284614fdf565b6040516bffffffffffffffffffffffff8216815290915086906001600160a01b0389169033907f91fb9d98b786c57d74c099ccd2beca1739e9f6a81fb49001ca465c4b7591bbe29060200160405180910390a46040516bffffffffffffffffffffffff8316815286906001600160a01b0389169033907f205442d60b70af1203d43cab62352c3b69b94f091be32fe683198057282b5c929060200160405180910390a46001600160a01b03871660009081526005602052604090206001018054839190601490612fb79084907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff166150d0565b82546101009290920a6bffffffffffffffffffffffff818102199093169183160217909155336000818152602081815260408083206001600160a01b038e168085529083528184208d855290925290912080547fffffffff0000000000000000000000000000000000000000000000000000000016815560010180547fffffffffffffffff0000000000000000000000000000000000000000000000001690556130649350918416613ac6565b50505050505050565b336000908152602081815260408083206001600160a01b038716845282528083208584529091528120805490916bffffffffffffffffffffffff90911690036130e2576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b03841660009081526002602081905260409091205460ff169081600281111561311457613114614bbc565b1461314f5784816040517ff8d3a936000000000000000000000000000000000000000000000000000000008152600401610893929190614f93565b81546c01000000000000000000000000900463ffffffff166000613175338888886142f2565b90508163ffffffff168163ffffffff16116131d4576040517f9e03653a00000000000000000000000000000000000000000000000000000000815263ffffffff8087166004830152808316602483015283166044820152606401610893565b835463ffffffff9091166c01000000000000000000000000027fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff909116179092555050505050565b613224613979565b61322d81614297565b63ffffffff168163ffffffff16141580613252575060035463ffffffff908116908216105b15613291576040517f74af145000000000000000000000000000000000000000000000000000000000815263ffffffff82166004820152602401610893565b600380547fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff1664010000000063ffffffff8416908102919091179091556040519081527fe02644567ab9266166c374f84f05396b070729fc139339e70d0237bb37e59dc59060200161255d565b6001600160a01b0380841660009081526005602052604090206001810154909116613355576040517fce16ea8400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81600003613392576040517f88967d2f00000000000000000000000000000000000000000000000000000000815260048101839052602401610893565b336000908152602081815260408083206001600160a01b038816845282528083208684529091528120805490916bffffffffffffffffffffffff9091169003613407576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805470010000000000000000000000000000000090046bffffffffffffffffffffffff168311156134915780546040517f5f6faf860000000000000000000000000000000000000000000000000000000081527001000000000000000000000000000000009091046bffffffffffffffffffffffff16600482015260248101849052604401610893565b61349b8186613f1c565b8054839082906010906134d190849070010000000000000000000000000000000090046bffffffffffffffffffffffff16614fdf565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550828260000160008282829054906101000a90046bffffffffffffffffffffffff1661352a9190614fdf565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff16021790555083856001600160a01b0316336001600160a01b03167f6043289a72dfdddcba5a5eebd82a24572023a2344a1292dfcf3b56c1a142f6068660405161359e91815260200190565b60405180910390a460018201546135c0906001600160a01b03163330866142b9565b60018201546040517f942c5875000000000000000000000000000000000000000000000000000000008152600481018590526001600160a01b039091169063942c587590602401600060405180830381600087803b15801561362157600080fd5b505af1158015613635573d6000803e3d6000fd5b505050505050505050565b6007546001600160a01b03163314613684576040517fc670608600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000805b825181101561388e5760008382815181106136a5576136a5615004565b602002602001015160000151905060008483815181106136c7576136c7615004565b6020908102919091018101518101516001600160a01b0380851660009081528084526040808220928b16825291845281812083825290935282208054919350916bffffffffffffffffffffffff909116900361374f576040517f411321ed00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60018082015477010000000000000000000000000000000000000000000000900460ff16600381111561378457613784614bbc565b146137e2576001818101546040517f0c278ce100000000000000000000000000000000000000000000000000000000815261089392859277010000000000000000000000000000000000000000000000900460ff16916004016150f5565b6001810180547fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff16770200000000000000000000000000000000000000000000001790558054613840906bffffffffffffffffffffffff16866150d0565b945081876001600160a01b0316846001600160a01b03167f41f6c6b107a872f7e7a62127f1104669af1b4b25a8eba2a4207a8266bd2b2c6460405160405180910390a4505050600101613688565b506001600160a01b03831660009081526008602052604090208054829082906001906138ce90849061010090046bffffffffffffffffffffffff166150d0565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550836001600160a01b03167fd9953834583f8ccc107d531dd2133b07f00bf5c8cebe8f594486930986996c988360405161394991906bffffffffffffffffffffffff91909116815260200190565b60405180910390a2600754612860906001600160a01b0386811691166bffffffffffffffffffffffff8516613ac6565b33613982612282565b6001600160a01b0316146120a7576040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152602401610893565b6001600160a01b0381166000908152600560205260408120906139e68361440b565b8254909150600090613a0e906c01000000000000000000000000900463ffffffff1642615116565b90506000613a228363ffffffff8416615033565b845490915081908590601090613a5a90849070010000000000000000000000000000000090046affffffffffffffffffffff16615133565b82546101009290920a6affffffffffffffffffffff818102199093169190921691909102179055505082547fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff166c010000000000000000000000004263ffffffff160217909255505050565b6040516001600160a01b0383811660248301526044820183905261164891859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505061452c565b613b426145a8565b6121078161460f565b6001600160a01b03831660009081526002602052604081205460ff1690816002811115613b7a57613b7a614bbc565b03613bbc576040517f06439c6b0000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610893565b60008263ffffffff16118015613be357506001816002811115613be157613be1614bbc565b145b15613c1e5783816040517ff8d3a936000000000000000000000000000000000000000000000000000000008152600401610893929190614f93565b6001600160a01b038616613c69576040517f9c64f6a10000000000000000000000000000000000000000000000000000000081526001600160a01b0387166004820152602401610893565b826bffffffffffffffffffffffff16600003613cc1576040517f88967d2f0000000000000000000000000000000000000000000000000000000081526bffffffffffffffffffffffff84166004820152602401610893565b60018054906000613cd183615157565b90915550506001546040516bffffffffffffffffffffffff8516815281906001600160a01b0380881691908a16907ff5681f9d0db1b911ac18ee83d515a1cf1051853a9eae418316a2fdf7dea427c59060200160405180910390a46040518060c00160405280856bffffffffffffffffffffffff168152602001613d57898885886142f2565b63ffffffff16815260006020820181905260408201819052606082018190526080909101526001600160a01b03808916600090815260208181526040808320938a1683529281528282208583528152908290208351815492850151938501516bffffffffffffffffffffffff908116700100000000000000000000000000000000027fffffffff000000000000000000000000ffffffffffffffffffffffffffffffff63ffffffff9096166c010000000000000000000000009081027fffffffffffffffffffffffffffffffff00000000000000000000000000000000909616938316939093179490941794909416929092178155606084015160018201805460808701516affffffffffffffffffffff169094027fffffffffffffffffff000000000000000000000000000000000000000000000090941691909416179190911780835560a08401519192907fffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffffffff1677010000000000000000000000000000000000000000000000836003811115613ef157613ef1614bbc565b02179055506130649150506001600160a01b03861687306bffffffffffffffffffffffff88166142b9565b613f25816139c4565b6001600160a01b03818116600090815260056020908152604091829020825160e08101845281546bffffffffffffffffffffffff80821683526c0100000000000000000000000080830463ffffffff169584019590955270010000000000000000000000000000000082046affffffffffffffffffffff9081169684018790527b01000000000000000000000000000000000000000000000000000000830460ff90811660608601527c0100000000000000000000000000000000000000000000000000000000909304909216608084015260019384015496871660a08401527401000000000000000000000000000000000000000090960490951660c08201529086015490936140679361403e93909204169061518f565b845470010000000000000000000000000000000090046bffffffffffffffffffffffff1661465a565b6001840180546000906140899084906bffffffffffffffffffffffff166150d0565b92506101000a8154816bffffffffffffffffffffffff02191690836bffffffffffffffffffffffff160217905550806040015183600101600c6101000a8154816affffffffffffffffffffff02191690836affffffffffffffffffffff160217905550505050565b600080846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614132573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141569190614f5b565b90506000846001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015614198573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906141bc9190614f5b565b90508060ff168260ff161015614202576141d682826151b3565b6141e190600a6152ec565b6141f9906bffffffffffffffffffffffff86166152fb565b9250505061193b565b8060ff168260ff16111561423d5761421a81836151b3565b61422590600a6152ec565b6141f9906bffffffffffffffffffffffff8616615312565b5091949350505050565b7f237e158222e3e6968b72b9db0d8043aacf074ad9f650f0d1606b4d82ee432c0080547fffffffffffffffffffffffff000000000000000000000000000000000000000016815561180c82614694565b60006142a662093a8083615326565b6142b39062093a8061533d565b92915050565b6040516001600160a01b0384811660248301528381166044830152606482018390526128609186918216906323b872dd90608401613af3565b60008163ffffffff16600003614309575042614403565b600061431483614297565b905063ffffffff81161580614334575060035463ffffffff908116908216105b80614352575060035463ffffffff6401000000009091048116908216115b15614391576040517f8dbf963f00000000000000000000000000000000000000000000000000000000815263ffffffff84166004820152602401610893565b600061439d824261535d565b905084866001600160a01b0316886001600160a01b03167f8b65b80ac62fde507cb8196bad6c93c114c2babc6ac846aae39ed6943ad36c4984866040516143f792919063ffffffff92831681529116602082015260400190565b60405180910390a49150505b949350505050565b6001600160a01b038082166000908152600560209081526040808320815160e08101835281546bffffffffffffffffffffffff808216835263ffffffff6c01000000000000000000000000830416958301959095526affffffffffffffffffffff7001000000000000000000000000000000008204169382019390935260ff7b0100000000000000000000000000000000000000000000000000000084048116606083018190527c010000000000000000000000000000000000000000000000000000000090940416608082015260019091015494851660a08201527401000000000000000000000000000000000000000090940490911660c08401529091906301e133809061452290662386f26fc10000615033565b61193b9190615092565b60006145416001600160a01b0384168361471d565b90508051600014158015614566575080806020019051810190614564919061537a565b155b15611648576040517f5274afe70000000000000000000000000000000000000000000000000000000081526001600160a01b0384166004820152602401610893565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff166120a7576040517fd7e6bcf800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6146176145a8565b6001600160a01b0381166120fe576040517f1e4fbdf700000000000000000000000000000000000000000000000000000000815260006004820152602401610893565b6000670de0b6b3a764000061468a6bffffffffffffffffffffffff84166affffffffffffffffffffff86166152fb565b61193b9190615312565b7f9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c19930080547fffffffffffffffffffffffff000000000000000000000000000000000000000081166001600160a01b03848116918217845560405192169182907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a3505050565b606061193b8383600084600080856001600160a01b031684866040516147439190615397565b60006040518083038185875af1925050503d8060008114614780576040519150601f19603f3d011682016040523d82523d6000602084013e614785565b606091505b509150915061479586838361479f565b9695505050505050565b6060826147b4576147af82614814565b61193b565b81511580156147cb57506001600160a01b0384163b155b1561480d576040517f9996b3150000000000000000000000000000000000000000000000000000000081526001600160a01b0385166004820152602401610893565b508061193b565b8051156148245780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b80356001600160a01b038116811461486d57600080fd5b919050565b60ff8116811461210757600080fd5b6000806000806080858703121561489757600080fd5b6148a085614856565b935060208501356148b081614872565b925060408501356148c081614872565b91506148ce60608601614856565b905092959194509250565b6000604082840312156148eb57600080fd5b50919050565b60006020828403121561490357600080fd5b61193b82614856565b80356bffffffffffffffffffffffff8116811461486d57600080fd5b60008060006060848603121561493d57600080fd5b61494684614856565b92506020840135915061495b6040850161490c565b90509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040805190810167ffffffffffffffff811182821017156149b6576149b6614964565b60405290565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff81118282101715614a0357614a03614964565b604052919050565b600067ffffffffffffffff821115614a2557614a25614964565b5060051b60200190565b80356003811061486d57600080fd5b60006020808385031215614a5157600080fd5b823567ffffffffffffffff811115614a6857600080fd5b8301601f81018513614a7957600080fd5b8035614a8c614a8782614a0b565b6149bc565b81815260069190911b82018301908381019087831115614aab57600080fd5b928401925b82841015614afd5760408489031215614ac95760008081fd5b614ad1614993565b614ada85614856565b8152614ae7868601614a2f565b8187015282526040939093019290840190614ab0565b979650505050505050565b803563ffffffff8116811461486d57600080fd5b600080600060608486031215614b3157600080fd5b614b3a84614856565b9250614b486020850161490c565b915061495b60408501614b08565b60008060408385031215614b6957600080fd5b614b7283614856565b946020939093013593505050565b600080600060608486031215614b9557600080fd5b614b9e84614856565b9250614bac60208501614856565b9150604084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110614bfb57614bfb614bbc565b9052565b600060c0820190506bffffffffffffffffffffffff80845116835263ffffffff6020850151166020840152806040850151166040840152806060850151166060840152506affffffffffffffffffffff608084015116608083015260a0830151614c6c60a0840182614beb565b5092915050565b801515811461210757600080fd5b60008060408385031215614c9457600080fd5b614c9d83614856565b91506020830135614cad81614c73565b809150509250929050565b6bffffffffffffffffffffffff878116825263ffffffff871660208301528581166040830152841660608201526affffffffffffffffffffff8316608082015260c08101614afd60a0830184614beb565b600080600060608486031215614d1e57600080fd5b614d2784614856565b95602085013595506040909401359392505050565b60038110614bfb57614bfb614bbc565b602081016142b38284614d3c565b600080600080600060808688031215614d7257600080fd5b614d7b86614856565b945060208601359350614d9060408701614856565b9250606086013567ffffffffffffffff80821115614dad57600080fd5b818801915088601f830112614dc157600080fd5b813581811115614dd057600080fd5b896020828501011115614de257600080fd5b9699959850939650602001949392505050565b600060208284031215614e0757600080fd5b61193b82614b08565b6000806040808486031215614e2457600080fd5b614e2d84614856565b925060208085013567ffffffffffffffff811115614e4a57600080fd5b8501601f81018713614e5b57600080fd5b8035614e69614a8782614a0b565b81815260069190911b82018301908381019089831115614e8857600080fd5b928401925b82841015614ecf5785848b031215614ea55760008081fd5b614ead614993565b614eb685614856565b8152848601358682015282529285019290840190614e8d565b8096505050505050509250929050565b60008060008060808587031215614ef557600080fd5b614efe85614856565b9350614f0c60208601614856565b9250614f1a6040860161490c565b91506148ce60608601614b08565b600080600060608486031215614f3d57600080fd5b614f4684614856565b92506020840135915061495b60408501614b08565b600060208284031215614f6d57600080fd5b815161193b81614872565b600060208284031215614f8a57600080fd5b61193b82614a2f565b6001600160a01b03831681526040810161193b6020830184614d3c565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6bffffffffffffffffffffffff828116828216039080821115614c6c57614c6c614fb0565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6bffffffffffffffffffffffff81811683821602808216919082811461505b5761505b614fb0565b505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006bffffffffffffffffffffffff808416806150b1576150b1615063565b92169190910492915050565b808201808211156142b3576142b3614fb0565b6bffffffffffffffffffffffff818116838216019080821115614c6c57614c6c614fb0565b838152606081016151096020830185614beb565b6144036040830184614beb565b63ffffffff828116828216039080821115614c6c57614c6c614fb0565b6affffffffffffffffffffff818116838216019080821115614c6c57614c6c614fb0565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361518857615188614fb0565b5060010190565b6affffffffffffffffffffff828116828216039080821115614c6c57614c6c614fb0565b60ff82811682821603908111156142b3576142b3614fb0565b600181815b8085111561522557817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0482111561520b5761520b614fb0565b8085161561521857918102915b93841c93908002906151d1565b509250929050565b60008261523c575060016142b3565b81615249575060006142b3565b816001811461525f576002811461526957615285565b60019150506142b3565b60ff84111561527a5761527a614fb0565b50506001821b6142b3565b5060208310610133831016604e8410600b84101617156152a8575081810a6142b3565b6152b283836151cc565b807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211156152e4576152e4614fb0565b029392505050565b600061193b60ff84168361522d565b80820281158282048414176142b3576142b3614fb0565b60008261532157615321615063565b500490565b600063ffffffff808416806150b1576150b1615063565b63ffffffff81811683821602808216919082811461505b5761505b614fb0565b63ffffffff818116838216019080821115614c6c57614c6c614fb0565b60006020828403121561538c57600080fd5b815161193b81614c73565b6000825160005b818110156153b8576020818601810151858301520161539e565b50600092019182525091905056fea26469706673582212204f4c8738f189afb9d4789abd80b92b01657882f015929fba454de37d775515b164736f6c63430008180033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.