Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
Contract Name:
AccountV3
Compiler Version
v0.8.30+commit.73712a01
Optimization Enabled:
Yes with 200 runs
Other Settings:
prague EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.30;
import { AccountErrors } from "../libraries/Errors.sol";
import { AccountStorageV1 } from "./AccountStorageV1.sol";
import { ActionData, IActionBase } from "../interfaces/IActionBase.sol";
import { AssetValuationLib, AssetValueAndRiskFactors } from "../libraries/AssetValuationLib.sol";
import { ERC20, SafeTransferLib } from "../../lib/solmate/src/utils/SafeTransferLib.sol";
import { IAccount } from "../interfaces/IAccount.sol";
import { IAccountsGuard } from "../interfaces/IAccountsGuard.sol";
import { IAssetManager } from "../interfaces/IAssetManager.sol";
import { ICreditor } from "../interfaces/ICreditor.sol";
import { IDistributor } from "../interfaces/IDistributor.sol";
import { IERC721 } from "../interfaces/IERC721.sol";
import { IERC1155 } from "../interfaces/IERC1155.sol";
import { IFactory } from "../interfaces/IFactory.sol";
import { IMerklOperator } from "../interfaces/IMerklOperator.sol";
import { IPermit2 } from "../interfaces/IPermit2.sol";
import { IRegistry } from "../interfaces/IRegistry.sol";
/**
* @title Arcadia Accounts
* @author Pragma Labs
* @notice Arcadia Accounts are smart contracts that act as onchain, decentralized and composable margin accounts.
* They provide individuals, DAOs, and other protocols with a simple and flexible way to deposit and manage multiple assets as collateral.
* The total combination of assets can be used as margin to back liabilities issued by any financial protocol (lending, leverage, futures...).
* @dev Users can use this Account to deposit assets (fungible, non-fungible, LP positions, yield bearing assets...).
* The Account will denominate all the deposited assets into one Numeraire (one unit of account, like USD or ETH).
* Users can use the single denominated value of all their assets to take margin (take credit line, financing for leverage...).
* An increase of value of one asset will offset a decrease in value of another asset.
* Ensure your total value denomination remains above the liquidation threshold, or risk being liquidated!
* @dev Integrating this Account as means of margin/collateral management for your own protocol that requires collateral is encouraged.
* Arcadia's Account functions will guarantee you a certain value of the Account.
* For allowlists or liquidation strategies specific to your protocol, contact pragmalabs.dev
*/
contract AccountV3 is AccountStorageV1, IAccount {
using AssetValuationLib for AssetValueAndRiskFactors[];
using SafeTransferLib for ERC20;
/* //////////////////////////////////////////////////////////////
CONSTANTS
////////////////////////////////////////////////////////////// */
// Bools indicating if the function being called can be paused by the AccountsGuard.
bool internal constant WITH_PAUSE_CHECK = true;
bool internal constant WITHOUT_PAUSE_CHECK = false;
// The current Account Version.
uint256 public constant ACCOUNT_VERSION = 3;
// The maximum amount of different assets that can be used as collateral within an Arcadia Account.
uint256 public constant ASSET_LIMIT = 15;
// The cool-down period after an account action, that might be disadvantageous for a new Owner,
// during which ownership cannot be transferred to prevent the old Owner from frontrunning a transferFrom().
uint256 internal constant COOL_DOWN_PERIOD = 5 minutes;
// Storage slot with the address of the current implementation.
// This is the hardcoded keccak-256 hash of: "eip1967.proxy.implementation" subtracted by 1.
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
// The contract address of the Arcadia Accounts Factory.
address public immutable FACTORY;
// The contract address of the Accounts Guard.
IAccountsGuard public immutable ACCOUNTS_GUARD;
// The contract address of the Merkl Distributor.
IDistributor public immutable MERKL_DISTRIBUTOR;
// Uniswap Permit2 contract.
IPermit2 internal immutable PERMIT2 = IPermit2(0x000000000022D473030F116dDEE9F6B43aC78BA3);
// Storage slot for the Account implementation, a struct to avoid storage conflict when dealing with upgradeable contracts.
struct AddressSlot {
address value;
}
/* //////////////////////////////////////////////////////////////
EVENTS
////////////////////////////////////////////////////////////// */
event AssetManagerSet(address indexed owner, address indexed assetManager, bool status);
event MarginAccountChanged(address indexed creditor, address indexed liquidator);
event MerklOperatorSet(address indexed merklOperator, bool status);
event NumeraireSet(address indexed numeraire);
event Skim(address indexed from, address indexed to, address asset, uint256 id, uint256 amount, uint256 type_);
event Transfers(
address indexed from, address indexed to, address[] assets, uint256[] ids, uint256[] amounts, uint256[] types
);
/* //////////////////////////////////////////////////////////////
MODIFIERS
////////////////////////////////////////////////////////////// */
/**
* @param pauseCheck Bool indicating if a pause check should be done.
* @dev Throws if cross accounts guard is reentered or paused.
* @dev Locks/unlocks the cross accounts guard before/after the function is executed.
*/
modifier nonReentrant(bool pauseCheck) {
ACCOUNTS_GUARD.lock(pauseCheck);
_;
ACCOUNTS_GUARD.unLock();
}
/**
* @dev Throws if called when the Account is in an auction.
*/
modifier notDuringAuction() {
if (inAuction) revert AccountErrors.AccountInAuction();
_;
}
/**
* @dev Throws if called by any address other than an Asset Manager or the owner.
*/
modifier onlyAssetManager() {
// A custom error would need to read out owner + isAssetManager storage
require(msg.sender == owner || isAssetManager[owner][msg.sender], "A: Only Asset Manager");
_;
}
/**
* @dev Throws if called by any address other than the Creditor.
*/
modifier onlyCreditor() {
if (msg.sender != creditor) revert AccountErrors.OnlyCreditor();
_;
}
/**
* @dev Throws if called by any address other than the Factory address.
*/
modifier onlyFactory() {
if (msg.sender != FACTORY) revert AccountErrors.OnlyFactory();
_;
}
/**
* @dev Throws if called by any address other than the Liquidator address.
*/
modifier onlyLiquidator() {
if (msg.sender != liquidator) revert AccountErrors.OnlyLiquidator();
_;
}
/**
* @dev Throws if called by any address other than the owner.
*/
modifier onlyOwner() {
if (msg.sender != owner) revert AccountErrors.OnlyOwner();
_;
}
/**
* @dev Starts the cool-down period during which ownership cannot be transferred.
* This prevents the old Owner from frontrunning a transferFrom().
*/
modifier updateActionTimestamp() {
lastActionTimestamp = uint32(block.timestamp);
_;
}
/* //////////////////////////////////////////////////////////////
CONSTRUCTOR
////////////////////////////////////////////////////////////// */
/**
* @param factory The contract address of the Arcadia Accounts Factory.
* @param accountsGuard The contract address of the Accounts Guard.
* @param merklDistributor The contract address of the Merkl Distributor.
*/
constructor(address factory, address accountsGuard, address merklDistributor) {
// This will only be the owner of the Account implementation.
// and will not affect any subsequent proxy implementation using this Account implementation.
owner = msg.sender;
FACTORY = factory;
ACCOUNTS_GUARD = IAccountsGuard(accountsGuard);
MERKL_DISTRIBUTOR = IDistributor(merklDistributor);
}
/* ///////////////////////////////////////////////////////////////
ACCOUNT MANAGEMENT
/////////////////////////////////////////////////////////////// */
/**
* @notice Initiates the variables of the Account.
* @param owner_ The sender of the 'createAccount' on the Factory
* @param registry_ The 'beacon' contract with the external logic to price assets.
* @param creditor_ The contract address of the Creditor.
* @dev A proxy will be used to interact with the Account implementation.
* This function will only be called (once) in the same transaction as the proxy Account creation through the Factory.
* @dev The Creditor will only be set if it's a non-zero address, in this case the numeraire_ passed as input will be ignored.
*/
function initialize(address owner_, address registry_, address creditor_)
external
onlyFactory
nonReentrant(WITH_PAUSE_CHECK)
{
if (registry_ == address(0)) revert AccountErrors.InvalidRegistry();
owner = owner_;
registry = registry_;
if (creditor_ != address(0)) _openMarginAccount(creditor_);
}
/**
* @notice Upgrades the Account version and stores a new address in the EIP1967 implementation slot.
* @param newImplementation The new contract address of the Account implementation.
* @param newRegistry The Registry for this specific implementation (might be identical to the old registry).
* @param data Arbitrary data, can contain instructions to execute when updating Account to new implementation.
* @param newVersion The new version of the Account implementation.
* @dev This function MUST be added to new Account implementations.
*/
function upgradeAccount(address newImplementation, address newRegistry, uint256 newVersion, bytes calldata data)
external
onlyFactory
nonReentrant(WITHOUT_PAUSE_CHECK)
notDuringAuction
updateActionTimestamp
{
// Cache old parameters.
address oldImplementation = _getAddressSlot(IMPLEMENTATION_SLOT).value;
address oldRegistry = registry;
uint256 oldVersion = ACCOUNT_VERSION;
// Store new parameters.
_getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
registry = newRegistry;
// Prevent that Account is upgraded to a new version where the Numeraire can't be priced.
if (newRegistry != oldRegistry && !IRegistry(newRegistry).inRegistry(numeraire)) {
revert AccountErrors.InvalidRegistry();
}
// If a Creditor is set, new version should be compatible.
if (creditor != address(0)) {
(bool success,,,) = ICreditor(creditor).openMarginAccount(newVersion);
if (!success) revert AccountErrors.InvalidAccountVersion();
}
// Hook on the new logic to finalize upgrade.
// Used to eg. Remove exposure from old Registry and add exposure to the new RegistryL2.
// Extra data can be added by the Factory for complex instructions.
this.upgradeHook(oldImplementation, oldRegistry, oldVersion, data);
// Event emitted by Factory.
}
/**
* @notice Returns the "AddressSlot" with member "value" located at "slot".
* @param slot The slot where the address of the Logic contract is stored.
* @return r The address stored in slot.
*/
function _getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
assembly {
r.slot := slot
}
}
/**
* @notice Finalizes the Upgrade to a new Account version on the new implementation Contract.
* @param oldImplementation The old contract address of the Account implementation.
* @param oldRegistry The Registry of the old version (might be identical to the new registry)
* @param oldVersion The old version of the Account implementation.
* @param data Arbitrary data, can contain instructions to execute in this function.
* @dev If upgradeHook() is implemented, it MUST verify that msg.sender == address(this).
*/
function upgradeHook(address oldImplementation, address oldRegistry, uint256 oldVersion, bytes calldata data)
external { }
/* ///////////////////////////////////////////////////////////////
OWNERSHIP MANAGEMENT
/////////////////////////////////////////////////////////////// */
/**
* @notice Transfers ownership of the contract to a new Account.
* @param newOwner The new owner of the Account.
* @dev Can only be called by the current owner via the Factory.
* A transfer of ownership of the Account is triggered by a transfer
* of ownership of the accompanying ERC721 Account NFT, issued by the Factory.
* Owner of Account NFT = owner of Account
* @dev Function uses a cool-down period during which ownership cannot be transferred.
* Cool-down period is triggered after any account action, that might be disadvantageous for a new Owner.
* This prevents the old Owner from frontrunning a transferFrom().
*/
function transferOwnership(address newOwner)
external
onlyFactory
nonReentrant(WITHOUT_PAUSE_CHECK)
notDuringAuction
{
if (block.timestamp <= lastActionTimestamp + COOL_DOWN_PERIOD) {
revert AccountErrors.CoolDownPeriodNotPassed();
}
// The Factory will check that the new owner is not address(0).
owner = newOwner;
}
function _transferOwnership(address newOwner) internal {
// The Factory will check that the new owner is not address(0).
owner = newOwner;
IFactory(FACTORY).safeTransferAccount(newOwner);
}
/* ///////////////////////////////////////////////////////////////
NUMERAIRE LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Sets the Numeraire of the Account.
* @param numeraire_ The new Numeraire for the Account.
*/
function setNumeraire(address numeraire_) external onlyOwner nonReentrant(WITH_PAUSE_CHECK) {
if (creditor != address(0)) revert AccountErrors.CreditorAlreadySet();
_setNumeraire(numeraire_);
}
/**
* @notice Sets the Numeraire of the Account.
* @param numeraire_ The new Numeraire for the Account.
*/
function _setNumeraire(address numeraire_) internal {
if (!IRegistry(registry).inRegistry(numeraire_)) revert AccountErrors.NumeraireNotFound();
emit NumeraireSet(numeraire = numeraire_);
}
/* ///////////////////////////////////////////////////////////////
MARGIN ACCOUNT SETTINGS
/////////////////////////////////////////////////////////////// */
/**
* @notice Opens a margin account on the Account for a new Creditor.
* @param newCreditor The contract address of the Creditor.
* @dev Currently only one Creditor can be set
* (we are working towards a single account for multiple Creditors tho!).
* @dev Only open margin accounts for Creditors you trust!
* The Creditor has significant authorization: use margin, trigger liquidation, and manage assets.
*/
function openMarginAccount(address newCreditor)
external
onlyOwner
nonReentrant(WITH_PAUSE_CHECK)
notDuringAuction
updateActionTimestamp
{
(address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
generateAssetData();
// Cache old Creditor.
address oldCreditor = creditor;
if (oldCreditor == newCreditor) revert AccountErrors.CreditorAlreadySet();
// Remove the exposures of the Account for the old Creditor.
if (oldCreditor != address(0)) {
IRegistry(registry).batchProcessWithdrawal(oldCreditor, assetAddresses, assetIds, assetAmounts);
}
// Check if all assets in the Account are allowed by the new Creditor
// and add the exposure of the account for the new Creditor.
IRegistry(registry).batchProcessDeposit(newCreditor, assetAddresses, assetIds, assetAmounts);
// Open margin account for the new Creditor.
_openMarginAccount(newCreditor);
// A margin account can only be opened for one Creditor at a time.
// If set, close the margin account for the old Creditor.
if (oldCreditor != address(0)) {
// closeMarginAccount() checks if there is still an open position (open liabilities) of the Account for the old Creditor.
// If so, the function reverts.
ICreditor(oldCreditor).closeMarginAccount(address(this));
}
}
/**
* @notice Internal function: Opens a margin account for a new Creditor.
* @param creditor_ The contract address of the Creditor.
*/
function _openMarginAccount(address creditor_) internal {
(bool success, address numeraire_, address liquidator_, uint256 minimumMargin_) =
ICreditor(creditor_).openMarginAccount(ACCOUNT_VERSION);
if (!success) revert AccountErrors.InvalidAccountVersion();
// forge-lint: disable-next-line(unsafe-typecast)
minimumMargin = uint96(minimumMargin_);
if (numeraire != numeraire_) _setNumeraire(numeraire_);
emit MarginAccountChanged(creditor = creditor_, liquidator = liquidator_);
}
/**
* @notice Closes the margin account of the Creditor.
* @dev Currently only one Creditor can be set.
*/
function closeMarginAccount() external onlyOwner nonReentrant(WITHOUT_PAUSE_CHECK) notDuringAuction {
// Cache creditor.
address creditor_ = creditor;
if (creditor_ == address(0)) revert AccountErrors.CreditorNotSet();
creditor = address(0);
liquidator = address(0);
minimumMargin = 0;
// Remove the exposures of the Account for the old Creditor.
(address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
generateAssetData();
IRegistry(registry).batchProcessWithdrawal(creditor_, assetAddresses, assetIds, assetAmounts);
// closeMarginAccount() checks if there is still an open position (open liabilities) for the Account.
// If so, the function reverts.
ICreditor(creditor_).closeMarginAccount(address(this));
emit MarginAccountChanged(address(0), address(0));
}
/**
* @notice Sets an approved Creditor.
* @param creditor_ The contract address of the approved Creditor.
* @dev An approved Creditor is a Creditor for which no margin Account is immediately opened.
* But the approved Creditor itself can open the margin Account later in time to e.g. refinance liabilities.
* @dev Potential use-cases of the approved Creditor might be to:
* - Refinance liabilities (change creditor) without having to sell collateral to close the current position first.
* @dev Anyone can set the approved creditor for themselves, this will not impact the current owner of the Account
* since the combination of "current owner -> approved creditor" is used in authentication checks.
* This guarantees that when the ownership of the Account is transferred, the approved Creditor of the old owner has no
* impact on the new owner. But the new owner can still remove any existing approved Creditors before the transfer.
*/
function setApprovedCreditor(address creditor_) external nonReentrant(WITH_PAUSE_CHECK) {
approvedCreditor[msg.sender] = creditor_;
}
/* ///////////////////////////////////////////////////////////////
MARGIN REQUIREMENTS
/////////////////////////////////////////////////////////////// */
/**
* @notice Calculates the total collateral value (MTM discounted with a haircut) of the Account.
* @return collateralValue The collateral value, returned in the decimal precision of the Numeraire.
* @dev Returns the value denominated in the Numeraire of the Account.
* @dev The collateral value of the Account is equal to the spot value of the underlying assets,
* discounted by a haircut (the collateral factor). Since the value of
* collateralized assets can fluctuate, the haircut guarantees that the Account
* remains over-collateralized with a high confidence level.
* The size of the haircut depends on the underlying risk of the assets in the Account.
* The bigger the volatility or the smaller the onchain liquidity, the bigger the haircut will be.
*/
function getCollateralValue() public view returns (uint256 collateralValue) {
(address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
generateAssetData();
collateralValue =
IRegistry(registry).getCollateralValue(numeraire, creditor, assetAddresses, assetIds, assetAmounts);
}
/**
* @notice Calculates the total liquidation value (MTM discounted with a factor to account for slippage) of the Account.
* @return liquidationValue The liquidation value, returned in the decimal precision of the Numeraire.
* @dev The liquidation value of the Account is equal to the spot value of the underlying assets,
* discounted by a haircut (the liquidation factor).
* The liquidation value takes into account that not the full value of the assets can go towards
* repaying the liabilities: a fraction of the value is lost due to:
* slippage while liquidating the assets,
* fees for the auction initiator,
* fees for the auction terminator and
* a penalty to the protocol.
*/
function getLiquidationValue() public view returns (uint256 liquidationValue) {
(address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
generateAssetData();
liquidationValue =
IRegistry(registry).getLiquidationValue(numeraire, creditor, assetAddresses, assetIds, assetAmounts);
}
/**
* @notice Returns the used margin of the Account.
* @return usedMargin The total amount of margin that is currently in use to back liabilities.
* @dev Used Margin is the value of the assets that is currently 'locked' to back:
* - All the liabilities issued against the Account.
* - An additional fixed buffer to cover gas fees in case of a liquidation.
* @dev The used margin is denominated in the Numeraire.
* @dev Currently only one Creditor at a time can open a margin account.
* The open liability is fetched at the contract of the Creditor -> only allow trusted audited Creditors!!!
*/
function getUsedMargin() public view returns (uint256 usedMargin) {
// Cache creditor
address creditor_ = creditor;
if (creditor_ == address(0)) return 0;
// getOpenPosition() is a view function, cannot modify state.
usedMargin = ICreditor(creditor_).getOpenPosition(address(this)) + minimumMargin;
}
/**
* @notice Calculates the remaining margin the owner of the Account can use.
* @return freeMargin The remaining amount of margin a user can take.
* @dev Free Margin is the value of the assets that is still free to back additional liabilities.
* @dev The free margin is denominated in the Numeraire.
*/
function getFreeMargin() public view returns (uint256 freeMargin) {
uint256 collateralValue = getCollateralValue();
uint256 usedMargin = getUsedMargin();
unchecked {
freeMargin = collateralValue > usedMargin ? collateralValue - usedMargin : 0;
}
}
/**
* @notice Checks if the Account is unhealthy.
* @return isUnhealthy Boolean indicating if the Account is unhealthy.
*/
function isAccountUnhealthy() public view returns (bool isUnhealthy) {
// If usedMargin is equal to minimumMargin, the open liabilities are 0 and the Account is always healthy.
// An Account is unhealthy if the collateral value is smaller than the used margin.
uint256 usedMargin = getUsedMargin();
isUnhealthy = usedMargin > minimumMargin && getCollateralValue() < usedMargin;
}
/**
* @notice Checks if the Account can be liquidated.
* @return success Boolean indicating if the Account can be liquidated.
*/
function isAccountLiquidatable() external view returns (bool success) {
// If usedMargin is equal to minimumMargin, the open liabilities are 0 and the Account is never liquidatable.
// An Account can be liquidated if the liquidation value is smaller than the used margin.
uint256 usedMargin = getUsedMargin();
success = usedMargin > minimumMargin && getLiquidationValue() < usedMargin;
}
/* ///////////////////////////////////////////////////////////////
LIQUIDATION LOGIC
/////////////////////////////////////////////////////////////// */
/**
* @notice Checks if an Account is liquidatable and continues the liquidation flow.
* @param initiator The address of the liquidation initiator.
* @return assetAddresses Array of the contract addresses of the assets in Account.
* @return assetIds Array of the IDs of the assets in Account.
* @return assetAmounts Array with the amounts of the assets in Account.
* @return creditor_ The contract address of the Creditor.
* @return minimumMargin_ The minimum margin.
* @return openPosition The open position (liabilities) issued against the Account.
* @return assetAndRiskValues Array of asset values and corresponding collateral and liquidation factors.
*/
function startLiquidation(address initiator)
external
onlyLiquidator
nonReentrant(WITHOUT_PAUSE_CHECK)
updateActionTimestamp
returns (
address[] memory assetAddresses,
uint256[] memory assetIds,
uint256[] memory assetAmounts,
address creditor_,
uint96 minimumMargin_,
uint256 openPosition,
AssetValueAndRiskFactors[] memory assetAndRiskValues
)
{
inAuction = true;
creditor_ = creditor;
minimumMargin_ = minimumMargin;
(assetAddresses, assetIds, assetAmounts) = generateAssetData();
assetAndRiskValues =
IRegistry(registry).getValuesInNumeraire(numeraire, creditor_, assetAddresses, assetIds, assetAmounts);
// Since the function is only callable by the Liquidator, we know that a liquidator and a Creditor are set.
openPosition = ICreditor(creditor_).startLiquidation(initiator, minimumMargin_);
uint256 usedMargin = openPosition + minimumMargin_;
if (openPosition == 0 || assetAndRiskValues._calculateLiquidationValue() >= usedMargin) {
revert AccountErrors.AccountNotLiquidatable();
}
}
/**
* @notice Transfers the asset bought by a bidder during a liquidation event.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @param bidder The address of the bidder.
* @return assetAmounts_ Array with the actual transferred amounts of assets.
*/
function auctionBid(
address[] memory assetAddresses,
uint256[] memory assetIds,
uint256[] memory assetAmounts,
address bidder
) external onlyLiquidator nonReentrant(WITHOUT_PAUSE_CHECK) returns (uint256[] memory assetAmounts_) {
uint256[] memory assetTypes = IRegistry(registry).batchGetAssetTypes(assetAddresses);
uint256 balance;
for (uint256 i; i < assetAddresses.length; ++i) {
// Skip if amount is 0.
if (assetAmounts[i] == 0) continue;
if (assetTypes[i] == 1) {
balance = erc20Balances[assetAddresses[i]];
} else if (assetTypes[i] == 2) {
balance = (address(this) == IERC721(assetAddresses[i]).ownerOf(assetIds[i])) ? 1 : 0;
} else if (assetTypes[i] == 3) {
balance = erc1155Balances[assetAddresses[i]][assetIds[i]];
} else {
revert AccountErrors.UnknownAssetType();
}
if (assetAmounts[i] > balance) assetAmounts[i] = balance;
}
_withdraw(assetAddresses, assetIds, assetAmounts, bidder);
// Return the actual withdrawn amounts.
assetAmounts_ = assetAmounts;
}
/**
* @notice Transfers all assets of the Account in case the auction did not end successful (= Bought In).
* @param recipient The recipient address to receive the assets, set by the Creditor.
* @dev When an auction is not successful, the Account is considered "Bought In":
* The whole Account including any remaining assets are transferred to a certain recipient address, set by the Creditor.
*/
function auctionBoughtIn(address recipient) external onlyLiquidator nonReentrant(WITHOUT_PAUSE_CHECK) {
_transferOwnership(recipient);
}
/**
* @notice Sets the "inAuction" flag to false when an auction ends.
*/
function endAuction() external onlyLiquidator nonReentrant(WITHOUT_PAUSE_CHECK) {
inAuction = false;
}
/*///////////////////////////////////////////////////////////////
ASSET MANAGER ACTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Removes an Asset Manager.
* @param assetManager The address of the Asset Manager.
* @dev Anyone can remove an Asset Manager for themselves, this will not impact the current owner of the Account
* since the combination of "stored owner -> asset manager" is used in authentication checks.
* This guarantees that when the ownership of the Account is transferred, the asset managers of the old owner have no
* impact on the new owner. But the new owner can still remove any existing asset managers before the transfer.
*/
function removeAssetManager(address assetManager) external nonReentrant(WITHOUT_PAUSE_CHECK) {
emit AssetManagerSet(msg.sender, assetManager, isAssetManager[msg.sender][assetManager] = false);
}
/**
* @notice Adds, removes or modifies Asset Managers.
* @param assetManagers Array of Asset Managers.
* @param statuses Array of Bools indicating if the corresponding Asset Manager should be enabled or disabled.
* @param datas Array of calldata optionally passed to the corresponding Asset Manager via hook.
* @dev Only set trusted addresses as Asset Manager. Asset Managers have full control over assets in the Account.
* @dev No need to set the Owner as Asset Manager as they will automatically have all permissions of an Asset Manager.
* @dev Potential use-cases of the Asset Manager might be to:
* - Liquidity Management.
* - Do flash actions (optimistic actions).
* - Compounding.
* - Chain multiple interactions together.
*/
function setAssetManagers(address[] calldata assetManagers, bool[] calldata statuses, bytes[] calldata datas)
external
onlyOwner
nonReentrant(WITH_PAUSE_CHECK)
updateActionTimestamp
{
if (assetManagers.length != statuses.length || assetManagers.length != datas.length) {
revert AccountErrors.LengthMismatch();
}
address assetManager;
for (uint256 i; i < assetManagers.length; ++i) {
assetManager = assetManagers[i];
emit AssetManagerSet(msg.sender, assetManager, isAssetManager[msg.sender][assetManager] = statuses[i]);
// Optionally call Hook on the Asset Manager to initialize/update it.
if (datas[i].length > 0) {
IAssetManager(assetManager).onSetAssetManager(msg.sender, statuses[i], datas[i]);
}
}
}
/**
* @notice Executes a flash action.
* @param actionTarget The contract address of the actionTarget to execute external logic.
* @param actionData A bytes object containing three structs and two bytes objects.
* The first struct contains the info about the assets to withdraw from this Account to the actionTarget.
* The second struct contains the info about the owner's assets that need to be transferred from the owner to the actionTarget.
* The third struct contains the permit for the Permit2 transfer.
* The first bytes object contains the signature for the Permit2 transfer.
* The second bytes object contains the encoded input for the actionTarget.
* @dev This function optimistically chains multiple actions together (= do a flash action):
* - It can optimistically withdraw assets from the Account to the actionTarget.
* - It can transfer assets directly from the owner to the actionTarget.
* - It can execute external logic on the actionTarget, and interact with any DeFi protocol to swap, stake, claim...
* - It can deposit all recipient tokens from the actionTarget back into the Account.
* At the very end of the flash action, the following check is performed:
* - The Account is in a healthy state (collateral value is greater than open liabilities).
* If a check fails, the whole transaction reverts.
*/
function flashAction(address actionTarget, bytes calldata actionData)
external
onlyAssetManager
nonReentrant(WITH_PAUSE_CHECK)
notDuringAuction
updateActionTimestamp
{
// Decode flash action data.
(
ActionData memory withdrawData,
ActionData memory transferFromOwnerData,
IPermit2.PermitBatchTransferFrom memory permit,
bytes memory signature,
bytes memory actionTargetData
) = abi.decode(actionData, (ActionData, ActionData, IPermit2.PermitBatchTransferFrom, bytes, bytes));
// Withdraw assets to the actionTarget.
_withdraw(withdrawData.assets, withdrawData.assetIds, withdrawData.assetAmounts, actionTarget);
// Transfer assets from owner (that are not assets in this account) to the actionTarget.
_transferFromOwner(transferFromOwnerData, actionTarget);
// If the function input includes non-empty token permissions,
// initiate a transfer from the owner to the actionTarget via Permit2.
_transferFromOwnerWithPermit(permit, signature, actionTarget);
// Execute external logic on the actionTarget.
ActionData memory depositData = IActionBase(actionTarget).executeAction(actionTargetData);
// Deposit assets from actionTarget into Account.
_deposit(depositData.assets, depositData.assetIds, depositData.assetAmounts, actionTarget);
// Account must be healthy after actions are executed.
if (isAccountUnhealthy()) revert AccountErrors.AccountUnhealthy();
}
/**
* @notice Removes a Merkl Operator.
* @param operator The merkl operator.
*/
function removeMerklOperator(address operator) external onlyOwner nonReentrant(WITHOUT_PAUSE_CHECK) {
bool enabled = MERKL_DISTRIBUTOR.operators(address(this), operator) > 0;
if (enabled) MERKL_DISTRIBUTOR.toggleOperator(address(this), operator);
emit MerklOperatorSet(operator, false);
}
/**
* @notice Manages Merkl Operators.
* @param operators Array of merkl operators.
* @param operatorStatuses Array of Bools indicating if the corresponding operator should be enabled or disabled.
* @param operatorDatas Array of calldata optionally passed to the corresponding merkl operator via hook.
* @param recipient The address of the recipient of the merkl rewards for each of the tokens.
* @param tokens Array of tokens for which the recipient will be set.
* @dev A Merkl Operator can claim any pending merkl rewards on behalf of the Account.
* @dev The recipient will receive the Merkl rewards and only one recipient can be set per token.
* The Recipient can be the operator itself, the Account itself, the Account owner or any other address.
* It is up to the Account owner to add/remove/change recipient for some/all tokens when adding/removing/changing operators.
* @dev The Operator and recipients are NOT reset when transferring ownership of the Account.
* Since the Account is not involved in the claiming flow, a check on owner must be implemented in the Operator.
*/
function setMerklOperators(
address[] calldata operators,
bool[] calldata operatorStatuses,
bytes[] calldata operatorDatas,
address recipient,
address[] calldata tokens
) external onlyOwner nonReentrant(WITH_PAUSE_CHECK) updateActionTimestamp {
if (operators.length != operatorStatuses.length || operators.length != operatorDatas.length) {
revert AccountErrors.LengthMismatch();
}
address operator;
bool currentStatus;
for (uint256 i; i < operators.length; ++i) {
operator = operators[i];
// If the current status is different from the desired status, toggle the status of the operator.
currentStatus = MERKL_DISTRIBUTOR.operators(address(this), operator) > 0;
if (operatorStatuses[i] != currentStatus) MERKL_DISTRIBUTOR.toggleOperator(address(this), operator);
// Optionally call Hook on the Operator to initialize/update it.
if (operatorDatas[i].length > 0) {
IMerklOperator(operator).onSetMerklOperator(msg.sender, operatorStatuses[i], operatorDatas[i]);
}
emit MerklOperatorSet(operator, operatorStatuses[i]);
}
// If provided, set recipient for tokens.
for (uint256 j; j < tokens.length; ++j) {
MERKL_DISTRIBUTOR.setClaimRecipient(recipient, tokens[j]);
}
}
/*///////////////////////////////////////////////////////////////
CREDITOR ACTIONS
///////////////////////////////////////////////////////////////*/
/**
* @notice Checks that the increase of the open position is allowed.
* @param openPosition The new open position.
* @return accountVersion The current Account version.
* @dev The Account performs the following checks when an open position (liabilities) are increased:
* - The caller is indeed a Creditor for which a margin account is opened.
* - The Account is still healthy after given the new open position.
*/
function increaseOpenPosition(uint256 openPosition)
external
onlyCreditor
nonReentrant(WITH_PAUSE_CHECK)
notDuringAuction
updateActionTimestamp
returns (uint256 accountVersion)
{
// If the open position is 0, the Account is always healthy.
// An Account is unhealthy if the collateral value is smaller than the used margin.
// The used margin equals the sum of the given open position and the minimum margin.
if (openPosition > 0 && getCollateralValue() < openPosition + minimumMargin) {
revert AccountErrors.AccountUnhealthy();
}
accountVersion = ACCOUNT_VERSION;
}
/**
* @notice Executes a flash action initiated by a Creditor.
* @param callbackData A bytes object containing the data to execute arbitrary logic on the Creditor.
* @param actionTarget The contract address of the actionTarget to execute external logic.
* @param actionData A bytes object containing three structs and two bytes objects.
* The first struct contains the info about the assets to withdraw from this Account to the actionTarget.
* The second struct contains the info about the owner's assets that need to be transferred from the owner to the actionTarget.
* The third struct contains the permit for the Permit2 transfer.
* The first bytes object contains the signature for the Permit2 transfer.
* The second bytes object contains the encoded input for the actionTarget.
* @return accountVersion The current Account version.
* @dev This function optimistically chains multiple actions together (= do a flash action):
* - A margin Account can be opened for a new Creditor, if the new Creditor is approved by the Account Owner.
* - It can execute arbitrary logic on the Creditor (e.g. give a flashloan to the actionTarget).
* - It can optimistically withdraw assets from the Account to the actionTarget.
* - It can transfer assets directly from the owner to the actionTarget.
* - It can execute external logic on the actionTarget, and interact with any DeFi protocol to swap, stake, claim...
* - It can deposit all recipient tokens from the actionTarget back into the Account.
* At the very end of the flash action, the following checks are performed:
* - The Account is in a healthy state (collateral value is greater than open liabilities).
* - If a new margin Account is opened for a new Creditor, then the Account has no open positions anymore with the old Creditor.
* If a check fails, the whole transaction reverts.
* @dev This function can be used to refinance liabilities between different Creditors,
* without the need to first sell collateral to close the open position of the old Creditor.
*/
function flashActionByCreditor(bytes calldata callbackData, address actionTarget, bytes calldata actionData)
external
nonReentrant(WITH_PAUSE_CHECK)
notDuringAuction
updateActionTimestamp
returns (uint256 accountVersion)
{
// Cache the current Creditor.
address currentCreditor = creditor;
// The caller has to be or the Creditor of the Account, or an approved Creditor.
if (msg.sender != currentCreditor && msg.sender != approvedCreditor[owner]) {
revert AccountErrors.OnlyCreditor();
}
// Decode flash action data.
ActionData memory transferFromOwnerData;
IPermit2.PermitBatchTransferFrom memory permit;
bytes memory signature;
bytes memory actionTargetData;
{
ActionData memory withdrawData;
(withdrawData, transferFromOwnerData, permit, signature, actionTargetData) =
abi.decode(actionData, (ActionData, ActionData, IPermit2.PermitBatchTransferFrom, bytes, bytes));
// Callback to execute external logic on the Creditor.
ICreditor(msg.sender).flashActionCallback(callbackData);
// Withdraw assets to the actionTarget.
_withdraw(withdrawData.assets, withdrawData.assetIds, withdrawData.assetAmounts, actionTarget);
}
if (msg.sender != currentCreditor) {
// If the caller is the approved Creditor, a margin Account must be opened for the approved Creditor.
// And the exposures for the current and approved Creditors need to be updated.
approvedCreditor[owner] = address(0);
(address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
generateAssetData();
// Remove the exposures of the Account for the current Creditor.
if (currentCreditor != address(0)) {
IRegistry(registry).batchProcessWithdrawal(currentCreditor, assetAddresses, assetIds, assetAmounts);
}
// Check if all assets in the Account are allowed by the approved Creditor
// and add the exposure of the account for the approved Creditor.
IRegistry(registry).batchProcessDeposit(msg.sender, assetAddresses, assetIds, assetAmounts);
// Open margin account for the approved Creditor.
_openMarginAccount(msg.sender);
}
// Transfer assets from owner (that are not assets in this account) to the actionTarget.
_transferFromOwner(transferFromOwnerData, actionTarget);
// If the function input includes a signature and non-empty token permissions,
// initiate a transfer from the owner to the actionTarget via Permit2.
_transferFromOwnerWithPermit(permit, signature, actionTarget);
// Execute external logic on the actionTarget.
ActionData memory depositData = IActionBase(actionTarget).executeAction(actionTargetData);
// Deposit assets from actionTarget into Account.
_deposit(depositData.assets, depositData.assetIds, depositData.assetAmounts, actionTarget);
if (currentCreditor != address(0) && msg.sender != currentCreditor) {
// If the caller is the approved Creditor, the margin Account for the current Creditor (if set) must be closed.
// closeMarginAccount() checks if there is still an open position (open liabilities) of the Account for the old Creditor.
// If so, the function reverts.
ICreditor(currentCreditor).closeMarginAccount(address(this));
}
// Account must be healthy after actions are executed.
if (isAccountUnhealthy()) revert AccountErrors.AccountUnhealthy();
accountVersion = ACCOUNT_VERSION;
}
/*///////////////////////////////////////////////////////////////
ASSET MANAGEMENT
///////////////////////////////////////////////////////////////*/
/**
* @notice Deposits assets into the Account.
* @param assetAddresses Array of the contract addresses of the assets.
* One address for each asset to be deposited, even if multiple assets of the same contract address are deposited.
* @param assetIds Array of the IDs of the assets.
* When depositing an ERC20 token, this will be disregarded, HOWEVER a value (eg. 0) must be set in the array!
* @param assetAmounts Array with the amounts of the assets.
* When depositing an ERC721 token, this will be disregarded, HOWEVER a value (eg. 1) must be set in the array!
* @dev All arrays should be of same length, each index in each array corresponding
* to the same asset that will get deposited. If multiple asset IDs of the same contract address
* are deposited, the assetAddress must be repeated in assetAddresses.
* Example inputs:
* [wETH, DAI, BAYC, SandboxASSET], [0, 0, 15, 2], [10**18, 10**18, 1, 100]
* [SandboxASSET, SandboxASSET, BAYC, BAYC, wETH], [3, 5, 16, 17, 0], [123, 456, 1, 1, 10**18]
*/
function deposit(address[] calldata assetAddresses, uint256[] calldata assetIds, uint256[] calldata assetAmounts)
external
onlyOwner
nonReentrant(WITH_PAUSE_CHECK)
notDuringAuction
{
// No need to check that all arrays have equal length, this check will be done in the Registry.
_deposit(assetAddresses, assetIds, assetAmounts, msg.sender);
}
/**
* @notice Deposits assets into the Account.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @param from The assets deposited into the Account will come from this address.
*/
function _deposit(
address[] memory assetAddresses,
uint256[] memory assetIds,
uint256[] memory assetAmounts,
address from
) internal {
// Transfer assets to Account before processing them.
uint256[] memory assetTypes = IRegistry(registry).batchGetAssetTypes(assetAddresses);
for (uint256 i; i < assetAddresses.length; ++i) {
// Skip if amount is 0 to prevent storing addresses that have 0 balance.
if (assetAmounts[i] == 0) continue;
if (assetTypes[i] == 1) {
if (assetIds[i] != 0) revert AccountErrors.InvalidERC20Id();
_depositERC20(from, assetAddresses[i], assetAmounts[i]);
} else if (assetTypes[i] == 2) {
if (assetAmounts[i] != 1) revert AccountErrors.InvalidERC721Amount();
_depositERC721(from, assetAddresses[i], assetIds[i]);
} else if (assetTypes[i] == 3) {
_depositERC1155(from, assetAddresses[i], assetIds[i], assetAmounts[i]);
} else {
revert AccountErrors.UnknownAssetType();
}
}
if (erc20Stored.length + erc721Stored.length + erc1155Stored.length > ASSET_LIMIT) {
revert AccountErrors.TooManyAssets();
}
// If no Creditor is set, batchProcessDeposit only checks if the assets can be priced.
// If a Creditor is set, batchProcessDeposit will also update the exposures of assets and underlying assets for the Creditor.
IRegistry(registry).batchProcessDeposit(creditor, assetAddresses, assetIds, assetAmounts);
if (assetAddresses.length > 0) {
emit Transfers(from, address(this), assetAddresses, assetIds, assetAmounts, assetTypes);
}
}
/**
* @notice Withdraws assets from the Account to the owner.
* @param assetAddresses Array of the contract addresses of the assets.
* One address for each asset to be withdrawn, even if multiple assets of the same contract address are withdrawn.
* @param assetIds Array of the IDs of the assets.
* For ERC20 assets, the id must be 0.
* @param assetAmounts Array with the amounts of the assets.
* For ERC721 assets, the amount must be 1.
* @dev All arrays should be of same length, each index in each array corresponding
* to the same asset that will get withdrawn. If multiple asset IDs of the same contract address
* are to be withdrawn, the assetAddress must be repeated in assetAddresses.
* Example inputs:
* [wETH, DAI, BAYC, SandboxASSET], [0, 0, 15, 2], [10**18, 10**18, 1, 100]
* [SandboxASSET, SandboxASSET, BAYC, BAYC, wETH], [3, 5, 16, 17, 0], [123, 456, 1, 1, 10**18]
* @dev Will fail if the Account is in an unhealthy state after withdrawal (collateral value is smaller than the used margin).
* If the Account has no open position (liabilities), users are free to withdraw any asset at any time.
*/
function withdraw(address[] calldata assetAddresses, uint256[] calldata assetIds, uint256[] calldata assetAmounts)
external
onlyOwner
nonReentrant(WITHOUT_PAUSE_CHECK)
notDuringAuction
updateActionTimestamp
{
// No need to check that all arrays have equal length, this check is will be done in the Registry.
_withdraw(assetAddresses, assetIds, assetAmounts, msg.sender);
// Account must be healthy after assets are withdrawn.
if (isAccountUnhealthy()) revert AccountErrors.AccountUnhealthy();
}
/**
* @notice Withdraws assets from the Account to the owner.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @param to The address to withdraw to.
* @dev (batch)ProcessWithdrawal handles the accounting of assets in the Registry.
*/
function _withdraw(
address[] memory assetAddresses,
uint256[] memory assetIds,
uint256[] memory assetAmounts,
address to
) internal {
// Process assets before transferring them out of the Account.
// If a Creditor is set, batchProcessWithdrawal will also update the exposures of assets and underlying assets for the Creditor.
uint256[] memory assetTypes =
IRegistry(registry).batchProcessWithdrawal(creditor, assetAddresses, assetIds, assetAmounts);
// If no assets are being withdrawn, return early.
if (assetAddresses.length == 0) return;
for (uint256 i; i < assetAddresses.length; ++i) {
// Skip if amount is 0 to prevent transferring 0 balances.
if (assetAmounts[i] == 0) continue;
if (assetTypes[i] == 1) {
if (assetIds[i] != 0) revert AccountErrors.InvalidERC20Id();
_withdrawERC20(to, assetAddresses[i], assetAmounts[i]);
} else if (assetTypes[i] == 2) {
if (assetAmounts[i] != 1) revert AccountErrors.InvalidERC721Amount();
_withdrawERC721(to, assetAddresses[i], assetIds[i]);
} else if (assetTypes[i] == 3) {
_withdrawERC1155(to, assetAddresses[i], assetIds[i], assetAmounts[i]);
} else {
revert AccountErrors.UnknownAssetType();
}
}
emit Transfers(address(this), to, assetAddresses, assetIds, assetAmounts, assetTypes);
}
/**
* @notice Internal function to deposit ERC20 assets.
* @param from Address the tokens should be transferred from. This address must have approved the Account.
* @param ERC20Address The contract address of the asset.
* @param amount The amount of ERC20 assets.
* @dev Used for all asset type == 1.
* @dev If the token has not yet been deposited, the ERC20 token address is stored.
*/
// forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
function _depositERC20(address from, address ERC20Address, uint256 amount) internal {
ERC20(ERC20Address).safeTransferFrom(from, address(this), amount);
uint256 currentBalance = erc20Balances[ERC20Address];
if (currentBalance == 0) {
erc20Stored.push(ERC20Address);
}
unchecked {
erc20Balances[ERC20Address] = currentBalance + amount;
}
}
/**
* @notice Internal function to deposit ERC721 tokens.
* @param from Address the tokens should be transferred from. This address must have approved the Account.
* @param ERC721Address The contract address of the asset.
* @param id The ID of the ERC721 token.
* @dev Used for all asset type == 2.
* @dev After successful transfer, the function pushes the ERC721 address to the stored token and stored ID array.
* This may cause duplicates in the ERC721 stored addresses array, but this is intended.
*/
// forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
function _depositERC721(address from, address ERC721Address, uint256 id) internal {
IERC721(ERC721Address).safeTransferFrom(from, address(this), id);
erc721Stored.push(ERC721Address);
erc721TokenIds.push(id);
}
/**
* @notice Internal function to deposit ERC1155 tokens.
* @param from The Address the tokens should be transferred from. This address must have approved the Account.
* @param ERC1155Address The contract address of the asset.
* @param id The ID of the ERC1155 tokens.
* @param amount The amount of ERC1155 tokens.
* @dev Used for all asset type == 3.
* @dev After successful transfer, the function checks whether the combination of address & ID has already been stored.
* If not, the function pushes the new address and ID to the stored arrays.
* This may cause duplicates in the ERC1155 stored addresses array, this is intended.
*/
// forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
function _depositERC1155(address from, address ERC1155Address, uint256 id, uint256 amount) internal {
IERC1155(ERC1155Address).safeTransferFrom(from, address(this), id, amount, "");
uint256 currentBalance = erc1155Balances[ERC1155Address][id];
if (currentBalance == 0) {
erc1155Stored.push(ERC1155Address);
erc1155TokenIds.push(id);
}
unchecked {
erc1155Balances[ERC1155Address][id] = currentBalance + amount;
}
}
/**
* @notice Internal function to withdraw ERC20 assets.
* @param to Address the tokens should be sent to.
* @param ERC20Address The contract address of the asset.
* @param amount The amount of ERC20 assets.
* @dev Used for all asset type == 1.
* @dev The function checks whether the Account has any leftover balance of said asset.
* If not, it will pop() the ERC20 asset address from the stored addresses array.
* Note: this shifts the order of erc20Stored!
* @dev This check is done using a loop:
* gas usage of writing it in a mapping vs extra loops is in favor of extra loops in this case.
*/
// forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
function _withdrawERC20(address to, address ERC20Address, uint256 amount) internal {
erc20Balances[ERC20Address] -= amount;
if (erc20Balances[ERC20Address] == 0) {
uint256 erc20StoredLength = erc20Stored.length;
if (erc20StoredLength == 1) {
// There was only one ERC20 stored on the contract, safe to remove from array.
erc20Stored.pop();
} else {
for (uint256 i; i < erc20StoredLength; ++i) {
if (erc20Stored[i] == ERC20Address) {
erc20Stored[i] = erc20Stored[erc20StoredLength - 1];
erc20Stored.pop();
break;
}
}
}
}
ERC20(ERC20Address).safeTransfer(to, amount);
}
/**
* @notice Internal function to withdraw ERC721 tokens.
* @param to Address the tokens should be sent to.
* @param ERC721Address The contract address of the asset.
* @param id The ID of the ERC721 token.
* @dev Used for all asset type == 2.
* @dev The function checks whether any other ERC721 is deposited in the Account.
* If not, it pops the stored addresses and stored IDs (pop() of two arrays is 180 gas cheaper than deleting).
* If there are, it loops through the stored arrays and searches the ID that's withdrawn,
* then replaces it with the last index, followed by a pop().
* @dev Sensitive to ReEntrance attacks! SafeTransferFrom therefore done at the end of the function.
*/
// forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
function _withdrawERC721(address to, address ERC721Address, uint256 id) internal {
uint256 tokenIdLength = erc721TokenIds.length;
uint256 i;
if (tokenIdLength == 1) {
// There was only one ERC721 stored on the contract, safe to remove both lists.
if (erc721TokenIds[0] != id || erc721Stored[0] != ERC721Address) revert AccountErrors.UnknownAsset();
erc721TokenIds.pop();
erc721Stored.pop();
} else {
for (; i < tokenIdLength; ++i) {
if (erc721TokenIds[i] == id && erc721Stored[i] == ERC721Address) {
erc721TokenIds[i] = erc721TokenIds[tokenIdLength - 1];
erc721TokenIds.pop();
erc721Stored[i] = erc721Stored[tokenIdLength - 1];
erc721Stored.pop();
break;
}
}
// For loop should break, otherwise we never went into the if-branch, meaning the token being withdrawn
// is unknown and not properly deposited.
// i + 1 is done after loop, so i reaches tokenIdLength.
if (i == tokenIdLength) revert AccountErrors.UnknownAsset();
}
IERC721(ERC721Address).safeTransferFrom(address(this), to, id);
}
/**
* @notice Internal function to withdraw ERC1155 tokens.
* @param to Address the tokens should be sent to.
* @param ERC1155Address The contract address of the asset.
* @param id The ID of the ERC1155 tokens.
* @param amount The amount of ERC1155 tokens.
* @dev Used for all asset types = 3.
* @dev After successful transfer, the function checks whether there is any balance left for that ERC1155.
* If there is, it simply transfers the tokens.
* If not, it checks whether it can pop() (used for gas savings vs delete) the stored arrays.
* If there are still other ERC1155's on the contract, it looks for the ID and token address to be withdrawn
* and then replaces it with the last index, followed by a pop().
* @dev Sensitive to ReEntrance attacks! SafeTransferFrom therefore done at the end of the function.
*/
// forge-lint: disable-next-item(mixed-case-function,mixed-case-variable)
function _withdrawERC1155(address to, address ERC1155Address, uint256 id, uint256 amount) internal {
uint256 tokenIdLength = erc1155TokenIds.length;
erc1155Balances[ERC1155Address][id] -= amount;
if (erc1155Balances[ERC1155Address][id] == 0) {
if (tokenIdLength == 1) {
erc1155TokenIds.pop();
erc1155Stored.pop();
} else {
for (uint256 i; i < tokenIdLength; ++i) {
if (erc1155TokenIds[i] == id) {
if (erc1155Stored[i] == ERC1155Address) {
erc1155TokenIds[i] = erc1155TokenIds[tokenIdLength - 1];
erc1155TokenIds.pop();
erc1155Stored[i] = erc1155Stored[tokenIdLength - 1];
erc1155Stored.pop();
break;
}
}
}
}
}
IERC1155(ERC1155Address).safeTransferFrom(address(this), to, id, amount, "");
}
/**
* @notice Transfers assets directly from the owner to the actionTarget contract.
* @param transferFromOwnerData A struct containing the info of all assets transferred from the owner that are not in this account.
* @param to The address to withdraw to.
*/
function _transferFromOwner(ActionData memory transferFromOwnerData, address to) internal {
uint256 assetAddressesLength = transferFromOwnerData.assets.length;
// If no assets are being transferred, return early.
if (assetAddressesLength == 0) return;
address owner_ = owner;
for (uint256 i; i < assetAddressesLength; ++i) {
if (transferFromOwnerData.assetAmounts[i] == 0) {
// Skip if amount is 0 to prevent transferring 0 balances.
continue;
}
if (transferFromOwnerData.assetTypes[i] == 1) {
ERC20(transferFromOwnerData.assets[i])
.safeTransferFrom(owner_, to, transferFromOwnerData.assetAmounts[i]);
} else if (transferFromOwnerData.assetTypes[i] == 2) {
IERC721(transferFromOwnerData.assets[i]).safeTransferFrom(owner_, to, transferFromOwnerData.assetIds[i]);
} else if (transferFromOwnerData.assetTypes[i] == 3) {
IERC1155(transferFromOwnerData.assets[i])
.safeTransferFrom(
owner_, to, transferFromOwnerData.assetIds[i], transferFromOwnerData.assetAmounts[i], ""
);
} else {
revert AccountErrors.UnknownAssetType();
}
}
emit Transfers(
owner_,
to,
transferFromOwnerData.assets,
transferFromOwnerData.assetIds,
transferFromOwnerData.assetAmounts,
transferFromOwnerData.assetTypes
);
}
/**
* @notice Transfers assets from the owner to the actionTarget contract via Permit2.
* @param permit Data specifying the terms of the transfer.
* @param signature The signature to verify.
* @param to The address to withdraw to.
*/
function _transferFromOwnerWithPermit(
IPermit2.PermitBatchTransferFrom memory permit,
bytes memory signature,
address to
) internal {
uint256 tokenPermissionsLength = permit.permitted.length;
// If no assets are being transferred, return early.
if (tokenPermissionsLength == 0) return;
IPermit2.SignatureTransferDetails[] memory transferDetails =
new IPermit2.SignatureTransferDetails[](tokenPermissionsLength);
address[] memory addresses = new address[](tokenPermissionsLength);
uint256[] memory amounts = new uint256[](tokenPermissionsLength);
uint256[] memory types = new uint256[](tokenPermissionsLength);
for (uint256 i; i < tokenPermissionsLength; ++i) {
transferDetails[i].to = to;
transferDetails[i].requestedAmount = permit.permitted[i].amount;
addresses[i] = permit.permitted[i].token;
amounts[i] = permit.permitted[i].amount;
types[i] = 1;
}
address owner_ = owner;
PERMIT2.permitTransferFrom(permit, transferDetails, owner_, signature);
emit Transfers(owner_, to, addresses, new uint256[](tokenPermissionsLength), amounts, types);
}
/**
* @notice Skims non-deposited assets from the Account.
* @param token The contract address of the asset, address(0) for native token.
* @param id The ID of the asset.
* @param type_ The asset type of the asset.
* @dev Function can retrieve assets that were transferred to the Account but not deposited
* or can be used to claim yield for strictly upwards rebasing tokens.
* @dev Unused input parameters (e.g. id's for ERC20, or type for native token) are not validated,
* hence arbitrary value can be emitted in events.
*/
function skim(address token, uint256 id, uint256 type_)
external
onlyOwner
nonReentrant(WITHOUT_PAUSE_CHECK)
updateActionTimestamp
{
uint256 amount;
if (token == address(0)) {
amount = address(this).balance;
(bool success, bytes memory result) = payable(msg.sender).call{ value: amount }("");
require(success, string(result));
} else if (type_ == 1) {
uint256 balance = ERC20(token).balanceOf(address(this));
uint256 balanceStored = erc20Balances[token];
if (balance > balanceStored) {
amount = balance - balanceStored;
ERC20(token).safeTransfer(msg.sender, amount);
}
} else if (type_ == 2) {
bool isStored;
uint256 erc721StoredLength = erc721Stored.length;
for (uint256 i; i < erc721StoredLength; ++i) {
if (erc721Stored[i] == token && erc721TokenIds[i] == id) {
isStored = true;
break;
}
}
if (!isStored) {
amount = 1;
IERC721(token).safeTransferFrom(address(this), msg.sender, id);
}
} else if (type_ == 3) {
uint256 balance = IERC1155(token).balanceOf(address(this), id);
uint256 balanceStored = erc1155Balances[token][id];
if (balance > balanceStored) {
amount = balance - balanceStored;
IERC1155(token).safeTransferFrom(address(this), msg.sender, id, amount, "");
}
} else {
revert AccountErrors.UnknownAssetType();
}
if (amount > 0) emit Skim(address(this), msg.sender, token, id, amount, type_);
}
/* ///////////////////////////////////////////////////////////////
HELPER FUNCTIONS
/////////////////////////////////////////////////////////////// */
/**
* @notice Returns the total value (mark to market) of the Account in a specific Numeraire
* @param numeraire_ The Numeraire to return the value in.
* @return accountValue Total value stored in the account, denominated in Numeraire.
* @dev Fetches all stored assets with their amounts.
* Using a specified Numeraire, fetches the value of all assets in said Numeraire.
*/
function getAccountValue(address numeraire_) external view returns (uint256 accountValue) {
(address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts) =
generateAssetData();
accountValue = IRegistry(registry).getTotalValue(numeraire_, creditor, assetAddresses, assetIds, assetAmounts);
}
/**
* @notice Generates three arrays of all the stored assets in the Account.
* @return assetAddresses Array of the contract addresses of the assets.
* @return assetIds Array of the IDs of the assets.
* @return assetAmounts Array with the amounts of the assets.
* @dev Balances are stored on the contract to prevent working around the deposit limits.
* @dev Loops through the stored asset addresses and fills the arrays.
* @dev There is no importance of the order in the arrays, but all indexes of the arrays correspond to the same asset.
*/
function generateAssetData()
public
view
returns (address[] memory assetAddresses, uint256[] memory assetIds, uint256[] memory assetAmounts)
{
uint256 totalLength;
unchecked {
totalLength = erc20Stored.length + erc721Stored.length + erc1155Stored.length;
} // Cannot realistically overflow.
assetAddresses = new address[](totalLength);
assetIds = new uint256[](totalLength);
assetAmounts = new uint256[](totalLength);
uint256 i;
uint256 erc20StoredLength = erc20Stored.length;
address cacheAddr;
for (; i < erc20StoredLength; ++i) {
cacheAddr = erc20Stored[i];
assetAddresses[i] = cacheAddr;
// Gas: no need to store 0, index will continue anyway.
// assetIds[i] = 0;
assetAmounts[i] = erc20Balances[cacheAddr];
}
uint256 j;
uint256 erc721StoredLength = erc721Stored.length;
for (; j < erc721StoredLength; ++j) {
cacheAddr = erc721Stored[j];
assetAddresses[i] = cacheAddr;
assetIds[i] = erc721TokenIds[j];
assetAmounts[i] = 1;
unchecked {
++i;
}
}
uint256 k;
uint256 erc1155StoredLength = erc1155Stored.length;
uint256 cacheId;
for (; k < erc1155StoredLength; ++k) {
cacheAddr = erc1155Stored[k];
cacheId = erc1155TokenIds[k];
assetAddresses[i] = cacheAddr;
assetIds[i] = cacheId;
assetAmounts[i] = erc1155Balances[cacheAddr][cacheId];
unchecked {
++i;
}
}
}
/*
@notice Returns the onERC721Received selector.
@dev Needed to receive ERC721 tokens.
*/
// forge-lint: disable-next-item(mixed-case-function)
function onERC721Received(address, address, uint256, bytes calldata) public pure returns (bytes4) {
return this.onERC721Received.selector;
}
/*
@notice Returns the onERC1155Received selector.
@dev Needed to receive ERC1155 tokens.
*/
// forge-lint: disable-next-item(mixed-case-function)
function onERC1155Received(address, address, uint256, uint256, bytes calldata) public pure returns (bytes4) {
return this.onERC1155Received.selector;
}
/*
@notice Called when function selector doesn't match any other.
@dev No fallback allowed.
*/
fallback() external {
revert AccountErrors.NoFallback();
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
library AccountErrors {
error AccountInAuction();
error AccountNotLiquidatable();
error AccountUnhealthy();
error AlreadyInitialized();
error CreditorAlreadySet();
error CreditorNotSet();
error CoolDownPeriodNotPassed();
error InvalidAccountVersion();
error InvalidERC20Id();
error InvalidERC721Amount();
error InvalidRecipient();
error InvalidRegistry();
error InvalidUpgrade();
error LengthMismatch();
error NoFallback();
error NoReentry();
error NonZeroOpenPosition();
error NumeraireNotFound();
error OnlyCreditor();
error OnlyFactory();
error OnlyLiquidator();
error OnlyOwner();
error OnlySelf();
error TooManyAssets();
error UnknownAsset();
error UnknownAssetType();
}
library FactoryErrors {
error AccountCreationFailed();
error AccountVersionBlocked();
error FactoryMismatch();
error InvalidAccountVersion();
error InvalidRecipient();
error InvalidUpgrade();
error ImplIsZero();
error OnlyAccount();
error OnlyAccountOwner();
error UnsafeRecipient();
error VersionMismatch();
error VersionRootIsZero();
}
library GuardianErrors {
// Thrown when the cool-down period has not yet passed.
error CoolDownPeriodNotPassed();
// Thrown when the functionality is paused.
error FunctionIsPaused();
// Thrown when the caller is not the Guardian.
error OnlyGuardian();
}
library RegistryErrors {
error AssetAlreadyInRegistry();
error AssetModNotUnique();
error AssetNotAllowed();
error InvalidAssetType();
error LengthMismatch();
error MaxRecursiveCallsReached();
error Min1Oracle();
error OnlyAccount();
error OnlyAssetModule();
error OnlyOracleModule();
error OracleModNotUnique();
error OracleNotReverting();
error OracleReverting();
error SequencerDown();
error Unauthorized();
error UnknownAsset();
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
/**
* @title Arcadia Accounts Storage
* @author Pragma Labs
* @notice This contract is the storage contract for the Accounts.
* Arcadia Accounts are smart contracts that act as onchain, decentralized and composable margin accounts.
* They provide individuals, DAOs, and other protocols with a simple and flexible way to deposit and manage multiple assets as collateral.
* More detail about the Accounts can be found in the AccountsV1.sol contract.
* @dev Since Accounts are proxies and can be upgraded by the user, all storage variables should be declared in this contract.
* New account versions must create a new account storage contract and inherit this storage contract.
*/
abstract contract AccountStorageV1 {
/* //////////////////////////////////////////////////////////////
STORAGE
////////////////////////////////////////////////////////////// */
// Flag indicating if the Account is in an auction (in liquidation).
bool public inAuction;
// Flag Indicating if a function is locked to protect against reentrancy.
uint8 internal locked;
// Used to prevent the old Owner from frontrunning a transferFrom().
// The Timestamp of the last account action, that might be disadvantageous for a new Owner
// (withdrawals, change of Creditor, increasing liabilities...).
uint32 public lastActionTimestamp;
// The contract address of the liquidator, address 0 if no creditor is set.
address public liquidator;
// The minimum amount of collateral that must be held in the Account before a position can be opened, denominated in the numeraire.
// Will count as Used Margin after a position is opened.
uint96 public minimumMargin;
// The contract address of the Registry.
address public registry;
// The owner of the Account.
address public owner;
// The contract address of the Creditor.
address public creditor;
// The Numeraire (the unit in which prices are measured) of the Account,
// in which all assets and liabilities are denominated.
address public numeraire;
// Array with all the contract addresses of ERC20 tokens in the account.
address[] internal erc20Stored;
// Array with all the contract addresses of ERC721 tokens in the account.
address[] internal erc721Stored;
// Array with all the contract addresses of ERC1155 tokens in the account.
address[] internal erc1155Stored;
// Array with all the corresponding ids for each ERC721 token in the account.
uint256[] internal erc721TokenIds;
// Array with all the corresponding ids for each ERC1155 token in the account.
uint256[] internal erc1155TokenIds;
// Map asset => balance.
mapping(address => uint256) public erc20Balances;
// Map asset => id => balance.
mapping(address => mapping(uint256 => uint256)) public erc1155Balances;
// Map owner => approved Creditor.
// This is a Creditor for which a margin Account can be opened later in time to e.g. refinance liabilities.
mapping(address => address) public approvedCreditor;
// Map owner => assetManager => flag.
mapping(address => mapping(address => bool)) public isAssetManager;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
// Struct with information to pass to and from the actionTarget.
struct ActionData {
// Array of the contract addresses of the assets.
address[] assets;
// Array of the IDs of the assets.
uint256[] assetIds;
// Array with the amounts of the assets.
uint256[] assetAmounts;
// Array with the types of the assets.
uint256[] assetTypes;
}
interface IActionBase {
/**
* @notice Calls an external target contract with arbitrary calldata.
* @param actionTargetData A bytes object containing the encoded input for the actionTarget.
* @return resultData An actionAssetData struct with the final balances of this actionTarget contract.
*/
function executeAction(bytes calldata actionTargetData) external returns (ActionData memory);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: BUSL-1.1
*/
pragma solidity ^0.8.0;
// Struct with risk and valuation related information for a certain asset.
struct AssetValueAndRiskFactors {
// The value of the asset.
uint256 assetValue;
// The collateral factor of the asset, for a given creditor.
uint256 collateralFactor;
// The liquidation factor of the asset, for a given creditor.
uint256 liquidationFactor;
}
/**
* @title Asset Valuation Library
* @author Pragma Labs
* @notice The Asset Valuation Library is responsible for calculating the risk weighted values of combinations of assets.
*/
library AssetValuationLib {
/*///////////////////////////////////////////////////////////////
CONSTANTS
///////////////////////////////////////////////////////////////*/
uint256 internal constant ONE_4 = 10_000;
/*///////////////////////////////////////////////////////////////
RISK FACTORS
///////////////////////////////////////////////////////////////*/
/**
* @notice Calculates the collateral value given a combination of asset values and corresponding collateral factors.
* @param valuesAndRiskFactors Array of asset values and corresponding collateral factors.
* @return collateralValue The collateral value of the given assets.
*/
function _calculateCollateralValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
internal
pure
returns (uint256 collateralValue)
{
for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
collateralValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].collateralFactor;
}
collateralValue = collateralValue / ONE_4;
}
/**
* @notice Calculates the liquidation value given a combination of asset values and corresponding liquidation factors.
* @param valuesAndRiskFactors List of asset values and corresponding liquidation factors.
* @return liquidationValue The liquidation value of the given assets.
*/
function _calculateLiquidationValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
internal
pure
returns (uint256 liquidationValue)
{
for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
liquidationValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].liquidationFactor;
}
liquidationValue = liquidationValue / ONE_4;
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "APPROVE_FAILED");
}
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
// forge-lint: disable-next-item(mixed-case-function)
interface IAccount {
/**
* @notice Returns the Account version.
* @return version The Account version.
*/
function ACCOUNT_VERSION() external view returns (uint256);
/**
* @notice Returns the Arcadia Accounts Factory.
* @return factory The contract address of the Arcadia Accounts Factory.
*/
function FACTORY() external view returns (address);
/**
* @notice Initiates the variables of the Account.
* @param owner The sender of the 'createAccount' on the factory
* @param registry The 'beacon' contract with the external logic.
* @param creditor The contract address of the creditor.
*/
function initialize(address owner, address registry, address creditor) external;
/**
* @notice Updates the Account version and stores a new address in the EIP1967 implementation slot.
* @param newImplementation The contract with the new Account logic.
* @param newRegistry The Registry for this specific implementation (might be identical as the old registry).
* @param data Arbitrary data, can contain instructions to execute when updating Account to new logic.
* @param newVersion The new version of the Account logic.
*/
function upgradeAccount(address newImplementation, address newRegistry, uint256 newVersion, bytes calldata data)
external;
/**
* @notice Transfers ownership of the contract to a new account.
* @param newOwner The new owner of the Account.
*/
function transferOwnership(address newOwner) external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
interface IAccountsGuard {
function lock(bool pauseCheck) external;
function unLock() external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
interface IAssetManager {
function onSetAssetManager(address owner, bool status, bytes calldata data) external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
/**
* @title Creditor implementation.
* @author Pragma Labs
* @notice This contract contains the minimum functionality a Creditor, interacting with Arcadia Accounts, needs to implement.
* @dev For the implementation of Arcadia Accounts, see: https://github.com/arcadia-finance/accounts-v2.
*/
interface ICreditor {
/**
* @notice Checks if Account fulfills all requirements and returns Creditor parameters.
* @param accountVersion The version of the Arcadia Account.
* @return success Bool indicating if all requirements are met.
* @return numeraire The Numeraire of the Creditor.
* @return liquidator The liquidator of the Creditor.
* @return minimumMargin The minimum amount of collateral that must be held in the Account before a position can be opened,
* denominated in the numeraire.
*/
function openMarginAccount(uint256 accountVersion) external returns (bool, address, address, uint256);
/**
* @notice Checks if Account can be closed.
* @param account The Account address.
*/
function closeMarginAccount(address account) external;
/**
* @notice Returns the open position of the Account.
* @param account The Account address.
* @return openPosition The open position of the Account.
*/
function getOpenPosition(address account) external view returns (uint256);
/**
* @notice Returns the Risk Manager of the creditor.
* @return riskManager The Risk Manager of the creditor.
*/
function riskManager() external view returns (address);
/**
* @notice Callback of the Account during a flashAction.
* @param callbackData The data for the actions that have to be executed by the Creditor during a flashAction.
*/
function flashActionCallback(bytes calldata callbackData) external;
/**
* @notice Starts the liquidation of an account and returns the open position of the Account.
* @param initiator The address of the liquidation initiator.
* @param minimumMargin The minimum margin of the Account.
* @return openPosition the open position of the Account.
*/
function startLiquidation(address initiator, uint256 minimumMargin) external returns (uint256);
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
interface IDistributor {
function operators(address user, address operator) external view returns (uint256);
function setClaimRecipient(address recipient, address token) external;
function toggleOperator(address user, address operator) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC721 {
function ownerOf(uint256 id) external view returns (address);
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function transferFrom(address from, address to, uint256 id) external;
function approve(address spender, uint256 id) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC1155 {
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function balanceOf(address account, uint256 id) external view returns (uint256);
function setApprovalForAll(address operator, bool approved) external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
interface IFactory {
/**
* @notice Checks if a contract is an Account.
* @param account The contract address of the Account.
* @return bool indicating if the address is an Account or not.
*/
function isAccount(address account) external view returns (bool);
/**
* @notice Function used to transfer an Account, called by the Account itself.
* @param to The target.
*/
function safeTransferAccount(address to) external;
/**
* @notice Function used to transfer an Account between users based on Account address.
* @param from The sender.
* @param to The target.
* @param account The address of the Account that is transferred.
* @dev This method transfers an Account on Account address instead of id and
* also transfers the Account proxy contract to the new owner.
* @dev The Account itself cannot become its owner.
*/
function safeTransferFrom(address from, address to, address account) external;
/**
* @notice This function allows Account owners to upgrade the implementation of the Account.
* @param account Account that needs to be upgraded.
* @param version The accountVersion to upgrade to.
* @param proofs The Merkle proofs that prove the compatibility of the upgrade from current to new account version.
* @dev As each Account is a proxy, the implementation of the proxy can be changed by the owner of the Account.
* Checks are done such that only compatible versions can be upgraded to.
* Merkle proofs and their leaves can be found on https://www.github.com/arcadia-finance.
*/
function upgradeAccountVersion(address account, uint256 version, bytes32[] calldata proofs) external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
interface IMerklOperator {
function onSetMerklOperator(address accountOwner, bool status, bytes calldata data) external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
interface IPermit2 {
/**
* @notice The token and amount details for a transfer signed in the permit transfer signature
*/
struct TokenPermissions {
// ERC20 token address
address token;
// the maximum amount that can be spent
uint256 amount;
}
/**
* @notice Used to reconstruct the signed permit message for multiple token transfers
* @dev Do not need to pass in spender address as it is required that it is msg.sender
* @dev Note that a user still signs over a spender address
*/
struct PermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/**
* @notice Specifies the recipient address and amount for batched transfers.
* @dev Recipients and amounts correspond to the index of the signed token permissions array.
* @dev Reverts if the requested amount is greater than the permitted signed amount.
*/
struct SignatureTransferDetails {
// recipient address
address to;
// spender requested amount
uint256 requestedAmount;
}
/**
* @notice Transfers multiple tokens using a signed permit message
* @param permit The permit data signed over by the owner
* @param owner The owner of the tokens to transfer
* @param transferDetails Specifies the recipient and requested amount for the token transfer
* @param signature The signature to verify
*/
function permitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes calldata signature
) external;
}/**
* Created by Pragma Labs
* SPDX-License-Identifier: MIT
*/
pragma solidity ^0.8.0;
import { AssetValueAndRiskFactors } from "../libraries/AssetValuationLib.sol";
interface IRegistry {
/**
* @notice Returns the Arcadia Accounts Factory.
* @return factory The contract address of the Arcadia Accounts Factory.
*/
// forge-lint: disable-next-item(mixed-case-function)
function FACTORY() external view returns (address);
/**
* @notice Checks if an asset is in the Registry.
* @param asset The contract address of the asset.
* @return boolean.
*/
function inRegistry(address asset) external view returns (bool);
/**
* @notice Batch retrieves the asset types.
* @param assetAddresses Array of the contract addresses of the assets.
* @return assetTypes Array with the types of the assets.
* 0 = Unknown asset.
* 1 = ERC20.
* 2 = ERC721.
* 3 = ERC1155.
* ...
*/
function batchGetAssetTypes(address[] calldata assetAddresses) external view returns (uint256[] memory);
/**
* @notice Batch deposit multiple assets.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param amounts Array with the amounts of the assets.
*/
function batchProcessDeposit(
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata amounts
) external;
/**
* @notice Batch withdraw multiple assets.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param amounts Array with the amounts of the assets.
* @return assetTypes Array with the types of the assets.
* 0 = Unknown asset.
* 1 = ERC20.
* 2 = ERC721.
* 3 = ERC1155.
*/
function batchProcessWithdrawal(
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata amounts
) external returns (uint256[] memory);
/**
* @notice Calculates the combined value of a combination of assets, denominated in a given Numeraire.
* @param numeraire The contract address of the Numeraire.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @return assetValue The combined value of the assets, denominated in the Numeraire.
*/
function getTotalValue(
address numeraire,
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (uint256);
/**
* @notice Calculates the collateralValue of a combination of assets, denominated in a given Numeraire.
* @param numeraire The contract address of the Numeraire.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @return collateralValue The collateral value of the assets, denominated in the Numeraire.
*/
function getCollateralValue(
address numeraire,
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (uint256);
/**
* @notice Calculates the getLiquidationValue of a combination of assets, denominated in a given Numeraire.
* @param numeraire The contract address of the Numeraire.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @return liquidationValue The liquidation value of the assets, denominated in the Numeraire.
*/
function getLiquidationValue(
address numeraire,
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (uint256);
/**
* @notice Calculates the values per asset, denominated in a given Numeraire.
* @param numeraire The contract address of the Numeraire.
* @param creditor The contract address of the creditor.
* @param assetAddresses Array of the contract addresses of the assets.
* @param assetIds Array of the IDs of the assets.
* @param assetAmounts Array with the amounts of the assets.
* @return valuesAndRiskFactors The array of values per assets, denominated in the Numeraire.
*/
function getValuesInNumeraire(
address numeraire,
address creditor,
address[] calldata assetAddresses,
uint256[] calldata assetIds,
uint256[] calldata assetAmounts
) external view returns (AssetValueAndRiskFactors[] memory valuesAndRiskFactors);
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}{
"remappings": [
"@ensdomains/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/@ensdomains/",
"@nomad-xyz/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/",
"@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"@solidity-parser/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/solhint/node_modules/@solidity-parser/",
"@uniswap/v2-core/contracts/=lib/lending-v2/lib/accounts-v2/./test/utils/fixtures/swap-router-02/",
"@uniswap/v3-core/contracts/=lib/lending-v2/lib/accounts-v2/lib/v3-core/contracts/",
"@uniswap/v3-periphery/contracts/=lib/lending-v2/lib/accounts-v2/lib/v3-periphery/contracts/",
"@uniswap/v4-core/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/",
"@utils/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/node_modules/utils/src/",
"ExcessivelySafeCall/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/ExcessivelySafeCall/src/",
"accounts-v2/=lib/lending-v2/lib/accounts-v2/src/",
"arcadia-periphery/=lib/arcadia-periphery/src/",
"asset-managers/=lib/arcadia-periphery/lib/asset-managers/src/",
"base64-sol/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/base64/",
"base64/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/base64/",
"contracts/=lib/lending-v2/lib/accounts-v2/lib/slipstream/contracts/",
"ds-test/=lib/lending-v2/lib/accounts-v2/lib/solmate/lib/ds-test/src/",
"erc4626-tests/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/lib/erc4626-tests/",
"forge-gas-snapshot/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/permit2/lib/forge-gas-snapshot/src/",
"forge-std/=lib/lending-v2/lib/accounts-v2/lib/forge-std/src/",
"hardhat/=lib/lending-v2/lib/accounts-v2/lib/slipstream/node_modules/hardhat/",
"lending-v2/=lib/lending-v2/src/",
"merkl-contracts/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/",
"openzeppelin-contracts-upgradeable-v4.9/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/",
"openzeppelin-contracts-v3.4/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/contracts/",
"openzeppelin-contracts-v4.9/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
"openzeppelin-contracts/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/contracts/",
"openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
"oz/=lib/lending-v2/lib/accounts-v2/lib/merkl-contracts/node_modules/@openzeppelin/contracts/",
"permit2/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/permit2/",
"slipstream/=lib/lending-v2/lib/accounts-v2/lib/slipstream/",
"solady/=lib/lending-v2/lib/accounts-v2/lib/solady/src/",
"solidity-lib/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/solidity-lib/contracts/",
"solmate/=lib/lending-v2/lib/accounts-v2/lib/solmate/",
"swap-router-contracts/=lib/lending-v2/lib/accounts-v2/lib/swap-router-contracts/contracts/",
"v3-core/=lib/lending-v2/lib/accounts-v2/lib/v3-core/",
"v3-periphery/=lib/lending-v2/lib/accounts-v2/lib/v3-periphery/contracts/",
"v4-core/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/src/",
"v4-periphery/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/",
"lib/accounts-v2/lib/merkl-contracts:@openzeppelin/contracts-upgradeable/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/contracts/",
"lib/accounts-v2/lib/merkl-contracts:@openzeppelin/contracts/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
"lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
"lib/accounts-v2/lib/slipstream:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"lib/accounts-v2/lib/swap-router-contracts:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/accounts-v2/lib/v3-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/accounts-v2/lib/v4-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/accounts-v2/lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/asset-managers/lib/accounts-v2/lib/slipstream:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"lib/asset-managers/lib/accounts-v2/lib/swap-router-contracts:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/asset-managers/lib/accounts-v2/lib/v3-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/asset-managers/lib/accounts-v2/lib/v4-periphery:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/arcadia-periphery/lib/asset-managers/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/merkl-contracts:@openzeppelin/contracts-upgradeable/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-upgradeable-v4.9/contracts/",
"lib/merkl-contracts:@openzeppelin/contracts/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/contracts/",
"lib/openzeppelin-contracts-upgradeable-v4.9:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v4.9/",
"lib/slipstream:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/slipstream/lib/openzeppelin-contracts/",
"lib/v3-periphery:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/openzeppelin-contracts-v3.4/",
"lib/v4-periphery:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/",
"lib/v4-periphery/lib/v4-core:@openzeppelin/=lib/lending-v2/lib/accounts-v2/lib/v4-periphery/lib/v4-core/lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"factory","type":"address"},{"internalType":"address","name":"accountsGuard","type":"address"},{"internalType":"address","name":"merklDistributor","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AccountInAuction","type":"error"},{"inputs":[],"name":"AccountNotLiquidatable","type":"error"},{"inputs":[],"name":"AccountUnhealthy","type":"error"},{"inputs":[],"name":"CoolDownPeriodNotPassed","type":"error"},{"inputs":[],"name":"CreditorAlreadySet","type":"error"},{"inputs":[],"name":"CreditorNotSet","type":"error"},{"inputs":[],"name":"InvalidAccountVersion","type":"error"},{"inputs":[],"name":"InvalidERC20Id","type":"error"},{"inputs":[],"name":"InvalidERC721Amount","type":"error"},{"inputs":[],"name":"InvalidRegistry","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"NoFallback","type":"error"},{"inputs":[],"name":"NumeraireNotFound","type":"error"},{"inputs":[],"name":"OnlyCreditor","type":"error"},{"inputs":[],"name":"OnlyFactory","type":"error"},{"inputs":[],"name":"OnlyLiquidator","type":"error"},{"inputs":[],"name":"OnlyOwner","type":"error"},{"inputs":[],"name":"TooManyAssets","type":"error"},{"inputs":[],"name":"UnknownAsset","type":"error"},{"inputs":[],"name":"UnknownAssetType","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"assetManager","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"AssetManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"creditor","type":"address"},{"indexed":true,"internalType":"address","name":"liquidator","type":"address"}],"name":"MarginAccountChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"merklOperator","type":"address"},{"indexed":false,"internalType":"bool","name":"status","type":"bool"}],"name":"MerklOperatorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"numeraire","type":"address"}],"name":"NumeraireSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"},{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"type_","type":"uint256"}],"name":"Skim","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"address[]","name":"assets","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"types","type":"uint256[]"}],"name":"Transfers","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"ACCOUNTS_GUARD","outputs":[{"internalType":"contract IAccountsGuard","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ACCOUNT_VERSION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ASSET_LIMIT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MERKL_DISTRIBUTOR","outputs":[{"internalType":"contract IDistributor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"approvedCreditor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"},{"internalType":"address","name":"bidder","type":"address"}],"name":"auctionBid","outputs":[{"internalType":"uint256[]","name":"assetAmounts_","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"}],"name":"auctionBoughtIn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"closeMarginAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"creditor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"endAuction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"erc1155Balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"erc20Balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"actionTarget","type":"address"},{"internalType":"bytes","name":"actionData","type":"bytes"}],"name":"flashAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"callbackData","type":"bytes"},{"internalType":"address","name":"actionTarget","type":"address"},{"internalType":"bytes","name":"actionData","type":"bytes"}],"name":"flashActionByCreditor","outputs":[{"internalType":"uint256","name":"accountVersion","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"generateAssetData","outputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"numeraire_","type":"address"}],"name":"getAccountValue","outputs":[{"internalType":"uint256","name":"accountValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCollateralValue","outputs":[{"internalType":"uint256","name":"collateralValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFreeMargin","outputs":[{"internalType":"uint256","name":"freeMargin","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidationValue","outputs":[{"internalType":"uint256","name":"liquidationValue","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUsedMargin","outputs":[{"internalType":"uint256","name":"usedMargin","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"inAuction","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"openPosition","type":"uint256"}],"name":"increaseOpenPosition","outputs":[{"internalType":"uint256","name":"accountVersion","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"},{"internalType":"address","name":"registry_","type":"address"},{"internalType":"address","name":"creditor_","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isAccountLiquidatable","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isAccountUnhealthy","outputs":[{"internalType":"bool","name":"isUnhealthy","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"isAssetManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastActionTimestamp","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumMargin","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"numeraire","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC1155Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newCreditor","type":"address"}],"name":"openMarginAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"assetManager","type":"address"}],"name":"removeAssetManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"}],"name":"removeMerklOperator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"creditor_","type":"address"}],"name":"setApprovedCreditor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetManagers","type":"address[]"},{"internalType":"bool[]","name":"statuses","type":"bool[]"},{"internalType":"bytes[]","name":"datas","type":"bytes[]"}],"name":"setAssetManagers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"operators","type":"address[]"},{"internalType":"bool[]","name":"operatorStatuses","type":"bool[]"},{"internalType":"bytes[]","name":"operatorDatas","type":"bytes[]"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"setMerklOperators","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"numeraire_","type":"address"}],"name":"setNumeraire","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"type_","type":"uint256"}],"name":"skim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"initiator","type":"address"}],"name":"startLiquidation","outputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"},{"internalType":"address","name":"creditor_","type":"address"},{"internalType":"uint96","name":"minimumMargin_","type":"uint96"},{"internalType":"uint256","name":"openPosition","type":"uint256"},{"components":[{"internalType":"uint256","name":"assetValue","type":"uint256"},{"internalType":"uint256","name":"collateralFactor","type":"uint256"},{"internalType":"uint256","name":"liquidationFactor","type":"uint256"}],"internalType":"struct AssetValueAndRiskFactors[]","name":"assetAndRiskValues","type":"tuple[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newImplementation","type":"address"},{"internalType":"address","name":"newRegistry","type":"address"},{"internalType":"uint256","name":"newVersion","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeAccount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"oldImplementation","type":"address"},{"internalType":"address","name":"oldRegistry","type":"address"},{"internalType":"uint256","name":"oldVersion","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"upgradeHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"assetAddresses","type":"address[]"},{"internalType":"uint256[]","name":"assetIds","type":"uint256[]"},{"internalType":"uint256[]","name":"assetAmounts","type":"uint256[]"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
6101003461018e57601f6158d338819003918201601f19168301916001600160401b038311848410176101925780849260609460405283398101031261018e57610048816101a6565b90610061604061005a602084016101a6565b92016101a6565b6e22d473030f116ddee9f6b43ac78ba360e052600280546001600160a01b031916331790556080929092526001600160a01b0390811660a0521660c05260405161571890816101bb823960805181818161059001528181610c9101528181610db201528181612ba60152612bec015260a05181818161027201528181610378015281816105c0015281816107fd01528181610c1301528181610de201528181610f770152818161115901528181611236015281816112fb0152818161156401528181611a3d01528181611c7701528181611dce0152818161215c0152818161260b01528181612a9b01528181612c1c01528181612fb10152818161322d015281816134380152613524015260c05181818161137d015281816121e20152612f56015260e05181614d570152f35b5f80fd5b634e487b7160e01b5f52604160045260245ffd5b51906001600160a01b038216820361018e5756fe6080806040526004361015610028575b50346100245763b25befbf60e01b5f5260045ffd5b5f80fd5b5f905f3560e01c90816304dababe1461364557816305549367146134f95750806305c0608c146134d257806305f1a68d1461340757806308f1bdaa146133b25780630b9252f0146131d4578063150b7a02146131af5780631e03544214612f85578063219461ed14612f4057806321ab889d14612f075780632723ae1f14612ede5780632c3c717314612bd55780632dd3100014612b90578063358bca7714612a7e5780634046ebae14612a5357806342c8d71514612a3857806347cf6c9e146129f75780634a1ec375146125d45780635ae80951146125185780636048dd00146120a6578063653b92b414611d3c578063656e872914611c4c57806368719ee314611a0c57806371a84c71146119b45780637afc01be146115055780637b103999146114e45780637c0b5337146112cb57806387bbaeb6146112a95780638da5cb5b14611280578063907a9429146112655780639086ab3914611220578063a0d9f5cd1461113c578063ac891de4146110f0578063adb33ab6146110ad578063b8eabab014610f4f578063bc68c67614610f33578063bfed6b0314610f17578063c047e56314610efc578063c0c53b8b14610d79578063d5d4305b14610d54578063df03e1a014610be1578063e1c50100146107ca578063e36db785146107a1578063eac697c91461077b578063edfe1faf1461073b578063f23a6e61146106e4578063f2fde38b14610573578063fb787d01146103535763fe67a54b0361000f5734610313578060031936011261031357805460301c6001600160a01b0316330361034457807f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b15610321576040516306e8502160e11b815260048101839052828160248183865af1908115610339578391610324575b5050815460ff19168255803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b8161030c916138a5565b6103135780f35b80fd5b6040513d84823e3d90fd5b50fd5b8161032e916138a5565b61032157815f6102cc565b6040513d85823e3d90fd5b6399b01c4760e01b8152600490fd5b50346103135780600319360112610313576002546001600160a01b03163303610564577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b15610313576040516306e8502160e11b815260048101829052818160248183875af180156103165790829161054f575b50549160ff831661054057600354926001600160a01b0384169081156105325783946001600160601b0360a01b166003556601000000000000600160d01b031916835561045583806001546001600160601b03198116600155610434613abe565b604051630578da2160e41b8152968795869490938593918b6004860161427a565b039260601c5af1801561052757610507575b50803b156104ee57828091602460405180948193632ce1ed1560e01b83523060048401525af19081156103395783916104f2575b50506040519082807fa521c704a92cee73ea8410dc251eece5bd5362f6887c45f3e9566e817aee73698180a3803b156104ee5781600481858094633b4438cf60e21b83525af18015610316576103025750f35b5050fd5b816104fc916138a5565b61032157815f61049b565b610522903d8086833e61051a81836138a5565b810190614255565b610467565b6040513d86823e3d90fd5b623cb2cf60e01b8452600484fd5b635441f54960e11b8252600482fd5b81610559916138a5565b61031357805f6103d3565b635fc483c560e01b8152600490fd5b50346103135760203660031901126103135761058d613655565b907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031633036106d5577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156106d1576040516306e8502160e11b815260048101839052828160248183865af18015610339579083916106bc575b505460ff81166106ad5763ffffffff9060101c1661012c81018091116106995742111561068a57819260018060a01b03166001600160601b0360a01b6002541617600255803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b63036a87d960e21b8252600482fd5b634e487b7160e01b83526011600452602483fd5b635441f54960e11b8352600483fd5b816106c6916138a5565b6106d157815f61061a565b5080fd5b630636a15760e11b8152600490fd5b50346103135760a0366003190112610313576106fe613655565b50610707613681565b506084356001600160401b0381116106d1576107279036906004016136ab565b505060405163f23a6e6160e01b8152602090f35b5034610313578060031936011261031357602090610757614374565b61075f614130565b91828211156107735750035b604051908152f35b91505061076b565b503461031357806003193601126103135763ffffffff6020915460101c16604051908152f35b50346103135780600319360112610313576003546040516001600160a01b039091168152602090f35b5034610313576020366003190112610313576107e4613655565b815490919060301c6001600160a01b03163303610344577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b15610313576040516306e8502160e11b815260048101829052818160248183875af1801561031657908291610bcc575b505465ffffffff00ff19164260101b65ffffffff00001617600190811782556003549054936001600160a01b039190911691906001600160601b0385168261089e613abe565b9282829992939a8960018060a01b03600454166108d2886040519e8f9687958695633a137bf360e11b875260048701614202565b039160601c5afa978815610a2a578698610b00575b506040516342f2e72760e11b81526001600160a01b03909516600486015260248501849052602085604481898b5af1948515610a2a578695610acc575b5061092f84866139b6565b8515908115610a47575b50610a3957803b15610a3557858091600460405180948193633b4438cf60e21b83525af18015610a2a57908691610a11575b505061099197969593929161099f6109ad926040519a8b9a60e08c5260e08c0190613733565b908a820360208c015261376f565b9088820360408a015261376f565b926060870152608086015260a085015283810360c0850152602080845192838152019301915b8181106109e1575050500390f35b919350916020606060019260408751805183528481015185840152015160408201520194019101918493926109d3565b81610a1b916138a5565b610a2657845f61096b565b8480fd5b6040513d88823e3d90fd5b8580fd5b627616e960e61b8652600486fd5b98999593919794929050869787985b8b518a1015610ab5578b906040610a798c610a718186613aaa565b515194613aaa565b51015191828102928184041490151715610aa157600191610a99916139b6565b990198610a56565b634e487b7160e01b8a52601160045260248afd5b612710919395979b9a92949699500410155f610939565b9094506020813d602011610af8575b81610ae8602093836138a5565b810103126100245751935f610924565b3d9150610adb565b9097503d8087833e610b1281836138a5565b810190602081830312610bc4578051906001600160401b038211610bc8570181601f82011215610bc457805190610b48826138c6565b92610b5660405194856138a5565b82845260206060818601940283010191818311610bc057602001925b828410610b845750505050965f6108e7565b606084830312610bc0576020606091604051610b9f81613840565b86518152828701518382015260408701516040820152815201930192610b72565b8980fd5b8680fd5b8780fd5b81610bd6916138a5565b61031357805f610858565b503461031357602036600319011261031357610bfb613655565b815460301c6001600160a01b03163303610d455781907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af1908115610527578491610d30575b5050600280546001600160a01b0319166001600160a01b039283169081179091557f000000000000000000000000000000000000000000000000000000000000000090911690813b15610d2b578391602483926040519485938492637ccd031560e01b845260048401525af1908115610339578391610d16575b5050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81610d20916138a5565b61032157815f610ce8565b505050fd5b81610d3a916138a5565b6104ee57825f610c6e565b6399b01c4760e01b8252600482fd5b50346103135780600319360112610313576020610d6f6143c2565b6040519015158152f35b503461031357606036600319011261031357610d93613655565b90610d9c613681565b6044356001600160a01b038116808203610ef8577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303610ee9577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692833b15610a26576040516306e8502160e11b815260016004820152858160248183895af18015610a2a57908691610ed4575b50506001600160a01b03811615610ec557849560018060a01b03166001600160601b0360a01b60025416176002556001600160601b036001549181199060601b16911617600155610eb6575b50803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b610ebf90615425565b5f610e89565b6311a1e69760e01b8552600485fd5b81610ede916138a5565b610a2657845f610e3d565b630636a15760e11b8452600484fd5b8380fd5b5034610313578060031936011261031357602061076b614374565b50346103135780600319360112610313576020604051600f8152f35b5034610313578060031936011261031357602060405160038152f35b503461031357610f5e366137d2565b60025493969590936001600160a01b0316330361109e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031694853b15610bc4576040516306e8502160e11b8152600481018890528781602481838b5af180156110935790889161107e575b505460ff811661106f579761100e6110209361101693611026999a9b65ffffffff00004260101b169065ffffffff00001916178c5536916138dd565b94369161394f565b913394369161394f565b916144e4565b61102e6143c2565b611060578082913b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b6302fb7a9160e31b8252600482fd5b635441f54960e11b8852600488fd5b81611088916138a5565b610bc457865f610fd2565b6040513d8a823e3d90fd5b635fc483c560e01b8652600486fd5b5034610313576040366003190112610313576020906040906001600160a01b036110d5613655565b168152600b8352818120602435825283522054604051908152f35b5034610313578060031936011261031357602061110b614130565b6001600160601b03600154168111908161112b575b506040519015158152f35b90506111356142e2565b1082611120565b50346103135760203660031901126103135780611157613655565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156104ee576040516306e8502160e11b815260016004820152838160248183875af190811561052757849161120b575b5050338352600c6020526040832080546001600160a01b0319166001600160a01b03909216919091179055803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81611215916138a5565b6104ee57825f6111b4565b50346103135780600319360112610313576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b5034610313578060031936011261031357602061076b6142e2565b50346103135780600319360112610313576002546040516001600160a01b039091168152602090f35b503461031357806003193601126103135760ff60209154166040519015158152f35b5034610313576020366003190112610313576112e5613655565b6002546001600160a01b031633036114d55781907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af19081156105275784916114c0575b505060405163131bac4760e11b81523060048201526001600160a01b0382811660248301527f00000000000000000000000000000000000000000000000000000000000000001690602081604481855afa9081156114b5578591611480575b50611420575b506040518381526001600160a01b03909116907fd61f2c33e366f60a2f32f50d952952a311e9ed22662d58f31dc6a3e2c8ba61f190602090a2803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b803b15610d2b5760405163bdac7ca360e01b81523060048201526001600160a01b03831660248201529084908290604490829084905af190811561052757849161146b575b506113bb565b81611475916138a5565b6104ee57825f611465565b9450506020843d6020116114ad575b8161149c602093836138a5565b81010312610024578493515f6113b5565b3d915061148f565b6040513d87823e3d90fd5b816114ca916138a5565b6104ee57825f611356565b635fc483c560e01b8252600482fd5b5034610313578060031936011261031357602060015460601c604051908152f35b5034610313576060366003190112610313576004356001600160401b0381116106d1576115369036906004016136ab565b9190611540613681565b906044356001600160401b038111610ef8576115609036906004016136ab565b90947f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031693843b15610a35576040516306e8502160e11b8152600160048201528681602481838a5af180156119a957908791611994575b505460ff81166119855765ffffffff000019164260101b65ffffffff0000161786556003546001600160a01b03169333851415938480611961575b61195257879861163b9161160f9998996142be565b50886040805161161e81613840565b6060815282602082015201526116326142be565b50810190613d25565b93949096929192333b1561194e57604051637afcd25560e11b815260206004820152918b9183918291611673916024840191906140a2565b038183335af18015611943578a9589928892611921575b5090816116a392519060406020820151910151916144e4565b6117ee575b6116e2956116c0926116bb878094614afe565b614ce0565b6040518094819263a129568d60e01b835260206004840152602483019061400c565b0381836001600160a01b0386165af180156114b5576117169286916117cc575b508051906040602082015191015191614f8c565b81151590816117c4575b5061177f575b50506117306143c2565b611060578082913b15610321578190600460405180948193633b4438cf60e21b83525af180156103165761176a575b602060405160038152f35b6117758280926138a5565b610313578061175f565b803b156106d157818091602460405180948193632ce1ed1560e01b83523060048401525af18015610316571561172657816117b9916138a5565b6106d157815f611726565b90505f611720565b6117e891503d8088833e6117e081836138a5565b810190613ef5565b5f611702565b6002546001600160a01b03168952600c6020526040892080546001600160a01b0319169055925061181d613abe565b89959291956118ba575b60015460601c803b156118b657611859968c8094604051998a9586948593631a1bb38960e21b8552336004860161427a565b03925af19384156118ab578994611890575b506116e2956116c0926116bb87809461188333615425565b94505050925095506116a8565b8461189e91979592976138a5565b610bc8578792945f61186b565b6040513d8b823e3d90fd5b8b80fd5b808b878c858360015460601c926118e760405197889687958694630578da2160e41b86526004860161427a565b03925af18015611916576118fc575b50611827565b61190f903d808e833e61051a81836138a5565b505f6118f6565b6040513d8e823e3d90fd5b87935061193192509690966138a5565b61193f5786858a955f61168a565b8880fd5b6040513d8c823e3d90fd5b8a80fd5b63a9faa8f560e01b8852600488fd5b506002546001600160a01b039081168952600c6020526040892054163314156115fa565b635441f54960e11b8752600487fd5b8161199e916138a5565b610a3557855f6115bf565b6040513d89823e3d90fd5b50346103135760403660031901126103135760406119d0613655565b916119d9613681565b9260018060a01b03168152600d602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b503461031357602036600319011261031357611a26613655565b6002549091906001600160a01b03163303610564577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156106d1576040516306e8502160e11b815260016004820152828160248183865af1801561033957908391611c37575b505460ff81166106ad5765ffffffff000019164260101b65ffffffff000016178255611ac1613abe565b60035492956001600160a01b03938416939192909182168414611c285785968415159384611bcd575b60015460601c803b1561193f57611b1c9389809460405196879586948593631a1bb38960e21b85528b6004860161427a565b03925af1908115610a2a578691611bb8575b5050611b3990615425565b611b6a5750803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b803b156104ee57828091602460405180948193632ce1ed1560e01b83523060048401525af1908115610339578391611ba3575b50610e89565b81611bad916138a5565b61032157815f611b9d565b81611bc2916138a5565b610a2657845f611b2e565b80888460015460601c85838b611bf960405197889687958694630578da2160e41b86526004860161427a565b03925af180156118ab57611c0e575b50611aea565b611c21903d808b833e61051a81836138a5565b505f611c08565b63ce5a86f760e01b8652600486fd5b81611c41916138a5565b6106d157815f611a97565b503461031357611c5b366137d2565b60025490969592939192906001600160a01b0316330361109e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031694853b15610bc4576040516306e8502160e11b8152600160048201528781602481838b5af1801561109357908891611d27575b505460ff1661198557611016611cfb959361100e899a9694611cf59436916138dd565b91614f8c565b803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81611d31916138a5565b610bc457865f611cd2565b5034610313576080366003190112610313576004356001600160401b0381116106d157611d6d903690600401613931565b6024356001600160401b0381116120a257611d8c90369060040161399b565b916044356001600160401b0381116106d157611dac90369060040161399b565b90611db561366b565b815490939060301c6001600160a01b03163303610d45577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156120a2576040516306e8502160e11b815260048101849052838160248183865af180156105275790849161208d575b505060015460601c90836040518093630c3d587560e11b8252602060048301528180611e576024820189613733565b03915afa918215610527578492612071575b50835b835181101561200957611e7f8187613aaa565b5115612001576001611e918285613aaa565b5103611ee6576001906001600160a01b03611eac8287613aaa565b51168652600a60205260408620545b80611ec6838a613aaa565b5111611ed5575b505b01611e6c565b611edf8289613aaa565b525f611ecd565b6002611ef28285613aaa565b5103611fa4576001600160a01b03611f0a8286613aaa565b5116906020611f19828b613aaa565b516024604051809581936331a9108f60e11b835260048301525afa8015610a2a578690611f69575b60019250306001600160a01b039190911603611f615760ff825b16611ebb565b60ff86611f5b565b50906020813d8211611f9c575b81611f83602093836138a5565b81010312610a355790611f97600192613e84565b611f41565b3d9150611f76565b6003611fb08285613aaa565b5103611ff2576001906001600160a01b03611fcb8287613aaa565b51168652600b60205260408620611fe2828b613aaa565b5187526020526040862054611ebb565b63b8bcee3f60e01b8552600485fd5b600190611ecf565b848681846120198b848e8b6144e4565b803b156106d1578190600460405180948193633b4438cf60e21b83525af180156103395761205c575b604051602080825281906120589082018561376f565b0390f35b6120678380926138a5565b6106d15781612042565b6120869192503d8086833e61051a81836138a5565b905f611e69565b81612097916138a5565b6120a257825f611e28565b8280fd5b50346103135760a0366003190112610313576004356001600160401b0381116106d1576120d79036906004016137a2565b91906024356001600160401b0381116120a2576120f89036906004016137a2565b936044356001600160401b038111610a26576121189036906004016137a2565b93909261212361366b565b926084356001600160401b038111610bc8576121439036906004016137a2565b60025491969093916001600160a01b03163303612509577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031697883b15610bc0576040516306e8502160e11b8152600160048201528a81602481838e5af180156124fe57908b916124e9575b505465ffffffff000019164260101b65ffffffff000016178a55838b148015906124df575b6124cd577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031695938a5b8181106122ce575050505050508580975050855b81811061225857868087803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b61226b612266828488614030565b614040565b833b15610bc857604051630d0a119360e31b81526001600160a01b038681166004830152919091166024820152878160448183885af19081156110935788916122b9575b5050600101612222565b816122c3916138a5565b610bc457865f6122af565b6122dc612266828489614030565b60405163131bac4760e11b81523060048201526001600160a01b03821660248201526020816044818d5afa908115612427578f918f91859291612494575b506123319161232c911515938a614030565b614054565b151503612438575b8d612345838688614061565b9050612399575b907fd61f2c33e366f60a2f32f50d952952a311e9ed22662d58f31dc6a3e2c8ba61f1602061238161232c86600197968c614030565b926040519315158452858060a01b031692a20161220e565b61232c836123a79289614030565b6123b2838688614061565b916001600160a01b0384163b1561243457908f916123e56040519485938493630ede8ef360e11b855233600486016140c2565b0381836001600160a01b0387165af18015612427578f918f9161240a575b505061234c565b81925090612417916138a5565b612423578d8d5f612403565b8c80fd5b8e604051903d90823e3d90fd5b8f80fd5b883b156124235760405163bdac7ca360e01b81523060048201526001600160a01b03821660248201528d81604481838e5af1801561242757908e9161247f575b5050612339565b81612489916138a5565b612423578c5f612478565b925050506020813d82116124c5575b816124b0602093836138a5565b8101031261002457518e90839061233161231a565b3d91506124a3565b6001621398b960e31b03198a5260048afd5b50808414156121dc565b816124f3916138a5565b610bc057895f6121b7565b6040513d8d823e3d90fd5b635fc483c560e01b8952600489fd5b503461031357602036600319011261031357612532613655565b90602061253d613abe565b60015460035460405163068e097b60e51b815297889560609390931c948694859461257994919390916001600160a01b03169060048701614202565b03915afa9081156125c85790612595575b602090604051908152f35b506020813d6020116125c0575b816125af602093836138a5565b81010312610024576020905161258a565b3d91506125a2565b604051903d90823e3d90fd5b5034610313576060366003190112610313576125ee613655565b6002546024359291604435916001600160a01b031633036129e8577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b15610ef8576040516306e8502160e11b815260048101859052848160248183885af180156114b5579085916129d3575b505465ffffffff000019164260101b65ffffffff0000161784556001600160a01b039091169083826127785750478480808084335af1953d15612770573d966126ae88613cc4565b976126bc604051998a6138a5565b88523d8760208a013e5b1561274b578596505b81612705575b50505050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b60405193845260208401526040830152606082015233907fb180a6a3fa923907a3d3d7175c65292a5003d4f5b24dea54d607814d5664378d60803092a35f8080806126d5565b60405162461bcd60e51b8152602060048201528061276c602482018a61400c565b0390fd5b6060966126c6565b6001820361281d5784956040516370a0823160e01b8152306004820152602081602481885afa9081156119a95787916127e8575b50848752600a6020526040872054908181116127ca575b50506126cf565b6127d59293506141bd565b906127e1823386615395565b5f806127c3565b9650506020863d602011612815575b81612804602093836138a5565b81010312610024578695515f6127ac565b3d91506127f7565b600282036128ed576006549585805b888110612899575b508697506126cf5760019150833b15610a3557604051632142170760e11b815230600482015233602482015260448101829052868160648183895af19081156119a95787916128845750506126cf565b8161288e916138a5565b610a3557855f6127c3565b856128a382613a09565b905460039190911b1c6001600160a01b031614806128d5575b6128c85760010161282c565b505085965060015f612834565b50826128e082613a35565b90549060031b1c146128bc565b60038203611ff2578495604051627eeac760e11b8152306004820152816024820152602081604481885afa9081156119a957879161299e575b50848752600b6020526040872082885260205260408720549081811161294d5750506126cf565b6129589293506141bd565b90833b15610a3557604051637921219560e11b815286818061298086863330600486016141ca565b038183895af19081156119a9578791156127c3578161288e916138a5565b9650506020863d6020116129cb575b816129ba602093836138a5565b81010312610024578695515f612926565b3d91506129ad565b816129dd916138a5565b610ef857835f612666565b635fc483c560e01b8352600483fd5b5034610313576020366003190112610313576020906001600160a01b03612a1c613655565b168152600c8252604060018060a01b0391205416604051908152f35b5034610313578060031936011261031357602061076b614130565b50346103135780600319360112610313575460405160309190911c6001600160a01b03168152602090f35b50346103135760203660031901126103135780612a99613655565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af1908115610527578491612b7b575b505033808452600d602090815260408086206001600160a01b03949094165f81815294835293819020805460ff19169055518581527f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c19190a3803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81612b85916138a5565b6104ee57825f612af6565b50346103135780600319360112610313576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461031357612be4366136d8565b9294939192917f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163303612ecf577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031693843b15610a35576040516306e8502160e11b8152600481018790528681602481838a5af180156119a957908791612eba575b505460ff81166119855765ffffffff000019164260101b65ffffffff0000161786557f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8054600180546001600160a01b031983166001600160a01b03968716179093556001600160601b03831660608b811b6bffffffffffffffffffffffff191691909117909155909891901c9216808314159081612e41575b50612e32576003546001600160a01b03169081612db6575b50508495303b15610a3557612d758693604051958694859463026d5d5f60e11b865260018060a01b031660048601526024850152600360448501526080606485015260848401916140a2565b038183305af1908115610339578391610d16575050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b869160246080926040519485938492638c48052360e01b845260048401525af1908115610a2a578691612e00575b5015612df1575f80612d29565b63a93eca7960e01b8552600485fd5b612e22915060803d608011612e2b575b612e1a81836138a5565b8101906140f9565b5050505f612de4565b503d612e10565b6311a1e69760e01b8652600486fd5b9050602060018060a01b036004541660246040518094819363e4ec552b60e01b835260048301525afa9081156119a9578791612e80575b50155f612d11565b90506020813d602011612eb2575b81612e9b602093836138a5565b81010312610bc457612eac906140ec565b5f612e78565b3d9150612e8e565b81612ec4916138a5565b610a3557855f612c77565b630636a15760e11b8552600485fd5b50346103135780600319360112610313576004546040516001600160a01b039091168152602090f35b5034610313576020366003190112610313576020906040906001600160a01b03612f2f613655565b168152600a83522054604051908152f35b50346103135780600319360112610313576040517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03168152602090f35b503461031357612f94366137d2565b6002549496959294929391926001600160a01b0316330361109e577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031694853b15610bc4576040516306e8502160e11b8152600160048201528781602481838b5af180156110935790889161319a575b505465ffffffff000019164260101b65ffffffff000016178755878114801590613190575b61317e578697875b81811061306f57888089803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b61307d61226682848a614030565b61308b61232c838689614030565b33808c52600d60209081526040808e206001600160a01b039095165f81815295835294819020805460ff95151595861660ff19919091161790555192835283927f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c19190a36130fa828689614061565b905061310a575b50600101613039565b61311861232c838689614030565b61312383878a614061565b9092803b1561242357613151938d809460405196879586948593635f4860df60e01b855233600486016140c2565b03925af1908115611943578a91613169575b50613101565b81613173916138a5565b61193f57885f613163565b6001621398b960e31b03198752600487fd5b5081881415613031565b816131a4916138a5565b610bc457865f61300c565b5034610313576131be366136d8565b50505050506020604051630a85bd0160e11b8152f35b5034610313576040366003190112610313576131ee613655565b6024356001600160401b0381116120a25761320d9036906004016136ab565b919060018060a01b0360025416803314908115613390575b5015613353577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031692833b15610a26576040516306e8502160e11b81526001600482015285908181602481838a5af180156103165761333e575b505460ff811661332f57926116c0816132e3816116bb6132c982988c986132e89b65ffffffff00004260101b169065ffffffff00001916178a55810190613d25565b9993809792969197519060406020820151910151916144e4565b614afe565b0381836001600160a01b0386165af180156105275761102692859161331b57508051906040602082015191015191614f8c565b6117e891503d8087833e6117e081836138a5565b635441f54960e11b8652600486fd5b81613348916138a5565b610a2657845f613287565b60405162461bcd60e51b8152602060048201526015602482015274209d1027b7363c9020b9b9b2ba1026b0b730b3b2b960591b6044820152606490fd5b855250600d60209081526040808620335f908152925281205460ff1690613225565b50346103135780600319360112610313576133eb6133f96120586133d4613abe565b919390604051958695606087526060870190613733565b90858203602087015261376f565b90838203604085015261376f565b503461031357602036600319011261031357613421613655565b6002549091906001600160a01b03163303610564577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316803b156106d1576040516306e8502160e11b815260016004820152828160248183865af18015610339579083916134bd575b50506003546001600160a01b03166134ae57611cfb82936143f0565b63ce5a86f760e01b8252600482fd5b816134c7916138a5565b6106d157815f613492565b503461031357806003193601126103135760206001600160601b0360015416604051908152f35b90503461002457602036600319011261002457600354600435906001600160a01b03163303613636577f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691823b15610024576306e8502160e11b8152600160048201525f8160248183875af1801561362b57613616575b50825460ff81166136075765ffffffff000019164260101b65ffffffff00001617835580151590816135e0575b50611060578082913b15610321578190600460405180948193633b4438cf60e21b83525af180156103165761176a57602060405160038152f35b90506136006135ed614374565b916001600160601b0360015416906139b6565b115f6135a6565b635441f54960e11b8452600484fd5b6136239193505f906138a5565b5f915f613579565b6040513d5f823e3d90fd5b63a9faa8f560e01b5f5260045ffd5b3461002457613653366136d8565b005b600435906001600160a01b038216820361002457565b606435906001600160a01b038216820361002457565b602435906001600160a01b038216820361002457565b35906001600160a01b038216820361002457565b9181601f84011215610024578235916001600160401b038311610024576020838186019501011161002457565b906080600319830112610024576004356001600160a01b038116810361002457916024356001600160a01b0381168103610024579160443591606435906001600160401b0382116100245761372f916004016136ab565b9091565b90602080835192838152019201905f5b8181106137505750505090565b82516001600160a01b0316845260209384019390920191600101613743565b90602080835192838152019201905f5b81811061378c5750505090565b825184526020938401939092019160010161377f565b9181601f84011215610024578235916001600160401b038311610024576020808501948460051b01011161002457565b6060600319820112610024576004356001600160401b03811161002457816137fc916004016137a2565b929092916024356001600160401b038111610024578161381e916004016137a2565b92909291604435906001600160401b0382116100245761372f916004016137a2565b606081019081106001600160401b0382111761385b57604052565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761385b57604052565b604081019081106001600160401b0382111761385b57604052565b90601f801991011681019081106001600160401b0382111761385b57604052565b6001600160401b03811161385b5760051b60200190565b9291906138e9816138c6565b936138f760405195866138a5565b602085838152019160051b810192831161002457905b82821061391957505050565b6020809161392684613697565b81520191019061390d565b9080601f830112156100245781602061394c933591016138dd565b90565b92919061395b816138c6565b9361396960405195866138a5565b602085838152019160051b810192831161002457905b82821061398b57505050565b813581526020918201910161397f565b9080601f830112156100245781602061394c9335910161394f565b919082018092116139c357565b634e487b7160e01b5f52601160045260245ffd5b906139e1826138c6565b6139ee60405191826138a5565b82815280926139ff601f19916138c6565b0190602036910137565b600654811015613a215760065f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b600854811015613a215760085f5260205f2001905f90565b600554811015613a215760055f5260205f2001905f90565b600754811015613a215760075f5260205f2001905f90565b600954811015613a215760095f5260205f2001905f90565b8054821015613a21575f5260205f2001905f90565b8051821015613a215760209160051b010190565b6005549060065460075491828285010192613ad8846139d7565b94613aeb613ae5866139d7565b956139d7565b935f915b808310613bd857505f905b808210613b805750505f905b828210613b1257505050565b60018091613b1f84613a65565b838060a01b0391549060031b1c16613b3685613a7d565b90549060031b1c9080613b49848d613aaa565b5281613b55848c613aaa565b525f52600b60205260405f20905f5260205260405f2054613b768289613aaa565b5201910190613b06565b909160018091613b8f85613a09565b838060a01b0391549060031b1c16613ba7828c613aaa565b52613bb185613a35565b90549060031b1c613bc2828b613aaa565b5281613bce828a613aaa565b5201920190613afa565b9180613be5600192613a4d565b838060a01b0391549060031b1c1680613bfe838c613aaa565b525f52600a60205260405f2054613c158289613aaa565b520191613aef565b91906080838203126100245760405190613c368261386f565b819380356001600160401b0381116100245782613c54918301613931565b835260208101356001600160401b0381116100245782613c7591830161399b565b602084015260408101356001600160401b0381116100245782613c9991830161399b565b60408401526060810135916001600160401b03831161002457606092613cbf920161399b565b910152565b6001600160401b03811161385b57601f01601f191660200190565b81601f8201121561002457803590613cf682613cc4565b92613d0460405194856138a5565b8284526020838301011161002457815f926020809301838601378301015290565b91909160a0818403126100245780356001600160401b0381116100245783613d4e918301613c1d565b9260208201356001600160401b0381116100245781613d6e918401613c1d565b9260408301356001600160401b0381116100245783016060818403126100245760405190613d9b82613840565b80356001600160401b03811161002457810184601f82011215610024578035613dc3816138c6565b91613dd160405193846138a5565b81835260208084019260061b8201019087821161002457602001915b818310613e4c5750505090604091835260208101356020840152013560408201529260608101356001600160401b0381116100245783613e2e918301613cdf565b9260808201356001600160401b0381116100245761394c9201613cdf565b6040838903126100245760206040918251613e668161388a565b613e6f86613697565b81528286013583820152815201920191613ded565b51906001600160a01b038216820361002457565b9080601f83011215610024578151613eaf816138c6565b92613ebd60405194856138a5565b81845260208085019260051b82010192831161002457602001905b828210613ee55750505090565b8151815260209182019101613ed8565b602081830312610024578051906001600160401b03821161002457016080818303126100245760405191613f288361386f565b81516001600160401b03811161002457820181601f8201121561002457805190613f51826138c6565b91613f5f60405193846138a5565b80835260208084019160051b8301019184831161002457602001905b828210613ff457505050835260208201516001600160401b0381116100245781613fa6918401613e98565b602084015260408201516001600160401b0381116100245781613fca918401613e98565b604084015260608201516001600160401b03811161002457613fec9201613e98565b606082015290565b6020809161400184613e84565b815201910190613f7b565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9190811015613a215760051b0190565b356001600160a01b03811681036100245790565b3580151581036100245790565b9190811015613a215760051b81013590601e19813603018212156100245701908135916001600160401b038311610024576020018236038113610024579190565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160a01b039091168152901515602082015260606040820181905261394c939101916140a2565b5190811515820361002457565b91908260809103126100245761410e826140ec565b9161411b60208201613e84565b91606061412a60408401613e84565b92015190565b6003546001600160a01b031680156141b85760206024916040519283809263c11002df60e01b82523060048301525afa801561362b575f90614184575b61394c91506001600160601b0360015416906139b6565b506020813d6020116141b0575b8161419e602093836138a5565b810103126100245761394c905161416d565b3d9150614191565b505f90565b919082039182116139c357565b6001600160a01b039182168152911660208201526040810191909152606081019190915260a0608082018190525f9082015260c00190565b6001600160a01b0391821681529116602082015260a06040820181905261394c949193919261424792916142399190860190613733565b90848203606086015261376f565b91608081840391015261376f565b906020828203126100245781516001600160401b0381116100245761394c9201613e98565b926142a261394c95936142b09360018060a01b03168652608060208701526080860190613733565b90848203604086015261376f565b91606081840391015261376f565b604051906142cb8261386f565b606080838181528160208201528160408201520152565b60206142ec613abe565b6001546004805460035460405163c068a4cd60e01b81529796889660609590951c95879586956143309592949193926001600160a01b039283169216908701614202565b03915afa90811561362b575f91614345575090565b90506020813d60201161436c575b81614360602093836138a5565b81010312610024575190565b3d9150614353565b602061437e613abe565b60015460048054600354604051631b3355ef60e01b81529796889660609590951c95879586956143309592949193926001600160a01b039283169216908701614202565b6143ca614130565b6001600160601b0360015416811190816143e2575090565b90506143ec614374565b1090565b60015460405163e4ec552b60e01b81526001600160a01b039092166004830181905291906020908290602490829060601c5afa90811561362b575f9161447e575b501561446f57600480546001600160a01b031916821790557f9ed2bd12cb184cf96abec450551864ef441ead0f80ac1c3bab504ce07d5a84cd5f80a2565b635958323160e01b5f5260045ffd5b90506020813d6020116144b0575b81614499602093836138a5565b81010312610024576144aa906140ec565b5f614431565b3d915061448c565b926142a261394c95936144d66142b094608088526080880190613733565b90868203602088015261376f565b9093929193845f8260015460601c858360018060a01b036003541661451f60405197889687958694630578da2160e41b86526004860161427a565b03925af190811561362b575f91614ae4575b50825115614adc575f5b8351811015614aa75761454e8188613aaa565b5115614a9f5760016145608284613aaa565b51036146ca576145708184613aaa565b516146bb576001600160a01b036145878286613aaa565b511685614594838a613aaa565b5191805f52600a60205260405f206145ad8482546141bd565b9055805f52600a60205260405f2054156145d6575b916145d09160019493615395565b0161453b565b600554600181036145fe5750916145d091600194936145f3615605565b9193945091506145c2565b5f5b818110614618575b5050916145d091600194936145f3565b909180935061462682613a4d565b905460039190911b1c6001600160a01b0316146146495760010190889291614600565b9193925f19820192909183116139c3576001946146a88a926146846146706145d097613a4d565b8a8060a01b0391549060031b1c1691613a4d565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b6146b0615605565b919394819350614608565b6318ffa99360e31b5f5260045ffd5b60026146d68284613aaa565b510361490d5760016146e88289613aaa565b51036148fe576001600160a01b036147008286613aaa565b51169061470d8185613aaa565b51916008545f600182145f1461482057505060085415613a215760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee35483148015906147e0575b6147d157614763615593565b61476b6155e6565b803b1561002457604051632142170760e11b81523060048201526001600160a01b038816602482015260448101939093525f908390818381606481015b03925af191821561362b576001926147c1575b506145d0565b5f6147cb916138a5565b5f6147bb565b63c97d95cf60e01b5f5260045ffd5b5060065415613a215760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f546001600160a01b0316811415614757565b81811061483c575b0361476b5763c97d95cf60e01b5f5260045ffd5b8461484682613a35565b90549060031b1c14806148db575b61486057600101614820565b5f1982018281116139c3576148b3816148a661487e6148ce94613a35565b90549060031b1c61488e86613a35565b90919082549060031b91821b915f19901b1916179055565b6148ae615593565b613a09565b905460039190911b1c6001600160a01b031661468483613a09565b6148d66155e6565b614828565b50826148e682613a09565b905460039190911b1c6001600160a01b031614614854565b63053dda6d60e21b5f5260045ffd5b60036149198284613aaa565b5103614a90576001600160a01b036149318286613aaa565b51169061493e8185613aaa565b51614949828a613aaa565b51600954845f52600b60205260405f20835f5260205260405f2061496e8382546141bd565b9055845f52600b60205260405f20835f5260205260405f2054156149bb575b50833b15610024576147a8935f92838a60405197889586948593637921219560e11b855230600486016141ca565b600181036149da57506149cc615550565b6149d46155b2565b5f61498d565b5f5b8181106149eb575b50506149d4565b836149f582613a7d565b90549060031b1c14614a0a575b6001016149dc565b85614a1482613a65565b905460039190911b1c6001600160a01b031603614a02575f1982019182116139c357614684614a6983614a5c614a4c614a8196613a7d565b90549060031b1c61488e86613a7d565b614a64615550565b613a65565b905460039190911b1c6001600160a01b031691613a65565b614a896155b2565b5f806149e4565b63b8bcee3f60e01b5f5260045ffd5b6001906145d0565b5092614ad75f5160206156c35f395f51905f5293949692959660405193849360018060a01b0316973097856144b8565b0390a3565b505050509050565b614af891503d805f833e61051a81836138a5565b5f614531565b805151908115614cdb576002546001600160a01b0316915f5b818110614b5b575050805f5160206156c35f395f51905f52915193614ad7602083015192606060408201519101519060405194859460018060a01b031698856144b8565b6040830190614b6b818351613aaa565b5115614cd257606084016001614b82838351613aaa565b5103614bbc5750614bb66001928787614baf85878060a01b03614ba6828c51613aaa565b51169451613aaa565b5192615624565b01614b17565b6002614bc9838351613aaa565b5103614c5f5750905060018060a01b03614be4828551613aaa565b511690614bf5816020860151613aaa565b5191803b1561002457604051632142170760e11b81526001600160a01b0380881660048301528816602482015260448101939093525f908390818381606481015b03925af191821561362b57600192614c4f575b50614bb6565b5f614c59916138a5565b5f614c49565b614c6c8260039251613aaa565b5103614a905783516001600160a01b0390614c88908390613aaa565b5116614ca482614c9c816020890151613aaa565b519451613aaa565b5192813b15610024575f91888389614c3660405198899687958694637921219560e11b8652600486016141ca565b60019150614bb6565b505050565b9190825151908115614f8657614cf5826138c6565b92614d0360405194856138a5565b828452601f19614d12846138c6565b015f5b818110614f63575050614d27836139d7565b94614d31846139d7565b92614d3b856139d7565b905f5b868110614ee057506002546001600160a01b03908116977f000000000000000000000000000000000000000000000000000000000000000090911691823b15610024579088949160405195869363edd9444b60e01b85526080600486015260e4850190805191606060848801528251809152602061010488019301905f5b818110614ea357505050806020604092015160a4880152015160c4860152600319858203016024860152602080855192838152019401905f5b818110614e725750505083614e205f969482948894604485015260031984830301606485015261400c565b03925af1801561362b575f5160206156c35f395f51905f5295614ad793614e4c92614e62575b506139d7565b9360405194859460018060a01b031698856144b8565b5f614e6c916138a5565b5f614e46565b825180516001600160a01b03168752602090810151818801528a97506040909601958e945090920191600101614df5565b825180516001600160a01b0316865260209081015181870152979850959691959294509260019190604001950191019089969594928e9492614dbc565b80614eed6001928a613aaa565b51828060a01b03871690526020614f05828751613aaa565b5101516020614f14838c613aaa565b510152818060a01b03614f28828751613aaa565b515116614f35828c613aaa565b526020614f43828751613aaa565b510151614f508289613aaa565b5281614f5c8286613aaa565b5201614d3e565b602090604051614f728161388a565b5f81525f8382015282828901015201614d15565b50505050565b909392919360015460601c5f6040518092630c3d587560e11b8252602060048301528180614fbd6024820189613733565b03915afa90811561362b575f9161537b575b505f5b835181101561529b57614fe58188613aaa565b5115615293576001614ff78284613aaa565b5103615091576150078184613aaa565b516146bb576001600160a01b0361501e8286613aaa565b51169061502b8189613aaa565b5161503881308986615624565b825f52600a60205260405f20548015615063575b6001935f52600a6020520160405f20555b01614fd2565b60055493600160401b85101561385b5761508a816146848760018099016005556005613a95565b935061504c565b600261509d8284613aaa565b510361517b5760016150af8289613aaa565b51036148fe576001600160a01b036150c78286613aaa565b5116906150d48185613aaa565b5191803b1561002457604051632142170760e11b81526001600160a01b0388166004820152306024820152604481018490525f8160648183865af1801561362b5761516b575b5060065490600160401b82101561385b5761468482600161514094016006556006613a95565b600854600160401b81101561385b5760019261488e828561516694016008556008613a95565b61505d565b5f615175916138a5565b5f61511a565b60036151878284613aaa565b5103614a90576001600160a01b0361519f8286613aaa565b5116906151ac8185613aaa565b516151b7828a613aaa565b5190833b1561002457604051637921219560e11b81525f81806151e08686308f600486016141ca565b038183895af1801561362b57615283575b50835f52600b60205260405f20815f5260205260405f2054908115615230575b6001945f52600b60205260405f20905f526020520160405f205561505d565b600754600160401b81101561385b578561468482600161525594016007556007613a95565b60095494600160401b86101561385b5761527c8261488e886001809a016009556009613a95565b9450615211565b5f61528d916138a5565b5f6151f1565b60019061505d565b509192939490600f6152be6152b5600554600654906139b6565b600754906139b6565b1161536c576001546003546001600160a01b03169060601c803b1561002457825f918783879561530460405197889687958694631a1bb38960e21b86526004860161427a565b03925af1801561362b5761535c575b508351615322575b5050505050565b60405130956001600160a01b0316945f5160206156c35f395f51905f52949193849361534f9392856144b8565b0390a35f8080808061531b565b5f615366916138a5565b5f615313565b63df8153c760e01b5f5260045ffd5b61538f91503d805f833e61051a81836138a5565b5f614fcf565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f5114161516615418575b50156153e157565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f6153d9565b604051638c48052360e01b8152600360048201526001600160a01b0391909116906080816024815f865af1801561362b575f905f915f935f9261552b575b501561551c576001600160601b03166001600160601b0319600154161760015560018060a01b036004541660018060a01b0382160361550d575b50600380546001600160a01b031916831790555f80546601000000000000600160d01b031916603083901b6601000000000000600160d01b03161781556001600160a01b0390911691907fa521c704a92cee73ea8410dc251eece5bd5362f6887c45f3e9566e817aee73699080a3565b615516906143f0565b5f61549d565b63a93eca7960e01b5f5260045ffd5b92505050615548915060803d608011612e2b57612e1a81836138a5565b90925f615463565b600954801561557f575f190161557a61556a826009613a95565b8154905f199060031b1b19169055565b600955565b634e487b7160e01b5f52603160045260245ffd5b600854801561557f575f19016155ad61556a826008613a95565b600855565b600754801561557f575f19016155e16155cc826007613a95565b81549060018060a01b039060031b1b19169055565b600755565b600654801561557f575f19016156006155cc826006613a95565b600655565b600554801561557f575f190161561f6155cc826005613a95565b600555565b6040516323b872dd60e01b81526001600160a01b03928316600482015292909116602483015260448201929092526020905f9060649082855af19081601f3d1160015f51141615166156b5575b501561567957565b60405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606490fd5b3b153d171590505f61567156fe71e9b057bc061d94e1e08fc8c03d958f8fe3068ec75e774d478e7e32b9ff4b2aa26469706673582212209a3a8df7f3b31a9d91364ceefdcb0479c37e5a9f459182a5c83d27ad844dfa3764736f6c634300081e0033000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e590000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae
Deployed Bytecode
0x6080806040526004361015610028575b50346100245763b25befbf60e01b5f5260045ffd5b5f80fd5b5f905f3560e01c90816304dababe1461364557816305549367146134f95750806305c0608c146134d257806305f1a68d1461340757806308f1bdaa146133b25780630b9252f0146131d4578063150b7a02146131af5780631e03544214612f85578063219461ed14612f4057806321ab889d14612f075780632723ae1f14612ede5780632c3c717314612bd55780632dd3100014612b90578063358bca7714612a7e5780634046ebae14612a5357806342c8d71514612a3857806347cf6c9e146129f75780634a1ec375146125d45780635ae80951146125185780636048dd00146120a6578063653b92b414611d3c578063656e872914611c4c57806368719ee314611a0c57806371a84c71146119b45780637afc01be146115055780637b103999146114e45780637c0b5337146112cb57806387bbaeb6146112a95780638da5cb5b14611280578063907a9429146112655780639086ab3914611220578063a0d9f5cd1461113c578063ac891de4146110f0578063adb33ab6146110ad578063b8eabab014610f4f578063bc68c67614610f33578063bfed6b0314610f17578063c047e56314610efc578063c0c53b8b14610d79578063d5d4305b14610d54578063df03e1a014610be1578063e1c50100146107ca578063e36db785146107a1578063eac697c91461077b578063edfe1faf1461073b578063f23a6e61146106e4578063f2fde38b14610573578063fb787d01146103535763fe67a54b0361000f5734610313578060031936011261031357805460301c6001600160a01b0316330361034457807f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b15610321576040516306e8502160e11b815260048101839052828160248183865af1908115610339578391610324575b5050815460ff19168255803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b8161030c916138a5565b6103135780f35b80fd5b6040513d84823e3d90fd5b50fd5b8161032e916138a5565b61032157815f6102cc565b6040513d85823e3d90fd5b6399b01c4760e01b8152600490fd5b50346103135780600319360112610313576002546001600160a01b03163303610564577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b15610313576040516306e8502160e11b815260048101829052818160248183875af180156103165790829161054f575b50549160ff831661054057600354926001600160a01b0384169081156105325783946001600160601b0360a01b166003556601000000000000600160d01b031916835561045583806001546001600160601b03198116600155610434613abe565b604051630578da2160e41b8152968795869490938593918b6004860161427a565b039260601c5af1801561052757610507575b50803b156104ee57828091602460405180948193632ce1ed1560e01b83523060048401525af19081156103395783916104f2575b50506040519082807fa521c704a92cee73ea8410dc251eece5bd5362f6887c45f3e9566e817aee73698180a3803b156104ee5781600481858094633b4438cf60e21b83525af18015610316576103025750f35b5050fd5b816104fc916138a5565b61032157815f61049b565b610522903d8086833e61051a81836138a5565b810190614255565b610467565b6040513d86823e3d90fd5b623cb2cf60e01b8452600484fd5b635441f54960e11b8252600482fd5b81610559916138a5565b61031357805f6103d3565b635fc483c560e01b8152600490fd5b50346103135760203660031901126103135761058d613655565b907f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b031633036106d5577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b156106d1576040516306e8502160e11b815260048101839052828160248183865af18015610339579083916106bc575b505460ff81166106ad5763ffffffff9060101c1661012c81018091116106995742111561068a57819260018060a01b03166001600160601b0360a01b6002541617600255803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b63036a87d960e21b8252600482fd5b634e487b7160e01b83526011600452602483fd5b635441f54960e11b8352600483fd5b816106c6916138a5565b6106d157815f61061a565b5080fd5b630636a15760e11b8152600490fd5b50346103135760a0366003190112610313576106fe613655565b50610707613681565b506084356001600160401b0381116106d1576107279036906004016136ab565b505060405163f23a6e6160e01b8152602090f35b5034610313578060031936011261031357602090610757614374565b61075f614130565b91828211156107735750035b604051908152f35b91505061076b565b503461031357806003193601126103135763ffffffff6020915460101c16604051908152f35b50346103135780600319360112610313576003546040516001600160a01b039091168152602090f35b5034610313576020366003190112610313576107e4613655565b815490919060301c6001600160a01b03163303610344577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b15610313576040516306e8502160e11b815260048101829052818160248183875af1801561031657908291610bcc575b505465ffffffff00ff19164260101b65ffffffff00001617600190811782556003549054936001600160a01b039190911691906001600160601b0385168261089e613abe565b9282829992939a8960018060a01b03600454166108d2886040519e8f9687958695633a137bf360e11b875260048701614202565b039160601c5afa978815610a2a578698610b00575b506040516342f2e72760e11b81526001600160a01b03909516600486015260248501849052602085604481898b5af1948515610a2a578695610acc575b5061092f84866139b6565b8515908115610a47575b50610a3957803b15610a3557858091600460405180948193633b4438cf60e21b83525af18015610a2a57908691610a11575b505061099197969593929161099f6109ad926040519a8b9a60e08c5260e08c0190613733565b908a820360208c015261376f565b9088820360408a015261376f565b926060870152608086015260a085015283810360c0850152602080845192838152019301915b8181106109e1575050500390f35b919350916020606060019260408751805183528481015185840152015160408201520194019101918493926109d3565b81610a1b916138a5565b610a2657845f61096b565b8480fd5b6040513d88823e3d90fd5b8580fd5b627616e960e61b8652600486fd5b98999593919794929050869787985b8b518a1015610ab5578b906040610a798c610a718186613aaa565b515194613aaa565b51015191828102928184041490151715610aa157600191610a99916139b6565b990198610a56565b634e487b7160e01b8a52601160045260248afd5b612710919395979b9a92949699500410155f610939565b9094506020813d602011610af8575b81610ae8602093836138a5565b810103126100245751935f610924565b3d9150610adb565b9097503d8087833e610b1281836138a5565b810190602081830312610bc4578051906001600160401b038211610bc8570181601f82011215610bc457805190610b48826138c6565b92610b5660405194856138a5565b82845260206060818601940283010191818311610bc057602001925b828410610b845750505050965f6108e7565b606084830312610bc0576020606091604051610b9f81613840565b86518152828701518382015260408701516040820152815201930192610b72565b8980fd5b8680fd5b8780fd5b81610bd6916138a5565b61031357805f610858565b503461031357602036600319011261031357610bfb613655565b815460301c6001600160a01b03163303610d455781907f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af1908115610527578491610d30575b5050600280546001600160a01b0319166001600160a01b039283169081179091557f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e5990911690813b15610d2b578391602483926040519485938492637ccd031560e01b845260048401525af1908115610339578391610d16575b5050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81610d20916138a5565b61032157815f610ce8565b505050fd5b81610d3a916138a5565b6104ee57825f610c6e565b6399b01c4760e01b8252600482fd5b50346103135780600319360112610313576020610d6f6143c2565b6040519015158152f35b503461031357606036600319011261031357610d93613655565b90610d9c613681565b6044356001600160a01b038116808203610ef8577f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03163303610ee9577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031692833b15610a26576040516306e8502160e11b815260016004820152858160248183895af18015610a2a57908691610ed4575b50506001600160a01b03811615610ec557849560018060a01b03166001600160601b0360a01b60025416176002556001600160601b036001549181199060601b16911617600155610eb6575b50803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b610ebf90615425565b5f610e89565b6311a1e69760e01b8552600485fd5b81610ede916138a5565b610a2657845f610e3d565b630636a15760e11b8452600484fd5b8380fd5b5034610313578060031936011261031357602061076b614374565b50346103135780600319360112610313576020604051600f8152f35b5034610313578060031936011261031357602060405160038152f35b503461031357610f5e366137d2565b60025493969590936001600160a01b0316330361109e577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031694853b15610bc4576040516306e8502160e11b8152600481018890528781602481838b5af180156110935790889161107e575b505460ff811661106f579761100e6110209361101693611026999a9b65ffffffff00004260101b169065ffffffff00001916178c5536916138dd565b94369161394f565b913394369161394f565b916144e4565b61102e6143c2565b611060578082913b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b6302fb7a9160e31b8252600482fd5b635441f54960e11b8852600488fd5b81611088916138a5565b610bc457865f610fd2565b6040513d8a823e3d90fd5b635fc483c560e01b8652600486fd5b5034610313576040366003190112610313576020906040906001600160a01b036110d5613655565b168152600b8352818120602435825283522054604051908152f35b5034610313578060031936011261031357602061110b614130565b6001600160601b03600154168111908161112b575b506040519015158152f35b90506111356142e2565b1082611120565b50346103135760203660031901126103135780611157613655565b7f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b156104ee576040516306e8502160e11b815260016004820152838160248183875af190811561052757849161120b575b5050338352600c6020526040832080546001600160a01b0319166001600160a01b03909216919091179055803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81611215916138a5565b6104ee57825f6111b4565b50346103135780600319360112610313576040517f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b03168152602090f35b5034610313578060031936011261031357602061076b6142e2565b50346103135780600319360112610313576002546040516001600160a01b039091168152602090f35b503461031357806003193601126103135760ff60209154166040519015158152f35b5034610313576020366003190112610313576112e5613655565b6002546001600160a01b031633036114d55781907f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af19081156105275784916114c0575b505060405163131bac4760e11b81523060048201526001600160a01b0382811660248301527f0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae1690602081604481855afa9081156114b5578591611480575b50611420575b506040518381526001600160a01b03909116907fd61f2c33e366f60a2f32f50d952952a311e9ed22662d58f31dc6a3e2c8ba61f190602090a2803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b803b15610d2b5760405163bdac7ca360e01b81523060048201526001600160a01b03831660248201529084908290604490829084905af190811561052757849161146b575b506113bb565b81611475916138a5565b6104ee57825f611465565b9450506020843d6020116114ad575b8161149c602093836138a5565b81010312610024578493515f6113b5565b3d915061148f565b6040513d87823e3d90fd5b816114ca916138a5565b6104ee57825f611356565b635fc483c560e01b8252600482fd5b5034610313578060031936011261031357602060015460601c604051908152f35b5034610313576060366003190112610313576004356001600160401b0381116106d1576115369036906004016136ab565b9190611540613681565b906044356001600160401b038111610ef8576115609036906004016136ab565b90947f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031693843b15610a35576040516306e8502160e11b8152600160048201528681602481838a5af180156119a957908791611994575b505460ff81166119855765ffffffff000019164260101b65ffffffff0000161786556003546001600160a01b03169333851415938480611961575b61195257879861163b9161160f9998996142be565b50886040805161161e81613840565b6060815282602082015201526116326142be565b50810190613d25565b93949096929192333b1561194e57604051637afcd25560e11b815260206004820152918b9183918291611673916024840191906140a2565b038183335af18015611943578a9589928892611921575b5090816116a392519060406020820151910151916144e4565b6117ee575b6116e2956116c0926116bb878094614afe565b614ce0565b6040518094819263a129568d60e01b835260206004840152602483019061400c565b0381836001600160a01b0386165af180156114b5576117169286916117cc575b508051906040602082015191015191614f8c565b81151590816117c4575b5061177f575b50506117306143c2565b611060578082913b15610321578190600460405180948193633b4438cf60e21b83525af180156103165761176a575b602060405160038152f35b6117758280926138a5565b610313578061175f565b803b156106d157818091602460405180948193632ce1ed1560e01b83523060048401525af18015610316571561172657816117b9916138a5565b6106d157815f611726565b90505f611720565b6117e891503d8088833e6117e081836138a5565b810190613ef5565b5f611702565b6002546001600160a01b03168952600c6020526040892080546001600160a01b0319169055925061181d613abe565b89959291956118ba575b60015460601c803b156118b657611859968c8094604051998a9586948593631a1bb38960e21b8552336004860161427a565b03925af19384156118ab578994611890575b506116e2956116c0926116bb87809461188333615425565b94505050925095506116a8565b8461189e91979592976138a5565b610bc8578792945f61186b565b6040513d8b823e3d90fd5b8b80fd5b808b878c858360015460601c926118e760405197889687958694630578da2160e41b86526004860161427a565b03925af18015611916576118fc575b50611827565b61190f903d808e833e61051a81836138a5565b505f6118f6565b6040513d8e823e3d90fd5b87935061193192509690966138a5565b61193f5786858a955f61168a565b8880fd5b6040513d8c823e3d90fd5b8a80fd5b63a9faa8f560e01b8852600488fd5b506002546001600160a01b039081168952600c6020526040892054163314156115fa565b635441f54960e11b8752600487fd5b8161199e916138a5565b610a3557855f6115bf565b6040513d89823e3d90fd5b50346103135760403660031901126103135760406119d0613655565b916119d9613681565b9260018060a01b03168152600d602052209060018060a01b03165f52602052602060ff60405f2054166040519015158152f35b503461031357602036600319011261031357611a26613655565b6002549091906001600160a01b03163303610564577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b156106d1576040516306e8502160e11b815260016004820152828160248183865af1801561033957908391611c37575b505460ff81166106ad5765ffffffff000019164260101b65ffffffff000016178255611ac1613abe565b60035492956001600160a01b03938416939192909182168414611c285785968415159384611bcd575b60015460601c803b1561193f57611b1c9389809460405196879586948593631a1bb38960e21b85528b6004860161427a565b03925af1908115610a2a578691611bb8575b5050611b3990615425565b611b6a5750803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b803b156104ee57828091602460405180948193632ce1ed1560e01b83523060048401525af1908115610339578391611ba3575b50610e89565b81611bad916138a5565b61032157815f611b9d565b81611bc2916138a5565b610a2657845f611b2e565b80888460015460601c85838b611bf960405197889687958694630578da2160e41b86526004860161427a565b03925af180156118ab57611c0e575b50611aea565b611c21903d808b833e61051a81836138a5565b505f611c08565b63ce5a86f760e01b8652600486fd5b81611c41916138a5565b6106d157815f611a97565b503461031357611c5b366137d2565b60025490969592939192906001600160a01b0316330361109e577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031694853b15610bc4576040516306e8502160e11b8152600160048201528781602481838b5af1801561109357908891611d27575b505460ff1661198557611016611cfb959361100e899a9694611cf59436916138dd565b91614f8c565b803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81611d31916138a5565b610bc457865f611cd2565b5034610313576080366003190112610313576004356001600160401b0381116106d157611d6d903690600401613931565b6024356001600160401b0381116120a257611d8c90369060040161399b565b916044356001600160401b0381116106d157611dac90369060040161399b565b90611db561366b565b815490939060301c6001600160a01b03163303610d45577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b156120a2576040516306e8502160e11b815260048101849052838160248183865af180156105275790849161208d575b505060015460601c90836040518093630c3d587560e11b8252602060048301528180611e576024820189613733565b03915afa918215610527578492612071575b50835b835181101561200957611e7f8187613aaa565b5115612001576001611e918285613aaa565b5103611ee6576001906001600160a01b03611eac8287613aaa565b51168652600a60205260408620545b80611ec6838a613aaa565b5111611ed5575b505b01611e6c565b611edf8289613aaa565b525f611ecd565b6002611ef28285613aaa565b5103611fa4576001600160a01b03611f0a8286613aaa565b5116906020611f19828b613aaa565b516024604051809581936331a9108f60e11b835260048301525afa8015610a2a578690611f69575b60019250306001600160a01b039190911603611f615760ff825b16611ebb565b60ff86611f5b565b50906020813d8211611f9c575b81611f83602093836138a5565b81010312610a355790611f97600192613e84565b611f41565b3d9150611f76565b6003611fb08285613aaa565b5103611ff2576001906001600160a01b03611fcb8287613aaa565b51168652600b60205260408620611fe2828b613aaa565b5187526020526040862054611ebb565b63b8bcee3f60e01b8552600485fd5b600190611ecf565b848681846120198b848e8b6144e4565b803b156106d1578190600460405180948193633b4438cf60e21b83525af180156103395761205c575b604051602080825281906120589082018561376f565b0390f35b6120678380926138a5565b6106d15781612042565b6120869192503d8086833e61051a81836138a5565b905f611e69565b81612097916138a5565b6120a257825f611e28565b8280fd5b50346103135760a0366003190112610313576004356001600160401b0381116106d1576120d79036906004016137a2565b91906024356001600160401b0381116120a2576120f89036906004016137a2565b936044356001600160401b038111610a26576121189036906004016137a2565b93909261212361366b565b926084356001600160401b038111610bc8576121439036906004016137a2565b60025491969093916001600160a01b03163303612509577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031697883b15610bc0576040516306e8502160e11b8152600160048201528a81602481838e5af180156124fe57908b916124e9575b505465ffffffff000019164260101b65ffffffff000016178a55838b148015906124df575b6124cd577f0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae6001600160a01b031695938a5b8181106122ce575050505050508580975050855b81811061225857868087803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b61226b612266828488614030565b614040565b833b15610bc857604051630d0a119360e31b81526001600160a01b038681166004830152919091166024820152878160448183885af19081156110935788916122b9575b5050600101612222565b816122c3916138a5565b610bc457865f6122af565b6122dc612266828489614030565b60405163131bac4760e11b81523060048201526001600160a01b03821660248201526020816044818d5afa908115612427578f918f91859291612494575b506123319161232c911515938a614030565b614054565b151503612438575b8d612345838688614061565b9050612399575b907fd61f2c33e366f60a2f32f50d952952a311e9ed22662d58f31dc6a3e2c8ba61f1602061238161232c86600197968c614030565b926040519315158452858060a01b031692a20161220e565b61232c836123a79289614030565b6123b2838688614061565b916001600160a01b0384163b1561243457908f916123e56040519485938493630ede8ef360e11b855233600486016140c2565b0381836001600160a01b0387165af18015612427578f918f9161240a575b505061234c565b81925090612417916138a5565b612423578d8d5f612403565b8c80fd5b8e604051903d90823e3d90fd5b8f80fd5b883b156124235760405163bdac7ca360e01b81523060048201526001600160a01b03821660248201528d81604481838e5af1801561242757908e9161247f575b5050612339565b81612489916138a5565b612423578c5f612478565b925050506020813d82116124c5575b816124b0602093836138a5565b8101031261002457518e90839061233161231a565b3d91506124a3565b6001621398b960e31b03198a5260048afd5b50808414156121dc565b816124f3916138a5565b610bc057895f6121b7565b6040513d8d823e3d90fd5b635fc483c560e01b8952600489fd5b503461031357602036600319011261031357612532613655565b90602061253d613abe565b60015460035460405163068e097b60e51b815297889560609390931c948694859461257994919390916001600160a01b03169060048701614202565b03915afa9081156125c85790612595575b602090604051908152f35b506020813d6020116125c0575b816125af602093836138a5565b81010312610024576020905161258a565b3d91506125a2565b604051903d90823e3d90fd5b5034610313576060366003190112610313576125ee613655565b6002546024359291604435916001600160a01b031633036129e8577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031691823b15610ef8576040516306e8502160e11b815260048101859052848160248183885af180156114b5579085916129d3575b505465ffffffff000019164260101b65ffffffff0000161784556001600160a01b039091169083826127785750478480808084335af1953d15612770573d966126ae88613cc4565b976126bc604051998a6138a5565b88523d8760208a013e5b1561274b578596505b81612705575b50505050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b60405193845260208401526040830152606082015233907fb180a6a3fa923907a3d3d7175c65292a5003d4f5b24dea54d607814d5664378d60803092a35f8080806126d5565b60405162461bcd60e51b8152602060048201528061276c602482018a61400c565b0390fd5b6060966126c6565b6001820361281d5784956040516370a0823160e01b8152306004820152602081602481885afa9081156119a95787916127e8575b50848752600a6020526040872054908181116127ca575b50506126cf565b6127d59293506141bd565b906127e1823386615395565b5f806127c3565b9650506020863d602011612815575b81612804602093836138a5565b81010312610024578695515f6127ac565b3d91506127f7565b600282036128ed576006549585805b888110612899575b508697506126cf5760019150833b15610a3557604051632142170760e11b815230600482015233602482015260448101829052868160648183895af19081156119a95787916128845750506126cf565b8161288e916138a5565b610a3557855f6127c3565b856128a382613a09565b905460039190911b1c6001600160a01b031614806128d5575b6128c85760010161282c565b505085965060015f612834565b50826128e082613a35565b90549060031b1c146128bc565b60038203611ff2578495604051627eeac760e11b8152306004820152816024820152602081604481885afa9081156119a957879161299e575b50848752600b6020526040872082885260205260408720549081811161294d5750506126cf565b6129589293506141bd565b90833b15610a3557604051637921219560e11b815286818061298086863330600486016141ca565b038183895af19081156119a9578791156127c3578161288e916138a5565b9650506020863d6020116129cb575b816129ba602093836138a5565b81010312610024578695515f612926565b3d91506129ad565b816129dd916138a5565b610ef857835f612666565b635fc483c560e01b8352600483fd5b5034610313576020366003190112610313576020906001600160a01b03612a1c613655565b168152600c8252604060018060a01b0391205416604051908152f35b5034610313578060031936011261031357602061076b614130565b50346103135780600319360112610313575460405160309190911c6001600160a01b03168152602090f35b50346103135760203660031901126103135780612a99613655565b7f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031690813b156104ee576040516306e8502160e11b815260048101849052838160248183875af1908115610527578491612b7b575b505033808452600d602090815260408086206001600160a01b03949094165f81815294835293819020805460ff19169055518581527f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c19190a3803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b81612b85916138a5565b6104ee57825f612af6565b50346103135780600319360112610313576040517f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03168152602090f35b503461031357612be4366136d8565b9294939192917f000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e596001600160a01b03163303612ecf577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031693843b15610a35576040516306e8502160e11b8152600481018790528681602481838a5af180156119a957908791612eba575b505460ff81166119855765ffffffff000019164260101b65ffffffff0000161786557f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc8054600180546001600160a01b031983166001600160a01b03968716179093556001600160601b03831660608b811b6bffffffffffffffffffffffff191691909117909155909891901c9216808314159081612e41575b50612e32576003546001600160a01b03169081612db6575b50508495303b15610a3557612d758693604051958694859463026d5d5f60e11b865260018060a01b031660048601526024850152600360448501526080606485015260848401916140a2565b038183305af1908115610339578391610d16575050803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b869160246080926040519485938492638c48052360e01b845260048401525af1908115610a2a578691612e00575b5015612df1575f80612d29565b63a93eca7960e01b8552600485fd5b612e22915060803d608011612e2b575b612e1a81836138a5565b8101906140f9565b5050505f612de4565b503d612e10565b6311a1e69760e01b8652600486fd5b9050602060018060a01b036004541660246040518094819363e4ec552b60e01b835260048301525afa9081156119a9578791612e80575b50155f612d11565b90506020813d602011612eb2575b81612e9b602093836138a5565b81010312610bc457612eac906140ec565b5f612e78565b3d9150612e8e565b81612ec4916138a5565b610a3557855f612c77565b630636a15760e11b8552600485fd5b50346103135780600319360112610313576004546040516001600160a01b039091168152602090f35b5034610313576020366003190112610313576020906040906001600160a01b03612f2f613655565b168152600a83522054604051908152f35b50346103135780600319360112610313576040517f0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae6001600160a01b03168152602090f35b503461031357612f94366137d2565b6002549496959294929391926001600160a01b0316330361109e577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031694853b15610bc4576040516306e8502160e11b8152600160048201528781602481838b5af180156110935790889161319a575b505465ffffffff000019164260101b65ffffffff000016178755878114801590613190575b61317e578697875b81811061306f57888089803b1561032157818091600460405180948193633b4438cf60e21b83525af18015610316576103025750f35b61307d61226682848a614030565b61308b61232c838689614030565b33808c52600d60209081526040808e206001600160a01b039095165f81815295835294819020805460ff95151595861660ff19919091161790555192835283927f8e88e5512acfbeb8b54f789484f9625bced0405b0d3bc16df01bc28421e5f8c19190a36130fa828689614061565b905061310a575b50600101613039565b61311861232c838689614030565b61312383878a614061565b9092803b1561242357613151938d809460405196879586948593635f4860df60e01b855233600486016140c2565b03925af1908115611943578a91613169575b50613101565b81613173916138a5565b61193f57885f613163565b6001621398b960e31b03198752600487fd5b5081881415613031565b816131a4916138a5565b610bc457865f61300c565b5034610313576131be366136d8565b50505050506020604051630a85bd0160e11b8152f35b5034610313576040366003190112610313576131ee613655565b6024356001600160401b0381116120a25761320d9036906004016136ab565b919060018060a01b0360025416803314908115613390575b5015613353577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031692833b15610a26576040516306e8502160e11b81526001600482015285908181602481838a5af180156103165761333e575b505460ff811661332f57926116c0816132e3816116bb6132c982988c986132e89b65ffffffff00004260101b169065ffffffff00001916178a55810190613d25565b9993809792969197519060406020820151910151916144e4565b614afe565b0381836001600160a01b0386165af180156105275761102692859161331b57508051906040602082015191015191614f8c565b6117e891503d8087833e6117e081836138a5565b635441f54960e11b8652600486fd5b81613348916138a5565b610a2657845f613287565b60405162461bcd60e51b8152602060048201526015602482015274209d1027b7363c9020b9b9b2ba1026b0b730b3b2b960591b6044820152606490fd5b855250600d60209081526040808620335f908152925281205460ff1690613225565b50346103135780600319360112610313576133eb6133f96120586133d4613abe565b919390604051958695606087526060870190613733565b90858203602087015261376f565b90838203604085015261376f565b503461031357602036600319011261031357613421613655565b6002549091906001600160a01b03163303610564577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b0316803b156106d1576040516306e8502160e11b815260016004820152828160248183865af18015610339579083916134bd575b50506003546001600160a01b03166134ae57611cfb82936143f0565b63ce5a86f760e01b8252600482fd5b816134c7916138a5565b6106d157815f613492565b503461031357806003193601126103135760206001600160601b0360015416604051908152f35b90503461002457602036600319011261002457600354600435906001600160a01b03163303613636577f0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b6001600160a01b031691823b15610024576306e8502160e11b8152600160048201525f8160248183875af1801561362b57613616575b50825460ff81166136075765ffffffff000019164260101b65ffffffff00001617835580151590816135e0575b50611060578082913b15610321578190600460405180948193633b4438cf60e21b83525af180156103165761176a57602060405160038152f35b90506136006135ed614374565b916001600160601b0360015416906139b6565b115f6135a6565b635441f54960e11b8452600484fd5b6136239193505f906138a5565b5f915f613579565b6040513d5f823e3d90fd5b63a9faa8f560e01b5f5260045ffd5b3461002457613653366136d8565b005b600435906001600160a01b038216820361002457565b606435906001600160a01b038216820361002457565b602435906001600160a01b038216820361002457565b35906001600160a01b038216820361002457565b9181601f84011215610024578235916001600160401b038311610024576020838186019501011161002457565b906080600319830112610024576004356001600160a01b038116810361002457916024356001600160a01b0381168103610024579160443591606435906001600160401b0382116100245761372f916004016136ab565b9091565b90602080835192838152019201905f5b8181106137505750505090565b82516001600160a01b0316845260209384019390920191600101613743565b90602080835192838152019201905f5b81811061378c5750505090565b825184526020938401939092019160010161377f565b9181601f84011215610024578235916001600160401b038311610024576020808501948460051b01011161002457565b6060600319820112610024576004356001600160401b03811161002457816137fc916004016137a2565b929092916024356001600160401b038111610024578161381e916004016137a2565b92909291604435906001600160401b0382116100245761372f916004016137a2565b606081019081106001600160401b0382111761385b57604052565b634e487b7160e01b5f52604160045260245ffd5b608081019081106001600160401b0382111761385b57604052565b604081019081106001600160401b0382111761385b57604052565b90601f801991011681019081106001600160401b0382111761385b57604052565b6001600160401b03811161385b5760051b60200190565b9291906138e9816138c6565b936138f760405195866138a5565b602085838152019160051b810192831161002457905b82821061391957505050565b6020809161392684613697565b81520191019061390d565b9080601f830112156100245781602061394c933591016138dd565b90565b92919061395b816138c6565b9361396960405195866138a5565b602085838152019160051b810192831161002457905b82821061398b57505050565b813581526020918201910161397f565b9080601f830112156100245781602061394c9335910161394f565b919082018092116139c357565b634e487b7160e01b5f52601160045260245ffd5b906139e1826138c6565b6139ee60405191826138a5565b82815280926139ff601f19916138c6565b0190602036910137565b600654811015613a215760065f5260205f2001905f90565b634e487b7160e01b5f52603260045260245ffd5b600854811015613a215760085f5260205f2001905f90565b600554811015613a215760055f5260205f2001905f90565b600754811015613a215760075f5260205f2001905f90565b600954811015613a215760095f5260205f2001905f90565b8054821015613a21575f5260205f2001905f90565b8051821015613a215760209160051b010190565b6005549060065460075491828285010192613ad8846139d7565b94613aeb613ae5866139d7565b956139d7565b935f915b808310613bd857505f905b808210613b805750505f905b828210613b1257505050565b60018091613b1f84613a65565b838060a01b0391549060031b1c16613b3685613a7d565b90549060031b1c9080613b49848d613aaa565b5281613b55848c613aaa565b525f52600b60205260405f20905f5260205260405f2054613b768289613aaa565b5201910190613b06565b909160018091613b8f85613a09565b838060a01b0391549060031b1c16613ba7828c613aaa565b52613bb185613a35565b90549060031b1c613bc2828b613aaa565b5281613bce828a613aaa565b5201920190613afa565b9180613be5600192613a4d565b838060a01b0391549060031b1c1680613bfe838c613aaa565b525f52600a60205260405f2054613c158289613aaa565b520191613aef565b91906080838203126100245760405190613c368261386f565b819380356001600160401b0381116100245782613c54918301613931565b835260208101356001600160401b0381116100245782613c7591830161399b565b602084015260408101356001600160401b0381116100245782613c9991830161399b565b60408401526060810135916001600160401b03831161002457606092613cbf920161399b565b910152565b6001600160401b03811161385b57601f01601f191660200190565b81601f8201121561002457803590613cf682613cc4565b92613d0460405194856138a5565b8284526020838301011161002457815f926020809301838601378301015290565b91909160a0818403126100245780356001600160401b0381116100245783613d4e918301613c1d565b9260208201356001600160401b0381116100245781613d6e918401613c1d565b9260408301356001600160401b0381116100245783016060818403126100245760405190613d9b82613840565b80356001600160401b03811161002457810184601f82011215610024578035613dc3816138c6565b91613dd160405193846138a5565b81835260208084019260061b8201019087821161002457602001915b818310613e4c5750505090604091835260208101356020840152013560408201529260608101356001600160401b0381116100245783613e2e918301613cdf565b9260808201356001600160401b0381116100245761394c9201613cdf565b6040838903126100245760206040918251613e668161388a565b613e6f86613697565b81528286013583820152815201920191613ded565b51906001600160a01b038216820361002457565b9080601f83011215610024578151613eaf816138c6565b92613ebd60405194856138a5565b81845260208085019260051b82010192831161002457602001905b828210613ee55750505090565b8151815260209182019101613ed8565b602081830312610024578051906001600160401b03821161002457016080818303126100245760405191613f288361386f565b81516001600160401b03811161002457820181601f8201121561002457805190613f51826138c6565b91613f5f60405193846138a5565b80835260208084019160051b8301019184831161002457602001905b828210613ff457505050835260208201516001600160401b0381116100245781613fa6918401613e98565b602084015260408201516001600160401b0381116100245781613fca918401613e98565b604084015260608201516001600160401b03811161002457613fec9201613e98565b606082015290565b6020809161400184613e84565b815201910190613f7b565b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b9190811015613a215760051b0190565b356001600160a01b03811681036100245790565b3580151581036100245790565b9190811015613a215760051b81013590601e19813603018212156100245701908135916001600160401b038311610024576020018236038113610024579190565b908060209392818452848401375f828201840152601f01601f1916010190565b6001600160a01b039091168152901515602082015260606040820181905261394c939101916140a2565b5190811515820361002457565b91908260809103126100245761410e826140ec565b9161411b60208201613e84565b91606061412a60408401613e84565b92015190565b6003546001600160a01b031680156141b85760206024916040519283809263c11002df60e01b82523060048301525afa801561362b575f90614184575b61394c91506001600160601b0360015416906139b6565b506020813d6020116141b0575b8161419e602093836138a5565b810103126100245761394c905161416d565b3d9150614191565b505f90565b919082039182116139c357565b6001600160a01b039182168152911660208201526040810191909152606081019190915260a0608082018190525f9082015260c00190565b6001600160a01b0391821681529116602082015260a06040820181905261394c949193919261424792916142399190860190613733565b90848203606086015261376f565b91608081840391015261376f565b906020828203126100245781516001600160401b0381116100245761394c9201613e98565b926142a261394c95936142b09360018060a01b03168652608060208701526080860190613733565b90848203604086015261376f565b91606081840391015261376f565b604051906142cb8261386f565b606080838181528160208201528160408201520152565b60206142ec613abe565b6001546004805460035460405163c068a4cd60e01b81529796889660609590951c95879586956143309592949193926001600160a01b039283169216908701614202565b03915afa90811561362b575f91614345575090565b90506020813d60201161436c575b81614360602093836138a5565b81010312610024575190565b3d9150614353565b602061437e613abe565b60015460048054600354604051631b3355ef60e01b81529796889660609590951c95879586956143309592949193926001600160a01b039283169216908701614202565b6143ca614130565b6001600160601b0360015416811190816143e2575090565b90506143ec614374565b1090565b60015460405163e4ec552b60e01b81526001600160a01b039092166004830181905291906020908290602490829060601c5afa90811561362b575f9161447e575b501561446f57600480546001600160a01b031916821790557f9ed2bd12cb184cf96abec450551864ef441ead0f80ac1c3bab504ce07d5a84cd5f80a2565b635958323160e01b5f5260045ffd5b90506020813d6020116144b0575b81614499602093836138a5565b81010312610024576144aa906140ec565b5f614431565b3d915061448c565b926142a261394c95936144d66142b094608088526080880190613733565b90868203602088015261376f565b9093929193845f8260015460601c858360018060a01b036003541661451f60405197889687958694630578da2160e41b86526004860161427a565b03925af190811561362b575f91614ae4575b50825115614adc575f5b8351811015614aa75761454e8188613aaa565b5115614a9f5760016145608284613aaa565b51036146ca576145708184613aaa565b516146bb576001600160a01b036145878286613aaa565b511685614594838a613aaa565b5191805f52600a60205260405f206145ad8482546141bd565b9055805f52600a60205260405f2054156145d6575b916145d09160019493615395565b0161453b565b600554600181036145fe5750916145d091600194936145f3615605565b9193945091506145c2565b5f5b818110614618575b5050916145d091600194936145f3565b909180935061462682613a4d565b905460039190911b1c6001600160a01b0316146146495760010190889291614600565b9193925f19820192909183116139c3576001946146a88a926146846146706145d097613a4d565b8a8060a01b0391549060031b1c1691613a4d565b81546001600160a01b0393841660039290921b91821b9390911b1916919091179055565b6146b0615605565b919394819350614608565b6318ffa99360e31b5f5260045ffd5b60026146d68284613aaa565b510361490d5760016146e88289613aaa565b51036148fe576001600160a01b036147008286613aaa565b51169061470d8185613aaa565b51916008545f600182145f1461482057505060085415613a215760085f527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee35483148015906147e0575b6147d157614763615593565b61476b6155e6565b803b1561002457604051632142170760e11b81523060048201526001600160a01b038816602482015260448101939093525f908390818381606481015b03925af191821561362b576001926147c1575b506145d0565b5f6147cb916138a5565b5f6147bb565b63c97d95cf60e01b5f5260045ffd5b5060065415613a215760065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f546001600160a01b0316811415614757565b81811061483c575b0361476b5763c97d95cf60e01b5f5260045ffd5b8461484682613a35565b90549060031b1c14806148db575b61486057600101614820565b5f1982018281116139c3576148b3816148a661487e6148ce94613a35565b90549060031b1c61488e86613a35565b90919082549060031b91821b915f19901b1916179055565b6148ae615593565b613a09565b905460039190911b1c6001600160a01b031661468483613a09565b6148d66155e6565b614828565b50826148e682613a09565b905460039190911b1c6001600160a01b031614614854565b63053dda6d60e21b5f5260045ffd5b60036149198284613aaa565b5103614a90576001600160a01b036149318286613aaa565b51169061493e8185613aaa565b51614949828a613aaa565b51600954845f52600b60205260405f20835f5260205260405f2061496e8382546141bd565b9055845f52600b60205260405f20835f5260205260405f2054156149bb575b50833b15610024576147a8935f92838a60405197889586948593637921219560e11b855230600486016141ca565b600181036149da57506149cc615550565b6149d46155b2565b5f61498d565b5f5b8181106149eb575b50506149d4565b836149f582613a7d565b90549060031b1c14614a0a575b6001016149dc565b85614a1482613a65565b905460039190911b1c6001600160a01b031603614a02575f1982019182116139c357614684614a6983614a5c614a4c614a8196613a7d565b90549060031b1c61488e86613a7d565b614a64615550565b613a65565b905460039190911b1c6001600160a01b031691613a65565b614a896155b2565b5f806149e4565b63b8bcee3f60e01b5f5260045ffd5b6001906145d0565b5092614ad75f5160206156c35f395f51905f5293949692959660405193849360018060a01b0316973097856144b8565b0390a3565b505050509050565b614af891503d805f833e61051a81836138a5565b5f614531565b805151908115614cdb576002546001600160a01b0316915f5b818110614b5b575050805f5160206156c35f395f51905f52915193614ad7602083015192606060408201519101519060405194859460018060a01b031698856144b8565b6040830190614b6b818351613aaa565b5115614cd257606084016001614b82838351613aaa565b5103614bbc5750614bb66001928787614baf85878060a01b03614ba6828c51613aaa565b51169451613aaa565b5192615624565b01614b17565b6002614bc9838351613aaa565b5103614c5f5750905060018060a01b03614be4828551613aaa565b511690614bf5816020860151613aaa565b5191803b1561002457604051632142170760e11b81526001600160a01b0380881660048301528816602482015260448101939093525f908390818381606481015b03925af191821561362b57600192614c4f575b50614bb6565b5f614c59916138a5565b5f614c49565b614c6c8260039251613aaa565b5103614a905783516001600160a01b0390614c88908390613aaa565b5116614ca482614c9c816020890151613aaa565b519451613aaa565b5192813b15610024575f91888389614c3660405198899687958694637921219560e11b8652600486016141ca565b60019150614bb6565b505050565b9190825151908115614f8657614cf5826138c6565b92614d0360405194856138a5565b828452601f19614d12846138c6565b015f5b818110614f63575050614d27836139d7565b94614d31846139d7565b92614d3b856139d7565b905f5b868110614ee057506002546001600160a01b03908116977f000000000000000000000000000000000022d473030f116ddee9f6b43ac78ba390911691823b15610024579088949160405195869363edd9444b60e01b85526080600486015260e4850190805191606060848801528251809152602061010488019301905f5b818110614ea357505050806020604092015160a4880152015160c4860152600319858203016024860152602080855192838152019401905f5b818110614e725750505083614e205f969482948894604485015260031984830301606485015261400c565b03925af1801561362b575f5160206156c35f395f51905f5295614ad793614e4c92614e62575b506139d7565b9360405194859460018060a01b031698856144b8565b5f614e6c916138a5565b5f614e46565b825180516001600160a01b03168752602090810151818801528a97506040909601958e945090920191600101614df5565b825180516001600160a01b0316865260209081015181870152979850959691959294509260019190604001950191019089969594928e9492614dbc565b80614eed6001928a613aaa565b51828060a01b03871690526020614f05828751613aaa565b5101516020614f14838c613aaa565b510152818060a01b03614f28828751613aaa565b515116614f35828c613aaa565b526020614f43828751613aaa565b510151614f508289613aaa565b5281614f5c8286613aaa565b5201614d3e565b602090604051614f728161388a565b5f81525f8382015282828901015201614d15565b50505050565b909392919360015460601c5f6040518092630c3d587560e11b8252602060048301528180614fbd6024820189613733565b03915afa90811561362b575f9161537b575b505f5b835181101561529b57614fe58188613aaa565b5115615293576001614ff78284613aaa565b5103615091576150078184613aaa565b516146bb576001600160a01b0361501e8286613aaa565b51169061502b8189613aaa565b5161503881308986615624565b825f52600a60205260405f20548015615063575b6001935f52600a6020520160405f20555b01614fd2565b60055493600160401b85101561385b5761508a816146848760018099016005556005613a95565b935061504c565b600261509d8284613aaa565b510361517b5760016150af8289613aaa565b51036148fe576001600160a01b036150c78286613aaa565b5116906150d48185613aaa565b5191803b1561002457604051632142170760e11b81526001600160a01b0388166004820152306024820152604481018490525f8160648183865af1801561362b5761516b575b5060065490600160401b82101561385b5761468482600161514094016006556006613a95565b600854600160401b81101561385b5760019261488e828561516694016008556008613a95565b61505d565b5f615175916138a5565b5f61511a565b60036151878284613aaa565b5103614a90576001600160a01b0361519f8286613aaa565b5116906151ac8185613aaa565b516151b7828a613aaa565b5190833b1561002457604051637921219560e11b81525f81806151e08686308f600486016141ca565b038183895af1801561362b57615283575b50835f52600b60205260405f20815f5260205260405f2054908115615230575b6001945f52600b60205260405f20905f526020520160405f205561505d565b600754600160401b81101561385b578561468482600161525594016007556007613a95565b60095494600160401b86101561385b5761527c8261488e886001809a016009556009613a95565b9450615211565b5f61528d916138a5565b5f6151f1565b60019061505d565b509192939490600f6152be6152b5600554600654906139b6565b600754906139b6565b1161536c576001546003546001600160a01b03169060601c803b1561002457825f918783879561530460405197889687958694631a1bb38960e21b86526004860161427a565b03925af1801561362b5761535c575b508351615322575b5050505050565b60405130956001600160a01b0316945f5160206156c35f395f51905f52949193849361534f9392856144b8565b0390a35f8080808061531b565b5f615366916138a5565b5f615313565b63df8153c760e01b5f5260045ffd5b61538f91503d805f833e61051a81836138a5565b5f614fcf565b60405163a9059cbb60e01b81526001600160a01b03909216600483015260248201929092526020905f9060449082855af19081601f3d1160015f5114161516615418575b50156153e157565b60405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b6044820152606490fd5b3b153d171590505f6153d9565b604051638c48052360e01b8152600360048201526001600160a01b0391909116906080816024815f865af1801561362b575f905f915f935f9261552b575b501561551c576001600160601b03166001600160601b0319600154161760015560018060a01b036004541660018060a01b0382160361550d575b50600380546001600160a01b031916831790555f80546601000000000000600160d01b031916603083901b6601000000000000600160d01b03161781556001600160a01b0390911691907fa521c704a92cee73ea8410dc251eece5bd5362f6887c45f3e9566e817aee73699080a3565b615516906143f0565b5f61549d565b63a93eca7960e01b5f5260045ffd5b92505050615548915060803d608011612e2b57612e1a81836138a5565b90925f615463565b600954801561557f575f190161557a61556a826009613a95565b8154905f199060031b1b19169055565b600955565b634e487b7160e01b5f52603160045260245ffd5b600854801561557f575f19016155ad61556a826008613a95565b600855565b600754801561557f575f19016155e16155cc826007613a95565b81549060018060a01b039060031b1b19169055565b600755565b600654801561557f575f19016156006155cc826006613a95565b600655565b600554801561557f575f190161561f6155cc826005613a95565b600555565b6040516323b872dd60e01b81526001600160a01b03928316600482015292909116602483015260448201929092526020905f9060649082855af19081601f3d1160015f51141615166156b5575b501561567957565b60405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b6044820152606490fd5b3b153d171590505f61567156fe71e9b057bc061d94e1e08fc8c03d958f8fe3068ec75e774d478e7e32b9ff4b2aa26469706673582212209a3a8df7f3b31a9d91364ceefdcb0479c37e5a9f459182a5c83d27ad844dfa3764736f6c634300081e0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e590000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae
-----Decoded View---------------
Arg [0] : factory (address): 0xDa14Fdd72345c4d2511357214c5B89A919768e59
Arg [1] : accountsGuard (address): 0x2529AE4a3c9d3285DD06CaDfc8516D3faBD6240b
Arg [2] : merklDistributor (address): 0x3Ef3D8bA38EBe18DB133cEc108f4D14CE00Dd9Ae
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000da14fdd72345c4d2511357214c5b89a919768e59
Arg [1] : 0000000000000000000000002529ae4a3c9d3285dd06cadfc8516d3fabd6240b
Arg [2] : 0000000000000000000000003ef3d8ba38ebe18db133cec108f4d14ce00dd9ae
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
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.