ETH Price: $3,498.50 (-0.14%)
Gas: 3 Gwei

Contract

0xA154EcFFfb46380991751DE890956b245142e10C
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x60806040180800212023-09-06 21:16:35318 days ago1694034995IN
 Create: VaultModule
0 ETH0.0884969625.60285653

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
VaultModule

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion
File 1 of 50 : VaultModule.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/DecimalMath.sol";
import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

import "../../storage/Account.sol";
import "../../storage/Pool.sol";

import "@synthetixio/core-modules/contracts/storage/FeatureFlag.sol";

import "../../interfaces/IVaultModule.sol";

/**
 * @title Allows accounts to delegate collateral to a pool.
 * @dev See IVaultModule.
 */
contract VaultModule is IVaultModule {
    using SetUtil for SetUtil.UintSet;
    using SetUtil for SetUtil.Bytes32Set;
    using SetUtil for SetUtil.AddressSet;
    using DecimalMath for uint256;
    using Pool for Pool.Data;
    using Vault for Vault.Data;
    using VaultEpoch for VaultEpoch.Data;
    using Collateral for Collateral.Data;
    using CollateralConfiguration for CollateralConfiguration.Data;
    using AccountRBAC for AccountRBAC.Data;
    using Distribution for Distribution.Data;
    using CollateralConfiguration for CollateralConfiguration.Data;
    using ScalableMapping for ScalableMapping.Data;
    using SafeCastU128 for uint128;
    using SafeCastU256 for uint256;
    using SafeCastI128 for int128;
    using SafeCastI256 for int256;

    bytes32 private constant _DELEGATE_FEATURE_FLAG = "delegateCollateral";

    /**
     * @inheritdoc IVaultModule
     */
    function delegateCollateral(
        uint128 accountId,
        uint128 poolId,
        address collateralType,
        uint256 newCollateralAmountD18,
        uint256 leverage
    ) external override {
        FeatureFlag.ensureAccessToFeature(_DELEGATE_FEATURE_FLAG);
        Account.loadAccountAndValidatePermission(accountId, AccountRBAC._DELEGATE_PERMISSION);

        // Each collateral type may specify a minimum collateral amount that can be delegated.
        // See CollateralConfiguration.minDelegationD18.
        if (newCollateralAmountD18 > 0) {
            CollateralConfiguration.requireSufficientDelegation(
                collateralType,
                newCollateralAmountD18
            );
        }

        // System only supports leverage of 1.0 for now.
        if (leverage != DecimalMath.UNIT) revert InvalidLeverage(leverage);

        // Identify the vault that corresponds to this collateral type and pool id.
        Vault.Data storage vault = Pool.loadExisting(poolId).vaults[collateralType];

        // Use account interaction to update its rewards.
        vault.updateRewards(accountId, poolId, collateralType);

        uint256 currentCollateralAmount = vault.currentAccountCollateral(accountId);

        // Conditions for collateral amount

        // Ensure current collateral amount differs from the new collateral amount.
        if (newCollateralAmountD18 == currentCollateralAmount) revert InvalidCollateralAmount();
        // If increasing delegated collateral amount,
        // Check that the account has sufficient collateral.
        else if (newCollateralAmountD18 > currentCollateralAmount) {
            // Check if the collateral is enabled here because we still want to allow reducing delegation for disabled collaterals.
            CollateralConfiguration.collateralEnabled(collateralType);

            Account.requireSufficientCollateral(
                accountId,
                collateralType,
                newCollateralAmountD18 - currentCollateralAmount
            );

            Pool.loadExisting(poolId).checkPoolCollateralLimit(
                collateralType,
                newCollateralAmountD18 - currentCollateralAmount
            );

            // if decreasing delegation amount, ensure min time has elapsed
        } else {
            Pool.loadExisting(poolId).requireMinDelegationTimeElapsed(
                vault.currentEpoch().lastDelegationTime[accountId]
            );
        }

        // Update the account's position for the given pool and collateral type,
        // Note: This will trigger an update in the entire debt distribution chain.
        uint256 collateralPrice = _updatePosition(
            accountId,
            poolId,
            collateralType,
            newCollateralAmountD18,
            currentCollateralAmount,
            leverage
        );

        _updateAccountCollateralPools(
            accountId,
            poolId,
            collateralType,
            newCollateralAmountD18 > 0
        );

        // If decreasing the delegated collateral amount,
        // check the account's collateralization ratio.
        // Note: This is the best time to do so since the user's debt and the collateral's price have both been updated.
        if (newCollateralAmountD18 < currentCollateralAmount) {
            int256 debt = vault.currentEpoch().consolidatedDebtAmountsD18[accountId];

            uint256 minIssuanceRatioD18 = Pool
                .loadExisting(poolId)
                .collateralConfigurations[collateralType]
                .issuanceRatioD18;

            // Minimum collateralization ratios are configured in the system per collateral type.abi
            // Ensure that the account's updated position satisfies this requirement.
            CollateralConfiguration.load(collateralType).verifyIssuanceRatio(
                debt < 0 ? 0 : debt.toUint(),
                newCollateralAmountD18.mulDecimal(collateralPrice),
                minIssuanceRatioD18
            );

            // Accounts cannot reduce collateral if any of the pool's
            // connected market has its capacity locked.
            _verifyNotCapacityLocked(poolId);
        }

        // solhint-disable-next-line numcast/safe-cast
        vault.currentEpoch().lastDelegationTime[accountId] = uint64(block.timestamp);

        emit DelegationUpdated(
            accountId,
            poolId,
            collateralType,
            newCollateralAmountD18,
            leverage,
            msg.sender
        );
    }

    /**
     * @inheritdoc IVaultModule
     */
    function getPositionCollateralRatio(
        uint128 accountId,
        uint128 poolId,
        address collateralType
    ) external override returns (uint256) {
        return Pool.load(poolId).currentAccountCollateralRatio(collateralType, accountId);
    }

    /**
     * @inheritdoc IVaultModule
     */
    function getVaultCollateralRatio(
        uint128 poolId,
        address collateralType
    ) external override returns (uint256) {
        return Pool.load(poolId).currentVaultCollateralRatio(collateralType);
    }

    /**
     * @inheritdoc IVaultModule
     */
    function getPositionCollateral(
        uint128 accountId,
        uint128 poolId,
        address collateralType
    ) external view override returns (uint256 amount, uint256 value) {
        (amount, value) = Pool.load(poolId).currentAccountCollateral(collateralType, accountId);
    }

    /**
     * @inheritdoc IVaultModule
     */
    function getPosition(
        uint128 accountId,
        uint128 poolId,
        address collateralType
    )
        external
        override
        returns (
            uint256 collateralAmount,
            uint256 collateralValue,
            int256 debt,
            uint256 collateralizationRatio
        )
    {
        Pool.Data storage pool = Pool.load(poolId);

        debt = pool.updateAccountDebt(collateralType, accountId);
        pool.rebalanceMarketsInPool();
        (collateralAmount, collateralValue) = pool.currentAccountCollateral(
            collateralType,
            accountId
        );
        collateralizationRatio = pool.currentAccountCollateralRatio(collateralType, accountId);
    }

    /**
     * @inheritdoc IVaultModule
     */
    function getPositionDebt(
        uint128 accountId,
        uint128 poolId,
        address collateralType
    ) external override returns (int256 debt) {
        Pool.Data storage pool = Pool.loadExisting(poolId);
        debt = pool.updateAccountDebt(collateralType, accountId);
        pool.rebalanceMarketsInPool();
    }

    /**
     * @inheritdoc IVaultModule
     */
    function getVaultCollateral(
        uint128 poolId,
        address collateralType
    ) public view override returns (uint256 amount, uint256 value) {
        return Pool.loadExisting(poolId).currentVaultCollateral(collateralType);
    }

    /**
     * @inheritdoc IVaultModule
     */
    function getVaultDebt(uint128 poolId, address collateralType) public override returns (int256) {
        return Pool.loadExisting(poolId).currentVaultDebt(collateralType);
    }

    /**
     * @dev Updates the given account's position regarding the given pool and collateral type,
     * with the new amount of delegated collateral.
     *
     * The update will be reflected in the registered delegated collateral amount,
     * but it will also trigger updates to the entire debt distribution chain.
     */
    function _updatePosition(
        uint128 accountId,
        uint128 poolId,
        address collateralType,
        uint256 newCollateralAmount,
        uint256 oldCollateralAmount,
        uint256 leverage
    ) internal returns (uint256 collateralPrice) {
        Pool.Data storage pool = Pool.load(poolId);

        // Trigger an update in the debt distribution chain to make sure that
        // the user's debt is up to date.
        pool.updateAccountDebt(collateralType, accountId);

        // Get the collateral entry for the given account and collateral type.
        Collateral.Data storage collateral = Account.load(accountId).collaterals[collateralType];

        // Adjust collateral depending on increase/decrease of amount.
        if (newCollateralAmount > oldCollateralAmount) {
            collateral.decreaseAvailableCollateral(newCollateralAmount - oldCollateralAmount);
        } else {
            collateral.increaseAvailableCollateral(oldCollateralAmount - newCollateralAmount);
        }

        // If the collateral amount is not negative, make sure that the pool exists
        // in the collateral entry's pool array. Otherwise remove it.
        _updateAccountCollateralPools(accountId, poolId, collateralType, newCollateralAmount > 0);

        // Update the account's position in the vault data structure.
        pool.vaults[collateralType].currentEpoch().updateAccountPosition(
            accountId,
            newCollateralAmount,
            leverage
        );

        // Trigger another update in the debt distribution chain,
        // and surface the latest price for the given collateral type (which is retrieved in the update).
        collateralPrice = pool.recalculateVaultCollateral(collateralType);
    }

    function _verifyNotCapacityLocked(uint128 poolId) internal view {
        Pool.Data storage pool = Pool.load(poolId);

        Market.Data storage market = pool.findMarketWithCapacityLocked();

        if (market.id > 0) {
            revert CapacityLocked(market.id);
        }
    }

    /**
     * @dev Registers the pool in the given account's collaterals array.
     */
    function _updateAccountCollateralPools(
        uint128 accountId,
        uint128 poolId,
        address collateralType,
        bool added
    ) internal {
        Collateral.Data storage depositedCollateral = Account.load(accountId).collaterals[
            collateralType
        ];

        bool containsPool = depositedCollateral.pools.contains(poolId);
        if (added && !containsPool) {
            depositedCollateral.pools.add(poolId);
        } else if (!added && containsPool) {
            depositedCollateral.pools.remove(poolId);
        }
    }
}

File 2 of 50 : AccessError.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Library for access related errors.
 */
library AccessError {
    /**
     * @dev Thrown when an address tries to perform an unauthorized action.
     * @param addr The address that attempts the action.
     */
    error Unauthorized(address addr);
}

File 3 of 50 : AddressError.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Library for address related errors.
 */
library AddressError {
    /**
     * @dev Thrown when a zero address was passed as a function parameter (0x0000000000000000000000000000000000000000).
     */
    error ZeroAddress();

    /**
     * @dev Thrown when an address representing a contract is expected, but no code is found at the address.
     * @param contr The address that was expected to be a contract.
     */
    error NotAContract(address contr);
}

File 4 of 50 : ParameterError.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Library for errors related with expected function parameters.
 */
library ParameterError {
    /**
     * @dev Thrown when an invalid parameter is used in a function.
     * @param parameter The name of the parameter.
     * @param reason The reason why the received parameter is invalid.
     */
    error InvalidParameter(string parameter, string reason);
}

File 5 of 50 : IERC165.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title ERC165 interface for determining if a contract supports a given interface.
 */
interface IERC165 {
    /**
     * @notice Determines if the contract in question supports the specified interface.
     * @param interfaceID XOR of all selectors in the contract.
     * @return True if the contract supports the specified interface.
     */
    function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

File 6 of 50 : IERC20.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title ERC20 token implementation.
 */
interface IERC20 {
    /**
     * @notice Emitted when tokens have been transferred.
     * @param from The address that originally owned the tokens.
     * @param to The address that received the tokens.
     * @param amount The number of tokens that were transferred.
     */
    event Transfer(address indexed from, address indexed to, uint amount);

    /**
     * @notice Emitted when a user has provided allowance to another user for transferring tokens on its behalf.
     * @param owner The address that is providing the allowance.
     * @param spender The address that received the allowance.
     * @param amount The number of tokens that were added to `spender`'s allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint amount);

    /**
     * @notice Thrown when the address interacting with the contract does not have sufficient allowance to transfer tokens from another contract.
     * @param required The necessary allowance.
     * @param existing The current allowance.
     */
    error InsufficientAllowance(uint required, uint existing);

    /**
     * @notice Thrown when the address interacting with the contract does not have sufficient tokens.
     * @param required The necessary balance.
     * @param existing The current balance.
     */
    error InsufficientBalance(uint required, uint existing);

    /**
     * @notice Retrieves the name of the token, e.g. "Synthetix Network Token".
     * @return A string with the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @notice Retrieves the symbol of the token, e.g. "SNX".
     * @return A string with the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @notice Retrieves the number of decimals used by the token. The default is 18.
     * @return The number of decimals.
     */
    function decimals() external view returns (uint8);

    /**
     * @notice Returns the total number of tokens in circulation (minted - burnt).
     * @return The total number of tokens.
     */
    function totalSupply() external view returns (uint);

    /**
     * @notice Returns the balance of a user.
     * @param owner The address whose balance is being retrieved.
     * @return The number of tokens owned by the user.
     */
    function balanceOf(address owner) external view returns (uint);

    /**
     * @notice Returns how many tokens a user has allowed another user to transfer on its behalf.
     * @param owner The user who has given the allowance.
     * @param spender The user who was given the allowance.
     * @return The amount of tokens `spender` can transfer on `owner`'s behalf.
     */
    function allowance(address owner, address spender) external view returns (uint);

    /**
     * @notice Transfer tokens from one address to another.
     * @param to The address that will receive the tokens.
     * @param amount The amount of tokens to be transferred.
     * @return A boolean which is true if the operation succeeded.
     */
    function transfer(address to, uint amount) external returns (bool);

    /**
     * @notice Allows users to provide allowance to other users so that they can transfer tokens on their behalf.
     * @param spender The address that is receiving the allowance.
     * @param amount The amount of tokens that are being added to the allowance.
     * @return A boolean which is true if the operation succeeded.
     */
    function approve(address spender, uint amount) external returns (bool);

    /**
     * @notice Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) external returns (bool);

    /**
     * @notice Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool);

    /**
     * @notice Allows a user who has been given allowance to transfer tokens on another user's behalf.
     * @param from The address that owns the tokens that are being transferred.
     * @param to The address that will receive the tokens.
     * @param amount The number of tokens to transfer.
     * @return A boolean which is true if the operation succeeded.
     */
    function transferFrom(address from, address to, uint amount) external returns (bool);
}

File 7 of 50 : DecimalMath.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "./SafeCast.sol";

/**
 * @title Utility library used to represent "decimals" (fixed point numbers) with integers, with two different levels of precision.
 *
 * They are represented by N * UNIT, where UNIT is the number of decimals of precision in the representation.
 *
 * Examples:
 * 1) Given UNIT = 100
 * then if A = 50, A represents the decimal 0.50
 * 2) Given UNIT = 1000000000000000000
 * then if A = 500000000000000000, A represents the decimal 0.500000000000000000
 *
 * Note: An accompanying naming convention of the postfix "D<Precision>" is helpful with this utility. I.e. if a variable "myValue" represents a low resolution decimal, it should be named "myValueD18", and if it was a high resolution decimal "myValueD27". While scaling, intermediate precision decimals like "myValue45" could arise. Non-decimals should have no postfix, i.e. just "myValue".
 *
 * Important: Multiplication and division operations are currently not supported for high precision decimals. Using these operations on them will yield incorrect results and fail silently.
 */
library DecimalMath {
    using SafeCastU256 for uint256;
    using SafeCastI256 for int256;

    // solhint-disable numcast/safe-cast

    // Numbers representing 1.0 (low precision).
    uint256 public constant UNIT = 1e18;
    int256 public constant UNIT_INT = int256(UNIT);
    uint128 public constant UNIT_UINT128 = uint128(UNIT);
    int128 public constant UNIT_INT128 = int128(UNIT_INT);

    // Numbers representing 1.0 (high precision).
    uint256 public constant UNIT_PRECISE = 1e27;
    int256 public constant UNIT_PRECISE_INT = int256(UNIT_PRECISE);
    int128 public constant UNIT_PRECISE_INT128 = int128(UNIT_PRECISE_INT);

    // Precision scaling, (used to scale down/up from one precision to the other).
    uint256 public constant PRECISION_FACTOR = 9; // 27 - 18 = 9 :)

    // solhint-enable numcast/safe-cast

    // -----------------
    // uint256
    // -----------------

    /**
     * @dev Multiplies two low precision decimals.
     *
     * Since the two numbers are assumed to be fixed point numbers,
     * (x * UNIT) * (y * UNIT) = x * y * UNIT ^ 2,
     * the result is divided by UNIT to remove double scaling.
     */
    function mulDecimal(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return (x * y) / UNIT;
    }

    /**
     * @dev Divides two low precision decimals.
     *
     * Since the two numbers are assumed to be fixed point numbers,
     * (x * UNIT) / (y * UNIT) = x / y (Decimal representation is lost),
     * x is first scaled up to end up with a decimal representation.
     */
    function divDecimal(uint256 x, uint256 y) internal pure returns (uint256 z) {
        return (x * UNIT) / y;
    }

    /**
     * @dev Scales up a value.
     *
     * E.g. if value is not a decimal, a scale up by 18 makes it a low precision decimal.
     * If value is a low precision decimal, a scale up by 9 makes it a high precision decimal.
     */
    function upscale(uint x, uint factor) internal pure returns (uint) {
        return x * 10 ** factor;
    }

    /**
     * @dev Scales down a value.
     *
     * E.g. if value is a high precision decimal, a scale down by 9 makes it a low precision decimal.
     * If value is a low precision decimal, a scale down by 9 makes it a regular integer.
     *
     * Scaling down a regular integer would not make sense.
     */
    function downscale(uint x, uint factor) internal pure returns (uint) {
        return x / 10 ** factor;
    }

    // -----------------
    // uint128
    // -----------------

    // Note: Overloading doesn't seem to work for similar types, i.e. int256 and int128, uint256 and uint128, etc, so explicitly naming the functions differently here.

    /**
     * @dev See mulDecimal for uint256.
     */
    function mulDecimalUint128(uint128 x, uint128 y) internal pure returns (uint128) {
        return (x * y) / UNIT_UINT128;
    }

    /**
     * @dev See divDecimal for uint256.
     */
    function divDecimalUint128(uint128 x, uint128 y) internal pure returns (uint128) {
        return (x * UNIT_UINT128) / y;
    }

    /**
     * @dev See upscale for uint256.
     */
    function upscaleUint128(uint128 x, uint factor) internal pure returns (uint128) {
        return x * (10 ** factor).to128();
    }

    /**
     * @dev See downscale for uint256.
     */
    function downscaleUint128(uint128 x, uint factor) internal pure returns (uint128) {
        return x / (10 ** factor).to128();
    }

    // -----------------
    // int256
    // -----------------

    /**
     * @dev See mulDecimal for uint256.
     */
    function mulDecimal(int256 x, int256 y) internal pure returns (int256) {
        return (x * y) / UNIT_INT;
    }

    /**
     * @dev See divDecimal for uint256.
     */
    function divDecimal(int256 x, int256 y) internal pure returns (int256) {
        return (x * UNIT_INT) / y;
    }

    /**
     * @dev See upscale for uint256.
     */
    function upscale(int x, uint factor) internal pure returns (int) {
        return x * (10 ** factor).toInt();
    }

    /**
     * @dev See downscale for uint256.
     */
    function downscale(int x, uint factor) internal pure returns (int) {
        return x / (10 ** factor).toInt();
    }

    // -----------------
    // int128
    // -----------------

    /**
     * @dev See mulDecimal for uint256.
     */
    function mulDecimalInt128(int128 x, int128 y) internal pure returns (int128) {
        return (x * y) / UNIT_INT128;
    }

    /**
     * @dev See divDecimal for uint256.
     */
    function divDecimalInt128(int128 x, int128 y) internal pure returns (int128) {
        return (x * UNIT_INT128) / y;
    }

    /**
     * @dev See upscale for uint256.
     */
    function upscaleInt128(int128 x, uint factor) internal pure returns (int128) {
        return x * ((10 ** factor).toInt()).to128();
    }

    /**
     * @dev See downscale for uint256.
     */
    function downscaleInt128(int128 x, uint factor) internal pure returns (int128) {
        return x / ((10 ** factor).toInt().to128());
    }
}

File 8 of 50 : HeapUtil.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

// Eth Heap
// Author: Zac Mitton
// License: MIT

library HeapUtil {
    // default max-heap

    uint private constant _ROOT_INDEX = 1;

    struct Data {
        uint128 idCount;
        Node[] nodes; // root is index 1; index 0 not used
        mapping(uint128 => uint) indices; // unique id => node index
    }
    struct Node {
        uint128 id; //use with another mapping to store arbitrary object types
        int128 priority;
    }

    //call init before anything else
    function init(Data storage self) internal {
        if (self.nodes.length == 0) self.nodes.push(Node(0, 0));
    }

    function insert(Data storage self, uint128 id, int128 priority) internal returns (Node memory) {
        //√
        if (self.nodes.length == 0) {
            init(self);
        } // test on-the-fly-init

        Node memory n;

        // MODIFIED: support updates
        extractById(self, id);

        self.idCount++;
        self.nodes.push();
        n = Node(id, priority);
        _bubbleUp(self, n, self.nodes.length - 1);

        return n;
    }

    function extractMax(Data storage self) internal returns (Node memory) {
        //√
        return _extract(self, _ROOT_INDEX);
    }

    function extractById(Data storage self, uint128 id) internal returns (Node memory) {
        //√
        return _extract(self, self.indices[id]);
    }

    //view
    function dump(Data storage self) internal view returns (Node[] memory) {
        //note: Empty set will return `[Node(0,0)]`. uninitialized will return `[]`.
        return self.nodes;
    }

    function getById(Data storage self, uint128 id) internal view returns (Node memory) {
        return getByIndex(self, self.indices[id]); //test that all these return the emptyNode
    }

    function getByIndex(Data storage self, uint i) internal view returns (Node memory) {
        return self.nodes.length > i ? self.nodes[i] : Node(0, 0);
    }

    function getMax(Data storage self) internal view returns (Node memory) {
        return getByIndex(self, _ROOT_INDEX);
    }

    function size(Data storage self) internal view returns (uint) {
        return self.nodes.length > 0 ? self.nodes.length - 1 : 0;
    }

    function isNode(Node memory n) internal pure returns (bool) {
        return n.id > 0;
    }

    //private
    function _extract(Data storage self, uint i) private returns (Node memory) {
        //√
        if (self.nodes.length <= i || i <= 0) {
            return Node(0, 0);
        }

        Node memory extractedNode = self.nodes[i];
        delete self.indices[extractedNode.id];

        Node memory tailNode = self.nodes[self.nodes.length - 1];
        self.nodes.pop();

        if (i < self.nodes.length) {
            // if extracted node was not tail
            _bubbleUp(self, tailNode, i);
            _bubbleDown(self, self.nodes[i], i); // then try bubbling down
        }
        return extractedNode;
    }

    function _bubbleUp(Data storage self, Node memory n, uint i) private {
        //√
        if (i == _ROOT_INDEX || n.priority <= self.nodes[i / 2].priority) {
            _insert(self, n, i);
        } else {
            _insert(self, self.nodes[i / 2], i);
            _bubbleUp(self, n, i / 2);
        }
    }

    function _bubbleDown(Data storage self, Node memory n, uint i) private {
        //
        uint length = self.nodes.length;
        uint cIndex = i * 2; // left child index

        if (length <= cIndex) {
            _insert(self, n, i);
        } else {
            Node memory largestChild = self.nodes[cIndex];

            if (length > cIndex + 1 && self.nodes[cIndex + 1].priority > largestChild.priority) {
                largestChild = self.nodes[++cIndex]; // TEST ++ gets executed first here
            }

            if (largestChild.priority <= n.priority) {
                //TEST: priority 0 is valid! negative ints work
                _insert(self, n, i);
            } else {
                _insert(self, largestChild, i);
                _bubbleDown(self, n, cIndex);
            }
        }
    }

    function _insert(Data storage self, Node memory n, uint i) private {
        //√
        self.nodes[i] = n;
        self.indices[n.id] = i;
    }
}

File 9 of 50 : SafeCast.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * Utilities that convert numeric types avoiding silent overflows.
 */
import "./SafeCast/SafeCastU32.sol";
import "./SafeCast/SafeCastI32.sol";
import "./SafeCast/SafeCastI24.sol";
import "./SafeCast/SafeCastU56.sol";
import "./SafeCast/SafeCastI56.sol";
import "./SafeCast/SafeCastU64.sol";
import "./SafeCast/SafeCastI128.sol";
import "./SafeCast/SafeCastI256.sol";
import "./SafeCast/SafeCastU128.sol";
import "./SafeCast/SafeCastU160.sol";
import "./SafeCast/SafeCastU256.sol";
import "./SafeCast/SafeCastAddress.sol";
import "./SafeCast/SafeCastBytes32.sol";

File 10 of 50 : SafeCastAddress.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastAddress {
    function toBytes32(address x) internal pure returns (bytes32) {
        return bytes32(uint256(uint160(x)));
    }
}

File 11 of 50 : SafeCastBytes32.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastBytes32 {
    function toAddress(bytes32 x) internal pure returns (address) {
        return address(uint160(uint256(x)));
    }

    function toUint(bytes32 x) internal pure returns (uint) {
        return uint(x);
    }
}

File 12 of 50 : SafeCastI128.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI128 {
    error OverflowInt128ToUint128();
    error OverflowInt128ToInt32();

    function toUint(int128 x) internal pure returns (uint128) {
        // ----------------<==============o==============>-----------------
        // ----------------xxxxxxxxxxxxxxxo===============>----------------
        if (x < 0) {
            revert OverflowInt128ToUint128();
        }

        return uint128(x);
    }

    function to256(int128 x) internal pure returns (int256) {
        return int256(x);
    }

    function to32(int128 x) internal pure returns (int32) {
        // ----------------<==============o==============>-----------------
        // ----------------xxxxxxxxxxxx<==o==>xxxxxxxxxxxx-----------------
        if (x < int(type(int32).min) || x > int(type(int32).max)) {
            revert OverflowInt128ToInt32();
        }

        return int32(x);
    }

    function zero() internal pure returns (int128) {
        return int128(0);
    }
}

File 13 of 50 : SafeCastI24.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI24 {
    function to256(int24 x) internal pure returns (int256) {
        return int256(x);
    }
}

File 14 of 50 : SafeCastI256.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI256 {
    error OverflowInt256ToUint256();
    error OverflowInt256ToInt128();
    error OverflowInt256ToInt24();

    function to128(int256 x) internal pure returns (int128) {
        // ----<==========================o===========================>----
        // ----xxxxxxxxxxxx<==============o==============>xxxxxxxxxxxxx----
        if (x < int256(type(int128).min) || x > int256(type(int128).max)) {
            revert OverflowInt256ToInt128();
        }

        return int128(x);
    }

    function to24(int256 x) internal pure returns (int24) {
        // ----<==========================o===========================>----
        // ----xxxxxxxxxxxxxxxxxxxx<======o=======>xxxxxxxxxxxxxxxxxxxx----
        if (x < int256(type(int24).min) || x > int256(type(int24).max)) {
            revert OverflowInt256ToInt24();
        }

        return int24(x);
    }

    function toUint(int256 x) internal pure returns (uint256) {
        // ----<==========================o===========================>----
        // ----xxxxxxxxxxxxxxxxxxxxxxxxxxxo===============================>
        if (x < 0) {
            revert OverflowInt256ToUint256();
        }

        return uint256(x);
    }

    function zero() internal pure returns (int256) {
        return int256(0);
    }
}

File 15 of 50 : SafeCastI32.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI32 {
    error OverflowInt32ToUint32();

    function toUint(int32 x) internal pure returns (uint32) {
        // ----------------------<========o========>----------------------
        // ----------------------xxxxxxxxxo=========>----------------------
        if (x < 0) {
            revert OverflowInt32ToUint32();
        }

        return uint32(x);
    }
}

File 16 of 50 : SafeCastI56.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastI56 {
    error OverflowInt56ToInt24();

    function to24(int56 x) internal pure returns (int24) {
        // ----------------------<========o========>-----------------------
        // ----------------------xxx<=====o=====>xxx-----------------------
        if (x < int(type(int24).min) || x > int(type(int24).max)) {
            revert OverflowInt56ToInt24();
        }

        return int24(x);
    }
}

File 17 of 50 : SafeCastU128.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU128 {
    error OverflowUint128ToInt128();

    function to256(uint128 x) internal pure returns (uint256) {
        return uint256(x);
    }

    function toInt(uint128 x) internal pure returns (int128) {
        // -------------------------------o===============>----------------
        // ----------------<==============o==============>x----------------
        if (x > uint128(type(int128).max)) {
            revert OverflowUint128ToInt128();
        }

        return int128(x);
    }

    function toBytes32(uint128 x) internal pure returns (bytes32) {
        return bytes32(uint256(x));
    }
}

File 18 of 50 : SafeCastU160.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU160 {
    function to256(uint160 x) internal pure returns (uint256) {
        return uint256(x);
    }
}

File 19 of 50 : SafeCastU256.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU256 {
    error OverflowUint256ToUint128();
    error OverflowUint256ToInt256();
    error OverflowUint256ToUint64();
    error OverflowUint256ToUint32();
    error OverflowUint256ToUint160();

    function to128(uint256 x) internal pure returns (uint128) {
        // -------------------------------o===============================>
        // -------------------------------o===============>xxxxxxxxxxxxxxxx
        if (x > type(uint128).max) {
            revert OverflowUint256ToUint128();
        }

        return uint128(x);
    }

    function to64(uint256 x) internal pure returns (uint64) {
        // -------------------------------o===============================>
        // -------------------------------o======>xxxxxxxxxxxxxxxxxxxxxxxxx
        if (x > type(uint64).max) {
            revert OverflowUint256ToUint64();
        }

        return uint64(x);
    }

    function to32(uint256 x) internal pure returns (uint32) {
        // -------------------------------o===============================>
        // -------------------------------o===>xxxxxxxxxxxxxxxxxxxxxxxxxxxx
        if (x > type(uint32).max) {
            revert OverflowUint256ToUint32();
        }

        return uint32(x);
    }

    function to160(uint256 x) internal pure returns (uint160) {
        // -------------------------------o===============================>
        // -------------------------------o==================>xxxxxxxxxxxxx
        if (x > type(uint160).max) {
            revert OverflowUint256ToUint160();
        }

        return uint160(x);
    }

    function toBytes32(uint256 x) internal pure returns (bytes32) {
        return bytes32(x);
    }

    function toInt(uint256 x) internal pure returns (int256) {
        // -------------------------------o===============================>
        // ----<==========================o===========================>xxxx
        if (x > uint256(type(int256).max)) {
            revert OverflowUint256ToInt256();
        }

        return int256(x);
    }
}

File 20 of 50 : SafeCastU32.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU32 {
    error OverflowUint32ToInt32();

    function toInt(uint32 x) internal pure returns (int32) {
        // -------------------------------o=========>----------------------
        // ----------------------<========o========>x----------------------
        if (x > uint32(type(int32).max)) {
            revert OverflowUint32ToInt32();
        }

        return int32(x);
    }

    function to256(uint32 x) internal pure returns (uint256) {
        return uint256(x);
    }

    function to56(uint32 x) internal pure returns (uint56) {
        return uint56(x);
    }
}

File 21 of 50 : SafeCastU56.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU56 {
    error OverflowUint56ToInt56();

    function toInt(uint56 x) internal pure returns (int56) {
        // -------------------------------o=========>----------------------
        // ----------------------<========o========>x----------------------
        if (x > uint56(type(int56).max)) {
            revert OverflowUint56ToInt56();
        }

        return int56(x);
    }
}

File 22 of 50 : SafeCastU64.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title See SafeCast.sol.
 */
library SafeCastU64 {
    error OverflowUint64ToInt64();

    function toInt(uint64 x) internal pure returns (int64) {
        // -------------------------------o=========>----------------------
        // ----------------------<========o========>x----------------------
        if (x > uint64(type(int64).max)) {
            revert OverflowUint64ToInt64();
        }

        return int64(x);
    }
}

File 23 of 50 : SetUtil.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "./SafeCast.sol";

library SetUtil {
    using SafeCastAddress for address;
    using SafeCastBytes32 for bytes32;
    using SafeCastU256 for uint256;

    // ----------------------------------------
    // Uint support
    // ----------------------------------------

    struct UintSet {
        Bytes32Set raw;
    }

    function add(UintSet storage set, uint value) internal {
        add(set.raw, value.toBytes32());
    }

    function remove(UintSet storage set, uint value) internal {
        remove(set.raw, value.toBytes32());
    }

    function replace(UintSet storage set, uint value, uint newValue) internal {
        replace(set.raw, value.toBytes32(), newValue.toBytes32());
    }

    function contains(UintSet storage set, uint value) internal view returns (bool) {
        return contains(set.raw, value.toBytes32());
    }

    function length(UintSet storage set) internal view returns (uint) {
        return length(set.raw);
    }

    function valueAt(UintSet storage set, uint position) internal view returns (uint) {
        return valueAt(set.raw, position).toUint();
    }

    function positionOf(UintSet storage set, uint value) internal view returns (uint) {
        return positionOf(set.raw, value.toBytes32());
    }

    function values(UintSet storage set) internal view returns (uint[] memory) {
        bytes32[] memory store = values(set.raw);
        uint[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // ----------------------------------------
    // Address support
    // ----------------------------------------

    struct AddressSet {
        Bytes32Set raw;
    }

    function add(AddressSet storage set, address value) internal {
        add(set.raw, value.toBytes32());
    }

    function remove(AddressSet storage set, address value) internal {
        remove(set.raw, value.toBytes32());
    }

    function replace(AddressSet storage set, address value, address newValue) internal {
        replace(set.raw, value.toBytes32(), newValue.toBytes32());
    }

    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return contains(set.raw, value.toBytes32());
    }

    function length(AddressSet storage set) internal view returns (uint) {
        return length(set.raw);
    }

    function valueAt(AddressSet storage set, uint position) internal view returns (address) {
        return valueAt(set.raw, position).toAddress();
    }

    function positionOf(AddressSet storage set, address value) internal view returns (uint) {
        return positionOf(set.raw, value.toBytes32());
    }

    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = values(set.raw);
        address[] memory result;

        assembly {
            result := store
        }

        return result;
    }

    // ----------------------------------------
    // Core bytes32 support
    // ----------------------------------------

    error PositionOutOfBounds();
    error ValueNotInSet();
    error ValueAlreadyInSet();

    struct Bytes32Set {
        bytes32[] _values;
        mapping(bytes32 => uint) _positions; // Position zero is never used.
    }

    function add(Bytes32Set storage set, bytes32 value) internal {
        if (contains(set, value)) {
            revert ValueAlreadyInSet();
        }

        set._values.push(value);
        set._positions[value] = set._values.length;
    }

    function remove(Bytes32Set storage set, bytes32 value) internal {
        uint position = set._positions[value];
        if (position == 0) {
            revert ValueNotInSet();
        }

        uint index = position - 1;
        uint lastIndex = set._values.length - 1;

        // If the element being deleted is not the last in the values,
        // move the last element to its position.
        if (index != lastIndex) {
            bytes32 lastValue = set._values[lastIndex];

            set._values[index] = lastValue;
            set._positions[lastValue] = position;
        }

        // Remove the last element in the values.
        set._values.pop();
        delete set._positions[value];
    }

    function replace(Bytes32Set storage set, bytes32 value, bytes32 newValue) internal {
        if (!contains(set, value)) {
            revert ValueNotInSet();
        }

        if (contains(set, newValue)) {
            revert ValueAlreadyInSet();
        }

        uint position = set._positions[value];
        delete set._positions[value];

        uint index = position - 1;

        set._values[index] = newValue;
        set._positions[newValue] = position;
    }

    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return set._positions[value] != 0;
    }

    function length(Bytes32Set storage set) internal view returns (uint) {
        return set._values.length;
    }

    function valueAt(Bytes32Set storage set, uint position) internal view returns (bytes32) {
        if (position == 0 || position > set._values.length) {
            revert PositionOutOfBounds();
        }

        uint index = position - 1;

        return set._values[index];
    }

    function positionOf(Bytes32Set storage set, bytes32 value) internal view returns (uint) {
        if (!contains(set, value)) {
            revert ValueNotInSet();
        }

        return set._positions[value];
    }

    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        return set._values;
    }
}

File 24 of 50 : FeatureFlag.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/SetUtil.sol";

library FeatureFlag {
    using SetUtil for SetUtil.AddressSet;

    error FeatureUnavailable(bytes32 which);

    struct Data {
        bytes32 name;
        bool allowAll;
        bool denyAll;
        SetUtil.AddressSet permissionedAddresses;
        address[] deniers;
    }

    function load(bytes32 featureName) internal pure returns (Data storage store) {
        bytes32 s = keccak256(abi.encode("io.synthetix.core-modules.FeatureFlag", featureName));
        assembly {
            store.slot := s
        }
    }

    function ensureAccessToFeature(bytes32 feature) internal view {
        if (!hasAccess(feature, msg.sender)) {
            revert FeatureUnavailable(feature);
        }
    }

    function hasAccess(bytes32 feature, address value) internal view returns (bool) {
        Data storage store = FeatureFlag.load(feature);

        if (store.denyAll) {
            return false;
        }

        return store.allowAll || store.permissionedAddresses.contains(value);
    }

    function isDenier(Data storage self, address possibleDenier) internal view returns (bool) {
        for (uint i = 0; i < self.deniers.length; i++) {
            if (self.deniers[i] == possibleDenier) {
                return true;
            }
        }

        return false;
    }
}

File 25 of 50 : INodeModule.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "../storage/NodeOutput.sol";
import "../storage/NodeDefinition.sol";

/// @title Module for managing nodes
interface INodeModule {
    /**
     * @notice Thrown when the specified nodeId has not been registered in the system.
     */
    error NodeNotRegistered(bytes32 nodeId);

    /**
     * @notice Thrown when a node is registered without a valid definition.
     */
    error InvalidNodeDefinition(NodeDefinition.Data nodeType);

    /**
     * @notice Thrown when a node cannot be processed
     */
    error UnprocessableNode(bytes32 nodeId);

    /**
     * @notice Emitted when `registerNode` is called.
     * @param nodeId The id of the registered node.
     * @param nodeType The nodeType assigned to this node.
     * @param parameters The parameters assigned to this node.
     * @param parents The parents assigned to this node.
     */
    event NodeRegistered(
        bytes32 nodeId,
        NodeDefinition.NodeType nodeType,
        bytes parameters,
        bytes32[] parents
    );

    /**
     * @notice Registers a node
     * @param nodeType The nodeType assigned to this node.
     * @param parameters The parameters assigned to this node.
     * @param parents The parents assigned to this node.
     * @return nodeId The id of the registered node.
     */
    function registerNode(
        NodeDefinition.NodeType nodeType,
        bytes memory parameters,
        bytes32[] memory parents
    ) external returns (bytes32 nodeId);

    /**
     * @notice Returns the ID of a node, whether or not it has been registered.
     * @param parents The parents assigned to this node.
     * @param nodeType The nodeType assigned to this node.
     * @param parameters The parameters assigned to this node.
     * @return nodeId The id of the node.
     */
    function getNodeId(
        NodeDefinition.NodeType nodeType,
        bytes memory parameters,
        bytes32[] memory parents
    ) external returns (bytes32 nodeId);

    /**
     * @notice Returns a node's definition (type, parameters, and parents)
     * @param nodeId The node ID
     * @return node The node's definition data
     */
    function getNode(bytes32 nodeId) external pure returns (NodeDefinition.Data memory node);

    /**
     * @notice Returns a node current output data
     * @param nodeId The node ID
     * @return node The node's output data
     */
    function process(bytes32 nodeId) external view returns (NodeOutput.Data memory node);

    /**
     * @notice Returns a node current output data
     * @param nodeId The node ID
     * @param runtimeKeys Keys corresponding to runtime values which could be used by the node graph
     * @param runtimeValues The values used by the node graph
     * @return node The node's output data
     */
    function processWithRuntime(
        bytes32 nodeId,
        bytes32[] memory runtimeKeys,
        bytes32[] memory runtimeValues
    ) external view returns (NodeOutput.Data memory node);
}

File 26 of 50 : NodeDefinition.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

library NodeDefinition {
    enum NodeType {
        NONE,
        REDUCER,
        EXTERNAL,
        CHAINLINK,
        UNISWAP,
        PYTH,
        PRICE_DEVIATION_CIRCUIT_BREAKER,
        STALENESS_CIRCUIT_BREAKER,
        CONSTANT
    }

    struct Data {
        /**
         * @dev Oracle node type enum
         */
        NodeType nodeType;
        /**
         * @dev Node parameters, specific to each node type
         */
        bytes parameters;
        /**
         * @dev Parent node IDs, if any
         */
        bytes32[] parents;
    }

    /**
     * @dev Returns the node stored at the specified node ID.
     */
    function load(bytes32 id) internal pure returns (Data storage node) {
        bytes32 s = keccak256(abi.encode("io.synthetix.oracle-manager.Node", id));
        assembly {
            node.slot := s
        }
    }

    /**
     * @dev Register a new node for a given node definition. The resulting node is a function of the definition.
     */
    function create(
        Data memory nodeDefinition
    ) internal returns (NodeDefinition.Data storage node, bytes32 id) {
        id = getId(nodeDefinition);

        node = load(id);

        node.nodeType = nodeDefinition.nodeType;
        node.parameters = nodeDefinition.parameters;
        node.parents = nodeDefinition.parents;
    }

    /**
     * @dev Returns a node ID based on its definition
     */
    function getId(Data memory nodeDefinition) internal pure returns (bytes32 id) {
        return
            keccak256(
                abi.encode(
                    nodeDefinition.nodeType,
                    nodeDefinition.parameters,
                    nodeDefinition.parents
                )
            );
    }
}

File 27 of 50 : NodeOutput.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

library NodeOutput {
    struct Data {
        /**
         * @dev Price returned from the oracle node, expressed with 18 decimals of precision
         */
        int256 price;
        /**
         * @dev Timestamp associated with the price
         */
        uint256 timestamp;
        // solhint-disable-next-line private-vars-leading-underscore
        uint256 __slotAvailableForFutureUse1;
        // solhint-disable-next-line private-vars-leading-underscore
        uint256 __slotAvailableForFutureUse2;
    }
}

File 28 of 50 : IMarket.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/interfaces/IERC165.sol";

/// @title Interface for markets integrated with Synthetix
interface IMarket is IERC165 {
    /// @notice returns a human-readable name for a given market
    function name(uint128 marketId) external view returns (string memory);

    /// @notice returns amount of USD that the market would try to mint if everything was withdrawn
    function reportedDebt(uint128 marketId) external view returns (uint256);

    /// @notice prevents reduction of available credit capacity by specifying this amount, for which withdrawals will be disallowed
    function minimumCredit(uint128 marketId) external view returns (uint256);
}

File 29 of 50 : IRewardDistributor.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/interfaces/IERC165.sol";

/// @title Interface a reward distributor.
interface IRewardDistributor is IERC165 {
    /// @notice Returns a human-readable name for the reward distributor
    function name() external returns (string memory);

    /// @notice This function should revert if msg.sender is not the Synthetix CoreProxy address.
    /// @return whether or not the payout was executed
    function payout(
        uint128 accountId,
        uint128 poolId,
        address collateralType,
        address sender,
        uint256 amount
    ) external returns (bool);

    /// @notice This function is called by the Synthetix Core Proxy whenever
    /// a position is updated on a pool which this distributor is registered
    function onPositionUpdated(
        uint128 accountId,
        uint128 poolId,
        address collateralType,
        uint256 newShares
    ) external;

    /// @notice Address to ERC-20 token distributed by this distributor, for display purposes only
    /// @dev Return address(0) if providing non ERC-20 rewards
    function token() external returns (address);
}

File 30 of 50 : IVaultModule.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Allows accounts to delegate collateral to a pool.
 * @dev Delegation updates the account's position in the vault that corresponds to the associated pool and collateral type pair.
 * @dev A pool contains one vault for each collateral type it supports, and vaults are not shared between pools.
 */
interface IVaultModule {
    /**
     * @notice Thrown when attempting to delegate collateral to a vault with a leverage amount that is not supported by the system.
     */
    error InvalidLeverage(uint256 leverage);

    /**
     * @notice Thrown when attempting to delegate collateral to a market whose capacity is locked.
     */
    error CapacityLocked(uint256 marketId);

    /**
     * @notice Thrown when the specified new collateral amount to delegate to the vault equals the current existing amount.
     */
    error InvalidCollateralAmount();

    /**
     * @notice Emitted when {sender} updates the delegation of collateral in the specified liquidity position.
     * @param accountId The id of the account whose position was updated.
     * @param poolId The id of the pool in which the position was updated.
     * @param collateralType The address of the collateral associated to the position.
     * @param amount The new amount of the position, denominated with 18 decimals of precision.
     * @param leverage The new leverage value of the position, denominated with 18 decimals of precision.
     * @param sender The address that triggered the update of the position.
     */
    event DelegationUpdated(
        uint128 indexed accountId,
        uint128 indexed poolId,
        address collateralType,
        uint256 amount,
        uint256 leverage,
        address indexed sender
    );

    /**
     * @notice Updates an account's delegated collateral amount for the specified pool and collateral type pair.
     * @param accountId The id of the account associated with the position that will be updated.
     * @param poolId The id of the pool associated with the position.
     * @param collateralType The address of the collateral used in the position.
     * @param amount The new amount of collateral delegated in the position, denominated with 18 decimals of precision.
     * @param leverage The new leverage amount used in the position, denominated with 18 decimals of precision.
     *
     * Requirements:
     *
     * - `msg.sender` must be the owner of the account, have the `ADMIN` permission, or have the `DELEGATE` permission.
     * - If increasing the amount delegated, it must not exceed the available collateral (`getAccountAvailableCollateral`) associated with the account.
     * - If decreasing the amount delegated, the liquidity position must have a collateralization ratio greater than the target collateralization ratio for the corresponding collateral type.
     *
     * Emits a {DelegationUpdated} event.
     */
    function delegateCollateral(
        uint128 accountId,
        uint128 poolId,
        address collateralType,
        uint256 amount,
        uint256 leverage
    ) external;

    /**
     * @notice Returns the collateralization ratio of the specified liquidity position. If debt is negative, this function will return 0.
     * @dev Call this function using `callStatic` to treat it as a view function.
     * @dev The return value is a percentage with 18 decimals places.
     * @param accountId The id of the account whose collateralization ratio is being queried.
     * @param poolId The id of the pool in which the account's position is held.
     * @param collateralType The address of the collateral used in the queried position.
     * @return ratioD18 The collateralization ratio of the position (collateral / debt), denominated with 18 decimals of precision.
     */
    function getPositionCollateralRatio(
        uint128 accountId,
        uint128 poolId,
        address collateralType
    ) external returns (uint256 ratioD18);

    /**
     * @notice Returns the debt of the specified liquidity position. Credit is expressed as negative debt.
     * @dev This is not a view function, and actually updates the entire debt distribution chain.
     * @dev Call this function using `callStatic` to treat it as a view function.
     * @param accountId The id of the account being queried.
     * @param poolId The id of the pool in which the account's position is held.
     * @param collateralType The address of the collateral used in the queried position.
     * @return debtD18 The amount of debt held by the position, denominated with 18 decimals of precision.
     */
    function getPositionDebt(
        uint128 accountId,
        uint128 poolId,
        address collateralType
    ) external returns (int256 debtD18);

    /**
     * @notice Returns the amount and value of the collateral associated with the specified liquidity position.
     * @dev Call this function using `callStatic` to treat it as a view function.
     * @dev collateralAmount is represented as an integer with 18 decimals.
     * @dev collateralValue is represented as an integer with the number of decimals specified by the collateralType.
     * @param accountId The id of the account being queried.
     * @param poolId The id of the pool in which the account's position is held.
     * @param collateralType The address of the collateral used in the queried position.
     * @return collateralAmountD18 The amount of collateral used in the position, denominated with 18 decimals of precision.
     * @return collateralValueD18 The value of collateral used in the position, denominated with 18 decimals of precision.
     */
    function getPositionCollateral(
        uint128 accountId,
        uint128 poolId,
        address collateralType
    ) external view returns (uint256 collateralAmountD18, uint256 collateralValueD18);

    /**
     * @notice Returns all information pertaining to a specified liquidity position in the vault module.
     * @param accountId The id of the account being queried.
     * @param poolId The id of the pool in which the account's position is held.
     * @param collateralType The address of the collateral used in the queried position.
     * @return collateralAmountD18 The amount of collateral used in the position, denominated with 18 decimals of precision.
     * @return collateralValueD18 The value of the collateral used in the position, denominated with 18 decimals of precision.
     * @return debtD18 The amount of debt held in the position, denominated with 18 decimals of precision.
     * @return collateralizationRatioD18 The collateralization ratio of the position (collateral / debt), denominated with 18 decimals of precision.
     **/
    function getPosition(
        uint128 accountId,
        uint128 poolId,
        address collateralType
    )
        external
        returns (
            uint256 collateralAmountD18,
            uint256 collateralValueD18,
            int256 debtD18,
            uint256 collateralizationRatioD18
        );

    /**
     * @notice Returns the total debt (or credit) that the vault is responsible for. Credit is expressed as negative debt.
     * @dev This is not a view function, and actually updates the entire debt distribution chain.
     * @dev Call this function using `callStatic` to treat it as a view function.
     * @param poolId The id of the pool that owns the vault whose debt is being queried.
     * @param collateralType The address of the collateral of the associated vault.
     * @return debtD18 The overall debt of the vault, denominated with 18 decimals of precision.
     **/
    function getVaultDebt(uint128 poolId, address collateralType) external returns (int256 debtD18);

    /**
     * @notice Returns the amount and value of the collateral held by the vault.
     * @dev Call this function using `callStatic` to treat it as a view function.
     * @dev collateralAmount is represented as an integer with 18 decimals.
     * @dev collateralValue is represented as an integer with the number of decimals specified by the collateralType.
     * @param poolId The id of the pool that owns the vault whose collateral is being queried.
     * @param collateralType The address of the collateral of the associated vault.
     * @return collateralAmountD18 The collateral amount of the vault, denominated with 18 decimals of precision.
     * @return collateralValueD18 The collateral value of the vault, denominated with 18 decimals of precision.
     */
    function getVaultCollateral(
        uint128 poolId,
        address collateralType
    ) external returns (uint256 collateralAmountD18, uint256 collateralValueD18);

    /**
     * @notice Returns the collateralization ratio of the vault. If debt is negative, this function will return 0.
     * @dev Call this function using `callStatic` to treat it as a view function.
     * @dev The return value is a percentage with 18 decimals places.
     * @param poolId The id of the pool that owns the vault whose collateralization ratio is being queried.
     * @param collateralType The address of the collateral of the associated vault.
     * @return ratioD18 The collateralization ratio of the vault, denominated with 18 decimals of precision.
     */
    function getVaultCollateralRatio(
        uint128 poolId,
        address collateralType
    ) external returns (uint256 ratioD18);
}

File 31 of 50 : Account.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "./AccountRBAC.sol";
import "./Collateral.sol";
import "./Pool.sol";

import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

/**
 * @title Object for tracking accounts with access control and collateral tracking.
 */
library Account {
    using AccountRBAC for AccountRBAC.Data;
    using Pool for Pool.Data;
    using Collateral for Collateral.Data;
    using SetUtil for SetUtil.UintSet;
    using SafeCastU128 for uint128;
    using SafeCastU256 for uint256;
    using SafeCastI128 for int128;
    using SafeCastI256 for int256;

    /**
     * @dev Thrown when the given target address does not have the given permission with the given account.
     */
    error PermissionDenied(uint128 accountId, bytes32 permission, address target);

    /**
     * @dev Thrown when an account cannot be found.
     */
    error AccountNotFound(uint128 accountId);

    /**
     * @dev Thrown when an account does not have sufficient collateral for a particular operation in the system.
     */
    error InsufficientAccountCollateral(uint256 requestedAmount);

    /**
     * @dev Thrown when the requested operation requires an activity timeout before the
     */
    error AccountActivityTimeoutPending(
        uint128 accountId,
        uint256 currentTime,
        uint256 requiredTime
    );

    struct Data {
        /**
         * @dev Numeric identifier for the account. Must be unique.
         * @dev There cannot be an account with id zero (See ERC721._mint()).
         */
        uint128 id;
        /**
         * @dev Role based access control data for the account.
         */
        AccountRBAC.Data rbac;
        uint64 lastInteraction;
        uint64 __slotAvailableForFutureUse;
        uint128 __slot2AvailableForFutureUse;
        /**
         * @dev Address set of collaterals that are being used in the system by this account.
         */
        mapping(address => Collateral.Data) collaterals;
    }

    /**
     * @dev Returns the account stored at the specified account id.
     */
    function load(uint128 id) internal pure returns (Data storage account) {
        bytes32 s = keccak256(abi.encode("io.synthetix.synthetix.Account", id));
        assembly {
            account.slot := s
        }
    }

    /**
     * @dev Creates an account for the given id, and associates it to the given owner.
     *
     * Note: Will not fail if the account already exists, and if so, will overwrite the existing owner. Whatever calls this internal function must first check that the account doesn't exist before re-creating it.
     */
    function create(uint128 id, address owner) internal returns (Data storage account) {
        account = load(id);

        account.id = id;
        account.rbac.owner = owner;
    }

    /**
     * @dev Reverts if the account does not exist with appropriate error. Otherwise, returns the account.
     */
    function exists(uint128 id) internal view returns (Data storage account) {
        Data storage a = load(id);
        if (a.rbac.owner == address(0)) {
            revert AccountNotFound(id);
        }

        return a;
    }

    /**
     * @dev Given a collateral type, returns information about the total collateral assigned, deposited, and locked by the account
     */
    function getCollateralTotals(
        Data storage self,
        address collateralType
    )
        internal
        view
        returns (uint256 totalDepositedD18, uint256 totalAssignedD18, uint256 totalLockedD18)
    {
        totalAssignedD18 = getAssignedCollateral(self, collateralType);
        totalDepositedD18 =
            totalAssignedD18 +
            self.collaterals[collateralType].amountAvailableForDelegationD18;
        totalLockedD18 = self.collaterals[collateralType].getTotalLocked();

        return (totalDepositedD18, totalAssignedD18, totalLockedD18);
    }

    /**
     * @dev Returns the total amount of collateral that has been delegated to pools by the account, for the given collateral type.
     */
    function getAssignedCollateral(
        Data storage self,
        address collateralType
    ) internal view returns (uint256) {
        uint256 totalAssignedD18 = 0;

        SetUtil.UintSet storage pools = self.collaterals[collateralType].pools;

        for (uint256 i = 1; i <= pools.length(); i++) {
            uint128 poolIdx = pools.valueAt(i).to128();

            Pool.Data storage pool = Pool.load(poolIdx);

            (uint256 collateralAmountD18, ) = pool.currentAccountCollateral(
                collateralType,
                self.id
            );
            totalAssignedD18 += collateralAmountD18;
        }

        return totalAssignedD18;
    }

    function recordInteraction(Data storage self) internal {
        // solhint-disable-next-line numcast/safe-cast
        self.lastInteraction = uint64(block.timestamp);
    }

    /**
     * @dev Loads the Account object for the specified accountId,
     * and validates that sender has the specified permission. It also resets
     * the interaction timeout. These
     * are different actions but they are merged in a single function
     * because loading an account and checking for a permission is a very
     * common use case in other parts of the code.
     */
    function loadAccountAndValidatePermission(
        uint128 accountId,
        bytes32 permission
    ) internal returns (Data storage account) {
        account = Account.load(accountId);

        if (!account.rbac.authorized(permission, msg.sender)) {
            revert PermissionDenied(accountId, permission, msg.sender);
        }

        recordInteraction(account);
    }

    /**
     * @dev Loads the Account object for the specified accountId,
     * and validates that sender has the specified permission. It also resets
     * the interaction timeout. These
     * are different actions but they are merged in a single function
     * because loading an account and checking for a permission is a very
     * common use case in other parts of the code.
     */
    function loadAccountAndValidatePermissionAndTimeout(
        uint128 accountId,
        bytes32 permission,
        uint256 timeout
    ) internal view returns (Data storage account) {
        account = Account.load(accountId);

        if (!account.rbac.authorized(permission, msg.sender)) {
            revert PermissionDenied(accountId, permission, msg.sender);
        }

        uint256 endWaitingPeriod = account.lastInteraction + timeout;
        if (block.timestamp < endWaitingPeriod) {
            revert AccountActivityTimeoutPending(accountId, block.timestamp, endWaitingPeriod);
        }
    }

    /**
     * @dev Ensure that the account has the required amount of collateral funds remaining
     */
    function requireSufficientCollateral(
        uint128 accountId,
        address collateralType,
        uint256 amountD18
    ) internal view {
        if (
            Account.load(accountId).collaterals[collateralType].amountAvailableForDelegationD18 <
            amountD18
        ) {
            revert InsufficientAccountCollateral(amountD18);
        }
    }
}

File 32 of 50 : AccountRBAC.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/SetUtil.sol";
import "@synthetixio/core-contracts/contracts/errors/AddressError.sol";

/**
 * @title Object for tracking an accounts permissions (role based access control).
 */
library AccountRBAC {
    using SetUtil for SetUtil.Bytes32Set;
    using SetUtil for SetUtil.AddressSet;

    /**
     * @dev All permissions used by the system
     * need to be hardcoded here.
     */
    bytes32 internal constant _ADMIN_PERMISSION = "ADMIN";
    bytes32 internal constant _WITHDRAW_PERMISSION = "WITHDRAW";
    bytes32 internal constant _DELEGATE_PERMISSION = "DELEGATE";
    bytes32 internal constant _MINT_PERMISSION = "MINT";
    bytes32 internal constant _REWARDS_PERMISSION = "REWARDS";
    bytes32 internal constant _PERPS_MODIFY_COLLATERAL_PERMISSION = "PERPS_MODIFY_COLLATERAL";
    bytes32 internal constant _PERPS_COMMIT_ASYNC_ORDER_PERMISSION = "PERPS_COMMIT_ASYNC_ORDER";
    bytes32 internal constant _BURN_PERMISSION = "BURN";

    /**
     * @dev Thrown when a permission specified by a user does not exist or is invalid.
     */
    error InvalidPermission(bytes32 permission);

    struct Data {
        /**
         * @dev The owner of the account and admin of all permissions.
         */
        address owner;
        /**
         * @dev Set of permissions for each address enabled by the account.
         */
        mapping(address => SetUtil.Bytes32Set) permissions;
        /**
         * @dev Array of addresses that this account has given permissions to.
         */
        SetUtil.AddressSet permissionAddresses;
    }

    /**
     * @dev Reverts if the specified permission is unknown to the account RBAC system.
     */
    function isPermissionValid(bytes32 permission) internal pure {
        if (
            permission != AccountRBAC._WITHDRAW_PERMISSION &&
            permission != AccountRBAC._DELEGATE_PERMISSION &&
            permission != AccountRBAC._MINT_PERMISSION &&
            permission != AccountRBAC._ADMIN_PERMISSION &&
            permission != AccountRBAC._REWARDS_PERMISSION &&
            permission != AccountRBAC._PERPS_MODIFY_COLLATERAL_PERMISSION &&
            permission != AccountRBAC._PERPS_COMMIT_ASYNC_ORDER_PERMISSION &&
            permission != AccountRBAC._BURN_PERMISSION
        ) {
            revert InvalidPermission(permission);
        }
    }

    /**
     * @dev Sets the owner of the account.
     */
    function setOwner(Data storage self, address owner) internal {
        self.owner = owner;
    }

    /**
     * @dev Grants a particular permission to the specified target address.
     */
    function grantPermission(Data storage self, bytes32 permission, address target) internal {
        if (target == address(0)) {
            revert AddressError.ZeroAddress();
        }

        if (permission == "") {
            revert InvalidPermission("");
        }

        if (!self.permissionAddresses.contains(target)) {
            self.permissionAddresses.add(target);
        }

        self.permissions[target].add(permission);
    }

    /**
     * @dev Revokes a particular permission from the specified target address.
     */
    function revokePermission(Data storage self, bytes32 permission, address target) internal {
        self.permissions[target].remove(permission);

        if (self.permissions[target].length() == 0) {
            self.permissionAddresses.remove(target);
        }
    }

    /**
     * @dev Revokes all permissions for the specified target address.
     * @notice only removes permissions for the given address, not for the entire account
     */
    function revokeAllPermissions(Data storage self, address target) internal {
        bytes32[] memory permissions = self.permissions[target].values();

        if (permissions.length == 0) {
            return;
        }

        for (uint256 i = 0; i < permissions.length; i++) {
            self.permissions[target].remove(permissions[i]);
        }

        self.permissionAddresses.remove(target);
    }

    /**
     * @dev Returns wether the specified address has the given permission.
     */
    function hasPermission(
        Data storage self,
        bytes32 permission,
        address target
    ) internal view returns (bool) {
        return target != address(0) && self.permissions[target].contains(permission);
    }

    /**
     * @dev Returns wether the specified target address has the given permission, or has the high level admin permission.
     */
    function authorized(
        Data storage self,
        bytes32 permission,
        address target
    ) internal view returns (bool) {
        return ((target == self.owner) ||
            hasPermission(self, _ADMIN_PERMISSION, target) ||
            hasPermission(self, permission, target));
    }
}

File 33 of 50 : Collateral.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/SetUtil.sol";
import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

import "./CollateralLock.sol";

/**
 * @title Stores information about a deposited asset for a given account.
 *
 * Each account will have one of these objects for each type of collateral it deposited in the system.
 */
library Collateral {
    using SafeCastU256 for uint256;

    /**
     * @dev Thrown when a specified market is not found.
     */
    error InsufficentAvailableCollateral(
        uint256 amountAvailableForDelegationD18,
        uint256 amountD18
    );

    struct Data {
        /**
         * @dev The amount that can be withdrawn or delegated in this collateral.
         */
        uint256 amountAvailableForDelegationD18;
        /**
         * @dev The pools to which this collateral delegates to.
         */
        SetUtil.UintSet pools;
        /**
         * @dev Marks portions of the collateral as locked,
         * until a given unlock date.
         *
         * Note: Locks apply to delegated collateral and to collateral not
         * assigned or delegated to a pool (see ICollateralModule).
         */
        CollateralLock.Data[] locks;
    }

    /**
     * @dev Increments the entry's availableCollateral.
     */
    function increaseAvailableCollateral(Data storage self, uint256 amountD18) internal {
        self.amountAvailableForDelegationD18 += amountD18;
    }

    /**
     * @dev Decrements the entry's availableCollateral.
     */
    function decreaseAvailableCollateral(Data storage self, uint256 amountD18) internal {
        if (self.amountAvailableForDelegationD18 < amountD18) {
            revert InsufficentAvailableCollateral(self.amountAvailableForDelegationD18, amountD18);
        }
        self.amountAvailableForDelegationD18 -= amountD18;
    }

    /**
     * @dev Returns the total amount in this collateral entry that is locked.
     *
     * Sweeps through all existing locks and accumulates their amount,
     * if their unlock date is in the future.
     */
    function getTotalLocked(Data storage self) internal view returns (uint256) {
        uint64 currentTime = block.timestamp.to64();

        uint256 lockedD18;
        for (uint256 i = 0; i < self.locks.length; i++) {
            CollateralLock.Data storage lock = self.locks[i];

            if (lock.lockExpirationTime > currentTime) {
                lockedD18 += lock.amountD18;
            }
        }

        return lockedD18;
    }
}

File 34 of 50 : CollateralConfiguration.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/SetUtil.sol";
import "@synthetixio/core-contracts/contracts/utils/DecimalMath.sol";
import "@synthetixio/core-contracts/contracts/errors/ParameterError.sol";
import "@synthetixio/oracle-manager/contracts/interfaces/INodeModule.sol";
import "@synthetixio/oracle-manager/contracts/storage/NodeOutput.sol";
import "@synthetixio/core-contracts/contracts/interfaces/IERC20.sol";
import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

import "./OracleManager.sol";

/**
 * @title Tracks system-wide settings for each collateral type, as well as helper functions for it, such as retrieving its current price from the oracle manager.
 */
library CollateralConfiguration {
    bytes32 private constant _SLOT_AVAILABLE_COLLATERALS =
        keccak256(
            abi.encode("io.synthetix.synthetix.CollateralConfiguration_availableCollaterals")
        );

    using SetUtil for SetUtil.AddressSet;
    using DecimalMath for uint256;
    using SafeCastI256 for int256;

    /**
     * @dev Thrown when the token address of a collateral cannot be found.
     */
    error CollateralNotFound();

    /**
     * @dev Thrown when deposits are disabled for the given collateral type.
     * @param collateralType The address of the collateral type for which depositing was disabled.
     */
    error CollateralDepositDisabled(address collateralType);

    /**
     * @dev Thrown when collateral ratio is not sufficient in a given operation in the system.
     * @param collateralValue The net USD value of the position.
     * @param debt The net USD debt of the position.
     * @param ratio The collateralization ratio of the position.
     * @param minRatio The minimum c-ratio which was not met. Could be issuance ratio or liquidation ratio, depending on the case.
     */
    error InsufficientCollateralRatio(
        uint256 collateralValue,
        uint256 debt,
        uint256 ratio,
        uint256 minRatio
    );

    /**
     * @dev Thrown when the amount being delegated is less than the minimum expected amount.
     * @param minDelegation The current minimum for deposits and delegation set to this collateral type.
     */
    error InsufficientDelegation(uint256 minDelegation);

    /**
     * @dev Thrown when attempting to convert a token to the system amount and the conversion results in a loss of precision.
     * @param tokenAmount The amount of tokens that were attempted to be converted.
     * @param decimals The number of decimals of the token that was attempted to be converted.
     */
    error PrecisionLost(uint256 tokenAmount, uint8 decimals);

    struct Data {
        /**
         * @dev Allows the owner to control deposits and delegation of collateral types.
         */
        bool depositingEnabled;
        /**
         * @dev System-wide collateralization ratio for issuance of snxUSD.
         * Accounts will not be able to mint snxUSD if they are below this issuance c-ratio.
         */
        uint256 issuanceRatioD18;
        /**
         * @dev System-wide collateralization ratio for liquidations of this collateral type.
         * Accounts below this c-ratio can be immediately liquidated.
         */
        uint256 liquidationRatioD18;
        /**
         * @dev Amount of tokens to award when an account is liquidated.
         */
        uint256 liquidationRewardD18;
        /**
         * @dev The oracle manager node id which reports the current price for this collateral type.
         */
        bytes32 oracleNodeId;
        /**
         * @dev The token address for this collateral type.
         */
        address tokenAddress;
        /**
         * @dev Minimum amount that accounts can delegate to pools.
         * Helps prevent spamming on the system.
         * Note: If zero, liquidationRewardD18 will be used.
         */
        uint256 minDelegationD18;
    }

    /**
     * @dev Loads the CollateralConfiguration object for the given collateral type.
     * @param token The address of the collateral type.
     * @return collateralConfiguration The CollateralConfiguration object.
     */
    function load(address token) internal pure returns (Data storage collateralConfiguration) {
        bytes32 s = keccak256(abi.encode("io.synthetix.synthetix.CollateralConfiguration", token));
        assembly {
            collateralConfiguration.slot := s
        }
    }

    /**
     * @dev Loads all available collateral types configured in the system.
     * @return availableCollaterals An array of addresses, one for each collateral type supported by the system.
     */
    function loadAvailableCollaterals()
        internal
        pure
        returns (SetUtil.AddressSet storage availableCollaterals)
    {
        bytes32 s = _SLOT_AVAILABLE_COLLATERALS;
        assembly {
            availableCollaterals.slot := s
        }
    }

    /**
     * @dev Configures a collateral type.
     * @param config The CollateralConfiguration object with all the settings for the collateral type being configured.
     */
    function set(Data memory config) internal {
        SetUtil.AddressSet storage collateralTypes = loadAvailableCollaterals();

        if (!collateralTypes.contains(config.tokenAddress)) {
            collateralTypes.add(config.tokenAddress);
        }

        if (config.minDelegationD18 < config.liquidationRewardD18) {
            revert ParameterError.InvalidParameter(
                "minDelegation",
                "must be greater than liquidationReward"
            );
        }

        if (config.issuanceRatioD18 <= 1e18) {
            revert ParameterError.InvalidParameter("issuanceRatioD18", "must be greater than 100%");
        }

        if (config.liquidationRatioD18 <= 1e18) {
            revert ParameterError.InvalidParameter(
                "liquidationRatioD18",
                "must be greater than 100%"
            );
        }

        if (config.issuanceRatioD18 < config.liquidationRatioD18) {
            revert ParameterError.InvalidParameter(
                "issuanceRatioD18",
                "must be greater than liquidationRatioD18"
            );
        }

        Data storage storedConfig = load(config.tokenAddress);

        storedConfig.tokenAddress = config.tokenAddress;
        storedConfig.issuanceRatioD18 = config.issuanceRatioD18;
        storedConfig.liquidationRatioD18 = config.liquidationRatioD18;
        storedConfig.oracleNodeId = config.oracleNodeId;
        storedConfig.liquidationRewardD18 = config.liquidationRewardD18;
        storedConfig.minDelegationD18 = config.minDelegationD18;
        storedConfig.depositingEnabled = config.depositingEnabled;
    }

    /**
     * @dev Shows if a given collateral type is enabled for deposits and delegation.
     * @param token The address of the collateral being queried.
     */
    function collateralEnabled(address token) internal view {
        if (!load(token).depositingEnabled) {
            revert CollateralDepositDisabled(token);
        }
    }

    /**
     * @dev Reverts if the amount being delegated is insufficient for the system.
     * @param token The address of the collateral type.
     * @param amountD18 The amount being checked for sufficient delegation.
     */
    function requireSufficientDelegation(address token, uint256 amountD18) internal view {
        CollateralConfiguration.Data storage config = load(token);

        uint256 minDelegationD18 = config.minDelegationD18;

        if (minDelegationD18 == 0) {
            minDelegationD18 = config.liquidationRewardD18;
        }

        if (amountD18 < minDelegationD18) {
            revert InsufficientDelegation(minDelegationD18);
        }
    }

    /**
     * @dev Returns the price of this collateral configuration object.
     * @param self The CollateralConfiguration object.
     * @return The price of the collateral with 18 decimals of precision.
     */
    function getCollateralPrice(Data storage self) internal view returns (uint256) {
        OracleManager.Data memory oracleManager = OracleManager.load();
        NodeOutput.Data memory node = INodeModule(oracleManager.oracleManagerAddress).process(
            self.oracleNodeId
        );

        return node.price.toUint();
    }

    /**
     * @dev Reverts if the specified collateral and debt values produce a collateralization ratio which is below the amount required for new issuance of snxUSD.
     * @param self The CollateralConfiguration object whose collateral and settings are being queried.
     * @param debtD18 The debt component of the ratio.
     * @param collateralValueD18 The collateral component of the ratio.
     */
    function verifyIssuanceRatio(
        Data storage self,
        uint256 debtD18,
        uint256 collateralValueD18,
        uint256 minIssuanceRatioD18
    ) internal view {
        uint256 issuanceRatioD18 = self.issuanceRatioD18 > minIssuanceRatioD18
            ? self.issuanceRatioD18
            : minIssuanceRatioD18;

        if (
            debtD18 != 0 &&
            (collateralValueD18 == 0 || collateralValueD18.divDecimal(debtD18) < issuanceRatioD18)
        ) {
            revert InsufficientCollateralRatio(
                collateralValueD18,
                debtD18,
                collateralValueD18.divDecimal(debtD18),
                issuanceRatioD18
            );
        }
    }

    /**
     * @dev Converts token amounts with non-system decimal precisions, to 18 decimals of precision.
     * E.g: $TOKEN_A uses 6 decimals of precision, so this would upscale it by 12 decimals.
     * E.g: $TOKEN_B uses 20 decimals of precision, so this would downscale it by 2 decimals.
     * @param self The CollateralConfiguration object corresponding to the collateral type being converted.
     * @param tokenAmount The token amount, denominated in its native decimal precision.
     * @return amountD18 The converted amount, denominated in the system's 18 decimal precision.
     */
    function convertTokenToSystemAmount(
        Data storage self,
        uint256 tokenAmount
    ) internal view returns (uint256 amountD18) {
        // this extra condition is to prevent potentially malicious untrusted code from being executed on the next statement
        if (self.tokenAddress == address(0)) {
            revert CollateralNotFound();
        }

        /// @dev this try-catch block assumes there is no malicious code in the token's fallback function
        try IERC20(self.tokenAddress).decimals() returns (uint8 decimals) {
            if (decimals == 18) {
                amountD18 = tokenAmount;
            } else if (decimals < 18) {
                amountD18 = (tokenAmount * DecimalMath.UNIT) / (10 ** decimals);
            } else {
                // ensure no precision is lost when converting to 18 decimals
                if (tokenAmount % (10 ** (decimals - 18)) != 0) {
                    revert PrecisionLost(tokenAmount, decimals);
                }

                // this will scale down the amount by the difference between the token's decimals and 18
                amountD18 = (tokenAmount * DecimalMath.UNIT) / (10 ** decimals);
            }
        } catch {
            // if the token doesn't have a decimals function, assume it's 18 decimals
            amountD18 = tokenAmount;
        }
    }
}

File 35 of 50 : CollateralLock.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Represents a given amount of collateral locked until a given date.
 */
library CollateralLock {
    struct Data {
        /**
         * @dev The amount of collateral that has been locked.
         */
        uint128 amountD18;
        /**
         * @dev The date when the locked amount becomes unlocked.
         */
        uint64 lockExpirationTime;
    }
}

File 36 of 50 : Config.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title System wide configuration for anything
 */
library Config {
    struct Data {
        uint256 __unused;
    }

    /**
     * @dev Returns a config value
     */
    function read(bytes32 k, bytes32 zeroValue) internal view returns (bytes32 v) {
        bytes32 s = keccak256(abi.encode("Config", k));
        assembly {
            v := sload(s)
        }

        if (v == bytes32(0)) {
            v = zeroValue;
        }
    }

    function readUint(bytes32 k, uint256 zeroValue) internal view returns (uint256 v) {
        // solhint-disable-next-line numcast/safe-cast
        return uint(read(k, bytes32(zeroValue)));
    }

    function readAddress(bytes32 k, address zeroValue) internal view returns (address v) {
        // solhint-disable-next-line numcast/safe-cast
        return address(uint160(readUint(k, uint160(zeroValue))));
    }

    function put(bytes32 k, bytes32 v) internal {
        bytes32 s = keccak256(abi.encode("Config", k));
        assembly {
            sstore(s, v)
        }
    }
}

File 37 of 50 : Distribution.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/DecimalMath.sol";
import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

import "./DistributionActor.sol";

/**
 * @title Data structure that allows you to track some global value, distributed amongst a set of actors.
 *
 * The total value can be scaled with a valuePerShare multiplier, and individual actor shares can be calculated as their amount of shares times this multiplier.
 *
 * Furthermore, changes in the value of individual actors can be tracked since their last update, by keeping track of the value of the multiplier, per user, upon each interaction. See DistributionActor.lastValuePerShare.
 *
 * A distribution is similar to a ScalableMapping, but it has the added functionality of being able to remember the previous value of the scalar multiplier for each actor.
 *
 * Whenever the shares of an actor of the distribution is updated, you get information about how the actor's total value changed since it was last updated.
 */
library Distribution {
    using SafeCastU128 for uint128;
    using SafeCastU256 for uint256;
    using SafeCastI128 for int128;
    using SafeCastI256 for int256;
    using DecimalMath for int256;

    /**
     * @dev Thrown when an attempt is made to distribute value to a distribution
     * with no shares.
     */
    error EmptyDistribution();

    struct Data {
        /**
         * @dev The total number of shares in the distribution.
         */
        uint128 totalSharesD18;
        /**
         * @dev The value per share of the distribution, represented as a high precision decimal.
         */
        int128 valuePerShareD27;
        /**
         * @dev Tracks individual actor information, such as how many shares an actor has, their lastValuePerShare, etc.
         */
        mapping(bytes32 => DistributionActor.Data) actorInfo;
    }

    /**
     * @dev Inflates or deflates the total value of the distribution by the given value.
     *
     * The value being distributed ultimately modifies the distribution's valuePerShare.
     */
    function distributeValue(Data storage self, int256 valueD18) internal {
        if (valueD18 == 0) {
            return;
        }

        uint256 totalSharesD18 = self.totalSharesD18;

        if (totalSharesD18 == 0) {
            revert EmptyDistribution();
        }

        int256 valueD45 = valueD18 * DecimalMath.UNIT_PRECISE_INT;
        int256 deltaValuePerShareD27 = valueD45 / totalSharesD18.toInt();

        self.valuePerShareD27 += deltaValuePerShareD27.to128();
    }

    /**
     * @dev Updates an actor's number of shares in the distribution to the specified amount.
     *
     * Whenever an actor's shares are changed in this way, we record the distribution's current valuePerShare into the actor's lastValuePerShare record.
     *
     * Returns the the amount by which the actors value changed since the last update.
     */
    function setActorShares(
        Data storage self,
        bytes32 actorId,
        uint256 newActorSharesD18
    ) internal returns (int256 valueChangeD18) {
        valueChangeD18 = getActorValueChange(self, actorId);

        DistributionActor.Data storage actor = self.actorInfo[actorId];

        uint128 sharesUint128D18 = newActorSharesD18.to128();
        self.totalSharesD18 = self.totalSharesD18 + sharesUint128D18 - actor.sharesD18;

        actor.sharesD18 = sharesUint128D18;
        _updateLastValuePerShare(self, actor, newActorSharesD18);
    }

    /**
     * @dev Updates an actor's lastValuePerShare to the distribution's current valuePerShare, and
     * returns the change in value for the actor, since their last update.
     */
    function accumulateActor(
        Data storage self,
        bytes32 actorId
    ) internal returns (int256 valueChangeD18) {
        DistributionActor.Data storage actor = self.actorInfo[actorId];
        return _updateLastValuePerShare(self, actor, actor.sharesD18);
    }

    /**
     * @dev Calculates how much an actor's value has changed since its shares were last updated.
     *
     * This change is calculated as:
     * Since `value = valuePerShare * shares`,
     * then `delta_value = valuePerShare_now * shares - valuePerShare_then * shares`,
     * which is `(valuePerShare_now - valuePerShare_then) * shares`,
     * or just `delta_valuePerShare * shares`.
     */
    function getActorValueChange(
        Data storage self,
        bytes32 actorId
    ) internal view returns (int256 valueChangeD18) {
        return _getActorValueChange(self, self.actorInfo[actorId]);
    }

    /**
     * @dev Returns the number of shares owned by an actor in the distribution.
     */
    function getActorShares(
        Data storage self,
        bytes32 actorId
    ) internal view returns (uint256 sharesD18) {
        return self.actorInfo[actorId].sharesD18;
    }

    /**
     * @dev Returns the distribution's value per share in normal precision (18 decimals).
     * @param self The distribution whose value per share is being queried.
     * @return The value per share in 18 decimal precision.
     */
    function getValuePerShare(Data storage self) internal view returns (int256) {
        return self.valuePerShareD27.to256().downscale(DecimalMath.PRECISION_FACTOR);
    }

    function _updateLastValuePerShare(
        Data storage self,
        DistributionActor.Data storage actor,
        uint256 newActorShares
    ) private returns (int256 valueChangeD18) {
        valueChangeD18 = _getActorValueChange(self, actor);

        actor.lastValuePerShareD27 = newActorShares == 0
            ? SafeCastI128.zero()
            : self.valuePerShareD27;
    }

    function _getActorValueChange(
        Data storage self,
        DistributionActor.Data storage actor
    ) private view returns (int256 valueChangeD18) {
        int256 deltaValuePerShareD27 = self.valuePerShareD27 - actor.lastValuePerShareD27;

        int256 changedValueD45 = deltaValuePerShareD27 * actor.sharesD18.toInt();
        valueChangeD18 = changedValueD45 / DecimalMath.UNIT_PRECISE_INT;
    }
}

File 38 of 50 : DistributionActor.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Stores information for specific actors in a Distribution.
 */
library DistributionActor {
    struct Data {
        /**
         * @dev The actor's current number of shares in the associated distribution.
         */
        uint128 sharesD18;
        /**
         * @dev The value per share that the associated distribution had at the time that the actor's number of shares was last modified.
         *
         * Note: This is also a high precision decimal. See Distribution.valuePerShare.
         */
        int128 lastValuePerShareD27;
    }
}

File 39 of 50 : Market.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/HeapUtil.sol";

import "./Distribution.sol";
import "./CollateralConfiguration.sol";
import "./MarketPoolInfo.sol";

import "../interfaces/external/IMarket.sol";

/**
 * @title Connects external contracts that implement the `IMarket` interface to the system.
 *
 * Pools provide credit capacity (collateral) to the markets, and are reciprocally exposed to the associated market's debt.
 *
 * The Market object's main responsibility is to track collateral provided by the pools that support it, and to trace their debt back to such pools.
 */
library Market {
    using Distribution for Distribution.Data;
    using HeapUtil for HeapUtil.Data;
    using DecimalMath for uint256;
    using DecimalMath for uint128;
    using DecimalMath for int256;
    using DecimalMath for int128;
    using SafeCastU256 for uint256;
    using SafeCastU128 for uint128;
    using SafeCastI256 for int256;
    using SafeCastI128 for int128;

    /**
     * @dev Thrown when a specified market is not found.
     */
    error MarketNotFound(uint128 marketId);

    struct Data {
        /**
         * @dev Numeric identifier for the market. Must be unique.
         * @dev There cannot be a market with id zero (See MarketCreator.create()). Id zero is used as a null market reference.
         */
        uint128 id;
        /**
         * @dev Address for the external contract that implements the `IMarket` interface, which this Market objects connects to.
         *
         * Note: This object is how the system tracks the market. The actual market is external to the system, i.e. its own contract.
         */
        address marketAddress;
        /**
         * @dev Issuance can be seen as how much USD the Market "has issued", printed, or has asked the system to mint on its behalf.
         *
         * More precisely it can be seen as the net difference between the USD burnt and the USD minted by the market.
         *
         * More issuance means that the market owes more USD to the system.
         *
         * A market burns USD when users deposit it in exchange for some asset that the market offers.
         * The Market object calls `MarketManager.depositUSD()`, which burns the USD, and decreases its issuance.
         *
         * A market mints USD when users return the asset that the market offered and thus withdraw their USD.
         * The Market object calls `MarketManager.withdrawUSD()`, which mints the USD, and increases its issuance.
         *
         * Instead of burning, the Market object could transfer USD to and from the MarketManager, but minting and burning takes the USD out of circulation, which doesn't affect `totalSupply`, thus simplifying accounting.
         *
         * How much USD a market can mint depends on how much credit capacity is given to the market by the pools that support it, and reflected in `Market.capacity`.
         *
         */
        int128 netIssuanceD18;
        /**
         * @dev The total amount of USD that the market could withdraw if it were to immediately unwrap all its positions.
         *
         * The Market's credit capacity increases when the market burns USD, i.e. when it deposits USD in the MarketManager.
         *
         * It decreases when the market mints USD, i.e. when it withdraws USD from the MarketManager.
         *
         * The Market's credit capacity also depends on how much credit is given to it by the pools that support it.
         *
         * The Market's credit capacity also has a dependency on the external market reported debt as it will respond to that debt (and hence change the credit capacity if it increases or decreases)
         *
         * The credit capacity can go negative if all of the collateral provided by pools is exhausted, and there is market provided collateral available to consume. in this case, the debt is still being
         * appropriately assigned, but the market has a dynamic cap based on deposited collateral types.
         *
         */
        int128 creditCapacityD18;
        /**
         * @dev The total balance that the market had the last time that its debt was distributed.
         *
         * A Market's debt is distributed when the reported debt of its associated external market is rolled into the pools that provide credit capacity to it.
         */
        int128 lastDistributedMarketBalanceD18;
        /**
         * @dev A heap of pools for which the market has not yet hit its maximum credit capacity.
         *
         * The heap is ordered according to this market's max value per share setting in the pools that provide credit capacity to it. See `MarketConfiguration.maxDebtShareValue`.
         *
         * The heap's getMax() and extractMax() functions allow us to retrieve the pool with the lowest `maxDebtShareValue`, since its elements are inserted and prioritized by negating their `maxDebtShareValue`.
         *
         * Lower max values per share are on the top of the heap. I.e. the heap could look like this:
         *  .    -1
         *      / \
         *     /   \
         *    -2    \
         *   / \    -3
         * -4   -5
         *
         * TL;DR: This data structure allows us to easily find the pool with the lowest or "most vulnerable" max value per share and process it if its actual value per share goes beyond this limit.
         */
        HeapUtil.Data inRangePools;
        /**
         * @dev A heap of pools for which the market has hit its maximum credit capacity.
         *
         * Used to reconnect pools to the market, when it falls back below its maximum credit capacity.
         *
         * See inRangePools for why a heap is used here.
         */
        HeapUtil.Data outRangePools;
        /**
         * @dev A market's debt distribution connects markets to the debt distribution chain, in this case pools. Pools are actors in the market's debt distribution, where the amount of shares they possess depends on the amount of collateral they provide to the market. The value per share of this distribution depends on the total debt or balance of the market (netIssuance + reportedDebt).
         *
         * The debt distribution chain will move debt from the market into its connected pools.
         *
         * Actors: Pools.
         * Shares: The USD denominated credit capacity that the pool provides to the market.
         * Value per share: Debt per dollar of credit that the associated external market accrues.
         *
         */
        Distribution.Data poolsDebtDistribution;
        /**
         * @dev Additional info needed to remember pools when they are removed from the distribution (or subsequently re-added).
         */
        mapping(uint128 => MarketPoolInfo.Data) pools;
        /**
         * @dev Array of entries of market provided collateral.
         *
         * Markets may obtain additional liquidity, beyond that coming from depositors, by providing their own collateral.
         *
         */
        DepositedCollateral[] depositedCollateral;
        /**
         * @dev The maximum amount of market provided collateral, per type, that this market can deposit.
         */
        mapping(address => uint256) maximumDepositableD18;
        uint32 minDelegateTime;
        uint32 __reservedForLater1;
        uint64 __reservedForLater2;
        uint64 __reservedForLater3;
        uint64 __reservedForLater4;
        /**
         * @dev Market-specific override of the minimum liquidity ratio
         */
        uint256 minLiquidityRatioD18;
    }

    /**
     * @dev Data structure that allows the Market to track the amount of market provided collateral, per type.
     */
    struct DepositedCollateral {
        address collateralType;
        uint256 amountD18;
    }

    /**
     * @dev Returns the market stored at the specified market id.
     */
    function load(uint128 id) internal pure returns (Data storage market) {
        bytes32 s = keccak256(abi.encode("io.synthetix.synthetix.Market", id));
        assembly {
            market.slot := s
        }
    }

    /**
     * @dev Queries the external market contract for the amount of debt it has issued.
     *
     * The reported debt of a market represents the amount of USD that the market would ask the system to mint, if all of its positions were to be immediately closed.
     *
     * The reported debt of a market is collateralized by the assets in the pools which back it.
     *
     * See the `IMarket` interface.
     */
    function getReportedDebt(Data storage self) internal view returns (uint256) {
        return IMarket(self.marketAddress).reportedDebt(self.id);
    }

    /**
     * @dev Queries the market for the amount of collateral which should be prevented from withdrawal.
     */
    function getLockedCreditCapacity(Data storage self) internal view returns (uint256) {
        return IMarket(self.marketAddress).minimumCredit(self.id);
    }

    /**
     * @dev Returns the total debt of the market.
     *
     * A market's total debt represents its debt plus its issuance, and thus represents the total outstanding debt of the market.
     *
     * Note: it also takes into account the deposited collateral value. See note in  getDepositedCollateralValue()
     *
     * Example:
     * (1 EUR = 1.11 USD)
     * If an Euro market has received 100 USD to mint 90 EUR, its reported debt is 90 EUR or 100 USD, and its issuance is -100 USD.
     * Thus, its total balance is 100 USD of reported debt minus 100 USD of issuance, which is 0 USD.
     *
     * Additionally, the market's totalDebt might be affected by price fluctuations via reportedDebt, or fees.
     *
     */
    function totalDebt(Data storage self) internal view returns (int256) {
        return
            getReportedDebt(self).toInt() +
            self.netIssuanceD18 -
            getDepositedCollateralValue(self).toInt();
    }

    /**
     * @dev Returns the USD value for the total amount of collateral provided by the market itself.
     *
     * Note: This is not credit capacity provided by depositors through pools.
     */
    function getDepositedCollateralValue(Data storage self) internal view returns (uint256) {
        uint256 totalDepositedCollateralValueD18 = 0;

        // Sweep all DepositedCollateral entries and aggregate their USD value.
        for (uint256 i = 0; i < self.depositedCollateral.length; i++) {
            DepositedCollateral memory entry = self.depositedCollateral[i];
            CollateralConfiguration.Data storage collateralConfiguration = CollateralConfiguration
                .load(entry.collateralType);

            if (entry.amountD18 == 0) {
                continue;
            }

            uint256 priceD18 = CollateralConfiguration.getCollateralPrice(collateralConfiguration);

            totalDepositedCollateralValueD18 += priceD18.mulDecimal(entry.amountD18);
        }

        return totalDepositedCollateralValueD18;
    }

    /**
     * @dev Returns the amount of credit capacity that a certain pool provides to the market.

     * This credit capacity is obtained by reading the amount of shares that the pool has in the market's debt distribution, which represents the amount of USD denominated credit capacity that the pool has provided to the market.
     */
    function getPoolCreditCapacity(
        Data storage self,
        uint128 poolId
    ) internal view returns (uint256) {
        return self.poolsDebtDistribution.getActorShares(poolId.toBytes32());
    }

    /**
     * @dev Given an amount of shares that represent USD credit capacity from a pool, and a maximum value per share, returns the potential contribution to credit capacity that these shares could accrue, if their value per share was to hit the maximum.
     *
     * The resulting value is calculated multiplying the amount of creditCapacity provided by the pool by the delta between the maxValue per share vs current value.
     *
     * This function is used when the Pools are rebalanced to adjust each pool credit capacity based on a change in the amount of shares provided and/or a new maxValue per share
     *
     */
    function getCreditCapacityContribution(
        Data storage self,
        uint256 creditCapacitySharesD18,
        int256 maxShareValueD18
    ) internal view returns (int256 contributionD18) {
        // Determine how much the current value per share deviates from the maximum.
        uint256 deltaValuePerShareD18 = (maxShareValueD18 -
            self.poolsDebtDistribution.getValuePerShare()).toUint();

        return deltaValuePerShareD18.mulDecimal(creditCapacitySharesD18).toInt();
    }

    /**
     * @dev Returns true if the market's current capacity is below the amount of locked capacity.
     *
     */
    function isCapacityLocked(Data storage self) internal view returns (bool) {
        return self.creditCapacityD18 < getLockedCreditCapacity(self).toInt();
    }

    /**
     * @dev Gets any outstanding debt. Do not call this method except in tests
     *
     * Note: This function should only be used in tests!
     */
    // solhint-disable-next-line private-vars-leading-underscore, func-name-mixedcase
    function _testOnly_getOutstandingDebt(
        Data storage self,
        uint128 poolId
    ) internal returns (int256 debtChangeD18) {
        return
            self.pools[poolId].pendingDebtD18.toInt() +
            self.poolsDebtDistribution.accumulateActor(poolId.toBytes32());
    }

    /**
     * Returns the number of pools currently active in the market
     *
     * Note: this is test only
     */
    // solhint-disable-next-line private-vars-leading-underscore, func-name-mixedcase
    function _testOnly_inRangePools(Data storage self) internal view returns (uint256) {
        return self.inRangePools.size();
    }

    /**
     * Returns the number of pools currently active in the market
     *
     * Note: this is test only
     */
    // solhint-disable-next-line private-vars-leading-underscore, func-name-mixedcase
    function _testOnly_outRangePools(Data storage self) internal view returns (uint256) {
        return self.outRangePools.size();
    }

    /**
     * @dev Returns the debt value per share
     */
    function getDebtPerShare(Data storage self) internal view returns (int256 debtPerShareD18) {
        return self.poolsDebtDistribution.getValuePerShare();
    }

    /**
     * @dev Determine the amount of debt the pool would assume if its lastValue was updated
     * Needed for optimization.
     *
     * Called by a pool when it distributes its debt.
     *
     */
    function accumulateDebtChange(
        Data storage self,
        uint128 poolId
    ) internal returns (int256 debtChangeD18) {
        int256 changedValueD18 = self.poolsDebtDistribution.accumulateActor(poolId.toBytes32());
        debtChangeD18 = self.pools[poolId].pendingDebtD18.toInt() + changedValueD18;
        self.pools[poolId].pendingDebtD18 = 0;
    }

    /**
     * @dev Wrapper that adjusts a pool's shares in the market's credit capacity, making sure that the market's outstanding debt is first passed on to its connected pools.
     *
     * Called by a pool when it distributes its debt.
     *
     */
    function rebalancePools(
        uint128 marketId,
        uint128 poolId,
        int256 maxDebtShareValueD18, // (in USD)
        uint256 newCreditCapacityD18 // in collateralValue (USD)
    ) internal returns (int256 debtChangeD18) {
        Data storage self = load(marketId);

        if (self.marketAddress == address(0)) {
            revert MarketNotFound(marketId);
        }

        return adjustPoolShares(self, poolId, newCreditCapacityD18, maxDebtShareValueD18);
    }

    /**
     * @dev Called by pools when they modify the credit capacity provided to the market, as well as the maximum value per share they tolerate for the market.
     *
     * These two settings affect the market in the following ways:
     * - Updates the pool's shares in `poolsDebtDistribution`.
     * - Moves the pool in and out of inRangePools/outRangePools.
     * - Updates the market credit capacity property.
     */
    function adjustPoolShares(
        Data storage self,
        uint128 poolId,
        uint256 newCreditCapacityD18,
        int256 newPoolMaxShareValueD18
    ) internal returns (int256 debtChangeD18) {
        uint256 oldCreditCapacityD18 = getPoolCreditCapacity(self, poolId);
        int256 oldPoolMaxShareValueD18 = -self.inRangePools.getById(poolId).priority;

        // Sanity checks
        // require(oldPoolMaxShareValue == 0, "value is not 0");
        // require(newPoolMaxShareValue == 0, "new pool max share value is in fact set");

        self.pools[poolId].creditCapacityAmountD18 = newCreditCapacityD18.to128();

        int128 valuePerShareD18 = self.poolsDebtDistribution.getValuePerShare().to128();

        if (newCreditCapacityD18 == 0) {
            self.inRangePools.extractById(poolId);
            self.outRangePools.extractById(poolId);
        } else if (newPoolMaxShareValueD18 < valuePerShareD18) {
            // this will ensure calculations below can correctly gauge shares changes
            newCreditCapacityD18 = 0;
            self.inRangePools.extractById(poolId);
            self.outRangePools.insert(poolId, newPoolMaxShareValueD18.to128());
        } else {
            self.inRangePools.insert(poolId, -newPoolMaxShareValueD18.to128());
            self.outRangePools.extractById(poolId);
        }

        int256 changedValueD18 = self.poolsDebtDistribution.setActorShares(
            poolId.toBytes32(),
            newCreditCapacityD18
        );
        debtChangeD18 = self.pools[poolId].pendingDebtD18.toInt() + changedValueD18;
        self.pools[poolId].pendingDebtD18 = 0;

        // recalculate market capacity
        if (newPoolMaxShareValueD18 > valuePerShareD18) {
            self.creditCapacityD18 += getCreditCapacityContribution(
                self,
                newCreditCapacityD18,
                newPoolMaxShareValueD18
            ).to128();
        }

        if (oldPoolMaxShareValueD18 > valuePerShareD18) {
            self.creditCapacityD18 -= getCreditCapacityContribution(
                self,
                oldCreditCapacityD18,
                oldPoolMaxShareValueD18
            ).to128();
        }
    }

    /**
     * @dev Moves debt from the market into the pools that connect to it.
     *
     * This function should be called before any of the pools' shares are modified in `poolsDebtDistribution`.
     *
     * Note: The parameter `maxIter` is used as an escape hatch to discourage griefing.
     */
    function distributeDebtToPools(
        Data storage self,
        uint256 maxIter
    ) internal returns (bool fullyDistributed) {
        // Get the current and last distributed market balances.
        // Note: The last distributed balance will be cached within this function's execution.
        int256 targetBalanceD18 = totalDebt(self);
        int256 outstandingBalanceD18 = targetBalanceD18 - self.lastDistributedMarketBalanceD18;

        (, bool exhausted) = bumpPools(self, outstandingBalanceD18, maxIter);

        if (!exhausted && self.poolsDebtDistribution.totalSharesD18 > 0) {
            // cannot use `outstandingBalance` here because `self.lastDistributedMarketBalance`
            // may have changed after calling the bump functions above
            self.poolsDebtDistribution.distributeValue(
                targetBalanceD18 - self.lastDistributedMarketBalanceD18
            );
            self.lastDistributedMarketBalanceD18 = targetBalanceD18.to128();
        }

        return !exhausted;
    }

    /**
     * @dev Determine the target valuePerShare of the poolsDebtDistribution, given the value that is yet to be distributed.
     */
    function getTargetValuePerShare(
        Market.Data storage self,
        int256 valueToDistributeD18
    ) internal view returns (int256 targetValuePerShareD18) {
        return
            self.poolsDebtDistribution.getValuePerShare() +
            (
                self.poolsDebtDistribution.totalSharesD18 > 0
                    ? valueToDistributeD18.divDecimal(
                        self.poolsDebtDistribution.totalSharesD18.toInt()
                    ) // solhint-disable-next-line numcast/safe-cast
                    : int256(0)
            );
    }

    /**
     * @dev Finds pools for which this market's max value per share limit is hit, distributes their debt, and disconnects the market from them.
     *
     * The debt is distributed up to the limit of the max value per share that the pool tolerates on the market.
     */
    function bumpPools(
        Data storage self,
        int256 maxDistributedD18,
        uint256 maxIter
    ) internal returns (int256 actuallyDistributedD18, bool exhausted) {
        if (maxDistributedD18 == 0) {
            return (0, false);
        }

        // Determine the direction based on the amount to be distributed.
        int128 k;
        HeapUtil.Data storage fromHeap;
        HeapUtil.Data storage toHeap;
        if (maxDistributedD18 > 0) {
            k = 1;
            fromHeap = self.inRangePools;
            toHeap = self.outRangePools;
        } else {
            k = -1;
            fromHeap = self.outRangePools;
            toHeap = self.inRangePools;
        }

        // Note: This loop should rarely execute its main body. When it does, it only executes once for each pool that exceeds the limit since `distributeValue` is not run for most pools. Thus, market users are not hit with any overhead as a result of this.
        uint256 iters;
        for (iters = 0; iters < maxIter; iters++) {
            // Exit if there are no pools that can be moved
            if (fromHeap.size() == 0) {
                break;
            }

            // Identify the pool with the lowest maximum value per share.
            HeapUtil.Node memory edgePool = fromHeap.getMax();

            // 2 cases where we want to break out of this loop
            if (
                // If there is no pool in range, and we are going down
                (maxDistributedD18 - actuallyDistributedD18 > 0 &&
                    self.poolsDebtDistribution.totalSharesD18 == 0) ||
                // If there is a pool in ragne, and the lowest max value per share does not hit the limit, exit
                // Note: `-edgePool.priority` is actually the max value per share limit of the pool
                (self.poolsDebtDistribution.totalSharesD18 > 0 &&
                    -edgePool.priority >=
                    k * getTargetValuePerShare(self, (maxDistributedD18 - actuallyDistributedD18)))
            ) {
                break;
            }

            // The pool has hit its maximum value per share and needs to be removed.
            // Note: No need to update capacity because pool max share value = valuePerShare when this happens.
            togglePool(fromHeap, toHeap);

            // Distribute the market's debt to the limit, i.e. for that which exceeds the maximum value per share.
            if (self.poolsDebtDistribution.totalSharesD18 > 0) {
                int256 debtToLimitD18 = self
                    .poolsDebtDistribution
                    .totalSharesD18
                    .toInt()
                    .mulDecimal(
                        -k * edgePool.priority - self.poolsDebtDistribution.getValuePerShare() // Diff between current value and max value per share.
                    );
                self.poolsDebtDistribution.distributeValue(debtToLimitD18);

                // Update the global distributed and outstanding balances with the debt that was just distributed.
                actuallyDistributedD18 += debtToLimitD18;
            } else {
                self.poolsDebtDistribution.valuePerShareD27 = (-k * edgePool.priority)
                    .to256()
                    .upscale(DecimalMath.PRECISION_FACTOR)
                    .to128();
            }

            // Detach the market from this pool by removing the pool's shares from the market.
            // The pool will remain "detached" until the pool manager specifies a new poolsDebtDistribution.
            if (maxDistributedD18 > 0) {
                // the below requires are only for sanity
                require(
                    self.poolsDebtDistribution.getActorShares(edgePool.id.toBytes32()) > 0,
                    "no shares before actor removal"
                );

                uint256 newPoolDebtD18 = self
                    .poolsDebtDistribution
                    .setActorShares(edgePool.id.toBytes32(), 0)
                    .toUint();
                self.pools[edgePool.id].pendingDebtD18 += newPoolDebtD18.to128();
            } else {
                require(
                    self.poolsDebtDistribution.getActorShares(edgePool.id.toBytes32()) == 0,
                    "actor has shares before add"
                );

                self.poolsDebtDistribution.setActorShares(
                    edgePool.id.toBytes32(),
                    self.pools[edgePool.id].creditCapacityAmountD18
                );
            }
        }

        // Record the accumulated distributed balance.
        self.lastDistributedMarketBalanceD18 += actuallyDistributedD18.to128();

        exhausted = iters == maxIter;
    }

    /**
     * @dev Moves a pool from one heap into another.
     */
    function togglePool(HeapUtil.Data storage from, HeapUtil.Data storage to) internal {
        HeapUtil.Node memory node = from.extractMax();
        to.insert(node.id, -node.priority);
    }
}

File 40 of 50 : MarketConfiguration.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Tracks a market's weight within a Pool, and its maximum debt.
 *
 * Each pool has an array of these, with one entry per market managed by the pool.
 *
 * A market's weight determines how much liquidity the pool provides to the market, and how much debt exposure the market gives the pool.
 *
 * Weights are used to calculate percentages by adding all the weights in the pool and dividing the market's weight by the total weights.
 *
 * A market's maximum debt in a pool is indicated with a maximum debt value per share.
 */
library MarketConfiguration {
    struct Data {
        /**
         * @dev Numeric identifier for the market.
         *
         * Must be unique, and in a list of `MarketConfiguration[]`, must be increasing.
         */
        uint128 marketId;
        /**
         * @dev The ratio of each market's `weight` to the pool's `totalWeights` determines the pro-rata share of the market to the pool's total liquidity.
         */
        uint128 weightD18;
        /**
         * @dev Maximum value per share that a pool will tolerate for this market.
         *
         * If the the limit is met, the markets exceeding debt will be distributed, and it will be disconnected from the pool that no longer provides credit to it.
         *
         * Note: This value will have no effect if the system wide limit is hit first. See `PoolConfiguration.minLiquidityRatioD18`.
         */
        int128 maxDebtShareValueD18;
    }
}

File 41 of 50 : MarketPoolInfo.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Stores information regarding a pool's relationship to a market, such that it can be added or removed from a distribution
 */
library MarketPoolInfo {
    struct Data {
        /**
         * @dev The credit capacity that this pool is providing to the relevant market. Needed to re-add the pool to the distribution when going back in range.
         */
        uint128 creditCapacityAmountD18;
        /**
         * @dev The amount of debt the pool has which hasn't been passed down the debt distribution chain yet.
         */
        uint128 pendingDebtD18;
    }
}

File 42 of 50 : OracleManager.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Represents Oracle Manager
 */
library OracleManager {
    bytes32 private constant _SLOT_ORACLE_MANAGER =
        keccak256(abi.encode("io.synthetix.synthetix.OracleManager"));

    struct Data {
        /**
         * @dev The oracle manager address.
         */
        address oracleManagerAddress;
    }

    /**
     * @dev Loads the singleton storage info about the oracle manager.
     */
    function load() internal pure returns (Data storage oracleManager) {
        bytes32 s = _SLOT_ORACLE_MANAGER;
        assembly {
            oracleManager.slot := s
        }
    }
}

File 43 of 50 : Pool.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "./Config.sol";
import "./Distribution.sol";
import "./MarketConfiguration.sol";
import "./Vault.sol";
import "./Market.sol";
import "./PoolCollateralConfiguration.sol";
import "./SystemPoolConfiguration.sol";
import "./PoolCollateralConfiguration.sol";

import "@synthetixio/core-contracts/contracts/errors/AccessError.sol";
import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

/**
 * @title Aggregates collateral from multiple users in order to provide liquidity to a configurable set of markets.
 *
 * The set of markets is configured as an array of MarketConfiguration objects, where the weight of the market can be specified. This weight, and the aggregated total weight of all the configured markets, determines how much collateral from the pool each market has, as well as in what proportion the market passes on debt to the pool and thus to all its users.
 *
 * The pool tracks the collateral provided by users using an array of Vaults objects, for which there will be one per collateral type. Each vault tracks how much collateral each user has delegated to this pool, how much debt the user has because of minting USD, as well as how much corresponding debt the pool has passed on to the user.
 */
library Pool {
    using CollateralConfiguration for CollateralConfiguration.Data;
    using Market for Market.Data;
    using Vault for Vault.Data;
    using VaultEpoch for VaultEpoch.Data;
    using Distribution for Distribution.Data;
    using DecimalMath for uint256;
    using DecimalMath for int256;
    using DecimalMath for int128;
    using SafeCastAddress for address;
    using SafeCastU128 for uint128;
    using SafeCastU256 for uint256;
    using SafeCastI128 for int128;
    using SafeCastI256 for int256;

    /**
     * @dev Thrown when the specified pool is not found.
     */
    error PoolNotFound(uint128 poolId);

    /**
     * @dev Thrown when attempting to create a pool that already exists.
     */
    error PoolAlreadyExists(uint128 poolId);

    /**
     * @dev Thrown when min delegation time for a market connected to the pool has not elapsed
     */
    error MinDelegationTimeoutPending(uint128 poolId, uint32 timeRemaining);

    /**
     * @dev Thrown when pool has surpassed max collateral deposit
     */
    error PoolCollateralLimitExceeded(
        uint128 poolId,
        address collateralType,
        uint256 currentCollateral,
        uint256 maxCollateral
    );

    bytes32 private constant _CONFIG_SET_MARKET_MIN_DELEGATE_MAX = "setMarketMinDelegateTime_max";

    struct Data {
        /**
         * @dev Numeric identifier for the pool. Must be unique.
         * @dev A pool with id zero exists! (See Pool.loadExisting()). Users can delegate to this pool to be able to mint USD without being exposed to fluctuating debt.
         */
        uint128 id;
        /**
         * @dev Text identifier for the pool.
         *
         * Not required to be unique.
         */
        string name;
        /**
         * @dev Creator of the pool, which has configuration access rights for the pool.
         *
         * See onlyPoolOwner.
         */
        address owner;
        /**
         * @dev Allows the current pool owner to nominate a new owner, and thus transfer pool configuration credentials.
         */
        address nominatedOwner;
        /**
         * @dev Sum of all market weights.
         *
         * Market weights are tracked in `MarketConfiguration.weight`, one for each market. The ratio of each market's `weight` to the pool's `totalWeights` determines the pro-rata share of the market to the pool's total liquidity.
         *
         * Reciprocally, this pro-rata share also determines how much the pool is exposed to each market's debt.
         */
        uint128 totalWeightsD18;
        /**
         * @dev Accumulated cache value of all vault collateral debts
         */
        int128 totalVaultDebtsD18;
        /**
         * @dev Array of markets connected to this pool, and their configurations. I.e. weight, etc.
         *
         * See totalWeights.
         */
        MarketConfiguration.Data[] marketConfigurations;
        /**
         * @dev A pool's debt distribution connects pools to the debt distribution chain, i.e. vaults and markets. Vaults are actors in the pool's debt distribution, where the amount of shares they possess depends on the amount of collateral each vault delegates to the pool.
         *
         * The debt distribution chain will move debt from markets into this pools, and then from pools to vaults.
         *
         * Actors: Vaults.
         * Shares: USD value, proportional to the amount of collateral that the vault delegates to the pool.
         * Value per share: Debt per dollar of collateral. Depends on aggregated debt of connected markets.
         *
         */
        Distribution.Data vaultsDebtDistribution;
        /**
         * @dev Reference to all the vaults that provide liquidity to this pool.
         *
         * Each collateral type will have its own vault, specific to this pool. I.e. if two pools both use SNX collateral, each will have its own SNX vault.
         *
         * Vaults track user collateral and debt using a debt distribution, which is connected to the debt distribution chain.
         */
        mapping(address => Vault.Data) vaults;
        uint64 lastConfigurationTime;
        uint64 __reserved1;
        uint64 __reserved2;
        uint64 __reserved3;
        mapping(address => PoolCollateralConfiguration.Data) collateralConfigurations;
        /**
         * @dev A switch to make the pool opt-in for new collateral
         *
         * By default it's set to false, which means any new collateral accepeted by the system will be accpeted by the pool.
         *
         * If the pool owner sets this value to true, then new collaterals will be disabled for the pool unless a maxDeposit is set for a that collateral.
         */
        bool collateralDisabledByDefault;
    }

    /**
     * @dev Returns the pool stored at the specified pool id.
     */
    function load(uint128 id) internal pure returns (Data storage pool) {
        bytes32 s = keccak256(abi.encode("io.synthetix.synthetix.Pool", id));
        assembly {
            pool.slot := s
        }
    }

    /**
     * @dev Creates a pool for the given pool id, and assigns the caller as its owner.
     *
     * Reverts if the specified pool already exists.
     */
    function create(uint128 id, address owner) internal returns (Pool.Data storage pool) {
        if (id == 0 || load(id).id == id) {
            revert PoolAlreadyExists(id);
        }

        pool = load(id);

        pool.id = id;
        pool.owner = owner;
    }

    /**
     * @dev Ticker function that updates the debt distribution chain downwards, from markets into the pool, according to each market's weight.
     * IMPORTANT: debt must be distributed downstream before invoking this function.
     *
     * It updates the chain by performing these actions:
     * - Splits the pool's total liquidity of the pool into each market, pro-rata. The amount of shares that the pool has on each market depends on how much liquidity the pool provides to the market.
     * - Accumulates the change in debt value from each market into the pools own vault debt distribution's value per share.
     */
    function rebalanceMarketsInPool(Data storage self) internal {
        uint256 totalWeightsD18 = self.totalWeightsD18;

        if (totalWeightsD18 == 0) {
            return; // Nothing to rebalance.
        }

        // Read from storage once, before entering the loop below.
        // These values should not change while iterating through each market.
        uint256 totalCreditCapacityD18 = self.vaultsDebtDistribution.totalSharesD18;
        int128 debtPerShareD18 = totalCreditCapacityD18 > 0 // solhint-disable-next-line numcast/safe-cast
            ? int(self.totalVaultDebtsD18).divDecimal(totalCreditCapacityD18.toInt()).to128() // solhint-disable-next-line numcast/safe-cast
            : int128(0);

        uint256 systemMinLiquidityRatioD18 = SystemPoolConfiguration.load().minLiquidityRatioD18;

        // Loop through the pool's markets, applying market weights, and tracking how this changes the amount of debt that this pool is responsible for.
        // This debt extracted from markets is then applied to the pool's vault debt distribution, which thus exposes debt to the pool's vaults.
        for (uint256 i = 0; i < self.marketConfigurations.length; i++) {
            MarketConfiguration.Data storage marketConfiguration = self.marketConfigurations[i];

            uint256 weightD18 = marketConfiguration.weightD18;

            // Calculate each market's pro-rata USD liquidity.
            // Note: the factor `(weight / totalWeights)` is not deduped in the operations below to maintain numeric precision.

            uint256 marketCreditCapacityD18 = (totalCreditCapacityD18 * weightD18) /
                totalWeightsD18;

            Market.Data storage marketData = Market.load(marketConfiguration.marketId);

            // Use market-specific minimum liquidity ratio if set, otherwise use system default.
            uint256 minLiquidityRatioD18 = marketData.minLiquidityRatioD18 > 0
                ? marketData.minLiquidityRatioD18
                : systemMinLiquidityRatioD18;

            // Contain the pool imposed market's maximum debt share value.
            // Imposed by system.
            int256 effectiveMaxShareValueD18 = getSystemMaxValuePerShare(
                marketData.id,
                minLiquidityRatioD18,
                debtPerShareD18
            );
            // Imposed by pool.
            int256 configuredMaxShareValueD18 = marketConfiguration.maxDebtShareValueD18;
            effectiveMaxShareValueD18 = effectiveMaxShareValueD18 < configuredMaxShareValueD18
                ? effectiveMaxShareValueD18
                : configuredMaxShareValueD18;

            // Update each market's corresponding credit capacity.
            // The returned value represents how much the market's debt changed after changing the shares of this pool actor, which is aggregated to later be passed on the pools debt distribution.
            Market.rebalancePools(
                marketConfiguration.marketId,
                self.id,
                effectiveMaxShareValueD18,
                marketCreditCapacityD18
            );
        }
    }

    /**
     * @dev Determines the resulting maximum value per share for a market, according to a system-wide minimum liquidity ratio. This prevents markets from assigning more debt to pools than they have collateral to cover.
     *
     * Note: There is a market-wide fail safe for each market at `MarketConfiguration.maxDebtShareValue`. The lower of the two values should be used.
     *
     * See `SystemPoolConfiguration.minLiquidityRatio`.
     */
    function getSystemMaxValuePerShare(
        uint128 marketId,
        uint256 minLiquidityRatioD18,
        int256 debtPerShareD18
    ) internal view returns (int256) {
        // Retrieve the current value per share of the market.
        Market.Data storage marketData = Market.load(marketId);
        int256 valuePerShareD18 = marketData.poolsDebtDistribution.getValuePerShare();

        // Calculate the margin of debt that the market would incur if it hit the system wide limit.
        uint256 marginD18 = minLiquidityRatioD18 == 0
            ? DecimalMath.UNIT
            : DecimalMath.UNIT.divDecimal(minLiquidityRatioD18);

        // The resulting maximum value per share is the distribution's value per share,
        // plus the margin to hit the limit, minus the current debt per share.
        return valuePerShareD18 + marginD18.toInt() - debtPerShareD18;
    }

    /**
     * @dev Reverts if the pool does not exist with appropriate error. Otherwise, returns the pool.
     */
    function loadExisting(uint128 id) internal view returns (Data storage) {
        Data storage p = load(id);
        if (id != 0 && p.id != id) {
            revert PoolNotFound(id);
        }

        return p;
    }

    /**
     * @dev Returns true if the pool is exposed to the specified market.
     */
    function hasMarket(Data storage self, uint128 marketId) internal view returns (bool) {
        for (uint256 i = 0; i < self.marketConfigurations.length; i++) {
            if (self.marketConfigurations[i].marketId == marketId) {
                return true;
            }
        }

        return false;
    }

    /**
     * IMPORTANT: after this function, you should accumulateVaultDebt
     */
    function distributeDebtToVaults(
        Data storage self,
        address optionalCollateralType
    ) internal returns (int256 cumulativeDebtChange) {
        // Update each market's pro-rata liquidity and collect accumulated debt into the pool's debt distribution.
        uint128 myPoolId = self.id;
        for (uint256 i = 0; i < self.marketConfigurations.length; i++) {
            Market.Data storage market = Market.load(self.marketConfigurations[i].marketId);

            market.distributeDebtToPools(9999999999);
            cumulativeDebtChange += market.accumulateDebtChange(myPoolId);
        }

        assignDebt(self, cumulativeDebtChange);

        // Transfer the debt change from the pool into the vault.
        if (optionalCollateralType != address(0)) {
            bytes32 actorId = optionalCollateralType.toBytes32();
            self.vaults[optionalCollateralType].distributeDebtToAccounts(
                self.vaultsDebtDistribution.accumulateActor(actorId)
            );
        }
    }

    function assignDebt(Data storage self, int256 debtAmountD18) internal {
        // Accumulate the change in total liquidity, from the vault, into the pool.
        self.totalVaultDebtsD18 = self.totalVaultDebtsD18 + debtAmountD18.to128();

        self.vaultsDebtDistribution.distributeValue(debtAmountD18);
    }

    function assignDebtToAccount(
        Data storage self,
        address collateralType,
        uint128 accountId,
        int256 debtAmountD18
    ) internal {
        self.totalVaultDebtsD18 = self.totalVaultDebtsD18 + debtAmountD18.to128();

        self.vaults[collateralType].currentEpoch().assignDebtToAccount(accountId, debtAmountD18);
    }

    /**
     * @dev Ticker function that updates the debt distribution chain for a specific collateral type downwards, from the pool into the corresponding the vault, according to changes in the collateral's price.
     * IMPORTANT: *should* call distributeDebtToVaults() to ensure that deltaDebtD18 is referencing the latest
     *
     * It updates the chain by performing these actions:
     * - Collects the latest price of the corresponding collateral and updates the vault's liquidity.
     * - Updates the vaults shares in the pool's debt distribution, according to the collateral provided by the vault.
     * - Updates the value per share of the vault's debt distribution.
     */
    function recalculateVaultCollateral(
        Data storage self,
        address collateralType
    ) internal returns (uint256 collateralPriceD18) {
        // Get the latest collateral price.
        collateralPriceD18 = CollateralConfiguration.load(collateralType).getCollateralPrice();

        // Changes in price update the corresponding vault's total collateral value as well as its liquidity (collateral - debt).
        (uint256 usdWeightD18, ) = self.vaults[collateralType].updateCreditCapacity(
            collateralPriceD18
        );

        // Update the vault's shares in the pool's debt distribution, according to the value of its collateral.
        self.vaultsDebtDistribution.setActorShares(collateralType.toBytes32(), usdWeightD18);

        // now that available vault collateral has been recalculated, we should also rebalance the pool markets
        rebalanceMarketsInPool(self);
    }

    /**
     * @dev Updates the debt distribution chain for this pool, and consolidates the given account's debt.
     */
    function updateAccountDebt(
        Data storage self,
        address collateralType,
        uint128 accountId
    ) internal returns (int256 debtD18) {
        distributeDebtToVaults(self, collateralType);
        return self.vaults[collateralType].consolidateAccountDebt(accountId);
    }

    /**
     * @dev Clears all vault data for the specified collateral type.
     */
    function resetVault(Data storage self, address collateralType) internal {
        // Creates a new epoch in the vault, effectively zeroing out all values.
        self.vaults[collateralType].reset();

        // Ensure that the vault's values update the debt distribution chain.
        recalculateVaultCollateral(self, collateralType);
    }

    /**
     * @dev Calculates the collateralization ratio of the vault that tracks the given collateral type.
     *
     * The c-ratio is the vault's share of the total debt of the pool, divided by the collateral it delegates to the pool.
     *
     * Note: This is not a view function. It updates the debt distribution chain before performing any calculations.
     */
    function currentVaultCollateralRatio(
        Data storage self,
        address collateralType
    ) internal returns (uint256) {
        int256 vaultDebtD18 = currentVaultDebt(self, collateralType);
        (, uint256 collateralValueD18) = currentVaultCollateral(self, collateralType);

        return vaultDebtD18 > 0 ? collateralValueD18.divDecimal(vaultDebtD18.toUint()) : 0;
    }

    /**
     * @dev Finds a connected market whose credit capacity has reached its locked limit.
     *
     * Note: Returns market zero (null market) if none is found.
     */
    function findMarketWithCapacityLocked(
        Data storage self
    ) internal view returns (Market.Data storage lockedMarket) {
        for (uint256 i = 0; i < self.marketConfigurations.length; i++) {
            Market.Data storage market = Market.load(self.marketConfigurations[i].marketId);

            if (market.isCapacityLocked()) {
                return market;
            }
        }

        // Market zero = null market.
        return Market.load(0);
    }

    function getRequiredMinDelegationTime(
        Data storage self
    ) internal view returns (uint32 requiredMinDelegateTime) {
        for (uint256 i = 0; i < self.marketConfigurations.length; i++) {
            uint32 marketMinDelegateTime = Market
                .load(self.marketConfigurations[i].marketId)
                .minDelegateTime;

            if (marketMinDelegateTime > requiredMinDelegateTime) {
                requiredMinDelegateTime = marketMinDelegateTime;
            }
        }

        // solhint-disable-next-line numcast/safe-cast
        uint32 maxMinDelegateTime = uint32(
            Config.readUint(_CONFIG_SET_MARKET_MIN_DELEGATE_MAX, 86400 * 30)
        );
        return
            maxMinDelegateTime < requiredMinDelegateTime
                ? maxMinDelegateTime
                : requiredMinDelegateTime;
    }

    /**
     * @dev Returns the debt of the vault that tracks the given collateral type.
     *
     * The vault's debt is the vault's share of the total debt of the pool, or its share of the total debt of the markets connected to the pool. The size of this share depends on how much collateral the pool provides to the pool.
     *
     * Note: This is not a view function. It updates the debt distribution chain before performing any calculations.
     */
    function currentVaultDebt(Data storage self, address collateralType) internal returns (int256) {
        // TODO: assert that all debts have been paid, otherwise vault cant be reset (its so critical here)
        distributeDebtToVaults(self, collateralType);
        rebalanceMarketsInPool(self);
        return self.vaults[collateralType].currentDebt();
    }

    /**
     * @dev Returns the total amount and value of the specified collateral delegated to this pool.
     */
    function currentVaultCollateral(
        Data storage self,
        address collateralType
    ) internal view returns (uint256 collateralAmountD18, uint256 collateralValueD18) {
        uint256 collateralPriceD18 = CollateralConfiguration
            .load(collateralType)
            .getCollateralPrice();

        collateralAmountD18 = self.vaults[collateralType].currentCollateral();
        collateralValueD18 = collateralPriceD18.mulDecimal(collateralAmountD18);
    }

    /**
     * @dev Returns the amount and value of collateral that the specified account has delegated to this pool.
     */
    function currentAccountCollateral(
        Data storage self,
        address collateralType,
        uint128 accountId
    ) internal view returns (uint256 collateralAmountD18, uint256 collateralValueD18) {
        uint256 collateralPriceD18 = CollateralConfiguration
            .load(collateralType)
            .getCollateralPrice();

        collateralAmountD18 = self.vaults[collateralType].currentAccountCollateral(accountId);
        collateralValueD18 = collateralPriceD18.mulDecimal(collateralAmountD18);
    }

    /**
     * @dev Returns the specified account's collateralization ratio (collateral / debt).
     * @dev If the account's debt is negative or zero, returns an "infinite" c-ratio.
     */
    function currentAccountCollateralRatio(
        Data storage self,
        address collateralType,
        uint128 accountId
    ) internal returns (uint256) {
        int256 positionDebtD18 = updateAccountDebt(self, collateralType, accountId);
        rebalanceMarketsInPool(self);
        if (positionDebtD18 <= 0) {
            return type(uint256).max;
        }

        (, uint256 positionCollateralValueD18) = currentAccountCollateral(
            self,
            collateralType,
            accountId
        );

        return positionCollateralValueD18.divDecimal(positionDebtD18.toUint());
    }

    /**
     * @dev Reverts if the caller is not the owner of the specified pool.
     */
    function onlyPoolOwner(uint128 poolId, address caller) internal view {
        if (Pool.load(poolId).owner != caller) {
            revert AccessError.Unauthorized(caller);
        }
    }

    function requireMinDelegationTimeElapsed(
        Data storage self,
        uint64 lastDelegationTime
    ) internal view {
        uint32 requiredMinDelegationTime = getRequiredMinDelegationTime(self);
        if (block.timestamp < lastDelegationTime + requiredMinDelegationTime) {
            revert MinDelegationTimeoutPending(
                self.id,
                // solhint-disable-next-line numcast/safe-cast
                uint32(lastDelegationTime + requiredMinDelegationTime - block.timestamp)
            );
        }
    }

    function checkPoolCollateralLimit(
        Data storage self,
        address collateralType,
        uint256 collateralAmountD18
    ) internal view {
        uint256 collateralLimitD18 = self
            .collateralConfigurations[collateralType]
            .collateralLimitD18;
        uint256 currentCollateral = self.vaults[collateralType].currentCollateral();

        if (
            (self.collateralDisabledByDefault && collateralLimitD18 == 0) ||
            (collateralLimitD18 > 0 && currentCollateral + collateralAmountD18 > collateralLimitD18)
        ) {
            revert PoolCollateralLimitExceeded(
                self.id,
                collateralType,
                currentCollateral + collateralAmountD18,
                collateralLimitD18
            );
        }
    }
}

File 44 of 50 : PoolCollateralConfiguration.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

library PoolCollateralConfiguration {
    bytes32 private constant _SLOT =
        keccak256(abi.encode("io.synthetix.synthetix.PoolCollateralConfiguration"));

    struct Data {
        uint256 collateralLimitD18;
        uint256 issuanceRatioD18;
    }
}

File 45 of 50 : RewardDistribution.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/DecimalMath.sol";
import "@synthetixio/core-contracts/contracts/errors/ParameterError.sol";
import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

import "../interfaces/external/IRewardDistributor.sol";

import "./Distribution.sol";
import "./RewardDistributionClaimStatus.sol";

/**
 * @title Used by vaults to track rewards for its participants. There will be one of these for each pool, collateral type, and distributor combination.
 */
library RewardDistribution {
    using DecimalMath for int256;
    using SafeCastU128 for uint128;
    using SafeCastU256 for uint256;
    using SafeCastI128 for int128;
    using SafeCastI256 for int256;
    using SafeCastU64 for uint64;
    using SafeCastU32 for uint32;
    using SafeCastI32 for int32;

    struct Data {
        /**
         * @dev The 3rd party smart contract which holds/mints tokens for distributing rewards to vault participants.
         */
        IRewardDistributor distributor;
        /**
         * @dev Available slot.
         */
        uint128 __slotAvailableForFutureUse;
        /**
         * @dev The value of the rewards in this entry.
         */
        uint128 rewardPerShareD18;
        /**
         * @dev The status for each actor, regarding this distribution's entry.
         */
        mapping(uint256 => RewardDistributionClaimStatus.Data) claimStatus;
        /**
         * @dev Value to be distributed as rewards in a scheduled form.
         */
        int128 scheduledValueD18;
        /**
         * @dev Date at which the entry's rewards will begin to be claimable.
         *
         * Note: Set to <= block.timestamp to distribute immediately to currently participating users.
         */
        uint64 start;
        /**
         * @dev Time span after the start date, in which the whole of the entry's rewards will become claimable.
         */
        uint32 duration;
        /**
         * @dev Date on which this distribution entry was last updated.
         */
        uint32 lastUpdate;
    }

    /**
     * @dev Distributes rewards into a new rewards distribution entry.
     *
     * Note: this function allows for more special cases such as distributing at a future date or distributing over time.
     * If you want to apply the distribution to the pool, call `distribute` with the return value. Otherwise, you can
     * record this independently as well.
     */
    function distribute(
        Data storage self,
        Distribution.Data storage dist,
        int256 amountD18,
        uint64 start,
        uint32 duration
    ) internal returns (int256 diffD18) {
        uint256 totalSharesD18 = dist.totalSharesD18;

        if (totalSharesD18 == 0) {
            revert ParameterError.InvalidParameter(
                "amount",
                "can't distribute to empty distribution"
            );
        }

        uint256 curTime = block.timestamp;

        // Unlocks the entry's distributed amount into its value per share.
        diffD18 += updateEntry(self, totalSharesD18);

        // If the current time is past the end of the entry's duration,
        // update any rewards which may have accrued since last run.
        // (instant distribution--immediately disperse amount).
        if (start + duration <= curTime) {
            diffD18 += amountD18.divDecimal(totalSharesD18.toInt());

            self.lastUpdate = 0;
            self.start = 0;
            self.duration = 0;
            self.scheduledValueD18 = 0;
            // Else, schedule the amount to distribute.
        } else {
            self.scheduledValueD18 = amountD18.to128();

            self.start = start;
            self.duration = duration;

            // The amount is actually the amount distributed already *plus* whatever has been specified now.
            self.lastUpdate = 0;

            diffD18 += updateEntry(self, totalSharesD18);
        }
    }

    /**
     * @dev Updates the total shares of a reward distribution entry, and releases its unlocked value into its value per share, depending on the time elapsed since the start of the distribution's entry.
     *
     * Note: call every time before `totalShares` changes.
     */
    function updateEntry(
        Data storage self,
        uint256 totalSharesAmountD18
    ) internal returns (int256) {
        // Cannot process distributed rewards if a pool is empty or if it has no rewards.
        if (self.scheduledValueD18 == 0 || totalSharesAmountD18 == 0) {
            return 0;
        }

        uint256 curTime = block.timestamp;

        int256 valuePerShareChangeD18 = 0;

        // Cannot update an entry whose start date has not being reached.
        if (curTime < self.start) {
            return 0;
        }

        // If the entry's duration is zero and the its last update is zero,
        // consider the entry to be an instant distribution.
        if (self.duration == 0 && self.lastUpdate < self.start) {
            // Simply update the value per share to the total value divided by the total shares.
            valuePerShareChangeD18 = self.scheduledValueD18.to256().divDecimal(
                totalSharesAmountD18.toInt()
            );
            // Else, if the last update was before the end of the duration.
        } else if (self.lastUpdate < self.start + self.duration) {
            // Determine how much was previously distributed.
            // If the last update is zero, then nothing was distributed,
            // otherwise the amount is proportional to the time elapsed since the start.
            int256 lastUpdateDistributedD18 = self.lastUpdate < self.start
                ? SafeCastI128.zero()
                : (self.scheduledValueD18 * (self.lastUpdate - self.start).toInt()) /
                    self.duration.toInt();

            // If the current time is beyond the duration, then consider all scheduled value to be distributed.
            // Else, the amount distributed is proportional to the elapsed time.
            int256 curUpdateDistributedD18 = self.scheduledValueD18;
            if (curTime < self.start + self.duration) {
                // Note: Not using an intermediate time ratio variable
                // in the following calculation to maintain precision.
                curUpdateDistributedD18 =
                    (curUpdateDistributedD18 * (curTime - self.start).toInt()) /
                    self.duration.toInt();
            }

            // The final value per share change is the difference between what is to be distributed and what was distributed.
            valuePerShareChangeD18 = (curUpdateDistributedD18 - lastUpdateDistributedD18)
                .divDecimal(totalSharesAmountD18.toInt());
        }

        self.lastUpdate = curTime.to32();

        return valuePerShareChangeD18;
    }
}

File 46 of 50 : RewardDistributionClaimStatus.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

/**
 * @title Tracks information per actor within a RewardDistribution.
 */
library RewardDistributionClaimStatus {
    struct Data {
        /**
         * @dev The last known reward per share for this actor.
         */
        uint128 lastRewardPerShareD18;
        /**
         * @dev The amount of rewards pending to be claimed by this actor.
         */
        uint128 pendingSendD18;
    }
}

File 47 of 50 : ScalableMapping.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/DecimalMath.sol";
import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

/**
 * @title Data structure that wraps a mapping with a scalar multiplier.
 *
 * If you wanted to modify all the values in a mapping by the same amount, you would normally have to loop through each entry in the mapping. This object allows you to modify all of them at once, by simply modifying the scalar multiplier.
 *
 * I.e. a regular mapping represents values like this:
 * value = mapping[id]
 *
 * And a scalable mapping represents values like this:
 * value = mapping[id] * scalar
 *
 * This reduces the number of computations needed for modifying the balances of N users from O(n) to O(1).

 * Note: Notice how users are tracked by a generic bytes32 id instead of an address. This allows the actors of the mapping not just to be addresses. They can be anything, for example a pool id, an account id, etc.
 *
 * *********************
 * Conceptual Examples
 * *********************
 *
 * 1) Socialization of collateral during a liquidation.
 *
 * Scalable mappings are very useful for "socialization" of collateral, that is, the re-distribution of collateral when an account is liquidated. Suppose 1000 ETH are liquidated, and would need to be distributed amongst 1000 depositors. With a regular mapping, every depositor's balance would have to be modified in a loop that iterates through every single one of them. With a scalable mapping, the scalar would simply need to be incremented so that the total value of the mapping increases by 1000 ETH.
 *
 * 2) Socialization of debt during a liquidation.
 *
 * Similar to the socialization of collateral during a liquidation, the debt of the position that is being liquidated can be re-allocated using a scalable mapping with a single action. Supposing a scalable mapping tracks each user's debt in the system, and that 1000 sUSD has to be distributed amongst 1000 depositors, the debt data structure's scalar would simply need to be incremented so that the total value or debt of the distribution increments by 1000 sUSD.
 *
 */
library ScalableMapping {
    using SafeCastU128 for uint128;
    using SafeCastU256 for uint256;
    using SafeCastI128 for int128;
    using SafeCastI256 for int256;
    using DecimalMath for int256;
    using DecimalMath for uint256;

    /**
     * @dev Thrown when attempting to scale a mapping with an amount that is lower than its resolution.
     */
    error InsufficientMappedAmount();

    /**
     * @dev Thrown when attempting to scale a mapping with no shares.
     */
    error CannotScaleEmptyMapping();

    struct Data {
        uint128 totalSharesD18;
        int128 scaleModifierD27;
        mapping(bytes32 => uint256) sharesD18;
    }

    /**
     * @dev Inflates or deflates the total value of the distribution by the given value.
     * @dev The incoming value is split per share, and used as a delta that is *added* to the existing scale modifier. The resulting scale modifier must be in the range [-1, type(int128).max).
     */
    function scale(Data storage self, int256 valueD18) internal {
        if (valueD18 == 0) {
            return;
        }

        uint256 totalSharesD18 = self.totalSharesD18;
        if (totalSharesD18 == 0) {
            revert CannotScaleEmptyMapping();
        }

        int256 valueD45 = valueD18 * DecimalMath.UNIT_PRECISE_INT;
        int256 deltaScaleModifierD27 = valueD45 / totalSharesD18.toInt();

        self.scaleModifierD27 += deltaScaleModifierD27.to128();

        if (self.scaleModifierD27 < -DecimalMath.UNIT_PRECISE_INT) {
            revert InsufficientMappedAmount();
        }
    }

    /**
     * @dev Updates an actor's individual value in the distribution to the specified amount.
     *
     * The change in value is manifested in the distribution by changing the actor's number of shares in it, and thus the distribution's total number of shares.
     *
     * Returns the resulting amount of shares that the actor has after this change in value.
     */
    function set(
        Data storage self,
        bytes32 actorId,
        uint256 newActorValueD18
    ) internal returns (uint256 resultingSharesD18) {
        // Represent the actor's change in value by changing the actor's number of shares,
        // and keeping the distribution's scaleModifier constant.

        resultingSharesD18 = getSharesForAmount(self, newActorValueD18);

        // Modify the total shares with the actor's change in shares.
        self.totalSharesD18 = (self.totalSharesD18 + resultingSharesD18 - self.sharesD18[actorId])
            .to128();

        self.sharesD18[actorId] = resultingSharesD18.to128();
    }

    /**
     * @dev Returns the value owned by the actor in the distribution.
     *
     * i.e. actor.shares * scaleModifier
     */
    function get(Data storage self, bytes32 actorId) internal view returns (uint256 valueD18) {
        uint256 totalSharesD18 = self.totalSharesD18;
        if (totalSharesD18 == 0) {
            return 0;
        }

        return (self.sharesD18[actorId] * totalAmount(self)) / totalSharesD18;
    }

    /**
     * @dev Returns the total value held in the distribution.
     *
     * i.e. totalShares * scaleModifier
     */
    function totalAmount(Data storage self) internal view returns (uint256 valueD18) {
        return
            ((self.scaleModifierD27 + DecimalMath.UNIT_PRECISE_INT).toUint() *
                self.totalSharesD18) / DecimalMath.UNIT_PRECISE;
    }

    function getSharesForAmount(
        Data storage self,
        uint256 amountD18
    ) internal view returns (uint256 sharesD18) {
        sharesD18 =
            (amountD18 * DecimalMath.UNIT_PRECISE) /
            (self.scaleModifierD27 + DecimalMath.UNIT_PRECISE_INT128).toUint();
    }
}

File 48 of 50 : SystemPoolConfiguration.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "@synthetixio/core-contracts/contracts/utils/SetUtil.sol";

/**
 * @title System wide configuration for pools.
 */
library SystemPoolConfiguration {
    bytes32 private constant _SLOT_SYSTEM_POOL_CONFIGURATION =
        keccak256(abi.encode("io.synthetix.synthetix.SystemPoolConfiguration"));

    struct Data {
        /**
         * @dev Owner specified system-wide limiting factor that prevents markets from minting too much debt, similar to the issuance ratio to a collateral type.
         *
         * Note: If zero, then this value defaults to 100%.
         */
        uint256 minLiquidityRatioD18;
        uint128 __reservedForFutureUse;
        /**
         * @dev Id of the main pool set by the system owner.
         */
        uint128 preferredPool;
        /**
         * @dev List of pools approved by the system owner.
         */
        SetUtil.UintSet approvedPools;
    }

    /**
     * @dev Returns the configuration singleton.
     */
    function load() internal pure returns (Data storage systemPoolConfiguration) {
        bytes32 s = _SLOT_SYSTEM_POOL_CONFIGURATION;
        assembly {
            systemPoolConfiguration.slot := s
        }
    }
}

File 49 of 50 : Vault.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "./VaultEpoch.sol";
import "./RewardDistribution.sol";

import "@synthetixio/core-contracts/contracts/utils/SetUtil.sol";
import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

/**
 * @title Tracks collateral and debt distributions in a pool, for a specific collateral type.
 *
 * I.e. if a pool supports SNX and ETH collaterals, it will have an SNX Vault, and an ETH Vault.
 *
 * The Vault data structure is itself split into VaultEpoch sub-structures. This facilitates liquidations,
 * so that whenever one occurs, a clean state of all data is achieved by simply incrementing the epoch index.
 *
 * It is recommended to understand VaultEpoch before understanding this object.
 */
library Vault {
    using VaultEpoch for VaultEpoch.Data;
    using Distribution for Distribution.Data;
    using RewardDistribution for RewardDistribution.Data;
    using ScalableMapping for ScalableMapping.Data;
    using DecimalMath for uint256;
    using DecimalMath for int128;
    using DecimalMath for int256;
    using SafeCastU128 for uint128;
    using SafeCastU256 for uint256;
    using SafeCastI128 for int128;
    using SafeCastI256 for int256;
    using SetUtil for SetUtil.Bytes32Set;

    /**
     * @dev Thrown when a non-existent reward distributor is referenced
     */
    error RewardDistributorNotFound();

    struct Data {
        /**
         * @dev The vault's current epoch number.
         *
         * Vault data is divided into epochs. An epoch changes when an entire vault is liquidated.
         */
        uint256 epoch;
        /**
         * @dev Unused property, maintained for backwards compatibility in storage layout.
         */
        // solhint-disable-next-line private-vars-leading-underscore
        bytes32 __slotAvailableForFutureUse;
        /**
         * @dev The previous debt of the vault, when `updateCreditCapacity` was last called by the Pool.
         */
        // solhint-disable-next-line var-name-mixedcase
        int128 _unused_prevTotalDebtD18;
        /**
         * @dev Vault data for all the liquidation cycles divided into epochs.
         */
        mapping(uint256 => VaultEpoch.Data) epochData;
        /**
         * @dev Tracks available rewards, per user, for this vault.
         */
        mapping(bytes32 => RewardDistribution.Data) rewards;
        /**
         * @dev Tracks reward ids, for this vault.
         */
        SetUtil.Bytes32Set rewardIds;
    }

    /**
     * @dev Return's the VaultEpoch data for the current epoch.
     */
    function currentEpoch(Data storage self) internal view returns (VaultEpoch.Data storage) {
        return self.epochData[self.epoch];
    }

    /**
     * @dev Updates the vault's credit capacity as the value of its collateral minus its debt.
     *
     * Called as a ticker when users interact with pools, allowing pools to set
     * vaults' credit capacity shares within them.
     *
     * Returns the amount of collateral that this vault is providing in net USD terms.
     */
    function updateCreditCapacity(
        Data storage self,
        uint256 collateralPriceD18
    ) internal view returns (uint256 usdWeightD18, int256 totalDebtD18) {
        VaultEpoch.Data storage epochData = currentEpoch(self);

        usdWeightD18 = (epochData.collateralAmounts.totalAmount()).mulDecimal(collateralPriceD18);

        totalDebtD18 = epochData.totalDebt();

        //self.prevTotalDebtD18 = totalDebtD18.to128();
    }

    /**
     * @dev Updated the value per share of the current epoch's incoming debt distribution.
     */
    function distributeDebtToAccounts(Data storage self, int256 debtChangeD18) internal {
        currentEpoch(self).distributeDebtToAccounts(debtChangeD18);
    }

    /**
     * @dev Consolidates an accounts debt.
     */
    function consolidateAccountDebt(
        Data storage self,
        uint128 accountId
    ) internal returns (int256) {
        return currentEpoch(self).consolidateAccountDebt(accountId);
    }

    /**
     * @dev Traverses available rewards for this vault, and updates an accounts
     * claim on them according to the amount of debt shares they have.
     */
    function updateRewards(
        Data storage self,
        uint128 accountId,
        uint128 poolId,
        address collateralType
    ) internal returns (uint256[] memory rewards, address[] memory distributors) {
        rewards = new uint256[](self.rewardIds.length());
        distributors = new address[](self.rewardIds.length());

        uint256 numRewards = self.rewardIds.length();
        for (uint256 i = 0; i < numRewards; i++) {
            RewardDistribution.Data storage dist = self.rewards[self.rewardIds.valueAt(i + 1)];

            if (address(dist.distributor) == address(0)) {
                continue;
            }

            distributors[i] = address(dist.distributor);
            rewards[i] = updateReward(
                self,
                accountId,
                poolId,
                collateralType,
                self.rewardIds.valueAt(i + 1)
            );
        }
    }

    /**
     * @dev Traverses available rewards for this vault and the reward id, and updates an accounts
     * claim on them according to the amount of debt shares they have.
     */
    function updateReward(
        Data storage self,
        uint128 accountId,
        uint128 poolId,
        address collateralType,
        bytes32 rewardId
    ) internal returns (uint256) {
        uint256 totalSharesD18 = currentEpoch(self).accountsDebtDistribution.totalSharesD18;
        uint256 actorSharesD18 = currentEpoch(self).accountsDebtDistribution.getActorShares(
            accountId.toBytes32()
        );

        RewardDistribution.Data storage dist = self.rewards[rewardId];

        if (address(dist.distributor) == address(0)) {
            revert RewardDistributorNotFound();
        }

        dist.distributor.onPositionUpdated(accountId, poolId, collateralType, actorSharesD18);

        dist.rewardPerShareD18 += dist.updateEntry(totalSharesD18).toUint().to128();

        dist.claimStatus[accountId].pendingSendD18 += actorSharesD18
            .mulDecimal(dist.rewardPerShareD18 - dist.claimStatus[accountId].lastRewardPerShareD18)
            .to128();

        dist.claimStatus[accountId].lastRewardPerShareD18 = dist.rewardPerShareD18;

        return dist.claimStatus[accountId].pendingSendD18;
    }

    /**
     * @dev Increments the current epoch index, effectively producing a
     * completely blank new VaultEpoch data structure in the vault.
     */
    function reset(Data storage self) internal {
        self.epoch++;
    }

    /**
     * @dev Returns the vault's combined debt (consolidated and unconsolidated),
     * for the current epoch.
     */
    function currentDebt(Data storage self) internal view returns (int256) {
        return currentEpoch(self).totalDebt();
    }

    /**
     * @dev Returns the total value in the Vault's collateral distribution, for the current epoch.
     */
    function currentCollateral(Data storage self) internal view returns (uint256) {
        return currentEpoch(self).collateralAmounts.totalAmount();
    }

    /**
     * @dev Returns an account's collateral value in this vault's current epoch.
     */
    function currentAccountCollateral(
        Data storage self,
        uint128 accountId
    ) internal view returns (uint256) {
        return currentEpoch(self).getAccountCollateral(accountId);
    }
}

File 50 of 50 : VaultEpoch.sol
//SPDX-License-Identifier: MIT
pragma solidity >=0.8.11 <0.9.0;

import "./Distribution.sol";
import "./ScalableMapping.sol";

import "@synthetixio/core-contracts/contracts/utils/SafeCast.sol";

/**
 * @title Tracks collateral and debt distributions in a pool, for a specific collateral type, in a given epoch.
 *
 * Collateral is tracked with a distribution as opposed to a regular mapping because liquidations cause collateral to be socialized. If collateral was tracked using a regular mapping, such socialization would be difficult and require looping through individual balances, or some other sort of complex and expensive mechanism. Distributions make socialization easy.
 *
 * Debt is also tracked in a distribution for the same reason, but it is additionally split in two distributions: incoming and consolidated debt.
 *
 * Incoming debt is modified when a liquidations occurs.
 * Consolidated debt is updated when users interact with the system.
 */
library VaultEpoch {
    using Distribution for Distribution.Data;
    using DecimalMath for uint256;
    using SafeCastU128 for uint128;
    using SafeCastU256 for uint256;
    using SafeCastI128 for int128;
    using SafeCastI256 for int256;
    using ScalableMapping for ScalableMapping.Data;

    struct Data {
        /**
         * @dev Amount of debt in this Vault that is yet to be consolidated.
         *
         * E.g. when a given amount of debt is socialized during a liquidation, but it yet hasn't been rolled into
         * the consolidated debt distribution.
         */
        int128 unconsolidatedDebtD18;
        /**
         * @dev Amount of debt in this Vault that has been consolidated.
         */
        int128 totalConsolidatedDebtD18;
        /**
         * @dev Tracks incoming debt for each user.
         *
         * The value of shares in this distribution change as the associate market changes, i.e. price changes in an asset in
         * a spot market.
         *
         * Also, when debt is socialized in a liquidation, it is done onto this distribution. As users
         * interact with the system, their independent debt is consolidated or rolled into consolidatedDebtDist.
         */
        Distribution.Data accountsDebtDistribution;
        /**
         * @dev Tracks collateral delegated to this vault, for each user.
         *
         * Uses a distribution instead of a regular market because of the way collateral is socialized during liquidations.
         *
         * A regular mapping would require looping over the mapping of each account's collateral, or moving the liquidated
         * collateral into a place where it could later be claimed. With a distribution, liquidated collateral can be
         * socialized very easily.
         */
        ScalableMapping.Data collateralAmounts;
        /**
         * @dev Tracks consolidated debt for each user.
         *
         * Updated when users interact with the system, consolidating changes from the fluctuating accountsDebtDistribution,
         * and directly when users mint or burn USD, or repay debt.
         */
        mapping(uint256 => int256) consolidatedDebtAmountsD18;
        /**
         * @dev Tracks last time a user delegated to this vault.
         *
         * Needed to validate min delegation time compliance to prevent small scale debt pool frontrunning
         */
        mapping(uint128 => uint64) lastDelegationTime;
    }

    /**
     * @dev Updates the value per share of the incoming debt distribution.
     * Used for socialization during liquidations, and to bake in market changes.
     *
     * Called from:
     * - LiquidationModule.liquidate
     * - Pool.recalculateVaultCollateral (ticker)
     */
    function distributeDebtToAccounts(Data storage self, int256 debtChangeD18) internal {
        self.accountsDebtDistribution.distributeValue(debtChangeD18);

        // Cache total debt here.
        // Will roll over to individual users as they interact with the system.
        self.unconsolidatedDebtD18 += debtChangeD18.to128();
    }

    /**
     * @dev Adjusts the debt associated with `accountId` by `amountD18`.
     * Used to add or remove debt from/to a specific account, instead of all accounts at once (use distributeDebtToAccounts for that)
     */
    function assignDebtToAccount(
        Data storage self,
        uint128 accountId,
        int256 amountD18
    ) internal returns (int256 newDebtD18) {
        int256 currentDebtD18 = self.consolidatedDebtAmountsD18[accountId];
        self.consolidatedDebtAmountsD18[accountId] += amountD18;
        self.totalConsolidatedDebtD18 += amountD18.to128();
        return currentDebtD18 + amountD18;
    }

    /**
     * @dev Consolidates user debt as they interact with the system.
     *
     * Fluctuating debt is moved from incoming to consolidated debt.
     *
     * Called as a ticker from various parts of the system, usually whenever the
     * real debt of a user needs to be known.
     */
    function consolidateAccountDebt(
        Data storage self,
        uint128 accountId
    ) internal returns (int256 currentDebtD18) {
        int256 newDebtD18 = self.accountsDebtDistribution.accumulateActor(accountId.toBytes32());

        currentDebtD18 = assignDebtToAccount(self, accountId, newDebtD18);
        self.unconsolidatedDebtD18 -= newDebtD18.to128();
    }

    /**
     * @dev Updates a user's collateral value, and sets their exposure to debt
     * according to the collateral they delegated and the leverage used.
     *
     * Called whenever a user's collateral changes.
     */
    function updateAccountPosition(
        Data storage self,
        uint128 accountId,
        uint256 collateralAmountD18,
        uint256 leverageD18
    ) internal {
        bytes32 actorId = accountId.toBytes32();

        // Ensure account debt is consolidated before we do next things.
        consolidateAccountDebt(self, accountId);

        self.collateralAmounts.set(actorId, collateralAmountD18);
        self.accountsDebtDistribution.setActorShares(
            actorId,
            self.collateralAmounts.sharesD18[actorId].mulDecimal(leverageD18)
        );
    }

    /**
     * @dev Returns the vault's total debt in this epoch, including the debt
     * that hasn't yet been consolidated into individual accounts.
     */
    function totalDebt(Data storage self) internal view returns (int256) {
        return self.unconsolidatedDebtD18 + self.totalConsolidatedDebtD18;
    }

    /**
     * @dev Returns an account's value in the Vault's collateral distribution.
     */
    function getAccountCollateral(
        Data storage self,
        uint128 accountId
    ) internal view returns (uint256 amountD18) {
        return self.collateralAmounts.get(accountId.toBytes32());
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"CapacityLocked","type":"error"},{"inputs":[{"internalType":"address","name":"collateralType","type":"address"}],"name":"CollateralDepositDisabled","type":"error"},{"inputs":[],"name":"EmptyDistribution","type":"error"},{"inputs":[{"internalType":"bytes32","name":"which","type":"bytes32"}],"name":"FeatureUnavailable","type":"error"},{"inputs":[{"internalType":"uint256","name":"amountAvailableForDelegationD18","type":"uint256"},{"internalType":"uint256","name":"amountD18","type":"uint256"}],"name":"InsufficentAvailableCollateral","type":"error"},{"inputs":[{"internalType":"uint256","name":"requestedAmount","type":"uint256"}],"name":"InsufficientAccountCollateral","type":"error"},{"inputs":[{"internalType":"uint256","name":"collateralValue","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"ratio","type":"uint256"},{"internalType":"uint256","name":"minRatio","type":"uint256"}],"name":"InsufficientCollateralRatio","type":"error"},{"inputs":[{"internalType":"uint256","name":"minDelegation","type":"uint256"}],"name":"InsufficientDelegation","type":"error"},{"inputs":[],"name":"InvalidCollateralAmount","type":"error"},{"inputs":[{"internalType":"uint256","name":"leverage","type":"uint256"}],"name":"InvalidLeverage","type":"error"},{"inputs":[{"internalType":"uint128","name":"marketId","type":"uint128"}],"name":"MarketNotFound","type":"error"},{"inputs":[{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"uint32","name":"timeRemaining","type":"uint32"}],"name":"MinDelegationTimeoutPending","type":"error"},{"inputs":[],"name":"OverflowInt128ToUint128","type":"error"},{"inputs":[],"name":"OverflowInt256ToInt128","type":"error"},{"inputs":[],"name":"OverflowInt256ToUint256","type":"error"},{"inputs":[],"name":"OverflowUint128ToInt128","type":"error"},{"inputs":[],"name":"OverflowUint256ToInt256","type":"error"},{"inputs":[],"name":"OverflowUint256ToUint128","type":"error"},{"inputs":[],"name":"OverflowUint256ToUint32","type":"error"},{"inputs":[],"name":"OverflowUint32ToInt32","type":"error"},{"inputs":[],"name":"OverflowUint64ToInt64","type":"error"},{"inputs":[{"internalType":"uint128","name":"accountId","type":"uint128"},{"internalType":"bytes32","name":"permission","type":"bytes32"},{"internalType":"address","name":"target","type":"address"}],"name":"PermissionDenied","type":"error"},{"inputs":[{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"address","name":"collateralType","type":"address"},{"internalType":"uint256","name":"currentCollateral","type":"uint256"},{"internalType":"uint256","name":"maxCollateral","type":"uint256"}],"name":"PoolCollateralLimitExceeded","type":"error"},{"inputs":[{"internalType":"uint128","name":"poolId","type":"uint128"}],"name":"PoolNotFound","type":"error"},{"inputs":[],"name":"PositionOutOfBounds","type":"error"},{"inputs":[],"name":"RewardDistributorNotFound","type":"error"},{"inputs":[],"name":"ValueAlreadyInSet","type":"error"},{"inputs":[],"name":"ValueNotInSet","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint128","name":"accountId","type":"uint128"},{"indexed":true,"internalType":"uint128","name":"poolId","type":"uint128"},{"indexed":false,"internalType":"address","name":"collateralType","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"leverage","type":"uint256"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"DelegationUpdated","type":"event"},{"inputs":[{"internalType":"uint128","name":"accountId","type":"uint128"},{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"address","name":"collateralType","type":"address"},{"internalType":"uint256","name":"newCollateralAmountD18","type":"uint256"},{"internalType":"uint256","name":"leverage","type":"uint256"}],"name":"delegateCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"accountId","type":"uint128"},{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"address","name":"collateralType","type":"address"}],"name":"getPosition","outputs":[{"internalType":"uint256","name":"collateralAmount","type":"uint256"},{"internalType":"uint256","name":"collateralValue","type":"uint256"},{"internalType":"int256","name":"debt","type":"int256"},{"internalType":"uint256","name":"collateralizationRatio","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"accountId","type":"uint128"},{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"address","name":"collateralType","type":"address"}],"name":"getPositionCollateral","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"accountId","type":"uint128"},{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"address","name":"collateralType","type":"address"}],"name":"getPositionCollateralRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"accountId","type":"uint128"},{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"address","name":"collateralType","type":"address"}],"name":"getPositionDebt","outputs":[{"internalType":"int256","name":"debt","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"address","name":"collateralType","type":"address"}],"name":"getVaultCollateral","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"value","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"address","name":"collateralType","type":"address"}],"name":"getVaultCollateralRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint128","name":"poolId","type":"uint128"},{"internalType":"address","name":"collateralType","type":"address"}],"name":"getVaultDebt","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50613d8e806100206000396000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c806360248c551161005b57806360248c55146101015780637b0532a414610114578063dc0a538414610129578063f544d66e1461013c57600080fd5b8063078145a81461008d5780632fb8ff24146100ba57806333cc422b146100db5780633593bbd2146100ee575b600080fd5b6100a061009b366004613817565b61016f565b604080519283526020830191909152015b60405180910390f35b6100cd6100c8366004613817565b610190565b6040519081526020016100b1565b6100a06100e936600461384a565b6101ae565b6100cd6100fc36600461384a565b6101d2565b6100cd61010f366004613817565b6101fe565b61012761012236600461388d565b610213565b005b6100cd61013736600461384a565b6104dd565b61014f61014a36600461384a565b6104fe565b6040805194855260208501939093529183015260608201526080016100b1565b6000806101858361017f8661054e565b906105aa565b915091509250929050565b60006101a58261019f8561054e565b906105fc565b90505b92915050565b6000806101c683866101bf87610635565b91906106ab565b90969095509350505050565b6000806101de8461054e565b90506101eb8184876106fa565b91506101f68161072b565b509392505050565b60006101a58261020d85610635565b906108ac565b6102317119195b1959d85d1950dbdb1b185d195c985b60721b6108f5565b610246856744454c454741544560c01b610922565b508115610257576102578383610994565b670de0b6b3a76400008114610287576040516369bb335560e01b8152600481018290526024015b60405180910390fd5b60006102928561054e565b6001600160a01b03851660009081526008919091016020526040902090506102bc818787876109e1565b50600090506102cb8288610b84565b90508084036102ed5760405163fbd85bc360e01b815260040160405180910390fd5b80841115610335576102fe85610ba0565b610312878661030d84886138f8565b610bd5565b6103308561032083876138f8565b6103298961054e565b9190610c23565b61037b565b8154600090815260038301602090815260408083206001600160801b038b16845260060190915290205461037b906001600160401b03166103758861054e565b90610cec565b600061038b888888888689610d78565b905061039c88888860008911610e4b565b8185101561043d578254600090815260038401602090815260408083206001600160801b038c168452600501909152812054906103d88961054e565b6001600160a01b0389166000908152600a91909101602052604081206001015491506104319083126104125761040d83610edd565b610415565b60005b61041f8986610f04565b836104298c610f23565b929190610f91565b61043a8961100d565b50505b8254600090815260038401602090815260408083206001600160801b038c8116808652600690920184529382902080546001600160401b03421667ffffffffffffffff1990911617905581516001600160a01b038b168152928301899052828201889052905133938b16927f7b12dd38f18c0ff77ae702f6da13fbbcb28f53f807ecc7d39ee8d8b1ea8295ad919081900360600190a45050505050505050565b60006104f482856104ed86610635565b919061105f565b90505b9392505050565b600080600080600061050f87610635565b905061051c81878a6106fa565b92506105278161072b565b61053281878a6106ab565b909550935061054281878a61105f565b91505093509350935093565b60008061055a83610635565b90506001600160801b03831615801590610581575080546001600160801b03848116911614155b156101a8576040516332b961eb60e01b81526001600160801b038416600482015260240161027e565b60008060006105c06105bb85610f23565b6110b1565b6001600160a01b038516600090815260088701602052604090209091506105e69061115a565b92506105f28184610f04565b9150509250929050565b60006106088383611178565b506106128361072b565b6001600160a01b038216600090815260088401602052604090206101a590611266565b6000808260405160200161068c91906040808252601b908201527f696f2e73796e7468657469782e73796e7468657469782e506f6f6c000000000060608201526001600160801b0391909116602082015260800190565b60408051601f1981840301815291905280516020909101209392505050565b60008060006106bc6105bb86610f23565b6001600160a01b038616600090815260088801602052604090209091506106e39085610b84565b92506106ef8184610f04565b915050935093915050565b60006107068484611178565b506001600160a01b038316600090815260088501602052604090206104f49083611281565b60048101546001600160801b03166000819003610746575050565b60068201546001600160801b031660008161076257600061078b565b61078b6107866107718461129d565b6004870154600160801b9004600f0b906112c7565b6112e6565b90506000610797611322565b54905060005b60058601548110156108a45760008660050182815481106107c0576107c061390b565b60009182526020822060029091020180549092506001600160801b03600160801b9091041690876107f18389613921565b6107fb919061394e565b8354909150600090610815906001600160801b031661139b565b905060008082601001541161082a5786610830565b81601001545b8254909150600090610850906001600160801b031683600f8c900b6113f2565b6001870154909150600f0b808212610868578061086a565b815b87548e54919350610889916001600160801b0391821691168488611464565b5050505050505050808061089c90613962565b91505061079d565b505050505050565b6000806108b984846105fc565b905060006108c785856105aa565b915050600082136108d95760006108ec565b6108ec6108e583610edd565b82906114b5565b95945050505050565b6108ff81336114ca565b61091f57604051637e023fb760e01b81526004810182905260240161027e565b50565b600061092d83611510565b905061093d600182018333611567565b6109715760405162ef2a9160e11b81526001600160801b03841660048201526024810183905233604482015260640161027e565b60058101805467ffffffffffffffff1916426001600160401b03161790556101a8565b600061099f83610f23565b600681015490915060008190036109b7575060038101545b808310156109db57604051634918e8e360e11b81526004810182905260240161027e565b50505050565b6060806109ef866005015490565b6001600160401b03811115610a0657610a0661397b565b604051908082528060200260200182016040528015610a2f578160200160208202803683370190505b509150610a3d866005015490565b6001600160401b03811115610a5457610a5461397b565b604051908082528060200260200182016040528015610a7d578160200160208202803683370190505b5090506000610a8d876005015490565b905060005b81811015610b795760006004890181610ab9610aaf856001613991565b60058d01906115a4565b8152602081019190915260400160002080549091506001600160a01b0316610ae15750610b67565b805484516001600160a01b0390911690859084908110610b0357610b0361390b565b6001600160a01b0390921660209283029190910190910152610b4789898989610b42610b30886001613991565b8f6005016115a490919063ffffffff16565b611609565b858381518110610b5957610b5961390b565b602002602001018181525050505b80610b7181613962565b915050610a92565b505094509492505050565b8154600090815260038301602052604081206101a5908361183f565b610ba981610f23565b5460ff1661091f576040516318213f3d60e31b81526001600160a01b038216600482015260240161027e565b80610bdf84611510565b6001600160a01b038416600090815260069190910160205260409020541015610c1e576040516313232e7760e21b81526004810182905260240161027e565b505050565b6001600160a01b0382166000908152600a84016020908152604080832054600887019092528220909190610c569061115a565b600b86015490915060ff168015610c6b575081155b80610c895750600082118015610c89575081610c878483613991565b115b15610ce55784546001600160801b031684610ca48584613991565b604051631903c6d560e11b81526001600160801b0390931660048401526001600160a01b03909116602483015260448201526064810183905260840161027e565b5050505050565b6000610cf783611857565b9050610d0963ffffffff8216836139a4565b6001600160401b0316421015610c1e5782546001600160801b031642610d3563ffffffff8416856139a4565b6001600160401b0316610d4891906138f8565b604051637ec5c33560e11b81526001600160801b03909216600483015263ffffffff16602482015260440161027e565b600080610d8487610635565b9050610d9181878a6106fa565b506000610d9d89611510565b6001600160a01b038816600090815260069190910160205260409020905084861115610ddc57610dd7610dd086886138f8565b82906118fa565b610df0565b610df0610de987876138f8565b8290611946565b610dff89898960008a11610e4b565b6001600160a01b03871660009081526008830160209081526040808320805484526003019091529020610e34908a888761195a565b610e3e82886119aa565b9998505050505050505050565b6000610e5685611510565b6001600160a01b0384166000908152600691909101602052604081209150610e8a600183016001600160801b038716611a04565b9050828015610e97575080155b15610eb757610eb2600183016001600160801b038716611a1c565b6108a4565b82158015610ec25750805b156108a4576108a4600183016001600160801b038716611a2a565b600080821215610f005760405163029f024d60e31b815260040160405180910390fd5b5090565b6000670de0b6b3a7640000610f198385613921565b6101a5919061394e565b6000808260405160200161068c91906040808252602e908201527f696f2e73796e7468657469782e73796e7468657469782e436f6c6c617465726160608201526d3621b7b73334b3bab930ba34b7b760911b60808201526001600160a01b0391909116602082015260a00190565b600081856001015411610fa45781610faa565b84600101545b90508315801590610fcb5750821580610fcb575080610fc984866114b5565b105b15610ce5578284610fdc82826114b5565b604051636119eca760e01b81526004810193909352602483019190915260448201526064810182905260840161027e565b600061101882610635565b9050600061102582611a34565b80549091506001600160801b031615610c1e5780546040516391b9419f60e01b81526001600160801b03909116600482015260240161027e565b60008061106d8585856106fa565b90506110788561072b565b6000811361108b576000199150506104f7565b60006110988686866106ab565b9150506110a76108e583610edd565b9695505050505050565b6000806110bc611a93565b6040805160208101825291546001600160a01b03168083526004808701549251632a952b2d60e01b81529394506000939192632a952b2d92611102920190815260200190565b608060405180830381865afa15801561111f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114391906139c4565b90506111528160000151610edd565b949350505050565b8054600090815260038201602052604081206101a890600301611ae4565b81546000906001600160801b0316815b60058501548110156112095760006111cd8660050183815481106111ae576111ae61390b565b60009182526020909120600290910201546001600160801b031661139b565b90506111de816402540be3ff611b2f565b506111e98184611bda565b6111f39085613a37565b935050808061120190613962565b915050611188565b506112148483611c5f565b6001600160a01b0383161561125f576001600160a01b03831661125d61123d6006870183611cad565b6001600160a01b0386166000908152600888016020526040902090611cd5565b505b5092915050565b8054600090815260038201602052604081206101a890611cf1565b8154600090815260038301602052604081206101a59083611d17565b60006001600160ff1b03821115610f005760405163677c430560e11b815260040160405180910390fd5b6000816112dc670de0b6b3a764000085613a57565b6101a59190613a87565b600060016001607f1b0319821280611304575060016001607f1b0382135b15610f0057604051634022cc0360e01b815260040160405180910390fd5b60008060405160200161137d906020808252602e908201527f696f2e73796e7468657469782e73796e7468657469782e53797374656d506f6f60408201526d3621b7b73334b3bab930ba34b7b760911b606082015260800190565b60408051601f19818403018152919052805160209091012092915050565b6000808260405160200161068c91906040808252601d908201527f696f2e73796e7468657469782e73796e7468657469782e4d61726b657400000060608201526001600160801b0391909116602082015260800190565b6000806113fe8561139b565b9050600061140e82600a01611d8b565b90506000851561142f5761142a670de0b6b3a7640000876114b5565b611439565b670de0b6b3a76400005b9050846114458261129d565b61144f9084613a37565b6114599190613ab5565b979650505050505050565b6000806114708661139b565b60018101549091506001600160a01b03166114a95760405163784060f560e11b81526001600160801b038716600482015260240161027e565b6110a781868587611da5565b600081610f19670de0b6b3a764000085613921565b6000806114d684611ff7565b6001810154909150610100900460ff16156114f55760009150506101a8565b600181015460ff168061115257506111526002820184612050565b6000808260405160200161068c91906040808252601e908201527f696f2e73796e7468657469782e73796e7468657469782e4163636f756e74000060608201526001600160801b0391909116602082015260800190565b82546000906001600160a01b03838116911614806115935750611593846420a226a4a760d91b84612072565b806104f457506104f4848484612072565b60008115806115b35750825482115b156115d15760405163eb9bc44760e01b815260040160405180910390fd5b60006115de6001846138f8565b90508360000181815481106115f5576115f561390b565b906000526020600020015491505092915050565b84546000908152600386016020908152604080832060018101546001600160801b038981168652600290920184528285205486865260048b01909452918420805492821693909116916001600160a01b03166116785760405163626c897560e11b815260040160405180910390fd5b8054604051639a99916f60e01b81526001600160801b03808b166004830152891660248201526001600160a01b0388811660448301526064820185905290911690639a99916f90608401600060405180830381600087803b1580156116dc57600080fd5b505af11580156116f0573d6000803e3d6000fd5b5050505061171761171261170d85846120b790919063ffffffff16565b610edd565b61236b565b60018201805460109061173b908490600160801b90046001600160801b0316613ad5565b82546101009290920a6001600160801b03818102199093169183160217909155898116600090815260028401602052604090205460018401546117a193506117129261179192811691600160801b900416613af5565b84906001600160801b0316610f04565b6001600160801b03808a1660009081526002840160205260409020805490916010916117d6918591600160801b900416613ad5565b82546101009290920a6001600160801b0381810219909316918316021790915560018301549981166000908152600290930160205260409092208054600160801b9a8b900484166001600160801b03199091161790819055989098041698975050505050505050565b60006101a5600384016001600160801b038416612395565b6000805b60058301548110156118af5760006118818460050183815481106111ae576111ae61390b565b600f015463ffffffff9081169150831681111561189c578092505b50806118a781613962565b91505061185b565b5060006118df7f7365744d61726b65744d696e44656c656761746554696d655f6d61780000000062278d006123e3565b90508163ffffffff168163ffffffff16106101a85750919050565b815481111561192957815460405163eb5d3f6b60e01b815260048101919091526024810182905260440161027e565b8082600001600082825461193d91906138f8565b90915550505050565b8082600001600082825461193d9190613991565b6001600160801b03831661196e8585611d17565b5061197d6003860182856123ef565b5060008181526004860160205260409020546108a490829061199f9085610f04565b60018801919061247d565b60006119b86105bb83610f23565b6001600160a01b03831660009081526008850160205260408120919250906119e09083612508565b5090506119fa600685016001600160a01b0385168361247d565b5061125f8461072b565b600081815260018301602052604081205415156101a5565b611a26828261253d565b5050565b611a268282612596565b6000805b6005830154811015611a88576000611a5e8460050183815481106111ae576111ae61390b565b9050611a6981612695565b15611a75579392505050565b5080611a8081613962565b915050611a38565b506101a8600061139b565b60008060405160200161137d9060208082526024908201527f696f2e73796e7468657469782e73796e7468657469782e4f7261636c654d616e60408201526330b3b2b960e11b606082015260800190565b80546000906b033b2e3c9fd0803ce8000000906001600160801b03811690611b1b9061170d908490600160801b9004600f0b613a37565b611b259190613921565b6101a8919061394e565b600080611b3b846126bc565b6003850154909150600090611b5390600f0b83613ab5565b90506000611b628683876126f2565b91505080158015611b7f5750600a8601546001600160801b031615155b15611bd0576003860154611ba490611b9a90600f0b85613ab5565b600a880190612acd565b611bad836112e6565b6003870180546001600160801b0319166001600160801b03929092169190911790555b1595945050505050565b600080611bf3600a85016001600160801b038516611cad565b6001600160801b038085166000908152600c870160205260409020549192508291611c2691600160801b90910416612b8a565b600f0b611c339190613a37565b6001600160801b039384166000908152600c909501602052604090942080549093169092555090919050565b611c68816112e6565b6004830154611c819190600160801b9004600f0b613b15565b6004830180546001600160801b03928316600160801b029216919091179055611a266006830182612acd565b60008181526001830160205260408120805461115290859083906001600160801b0316612bbd565b815460009081526003830160205260409020611a269082612c04565b8054600090611d0e90600160801b8104600f90810b91900b613b15565b600f0b92915050565b600080611d30600185016001600160801b038516611cad565b9050611d3d848483612c5a565b9150611d48816112e6565b84548590600090611d5d908490600f0b613b42565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505092915050565b80546000906101a890600160801b9004600f0b6009612ce2565b600080611db28686612cfc565b90506000611dc36004880187612d28565b60200151611dd090613b6f565b600f0b9050611dde8561236b565b6001600160801b038781166000908152600c8a016020526040812080546001600160801b03191693909216929092179055611e1e610786600a8a01611d8b565b905085600003611e4957611e356004890188612d62565b50611e436007890188612d62565b50611eaf565b80600f0b851215611e7e5760009550611e656004890188612d62565b50611e4387611e73876112e6565b60078b019190612d9c565b611e9f87611e8b876112e6565b611e9490613b6f565b60048b019190612d9c565b50611ead6007890188612d62565b505b6000611ec8600a8a016001600160801b038a168961247d565b6001600160801b03808a166000908152600c8c0160205260409020549192508291611efb91600160801b90910416612b8a565b600f0b611f089190613a37565b6001600160801b03808a166000908152600c8c0160205260409020805490911690559450600f82900b861315611f8c57611f466107868a8989612e60565b60028a018054601090611f64908490600160801b9004600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505b81600f0b831315611feb57611fa56107868a8686612e60565b60028a018054601090611fc3908490600160801b9004600f0b613b42565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505b50505050949350505050565b6040805160208101829052602560608201527f696f2e73796e7468657469782e636f72652d6d6f64756c65732e46656174757260808201526465466c616760d81b60a0820152908101829052600090819060c00161068c565b6001600160a01b038116600090815260018301602052604081205415156101a5565b60006001600160a01b038216158015906104f457506001600160a01b0382166000908152600185810160209081526040808420878552909201905290205415156104f4565b6003820154600090600f0b15806120cc575081155b156120d9575060006101a8565b60038301544290600090600160801b90046001600160401b0316821015612105576000925050506101a8565b6003850154600160c01b900463ffffffff1615801561214457506003850154600160801b81046001600160401b0316600160e01b90910463ffffffff16105b1561216b576121646121558561129d565b6003870154600f0b5b906112c7565b9050612333565b600385015461219790600160c01b810463ffffffff1690600160801b90046001600160401b03166139a4565b6001600160401b031685600301601c9054906101000a900463ffffffff1663ffffffff161015612333576003850154600090600160801b81046001600160401b0316600160e01b90910463ffffffff161061226e57600386015461220790600160c01b900463ffffffff16612e8b565b60038781015491900b9061224a9061223c90600160801b81046001600160401b031690600160e01b900463ffffffff16613b95565b6001600160401b0316612eb8565b600388015461225f9160070b90600f0b613bb5565b6122699190613bd5565b612271565b60005b6003870154600f91820b92509081900b906122a990600160c01b810463ffffffff1690600160801b90046001600160401b03166139a4565b6001600160401b03168410156123185760038701546122d490600160c01b900463ffffffff16612e8b565b60038881015491900b90612301906122fc90600160801b90046001600160401b0316876138f8565b61129d565b61230b9083613a57565b6123159190613a87565b90505b61232e6123248761129d565b61215e8484613ab5565b925050505b61233c82612eec565b60038601805463ffffffff92909216600160e01b026001600160e01b0390921691909117905591505092915050565b60006001600160801b03821115610f0057604051637d5864af60e11b815260040160405180910390fd5b81546000906001600160801b03168082036123b45760009150506101a8565b806123be85611ae4565b60008581526001870160205260409020546123d99190613921565b611152919061394e565b60006101a58383612f13565b60006123fb8483612f66565b60008481526001860160205260409020548554919250612433916124299084906001600160801b0316613991565b61171291906138f8565b84546001600160801b0319166001600160801b03919091161784556124578161236b565b6000938452600190940160205260409092206001600160801b0393909316909255919050565b60006124898484612fb7565b600084815260018601602052604081209192506124a58461236b565b825487549192506001600160801b03908116916124c491849116613ad5565b6124ce9190613af5565b86546001600160801b03199081166001600160801b039283161788558354169082161782556124fe868386612bbd565b5050509392505050565b81546000908152600383016020526040812081906125328461252c83600301611ae4565b90610f04565b92506105f281611cf1565b60008181526001830160205260409020541561256c5760405163682ad32d60e01b815260040160405180910390fd5b81546001818101845560008481526020808220909301849055845493815293019052604090912055565b6000818152600183016020526040812054908190036125c85760405163b3dbc29360e01b815260040160405180910390fd5b60006125d56001836138f8565b84549091506000906125e9906001906138f8565b905080821461264e5760008560000182815481106126095761260961390b565b906000526020600020015490508086600001848154811061262c5761262c61390b565b6000918252602080832090910192909255918252600187019052604090208390555b845485908061265f5761265f613c13565b60019003818190600052602060002001600090559055846001016000858152602001908152602001600020600090555050505050565b60006126a36122fc83612fd2565b600290920154600160801b9004600f0b91909112919050565b60006126ca6122fc8361304d565b6002830154600f0b6126de6122fc85613111565b6126e89190613a37565b6101a89190613ab5565b6000808360000361270857506000905080612ac5565b600080600080871315612728575060019150506004860160078701612738565b5060001991505060078601600487015b60005b86811015612a745761274c83613150565b15612a7457600061275c84613173565b9050600061276a888b613ab5565b1380156127825750600a8a01546001600160801b0316155b806127d45750600a8a01546001600160801b0316158015906127d457506127b28a6127ad898c613ab5565b613192565b85600f0b6127c09190613a57565b81602001516127ce90613b6f565b600f0b12155b156127df5750612a74565b6127e984846131e9565b600a8a01546001600160801b03161561287357600061285261280d8c600a01611d8b565b602084015161281b89613b6f565b6128259190613bb5565b600f0b6128329190613ab5565b600a8d0154612849906001600160801b0316612b8a565b600f0b90613214565b9050612861600a8c0182612acd565b61286b8189613a37565b9750506128c3565b6128a3610786600961289d84602001518961288d90613b6f565b6128979190613bb5565b600f0b90565b90613229565b600a8b0180546001600160801b03928316600160801b0292169190911790555b60008913156129c55780516001600160801b039081166000908152600b8c016020526040902054166129375760405162461bcd60e51b815260206004820152601e60248201527f6e6f20736861726573206265666f7265206163746f722072656d6f76616c0000604482015260640161027e565b80516000906129599061170d906001600160801b0316600a8e0190600061247d565b90506129648161236b565b82516001600160801b039081166000908152600c8e01602052604090208054909160109161299b918591600160801b900416613ad5565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555050612a61565b80516001600160801b039081166000908152600b8c0160205260409020541615612a315760405162461bcd60e51b815260206004820152601b60248201527f6163746f722068617320736861726573206265666f7265206164640000000000604482015260640161027e565b80516001600160801b039081166000818152600c8d016020526040902054612a5f92600a8e0192911661247d565b505b5080612a6c81613962565b91505061273b565b612a7d866112e6565b60038a018054600090612a94908490600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055508681149450505050505b935093915050565b80600003612ad9575050565b81546001600160801b03166000819003612b0657604051633b182f5560e01b815260040160405180910390fd5b6000612b1e6b033b2e3c9fd0803ce800000084613a57565b90506000612b2b8361129d565b612b359083613a87565b9050612b40816112e6565b85548690601090612b5c908490600160801b9004600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505050505050565b600060016001607f1b036001600160801b0383161115610f0057604051634593782f60e11b815260040160405180910390fd5b6000612bc98484613243565b90508115612be2578354600160801b9004600f0b612be5565b60005b83546001600160801b03918216600160801b0291161790925550919050565b612c116001830182612acd565b612c1a816112e6565b82548390600090612c2f908490600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505050565b6001600160801b03821660009081526005840160205260408120805490839083612c848385613a37565b90915550612c939050836112e6565b85548690601090612caf908490600160801b9004600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b0316021790555082816108ec9190613a37565b6000612cf26122fc83600a613d0d565b6101a59084613a87565b60006101a56001600160801b0383166000908152600b850160205260409020546001600160801b031690565b60408051808201909152600080825260208201526001600160801b03821660009081526002840160205260409020546101a59084906132ae565b60408051808201909152600080825260208201526001600160801b03821660009081526002840160205260409020546101a5908490613337565b60408051808201909152600080825260208201526001840154600003612dc557612dc5846134c8565b6040805180820190915260008082526020820152612de38585612d62565b5084546001600160801b0316856000612dfb83613d19565b82546101009290920a6001600160801b03818102199093169183160217909155600187810180548201808255600091909152604080518082019091529288168352600f87900b60208401529193506104f4925087918491612e5b916138f8565b613518565b600080612e7c612e7286600a01611d8b565b61170d9085613ab5565b90506108ec6122fc8286610f04565b6000637fffffff63ffffffff83161115610f0057604051630299decb60e01b815260040160405180910390fd5b6000677fffffffffffffff6001600160401b0383161115610f0057604051633340f63360e11b815260040160405180910390fd5b600063ffffffff821115610f00576040516334bee0b960e01b815260040160405180910390fd5b6040805160208082018390526006606083015265436f6e66696760d01b6080808401919091528284018690528351808403909101815260a090920190925280519101208054908161125f57509092915050565b8154600090612f9890612f90906b033b2e3c9fd0803ce800000090600160801b9004600f0b613b15565b600f0b6135e3565b6001600160801b0316610f196b033b2e3c9fd0803ce800000084613921565b600081815260018301602052604081206101a5908490613243565b600181015481546040516257f3c960e91b81526001600160801b0390911660048201526000916001600160a01b03169063afe79200906024015b602060405180830381865afa158015613029573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101a89190613d3f565b600080805b600d84015481101561125f57600084600d0182815481106130755761307561390b565b600091825260208083206040805180820190915260029093020180546001600160a01b0316808452600190910154918301919091529092506130b690610f23565b905081602001516000036130cb5750506130ff565b60006130d6826110b1565b90506130ef836020015182610f0490919063ffffffff16565b6130f99086613991565b94505050505b8061310981613962565b915050613052565b6001810154815460405163bcec0d0f60e01b81526001600160801b0390911660048201526000916001600160a01b03169063bcec0d0f9060240161300c565b60018101546000906131635760006101a8565b6001828101546101a891906138f8565b60408051808201909152600080825260208201526101a88260016132ae565b600a8201546000906001600160801b03166131ae5760006131d3565b600a8301546131d3906131c9906001600160801b0316612b8a565b8390600f0b6112c7565b6131df84600a01611d8b565b6101a59190613a37565b60006131f483613609565b90506109db8160000151826020015161320c90613b6f565b849190612d9c565b6000670de0b6b3a76400006112dc8385613a57565b60006132396122fc83600a613d0d565b6101a59084613a57565b80548254600091829161326a91600160801b90819004600f90810b9291909104900b613b42565b8354600f9190910b9150600090613289906001600160801b0316612b8a565b61329690600f0b83613a57565b90506108ec6b033b2e3c9fd0803ce800000082613a87565b6040805180820190915260008082526020820152600183015482106132e65760408051808201909152600080825260208201526101a5565b8260010182815481106132fb576132fb61390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b918101919091529392505050565b604080518082019091526000808252602082015260018301548210158061335c575081155b1561337b575060408051808201909152600080825260208201526101a8565b60008360010183815481106133925761339261390b565b6000918252602080832060408051808201825293909101546001600160801b038116808552600160801b909104600f0b84840152845260028801909152822082905560018087018054929450916133e991906138f8565b815481106133f9576133f961390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b91810191909152600186018054919250908061344557613445613c13565b60008281526020812082016000199081019190915501905560018501548410156101f657613474858286613518565b6101f68586600101868154811061348d5761348d61390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b9181019190915286613628565b600181015460000361091f57604080518082019091526000808252602080830182815260019485018054958601815583529120915190516001600160801b03908116600160801b02911617910155565b600181148061356557506001830161353160028361394e565b815481106135415761354161390b565b6000918252602091829020015490830151600160801b909104600f90810b91900b13155b1561357557610c1e838383613789565b6135d3836001810161358860028561394e565b815481106135985761359861390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b9181019190915283613789565b610c1e8383612e5b60028561394e565b60008082600f0b1215610f005760405163144ec41160e21b815260040160405180910390fd5b60408051808201909152600080825260208201526101a8826001613337565b6001830154600061363a836002613921565b90508082116136535761364e858585613789565b610ce5565b600085600101828154811061366a5761366a61390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b9181019190915290506136ac826001613991565b831180156136f657508060200151600f0b866001018360016136ce9190613991565b815481106136de576136de61390b565b600091825260209091200154600160801b9004600f0b135b15613753576001860161370883613962565b9250828154811061371b5761371b61390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b9181019190915290505b8460200151600f0b8160200151600f0b1361377357610eb2868686613789565b61377e868286613789565b6108a4868684613628565b8183600101828154811061379f5761379f61390b565b600091825260208083208451948201516001600160801b03908116600160801b0295811695909517920191909155935190911681526002909301909152604090912055565b80356001600160801b03811681146137fb57600080fd5b919050565b80356001600160a01b03811681146137fb57600080fd5b6000806040838503121561382a57600080fd5b613833836137e4565b915061384160208401613800565b90509250929050565b60008060006060848603121561385f57600080fd5b613868846137e4565b9250613876602085016137e4565b915061388460408501613800565b90509250925092565b600080600080600060a086880312156138a557600080fd5b6138ae866137e4565b94506138bc602087016137e4565b93506138ca60408701613800565b94979396509394606081013594506080013592915050565b634e487b7160e01b600052601160045260246000fd5b818103818111156101a8576101a86138e2565b634e487b7160e01b600052603260045260246000fd5b80820281158282048414176101a8576101a86138e2565b634e487b7160e01b600052601260045260246000fd5b60008261395d5761395d613938565b500490565b600060018201613974576139746138e2565b5060010190565b634e487b7160e01b600052604160045260246000fd5b808201808211156101a8576101a86138e2565b6001600160401b0381811683821601908082111561125f5761125f6138e2565b6000608082840312156139d657600080fd5b604051608081018181106001600160401b0382111715613a0657634e487b7160e01b600052604160045260246000fd5b8060405250825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b808201828112600083128015821682158216171561125d5761125d6138e2565b80820260008212600160ff1b84141615613a7357613a736138e2565b81810583148215176101a8576101a86138e2565b600082613a9657613a96613938565b600160ff1b821460001984141615613ab057613ab06138e2565b500590565b818103600083128015838313168383128216171561125f5761125f6138e2565b6001600160801b0381811683821601908082111561125f5761125f6138e2565b6001600160801b0382811682821603908082111561125f5761125f6138e2565b600f81810b9083900b0160016001607f1b03811360016001607f1b0319821217156101a8576101a86138e2565b600f82810b9082900b0360016001607f1b0319811260016001607f1b03821317156101a8576101a86138e2565b600081600f0b60016001607f1b03198103613b8c57613b8c6138e2565b60000392915050565b6001600160401b0382811682821603908082111561125f5761125f6138e2565b600082600f0b82600f0b0280600f0b915080821461125f5761125f6138e2565b600081600f0b83600f0b80613bec57613bec613938565b60016001607f1b0319821460001982141615613c0a57613c0a6138e2565b90059392505050565b634e487b7160e01b600052603160045260246000fd5b600181815b80851115613c64578160001904821115613c4a57613c4a6138e2565b80851615613c5757918102915b93841c9390800290613c2e565b509250929050565b600082613c7b575060016101a8565b81613c88575060006101a8565b8160018114613c9e5760028114613ca857613cc4565b60019150506101a8565b60ff841115613cb957613cb96138e2565b50506001821b6101a8565b5060208310610133831016604e8410600b8410161715613ce7575081810a6101a8565b613cf18383613c29565b8060001904821115613d0557613d056138e2565b029392505050565b60006101a58383613c6c565b60006001600160801b03808316818103613d3557613d356138e2565b6001019392505050565b600060208284031215613d5157600080fd5b505191905056fea26469706673582212209662fe4746b055bdf90d6ef68f0cc45a7c562f7fd9d42b17d1c6376c345543bc64736f6c63430008110033

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106100885760003560e01c806360248c551161005b57806360248c55146101015780637b0532a414610114578063dc0a538414610129578063f544d66e1461013c57600080fd5b8063078145a81461008d5780632fb8ff24146100ba57806333cc422b146100db5780633593bbd2146100ee575b600080fd5b6100a061009b366004613817565b61016f565b604080519283526020830191909152015b60405180910390f35b6100cd6100c8366004613817565b610190565b6040519081526020016100b1565b6100a06100e936600461384a565b6101ae565b6100cd6100fc36600461384a565b6101d2565b6100cd61010f366004613817565b6101fe565b61012761012236600461388d565b610213565b005b6100cd61013736600461384a565b6104dd565b61014f61014a36600461384a565b6104fe565b6040805194855260208501939093529183015260608201526080016100b1565b6000806101858361017f8661054e565b906105aa565b915091509250929050565b60006101a58261019f8561054e565b906105fc565b90505b92915050565b6000806101c683866101bf87610635565b91906106ab565b90969095509350505050565b6000806101de8461054e565b90506101eb8184876106fa565b91506101f68161072b565b509392505050565b60006101a58261020d85610635565b906108ac565b6102317119195b1959d85d1950dbdb1b185d195c985b60721b6108f5565b610246856744454c454741544560c01b610922565b508115610257576102578383610994565b670de0b6b3a76400008114610287576040516369bb335560e01b8152600481018290526024015b60405180910390fd5b60006102928561054e565b6001600160a01b03851660009081526008919091016020526040902090506102bc818787876109e1565b50600090506102cb8288610b84565b90508084036102ed5760405163fbd85bc360e01b815260040160405180910390fd5b80841115610335576102fe85610ba0565b610312878661030d84886138f8565b610bd5565b6103308561032083876138f8565b6103298961054e565b9190610c23565b61037b565b8154600090815260038301602090815260408083206001600160801b038b16845260060190915290205461037b906001600160401b03166103758861054e565b90610cec565b600061038b888888888689610d78565b905061039c88888860008911610e4b565b8185101561043d578254600090815260038401602090815260408083206001600160801b038c168452600501909152812054906103d88961054e565b6001600160a01b0389166000908152600a91909101602052604081206001015491506104319083126104125761040d83610edd565b610415565b60005b61041f8986610f04565b836104298c610f23565b929190610f91565b61043a8961100d565b50505b8254600090815260038401602090815260408083206001600160801b038c8116808652600690920184529382902080546001600160401b03421667ffffffffffffffff1990911617905581516001600160a01b038b168152928301899052828201889052905133938b16927f7b12dd38f18c0ff77ae702f6da13fbbcb28f53f807ecc7d39ee8d8b1ea8295ad919081900360600190a45050505050505050565b60006104f482856104ed86610635565b919061105f565b90505b9392505050565b600080600080600061050f87610635565b905061051c81878a6106fa565b92506105278161072b565b61053281878a6106ab565b909550935061054281878a61105f565b91505093509350935093565b60008061055a83610635565b90506001600160801b03831615801590610581575080546001600160801b03848116911614155b156101a8576040516332b961eb60e01b81526001600160801b038416600482015260240161027e565b60008060006105c06105bb85610f23565b6110b1565b6001600160a01b038516600090815260088701602052604090209091506105e69061115a565b92506105f28184610f04565b9150509250929050565b60006106088383611178565b506106128361072b565b6001600160a01b038216600090815260088401602052604090206101a590611266565b6000808260405160200161068c91906040808252601b908201527f696f2e73796e7468657469782e73796e7468657469782e506f6f6c000000000060608201526001600160801b0391909116602082015260800190565b60408051601f1981840301815291905280516020909101209392505050565b60008060006106bc6105bb86610f23565b6001600160a01b038616600090815260088801602052604090209091506106e39085610b84565b92506106ef8184610f04565b915050935093915050565b60006107068484611178565b506001600160a01b038316600090815260088501602052604090206104f49083611281565b60048101546001600160801b03166000819003610746575050565b60068201546001600160801b031660008161076257600061078b565b61078b6107866107718461129d565b6004870154600160801b9004600f0b906112c7565b6112e6565b90506000610797611322565b54905060005b60058601548110156108a45760008660050182815481106107c0576107c061390b565b60009182526020822060029091020180549092506001600160801b03600160801b9091041690876107f18389613921565b6107fb919061394e565b8354909150600090610815906001600160801b031661139b565b905060008082601001541161082a5786610830565b81601001545b8254909150600090610850906001600160801b031683600f8c900b6113f2565b6001870154909150600f0b808212610868578061086a565b815b87548e54919350610889916001600160801b0391821691168488611464565b5050505050505050808061089c90613962565b91505061079d565b505050505050565b6000806108b984846105fc565b905060006108c785856105aa565b915050600082136108d95760006108ec565b6108ec6108e583610edd565b82906114b5565b95945050505050565b6108ff81336114ca565b61091f57604051637e023fb760e01b81526004810182905260240161027e565b50565b600061092d83611510565b905061093d600182018333611567565b6109715760405162ef2a9160e11b81526001600160801b03841660048201526024810183905233604482015260640161027e565b60058101805467ffffffffffffffff1916426001600160401b03161790556101a8565b600061099f83610f23565b600681015490915060008190036109b7575060038101545b808310156109db57604051634918e8e360e11b81526004810182905260240161027e565b50505050565b6060806109ef866005015490565b6001600160401b03811115610a0657610a0661397b565b604051908082528060200260200182016040528015610a2f578160200160208202803683370190505b509150610a3d866005015490565b6001600160401b03811115610a5457610a5461397b565b604051908082528060200260200182016040528015610a7d578160200160208202803683370190505b5090506000610a8d876005015490565b905060005b81811015610b795760006004890181610ab9610aaf856001613991565b60058d01906115a4565b8152602081019190915260400160002080549091506001600160a01b0316610ae15750610b67565b805484516001600160a01b0390911690859084908110610b0357610b0361390b565b6001600160a01b0390921660209283029190910190910152610b4789898989610b42610b30886001613991565b8f6005016115a490919063ffffffff16565b611609565b858381518110610b5957610b5961390b565b602002602001018181525050505b80610b7181613962565b915050610a92565b505094509492505050565b8154600090815260038301602052604081206101a5908361183f565b610ba981610f23565b5460ff1661091f576040516318213f3d60e31b81526001600160a01b038216600482015260240161027e565b80610bdf84611510565b6001600160a01b038416600090815260069190910160205260409020541015610c1e576040516313232e7760e21b81526004810182905260240161027e565b505050565b6001600160a01b0382166000908152600a84016020908152604080832054600887019092528220909190610c569061115a565b600b86015490915060ff168015610c6b575081155b80610c895750600082118015610c89575081610c878483613991565b115b15610ce55784546001600160801b031684610ca48584613991565b604051631903c6d560e11b81526001600160801b0390931660048401526001600160a01b03909116602483015260448201526064810183905260840161027e565b5050505050565b6000610cf783611857565b9050610d0963ffffffff8216836139a4565b6001600160401b0316421015610c1e5782546001600160801b031642610d3563ffffffff8416856139a4565b6001600160401b0316610d4891906138f8565b604051637ec5c33560e11b81526001600160801b03909216600483015263ffffffff16602482015260440161027e565b600080610d8487610635565b9050610d9181878a6106fa565b506000610d9d89611510565b6001600160a01b038816600090815260069190910160205260409020905084861115610ddc57610dd7610dd086886138f8565b82906118fa565b610df0565b610df0610de987876138f8565b8290611946565b610dff89898960008a11610e4b565b6001600160a01b03871660009081526008830160209081526040808320805484526003019091529020610e34908a888761195a565b610e3e82886119aa565b9998505050505050505050565b6000610e5685611510565b6001600160a01b0384166000908152600691909101602052604081209150610e8a600183016001600160801b038716611a04565b9050828015610e97575080155b15610eb757610eb2600183016001600160801b038716611a1c565b6108a4565b82158015610ec25750805b156108a4576108a4600183016001600160801b038716611a2a565b600080821215610f005760405163029f024d60e31b815260040160405180910390fd5b5090565b6000670de0b6b3a7640000610f198385613921565b6101a5919061394e565b6000808260405160200161068c91906040808252602e908201527f696f2e73796e7468657469782e73796e7468657469782e436f6c6c617465726160608201526d3621b7b73334b3bab930ba34b7b760911b60808201526001600160a01b0391909116602082015260a00190565b600081856001015411610fa45781610faa565b84600101545b90508315801590610fcb5750821580610fcb575080610fc984866114b5565b105b15610ce5578284610fdc82826114b5565b604051636119eca760e01b81526004810193909352602483019190915260448201526064810182905260840161027e565b600061101882610635565b9050600061102582611a34565b80549091506001600160801b031615610c1e5780546040516391b9419f60e01b81526001600160801b03909116600482015260240161027e565b60008061106d8585856106fa565b90506110788561072b565b6000811361108b576000199150506104f7565b60006110988686866106ab565b9150506110a76108e583610edd565b9695505050505050565b6000806110bc611a93565b6040805160208101825291546001600160a01b03168083526004808701549251632a952b2d60e01b81529394506000939192632a952b2d92611102920190815260200190565b608060405180830381865afa15801561111f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114391906139c4565b90506111528160000151610edd565b949350505050565b8054600090815260038201602052604081206101a890600301611ae4565b81546000906001600160801b0316815b60058501548110156112095760006111cd8660050183815481106111ae576111ae61390b565b60009182526020909120600290910201546001600160801b031661139b565b90506111de816402540be3ff611b2f565b506111e98184611bda565b6111f39085613a37565b935050808061120190613962565b915050611188565b506112148483611c5f565b6001600160a01b0383161561125f576001600160a01b03831661125d61123d6006870183611cad565b6001600160a01b0386166000908152600888016020526040902090611cd5565b505b5092915050565b8054600090815260038201602052604081206101a890611cf1565b8154600090815260038301602052604081206101a59083611d17565b60006001600160ff1b03821115610f005760405163677c430560e11b815260040160405180910390fd5b6000816112dc670de0b6b3a764000085613a57565b6101a59190613a87565b600060016001607f1b0319821280611304575060016001607f1b0382135b15610f0057604051634022cc0360e01b815260040160405180910390fd5b60008060405160200161137d906020808252602e908201527f696f2e73796e7468657469782e73796e7468657469782e53797374656d506f6f60408201526d3621b7b73334b3bab930ba34b7b760911b606082015260800190565b60408051601f19818403018152919052805160209091012092915050565b6000808260405160200161068c91906040808252601d908201527f696f2e73796e7468657469782e73796e7468657469782e4d61726b657400000060608201526001600160801b0391909116602082015260800190565b6000806113fe8561139b565b9050600061140e82600a01611d8b565b90506000851561142f5761142a670de0b6b3a7640000876114b5565b611439565b670de0b6b3a76400005b9050846114458261129d565b61144f9084613a37565b6114599190613ab5565b979650505050505050565b6000806114708661139b565b60018101549091506001600160a01b03166114a95760405163784060f560e11b81526001600160801b038716600482015260240161027e565b6110a781868587611da5565b600081610f19670de0b6b3a764000085613921565b6000806114d684611ff7565b6001810154909150610100900460ff16156114f55760009150506101a8565b600181015460ff168061115257506111526002820184612050565b6000808260405160200161068c91906040808252601e908201527f696f2e73796e7468657469782e73796e7468657469782e4163636f756e74000060608201526001600160801b0391909116602082015260800190565b82546000906001600160a01b03838116911614806115935750611593846420a226a4a760d91b84612072565b806104f457506104f4848484612072565b60008115806115b35750825482115b156115d15760405163eb9bc44760e01b815260040160405180910390fd5b60006115de6001846138f8565b90508360000181815481106115f5576115f561390b565b906000526020600020015491505092915050565b84546000908152600386016020908152604080832060018101546001600160801b038981168652600290920184528285205486865260048b01909452918420805492821693909116916001600160a01b03166116785760405163626c897560e11b815260040160405180910390fd5b8054604051639a99916f60e01b81526001600160801b03808b166004830152891660248201526001600160a01b0388811660448301526064820185905290911690639a99916f90608401600060405180830381600087803b1580156116dc57600080fd5b505af11580156116f0573d6000803e3d6000fd5b5050505061171761171261170d85846120b790919063ffffffff16565b610edd565b61236b565b60018201805460109061173b908490600160801b90046001600160801b0316613ad5565b82546101009290920a6001600160801b03818102199093169183160217909155898116600090815260028401602052604090205460018401546117a193506117129261179192811691600160801b900416613af5565b84906001600160801b0316610f04565b6001600160801b03808a1660009081526002840160205260409020805490916010916117d6918591600160801b900416613ad5565b82546101009290920a6001600160801b0381810219909316918316021790915560018301549981166000908152600290930160205260409092208054600160801b9a8b900484166001600160801b03199091161790819055989098041698975050505050505050565b60006101a5600384016001600160801b038416612395565b6000805b60058301548110156118af5760006118818460050183815481106111ae576111ae61390b565b600f015463ffffffff9081169150831681111561189c578092505b50806118a781613962565b91505061185b565b5060006118df7f7365744d61726b65744d696e44656c656761746554696d655f6d61780000000062278d006123e3565b90508163ffffffff168163ffffffff16106101a85750919050565b815481111561192957815460405163eb5d3f6b60e01b815260048101919091526024810182905260440161027e565b8082600001600082825461193d91906138f8565b90915550505050565b8082600001600082825461193d9190613991565b6001600160801b03831661196e8585611d17565b5061197d6003860182856123ef565b5060008181526004860160205260409020546108a490829061199f9085610f04565b60018801919061247d565b60006119b86105bb83610f23565b6001600160a01b03831660009081526008850160205260408120919250906119e09083612508565b5090506119fa600685016001600160a01b0385168361247d565b5061125f8461072b565b600081815260018301602052604081205415156101a5565b611a26828261253d565b5050565b611a268282612596565b6000805b6005830154811015611a88576000611a5e8460050183815481106111ae576111ae61390b565b9050611a6981612695565b15611a75579392505050565b5080611a8081613962565b915050611a38565b506101a8600061139b565b60008060405160200161137d9060208082526024908201527f696f2e73796e7468657469782e73796e7468657469782e4f7261636c654d616e60408201526330b3b2b960e11b606082015260800190565b80546000906b033b2e3c9fd0803ce8000000906001600160801b03811690611b1b9061170d908490600160801b9004600f0b613a37565b611b259190613921565b6101a8919061394e565b600080611b3b846126bc565b6003850154909150600090611b5390600f0b83613ab5565b90506000611b628683876126f2565b91505080158015611b7f5750600a8601546001600160801b031615155b15611bd0576003860154611ba490611b9a90600f0b85613ab5565b600a880190612acd565b611bad836112e6565b6003870180546001600160801b0319166001600160801b03929092169190911790555b1595945050505050565b600080611bf3600a85016001600160801b038516611cad565b6001600160801b038085166000908152600c870160205260409020549192508291611c2691600160801b90910416612b8a565b600f0b611c339190613a37565b6001600160801b039384166000908152600c909501602052604090942080549093169092555090919050565b611c68816112e6565b6004830154611c819190600160801b9004600f0b613b15565b6004830180546001600160801b03928316600160801b029216919091179055611a266006830182612acd565b60008181526001830160205260408120805461115290859083906001600160801b0316612bbd565b815460009081526003830160205260409020611a269082612c04565b8054600090611d0e90600160801b8104600f90810b91900b613b15565b600f0b92915050565b600080611d30600185016001600160801b038516611cad565b9050611d3d848483612c5a565b9150611d48816112e6565b84548590600090611d5d908490600f0b613b42565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505092915050565b80546000906101a890600160801b9004600f0b6009612ce2565b600080611db28686612cfc565b90506000611dc36004880187612d28565b60200151611dd090613b6f565b600f0b9050611dde8561236b565b6001600160801b038781166000908152600c8a016020526040812080546001600160801b03191693909216929092179055611e1e610786600a8a01611d8b565b905085600003611e4957611e356004890188612d62565b50611e436007890188612d62565b50611eaf565b80600f0b851215611e7e5760009550611e656004890188612d62565b50611e4387611e73876112e6565b60078b019190612d9c565b611e9f87611e8b876112e6565b611e9490613b6f565b60048b019190612d9c565b50611ead6007890188612d62565b505b6000611ec8600a8a016001600160801b038a168961247d565b6001600160801b03808a166000908152600c8c0160205260409020549192508291611efb91600160801b90910416612b8a565b600f0b611f089190613a37565b6001600160801b03808a166000908152600c8c0160205260409020805490911690559450600f82900b861315611f8c57611f466107868a8989612e60565b60028a018054601090611f64908490600160801b9004600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505b81600f0b831315611feb57611fa56107868a8686612e60565b60028a018054601090611fc3908490600160801b9004600f0b613b42565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505b50505050949350505050565b6040805160208101829052602560608201527f696f2e73796e7468657469782e636f72652d6d6f64756c65732e46656174757260808201526465466c616760d81b60a0820152908101829052600090819060c00161068c565b6001600160a01b038116600090815260018301602052604081205415156101a5565b60006001600160a01b038216158015906104f457506001600160a01b0382166000908152600185810160209081526040808420878552909201905290205415156104f4565b6003820154600090600f0b15806120cc575081155b156120d9575060006101a8565b60038301544290600090600160801b90046001600160401b0316821015612105576000925050506101a8565b6003850154600160c01b900463ffffffff1615801561214457506003850154600160801b81046001600160401b0316600160e01b90910463ffffffff16105b1561216b576121646121558561129d565b6003870154600f0b5b906112c7565b9050612333565b600385015461219790600160c01b810463ffffffff1690600160801b90046001600160401b03166139a4565b6001600160401b031685600301601c9054906101000a900463ffffffff1663ffffffff161015612333576003850154600090600160801b81046001600160401b0316600160e01b90910463ffffffff161061226e57600386015461220790600160c01b900463ffffffff16612e8b565b60038781015491900b9061224a9061223c90600160801b81046001600160401b031690600160e01b900463ffffffff16613b95565b6001600160401b0316612eb8565b600388015461225f9160070b90600f0b613bb5565b6122699190613bd5565b612271565b60005b6003870154600f91820b92509081900b906122a990600160c01b810463ffffffff1690600160801b90046001600160401b03166139a4565b6001600160401b03168410156123185760038701546122d490600160c01b900463ffffffff16612e8b565b60038881015491900b90612301906122fc90600160801b90046001600160401b0316876138f8565b61129d565b61230b9083613a57565b6123159190613a87565b90505b61232e6123248761129d565b61215e8484613ab5565b925050505b61233c82612eec565b60038601805463ffffffff92909216600160e01b026001600160e01b0390921691909117905591505092915050565b60006001600160801b03821115610f0057604051637d5864af60e11b815260040160405180910390fd5b81546000906001600160801b03168082036123b45760009150506101a8565b806123be85611ae4565b60008581526001870160205260409020546123d99190613921565b611152919061394e565b60006101a58383612f13565b60006123fb8483612f66565b60008481526001860160205260409020548554919250612433916124299084906001600160801b0316613991565b61171291906138f8565b84546001600160801b0319166001600160801b03919091161784556124578161236b565b6000938452600190940160205260409092206001600160801b0393909316909255919050565b60006124898484612fb7565b600084815260018601602052604081209192506124a58461236b565b825487549192506001600160801b03908116916124c491849116613ad5565b6124ce9190613af5565b86546001600160801b03199081166001600160801b039283161788558354169082161782556124fe868386612bbd565b5050509392505050565b81546000908152600383016020526040812081906125328461252c83600301611ae4565b90610f04565b92506105f281611cf1565b60008181526001830160205260409020541561256c5760405163682ad32d60e01b815260040160405180910390fd5b81546001818101845560008481526020808220909301849055845493815293019052604090912055565b6000818152600183016020526040812054908190036125c85760405163b3dbc29360e01b815260040160405180910390fd5b60006125d56001836138f8565b84549091506000906125e9906001906138f8565b905080821461264e5760008560000182815481106126095761260961390b565b906000526020600020015490508086600001848154811061262c5761262c61390b565b6000918252602080832090910192909255918252600187019052604090208390555b845485908061265f5761265f613c13565b60019003818190600052602060002001600090559055846001016000858152602001908152602001600020600090555050505050565b60006126a36122fc83612fd2565b600290920154600160801b9004600f0b91909112919050565b60006126ca6122fc8361304d565b6002830154600f0b6126de6122fc85613111565b6126e89190613a37565b6101a89190613ab5565b6000808360000361270857506000905080612ac5565b600080600080871315612728575060019150506004860160078701612738565b5060001991505060078601600487015b60005b86811015612a745761274c83613150565b15612a7457600061275c84613173565b9050600061276a888b613ab5565b1380156127825750600a8a01546001600160801b0316155b806127d45750600a8a01546001600160801b0316158015906127d457506127b28a6127ad898c613ab5565b613192565b85600f0b6127c09190613a57565b81602001516127ce90613b6f565b600f0b12155b156127df5750612a74565b6127e984846131e9565b600a8a01546001600160801b03161561287357600061285261280d8c600a01611d8b565b602084015161281b89613b6f565b6128259190613bb5565b600f0b6128329190613ab5565b600a8d0154612849906001600160801b0316612b8a565b600f0b90613214565b9050612861600a8c0182612acd565b61286b8189613a37565b9750506128c3565b6128a3610786600961289d84602001518961288d90613b6f565b6128979190613bb5565b600f0b90565b90613229565b600a8b0180546001600160801b03928316600160801b0292169190911790555b60008913156129c55780516001600160801b039081166000908152600b8c016020526040902054166129375760405162461bcd60e51b815260206004820152601e60248201527f6e6f20736861726573206265666f7265206163746f722072656d6f76616c0000604482015260640161027e565b80516000906129599061170d906001600160801b0316600a8e0190600061247d565b90506129648161236b565b82516001600160801b039081166000908152600c8e01602052604090208054909160109161299b918591600160801b900416613ad5565b92506101000a8154816001600160801b0302191690836001600160801b0316021790555050612a61565b80516001600160801b039081166000908152600b8c0160205260409020541615612a315760405162461bcd60e51b815260206004820152601b60248201527f6163746f722068617320736861726573206265666f7265206164640000000000604482015260640161027e565b80516001600160801b039081166000818152600c8d016020526040902054612a5f92600a8e0192911661247d565b505b5080612a6c81613962565b91505061273b565b612a7d866112e6565b60038a018054600090612a94908490600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055508681149450505050505b935093915050565b80600003612ad9575050565b81546001600160801b03166000819003612b0657604051633b182f5560e01b815260040160405180910390fd5b6000612b1e6b033b2e3c9fd0803ce800000084613a57565b90506000612b2b8361129d565b612b359083613a87565b9050612b40816112e6565b85548690601090612b5c908490600160801b9004600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505050505050565b600060016001607f1b036001600160801b0383161115610f0057604051634593782f60e11b815260040160405180910390fd5b6000612bc98484613243565b90508115612be2578354600160801b9004600f0b612be5565b60005b83546001600160801b03918216600160801b0291161790925550919050565b612c116001830182612acd565b612c1a816112e6565b82548390600090612c2f908490600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b031602179055505050565b6001600160801b03821660009081526005840160205260408120805490839083612c848385613a37565b90915550612c939050836112e6565b85548690601090612caf908490600160801b9004600f0b613b15565b92506101000a8154816001600160801b030219169083600f0b6001600160801b0316021790555082816108ec9190613a37565b6000612cf26122fc83600a613d0d565b6101a59084613a87565b60006101a56001600160801b0383166000908152600b850160205260409020546001600160801b031690565b60408051808201909152600080825260208201526001600160801b03821660009081526002840160205260409020546101a59084906132ae565b60408051808201909152600080825260208201526001600160801b03821660009081526002840160205260409020546101a5908490613337565b60408051808201909152600080825260208201526001840154600003612dc557612dc5846134c8565b6040805180820190915260008082526020820152612de38585612d62565b5084546001600160801b0316856000612dfb83613d19565b82546101009290920a6001600160801b03818102199093169183160217909155600187810180548201808255600091909152604080518082019091529288168352600f87900b60208401529193506104f4925087918491612e5b916138f8565b613518565b600080612e7c612e7286600a01611d8b565b61170d9085613ab5565b90506108ec6122fc8286610f04565b6000637fffffff63ffffffff83161115610f0057604051630299decb60e01b815260040160405180910390fd5b6000677fffffffffffffff6001600160401b0383161115610f0057604051633340f63360e11b815260040160405180910390fd5b600063ffffffff821115610f00576040516334bee0b960e01b815260040160405180910390fd5b6040805160208082018390526006606083015265436f6e66696760d01b6080808401919091528284018690528351808403909101815260a090920190925280519101208054908161125f57509092915050565b8154600090612f9890612f90906b033b2e3c9fd0803ce800000090600160801b9004600f0b613b15565b600f0b6135e3565b6001600160801b0316610f196b033b2e3c9fd0803ce800000084613921565b600081815260018301602052604081206101a5908490613243565b600181015481546040516257f3c960e91b81526001600160801b0390911660048201526000916001600160a01b03169063afe79200906024015b602060405180830381865afa158015613029573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101a89190613d3f565b600080805b600d84015481101561125f57600084600d0182815481106130755761307561390b565b600091825260208083206040805180820190915260029093020180546001600160a01b0316808452600190910154918301919091529092506130b690610f23565b905081602001516000036130cb5750506130ff565b60006130d6826110b1565b90506130ef836020015182610f0490919063ffffffff16565b6130f99086613991565b94505050505b8061310981613962565b915050613052565b6001810154815460405163bcec0d0f60e01b81526001600160801b0390911660048201526000916001600160a01b03169063bcec0d0f9060240161300c565b60018101546000906131635760006101a8565b6001828101546101a891906138f8565b60408051808201909152600080825260208201526101a88260016132ae565b600a8201546000906001600160801b03166131ae5760006131d3565b600a8301546131d3906131c9906001600160801b0316612b8a565b8390600f0b6112c7565b6131df84600a01611d8b565b6101a59190613a37565b60006131f483613609565b90506109db8160000151826020015161320c90613b6f565b849190612d9c565b6000670de0b6b3a76400006112dc8385613a57565b60006132396122fc83600a613d0d565b6101a59084613a57565b80548254600091829161326a91600160801b90819004600f90810b9291909104900b613b42565b8354600f9190910b9150600090613289906001600160801b0316612b8a565b61329690600f0b83613a57565b90506108ec6b033b2e3c9fd0803ce800000082613a87565b6040805180820190915260008082526020820152600183015482106132e65760408051808201909152600080825260208201526101a5565b8260010182815481106132fb576132fb61390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b918101919091529392505050565b604080518082019091526000808252602082015260018301548210158061335c575081155b1561337b575060408051808201909152600080825260208201526101a8565b60008360010183815481106133925761339261390b565b6000918252602080832060408051808201825293909101546001600160801b038116808552600160801b909104600f0b84840152845260028801909152822082905560018087018054929450916133e991906138f8565b815481106133f9576133f961390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b91810191909152600186018054919250908061344557613445613c13565b60008281526020812082016000199081019190915501905560018501548410156101f657613474858286613518565b6101f68586600101868154811061348d5761348d61390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b9181019190915286613628565b600181015460000361091f57604080518082019091526000808252602080830182815260019485018054958601815583529120915190516001600160801b03908116600160801b02911617910155565b600181148061356557506001830161353160028361394e565b815481106135415761354161390b565b6000918252602091829020015490830151600160801b909104600f90810b91900b13155b1561357557610c1e838383613789565b6135d3836001810161358860028561394e565b815481106135985761359861390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b9181019190915283613789565b610c1e8383612e5b60028561394e565b60008082600f0b1215610f005760405163144ec41160e21b815260040160405180910390fd5b60408051808201909152600080825260208201526101a8826001613337565b6001830154600061363a836002613921565b90508082116136535761364e858585613789565b610ce5565b600085600101828154811061366a5761366a61390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b9181019190915290506136ac826001613991565b831180156136f657508060200151600f0b866001018360016136ce9190613991565b815481106136de576136de61390b565b600091825260209091200154600160801b9004600f0b135b15613753576001860161370883613962565b9250828154811061371b5761371b61390b565b6000918252602091829020604080518082019091529101546001600160801b0381168252600160801b9004600f0b9181019190915290505b8460200151600f0b8160200151600f0b1361377357610eb2868686613789565b61377e868286613789565b6108a4868684613628565b8183600101828154811061379f5761379f61390b565b600091825260208083208451948201516001600160801b03908116600160801b0295811695909517920191909155935190911681526002909301909152604090912055565b80356001600160801b03811681146137fb57600080fd5b919050565b80356001600160a01b03811681146137fb57600080fd5b6000806040838503121561382a57600080fd5b613833836137e4565b915061384160208401613800565b90509250929050565b60008060006060848603121561385f57600080fd5b613868846137e4565b9250613876602085016137e4565b915061388460408501613800565b90509250925092565b600080600080600060a086880312156138a557600080fd5b6138ae866137e4565b94506138bc602087016137e4565b93506138ca60408701613800565b94979396509394606081013594506080013592915050565b634e487b7160e01b600052601160045260246000fd5b818103818111156101a8576101a86138e2565b634e487b7160e01b600052603260045260246000fd5b80820281158282048414176101a8576101a86138e2565b634e487b7160e01b600052601260045260246000fd5b60008261395d5761395d613938565b500490565b600060018201613974576139746138e2565b5060010190565b634e487b7160e01b600052604160045260246000fd5b808201808211156101a8576101a86138e2565b6001600160401b0381811683821601908082111561125f5761125f6138e2565b6000608082840312156139d657600080fd5b604051608081018181106001600160401b0382111715613a0657634e487b7160e01b600052604160045260246000fd5b8060405250825181526020830151602082015260408301516040820152606083015160608201528091505092915050565b808201828112600083128015821682158216171561125d5761125d6138e2565b80820260008212600160ff1b84141615613a7357613a736138e2565b81810583148215176101a8576101a86138e2565b600082613a9657613a96613938565b600160ff1b821460001984141615613ab057613ab06138e2565b500590565b818103600083128015838313168383128216171561125f5761125f6138e2565b6001600160801b0381811683821601908082111561125f5761125f6138e2565b6001600160801b0382811682821603908082111561125f5761125f6138e2565b600f81810b9083900b0160016001607f1b03811360016001607f1b0319821217156101a8576101a86138e2565b600f82810b9082900b0360016001607f1b0319811260016001607f1b03821317156101a8576101a86138e2565b600081600f0b60016001607f1b03198103613b8c57613b8c6138e2565b60000392915050565b6001600160401b0382811682821603908082111561125f5761125f6138e2565b600082600f0b82600f0b0280600f0b915080821461125f5761125f6138e2565b600081600f0b83600f0b80613bec57613bec613938565b60016001607f1b0319821460001982141615613c0a57613c0a6138e2565b90059392505050565b634e487b7160e01b600052603160045260246000fd5b600181815b80851115613c64578160001904821115613c4a57613c4a6138e2565b80851615613c5757918102915b93841c9390800290613c2e565b509250929050565b600082613c7b575060016101a8565b81613c88575060006101a8565b8160018114613c9e5760028114613ca857613cc4565b60019150506101a8565b60ff841115613cb957613cb96138e2565b50506001821b6101a8565b5060208310610133831016604e8410600b8410161715613ce7575081810a6101a8565b613cf18383613c29565b8060001904821115613d0557613d056138e2565b029392505050565b60006101a58383613c6c565b60006001600160801b03808316818103613d3557613d356138e2565b6001019392505050565b600060208284031215613d5157600080fd5b505191905056fea26469706673582212209662fe4746b055bdf90d6ef68f0cc45a7c562f7fd9d42b17d1c6376c345543bc64736f6c63430008110033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.