ETH Price: $3,435.42 (+4.40%)

Contract

0x15683c0C9AB93e03E082c9aC4aCd2069cD00dA37
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
0x6101a060193737672024-03-06 4:32:59262 days ago1709699579IN
 Create: TPlusNPortfolio
0 ETH0.1602164745

View more zero value Internal Transactions in Advanced View mode

Advanced mode:
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
TPlusNPortfolio

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 9999999 runs

Other Settings:
london EvmVersion
File 1 of 16 : TPlusNPortfolio.sol
//SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;

import {EntityBaseTokenTransferor} from "../EntityBaseTokenTransferor.sol";
import {TPlusNAsset} from "./TPlusNAsset.sol";
import {Registry} from "../Registry.sol";
import {Entity} from "../Entity.sol";
import {Portfolio} from "../Portfolio.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {Math} from "../lib/Math.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";

/// ENUMS
enum ConsolidationOperation {
    Deposit,
    Redeem
}

/// STRUCTS
/**
 * @notice Arguments for constructor. Using a struct to avoid stack too deep error.
 * @param _registry Endaoment registry.
 * @param _receiptAsset Address of the receipt asset. Should normally be a `TPlusNAsset` contract.
 * @param _shareTokenName Name of ERC20 portfolio share token.
 * @param _shareTokenSymbol Symbol of ERC20 portfolio share token.
 * @param _ebtt Address of the EBTT contract.
 * @param _processor Address to automatically route deposit base token to.
 * @param _minDeposit Minimum base token amount allowed for a valid deposit.
 * @param _cap Maximum amount of assets this portfolio can hold.
 * @param _feeTreasury Address of treasury that should receive fees.
 * @param _depositFee Percentage fee as ZOC that should go to treasury on deposit. (100 = 1%).
 * @param _redemptionFee Percentage fee as ZOC that should go to treasury on redemption. (100 = 1%).
 * @param _aumRate Percentage fee per second (as WAD) that should accrue to treasury as AUM fee. (1e16 = 1%).
 */
struct ConstructorArgs {
    Registry registry;
    address receiptAsset;
    string shareTokenName;
    string shareTokenSymbol;
    EntityBaseTokenTransferor ebtt;
    address processor;
    uint256 minDeposit;
    uint256 cap;
    address feeTreasury;
    uint256 depositFee;
    uint256 redemptionFee;
    uint256 aumRate;
}

/**
 * @notice Struct representing a single consolidation for a deposit/purchase or redeem/sale operation.
 * @param operation The type of consolidation operation - deposit/purchase or redeem/sale.
 * @param entity The entity whose pending balance should be consolidated.
 * @param amountBaseToken The amount of base token to consolidate. For purchases, this is the amount of base token used. For sales, this is the amount of base token received.
 * @param amountAssets The amount of assets to consolidate. For purchases, this is the amount of assets purchased. For sales, this is the amount of assets sold.
 */
struct Consolidation {
    ConsolidationOperation operation;
    Entity entity;
    uint256 amountBaseToken;
    uint256 amountAssets;
}

contract TPlusNPortfolio is Portfolio {
    using SafeTransferLib for ERC20;
    using Math for uint256;

    /// STATE

    /// @notice The EBTT contract.
    EntityBaseTokenTransferor public immutable ebtt;
    /// @notice The address to automatically route base tokens from and to on deposits and sales.
    address public processor;
    /// @notice Minimum base token amount that can be deposited.
    uint256 public minDeposit;
    /// @notice Maintenance flag to pause functionality.
    bool public underMaintenance;
    /// @notice Pending purchase balance of base tokens per address.
    mapping(Entity => uint256) public pendingPurchaseBalance;
    /// @notice Pending sale assets per address.
    mapping(Entity => uint256) public pendingSaleAssets;

    /// ERRORS

    /// @notice Emitted when deposit is below minimum.
    error MinDeposit();
    /// @notice Emitted when called under maintenance.
    error UnderMaintenance();
    /// @notice Emitted when informed entity parameter is bad (e.g. missing information, duplicated entities).
    error BadEntityInput();
    /// @notice Emitted when insufficient balances on consolidation.
    error InsufficientBalance();

    /// EVENTS

    /// @notice Emitted when processor is set.
    event ProcessorSet(address newProcessor);
    /// @notice Emitted when minDeposit is set.
    event MinDepositSet(uint256 newMinDeposit);
    /// @notice Emitted when underMaintenance is set.
    event UnderMaintenanceSet(bool newUnderMaintenance);
    /// @notice Emitted when a deposit/purchase consolidation is made.
    event DepositConsolidated(
        Entity indexed entity, uint256 amountBaseToken, uint256 amountAssets, uint256 amountShares
    );
    /// @notice Emitted when a correction mint is made.
    event CorrectionShareMinted(Entity indexed entity, uint256 amountShares);
    /// @notice Emitted when a correction burn is made.
    event CorrectionShareBurned(Entity indexed entity, uint256 amountShares);
    /// @notice Emitted when a redemption/sale consolidation is made.
    event RedeemConsolidated(Entity indexed entity, uint256 amountBaseToken, uint256 amountAssets, uint256 fee);

    /**
     * @param _args Constructor arguments struct.
     * @dev Args are passed in a struct to avoid stack too deep errors.
     * @dev The `true` parameter is to set this portfolio as `async` in the parent `Portfolio` contract. Async portfolios handle share lifecycle differently.
     * @dev EBTT is required to properly process payments back to entites on sale consolidations.
     * @dev While `cap` is not enforced in contract, its value can be utilized by UI to implement the behavior.
     */
    constructor(ConstructorArgs memory _args)
        Portfolio(
            _args.registry,
            _args.receiptAsset,
            _args.shareTokenName,
            _args.shareTokenSymbol,
            true, // Async portfolio, hence setting `_async` to true
            _args.cap,
            _args.feeTreasury,
            _args.depositFee,
            _args.redemptionFee,
            _args.aumRate
        )
    {
        // Approve EBTT to transfer this portfolio's balance for sale consolidations
        ebtt = _args.ebtt;
        baseToken.safeApprove(address(ebtt), type(uint256).max);

        processor = _args.processor;
        emit ProcessorSet(processor);

        minDeposit = _args.minDeposit;
        emit MinDepositSet(minDeposit);
    }

    /**
     * @inheritdoc Portfolio
     */
    function _getAsset(address _receiptAsset) internal pure override returns (address) {
        return _receiptAsset;
    }

    /**
     * @inheritdoc Portfolio
     */
    function convertReceiptAssetsToAssets(uint256 _receiptAssets) public pure override returns (uint256) {
        return _receiptAssets;
    }

    /**
     * @inheritdoc Portfolio
     * @notice T+N portfolios do not enforce cap onchain.
     * @dev Cap is not enforced because async portfolios do not have direct access to the spot price of the asset, hence not being able to determine a cap syncronously to deposits.
     * @dev While `cap` is not enforced in contract, it is settable in the constructor so external logic can read and utilize it.
     */
    function _checkCap() internal pure override {}

    /**
     * @inheritdoc Portfolio
     * @dev As an `async` portfolio, T+N portfolio do not mint shares on `deposit`, rather handling it on consolidations.
     * @dev Deposits smaller than `minDeposit` revert.
     */
    function _deposit(uint256 _amountBaseToken, bytes calldata /* _data */ )
        internal
        override
        returns (uint256, /* shares */ uint256, /* assets */ uint256 /* fee */ )
    {
        // Check if deposit is above minimum
        if (_amountBaseToken < minDeposit) revert MinDeposit();
        // Check if under maintenance
        if (underMaintenance) revert UnderMaintenance();

        // Calculate fee and net amount to be deposited and used for purchase
        // TODO: move deposit fee logic to `Portfolio`, for all portfolios
        (uint256 _amountIn, uint256 _amountFee) = _calculateFee(_amountBaseToken, depositFee);

        // Transfer amount from entity to `processor`
        baseToken.safeTransferFrom(msg.sender, processor, _amountIn);

        // Transfer fee to treasury
        baseToken.safeTransferFrom(msg.sender, feeTreasury, _amountFee);

        // Update pending balance
        unchecked {
            // Unchecked: no realistic amount of base token will overflow
            pendingPurchaseBalance[Entity(payable(msg.sender))] += _amountIn;
        }

        // No acquired shares or assets, and base token fee amount
        return (0, 0, _amountFee);
    }

    /**
     * @inheritdoc Portfolio
     * @notice Returns (0, 0) to signal that no asset and base token are produced on reddem for T+N.
     */
    function _redeem(uint256 _amountShares, bytes calldata /* _data */ )
        internal
        override
        returns (uint256, /* assetsOut */ uint256 /* baseTokenOut */ )
    {
        // Check if under maintenance
        if (underMaintenance) revert UnderMaintenance();

        // Verify how many assets this amount of shares is worth
        // This assumes `takeAUMFees` was already called in the wrapping `Portfolio.redeem` call
        uint256 _amountAssets = convertToAssets(_amountShares);

        // Update pending asset amount to sell
        unchecked {
            // Unchecked: asset total supply is capped at type(uint256).max, so an individual balance will also never overflow
            pendingSaleAssets[Entity(payable(msg.sender))] += _amountAssets;
        }

        // Burn asset to maintain correct supply in portfolio's balance
        // This is important so AUM fees are charged proportional to the asset balance that still
        // belongs to the portfolio. This also implies that once an entity performs a redeem,
        // we won't charge/be entitled to any AUM fees on that portion of the assets.
        TPlusNAsset(receiptAsset).burn(_amountAssets);

        // Return the amount of assets out and 0 base token for a T+N redemption
        return (_amountAssets, 0);
    }

    /**
     * @notice Consolidates pending purchase balances into shares, based on the amount of assets effectively purchased.
     * @param _entity The entity whose pending balance should be consolidated.
     * @param _amountBaseToken The amount of base token to consolidate.
     * @param _amountAssets The amount of assets this amount of base token was capable of purchasing.
     * @dev The value of _amountAssets must be chosen carefully to avoid rounding errors e.g. 1 ether = 1 "real world" asset is a good choice.
     */
    function _consolidateDeposit(Entity _entity, uint256 _amountBaseToken, uint256 _amountAssets) private {
        // Decrease pending balance
        // Checked: we want to revert on underflow
        pendingPurchaseBalance[_entity] -= _amountBaseToken;

        // Mint shares proportional to the amount of assets produced
        // ⚠️ Share calculation must happen before all mints to avoid wrong values
        uint256 _amountShares = convertToShares(_amountAssets);
        _mint(address(_entity), _amountShares);

        // Mint the receipt asset equal to the amount of asset produced, since the portfolio
        // controls the supply of the receipt asset
        TPlusNAsset(receiptAsset).mint(address(this), _amountAssets);

        // Emit
        emit DepositConsolidated(_entity, _amountBaseToken, _amountAssets, _amountShares);
    }

    /**
     * @notice Consolidate pending sale/redeemed assets into base token, based on the amount of assets effectively sold. Transfers base token to entity.
     * @param _entity The entity whose pending asset balance should be consolidated.
     * @param _amountBaseToken The amount of base token the sale of the asset amount was capable of selling for.
     * @param _amountAssets The amount of assets that effectively got sold.
     * @dev Method will revert if portfolio does not have enough base token in its balance to transfer to entity and treasury.
     */
    function _consolidateRedeem(Entity _entity, uint256 _amountBaseToken, uint256 _amountAssets) private {
        // Checked: Desired underflow if larger
        pendingSaleAssets[_entity] -= _amountAssets;

        // Get net and fee values
        (uint256 _amountOut, uint256 _amountFee) = _calculateFee(_amountBaseToken, redemptionFee);

        // Transfer sale-produced base token amount to entity
        // Uses EBTT contract to circumvent any fee or events being triggered incorrectly
        ebtt.transferFromPortfolio(_entity, _amountOut);

        // Transfer fee to treasury
        baseToken.safeTransfer(feeTreasury, _amountFee);

        // Emit
        emit RedeemConsolidated(_entity, _amountOut, _amountAssets, _amountFee);
    }

    /**
     * @notice Consolidate pending balances into shares or base token, based on the amount of assets effectively purchased or sold.
     * @param _consolidations Array of `Consolidation` structs to process.
     */
    function _consolidate(Consolidation[] calldata _consolidations) private {
        for (uint256 i = 0; i < _consolidations.length; ++i) {
            if (_consolidations[i].operation == ConsolidationOperation.Deposit) {
                _consolidateDeposit(
                    _consolidations[i].entity, _consolidations[i].amountBaseToken, _consolidations[i].amountAssets
                );
            } else {
                _consolidateRedeem(
                    _consolidations[i].entity, _consolidations[i].amountBaseToken, _consolidations[i].amountAssets
                );
            }
        }
    }

    /**
     * @notice Perform consolidation while skipping any accrual operations.
     * @param _consolidations Array of `Consolidation` structs to process.
     * @dev Reverts if the contract does not have enough base token to transfer to the entity and treasury on a redeem consolidation.
     */
    function consolidateNoAccrual(Consolidation[] calldata _consolidations) external requiresAuth {
        // AUM fees must be taken whenever the balance of assets changes
        takeAumFees();
        // Consolidate
        _consolidate(_consolidations);
    }

    /**
     * @notice Endaoment role authed method to consolidate pending purchases and sales while distributing accrued assets.
     * @param _consolidations Array of `Consolidation` structs to process.
     * @param _accruedAssets Amount of assets accrued since last consolidation.
     * @dev Reverts if the contract does not have enough base token to transfer to the entity and treasury on a redeem consolidation.
     */
    function consolidateWithAccrual(Consolidation[] calldata _consolidations, uint256 _accruedAssets)
        external
        requiresAuth
    {
        // AUM fees must be taken whenever the balance of assets changes
        takeAumFees();

        // Given how the operational flow of how T+N works, accruals are simply the minting of the underlying asset
        // This *must* be done before any consolidation, to properly reflect the contribution of each existing entity's
        // position to produce the accrued assets
        TPlusNAsset(receiptAsset).mint(address(this), _accruedAssets);

        // Consolidate
        _consolidate(_consolidations);
    }

    /**
     * @notice Endaoment role authed method to mint shares to an entity. Used solely for correcting share balances in case of errors.
     * @param _entity The entity to mint shares to.
     * @param _amount The amount of shares to mint.
     * @dev This method is only callable by Endaoment roles, and used only in case of error corrections.
     */
    function correctionMint(Entity _entity, uint256 _amount) external requiresAuth {
        _mint(address(_entity), _amount);

        emit CorrectionShareMinted(_entity, _amount);
    }

    /**
     * @notice Endaoment role authed method to burn shares from an entity. Used solely for correcting share balances in case of errors.
     * @param _entity The entity to burn shares from.
     * @param _amount The amount of shares to burn.
     * @dev This method is only callable by Endaoment roles, and used only in case of error corrections.
     */
    function correctionBurn(Entity _entity, uint256 _amount) external requiresAuth {
        _burn(address(_entity), _amount);

        emit CorrectionShareBurned(_entity, _amount);
    }

    // @inheritdoc Portfolio
    function _exit(
        uint256,
        /* _amount */
        bytes calldata /* _data */
    ) internal pure override returns (uint256, /* actualAssetsOut */ uint256 /* baseTokenOut */ ) {
        // Noop
        return (0, 0);
    }

    /**
     * @notice Endaoment role authed method to set the processor address.
     * @param _processor Address to automatically route deposit base token to.
     */
    function setProcessor(address _processor) external requiresAuth {
        processor = _processor;
        emit ProcessorSet(_processor);
    }

    /**
     * @notice Role authed method to set the minimum base token amount allowed for a valid deposit.
     * @param _min Minimum base token amount allowed for a valid deposit.
     */
    function setMinDeposit(uint256 _min) external requiresAuth {
        minDeposit = _min;
        emit MinDepositSet(_min);
    }

    /**
     * @notice Role authed method to set the maintenance flag.
     * @param _underMaintenance Maintenance flag to pause functionality.
     */
    function setUnderMaintenance(bool _underMaintenance) external requiresAuth {
        underMaintenance = _underMaintenance;
        emit UnderMaintenanceSet(_underMaintenance);
    }
}

File 2 of 16 : EntityBaseTokenTransferor.sol
//SPDX-License-Identifier: BSD 3-Clause
pragma solidity >=0.8.0;

import {EndaomentAuth} from "./lib/auth/EndaomentAuth.sol";
import {Registry} from "./Registry.sol";
import {Entity} from "./Entity.sol";
import {Portfolio} from "./Portfolio.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import "solmate/utils/SafeTransferLib.sol";

/**
 * @notice An Endaoment ecosystem contract capabale of transfering base tokens to Endaoment entities, updating their balances without triggering fees or emitting misleading events.
 * @dev This is due to this contract being enabled as a valid entity on the `Registry` and capable of calling `receiveTransfer` on entities.
 */
contract EntityBaseTokenTransferor is EndaomentAuth {
    using SafeTransferLib for ERC20;

    /// STRUCTS
    struct EntityTransfer {
        Entity entity;
        uint256 amount;
    }

    /// STATE
    /// @notice The Endaoment registry contract
    Registry public immutable registry;

    /// ERRORS
    /// @notice Error when the transfer destination isn't a valid and enabled Endaoment entity.
    error InvalidEntity();
    /// @notice Error when the transfer source isn't a valid and enabled Endaoment portfolio.
    error InvalidPortfolio();
    /// @notice Error when a call to another contract fails.
    error CallFailed(bytes response);

    /// EVENTS
    /// @notice Emitted when a transfer is made to an Endaoment entity.
    event TransferredToEntity(address indexed from, Entity indexed entity, uint256 amount);

    /**
     * @param _registry The Endaoment registry contract
     */
    constructor(Registry _registry) {
        __initEndaomentAuth(_registry, "");
        registry = _registry;
    }

    /**
     * Modifier to only allow valid and active Endaoment entities as transfer destinations.
     * @param _entity The attempted entity
     */
    modifier isActiveEntity(Entity _entity) {
        _checkEntity(_entity);
        _;
    }

    /**
     * Modifier to only allow valid and active Endaoment portfolios as callers.
     */
    modifier isActivePortfolioCaller() {
        if (!registry.isActivePortfolio(Portfolio(msg.sender))) revert InvalidPortfolio();
        _;
    }

    /**
     * Check if an entity is valid and active
     * @param _entity The entity to check
     */
    function _checkEntity(Entity _entity) private view {
        if (!registry.isActiveEntity(_entity)) revert InvalidEntity();
    }

    /**
     * Transfer base token from caller and consolidate balance of the Endaoment entity.
     * @param _entity The entity to transfer to.
     * @param _amount The amount to transfer.
     * @notice This functions exists so Endaoment admins can transfer arbitrary amounts of base tokens to entities without triggering fees.
     * @dev Caller must pre `approve` this contract to transfer the desired amount.
     */
    function transferFromCaller(Entity _entity, uint256 _amount) external requiresAuth isActiveEntity(_entity) {
        _transferToEntity(msg.sender, _entity, _amount);
    }

    /**
     * Batch transfer base token from caller and consolidate balance of the receiving Endaoment entities.
     * @param _entityTransfers The entity transfers to make.
     * @notice This functions exists so Endaoment admins can transfer arbitrary amounts of base tokens to entities without triggering fees.
     * @dev Caller must pre `approve` this contract to transfer the desired amount.
     */
    function batchTransferFromCaller(EntityTransfer[] calldata _entityTransfers) external requiresAuth {
        for (uint256 i = 0; i < _entityTransfers.length; ++i) {
            EntityTransfer memory _transfer = _entityTransfers[i];
            _checkEntity(_transfer.entity);
            _transferToEntity(msg.sender, _transfer.entity, _transfer.amount);
        }
    }

    /**
     * Transfer base token from an Endaoment portfolio to an Endaoment entity.
     * @param _entity The entity to transfer to.
     * @param _amount The amount to transfer.
     * @notice This functions exists so Endaoment portfolios can transfer arbitrary amounts of base tokens to entities without triggering fees.
     * An example of this use case is for T+N portfolios and their async nature of transferring base tokens back to entities on sale consolidation.
     * @dev This function is only callable by active Endaoment portfolios.
     * @dev Portfolio caller must pre `approve` this contract to transfer the desired amount.
     */
    function transferFromPortfolio(Entity _entity, uint256 _amount)
        external
        isActivePortfolioCaller
        isActiveEntity(_entity)
    {
        _transferToEntity(msg.sender, _entity, _amount);
    }

    /**
     * Batch transfer base token from an Endaoment portfolio to Endaoment entities.
     * @param _entityTransfers The entity transfers to make.
     * @notice This functions exists so Endaoment portfolios can transfer arbitrary amounts of base tokens to entities without triggering fees.
     * @dev This function is only callable by active Endaoment portfolios.
     * @dev Portfolio caller must pre `approve` this contract to transfer the desired amount.
     */
    function batchTransferFromPortfolio(EntityTransfer[] calldata _entityTransfers) external isActivePortfolioCaller {
        for (uint256 i = 0; i < _entityTransfers.length; ++i) {
            EntityTransfer memory _transfer = _entityTransfers[i];
            _checkEntity(_transfer.entity);
            _transferToEntity(msg.sender, _transfer.entity, _transfer.amount);
        }
    }

    /**
     * Transfer base token to an Endaoment entity.
     * @param _from The address to transfer from.
     * @param _entity The entity to transfer to.
     * @param _amount The amount to transfer.
     */
    function _transferToEntity(address _from, Entity _entity, uint256 _amount) private {
        // Emit event
        emit TransferredToEntity(_from, _entity, _amount);

        // Update entity balance through receiving
        _entity.receiveTransfer(_amount);

        // Transfer to entity, transferring from approved balance from the caller
        registry.baseToken().safeTransferFrom(_from, address(_entity), _amount);
    }

    /**
     * Make arbitrary calls to other contracts as this contract.
     * @param _target The target contract.
     * @param _value The ETH value to send.
     * @param _data The calldata.
     * @return _response The response from the call.
     * @notice This function exists so Endaoment admins can make arbitrary calls to other contracts as this contract, specially if to unlock incorrectly sent assets.
     */
    function callAsContract(address _target, uint256 _value, bytes memory _data)
        external
        payable
        requiresAuth
        returns (bytes memory)
    {
        (bool _success, bytes memory _response) = payable(_target).call{value: _value}(_data);
        if (!_success) revert CallFailed(_response);
        return _response;
    }
}

File 3 of 16 : TPlusNAsset.sol
//SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;

import {ERC20} from "solmate/tokens/ERC20.sol";

/// @title TPlusNAsset
/// @notice An ERC20 contract used by TPlusNPortfolio contracts to represent their underlying asset
contract TPlusNAsset is ERC20 {
    /// STATE

    /// @notice Address of the Endaoment Portfolio / Minter.
    address public portfolio;

    /// EVENTS
    event PortfolioSet(address indexed newPortfolio);

    /// ERRORS

    /// @notice Emitted when bad caller on portfolio-only calls.
    error OnlyPortfolio();

    constructor(string memory _name, string memory _symbol, address _portfolio) ERC20(_name, _symbol, 18) {
        portfolio = _portfolio;
        emit PortfolioSet(_portfolio);
    }

    /// MODIFIERS

    /**
     * @notice Make function only callable by the owning portfolio.
     */
    modifier onlyPortfolio() {
        _onlyPortfolio();
        _;
    }

    /**
     * @notice Internal function to check that the caller is the owning portfolio.
     * @dev Added for gas savings.
     */
    function _onlyPortfolio() private view {
        if (msg.sender != portfolio) revert OnlyPortfolio();
    }

    /**
     * @notice Mint assets for a given address.
     * @param _to The address to mint to.
     * @param _amount The amount to mint.
     * @dev Only callable by the owning portfolio.
     */
    function mint(address _to, uint256 _amount) external onlyPortfolio {
        _mint(_to, _amount);
    }

    /**
     * @notice Burn assets from a given address.
     * @param _from The address to burn from.
     * @param _amount The amount to burn.
     * @dev Only callable by the owning portfolio.
     */
    function burn(address _from, uint256 _amount) external onlyPortfolio {
        _burn(_from, _amount);
    }

    /**
     * @notice Burn assets from the caller.
     * @param _amount The amount to burn.
     * @dev Callable by anyone since it burns from the caller's balance.
     */
    function burn(uint256 _amount) external {
        _burn(msg.sender, _amount);
    }

    /**
     * @notice Update the owning portfolio
     * @param _newPortfolio The new portfolio address
     * @notice Should be rarely used but can be in case of a portfolio migration, to be able to use the same asset contract
     * @dev Only callable by the current portfolio
     */
    function setPortfolio(address _newPortfolio) external onlyPortfolio {
        portfolio = _newPortfolio;
        emit PortfolioSet(_newPortfolio);
    }
}

File 4 of 16 : Registry.sol
//SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;

import {Math} from "./lib/Math.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {Auth, Authority} from "./lib/auth/Auth.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";

import {RegistryAuth} from "./RegistryAuth.sol";
import {Entity} from "./Entity.sol";
import {ISwapWrapper} from "./interfaces/ISwapWrapper.sol";
import {Portfolio} from "./Portfolio.sol";

// --- Errors ---
error Unauthorized();
error UnsupportedSwapper();

/**
 * @notice Registry entity - manages Factory and Entity state info.
 */
contract Registry is RegistryAuth {
    // --- Storage ---

    /// @notice Treasury address can receives fees.
    address public treasury;

    /// @notice Base Token address is the stable coin contract used throughout the system.
    ERC20 public immutable baseToken;

    /// @notice Mapping of approved factory contracts that are allowed to register new Entities.
    mapping(address => bool) public isApprovedFactory;
    /// @notice Mapping of active status of entities.
    mapping(Entity => bool) public isActiveEntity;

    /// @notice Maps entity type to donation fee percentage stored as a zoc, where type(uint32).max represents 0.
    mapping(uint8 => uint32) defaultDonationFee;
    /// @notice Maps specific entity receiver to donation fee percentage stored as a zoc.
    mapping(Entity => uint32) donationFeeReceiverOverride;

    /// @notice Maps entity type to payout fee percentage stored as a zoc, where type(uint32).max represents 0.
    mapping(uint8 => uint32) defaultPayoutFee;
    /// @notice Maps specific entity sender to payout fee percentage stored as a zoc.
    mapping(Entity => uint32) payoutFeeOverride;

    /// @notice Maps sender entity type to receiver entity type to fee percentage as a zoc.
    mapping(uint8 => mapping(uint8 => uint32)) defaultTransferFee;
    /// @notice Maps specific entity sender to receiver entity type to fee percentage as a zoc.
    mapping(Entity => mapping(uint8 => uint32)) transferFeeSenderOverride;
    /// @notice Maps sender entity type to specific entity receiver to fee percentage as a zoc.
    mapping(uint8 => mapping(Entity => uint32)) transferFeeReceiverOverride;
    /// @notice Maps swap wrappers to their enabled/disabled status.

    mapping(ISwapWrapper => bool) public isSwapperSupported;
    /// @notice Maps portfolios to their enabled/disabled status.
    mapping(Portfolio => bool) public isActivePortfolio;

    // --- Events ---

    /// @notice The event emitted when a factory is approved (whitelisted) or has it's approval removed.
    event FactoryApprovalSet(address indexed factory, bool isApproved);

    /// @notice The event emitted when an entity is set active or inactive.
    event EntityStatusSet(address indexed entity, bool isActive);

    /// @notice The event emitted when a swap wrapper is set active or inactive.
    event SwapWrapperStatusSet(address indexed swapWrapper, bool isSupported);

    /// @notice The event emitted when a portfolio is set active or inactive.
    event PortfolioStatusSet(address indexed portfolio, bool isActive);

    /// @notice Emitted when a default donation fee is set for an entity type.
    event DefaultDonationFeeSet(uint8 indexed entityType, uint32 fee);

    /// @notice Emitted when a donation fee override is set for a specific receiving entity.
    event DonationFeeReceiverOverrideSet(address indexed entity, uint32 fee);

    /// @notice Emitted when a default payout fee is set for an entity type.
    event DefaultPayoutFeeSet(uint8 indexed entityType, uint32 fee);

    /// @notice Emitted when a payout fee override is set for a specific sender entity.
    event PayoutFeeOverrideSet(address indexed entity, uint32 fee);

    /// @notice Emitted when a default transfer fee is set for transfers between entity types.
    event DefaultTransferFeeSet(uint8 indexed fromEntityType, uint8 indexed toEntityType, uint32 fee);

    /// @notice Emitted when a transfer fee override is set for transfers from an entity to a specific entityType.
    event TransferFeeSenderOverrideSet(address indexed fromEntity, uint8 indexed toEntityType, uint32 fee);

    /// @notice Emitted when a transfer fee override is set for transfers from an entityType to an entity.
    event TransferFeeReceiverOverrideSet(uint8 indexed fromEntityType, address indexed toEntity, uint32 fee);

    /// @notice Emitted when the registry treasury contract is changed
    event TreasuryChanged(address oldTreasury, address indexed newTreasury);

    /**
     * @notice Modifier for methods that require auth and that the manager cannot access.
     * @dev Overridden from Auth.sol. Reason: use custom error.
     */
    modifier requiresAuth() override {
        if (!isAuthorized(msg.sender, msg.sig)) revert Unauthorized();

        _;
    }

    // --- Constructor ---
    constructor(address _admin, address _treasury, ERC20 _baseToken) RegistryAuth(_admin, Authority(address(this))) {
        treasury = _treasury;
        emit TreasuryChanged(address(0), _treasury);
        baseToken = _baseToken;
    }

    // --- Internal fns ---

    /**
     * @notice Fee parsing to convert the special "type(uint32).max" value to zero, and zero to the "max".
     * @dev After converting, "type(uint32).max" will cause overflow/revert when used as a fee percentage multiplier and zero will mean no fee.
     * @param _value The value to be converted.
     * @return The parsed fee to use.
     */
    function _parseFeeWithFlip(uint32 _value) private pure returns (uint32) {
        if (_value == 0) {
            return type(uint32).max;
        } else if (_value == type(uint32).max) {
            return 0;
        } else {
            return _value;
        }
    }

    // --- External fns ---

    /**
     * @notice Sets a new Endaoment treasury address.
     * @param _newTreasury The new treasury.
     */
    function setTreasury(address _newTreasury) external requiresAuth {
        emit TreasuryChanged(treasury, _newTreasury);
        treasury = _newTreasury;
    }

    /**
     * @notice Sets the approval state of a factory. Grants the factory permissions to set entity status.
     * @param _factory The factory whose approval state is to be updated.
     * @param _isApproved True if the factory should be approved, false otherwise.
     */
    function setFactoryApproval(address _factory, bool _isApproved) external requiresAuth {
        isApprovedFactory[_factory] = _isApproved;
        emit FactoryApprovalSet(address(_factory), _isApproved);
    }

    /**
     * @notice Sets the enable/disable state of an Entity.
     * @param _entity The entity whose active state is to be updated.
     * @param _isActive True if the entity should be active, false otherwise.
     */
    function setEntityStatus(Entity _entity, bool _isActive) external requiresAuth {
        isActiveEntity[_entity] = _isActive;
        emit EntityStatusSet(address(_entity), _isActive);
    }

    /**
     * @notice Sets Entity as active. This is a special method to be called only by approved factories.
     * Other callers should use `setEntityStatus` instead.
     * @param _entity The entity.
     */
    function setEntityActive(Entity _entity) external {
        if (!isApprovedFactory[msg.sender]) revert Unauthorized();
        isActiveEntity[_entity] = true;
        emit EntityStatusSet(address(_entity), true);
    }

    /**
     * @notice Sets the enable/disable state of a Portfolio.
     * @param _portfolio Portfolio.
     * @param _isActive True if setting portfolio to active, false otherwise.
     */
    function setPortfolioStatus(Portfolio _portfolio, bool _isActive) external requiresAuth {
        isActivePortfolio[_portfolio] = _isActive;
        emit PortfolioStatusSet(address(_portfolio), _isActive);
    }

    /**
     * @notice Gets default donation fee pct (as a zoc) for an Entity.
     * @param _entity The receiving entity of the donation for which the fee is being fetched.
     * @return uint32 The default donation fee for the entity's type.
     * @dev Makes use of _parseFeeWithFlip, so if no default exists, "max" will be returned.
     */
    function getDonationFee(Entity _entity) external view returns (uint32) {
        return _parseFeeWithFlip(defaultDonationFee[_entity.entityType()]);
    }

    /**
     * @notice Gets lowest possible donation fee pct (as a zoc) for an Entity, among default and override.
     * @param _entity The receiving entity of the donation for which the fee is being fetched.
     * @return uint32 The minimum of the default donation fee and the receiver's fee override.
     * @dev Makes use of _parseFeeWithFlip, so if no default or override exists, "max" will be returned.
     */
    function getDonationFeeWithOverrides(Entity _entity) external view returns (uint32) {
        uint32 _default = _parseFeeWithFlip(defaultDonationFee[_entity.entityType()]);
        uint32 _receiverOverride = _parseFeeWithFlip(donationFeeReceiverOverride[_entity]);
        return _receiverOverride < _default ? _receiverOverride : _default;
    }

    /**
     * @notice Gets default payout fee pct (as a zoc) for an Entity.
     * @param _entity The sender entity of the payout for which the fee is being fetched.
     * @return uint32 The default payout fee for the entity's type.
     * @dev Makes use of _parseFeeWithFlip, so if no default exists, "max" will be returned.
     */
    function getPayoutFee(Entity _entity) external view returns (uint32) {
        return _parseFeeWithFlip(defaultPayoutFee[_entity.entityType()]);
    }

    /**
     * @notice Gets lowest possible payout fee pct (as a zoc) for an Entity, among default and override.
     * @param _entity The sender entity of the payout for which the fee is being fetched.
     * @return uint32 The minimum of the default payout fee and the sender's fee override.
     * @dev Makes use of _parseFeeWithFlip, so if no default or override exists, "max" will be returned.
     */
    function getPayoutFeeWithOverrides(Entity _entity) external view returns (uint32) {
        uint32 _default = _parseFeeWithFlip(defaultPayoutFee[_entity.entityType()]);
        uint32 _senderOverride = _parseFeeWithFlip(payoutFeeOverride[_entity]);
        return _senderOverride < _default ? _senderOverride : _default;
    }

    /**
     * @notice Gets default transfer fee pct (as a zoc) between sender & receiver Entities.
     * @param _sender The sending entity of the transfer for which the fee is being fetched.
     * @param _receiver The receiving entity of the transfer for which the fee is being fetched.
     * @return uint32 The default transfer fee.
     * @dev Makes use of _parseFeeWithFlip, so if no default exists, "type(uint32).max" will be returned.
     */
    function getTransferFee(Entity _sender, Entity _receiver) external view returns (uint32) {
        return _parseFeeWithFlip(defaultTransferFee[_sender.entityType()][_receiver.entityType()]);
    }

    /**
     * @notice Gets lowest possible transfer fee pct (as a zoc) between sender & receiver Entities, among default and overrides.
     * @param _sender The sending entity of the transfer for which the fee is being fetched.
     * @param _receiver The receiving entity of the transfer for which the fee is being fetched.
     * @return uint32 The minimum of the default transfer fee, and sender and receiver overrides.
     * @dev Makes use of _parseFeeWithFlip, so if no default or overrides exist, "type(uint32).max" will be returned.
     */
    function getTransferFeeWithOverrides(Entity _sender, Entity _receiver) external view returns (uint32) {
        uint32 _default = _parseFeeWithFlip(defaultTransferFee[_sender.entityType()][_receiver.entityType()]);
        uint32 _senderOverride = _parseFeeWithFlip(transferFeeSenderOverride[_sender][_receiver.entityType()]);
        uint32 _receiverOverride = _parseFeeWithFlip(transferFeeReceiverOverride[_sender.entityType()][_receiver]);

        uint32 _lowestFee = _default;
        _lowestFee = _senderOverride < _lowestFee ? _senderOverride : _lowestFee;
        _lowestFee = _receiverOverride < _lowestFee ? _receiverOverride : _lowestFee;
        return _lowestFee;
    }

    /**
     * @notice Sets the default donation fee for an entity type.
     * @param _entityType Entity type.
     * @param _fee The fee percentage to be set (a zoc).
     */
    function setDefaultDonationFee(uint8 _entityType, uint32 _fee) external requiresAuth {
        defaultDonationFee[_entityType] = _parseFeeWithFlip(_fee);
        emit DefaultDonationFeeSet(_entityType, _fee);
    }

    /**
     * @notice Sets the donation fee receiver override for a specific entity.
     * @param _entity Entity.
     * @param _fee The overriding fee (a zoc).
     */
    function setDonationFeeReceiverOverride(Entity _entity, uint32 _fee) external requiresAuth {
        donationFeeReceiverOverride[_entity] = _parseFeeWithFlip(_fee);
        emit DonationFeeReceiverOverrideSet(address(_entity), _fee);
    }

    /**
     * @notice Sets the default payout fee for an entity type.
     * @param _entityType Entity type.
     * @param _fee The fee percentage to be set (a zoc).
     */
    function setDefaultPayoutFee(uint8 _entityType, uint32 _fee) external requiresAuth {
        defaultPayoutFee[_entityType] = _parseFeeWithFlip(_fee);
        emit DefaultPayoutFeeSet(_entityType, _fee);
    }

    /**
     * @notice Sets the payout fee override for a specific entity.
     * @param _entity Entity.
     * @param _fee The overriding fee (a zoc).
     */
    function setPayoutFeeOverride(Entity _entity, uint32 _fee) external requiresAuth {
        payoutFeeOverride[_entity] = _parseFeeWithFlip(_fee);
        emit PayoutFeeOverrideSet(address(_entity), _fee);
    }

    /**
     * @notice Sets the default transfer fee for transfers from one specific entity type to another.
     * @param _fromEntityType The entityType making the transfer.
     * @param _toEntityType The receiving entityType.
     * @param _fee The transfer fee percentage (a zoc).
     */
    function setDefaultTransferFee(uint8 _fromEntityType, uint8 _toEntityType, uint32 _fee) external requiresAuth {
        defaultTransferFee[_fromEntityType][_toEntityType] = _parseFeeWithFlip(_fee);
        emit DefaultTransferFeeSet(_fromEntityType, _toEntityType, _fee);
    }

    /**
     * @notice Sets the transfer fee override for transfers from one specific entity to entities of a given type.
     * @param _fromEntity The entity making the transfer.
     * @param _toEntityType The receiving entityType.
     * @param _fee The overriding fee percentage (a zoc).
     */
    function setTransferFeeSenderOverride(Entity _fromEntity, uint8 _toEntityType, uint32 _fee) external requiresAuth {
        transferFeeSenderOverride[_fromEntity][_toEntityType] = _parseFeeWithFlip(_fee);
        emit TransferFeeSenderOverrideSet(address(_fromEntity), _toEntityType, _fee);
    }

    /**
     * @notice Sets the transfer fee override for transfers from entities of a given type to a specific entity.
     * @param _fromEntityType The entityType making the transfer.
     * @param _toEntity The receiving entity.
     * @param _fee The overriding fee percentage (a zoc).
     */
    function setTransferFeeReceiverOverride(uint8 _fromEntityType, Entity _toEntity, uint32 _fee)
        external
        requiresAuth
    {
        transferFeeReceiverOverride[_fromEntityType][_toEntity] = _parseFeeWithFlip(_fee);
        emit TransferFeeReceiverOverrideSet(_fromEntityType, address(_toEntity), _fee);
    }

    /**
     * @notice Sets the enable/disable state of a SwapWrapper. System owners must ensure meticulous review of SwapWrappers before approving them.
     * @param _swapWrapper A contract that implements ISwapWrapper.
     * @param _supported `true` if supported, `false` if unsupported.
     */
    function setSwapWrapperStatus(ISwapWrapper _swapWrapper, bool _supported) external requiresAuth {
        isSwapperSupported[_swapWrapper] = _supported;
        emit SwapWrapperStatusSet(address(_swapWrapper), _supported);
    }
}

File 5 of 16 : Entity.sol
//SPDX-License-Identifier: BSD 3-Clause
pragma solidity >=0.8.0;

import "solmate/tokens/ERC20.sol";
import "solmate/utils/SafeTransferLib.sol";
import "./lib/ReentrancyGuard.sol";

import {Registry} from "./Registry.sol";
import {ISwapWrapper} from "./interfaces/ISwapWrapper.sol";
import {EndaomentAuth} from "./lib/auth/EndaomentAuth.sol";
import {Portfolio} from "./Portfolio.sol";
import {Math} from "./lib/Math.sol";

error EntityInactive();
error PortfolioInactive();
error InsufficientFunds();
error InvalidAction();
error BalanceMismatch();
error CallFailed(bytes response);

/**
 * @notice Entity contract inherited by Org and Fund contracts (and all future kinds of Entities).
 */
abstract contract Entity is EndaomentAuth, ReentrancyGuard {
    using Math for uint256;
    using SafeTransferLib for ERC20;

    /// @notice The base registry to which the entity is connected.
    Registry public registry;

    /// @notice The entity's manager.
    address public manager;

    // @notice The base token used for tracking the entity's fund balance.
    ERC20 public baseToken;

    /// @notice The current balance for the entity, denominated in the base token's units.
    uint256 public balance;

    /// @notice Placeholder address used in swapping method to denote usage of ETH instead of a token.
    address public constant ETH_PLACEHOLDER = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;

    /// @notice Emitted when manager is set.
    event EntityManagerSet(address indexed oldManager, address indexed newManager);

    /// @notice Emitted when a donation is made.
    event EntityDonationReceived(
        address indexed from,
        address indexed to,
        address indexed tokenIn,
        uint256 amountIn,
        uint256 amountReceived,
        uint256 amountFee
    );

    /// @notice Emitted when a payout is made from an entity.
    event EntityValuePaidOut(address indexed from, address indexed to, uint256 amountSent, uint256 amountFee);

    /// @notice Emitted when a transfer is made between entities.
    event EntityValueTransferred(address indexed from, address indexed to, uint256 amountReceived, uint256 amountFee);

    /// @notice Emitted when a base token reconciliation completes
    event EntityBalanceReconciled(address indexed entity, uint256 amountReceived, uint256 amountFee);

    /// @notice Emitted when a base token balance is used to correct the internal contract balance.
    event EntityBalanceCorrected(address indexed entity, uint256 newBalance);

    /// @notice Emitted when a portfolio deposit is made.
    event EntityDeposit(address indexed portfolio, uint256 baseTokenDeposited, uint256 sharesReceived);

    /// @notice Emitted when a portfolio share redemption is made.
    event EntityRedeem(address indexed portfolio, uint256 sharesRedeemed, uint256 baseTokenReceived);

    /// @notice Emitted when ether is received.
    event EntityEthReceived(address indexed sender, uint256 amount);

    /**
     * @notice Modifier for methods that require auth and that the manager can access.
     * @dev Uses the same condition as `requiresAuth` but with added manager access.
     */
    modifier requiresManager() {
        if (msg.sender != manager && !isAuthorized(msg.sender, msg.sig)) revert Unauthorized();
        _;
    }

    /// @notice Each entity will implement this function to allow a caller to interrogate what kind of entity it is.
    function entityType() public pure virtual returns (uint8);

    /**
     * @notice One time method to be called at deployment to configure the contract. Required so Entity
     * contracts can be deployed as minimal proxies (clones).
     * @param _registry The registry to host the Entity.
     * @param _manager The address of the Entity's manager.
     */
    function __initEntity(Registry _registry, address _manager) internal {
        // Call to EndaomentAuth's initialize function ensures that this can't be called again
        __initEndaomentAuth(_registry, bytes20(bytes.concat("entity", bytes1(entityType()))));
        __initReentrancyGuard();
        registry = _registry;
        manager = _manager;
        baseToken = _registry.baseToken();
    }

    /**
     * @notice Set a new manager for this entity.
     * @param _manager Address of new manager.
     * @dev Callable by current manager or permissioned role.
     */
    function setManager(address _manager) external virtual requiresManager {
        emit EntityManagerSet(manager, _manager);
        manager = _manager;
    }

    /**
     * @notice Receives a donated amount of base tokens to be added to the entity's balance. Transfers default fee to treasury.
     * @param _amount Amount donated in base token.
     * @dev Reverts if the donation fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts if the token transfer fails.
     */
    function donate(uint256 _amount) external virtual {
        uint32 _feeMultiplier = registry.getDonationFee(this);
        _donateWithFeeMultiplier(_amount, _feeMultiplier);
    }

    /**
     * @notice Receives a donated amount of base tokens to be added to the entity's balance. Transfers default or overridden fee to treasury.
     * @param _amount Amount donated in base token.
     * @dev Reverts if the donation fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts if the token transfer fails.
     */
    function donateWithOverrides(uint256 _amount) external virtual {
        uint32 _feeMultiplier = registry.getDonationFeeWithOverrides(this);
        _donateWithFeeMultiplier(_amount, _feeMultiplier);
    }

    /**
     * @notice Receives a donated amount of base tokens to be added to the entity's balance.
     * This method can be called by permissioned actors to make a donation with a manually specified fee.
     * @param _amount Amount donated in base token.
     * @param _feeOverride Fee percentage as zoc.
     * @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts if the token transfer fails.
     * @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
     */
    function donateWithAdminOverrides(uint256 _amount, uint32 _feeOverride) external virtual requiresAuth {
        _donateWithFeeMultiplier(_amount, _feeOverride);
    }

    /**
     * @notice Receives a donated amount of base tokens to be added to the entity's balance. Transfers fee calculated by fee multiplier to treasury.
     * @param _amount Amount donated in base token.
     * @param _feeMultiplier Value indicating the percentage of the Endaoment donation fee to go to the Endaoment treasury.
     * @dev Reverts if the donation fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts if the token transfer fails.
     */
    function _donateWithFeeMultiplier(uint256 _amount, uint32 _feeMultiplier) internal virtual {
        (uint256 _netAmount, uint256 _fee) = _calculateFee(_amount, _feeMultiplier);
        baseToken.safeTransferFrom(msg.sender, registry.treasury(), _fee);
        baseToken.safeTransferFrom(msg.sender, address(this), _netAmount);

        unchecked {
            // unchecked as no possibility of overflow with baseToken precision
            balance += _netAmount;
        }
        emit EntityDonationReceived(msg.sender, address(this), address(baseToken), _amount, _amount, _fee);
    }

    /**
     * @notice Receive a donated amount of ETH or ERC20 tokens, swaps them to base tokens, and adds the output to the
     * entity's balance. Fee calculated using default rate and sent to treasury.
     * @param _swapWrapper The swap wrapper to use for the donation. Must be whitelisted on the Registry.
     * @param _tokenIn The address of the ERC20 token to swap and donate, or ETH_PLACEHOLDER if donating ETH.
     * @param _amountIn The amount of tokens or ETH being swapped and donated.
     * @param _data Additional call data required by the ISwapWrapper being used.
     */
    function swapAndDonate(ISwapWrapper _swapWrapper, address _tokenIn, uint256 _amountIn, bytes calldata _data)
        external
        payable
        virtual
    {
        uint32 _feeMultiplier = registry.getDonationFee(this);
        _swapAndDonateWithFeeMultiplier(_swapWrapper, _tokenIn, _amountIn, _data, _feeMultiplier);
    }

    /**
     * @notice Receive a donated amount of ETH or ERC20 tokens, swaps them to base tokens, and adds the output to the
     * entity's balance. Fee calculated using override rate and sent to treasury.
     * @param _swapWrapper The swap wrapper to use for the donation. Must be whitelisted on the Registry.
     * @param _tokenIn The address of the ERC20 token to swap and donate, or ETH_PLACEHOLDER if donating ETH.
     * @param _amountIn The amount of tokens or ETH being swapped and donated.
     * @param _data Additional call data required by the ISwapWrapper being used.
     */
    function swapAndDonateWithOverrides(
        ISwapWrapper _swapWrapper,
        address _tokenIn,
        uint256 _amountIn,
        bytes calldata _data
    ) external payable virtual {
        uint32 _feeMultiplier = registry.getDonationFeeWithOverrides(this);
        _swapAndDonateWithFeeMultiplier(_swapWrapper, _tokenIn, _amountIn, _data, _feeMultiplier);
    }

    /// @dev Internal helper implementing swap and donate functionality for any fee multiplier provided.
    function _swapAndDonateWithFeeMultiplier(
        ISwapWrapper _swapWrapper,
        address _tokenIn,
        uint256 _amountIn,
        bytes calldata _data,
        uint32 _feeMultiplier
    ) internal virtual nonReentrant {
        if (!registry.isSwapperSupported(_swapWrapper)) revert InvalidAction();

        // THINK: do we need a re-entrancy guard on this method?
        if (_tokenIn != ETH_PLACEHOLDER) {
            ERC20(_tokenIn).safeTransferFrom(msg.sender, address(this), _amountIn);
            ERC20(_tokenIn).safeApprove(address(_swapWrapper), 0);
            ERC20(_tokenIn).safeApprove(address(_swapWrapper), _amountIn);
        }

        uint256 _amountOut =
            _swapWrapper.swap{value: msg.value}(_tokenIn, address(baseToken), address(this), _amountIn, _data);

        (uint256 _netAmount, uint256 _fee) = _calculateFee(_amountOut, _feeMultiplier);

        baseToken.safeTransfer(registry.treasury(), _fee);

        unchecked {
            // unchecked as no possibility of overflow with baseToken precision
            balance += _netAmount;
        }

        if (balance > baseToken.balanceOf(address(this))) revert BalanceMismatch();

        emit EntityDonationReceived(msg.sender, address(this), _tokenIn, _amountIn, _amountOut, _fee);
    }

    /**
     * @notice Transfers an amount of base tokens from one entity to another. Transfers default fee to treasury.
     * @param _to The entity to receive the tokens.
     * @param _amount Contains the amount being donated (denominated in the base token's units).
     * @dev Reverts if the entity is inactive or if the token transfer fails.
     * @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts with `Unauthorized` if the `msg.sender` is not the entity manager or a privileged role.
     * @dev Renamed from `transfer` to distinguish from ERC20 transfer in 3rd party tools.
     */
    function transferToEntity(Entity _to, uint256 _amount) external virtual requiresManager {
        uint32 _feeMultiplier = registry.getTransferFee(this, _to);
        _transferWithFeeMultiplier(_to, _amount, _feeMultiplier);
    }

    /**
     * @notice Transfers an amount of base tokens from one entity to another. Transfers default or overridden fee to treasury.
     * @param _to The entity to receive the tokens.
     * @param _amount Contains the amount being donated (denominated in the base token's units).
     * @dev Reverts if the entity is inactive or if the token transfer fails.
     * @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts with `Unauthorized` if the `msg.sender` is not the entity manager or a privileged role.
     */
    function transferToEntityWithOverrides(Entity _to, uint256 _amount) external virtual requiresManager {
        uint32 _feeMultiplier = registry.getTransferFeeWithOverrides(this, _to);
        _transferWithFeeMultiplier(_to, _amount, _feeMultiplier);
    }

    /**
     * @notice Transfers an amount of base tokens from one entity to another. Transfers fee specified by a privileged role.
     * @param _to The entity to receive the tokens.
     * @param _amount Contains the amount being donated (denominated in the base token's units).
     * @param _feeOverride Admin override configured by an Admin
     * @dev Reverts if the entity is inactive or if the token transfer fails.
     * @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
     */
    function transferToEntityWithAdminOverrides(Entity _to, uint256 _amount, uint32 _feeOverride)
        external
        virtual
        requiresAuth
    {
        _transferWithFeeMultiplier(_to, _amount, _feeOverride);
    }

    /**
     * @notice Transfers an amount of base tokens from one entity to another. Transfers fee calculated by fee multiplier to treasury.
     * @param _to The entity to receive the tokens.
     * @param _amount Contains the amount being donated (denominated in the base token's units).
     * @param _feeMultiplier Value indicating the percentage of the Endaoment donation fee to go to the Endaoment treasury.
     * @dev Reverts with 'Inactive' if the entity sending the transfer or the entity receiving the transfer is inactive.
     * @dev Reverts if the transfer fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts with `Unauthorized` if the `msg.sender` is not the entity manager or a privileged role.
     * @dev Reverts if the token transfer fails.
     */
    function _transferWithFeeMultiplier(Entity _to, uint256 _amount, uint32 _feeMultiplier) internal virtual {
        if (!registry.isActiveEntity(this) || !registry.isActiveEntity(_to)) revert EntityInactive();
        if (balance < _amount) revert InsufficientFunds();

        (uint256 _netAmount, uint256 _fee) = _calculateFee(_amount, _feeMultiplier);
        baseToken.safeTransfer(registry.treasury(), _fee);
        baseToken.safeTransfer(address(_to), _netAmount);

        unchecked {
            // unchecked as no possibility of overflow with baseToken precision
            balance -= _amount;
            _to.receiveTransfer(_netAmount);
        }
        emit EntityValueTransferred(address(this), address(_to), _amount, _fee);
    }

    /**
     * @notice Updates the receiving entity balance on a transfer.
     * @param _transferAmount The amount being received on the transfer.
     * @dev This function is external, but is restricted such that it can only be called by other entities.
     */
    function receiveTransfer(uint256 _transferAmount) external virtual {
        if (!registry.isActiveEntity(Entity(payable(msg.sender)))) revert EntityInactive();
        unchecked {
            // Cannot overflow with realistic balances.
            balance += _transferAmount;
        }
    }

    /**
     * @notice Deposits an amount of Entity's `baseToken` into an Endaoment-approved Portfolio.
     * @param _portfolio An Endaoment-approved portfolio.
     * @param _amount Amount of `baseToken` to deposit into the portfolio.
     * @param _data Data required by a portfolio to deposit.
     * @return _shares Amount of portfolio share tokens Entity received as a result of this deposit.
     */
    function portfolioDeposit(Portfolio _portfolio, uint256 _amount, bytes calldata _data)
        external
        virtual
        requiresManager
        returns (uint256)
    {
        if (!registry.isActivePortfolio(_portfolio)) revert PortfolioInactive();
        balance -= _amount;
        baseToken.safeApprove(address(_portfolio), _amount);
        uint256 _shares = _portfolio.deposit(_amount, _data);
        emit EntityDeposit(address(_portfolio), _amount, _shares);
        return _shares;
    }

    /**
     * @notice Redeems an amount of Entity's portfolio shares for an amount of `baseToken`.
     * @param _portfolio An Endaoment-approved portfolio.
     * @param _shares Amount of share tokens to redeem.
     * @param _data Data required by a portfolio to redeem.
     * @return _received Amount of `baseToken` Entity received as a result of this redemption.
     */
    function portfolioRedeem(Portfolio _portfolio, uint256 _shares, bytes calldata _data)
        external
        virtual
        requiresManager
        returns (uint256)
    {
        if (!registry.isActivePortfolio(_portfolio)) revert PortfolioInactive();
        uint256 _received = _portfolio.redeem(_shares, _data);
        // unchecked: a realistic balance can never overflow a uint256
        unchecked {
            balance += _received;
        }
        emit EntityRedeem(address(_portfolio), _shares, _received);
        return _received;
    }

    /**
     * @notice This method should be called to reconcile the Entity's internal baseToken accounting with the baseToken contract's accounting.
     * There are a 2 situations where calling this method is appropriate:
     * 1. To process amounts of baseToken that arrived at this Entity through methods besides Entity:donate or Entity:transfer. For example,
     * if this Entity receives a normal ERC20 transfer of baseToken, the amount received will be unavailable for Entity use until this method
     * is called to adjust the balance and process fees. OrgFundFactory.sol:_donate makes use of this method to do this as well.
     * 2. Unusually, the Entity's perspective of balance could be lower than `baseToken.balanceOf(this)`. This could happen if
     * Entity:callAsEntity is used to transfer baseToken. In this case, this method provides a way of correcting the Entity's internal balance.
     */
    function reconcileBalance() external virtual {
        uint256 _tokenBalance = baseToken.balanceOf(address(this));

        if (_tokenBalance >= balance) {
            uint256 _sweepAmount = _tokenBalance - balance;
            uint32 _feeMultiplier = registry.getDonationFeeWithOverrides(this);
            (uint256 _netAmount, uint256 _fee) = _calculateFee(_sweepAmount, _feeMultiplier);

            baseToken.safeTransfer(registry.treasury(), _fee);
            unchecked {
                balance += _netAmount;
            }
            emit EntityBalanceReconciled(address(this), _sweepAmount, _fee);
        } else {
            // Handle abnormal scenario where _tokenBalance < balance (see method docs)
            balance = _tokenBalance;
            emit EntityBalanceCorrected(address(this), _tokenBalance);
        }
    }

    /**
     * @notice Takes stray tokens or ETH sent directly to this Entity, swaps them for base token, then adds them to the
     * Entity's balance after paying the appropriate fee to the treasury.
     * @param _swapWrapper The swap wrapper to use to convert the assets. Must be whitelisted on the Registry.
     * @param _tokenIn The address of the ERC20 token to swap, or ETH_PLACEHOLDER if ETH.
     * @param _amountIn The amount of tokens or ETH being swapped and added to the balance.
     * @param _data Additional call data required by the ISwapWrapper being used.
     */
    function swapAndReconcileBalance(
        ISwapWrapper _swapWrapper,
        address _tokenIn,
        uint256 _amountIn,
        bytes calldata _data
    ) external virtual nonReentrant requiresManager {
        if (!registry.isSwapperSupported(_swapWrapper)) revert InvalidAction();

        uint32 _feeMultiplier = registry.getDonationFeeWithOverrides(this);

        if (_tokenIn != ETH_PLACEHOLDER) {
            ERC20(_tokenIn).safeApprove(address(_swapWrapper), 0);
            ERC20(_tokenIn).safeApprove(address(_swapWrapper), _amountIn);
        }

        // Send value only if token in is ETH
        uint256 _value = _tokenIn == ETH_PLACEHOLDER ? _amountIn : 0;

        uint256 _amountOut =
            _swapWrapper.swap{value: _value}(_tokenIn, address(baseToken), address(this), _amountIn, _data);

        (uint256 _netAmount, uint256 _fee) = _calculateFee(_amountOut, _feeMultiplier);
        baseToken.safeTransfer(registry.treasury(), _fee);

        unchecked {
            // unchecked as no possibility of overflow with baseToken precision
            balance += _netAmount;
        }

        if (balance > baseToken.balanceOf(address(this))) revert BalanceMismatch();

        emit EntityBalanceReconciled(address(this), _amountOut, _fee);
    }

    /**
     * @notice Permissioned method that allows Endaoment admin to make arbitrary calls acting as this Entity.
     * @param _target The address to which the call will be made.
     * @param _value The ETH value that should be forwarded with the call.
     * @param _data The calldata that will be sent with the call.
     * @return _return The data returned by the call.
     */
    function callAsEntity(address _target, uint256 _value, bytes memory _data)
        external
        payable
        virtual
        requiresAuth
        returns (bytes memory)
    {
        (bool _success, bytes memory _response) = payable(_target).call{value: _value}(_data);
        if (!_success) revert CallFailed(_response);
        return _response;
    }

    /**
     * @notice Pays out an amount of base tokens from the entity to an address. Transfers the fee calculated by the
     * default fee multiplier to the treasury.
     * @param _to The address to receive the tokens.
     * @param _amount Amount donated in base token.
     * @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
     * @dev Reverts if the fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts if the token transfer fails.
     */
    function payout(address _to, uint256 _amount) external virtual requiresAuth {
        uint32 _feeMultiplier = registry.getPayoutFee(this);
        _payoutWithFeeMultiplier(_to, _amount, _feeMultiplier);
    }

    /**
     * @notice Pays out an amount of base tokens from the entity to an address. Transfers the fee calculated by the
     * default fee multiplier to the treasury.
     * @param _amount Amount donated in base token.
     * @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
     * @dev Reverts if the fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts if the token transfer fails.
     */
    function payoutWithOverrides(address _to, uint256 _amount) external virtual requiresAuth {
        uint32 _feeMultiplier = registry.getPayoutFeeWithOverrides(this);
        _payoutWithFeeMultiplier(_to, _amount, _feeMultiplier);
    }

    /**
     * @notice Pays out an amount of base tokens from the entity to an address. Transfers fee specified by a privileged role.
     * @param _amount Amount donated in base token.
     * @param _feeOverride Payout override configured by an Admin
     * @dev Reverts with `Unauthorized` if the `msg.sender` is not a privileged role.
     * @dev Reverts if the fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     * @dev Reverts if the token transfer fails.
     */
    function payoutWithAdminOverrides(address _to, uint256 _amount, uint32 _feeOverride)
        external
        virtual
        requiresAuth
    {
        _payoutWithFeeMultiplier(_to, _amount, _feeOverride);
    }

    /**
     * @notice Pays out an amount of base tokens from the entity to an address. Transfers the fee calculated by fee multiplier to the treasury.
     * @param _to The address to receive the tokens.
     * @param _amount Contains the amount being paid out (denominated in the base token's units).
     * @param _feeMultiplier Value indicating the percentage of the Endaoment fee to go to the Endaoment treasury.
     * @dev Reverts if the token transfer fails.
     * @dev Reverts if the fee percentage is larger than 100% (equal to 1e4 when represented as a zoc).
     */
    function _payoutWithFeeMultiplier(address _to, uint256 _amount, uint32 _feeMultiplier) internal virtual {
        if (balance < _amount) revert InsufficientFunds();

        (uint256 _netAmount, uint256 _fee) = _calculateFee(_amount, _feeMultiplier);
        baseToken.safeTransfer(registry.treasury(), _fee);
        baseToken.safeTransfer(address(_to), _netAmount);

        unchecked {
            // unchecked because we've already validated that amount is less than or equal to the balance
            balance -= _amount;
        }
        emit EntityValuePaidOut(address(this), _to, _amount, _fee);
    }

    /// @dev Internal helper method to calculate the fee on a base token amount for a given fee multiplier.
    function _calculateFee(uint256 _amount, uint256 _feeMultiplier)
        internal
        pure
        virtual
        returns (uint256 _netAmount, uint256 _fee)
    {
        if (_feeMultiplier > Math.ZOC) revert InvalidAction();
        unchecked {
            // unchecked as no possibility of overflow with baseToken precision
            _fee = _amount.zocmul(_feeMultiplier);
            // unchecked as the _feeMultiplier check with revert above protects against overflow
            _netAmount = _amount - _fee;
        }
    }

    receive() external payable virtual {
        emit EntityEthReceived(msg.sender, msg.value);
    }
}

File 6 of 16 : Portfolio.sol
//SPDX-License-Identifier: BSD 3-Clause
pragma solidity >=0.8.0;

import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";
import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol";
import {Registry} from "./Registry.sol";
import {Entity} from "./Entity.sol";
import {EndaomentAuth} from "./lib/auth/EndaomentAuth.sol";
import {Math} from "./lib/Math.sol";

abstract contract Portfolio is ERC20, EndaomentAuth, ReentrancyGuard {
    using Math for uint256;
    using SafeTransferLib for ERC20;

    Registry public immutable registry;
    bool public immutable async;
    uint256 public cap;
    address public feeTreasury;
    uint256 public depositFee;
    uint256 public redemptionFee;
    address public immutable asset;
    address public immutable receiptAsset;
    ERC20 public immutable baseToken;
    bool public didShutdown;
    uint256 public timestampAumFeesTaken;
    uint256 public aumRate;
    uint256 internal constant MAX_AUM_RATE = 3168808782;

    error InvalidSwapper();
    error InvalidRate();
    error TransferDisallowed();
    error DepositAfterShutdown();
    error DidShutdown();
    error NotEntity();
    error BadCheckCapImplementation();
    error ExceedsCap();
    error PercentageOver100();
    error RoundsToZero();
    error Slippage();
    error CallFailed(bytes response);

    /// @notice `sender` has exchanged `assets` (after fees) for `shares`, and transferred those `shares` to `receiver`.
    /// The sender paid a total of `depositAmount` and was charged `fee` for the transaction.
    event Deposit(
        address indexed sender,
        address indexed receiver,
        uint256 assets,
        uint256 shares,
        uint256 depositAmount,
        uint256 fee
    );

    /// @notice `sender` has exchanged `shares` for `assets`, and transferred those `assets` to `receiver`.
    /// The sender received a net of `redeemedAmount` after the conversion of `assets` into base tokens
    /// and was charged `fee` for the transaction.
    event Redeem(
        address indexed sender,
        address indexed receiver,
        uint256 assets,
        uint256 shares,
        uint256 redeemedAmount,
        uint256 fee
    );

    /// @notice Event emitted when `cap` is set.
    event CapSet(uint256 cap);

    /// @notice Event emitted when `depositFee` is set.
    event DepositFeeSet(uint256 fee);

    /// @notice Event emitted when `redemptionFee` is set.
    event RedemptionFeeSet(uint256 fee);

    /// @notice Event emitted when `feeTreasury` is set.
    event FeeTreasurySet(address feeTreasury);

    /// @notice Event emitted when management takes fees.
    event FeesTaken(uint256 amount);

    /// @notice Event emitted when AUM fees are taken.
    event AumFeesTaken(uint256 feeAmount, uint256 timeDelta);

    /// @notice Event emitted when `aumRate` is set.
    event AumRateSet(uint256 rate);

    /// @notice Event emitted when admin forcefully swaps portfolio asset balance for baseToken.
    event Shutdown(uint256 assetAmount, uint256 baseTokenOut);

    /**
     * @param _registry Endaoment registry.
     * @param _receiptAsset Address of token that the portfolio receives from a deposit.
     * @param _name Name of the ERC20 Portfolio share tokens.
     * @param _async Whether the portfolio is async for deposits and redeems. Typically used for T+N portfolios
     * @param _symbol Symbol of the ERC20 Portfolio share tokens.
     * @param _cap Amount in baseToken that value of totalAssets should not exceed.
     * @param _depositFee Percentage fee as ZOC that will go to treasury on asset deposit.
     * @param _redemptionFee Percentage fee as ZOC that will go to treasury on share redemption.
     * @param _aumRate Percentage fee per second (as WAD) that should accrue to treasury as AUM fee. (1e16 = 1%).
     */
    constructor(
        Registry _registry,
        address _receiptAsset,
        string memory _name,
        string memory _symbol,
        bool _async,
        uint256 _cap,
        address _feeTreasury,
        uint256 _depositFee,
        uint256 _redemptionFee,
        uint256 _aumRate
    ) ERC20(_name, _symbol, ERC20(_getAsset(_receiptAsset)).decimals()) {
        __initEndaomentAuth(_registry, "portfolio");
        registry = _registry;

        async = _async;

        feeTreasury = _feeTreasury;
        emit FeeTreasurySet(_feeTreasury);

        if (_redemptionFee > Math.ZOC) revert PercentageOver100();
        redemptionFee = _redemptionFee;
        emit RedemptionFeeSet(_redemptionFee);

        if (_depositFee > Math.ZOC) revert PercentageOver100();
        depositFee = _depositFee;
        emit DepositFeeSet(_depositFee);

        cap = _cap;
        emit CapSet(_cap);

        receiptAsset = _receiptAsset;
        asset = _getAsset(_receiptAsset);
        baseToken = registry.baseToken();

        if (_aumRate > MAX_AUM_RATE) revert InvalidRate();
        aumRate = _aumRate;
        emit AumRateSet(_aumRate);

        timestampAumFeesTaken = block.timestamp;
    }

    /**
     * @notice Returns the underlying asset for the `receiptAsset`.
     * @param _receiptAsset Address of token that the portfolio receives from a deposit.
     * @return Address of the underlying asset.
     */
    function _getAsset(address _receiptAsset) internal view virtual returns (address);

    /**
     * @notice Function used to determine whether an Entity is active on the registry.
     * @param _entity The Entity.
     */
    function _isEntity(Entity _entity) internal view returns (bool) {
        return registry.isActiveEntity(_entity);
    }

    /**
     * @notice Set the Portfolio cap.
     * @param _amount Amount, denominated in baseToken.
     */
    function setCap(uint256 _amount) external requiresAuth {
        cap = _amount;
        emit CapSet(_amount);
    }

    /**
     * @notice Set deposit fee.
     * @param _pct Percentage as ZOC (e.g. 1000 = 10%).
     */
    function setDepositFee(uint256 _pct) external requiresAuth {
        if (_pct > Math.ZOC) revert PercentageOver100();
        depositFee = _pct;
        emit DepositFeeSet(_pct);
    }

    /**
     * @notice Set redemption fee.
     * @param _pct Percentage as ZOC (e.g. 1000 = 10%).
     */
    function setRedemptionFee(uint256 _pct) external requiresAuth {
        if (_pct > Math.ZOC) revert PercentageOver100();
        redemptionFee = _pct;
        emit RedemptionFeeSet(_pct);
    }

    /**
     * @notice Set fee treasury.
     * @param _feeTreasury Address of the treasury that should receive fees.
     *
     */
    function setFeeTreasury(address _feeTreasury) external requiresAuth {
        feeTreasury = _feeTreasury;
        emit FeeTreasurySet(_feeTreasury);
    }

    /**
     * @notice Set AUM rate.
     * @param _pct Percentage *per second* as WAD (e.g. .01e18 / 365.25 days = 1% per year).
     */
    function setAumRate(uint256 _pct) external requiresAuth {
        // check to make sure _pct isn't above 10% over a year (.1e18 / 365.25 days = 3168808782 per second)
        if (_pct > MAX_AUM_RATE) revert InvalidRate();
        takeAumFees();
        aumRate = _pct;
        emit AumRateSet(_pct);
    }

    /**
     * @notice Total amount of the underlying asset that is managed by the Portfolio.
     * @return Total amount of the underlying asset.
     */
    function totalAssets() public view returns (uint256) {
        return convertReceiptAssetsToAssets(totalReceiptAssets());
    }

    /**
     * @notice Total amount of the receipt asset that is managed by the Portfolio.
     * @return Total amount of the receipt asset.
     */
    function totalReceiptAssets() public view returns (uint256) {
        return ERC20(receiptAsset).balanceOf(address(this));
    }

    /**
     * @notice Calculates the equivalent amount of assets for the given amount of receipt assets.
     * @param _receiptAssets Amount of receipt assets to convert.
     * @return Amount of assets.
     */
    function convertReceiptAssetsToAssets(uint256 _receiptAssets) public view virtual returns (uint256);

    /**
     * @notice Takes some amount of receipt assets from this portfolio as management fee.
     * @param _amountReceiptAssets Amount of receipt assets to take.
     */
    function takeFees(uint256 _amountReceiptAssets) external requiresAuth {
        ERC20(receiptAsset).safeTransfer(feeTreasury, _amountReceiptAssets);
        emit FeesTaken(_amountReceiptAssets);
    }

    /**
     * @notice Takes accrued percentage of assets from this portfolio as AUM fee.
     */
    function takeAumFees() public {
        if (didShutdown) return _takeAumFeesShutdown();
        uint256 _totalReceiptAssets = totalReceiptAssets();
        uint256 _period = block.timestamp - timestampAumFeesTaken;
        uint256 _feeAmount = _calculateAumFee(_totalReceiptAssets, _period);
        if (_feeAmount > _totalReceiptAssets) _feeAmount = _totalReceiptAssets;
        if (_feeAmount > 0 || totalSupply == 0) {
            // in either case, we want to set `timestampAumFeesTaken`...
            timestampAumFeesTaken = block.timestamp;
            // but we only want to transfer/emit on non-zero amount
            if (_feeAmount > 0) {
                ERC20(receiptAsset).safeTransfer(feeTreasury, _feeAmount);
                emit AumFeesTaken(_feeAmount, _period);
            }
        }
    }

    /**
     * @notice Takes accrued percentage of post-shutdown baseToken from this portfolio as AUM fee.
     */
    function _takeAumFeesShutdown() internal {
        uint256 _totalAssets = baseToken.balanceOf(address(this));
        uint256 _period = block.timestamp - timestampAumFeesTaken;
        uint256 _feeAmount = _calculateAumFee(_totalAssets, _period);
        if (_feeAmount > _totalAssets) _feeAmount = _totalAssets;
        // in `takeAumFees`, the following conditional checks totalSupply as well, solving a first deposit corner case.
        // In this case, we don't need to check, because deposits aren't allowed after shutdown.
        if (_feeAmount > 0) {
            timestampAumFeesTaken = block.timestamp;
            baseToken.safeTransfer(feeTreasury, _feeAmount);
            emit AumFeesTaken(_feeAmount, _period);
        }
    }

    /**
     * @notice Exchange `_amountBaseToken` for some amount of Portfolio shares.
     * @param _amountBaseToken The amount of the Entity's baseToken to deposit.
     * @param _data Data that the portfolio needs to make the deposit. In some cases, this will be swap parameters.
     * The first 32 bytes of this data should be the ABI-encoded `minSharesOut`.
     * @return shares The amount of shares that this deposit yields to the Entity.
     * @dev If the portfolio is `async`, shares will not be minted on deposit. Instead, each async
     * portfolio will have a unique implementation that will handle the minting of those shares
     * elsewhere e.g. T+N portfolios perform minting in consolidations.
     */
    function deposit(uint256 _amountBaseToken, bytes calldata _data) external nonReentrant returns (uint256) {
        // All portfolios should revert on deposit after shutdown
        if (didShutdown) revert DepositAfterShutdown();

        // All portfolios should revert on a deposit from a non-entity (or inactive one)
        if (!_isEntity(Entity(payable(msg.sender)))) revert NotEntity();

        // All portfolios should take AUM fees
        takeAumFees();

        // All portfolios should make a deposit
        // All transferring of baseToken and share calculation should occur inside _deposit
        // TODO: move fee taking logic here instead of `_deposit` for all portfolios and update tests
        (uint256 _shares, uint256 _assets, uint256 _fee) = _deposit(_amountBaseToken, _data);

        // Only sync portfolios require minting and share amount checking on deposit
        if (!async) {
            if (_shares < abi.decode(_data, (uint256))) revert Slippage();
            if (_shares == 0) revert RoundsToZero();

            // mint shares
            _mint(msg.sender, _shares);
        }

        // And check cap
        _checkCap();

        // And emit an event
        emit Deposit(msg.sender, msg.sender, _assets, _shares, _amountBaseToken, _fee);

        return _shares;
    }

    /**
     * @notice Check to make sure the cap has not been exceeded.
     * @dev Most portfolios have the same asset and baseToken, so the _checkCap implementation here is written to accomodate
     * that situation. For portfolios where that is not the case, this method needs to be overwritten to ensure the cap
     * (denominated in baseToken) is properly compared to the number of assets.
     */
    function _checkCap() internal virtual {
        if (asset != address(baseToken)) revert BadCheckCapImplementation();
        if (totalAssets() > cap) revert ExceedsCap();
    }

    /**
     * @notice Exchange `_amountIn` for some amount of Portfolio shares.
     * @dev Should include the transferring of baseToken and conversion to shares.
     * @param _amountIn The amount of the Entity's baseToken to deposit.
     * @param _data Data that the portfolio needs to make the deposit. In some cases, this will be swap parameters.
     * @return shares The amount of shares that this deposit yields to the Entity.
     * @return assets The amount of assets that this deposit yields to the portfolio.
     * @return fee The baseToken fee that this deposit yields to the treasury.
     */
    function _deposit(uint256 _amountIn, bytes calldata _data)
        internal
        virtual
        returns (uint256 shares, uint256 assets, uint256 fee);

    /**
     * @notice Exchange `_amountShares` for some amount of baseToken.
     * @param _amountShares The amount of the Entity's portfolio shares to exchange.
     * @param _data Data that the portfolio needs to make the redemption. In some cases, this will be swap parameters.
     * @return baseTokenOut The amount of baseToken that this redemption yields to the Entity.
     */
    function redeem(uint256 _amountShares, bytes calldata _data)
        external
        nonReentrant
        returns (uint256 /* baseTokenOut */ )
    {
        // All redeems should take AUM fees
        takeAumFees();

        // All portfolios should handle redemption after shutdown
        if (didShutdown) return _redeemShutdown(_amountShares);

        // All portfolios should handle the actual redeem of shares
        (uint256 _assetsOut, uint256 _baseTokenOut) = _redeem(_amountShares, _data);

        // All portfolios should burn the redeemed shares from the caller
        _burn(msg.sender, _amountShares);

        // Portfolios must signal amount of assets being redeemed, which must be non-zero
        if (_assetsOut == 0) revert RoundsToZero();

        // Any portfolio that outputs base token should transfer to caller and charge fee for treasury
        uint256 _netAmount;
        uint256 _fee;
        if (_baseTokenOut > 0) {
            (_netAmount, _fee) = _calculateFee(_baseTokenOut, redemptionFee);
            baseToken.safeTransfer(feeTreasury, _fee);
            baseToken.safeTransfer(msg.sender, _netAmount);
        }

        // And emit an event
        emit Redeem(msg.sender, msg.sender, _assetsOut, _amountShares, _netAmount, _fee);

        return _netAmount;
    }

    /**
     * @notice Exchange `_amountShares` for some amount of Portfolio assets.
     * @param _amountShares The amount of portfolio shares to exchange.
     * @param _data Data that the portfolio needs to redeem the assets. In some cases, this will be swap parameters.
     * @return assetsOut The amount of assets that this redemption yielded (and then converted to baseToken).
     * @return baseTokenOut Amount in baseToken to which these assets were converted.
     */
    function _redeem(uint256 _amountShares, bytes calldata _data)
        internal
        virtual
        returns (uint256 assetsOut, uint256 baseTokenOut);

    /**
     * @notice Handles redemption after shutdown, exchanging shares for baseToken.
     * @param _amountShares Shares being redeemed.
     * @return Amount of baseToken received.
     */
    function _redeemShutdown(uint256 _amountShares) internal returns (uint256) {
        uint256 _baseTokenOut = convertToAssetsShutdown(_amountShares);
        _burn(msg.sender, _amountShares);
        (uint256 _netAmount, uint256 _fee) = _calculateFee(_baseTokenOut, redemptionFee);
        baseToken.safeTransfer(feeTreasury, _fee);
        baseToken.safeTransfer(msg.sender, _netAmount);
        emit Redeem(msg.sender, msg.sender, _baseTokenOut, _amountShares, _netAmount, _fee);
        return _netAmount;
    }

    /**
     * @notice Calculates the amount of shares that the Portfolio should exchange for the amount of assets provided.
     * @param _assets Amount of assets.
     * @return Amount of shares.
     */
    function convertToShares(uint256 _assets) public view returns (uint256) {
        uint256 _supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
        return _supply == 0 ? _assets : _assets.mulDivDown(_supply, totalAssets());
    }

    /**
     * @notice Calculates the amount of assets that the Portfolio should exchange for the amount of shares provided.
     * @param _shares Amount of shares.
     * @return Amount of assets.
     */
    function convertToAssets(uint256 _shares) public view returns (uint256) {
        uint256 _supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
        return _supply == 0 ? _shares : _shares.mulDivDown(totalAssets(), _supply);
    }

    /**
     * @notice Calculates the amount of baseToken that the Portfolio should exchange for the amount of shares provided.
     * Used only if the Portfolio has shut down.
     * @dev Rounding down here favors the portfolio, so the user gets slightly less and the portfolio gets slightly more,
     * that way it prevents a situation where the user is owed x but the vault only has x - epsilon, where epsilon is
     * some tiny number due to rounding error.
     * @param _shares Amount of shares.
     * @return Amount of baseToken.
     */
    function convertToAssetsShutdown(uint256 _shares) public view returns (uint256) {
        uint256 _supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.
        return _supply == 0 ? _shares : _shares.mulDivDown(baseToken.balanceOf(address(this)), _supply);
    }

    /**
     * @notice Exit out all assets of portfolio for baseToken. Must persist a mechanism for entities to redeem their shares for baseToken.
     * @param _data Data that the portfolio needs to exit from asset.  Consult the portfolio's `_exit` method to determine
     * the correct format for this data.
     * @return baseTokenOut The amount of baseToken that this exit yielded.
     */
    function shutdown(bytes calldata _data) external requiresAuth returns (uint256 baseTokenOut) {
        if (didShutdown) revert DidShutdown();
        didShutdown = true;
        uint256 _assetsOut = totalAssets();
        // In most cases, _actualAssetsOut will equal _assetsOut, but in SingleTokenPortfolio, it may be less.
        (uint256 _actualAssetsOut, uint256 _baseTokenOut) = _exit(_assetsOut, _data);
        emit Shutdown(_actualAssetsOut, _baseTokenOut);
        return _baseTokenOut;
    }

    /**
     * @notice Convert some amount of asset into baseToken, either partially or fully exiting the portfolio asset.
     * @dev This method is used in `redeem` and `shutdown` calls.
     * @param _amount The amount of the Entity's portfolio asset to exchange.
     * @param _data Data that the portfolio needs to exit from asset. In some cases, this will be swap parameters. Consult the portfolio's
     * `_exit` method to determine the correct format for this data.
     * @return actualAssetsOut The amount of assets that were exited. In most cases, this will be equal to `_amount`, but may differ
     * by some errorMarginPct in SingleTokenPortfolio.
     * @return baseTokenOut The amount of baseToken that this exit yielded.
     */
    function _exit(uint256 _amount, bytes calldata _data)
        internal
        virtual
        returns (uint256 actualAssetsOut, uint256 baseTokenOut);

    /// @notice `transfer` disabled on Portfolio tokens.
    function transfer(
        address, // to
        uint256 // amount
    ) public pure override returns (bool) {
        revert TransferDisallowed();
    }

    /// @notice `transferFrom` disabled on Portfolio tokens.
    function transferFrom(
        address,
        /* from */
        address,
        /* to */
        uint256 /* amount */
    ) public pure override returns (bool) {
        revert TransferDisallowed();
    }

    /// @notice `approve` disabled on Portfolio tokens.
    function approve(
        address,
        /* to */
        uint256 /* amount */
    ) public pure override returns (bool) {
        revert TransferDisallowed();
    }

    /// @notice `permit` disabled on Portfolio tokens.
    function permit(
        address, /* owner */
        address, /* spender */
        uint256, /* value */
        uint256, /* deadline */
        uint8, /* v */
        bytes32, /* r */
        bytes32 /* s */
    ) public pure override {
        revert TransferDisallowed();
    }

    /**
     * @notice Permissioned method that allows Endaoment admin to make arbitrary calls acting as this Portfolio.
     * @param _target The address to which the call will be made.
     * @param _value The ETH value that should be forwarded with the call.
     * @param _data The calldata that will be sent with the call.
     * @return _return The data returned by the call.
     */
    function callAsPortfolio(address _target, uint256 _value, bytes memory _data)
        external
        payable
        requiresAuth
        returns (bytes memory)
    {
        (bool _success, bytes memory _response) = payable(_target).call{value: _value}(_data);
        if (!_success) revert CallFailed(_response);
        return _response;
    }

    /**
     * @notice Internal helper method to calculate the fee on a base token amount for a given fee multiplier.
     * @param _amount Amount of baseToken.
     * @param _feeMultiplier Multiplier (as zoc) to apply to the amount.
     * @return _netAmount The amount of baseToken after the fee is applied.
     * @return _fee The amount of baseToken to be taken as a fee.
     */
    function _calculateFee(uint256 _amount, uint256 _feeMultiplier)
        internal
        pure
        returns (uint256 _netAmount, uint256 _fee)
    {
        if (_feeMultiplier > Math.ZOC) revert PercentageOver100();
        unchecked {
            // unchecked as no possibility of overflow with baseToken precision
            _fee = _amount.zocmul(_feeMultiplier);
            // unchecked as the _feeMultiplier check with revert above protects against overflow
            _netAmount = _amount - _fee;
        }
    }

    /**
     * @notice Helper method to calculate AUM fee based on assets and time elapsed.
     * @param _totalAssets Assets over which to calculate AUM fee.
     * @param _period Seconds elapsed since AUM fee was last taken.
     * @dev We chose to calculate using simple interest rather than compound interest because the error was small and
     * simple interest is easier to calculate, reason about, and test.
     * @return _aumFee The amount of baseToken to be taken as AUM fee.
     */
    function _calculateAumFee(uint256 _totalAssets, uint256 _period) internal view returns (uint256) {
        if (_totalAssets == 0 || aumRate == 0 || _period == 0) return 0;
        // _period * aumRate is safe; max expected aum rate * 10 years of seconds is just over 1 WAD
        return _totalAssets.mulWadDown(_period * aumRate);
    }
}

File 7 of 16 : ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*///////////////////////////////////////////////////////////////
                                  EVENTS
    //////////////////////////////////////////////////////////////*/

    event Transfer(address indexed from, address indexed to, uint256 amount);

    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /*///////////////////////////////////////////////////////////////
                             METADATA STORAGE
    //////////////////////////////////////////////////////////////*/

    string public name;

    string public symbol;

    uint8 public immutable decimals;

    /*///////////////////////////////////////////////////////////////
                              ERC20 STORAGE
    //////////////////////////////////////////////////////////////*/

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

    mapping(address => mapping(address => uint256)) public allowance;

    /*///////////////////////////////////////////////////////////////
                             EIP-2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    bytes32 public constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

    /*///////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(
        string memory _name,
        string memory _symbol,
        uint8 _decimals
    ) {
        name = _name;
        symbol = _symbol;
        decimals = _decimals;

        INITIAL_CHAIN_ID = block.chainid;
        INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
    }

    /*///////////////////////////////////////////////////////////////
                              ERC20 LOGIC
    //////////////////////////////////////////////////////////////*/

    function approve(address spender, uint256 amount) public virtual returns (bool) {
        allowance[msg.sender][spender] = amount;

        emit Approval(msg.sender, spender, amount);

        return true;
    }

    function transfer(address to, uint256 amount) public virtual returns (bool) {
        balanceOf[msg.sender] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(msg.sender, to, amount);

        return true;
    }

    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) public virtual returns (bool) {
        uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.

        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;

        balanceOf[from] -= amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(from, to, amount);

        return true;
    }

    /*///////////////////////////////////////////////////////////////
                              EIP-2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");

        // Unchecked because the only math done is incrementing
        // the owner's nonce which cannot realistically overflow.
        unchecked {
            bytes32 digest = keccak256(
                abi.encodePacked(
                    "\x19\x01",
                    DOMAIN_SEPARATOR(),
                    keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                )
            );

            address recoveredAddress = ecrecover(digest, v, r, s);

            require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
        return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
    }

    function computeDomainSeparator() internal view virtual returns (bytes32) {
        return
            keccak256(
                abi.encode(
                    keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
                    keccak256(bytes(name)),
                    keccak256("1"),
                    block.chainid,
                    address(this)
                )
            );
    }

    /*///////////////////////////////////////////////////////////////
                       INTERNAL MINT/BURN LOGIC
    //////////////////////////////////////////////////////////////*/

    function _mint(address to, uint256 amount) internal virtual {
        totalSupply += amount;

        // Cannot overflow because the sum of all user
        // balances can't exceed the max uint256 value.
        unchecked {
            balanceOf[to] += amount;
        }

        emit Transfer(address(0), to, amount);
    }

    function _burn(address from, uint256 amount) internal virtual {
        balanceOf[from] -= amount;

        // Cannot underflow because a user's balance
        // will never be larger than the total supply.
        unchecked {
            totalSupply -= amount;
        }

        emit Transfer(from, address(0), amount);
    }
}

File 8 of 16 : Math.sol
// SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;

library Math {
    uint256 internal constant ZOC = 1e4;

    /**
     * @dev Multiply 2 numbers where at least one is a zoc, return product in original units of the other number.
     */
    function zocmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = x * y;
        unchecked {
            z /= ZOC;
        }
    }

    // Below is WAD math from solmate's FixedPointMathLib.
    // https://github.com/Rari-Capital/solmate/blob/c8278b3cb948cffda3f1de5a401858035f262060/src/utils/FixedPointMathLib.sol

    uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.

    function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
    }

    function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
    }

    // For tokens with 6 decimals like USDC, these scale by 1e6 (one million).
    function mulMilDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, y, 1e6); // Equivalent to (x * y) / 1e6 rounded down.
    }

    function divMilDown(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivDown(x, 1e6, y); // Equivalent to (x * 1e6) / y rounded down.
    }

    /*//////////////////////////////////////////////////////////////
                    LOW LEVEL FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {
        assembly {
            // Store x * y in z for now.
            z := mul(x, y)

            // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
            if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) }

            // Divide z by the denominator.
            z := div(z, denominator)
        }
    }
}

File 9 of 16 : SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Gnosis (https://github.com/gnosis/gp-v2-contracts/blob/main/src/contracts/libraries/GPv2SafeERC20.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
    /*///////////////////////////////////////////////////////////////
                            ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferETH(address to, uint256 amount) internal {
        bool callStatus;

        assembly {
            // Transfer the ETH and store if it succeeded or not.
            callStatus := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(callStatus, "ETH_TRANSFER_FAILED");
    }

    /*///////////////////////////////////////////////////////////////
                           ERC20 OPERATIONS
    //////////////////////////////////////////////////////////////*/

    function safeTransferFrom(
        ERC20 token,
        address from,
        address to,
        uint256 amount
    ) internal {
        bool callStatus;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 100 because the calldata length is 4 + 32 * 3.
            callStatus := call(gas(), token, 0, freeMemoryPointer, 100, 0, 0)
        }

        require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FROM_FAILED");
    }

    function safeTransfer(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool callStatus;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 68 because the calldata length is 4 + 32 * 2.
            callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
        }

        require(didLastOptionalReturnCallSucceed(callStatus), "TRANSFER_FAILED");
    }

    function safeApprove(
        ERC20 token,
        address to,
        uint256 amount
    ) internal {
        bool callStatus;

        assembly {
            // Get a pointer to some free memory.
            let freeMemoryPointer := mload(0x40)

            // Write the abi-encoded calldata to memory piece by piece:
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) // Begin with the function selector.
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Mask and append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Finally append the "amount" argument. No mask as it's a full 32 byte value.

            // Call the token and store if it succeeded or not.
            // We use 68 because the calldata length is 4 + 32 * 2.
            callStatus := call(gas(), token, 0, freeMemoryPointer, 68, 0, 0)
        }

        require(didLastOptionalReturnCallSucceed(callStatus), "APPROVE_FAILED");
    }

    /*///////////////////////////////////////////////////////////////
                         INTERNAL HELPER LOGIC
    //////////////////////////////////////////////////////////////*/

    function didLastOptionalReturnCallSucceed(bool callStatus) private pure returns (bool success) {
        assembly {
            // Get how many bytes the call returned.
            let returnDataSize := returndatasize()

            // If the call reverted:
            if iszero(callStatus) {
                // Copy the revert message into memory.
                returndatacopy(0, 0, returnDataSize)

                // Revert with the same message.
                revert(0, returnDataSize)
            }

            switch returnDataSize
            case 32 {
                // Copy the return data into memory.
                returndatacopy(0, 0, returnDataSize)

                // Set success to whether it returned true.
                success := iszero(iszero(mload(0)))
            }
            case 0 {
                // There was no return data.
                success := 1
            }
            default {
                // It returned some malformed input.
                success := 0
            }
        }
    }
}

File 10 of 16 : EndaomentAuth.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {RolesAuthority} from "./authorities/RolesAuthority.sol";

/**
 * @notice An abstract Auth that contracts in the Endaoment ecosystem can inherit from. It is based on
 * the `Auth.sol` contract from Solmate, but does not inherit from it. Most of the functionality
 * is either slightly different, or not needed. In particular:
 * - EndaomentAuth uses an initializer such that it can be deployed with minimal proxies.
 * - EndaomentAuth contracts reference a RolesAuthority, not just an Authority, when looking up permissions.
 *   In the Endaoment ecosystem, this is assumed to be the Registry.
 * - EndaomentAuth contracts do not have an owner, but instead grant ubiquitous permission to its RoleAuthority's
 *   owner. In the Endaoment ecosystem, this is assumed to be the board of directors multi-sig.
 * - EndaomentAuth contracts can optionally declare themselves a "special target" at deploy time. Instead of passing
 *   their address to the authority when looking up their permissions, they'll instead pass the special target bytes.
 *   See documentation on `specialTarget` for more information.
 *
 */
abstract contract EndaomentAuth {
    /// @notice Thrown when an account without proper permissions calls a privileged method.
    error Unauthorized();

    /// @notice Thrown if there is an attempt to deploy with address 0 as the authority.
    error InvalidAuthority();

    /// @notice Thrown if there is a second call to initialize.
    error AlreadyInitialized();

    /// @notice The contract used to source permissions for accounts targeting this contract.
    RolesAuthority public authority;

    /**
     * @notice If set to a non-zero value, this contract will pass these byes as the target contract
     * to the RolesAuthority's `canCall` method, rather than its own contract. This allows a single
     * RolesAuthority permission to manage permissions simultaneously for a group of contracts that
     * identify themselves as a certain type. For example: set a permission for all "entity" contracts.
     */
    bytes20 public specialTarget;

    /**
     * @notice One time method to be called at deployment to configure the contract. Required so EndaomentAuth
     * contracts can be deployed as minimal proxies (clones).
     * @param _authority Contract that will be used to source permissions for accounts targeting this contract.
     * @param _specialTarget The bytes that this contract will pass as the "target" when looking up permissions
     * from the authority. If set to empty bytes, this contract will pass its own address instead.
     */
    function __initEndaomentAuth(RolesAuthority _authority, bytes20 _specialTarget) internal virtual {
        if (address(_authority) == address(0)) revert InvalidAuthority();
        if (address(authority) != address(0)) revert AlreadyInitialized();
        authority = _authority;
        specialTarget = _specialTarget;
    }

    /**
     * @notice Modifier for methods that require authorization to execute.
     */
    modifier requiresAuth() virtual {
        if (!isAuthorized(msg.sender, msg.sig)) revert Unauthorized();
        _;
    }

    /**
     * @notice Internal method that asks the authority whether the caller has permission to execute a method.
     * @param user The account attempting to call a permissioned method on this contract
     * @param functionSig The signature hash of the permissioned method being invoked.
     */
    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        RolesAuthority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.
        address _target = specialTarget == "" ? address(this) : address(specialTarget);

        // The caller has permission on authority, or the caller is the RolesAuthority owner
        return auth.canCall(user, _target, functionSig) || user == auth.owner();
    }
}

File 11 of 16 : Auth.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

// This contract is modified from Solmate only to make requiresAuth virtual on line 26

/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
    event OwnerUpdated(address indexed user, address indexed newOwner);

    event AuthorityUpdated(address indexed user, Authority indexed newAuthority);

    address public owner;

    Authority public authority;

    constructor(address _owner, Authority _authority) {
        owner = _owner;
        authority = _authority;

        emit OwnerUpdated(msg.sender, _owner);
        emit AuthorityUpdated(msg.sender, _authority);
    }

    modifier requiresAuth() virtual {
        require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");

        _;
    }

    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.

        // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
        // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
        return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
    }

    function setAuthority(Authority newAuthority) public virtual {
        // We check if the caller is the owner first because we want to ensure they can
        // always swap out the authority even if it's reverting or using up a lot of gas.
        require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));

        authority = newAuthority;

        emit AuthorityUpdated(msg.sender, newAuthority);
    }

    function setOwner(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnerUpdated(msg.sender, newOwner);
    }
}

/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
    function canCall(address user, address target, bytes4 functionSig) external view returns (bool);
}

File 12 of 16 : RegistryAuth.sol
//SPDX-License-Identifier: BSD 3-Clause
pragma solidity 0.8.13;

import {Auth, Authority} from "./lib/auth/Auth.sol";
import {RolesAuthority} from "./lib/auth/authorities/RolesAuthority.sol";

// --- Errors ---
error OwnershipInvalid();

/**
 * @notice RegistryAuth - contract to control ownership of the Registry.
 */
contract RegistryAuth is RolesAuthority {
    /// @notice Emitted when the first step of an ownership transfer (proposal) is done.
    event OwnershipTransferProposed(address indexed user, address indexed newOwner);

    /// @notice Emitted when the second step of an ownership transfer (claim) is done.
    event OwnershipChanged(address indexed owner, address indexed newOwner);

    // --- Storage ---
    /// @notice Pending owner for 2 step ownership transfer
    address public pendingOwner;

    // --- Constructor ---
    constructor(address _owner, Authority _authority) RolesAuthority(_owner, _authority) {}

    /**
     * @notice Starts the 2 step process of transferring registry authorization to a new owner.
     * @param _newOwner Proposed new owner of registry authorization.
     */
    function transferOwnership(address _newOwner) external requiresAuth {
        pendingOwner = _newOwner;

        emit OwnershipTransferProposed(msg.sender, _newOwner);
    }

    /**
     * @notice Completes the 2 step process of transferring registry authorization to a new owner.
     * This function must be called by the proposed new owner.
     */
    function claimOwnership() external {
        if (msg.sender != pendingOwner) revert OwnershipInvalid();
        emit OwnershipChanged(owner, pendingOwner);
        owner = pendingOwner;
        pendingOwner = address(0);
    }

    /**
     * @notice Old approach of setting a new owner in a single step.
     * @dev This function throws an error to force use of the new 2-step approach.
     */
    function setOwner(address /*newOwner*/ ) public view override requiresAuth {
        revert OwnershipInvalid();
    }
}

File 13 of 16 : ISwapWrapper.sol
//SPDX-License-Identifier: BSD 3-Clause
pragma solidity >=0.8.0;

error ETHAmountInMismatch();

/**
 * @notice ISwapWrapper is the interface that all swap wrappers should implement.
 * This will be used to support swap protocols like Uniswap V2 and V3, Sushiswap, 1inch, etc.
 */
interface ISwapWrapper {
    /// @notice Event emitted after a successful swap.
    event WrapperSwapExecuted(
        address indexed tokenIn,
        address indexed tokenOut,
        address sender,
        address indexed recipient,
        uint256 amountIn,
        uint256 amountOut
    );

    /// @notice Name of swap wrapper for UX readability.
    function name() external returns (string memory);

    /**
     * @notice Swap function. Generally we expect the implementer to call some exactAmountIn-like swap method, and so the documentation
     * is written with this in mind. However, the method signature is general enough to support exactAmountOut swaps as well.
     * @param _tokenIn Token to be swapped (or 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for ETH).
     * @param _tokenOut Token to receive (or 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE for ETH).
     * @param _recipient Receiver of `_tokenOut`.
     * @param _amount Amount of `_tokenIn` that should be swapped.
     * @param _data Additional data that the swap wrapper may require to execute the swap.
     * @return Amount of _tokenOut received.
     */
    function swap(address _tokenIn, address _tokenOut, address _recipient, uint256 _amount, bytes calldata _data)
        external
        payable
        returns (uint256);
}

File 14 of 16 : ReentrancyGuard.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Modified Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private reentrancyStatus;

    error Reentrancy();

    function __initReentrancyGuard() internal {
        if (reentrancyStatus != 0) revert Reentrancy();
        reentrancyStatus = 1;
    }

    modifier nonReentrant() {
        if (reentrancyStatus != 1) revert Reentrancy();

        reentrancyStatus = 2;

        _;

        reentrancyStatus = 1;
    }
}

File 15 of 16 : ReentrancyGuard.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Gas optimized reentrancy protection for smart contracts.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/ReentrancyGuard.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    uint256 private reentrancyStatus = 1;

    modifier nonReentrant() {
        require(reentrancyStatus == 1, "REENTRANCY");

        reentrancyStatus = 2;

        _;

        reentrancyStatus = 1;
    }
}

File 16 of 16 : RolesAuthority.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

// This contract is modified from Solmate only to import modified Auth.sol on line 5
import {Auth, Authority} from "../Auth.sol";

/// @notice Role based Authority that supports up to 256 roles.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/auth/authorities/RolesAuthority.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-roles/blob/master/src/roles.sol)
contract RolesAuthority is Auth, Authority {
    /*///////////////////////////////////////////////////////////////
                                  EVENTS
    //////////////////////////////////////////////////////////////*/

    event UserRoleUpdated(address indexed user, uint8 indexed role, bool enabled);

    event PublicCapabilityUpdated(address indexed target, bytes4 indexed functionSig, bool enabled);

    event RoleCapabilityUpdated(uint8 indexed role, address indexed target, bytes4 indexed functionSig, bool enabled);

    /*///////////////////////////////////////////////////////////////
                               CONSTRUCTOR
    //////////////////////////////////////////////////////////////*/

    constructor(address _owner, Authority _authority) Auth(_owner, _authority) {}

    /*///////////////////////////////////////////////////////////////
                            ROLE/USER STORAGE
    //////////////////////////////////////////////////////////////*/

    mapping(address => bytes32) public getUserRoles;

    mapping(address => mapping(bytes4 => bool)) public isCapabilityPublic;

    mapping(address => mapping(bytes4 => bytes32)) public getRolesWithCapability;

    function doesUserHaveRole(address user, uint8 role) public view virtual returns (bool) {
        return (uint256(getUserRoles[user]) >> role) & 1 != 0;
    }

    function doesRoleHaveCapability(uint8 role, address target, bytes4 functionSig)
        public
        view
        virtual
        returns (bool)
    {
        return (uint256(getRolesWithCapability[target][functionSig]) >> role) & 1 != 0;
    }

    /*///////////////////////////////////////////////////////////////
                          AUTHORIZATION LOGIC
    //////////////////////////////////////////////////////////////*/

    function canCall(address user, address target, bytes4 functionSig) public view virtual override returns (bool) {
        return isCapabilityPublic[target][functionSig]
            || bytes32(0) != getUserRoles[user] & getRolesWithCapability[target][functionSig];
    }

    /*///////////////////////////////////////////////////////////////
                  ROLE CAPABILITY CONFIGURATION LOGIC
    //////////////////////////////////////////////////////////////*/

    function setPublicCapability(address target, bytes4 functionSig, bool enabled) public virtual requiresAuth {
        isCapabilityPublic[target][functionSig] = enabled;

        emit PublicCapabilityUpdated(target, functionSig, enabled);
    }

    function setRoleCapability(uint8 role, address target, bytes4 functionSig, bool enabled)
        public
        virtual
        requiresAuth
    {
        if (enabled) {
            getRolesWithCapability[target][functionSig] |= bytes32(1 << role);
        } else {
            getRolesWithCapability[target][functionSig] &= ~bytes32(1 << role);
        }

        emit RoleCapabilityUpdated(role, target, functionSig, enabled);
    }

    /*///////////////////////////////////////////////////////////////
                      USER ROLE ASSIGNMENT LOGIC
    //////////////////////////////////////////////////////////////*/

    function setUserRole(address user, uint8 role, bool enabled) public virtual requiresAuth {
        if (enabled) {
            getUserRoles[user] |= bytes32(1 << role);
        } else {
            getUserRoles[user] &= ~bytes32(1 << role);
        }

        emit UserRoleUpdated(user, role, enabled);
    }
}

Settings
{
  "remappings": [
    "ds-test/=lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "murky/=lib/murky/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/solmate/src/",
    "weird-erc20/=lib/solmate/lib/weird-erc20/src/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 9999999
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"contract Registry","name":"registry","type":"address"},{"internalType":"address","name":"receiptAsset","type":"address"},{"internalType":"string","name":"shareTokenName","type":"string"},{"internalType":"string","name":"shareTokenSymbol","type":"string"},{"internalType":"contract EntityBaseTokenTransferor","name":"ebtt","type":"address"},{"internalType":"address","name":"processor","type":"address"},{"internalType":"uint256","name":"minDeposit","type":"uint256"},{"internalType":"uint256","name":"cap","type":"uint256"},{"internalType":"address","name":"feeTreasury","type":"address"},{"internalType":"uint256","name":"depositFee","type":"uint256"},{"internalType":"uint256","name":"redemptionFee","type":"uint256"},{"internalType":"uint256","name":"aumRate","type":"uint256"}],"internalType":"struct ConstructorArgs","name":"_args","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"BadCheckCapImplementation","type":"error"},{"inputs":[],"name":"BadEntityInput","type":"error"},{"inputs":[{"internalType":"bytes","name":"response","type":"bytes"}],"name":"CallFailed","type":"error"},{"inputs":[],"name":"DepositAfterShutdown","type":"error"},{"inputs":[],"name":"DidShutdown","type":"error"},{"inputs":[],"name":"ExceedsCap","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidAuthority","type":"error"},{"inputs":[],"name":"InvalidRate","type":"error"},{"inputs":[],"name":"InvalidSwapper","type":"error"},{"inputs":[],"name":"MinDeposit","type":"error"},{"inputs":[],"name":"NotEntity","type":"error"},{"inputs":[],"name":"PercentageOver100","type":"error"},{"inputs":[],"name":"RoundsToZero","type":"error"},{"inputs":[],"name":"Slippage","type":"error"},{"inputs":[],"name":"TransferDisallowed","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"UnderMaintenance","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"feeAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timeDelta","type":"uint256"}],"name":"AumFeesTaken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"}],"name":"AumRateSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cap","type":"uint256"}],"name":"CapSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Entity","name":"entity","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountShares","type":"uint256"}],"name":"CorrectionShareBurned","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Entity","name":"entity","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountShares","type":"uint256"}],"name":"CorrectionShareMinted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"depositAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Entity","name":"entity","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountBaseToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountAssets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountShares","type":"uint256"}],"name":"DepositConsolidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"DepositFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeTreasury","type":"address"}],"name":"FeeTreasurySet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesTaken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMinDeposit","type":"uint256"}],"name":"MinDepositSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newProcessor","type":"address"}],"name":"ProcessorSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract Entity","name":"entity","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountBaseToken","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountAssets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"RedeemConsolidated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"RedemptionFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"baseTokenOut","type":"uint256"}],"name":"Shutdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"newUnderMaintenance","type":"bool"}],"name":"UnderMaintenanceSet","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"async","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"aumRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract RolesAuthority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_target","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"callAsPortfolio","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"cap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"enum ConsolidationOperation","name":"operation","type":"uint8"},{"internalType":"contract Entity","name":"entity","type":"address"},{"internalType":"uint256","name":"amountBaseToken","type":"uint256"},{"internalType":"uint256","name":"amountAssets","type":"uint256"}],"internalType":"struct Consolidation[]","name":"_consolidations","type":"tuple[]"}],"name":"consolidateNoAccrual","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"enum ConsolidationOperation","name":"operation","type":"uint8"},{"internalType":"contract Entity","name":"entity","type":"address"},{"internalType":"uint256","name":"amountBaseToken","type":"uint256"},{"internalType":"uint256","name":"amountAssets","type":"uint256"}],"internalType":"struct Consolidation[]","name":"_consolidations","type":"tuple[]"},{"internalType":"uint256","name":"_accruedAssets","type":"uint256"}],"name":"consolidateWithAccrual","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_receiptAssets","type":"uint256"}],"name":"convertReceiptAssetsToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"convertToAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_shares","type":"uint256"}],"name":"convertToAssetsShutdown","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Entity","name":"_entity","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"correctionBurn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Entity","name":"_entity","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"correctionMint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountBaseToken","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"didShutdown","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ebtt","outputs":[{"internalType":"contract EntityBaseTokenTransferor","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeTreasury","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Entity","name":"","type":"address"}],"name":"pendingPurchaseBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract Entity","name":"","type":"address"}],"name":"pendingSaleAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"processor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"receiptAsset","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountShares","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"redemptionFee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"contract Registry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pct","type":"uint256"}],"name":"setAumRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setCap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pct","type":"uint256"}],"name":"setDepositFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_feeTreasury","type":"address"}],"name":"setFeeTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_min","type":"uint256"}],"name":"setMinDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_processor","type":"address"}],"name":"setProcessor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_pct","type":"uint256"}],"name":"setRedemptionFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_underMaintenance","type":"bool"}],"name":"setUnderMaintenance","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"shutdown","outputs":[{"internalType":"uint256","name":"baseTokenOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"specialTarget","outputs":[{"internalType":"bytes20","name":"","type":"bytes20"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"takeAumFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountReceiptAssets","type":"uint256"}],"name":"takeFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"timestampAumFeesTaken","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalReceiptAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"underMaintenance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]

6101a060405260016008553480156200001757600080fd5b506040516200441a3803806200441a8339810160408190526200003a916200083f565b806000015181602001518260400151836060015160018560e001518661010001518761012001518861014001518961016001518787620000808b6200047060201b60201c565b6001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015620000be573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620000e4919062000976565b8251620000f990600090602086019062000664565b5081516200010f90600190602085019062000664565b5060ff81166080524660a0526200012562000473565b60c052506200014591508b905068706f7274666f6c696f60b81b6200050f565b6001600160a01b038a811660e05286151561010052600a80546001600160a01b03191691861691821790556040519081527ffa91b2d5be9caed9cc205220f1d5f3c5fee252dea3faf893928e0e639c0ccc709060200160405180910390a1612710821115620001c7576040516378418ce360e11b815260040160405180910390fd5b600c8290556040518281527f91cc643d187eb250905520d3dae0b1017edd16961d27ab9a4a61fea5e38f717d9060200160405180910390a161271083111562000223576040516378418ce360e11b815260040160405180910390fd5b600b8390556040518381527f12865465a7036a0232cbf9fb63ce880a3ee54f702775fe7abbc3c416e7968cd59060200160405180910390a160098590556040518581527f9872d5eb566b79923d043f1b59aca655ca80a2bb5b6bca4824e515b0e398902f9060200160405180910390a16001600160a01b03891661014052886001600160a01b0316610120816001600160a01b03168152505060e0516001600160a01b031663c55dae636040518163ffffffff1660e01b8152600401602060405180830381865afa158015620002fd573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620003239190620009a2565b6001600160a01b03166101605263bce02f4e8111156200035657604051636a43f8d160e01b815260040160405180910390fd5b600f8190556040518181527fad2a1b372bf4c8402696d0ed78880c15faa5b69298ede2615a228810a52dc6c49060200160405180910390a1505042600e555050505060808501516001600160a01b0390811661018081905261016051620003d6965090911693509150600019905062000592602090811b62001fee17901c565b60a0810151601080546001600160a01b0319166001600160a01b0390921691821790556040519081527fd42d4de5d9e033ca6d7d5f77d77dcfc293d4145f08ded95fb625bc366cef017a9060200160405180910390a160c081015160118190556040519081527fc50a7f0bdf88c216b2541b0bdea26f22305460e39ffc672ec1a7501732c5ba819060200160405180910390a15062000aa1565b90565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6000604051620004a79190620009fe565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6001600160a01b0382166200053757604051636f6a1b8760e11b815260040160405180910390fd5b6006546001600160a01b031615620005615760405162dc149f60e41b815260040160405180910390fd5b600680546001600160a01b039093166001600160a01b03199384161790556007805460609290921c91909216179055565b600060405163095ea7b360e01b81526001600160a01b03841660048201528260248201526000806044836000895af19150620005d090508162000618565b620006125760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b604482015260640160405180910390fd5b50505050565b60003d826200062b57806000803e806000fd5b8060208114620006465780156200065857600092506200065d565b816000803e600051151592506200065d565b600192505b5050919050565b8280546200067290620009c2565b90600052602060002090601f016020900481019282620006965760008555620006e1565b82601f10620006b157805160ff1916838001178555620006e1565b82800160010185558215620006e1579182015b82811115620006e1578251825591602001919060010190620006c4565b50620006ef929150620006f3565b5090565b5b80821115620006ef5760008155600101620006f4565b634e487b7160e01b600052604160045260246000fd5b60405161018081016001600160401b03811182821017156200074657620007466200070a565b60405290565b604051601f8201601f191681016001600160401b03811182821017156200077757620007776200070a565b604052919050565b6001600160a01b03811681146200079557600080fd5b50565b8051620007a5816200077f565b919050565b600082601f830112620007bc57600080fd5b81516001600160401b03811115620007d857620007d86200070a565b6020620007ee601f8301601f191682016200074c565b82815285828487010111156200080357600080fd5b60005b838110156200082357858101830151828201840152820162000806565b83811115620008355760008385840101525b5095945050505050565b6000602082840312156200085257600080fd5b81516001600160401b03808211156200086a57600080fd5b9083019061018082860312156200088057600080fd5b6200088a62000720565b620008958362000798565b8152620008a56020840162000798565b6020820152604083015182811115620008bd57600080fd5b620008cb87828601620007aa565b604083015250606083015182811115620008e457600080fd5b620008f287828601620007aa565b606083015250620009066080840162000798565b60808201526200091960a0840162000798565b60a082015260c083015160c082015260e083015160e082015261010091506200094482840162000798565b918101919091526101208281015190820152610140808301519082015261016091820151918101919091529392505050565b6000602082840312156200098957600080fd5b815160ff811681146200099b57600080fd5b9392505050565b600060208284031215620009b557600080fd5b81516200099b816200077f565b600181811c90821680620009d757607f821691505b602082108103620009f857634e487b7160e01b600052602260045260246000fd5b50919050565b600080835481600182811c91508083168062000a1b57607f831692505b6020808410820362000a3b57634e487b7160e01b86526022600452602486fd5b81801562000a52576001811462000a645762000a93565b60ff1986168952848901965062000a93565b60008a81526020902060005b8681101562000a8b5781548b82015290850190830162000a70565b505084890196505b509498975050505050505050565b60805160a05160c05160e051610100516101205161014051610160516101805161388362000b9760003960008181610b2f0152612f6601526000818161096001528181611cd801528181611d1b01528181611e56015281816124b5015281816124fe015281816125fa015281816126cf01528181612a0801528181612a4b0152612fe101526000818161067e01528181610cac015281816115dc0152818161187301528181611b3901528181612b6f0152612df90152600061060f0152600081816108aa015261120d0152600081816107f4015261239801526000610e8301526000610e530152600061059e01526138836000f3fe6080604052600436106103765760003560e01c806367a52793116101d1578063c6e6f59211610102578063e37a8754116100a0578063f029748d1161006f578063f029748d14610afd578063fc7bb06114610b1d578063fef7849714610b51578063ff5ac9a414610b6457600080fd5b8063e37a875414610a87578063e37d15bf14610aa7578063e77c646d14610abd578063e83bb71414610add57600080fd5b8063d505accf116100dc578063d505accf146109ef578063d7ad932014610a0f578063dd62ed3e14610a2f578063e1a8eafd14610a6757600080fd5b8063c6e6f59214610982578063cbb94359146109a2578063ce1b1d43146109c257600080fd5b806396724a971161016f578063bf7e214f11610149578063bf7e214f146108e1578063bfa37e371461090e578063c37428e51461092e578063c55dae631461094e57600080fd5b806396724a971461089857806399aa7348146108cc578063a9059cbb146103e557600080fd5b80637dbc1df0116101ab5780637dbc1df0146108165780637ecebe00146108365780638fcc9cfb1461086357806395d89b411461088357600080fd5b806367a527931461079f57806370a08231146107b55780637b103999146107e257600080fd5b8063313ce567116102ab57806347786d37116102495780635bae19ef116102235780635bae19ef146107125780635d3035191461073257806360dc2340146107525780636549315d1461077f57600080fd5b806347786d37146106b6578063490ae210146106d85780635a41217e146106f857600080fd5b806338d52e0f1161028557806338d52e0f146105fd57806341b3d185146106565780634288d8711461066c578063458f5815146106a057600080fd5b8063313ce5671461058c578063355274ea146105d25780633644e515146105e857600080fd5b806318160ddd116103185780631e7ee237116102f25780631e7ee237146104d85780632031ee95146104f257806323b872dd1461053d57806330adf81f1461055857600080fd5b806318160ddd146104775780631924b9061461048d5780631e500759146104ba57600080fd5b8063095ea7b311610354578063095ea7b3146103e55780630a9d5514146104155780630aa4efa71461042a578063106f276f1461045757600080fd5b806301e1d1141461037b57806306fdde03146103a357806307a2d13a146103c5575b600080fd5b34801561038757600080fd5b50610390610b7a565b6040519081526020015b60405180910390f35b3480156103af57600080fd5b506103b8610b8c565b60405161039a91906130f9565b3480156103d157600080fd5b506103906103e036600461310c565b610c1a565b3480156103f157600080fd5b5061040561040036600461314a565b610c47565b604051901515815260200161039a565b34801561042157600080fd5b50610390610c7b565b34801561043657600080fd5b50610390610445366004613176565b60136020526000908152604090205481565b34801561046357600080fd5b506103906104723660046131dc565b610d2c565b34801561048357600080fd5b5061039060025481565b34801561049957600080fd5b506103906104a8366004613176565b60146020526000908152604090205481565b3480156104c657600080fd5b506103906104d536600461310c565b90565b3480156104e457600080fd5b50600d546104059060ff1681565b3480156104fe57600080fd5b5060075461050c9060601b81565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909116815260200161039a565b34801561054957600080fd5b5061040561040036600461321e565b34801561056457600080fd5b506103907f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b34801561059857600080fd5b506105c07f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff909116815260200161039a565b3480156105de57600080fd5b5061039060095481565b3480156105f457600080fd5b50610390610e4f565b34801561060957600080fd5b506106317f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b34801561066257600080fd5b5061039060115481565b34801561067857600080fd5b506106317f000000000000000000000000000000000000000000000000000000000000000081565b3480156106ac57600080fd5b50610390600c5481565b3480156106c257600080fd5b506106d66106d136600461310c565b610ea5565b005b3480156106e457600080fd5b506106d66106f336600461310c565b610f45565b34801561070457600080fd5b506012546104059060ff1681565b34801561071e57600080fd5b506106d661072d36600461310c565b61101a565b34801561073e57600080fd5b5061039061074d36600461325f565b6110f9565b34801561075e57600080fd5b50600a546106319073ffffffffffffffffffffffffffffffffffffffff1681565b34801561078b57600080fd5b506106d661079a36600461314a565b611317565b3480156107ab57600080fd5b50610390600b5481565b3480156107c157600080fd5b506103906107d0366004613176565b60036020526000908152604090205481565b3480156107ee57600080fd5b506106317f000000000000000000000000000000000000000000000000000000000000000081565b34801561082257600080fd5b506106d661083136600461310c565b6113d9565b34801561084257600080fd5b50610390610851366004613176565b60056020526000908152604090205481565b34801561086f57600080fd5b506106d661087e36600461310c565b6114ae565b34801561088f57600080fd5b506103b8611547565b3480156108a457600080fd5b506104057f000000000000000000000000000000000000000000000000000000000000000081565b3480156108d857600080fd5b506106d6611554565b3480156108ed57600080fd5b506006546106319073ffffffffffffffffffffffffffffffffffffffff1681565b34801561091a57600080fd5b506106d6610929366004613176565b611644565b34801561093a57600080fd5b506106d661094936600461314a565b61171b565b34801561095a57600080fd5b506106317f000000000000000000000000000000000000000000000000000000000000000081565b34801561098e57600080fd5b5061039061099d36600461310c565b6117d1565b3480156109ae57600080fd5b506106d66109bd36600461310c565b6117f1565b3480156109ce57600080fd5b506010546106319073ffffffffffffffffffffffffffffffffffffffff1681565b3480156109fb57600080fd5b506106d6610a0a3660046132ab565b6118cc565b348015610a1b57600080fd5b506106d6610a2a366004613330565b6118fe565b348015610a3b57600080fd5b50610390610a4a36600461334d565b600460209081526000928352604080842090915290825290205481565b348015610a7357600080fd5b506106d6610a82366004613176565b6119c1565b348015610a9357600080fd5b506106d6610aa23660046133cb565b611a98565b348015610ab357600080fd5b50610390600f5481565b348015610ac957600080fd5b50610390610ad836600461325f565b611bb4565b348015610ae957600080fd5b506106d6610af8366004613417565b611d9e565b348015610b0957600080fd5b50610390610b1836600461310c565b611e18565b348015610b2957600080fd5b506106317f000000000000000000000000000000000000000000000000000000000000000081565b6103b8610b5f36600461347c565b611ed6565b348015610b7057600080fd5b50610390600e5481565b6000610b876104d5610c7b565b905090565b60008054610b9990613567565b80601f0160208091040260200160405190810160405280929190818152602001828054610bc590613567565b8015610c125780601f10610be757610100808354040283529160200191610c12565b820191906000526020600020905b815481529060010190602001808311610bf557829003601f168201915b505050505081565b6002546000908015610c3e57610c39610c31610b7a565b8490836120bb565b610c40565b825b9392505050565b60006040517fc31c949300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610d08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8791906135ba565b6000610d5c336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b610d92576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5460ff1615610dcf576040517f2a542da400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556000610e04610b7a565b6040805160008082526020820181905282519394509283927fbf74a2393b907f335184e6dbeb4daa93812b077b8e034c2e61c8e6864002dda7928290030190a1925050505b92915050565b60007f00000000000000000000000000000000000000000000000000000000000000004614610e8057610b876122b6565b507f000000000000000000000000000000000000000000000000000000000000000090565b610ed3336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b610f09576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60098190556040518181527f9872d5eb566b79923d043f1b59aca655ca80a2bb5b6bca4824e515b0e398902f906020015b60405180910390a150565b610f73336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b610fa9576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710811115610fe5576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b8190556040518181527f12865465a7036a0232cbf9fb63ce880a3ee54f702775fe7abbc3c416e7968cd590602001610f3a565b611048336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b61107e576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63bce02f4e8111156110bc576040517f6a43f8d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110c4611554565b600f8190556040518181527fad2a1b372bf4c8402696d0ed78880c15faa5b69298ede2615a228810a52dc6c490602001610f3a565b600060085460011461116c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b6002600855600d5460ff16156111ae576040517f326eab7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111b733612350565b6111ed576040517f184849cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111f5611554565b6000806000611205878787612405565b9250925092507f00000000000000000000000000000000000000000000000000000000000000006112b95761123c8587018761310c565b831015611275576040517f7dd37f7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826000036112af576040517fc440e0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112b93384612550565b604080518381526020810185905290810188905260608101829052339081907f5f971bd00bf3ffbca8a6d72cdd4fd92cfd4f62636161921d1e5a64f0b64ccb6d9060800160405180910390a350909150505b60016008559392505050565b611345336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b61137b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113858282612550565b8173ffffffffffffffffffffffffffffffffffffffff167fa58661fc88ea7766ebe1d611a5040787ee19b571518d348deed432fb94344213826040516113cd91815260200190565b60405180910390a25050565b611407336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b61143d576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710811115611479576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8190556040518181527f91cc643d187eb250905520d3dae0b1017edd16961d27ab9a4a61fea5e38f717d90602001610f3a565b6114dc336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611512576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60118190556040518181527fc50a7f0bdf88c216b2541b0bdea26f22305460e39ffc672ec1a7501732c5ba8190602001610f3a565b60018054610b9990613567565b600d5460ff1615611569576115676125c9565b565b6000611573610c7b565b90506000600e54426115859190613602565b9050600061159383836126f8565b9050828111156115a05750815b60008111806115af5750600254155b1561163f5742600e55801561163f57600a546116059073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116911683612735565b60408051828152602081018490527fc5bd0ea7a1520a37af6ec1d295e0b4f9820e066c078ae82f4abcc49286ab1028910160405180910390a15b505050565b611672336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b6116a8576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527ffa91b2d5be9caed9cc205220f1d5f3c5fee252dea3faf893928e0e639c0ccc7090602001610f3a565b611749336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b61177f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61178982826127fc565b8173ffffffffffffffffffffffffffffffffffffffff167f76583919d2167957eef2c3fe269d6b8d6611a76c9313dd64a34fa3fe80457aa8826040516113cd91815260200190565b6002546000908015610c3e57610c39816117e9610b7a565b8591906120bb565b61181f336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611855576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a5461189c9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116911683612735565b6040518181527ffba1cbbf893e6a61412440b48fca1c80a5bf24d2f7deb6d5544717745077a36090602001610f3a565b6040517fc31c949300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61192c336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611962576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168215159081179091556040519081527f297471d8aee523d9bc01eb14e40f77893764adf15db1357c529e38d99486b75f90602001610f3a565b6119ef336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611a25576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fd42d4de5d9e033ca6d7d5f77d77dcfc293d4145f08ded95fb625bc366cef017a90602001610f3a565b611ac6336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611afc576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b04611554565b6040517f40c10f19000000000000000000000000000000000000000000000000000000008152306004820152602481018290527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906340c10f1990604401600060405180830381600087803b158015611b9257600080fd5b505af1158015611ba6573d6000803e3d6000fd5b5050505061163f838361288a565b6000600854600114611c22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401611163565b6002600855611c2f611554565b600d5460ff1615611c4a57611c43846129be565b905061130b565b600080611c58868686612ac6565b91509150611c6633876127fc565b81600003611ca0576040517fc440e0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808215611d4257611cb583600c54612bd8565b600a549193509150611d019073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116911683612735565b611d4273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163384612735565b60408051858152602081018a905290810183905260608101829052339081907f8caf04742286d017f9ac3924388e188c73e6e5094311c5e59a61a7ef86dda8bf9060800160405180910390a35060016008559695505050505050565b611dcc336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611e02576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e0a611554565b611e14828261288a565b5050565b6002546000908015610c3e576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610c39907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611eb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3191906135ba565b6060611f06336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611f3c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff168585604051611f659190613619565b60006040518083038185875af1925050503d8060008114611fa2576040519150601f19603f3d011682016040523d82523d6000602084013e611fa7565b606091505b509150915081611fe557806040517fa5fa8d2b00000000000000000000000000000000000000000000000000000000815260040161116391906130f9565b95945050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af191505061204f81612c2c565b6120b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401611163565b50505050565b8282028115158415858304851417166120d357600080fd5b0492915050565b60065460075460009173ffffffffffffffffffffffffffffffffffffffff1690829060601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016156121445760075473ffffffffffffffffffffffffffffffffffffffff16612146565b305b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015280831660248301527fffffffff00000000000000000000000000000000000000000000000000000000871660448301529192509083169063b700961390606401602060405180830381865afa1580156121e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061220a9190613635565b80611fe557508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561225b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061227f9190613652565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161495945050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516122e8919061366f565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6040517fea3fff6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063ea3fff6890602401602060405180830381865afa1580156123e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e499190613635565b6000806000601154861015612446576040517f11bcd83000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60125460ff1615612483576040517f4dfbeb3f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061249288600b54612bd8565b60105491935091506124e09073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081169133911685612c73565b600a546125299073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000081169133911684612c73565b33600090815260136020526040812080549390930190925590978897509095509350505050565b80600260008282546125629190613741565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612656573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061267a91906135ba565b90506000600e544261268c9190613602565b9050600061269a83836126f8565b9050828111156126a75750815b801561163f5742600e55600a546116059073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116911683612735565b60008215806127075750600f54155b80612710575081155b1561271d57506000610e49565b610c40600f548361272e9190613759565b8490612d5d565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af191505061279681612c2c565b6120b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401611163565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290612831908490613602565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020016125bd565b60005b8181101561163f5760008383838181106128a9576128a96137c5565b6128bf92602060809092020190810191506137f4565b60018111156128d0576128d0613796565b036129445761293f8383838181106128ea576128ea6137c5565b90506080020160200160208101906129029190613176565b848484818110612914576129146137c5565b90506080020160400135858585818110612930576129306137c5565b90506080020160600135612d72565b6129ae565b6129ae838383818110612959576129596137c5565b90506080020160200160208101906129719190613176565b848484818110612983576129836137c5565b9050608002016040013585858581811061299f5761299f6137c5565b90506080020160600135612ec9565b6129b781613815565b905061288d565b6000806129ca83611e18565b90506129d633846127fc565b6000806129e583600c54612bd8565b600a549193509150612a319073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000008116911683612735565b612a7273ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000163384612735565b604080518481526020810187905290810183905260608101829052339081907f8caf04742286d017f9ac3924388e188c73e6e5094311c5e59a61a7ef86dda8bf9060800160405180910390a3509392505050565b601254600090819060ff1615612b08576040517f4dfbeb3f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612b1386610c1a565b3360009081526014602052604090819020805483019055517f42966c680000000000000000000000000000000000000000000000000000000081526004810182905290915073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906342966c6890602401600060405180830381600087803b158015612bb357600080fd5b505af1158015612bc7573d6000803e3d6000fd5b509298600098509650505050505050565b600080612710831115612c17576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c21848461306b565b938490039492505050565b60003d82612c3e57806000803e806000fd5b8060208114612c56578015612c675760009250612c6c565b816000803e60005115159250612c6c565b600192505b5050919050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af1915050612cf081612c2c565b612d56576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401611163565b5050505050565b6000610c408383670de0b6b3a76400006120bb565b73ffffffffffffffffffffffffffffffffffffffff831660009081526013602052604081208054849290612da7908490613602565b9091555060009050612db8826117d1565b9050612dc48482612550565b6040517f40c10f19000000000000000000000000000000000000000000000000000000008152306004820152602481018390527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906340c10f1990604401600060405180830381600087803b158015612e5257600080fd5b505af1158015612e66573d6000803e3d6000fd5b5050604080518681526020810186905290810184905273ffffffffffffffffffffffffffffffffffffffff871692507fc6d9b758e81c9b14011e8efdc0d39baabb216903c6e6c11762cdd0a7bc13e121915060600160405180910390a250505050565b73ffffffffffffffffffffffffffffffffffffffff831660009081526014602052604081208054839290612efe908490613602565b92505081905550600080612f1484600c54612bd8565b6040517f6895db3400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152602482018490529294509092507f000000000000000000000000000000000000000000000000000000000000000090911690636895db3490604401600060405180830381600087803b158015612fac57600080fd5b505af1158015612fc0573d6000803e3d6000fd5b5050600a5461300b925073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000811692501683612735565b604080518381526020810185905290810182905273ffffffffffffffffffffffffffffffffffffffff8616907f69873b904850da89f6919c238b6542f58bae21dbe54bb481bd2e93a59de15a6b9060600160405180910390a25050505050565b60006130778284613759565b61271090049392505050565b60005b8381101561309e578181015183820152602001613086565b838111156120b55750506000910152565b600081518084526130c7816020860160208601613083565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c4060208301846130af565b60006020828403121561311e57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461314757600080fd5b50565b6000806040838503121561315d57600080fd5b823561316881613125565b946020939093013593505050565b60006020828403121561318857600080fd5b8135610c4081613125565b60008083601f8401126131a557600080fd5b50813567ffffffffffffffff8111156131bd57600080fd5b6020830191508360208285010111156131d557600080fd5b9250929050565b600080602083850312156131ef57600080fd5b823567ffffffffffffffff81111561320657600080fd5b61321285828601613193565b90969095509350505050565b60008060006060848603121561323357600080fd5b833561323e81613125565b9250602084013561324e81613125565b929592945050506040919091013590565b60008060006040848603121561327457600080fd5b83359250602084013567ffffffffffffffff81111561329257600080fd5b61329e86828701613193565b9497909650939450505050565b600080600080600080600060e0888a0312156132c657600080fd5b87356132d181613125565b965060208801356132e181613125565b95506040880135945060608801359350608088013560ff8116811461330557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b801515811461314757600080fd5b60006020828403121561334257600080fd5b8135610c4081613322565b6000806040838503121561336057600080fd5b823561336b81613125565b9150602083013561337b81613125565b809150509250929050565b60008083601f84011261339857600080fd5b50813567ffffffffffffffff8111156133b057600080fd5b6020830191508360208260071b85010111156131d557600080fd5b6000806000604084860312156133e057600080fd5b833567ffffffffffffffff8111156133f757600080fd5b61340386828701613386565b909790965060209590950135949350505050565b6000806020838503121561342a57600080fd5b823567ffffffffffffffff81111561344157600080fd5b61321285828601613386565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561349157600080fd5b833561349c81613125565b925060208401359150604084013567ffffffffffffffff808211156134c057600080fd5b818601915086601f8301126134d457600080fd5b8135818111156134e6576134e661344d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561352c5761352c61344d565b8160405282815289602084870101111561354557600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b600181811c9082168061357b57607f821691505b6020821081036135b4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156135cc57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015613614576136146135d3565b500390565b6000825161362b818460208701613083565b9190910192915050565b60006020828403121561364757600080fd5b8151610c4081613322565b60006020828403121561366457600080fd5b8151610c4081613125565b600080835481600182811c91508083168061368b57607f831692505b602080841082036136c3577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b8180156136d7576001811461370657613733565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528489019650613733565b60008a81526020902060005b8681101561372b5781548b820152908501908301613712565b505084890196505b509498975050505050505050565b60008219821115613754576137546135d3565b500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615613791576137916135d3565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561380657600080fd5b813560028110610c4057600080fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613846576138466135d3565b506001019056fea26469706673582212207c6cee41972f5bf0639a19fc3a0e4f5b6dfe163796a3965a83b38ba31af1a5af64736f6c634300080d0033000000000000000000000000000000000000000000000000000000000000002000000000000000000000000094106ca9c7e567109a1d39413052887d1f41218300000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f7000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000002113363253ecb409a816ddfed305dadc51e560fb000000000000000000000000899e57d9858e39c64fbb24fa7d9d0f993158e178000000000000000000000000000000000000000000000000000000001dcd6500ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000010fb0bbece3c5b893563bcb8850403eacaeae30c00000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000971a8ad00000000000000000000000000000000000000000000000000000000000000325350445220504f5254464f4c494f2041474752454741544520424f4e442045544620506f7274666f6c696f2053686172657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007535041422d505300000000000000000000000000000000000000000000000000

Deployed Bytecode

0x6080604052600436106103765760003560e01c806367a52793116101d1578063c6e6f59211610102578063e37a8754116100a0578063f029748d1161006f578063f029748d14610afd578063fc7bb06114610b1d578063fef7849714610b51578063ff5ac9a414610b6457600080fd5b8063e37a875414610a87578063e37d15bf14610aa7578063e77c646d14610abd578063e83bb71414610add57600080fd5b8063d505accf116100dc578063d505accf146109ef578063d7ad932014610a0f578063dd62ed3e14610a2f578063e1a8eafd14610a6757600080fd5b8063c6e6f59214610982578063cbb94359146109a2578063ce1b1d43146109c257600080fd5b806396724a971161016f578063bf7e214f11610149578063bf7e214f146108e1578063bfa37e371461090e578063c37428e51461092e578063c55dae631461094e57600080fd5b806396724a971461089857806399aa7348146108cc578063a9059cbb146103e557600080fd5b80637dbc1df0116101ab5780637dbc1df0146108165780637ecebe00146108365780638fcc9cfb1461086357806395d89b411461088357600080fd5b806367a527931461079f57806370a08231146107b55780637b103999146107e257600080fd5b8063313ce567116102ab57806347786d37116102495780635bae19ef116102235780635bae19ef146107125780635d3035191461073257806360dc2340146107525780636549315d1461077f57600080fd5b806347786d37146106b6578063490ae210146106d85780635a41217e146106f857600080fd5b806338d52e0f1161028557806338d52e0f146105fd57806341b3d185146106565780634288d8711461066c578063458f5815146106a057600080fd5b8063313ce5671461058c578063355274ea146105d25780633644e515146105e857600080fd5b806318160ddd116103185780631e7ee237116102f25780631e7ee237146104d85780632031ee95146104f257806323b872dd1461053d57806330adf81f1461055857600080fd5b806318160ddd146104775780631924b9061461048d5780631e500759146104ba57600080fd5b8063095ea7b311610354578063095ea7b3146103e55780630a9d5514146104155780630aa4efa71461042a578063106f276f1461045757600080fd5b806301e1d1141461037b57806306fdde03146103a357806307a2d13a146103c5575b600080fd5b34801561038757600080fd5b50610390610b7a565b6040519081526020015b60405180910390f35b3480156103af57600080fd5b506103b8610b8c565b60405161039a91906130f9565b3480156103d157600080fd5b506103906103e036600461310c565b610c1a565b3480156103f157600080fd5b5061040561040036600461314a565b610c47565b604051901515815260200161039a565b34801561042157600080fd5b50610390610c7b565b34801561043657600080fd5b50610390610445366004613176565b60136020526000908152604090205481565b34801561046357600080fd5b506103906104723660046131dc565b610d2c565b34801561048357600080fd5b5061039060025481565b34801561049957600080fd5b506103906104a8366004613176565b60146020526000908152604090205481565b3480156104c657600080fd5b506103906104d536600461310c565b90565b3480156104e457600080fd5b50600d546104059060ff1681565b3480156104fe57600080fd5b5060075461050c9060601b81565b6040517fffffffffffffffffffffffffffffffffffffffff000000000000000000000000909116815260200161039a565b34801561054957600080fd5b5061040561040036600461321e565b34801561056457600080fd5b506103907f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c981565b34801561059857600080fd5b506105c07f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff909116815260200161039a565b3480156105de57600080fd5b5061039060095481565b3480156105f457600080fd5b50610390610e4f565b34801561060957600080fd5b506106317f00000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f781565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161039a565b34801561066257600080fd5b5061039060115481565b34801561067857600080fd5b506106317f00000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f781565b3480156106ac57600080fd5b50610390600c5481565b3480156106c257600080fd5b506106d66106d136600461310c565b610ea5565b005b3480156106e457600080fd5b506106d66106f336600461310c565b610f45565b34801561070457600080fd5b506012546104059060ff1681565b34801561071e57600080fd5b506106d661072d36600461310c565b61101a565b34801561073e57600080fd5b5061039061074d36600461325f565b6110f9565b34801561075e57600080fd5b50600a546106319073ffffffffffffffffffffffffffffffffffffffff1681565b34801561078b57600080fd5b506106d661079a36600461314a565b611317565b3480156107ab57600080fd5b50610390600b5481565b3480156107c157600080fd5b506103906107d0366004613176565b60036020526000908152604090205481565b3480156107ee57600080fd5b506106317f00000000000000000000000094106ca9c7e567109a1d39413052887d1f41218381565b34801561082257600080fd5b506106d661083136600461310c565b6113d9565b34801561084257600080fd5b50610390610851366004613176565b60056020526000908152604090205481565b34801561086f57600080fd5b506106d661087e36600461310c565b6114ae565b34801561088f57600080fd5b506103b8611547565b3480156108a457600080fd5b506104057f000000000000000000000000000000000000000000000000000000000000000181565b3480156108d857600080fd5b506106d6611554565b3480156108ed57600080fd5b506006546106319073ffffffffffffffffffffffffffffffffffffffff1681565b34801561091a57600080fd5b506106d6610929366004613176565b611644565b34801561093a57600080fd5b506106d661094936600461314a565b61171b565b34801561095a57600080fd5b506106317f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b34801561098e57600080fd5b5061039061099d36600461310c565b6117d1565b3480156109ae57600080fd5b506106d66109bd36600461310c565b6117f1565b3480156109ce57600080fd5b506010546106319073ffffffffffffffffffffffffffffffffffffffff1681565b3480156109fb57600080fd5b506106d6610a0a3660046132ab565b6118cc565b348015610a1b57600080fd5b506106d6610a2a366004613330565b6118fe565b348015610a3b57600080fd5b50610390610a4a36600461334d565b600460209081526000928352604080842090915290825290205481565b348015610a7357600080fd5b506106d6610a82366004613176565b6119c1565b348015610a9357600080fd5b506106d6610aa23660046133cb565b611a98565b348015610ab357600080fd5b50610390600f5481565b348015610ac957600080fd5b50610390610ad836600461325f565b611bb4565b348015610ae957600080fd5b506106d6610af8366004613417565b611d9e565b348015610b0957600080fd5b50610390610b1836600461310c565b611e18565b348015610b2957600080fd5b506106317f0000000000000000000000002113363253ecb409a816ddfed305dadc51e560fb81565b6103b8610b5f36600461347c565b611ed6565b348015610b7057600080fd5b50610390600e5481565b6000610b876104d5610c7b565b905090565b60008054610b9990613567565b80601f0160208091040260200160405190810160405280929190818152602001828054610bc590613567565b8015610c125780601f10610be757610100808354040283529160200191610c12565b820191906000526020600020905b815481529060010190602001808311610bf557829003601f168201915b505050505081565b6002546000908015610c3e57610c39610c31610b7a565b8490836120bb565b610c40565b825b9392505050565b60006040517fc31c949300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f773ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015610d08573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b8791906135ba565b6000610d5c336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b610d92576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d5460ff1615610dcf576040517f2a542da400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660011790556000610e04610b7a565b6040805160008082526020820181905282519394509283927fbf74a2393b907f335184e6dbeb4daa93812b077b8e034c2e61c8e6864002dda7928290030190a1925050505b92915050565b60007f00000000000000000000000000000000000000000000000000000000000000014614610e8057610b876122b6565b507ff0583e86cc1a6e8e6772258829dbd9335f3b58ce64c1721ca32a5e69b5e0feb290565b610ed3336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b610f09576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60098190556040518181527f9872d5eb566b79923d043f1b59aca655ca80a2bb5b6bca4824e515b0e398902f906020015b60405180910390a150565b610f73336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b610fa9576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710811115610fe5576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600b8190556040518181527f12865465a7036a0232cbf9fb63ce880a3ee54f702775fe7abbc3c416e7968cd590602001610f3a565b611048336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b61107e576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b63bce02f4e8111156110bc576040517f6a43f8d100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6110c4611554565b600f8190556040518181527fad2a1b372bf4c8402696d0ed78880c15faa5b69298ede2615a228810a52dc6c490602001610f3a565b600060085460011461116c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e43590000000000000000000000000000000000000000000060448201526064015b60405180910390fd5b6002600855600d5460ff16156111ae576040517f326eab7500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111b733612350565b6111ed576040517f184849cf00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6111f5611554565b6000806000611205878787612405565b9250925092507f00000000000000000000000000000000000000000000000000000000000000016112b95761123c8587018761310c565b831015611275576040517f7dd37f7000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b826000036112af576040517fc440e0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6112b93384612550565b604080518381526020810185905290810188905260608101829052339081907f5f971bd00bf3ffbca8a6d72cdd4fd92cfd4f62636161921d1e5a64f0b64ccb6d9060800160405180910390a350909150505b60016008559392505050565b611345336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b61137b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6113858282612550565b8173ffffffffffffffffffffffffffffffffffffffff167fa58661fc88ea7766ebe1d611a5040787ee19b571518d348deed432fb94344213826040516113cd91815260200190565b60405180910390a25050565b611407336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b61143d576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612710811115611479576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c8190556040518181527f91cc643d187eb250905520d3dae0b1017edd16961d27ab9a4a61fea5e38f717d90602001610f3a565b6114dc336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611512576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60118190556040518181527fc50a7f0bdf88c216b2541b0bdea26f22305460e39ffc672ec1a7501732c5ba8190602001610f3a565b60018054610b9990613567565b600d5460ff1615611569576115676125c9565b565b6000611573610c7b565b90506000600e54426115859190613602565b9050600061159383836126f8565b9050828111156115a05750815b60008111806115af5750600254155b1561163f5742600e55801561163f57600a546116059073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f78116911683612735565b60408051828152602081018490527fc5bd0ea7a1520a37af6ec1d295e0b4f9820e066c078ae82f4abcc49286ab1028910160405180910390a15b505050565b611672336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b6116a8576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527ffa91b2d5be9caed9cc205220f1d5f3c5fee252dea3faf893928e0e639c0ccc7090602001610f3a565b611749336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b61177f576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61178982826127fc565b8173ffffffffffffffffffffffffffffffffffffffff167f76583919d2167957eef2c3fe269d6b8d6611a76c9313dd64a34fa3fe80457aa8826040516113cd91815260200190565b6002546000908015610c3e57610c39816117e9610b7a565b8591906120bb565b61181f336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611855576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600a5461189c9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f78116911683612735565b6040518181527ffba1cbbf893e6a61412440b48fca1c80a5bf24d2f7deb6d5544717745077a36090602001610f3a565b6040517fc31c949300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61192c336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611962576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00168215159081179091556040519081527f297471d8aee523d9bc01eb14e40f77893764adf15db1357c529e38d99486b75f90602001610f3a565b6119ef336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611a25576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b601080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83169081179091556040519081527fd42d4de5d9e033ca6d7d5f77d77dcfc293d4145f08ded95fb625bc366cef017a90602001610f3a565b611ac6336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611afc576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611b04611554565b6040517f40c10f19000000000000000000000000000000000000000000000000000000008152306004820152602481018290527f00000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f773ffffffffffffffffffffffffffffffffffffffff16906340c10f1990604401600060405180830381600087803b158015611b9257600080fd5b505af1158015611ba6573d6000803e3d6000fd5b5050505061163f838361288a565b6000600854600114611c22576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152606401611163565b6002600855611c2f611554565b600d5460ff1615611c4a57611c43846129be565b905061130b565b600080611c58868686612ac6565b91509150611c6633876127fc565b81600003611ca0576040517fc440e0aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808215611d4257611cb583600c54612bd8565b600a549193509150611d019073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488116911683612735565b611d4273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48163384612735565b60408051858152602081018a905290810183905260608101829052339081907f8caf04742286d017f9ac3924388e188c73e6e5094311c5e59a61a7ef86dda8bf9060800160405180910390a35060016008559695505050505050565b611dcc336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611e02576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611e0a611554565b611e14828261288a565b5050565b6002546000908015610c3e576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152610c39907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015611eb2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c3191906135ba565b6060611f06336000357fffffffff00000000000000000000000000000000000000000000000000000000166120da565b611f3c576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000808573ffffffffffffffffffffffffffffffffffffffff168585604051611f659190613619565b60006040518083038185875af1925050503d8060008114611fa2576040519150601f19603f3d011682016040523d82523d6000602084013e611fa7565b606091505b509150915081611fe557806040517fa5fa8d2b00000000000000000000000000000000000000000000000000000000815260040161116391906130f9565b95945050505050565b60006040517f095ea7b300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af191505061204f81612c2c565b6120b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f415050524f56455f4641494c45440000000000000000000000000000000000006044820152606401611163565b50505050565b8282028115158415858304851417166120d357600080fd5b0492915050565b60065460075460009173ffffffffffffffffffffffffffffffffffffffff1690829060601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016156121445760075473ffffffffffffffffffffffffffffffffffffffff16612146565b305b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff878116600483015280831660248301527fffffffff00000000000000000000000000000000000000000000000000000000871660448301529192509083169063b700961390606401602060405180830381865afa1580156121e6573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061220a9190613635565b80611fe557508173ffffffffffffffffffffffffffffffffffffffff16638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561225b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061227f9190613652565b73ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff161495945050505050565b60007f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f60006040516122e8919061366f565b6040805191829003822060208301939093528101919091527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b6040517fea3fff6800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82811660048301526000917f00000000000000000000000094106ca9c7e567109a1d39413052887d1f4121839091169063ea3fff6890602401602060405180830381865afa1580156123e1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e499190613635565b6000806000601154861015612446576040517f11bcd83000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60125460ff1615612483576040517f4dfbeb3f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008061249288600b54612bd8565b60105491935091506124e09073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881169133911685612c73565b600a546125299073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881169133911684612c73565b33600090815260136020526040812080549390930190925590978897509095509350505050565b80600260008282546125629190613741565b909155505073ffffffffffffffffffffffffffffffffffffffff82166000818152600360209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef91015b60405180910390a35050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4873ffffffffffffffffffffffffffffffffffffffff16906370a0823190602401602060405180830381865afa158015612656573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061267a91906135ba565b90506000600e544261268c9190613602565b9050600061269a83836126f8565b9050828111156126a75750815b801561163f5742600e55600a546116059073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488116911683612735565b60008215806127075750600f54155b80612710575081155b1561271d57506000610e49565b610c40600f548361272e9190613759565b8490612d5d565b60006040517fa9059cbb00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201528260248201526000806044836000895af191505061279681612c2c565b6120b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152606401611163565b73ffffffffffffffffffffffffffffffffffffffff821660009081526003602052604081208054839290612831908490613602565b909155505060028054829003905560405181815260009073ffffffffffffffffffffffffffffffffffffffff8416907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef906020016125bd565b60005b8181101561163f5760008383838181106128a9576128a96137c5565b6128bf92602060809092020190810191506137f4565b60018111156128d0576128d0613796565b036129445761293f8383838181106128ea576128ea6137c5565b90506080020160200160208101906129029190613176565b848484818110612914576129146137c5565b90506080020160400135858585818110612930576129306137c5565b90506080020160600135612d72565b6129ae565b6129ae838383818110612959576129596137c5565b90506080020160200160208101906129719190613176565b848484818110612983576129836137c5565b9050608002016040013585858581811061299f5761299f6137c5565b90506080020160600135612ec9565b6129b781613815565b905061288d565b6000806129ca83611e18565b90506129d633846127fc565b6000806129e583600c54612bd8565b600a549193509150612a319073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488116911683612735565b612a7273ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48163384612735565b604080518481526020810187905290810183905260608101829052339081907f8caf04742286d017f9ac3924388e188c73e6e5094311c5e59a61a7ef86dda8bf9060800160405180910390a3509392505050565b601254600090819060ff1615612b08576040517f4dfbeb3f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6000612b1386610c1a565b3360009081526014602052604090819020805483019055517f42966c680000000000000000000000000000000000000000000000000000000081526004810182905290915073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f716906342966c6890602401600060405180830381600087803b158015612bb357600080fd5b505af1158015612bc7573d6000803e3d6000fd5b509298600098509650505050505050565b600080612710831115612c17576040517ff08319c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b612c21848461306b565b938490039492505050565b60003d82612c3e57806000803e806000fd5b8060208114612c56578015612c675760009250612c6c565b816000803e60005115159250612c6c565b600192505b5050919050565b60006040517f23b872dd00000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8516600482015273ffffffffffffffffffffffffffffffffffffffff8416602482015282604482015260008060648360008a5af1915050612cf081612c2c565b612d56576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5452414e534645525f46524f4d5f4641494c45440000000000000000000000006044820152606401611163565b5050505050565b6000610c408383670de0b6b3a76400006120bb565b73ffffffffffffffffffffffffffffffffffffffff831660009081526013602052604081208054849290612da7908490613602565b9091555060009050612db8826117d1565b9050612dc48482612550565b6040517f40c10f19000000000000000000000000000000000000000000000000000000008152306004820152602481018390527f00000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f773ffffffffffffffffffffffffffffffffffffffff16906340c10f1990604401600060405180830381600087803b158015612e5257600080fd5b505af1158015612e66573d6000803e3d6000fd5b5050604080518681526020810186905290810184905273ffffffffffffffffffffffffffffffffffffffff871692507fc6d9b758e81c9b14011e8efdc0d39baabb216903c6e6c11762cdd0a7bc13e121915060600160405180910390a250505050565b73ffffffffffffffffffffffffffffffffffffffff831660009081526014602052604081208054839290612efe908490613602565b92505081905550600080612f1484600c54612bd8565b6040517f6895db3400000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8881166004830152602482018490529294509092507f0000000000000000000000002113363253ecb409a816ddfed305dadc51e560fb90911690636895db3490604401600060405180830381600087803b158015612fac57600080fd5b505af1158015612fc0573d6000803e3d6000fd5b5050600a5461300b925073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48811692501683612735565b604080518381526020810185905290810182905273ffffffffffffffffffffffffffffffffffffffff8616907f69873b904850da89f6919c238b6542f58bae21dbe54bb481bd2e93a59de15a6b9060600160405180910390a25050505050565b60006130778284613759565b61271090049392505050565b60005b8381101561309e578181015183820152602001613086565b838111156120b55750506000910152565b600081518084526130c7816020860160208601613083565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c4060208301846130af565b60006020828403121561311e57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff8116811461314757600080fd5b50565b6000806040838503121561315d57600080fd5b823561316881613125565b946020939093013593505050565b60006020828403121561318857600080fd5b8135610c4081613125565b60008083601f8401126131a557600080fd5b50813567ffffffffffffffff8111156131bd57600080fd5b6020830191508360208285010111156131d557600080fd5b9250929050565b600080602083850312156131ef57600080fd5b823567ffffffffffffffff81111561320657600080fd5b61321285828601613193565b90969095509350505050565b60008060006060848603121561323357600080fd5b833561323e81613125565b9250602084013561324e81613125565b929592945050506040919091013590565b60008060006040848603121561327457600080fd5b83359250602084013567ffffffffffffffff81111561329257600080fd5b61329e86828701613193565b9497909650939450505050565b600080600080600080600060e0888a0312156132c657600080fd5b87356132d181613125565b965060208801356132e181613125565b95506040880135945060608801359350608088013560ff8116811461330557600080fd5b9699959850939692959460a0840135945060c09093013592915050565b801515811461314757600080fd5b60006020828403121561334257600080fd5b8135610c4081613322565b6000806040838503121561336057600080fd5b823561336b81613125565b9150602083013561337b81613125565b809150509250929050565b60008083601f84011261339857600080fd5b50813567ffffffffffffffff8111156133b057600080fd5b6020830191508360208260071b85010111156131d557600080fd5b6000806000604084860312156133e057600080fd5b833567ffffffffffffffff8111156133f757600080fd5b61340386828701613386565b909790965060209590950135949350505050565b6000806020838503121561342a57600080fd5b823567ffffffffffffffff81111561344157600080fd5b61321285828601613386565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60008060006060848603121561349157600080fd5b833561349c81613125565b925060208401359150604084013567ffffffffffffffff808211156134c057600080fd5b818601915086601f8301126134d457600080fd5b8135818111156134e6576134e661344d565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190838211818310171561352c5761352c61344d565b8160405282815289602084870101111561354557600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b600181811c9082168061357b57607f821691505b6020821081036135b4577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b6000602082840312156135cc57600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600082821015613614576136146135d3565b500390565b6000825161362b818460208701613083565b9190910192915050565b60006020828403121561364757600080fd5b8151610c4081613322565b60006020828403121561366457600080fd5b8151610c4081613125565b600080835481600182811c91508083168061368b57607f831692505b602080841082036136c3577f4e487b710000000000000000000000000000000000000000000000000000000086526022600452602486fd5b8180156136d7576001811461370657613733565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00861689528489019650613733565b60008a81526020902060005b8681101561372b5781548b820152908501908301613712565b505084890196505b509498975050505050505050565b60008219821115613754576137546135d3565b500190565b6000817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615613791576137916135d3565b500290565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b60006020828403121561380657600080fd5b813560028110610c4057600080fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613846576138466135d3565b506001019056fea26469706673582212207c6cee41972f5bf0639a19fc3a0e4f5b6dfe163796a3965a83b38ba31af1a5af64736f6c634300080d0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000000000000000000000000000000000000000002000000000000000000000000094106ca9c7e567109a1d39413052887d1f41218300000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f7000000000000000000000000000000000000000000000000000000000000018000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000002113363253ecb409a816ddfed305dadc51e560fb000000000000000000000000899e57d9858e39c64fbb24fa7d9d0f993158e178000000000000000000000000000000000000000000000000000000001dcd6500ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000000000000000010fb0bbece3c5b893563bcb8850403eacaeae30c00000000000000000000000000000000000000000000000000000000000000190000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000971a8ad00000000000000000000000000000000000000000000000000000000000000325350445220504f5254464f4c494f2041474752454741544520424f4e442045544620506f7274666f6c696f2053686172657300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007535041422d505300000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : _args (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
18 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000020
Arg [1] : 00000000000000000000000094106ca9c7e567109a1d39413052887d1f412183
Arg [2] : 00000000000000000000000044ccce78b08c97a07fc3758e24f7bd8e808365f7
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [4] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [5] : 0000000000000000000000002113363253ecb409a816ddfed305dadc51e560fb
Arg [6] : 000000000000000000000000899e57d9858e39c64fbb24fa7d9d0f993158e178
Arg [7] : 000000000000000000000000000000000000000000000000000000001dcd6500
Arg [8] : ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Arg [9] : 00000000000000000000000010fb0bbece3c5b893563bcb8850403eacaeae30c
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000019
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [12] : 000000000000000000000000000000000000000000000000000000000971a8ad
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000032
Arg [14] : 5350445220504f5254464f4c494f2041474752454741544520424f4e44204554
Arg [15] : 4620506f7274666f6c696f205368617265730000000000000000000000000000
Arg [16] : 0000000000000000000000000000000000000000000000000000000000000007
Arg [17] : 535041422d505300000000000000000000000000000000000000000000000000


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.