BaseBase
0x80...98e2
ArcadiaV2 Wrapped Ether Debt

ArcadiaV2 Wrapped Ether Debt

darcV2WETH

代币
市值
$1.00
 
价格
2%
此合同的源代码已经过验证!
合同元数据
编译器
0.8.22+commit.4fc1097e
语言
Solidity
合同源代码
文件 1 的 20:AssetValuationLib.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity 0.8.22;

// Struct with risk and valuation related information for a certain asset.
struct AssetValueAndRiskFactors {
    // The value of the asset.
    uint256 assetValue;
    // The collateral factor of the asset, for a given creditor.
    uint256 collateralFactor;
    // The liquidation factor of the asset, for a given creditor.
    uint256 liquidationFactor;
}

/**
 * @title Asset Valuation Library
 * @author Pragma Labs
 * @notice The Asset Valuation Library is responsible for calculating the risk weighted values of combinations of assets.
 */
library AssetValuationLib {
    /*///////////////////////////////////////////////////////////////
                        CONSTANTS
    ///////////////////////////////////////////////////////////////*/

    uint256 internal constant ONE_4 = 10_000;

    /*///////////////////////////////////////////////////////////////
                        RISK FACTORS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Calculates the collateral value given a combination of asset values and corresponding collateral factors.
     * @param valuesAndRiskFactors Array of asset values and corresponding collateral factors.
     * @return collateralValue The collateral value of the given assets.
     */
    function _calculateCollateralValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
        internal
        pure
        returns (uint256 collateralValue)
    {
        for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
            collateralValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].collateralFactor;
        }
        collateralValue = collateralValue / ONE_4;
    }

    /**
     * @notice Calculates the liquidation value given a combination of asset values and corresponding liquidation factors.
     * @param valuesAndRiskFactors List of asset values and corresponding liquidation factors.
     * @return liquidationValue The liquidation value of the given assets.
     */
    function _calculateLiquidationValue(AssetValueAndRiskFactors[] memory valuesAndRiskFactors)
        internal
        pure
        returns (uint256 liquidationValue)
    {
        for (uint256 i; i < valuesAndRiskFactors.length; ++i) {
            liquidationValue += valuesAndRiskFactors[i].assetValue * valuesAndRiskFactors[i].liquidationFactor;
        }
        liquidationValue = liquidationValue / ONE_4;
    }
}
合同源代码
文件 2 的 20:BalancerErrors.sol
// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

pragma solidity 0.8.22;

// solhint-disable

/**
 * @dev Reverts if `condition` is false, with a revert reason containing `errorCode`. Only codes up to 999 are
 * supported.
 */
function _require(bool condition, uint256 errorCode) pure {
    if (!condition) {
        _revert(errorCode);
    }
}

/**
 * @dev Reverts with a revert reason containing `errorCode`. Only codes up to 999 are supported.
 */
function _revert(uint256 errorCode) pure {
    // We're going to dynamically create a revert string based on the error code, with the following format:
    // 'BAL#{errorCode}'
    // where the code is left-padded with zeroes to three digits (so they range from 000 to 999).
    //
    // We don't have revert strings embedded in the contract to save bytecode size: it takes much less space to store a
    // number (8 to 16 bits) than the individual string characters.
    //
    // The dynamic string creation algorithm that follows could be implemented in Solidity, but assembly allows for a
    // much denser implementation, again saving bytecode size. Given this function unconditionally reverts, this is a
    // safe place to rely on it without worrying about how its usage might affect e.g. memory contents.
    assembly {
        // First, we need to compute the ASCII representation of the error code. We assume that it is in the 0-999
        // range, so we only need to convert three digits. To convert the digits to ASCII, we add 0x30, the value for
        // the '0' character.

        let units := add(mod(errorCode, 10), 0x30)

        errorCode := div(errorCode, 10)
        let tenths := add(mod(errorCode, 10), 0x30)

        errorCode := div(errorCode, 10)
        let hundreds := add(mod(errorCode, 10), 0x30)

        // With the individual characters, we can now construct the full string. The "BAL#" part is a known constant
        // (0x42414c23): we simply shift this by 24 (to provide space for the 3 bytes of the error code), and add the
        // characters to it, each shifted by a multiple of 8.
        // The revert reason is then shifted left by 200 bits (256 minus the length of the string, 7 characters * 8 bits
        // per character = 56) to locate it in the most significant part of the 256 slot (the beginning of a byte
        // array).

        let revertReason := shl(200, add(0x42414c23000000, add(add(units, shl(8, tenths)), shl(16, hundreds))))

        // We can now encode the reason in memory, which can be safely overwritten as we're about to revert. The encoded
        // message will have the following layout:
        // [ revert reason identifier ] [ string location offset ] [ string length ] [ string contents ]

        // The Solidity revert reason identifier is 0x08c739a0, the function selector of the Error(string) function. We
        // also write zeroes to the next 28 bytes of memory, but those are about to be overwritten.
        mstore(0x0, 0x08c379a000000000000000000000000000000000000000000000000000000000)
        // Next is the offset to the location of the string, which will be placed immediately after (20 bytes away).
        mstore(0x04, 0x0000000000000000000000000000000000000000000000000000000000000020)
        // The string length is fixed: 7 characters.
        mstore(0x24, 7)
        // Finally, the string itself is stored.
        mstore(0x44, revertReason)

        // Even if the string is only 7 bytes long, we need to return a full 32 byte slot containing it. The length of
        // the encoded message is therefore 4 + 32 + 32 + 32 = 100.
        revert(0, 100)
    }
}

library Errors {
    // Math
    uint256 internal constant X_OUT_OF_BOUNDS = 6;
    uint256 internal constant Y_OUT_OF_BOUNDS = 7;
    uint256 internal constant PRODUCT_OUT_OF_BOUNDS = 8;
    uint256 internal constant INVALID_EXPONENT = 9;
}
合同源代码
文件 3 的 20:BaseGuardian.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity 0.8.22;

import { GuardianErrors } from "../libraries/Errors.sol";
import { Owned } from "../../lib/solmate/src/auth/Owned.sol";

/**
 * @title Guardian
 * @author Pragma Labs
 * @notice Abstract contract with the minimal implementation of a Guardian contract.
 * It implements the following logic:
 * - An authorized guardian can trigger an emergency stop.
 * - The protocol owner can unpause functionalities one-by-one.
 * - Anyone can unpause all functionalities after a fixed cool-down period.
 */
abstract contract BaseGuardian is Owned {
    /* //////////////////////////////////////////////////////////////
                                STORAGE
    ////////////////////////////////////////////////////////////// */

    // Last timestamp an emergency stop was triggered.
    uint96 public pauseTimestamp;
    // Address of the Guardian.
    address public guardian;

    /* //////////////////////////////////////////////////////////////
                                EVENTS
    ////////////////////////////////////////////////////////////// */

    event GuardianChanged(address indexed user, address indexed newGuardian);

    /* //////////////////////////////////////////////////////////////
                                MODIFIERS
    ////////////////////////////////////////////////////////////// */

    /**
     * @dev Only guardians can call functions with this modifier.
     */
    modifier onlyGuardian() {
        if (msg.sender != guardian) revert GuardianErrors.OnlyGuardian();
        _;
    }

    /**
     * @dev The public unpause() function, or a second pause() function, can only called a fixed coolDownPeriod after an initial pause().
     * This gives the protocol owner time to investigate and solve potential issues,
     * but ensures that no rogue owner or guardian can lock user funds for an indefinite amount of time.
     */
    modifier afterCoolDownOf(uint256 coolDownPeriod) {
        if (block.timestamp <= pauseTimestamp + coolDownPeriod) revert GuardianErrors.CoolDownPeriodNotPassed();
        _;
    }

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

    constructor() Owned(msg.sender) { }

    /* //////////////////////////////////////////////////////////////
                            GUARDIAN LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice This function is used to set the guardian address
     * @param guardian_ The address of the new guardian.
     */
    function changeGuardian(address guardian_) external onlyOwner {
        emit GuardianChanged(msg.sender, guardian = guardian_);
    }

    /* //////////////////////////////////////////////////////////////
                            PAUSING LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice This function is used to pause all the flags of the contract.
     * @dev The Guardian can only pause the protocol again after 32 days have passed since the last pause.
     * This is to prevent that a malicious owner or guardian can take user funds hostage for an indefinite time.
     * After the guardian has paused the protocol, the owner has 30 days to find potential problems,
     * find a solution and unpause the protocol. If the protocol is not unpaused after 30 days,
     * an emergency procedure can be started by any user to unpause the protocol.
     * All users have now at least a two-day window to withdraw assets and close positions before
     * the protocol can again be paused 32 days after the contract was previously paused.
     */
    function pause() external virtual;

    /**
     * @notice This function is used to unpause flags that could be abused to lock user assets.
     * @dev If the protocol is not unpaused after 30 days, any user can unpause the protocol.
     * This ensures that no rogue owner or guardian can lock user funds for an indefinite amount of time.
     * All users have now at least a two-day window to withdraw assets and close positions before
     * the protocol can again be paused 32 days after the contract was previously paused.
     */
    function unpause() external virtual;
}
合同源代码
文件 4 的 20:Creditor.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity 0.8.22;

import { ICreditor } from "../interfaces/ICreditor.sol";

/**
 * @title Creditor.
 * @author Pragma Labs
 * @notice See the documentation in ICreditor
 */
abstract contract Creditor is ICreditor {
    /* //////////////////////////////////////////////////////////////
                                STORAGE
    ////////////////////////////////////////////////////////////// */

    // The address of the riskManager.
    address public riskManager;

    // The Account for which a flashAction is called.
    address internal callbackAccount;

    // Map accountVersion => status.
    mapping(uint256 => bool) public isValidVersion;

    /* //////////////////////////////////////////////////////////////
                                EVENTS
    ////////////////////////////////////////////////////////////// */

    event RiskManagerUpdated(address riskManager);
    event ValidAccountVersionsUpdated(uint256 indexed accountVersion, bool valid);

    /* //////////////////////////////////////////////////////////////
                                ERRORS
    ////////////////////////////////////////////////////////////// */

    // Thrown when caller is not authorized.
    error Unauthorized();

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

    /**
     * @param riskManager_ The address of the Risk Manager.
     */
    constructor(address riskManager_) {
        _setRiskManager(riskManager_);
    }

    /* //////////////////////////////////////////////////////////////
                            ACCOUNT LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Sets a new Risk Manager. A Risk Manager can:
     * - Set risk parameters for collateral assets, including: max exposures, collateral factors and liquidation factors.
     * - Set minimum usd values for assets in order to be taken into account, to prevent dust attacks.
     * @param riskManager_ The address of the new Risk Manager.
     */
    function _setRiskManager(address riskManager_) internal {
        riskManager = riskManager_;

        emit RiskManagerUpdated(riskManager_);
    }

    /**
     * @notice Updates the validity of an Account version.
     * @param accountVersion The Account version.
     * @param isValid Will be "true" if respective Account version is valid, "false" if not.
     */
    function _setAccountVersion(uint256 accountVersion, bool isValid) internal {
        isValidVersion[accountVersion] = isValid;

        emit ValidAccountVersionsUpdated(accountVersion, isValid);
    }

    /**
     * @inheritdoc ICreditor
     * @dev This function response is used for the Arcadia Accounts margin account creation.
     * This function does not deploy a new Arcadia Account.
     * It just provides the parameters to be used in Arcadia Account to connect to the Creditor.
     */
    function openMarginAccount(uint256 accountVersion)
        external
        virtual
        returns (bool success, address numeraire, address liquidator, uint256 minimumMargin);

    /**
     * @inheritdoc ICreditor
     * @dev This function checks if the given Account address has an open position. If not, it can be closed.
     */
    function closeMarginAccount(address account) external virtual;

    /**
     * @inheritdoc ICreditor
     * @dev The open position is the sum of all liabilities.
     */
    function getOpenPosition(address account) external view virtual returns (uint256 openPosition);

    /**
     * @inheritdoc ICreditor
     * @dev During the callback, the Account cannot be reentered and the actionTimestamp is updated.
     */
    function flashActionCallback(bytes calldata callbackData) external virtual {
        // Only the Account for which the flashAction is initiated is allowed to callback.
        if (callbackAccount != msg.sender) revert Unauthorized();

        // Reset callbackAccount, the Account should only callback once during a flashAction.
        callbackAccount = address(0);

        _flashActionCallback(msg.sender, callbackData);
    }

    /**
     * @notice Callback of the Account during a flashAction.
     * @param account The contract address of the Arcadia Account.
     * @param callbackData The data for the actions that have to be executed by the Creditor during a flashAction.
     * @dev During the callback, the Account cannot be reentered and the actionTimestamp is updated.
     */
    function _flashActionCallback(address account, bytes calldata callbackData) internal virtual;

    /**
     * @inheritdoc ICreditor
     * @dev Starts the liquidation process in the Creditor.
     * This function should be callable by Arcadia Account.
     */
    function startLiquidation(address initiator, uint256 minimumMargin)
        external
        virtual
        returns (uint256 openPosition);
}
合同源代码
文件 5 的 20:DebtToken.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity 0.8.22;

import { ERC20, ERC4626 } from "../lib/solmate/src/mixins/ERC4626.sol";
import { FixedPointMathLib } from "../lib/solmate/src/utils/FixedPointMathLib.sol";
import { DebtTokenErrors } from "./libraries/Errors.sol";

/**
 * @title Debt Token.
 * @author Pragma Labs
 * @notice The Logic to do the debt accounting for a lending pool for a certain ERC20 token.
 * @dev Protocol is according the ERC4626 standard, with a certain ERC20 as underlying.
 * @dev Implementation slightly deviates from the ERC4626 specifications,
 * maxDeposit() and maxMint() are not implemented.
 */
abstract contract DebtToken is ERC4626 {
    using FixedPointMathLib for uint256;

    /* //////////////////////////////////////////////////////////////
                                STORAGE
    ////////////////////////////////////////////////////////////// */

    // Total amount of `underlying asset` that debtors have in debt, does not take into account pending interests.
    uint256 internal realisedDebt;

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

    /**
     * @notice The constructor for the debt token.
     * @param asset_ The underlying ERC20 token in which the debt is denominated.
     */
    constructor(ERC20 asset_)
        ERC4626(
            asset_,
            string(abi.encodePacked("ArcadiaV2 ", asset_.name(), " Debt")),
            string(abi.encodePacked("darcV2", asset_.symbol()))
        )
    { }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns the total amount of outstanding debt in the underlying asset.
     * @return totalDebt The total debt in underlying assets.
     * @dev Implementation overwritten in LendingPool.sol which inherits DebtToken.sol.
     * Implementation not vulnerable to ERC4626 inflation attacks,
     * totalAssets() does not rely on balanceOf call.
     */
    function totalAssets() public view virtual override returns (uint256);

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Modification of the standard ERC4626 deposit implementation.
     * @dev No public deposit allowed.
     */
    function deposit(uint256, address) public pure override returns (uint256) {
        revert DebtTokenErrors.FunctionNotImplemented();
    }

    /**
     * @notice Modification of the standard ERC4626 deposit implementation.
     * @param assets The amount of assets of the underlying ERC20 token being loaned out.
     * @param receiver The Arcadia Account with collateral covering the debt.
     * @return shares The corresponding amount of debt shares minted.
     * @dev Only the Lending Pool (which inherits this contract) can issue debt.
     */
    function _deposit(uint256 assets, address receiver) internal returns (uint256 shares) {
        // No need to check for rounding error, previewDeposit rounds up.
        shares = previewDeposit(assets);

        _mint(receiver, shares);

        realisedDebt += assets;

        emit Deposit(msg.sender, receiver, assets, shares);
    }

    /**
     * @notice Modification of the standard ERC4626 deposit implementation.
     * @dev No public mint allowed.
     */
    function mint(uint256, address) public pure override returns (uint256) {
        revert DebtTokenErrors.FunctionNotImplemented();
    }

    /**
     * @notice Modification of the standard ERC4626 withdraw implementation.
     * @dev No public withdraw allowed.
     */
    function withdraw(uint256, address, address) public pure override returns (uint256) {
        revert DebtTokenErrors.FunctionNotImplemented();
    }

    /**
     * @notice Modification of the standard ERC4626 withdraw implementation.
     * @param assets The amount of assets of the underlying ERC20 token being paid back.
     * @param receiver Will always be the Lending Pool.
     * @param account The Arcadia Account with collateral covering the debt.
     * @return shares The corresponding amount of debt shares redeemed.
     * @dev Only the Lending Pool (which inherits this contract) can issue debt.
     */
    function _withdraw(uint256 assets, address receiver, address account) internal returns (uint256 shares) {
        // Check for rounding error since we round down in previewWithdraw.
        if ((shares = previewWithdraw(assets)) == 0) revert DebtTokenErrors.ZeroShares();

        _burn(account, shares);

        realisedDebt -= assets;

        emit Withdraw(msg.sender, receiver, account, assets, shares);
    }

    /**
     * @notice Modification of the standard ERC4626 redeem implementation.
     * @dev No public redeem allowed.
     */
    function redeem(uint256, address, address) public pure override returns (uint256) {
        revert DebtTokenErrors.FunctionNotImplemented();
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Modification of the standard ERC4626 convertToShares implementation.
     * @dev Since debt is a liability instead of an asset, roundUp and roundDown are inverted compared to the standard implementation.
     */
    function convertToShares(uint256 assets) public view override returns (uint256) {
        // Cache totalSupply.
        uint256 supply = totalSupply;

        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

    /**
     * @notice Modification of the standard ERC4626 convertToShares implementation.
     * @dev Since debt is a liability instead of an asset, roundUp and roundDown are inverted compared to the standard implementation.
     */
    function convertToAssets(uint256 shares) public view override returns (uint256) {
        // Cache totalSupply.
        uint256 supply = totalSupply;

        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    /**
     * @notice Modification of the standard ERC4626 previewMint implementation.
     * @dev Since debt is a liability instead of an asset, roundUp and roundDown are inverted compared to the standard implementation.
     */
    function previewMint(uint256 shares) public view override returns (uint256) {
        // Cache totalSupply.
        uint256 supply = totalSupply;

        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
    }

    /**
     * @notice Modification of the standard ERC4626 previewWithdraw implementation.
     * @dev Since debt is a liability instead of an asset, roundUp and roundDown are inverted compared to the standard implementation.
     */
    function previewWithdraw(uint256 assets) public view override returns (uint256) {
        // Cache totalSupply.
        uint256 supply = totalSupply;

        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    /*//////////////////////////////////////////////////////////////
                            TRANSFER LOGIC
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Modification of the standard ERC4626 transfer implementation.
     * @dev No public transfer allowed.
     */
    function transfer(address, uint256) public pure override returns (bool) {
        revert DebtTokenErrors.FunctionNotImplemented();
    }

    /**
     * @notice Modification of the standard ERC4626 transferFrom implementation.
     * @dev No public transferFrom allowed.
     * @dev The functions approve() and permit() will not revert, but since transferFrom() reverts,
     * it can never be used to transfer tokens.
     */
    function transferFrom(address, address, uint256) public pure override returns (bool) {
        revert DebtTokenErrors.FunctionNotImplemented();
    }
}
合同源代码
文件 6 的 20: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/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

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

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

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

    string public name;

    string public symbol;

    uint8 public immutable decimals;

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

    uint256 public totalSupply;

    mapping(address => uint256) public balanceOf;

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

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

    uint256 internal immutable INITIAL_CHAIN_ID;

    bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;

    mapping(address => uint256) public nonces;

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

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

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

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

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

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

        return true;
    }

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

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

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

        return true;
    }

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

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

        balanceOf[from] -= amount;

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

        emit Transfer(from, to, amount);

        return true;
    }

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

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

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

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

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

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

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

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

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

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

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

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

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

        emit Transfer(from, address(0), amount);
    }
}
合同源代码
文件 7 的 20:ERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

import {ERC20} from "../tokens/ERC20.sol";
import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol";

/// @notice Minimal ERC4626 tokenized Vault implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)
abstract contract ERC4626 is ERC20 {
    using SafeTransferLib for ERC20;
    using FixedPointMathLib for uint256;

    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed caller,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /*//////////////////////////////////////////////////////////////
                               IMMUTABLES
    //////////////////////////////////////////////////////////////*/

    ERC20 public immutable asset;

    constructor(
        ERC20 _asset,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol, _asset.decimals()) {
        asset = _asset;
    }

    /*//////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    //////////////////////////////////////////////////////////////*/

    function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) {
        // Check for rounding error since we round down in previewDeposit.
        require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES");

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) {
        assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.

        // Need to transfer before minting or ERC777s could reenter.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        _mint(receiver, shares);

        emit Deposit(msg.sender, receiver, assets, shares);

        afterDeposit(assets, shares);
    }

    function withdraw(
        uint256 assets,
        address receiver,
        address owner
    ) public virtual returns (uint256 shares) {
        shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.

        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    function redeem(
        uint256 shares,
        address receiver,
        address owner
    ) public virtual returns (uint256 assets) {
        if (msg.sender != owner) {
            uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.

            if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
        }

        // Check for rounding error since we round down in previewRedeem.
        require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS");

        beforeWithdraw(assets, shares);

        _burn(owner, shares);

        emit Withdraw(msg.sender, receiver, owner, assets, shares);

        asset.safeTransfer(receiver, assets);
    }

    /*//////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    //////////////////////////////////////////////////////////////*/

    function totalAssets() public view virtual returns (uint256);

    function convertToShares(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets());
    }

    function convertToAssets(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply);
    }

    function previewDeposit(uint256 assets) public view virtual returns (uint256) {
        return convertToShares(assets);
    }

    function previewMint(uint256 shares) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply);
    }

    function previewWithdraw(uint256 assets) public view virtual returns (uint256) {
        uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.

        return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets());
    }

    function previewRedeem(uint256 shares) public view virtual returns (uint256) {
        return convertToAssets(shares);
    }

    /*//////////////////////////////////////////////////////////////
                     DEPOSIT/WITHDRAWAL LIMIT LOGIC
    //////////////////////////////////////////////////////////////*/

    function maxDeposit(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxMint(address) public view virtual returns (uint256) {
        return type(uint256).max;
    }

    function maxWithdraw(address owner) public view virtual returns (uint256) {
        return convertToAssets(balanceOf[owner]);
    }

    function maxRedeem(address owner) public view virtual returns (uint256) {
        return balanceOf[owner];
    }

    /*//////////////////////////////////////////////////////////////
                          INTERNAL HOOKS LOGIC
    //////////////////////////////////////////////////////////////*/

    function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {}

    function afterDeposit(uint256 assets, uint256 shares) internal virtual {}
}
合同源代码
文件 8 的 20:Errors.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity 0.8.22;

library LiquidatorErrors {
    // Thrown when the liquidateAccount function is called on an Account that is already in an auction.
    error AuctionOngoing();
    // Thrown when cutOffTime is above the maximum value.
    error CutOffTooHigh();
    // Thrown when cutOffTime is below the minimum value.
    error CutOffTooLow();
    // Thrown if the auction was not successfully ended.
    error EndAuctionFailed();
    // Thrown when halfLifeTime is above the maximum value.
    error HalfLifeTimeTooHigh();
    // Thrown when halfLifeTime is below the minimum value.
    error HalfLifeTimeTooLow();
    // Thrown when the auction has not yet expired.
    error InvalidBid();
    // Thrown when account specified is not an Arcadia Account.
    error IsNotAnAccount();
    // Thrown when the start price multiplier is above the maximum value.
    error MultiplierTooHigh();
    // Thrown when the start price multiplier is below the minimum value.
    error MultiplierTooLow();
    // Thrown when an Account is not for sale.
    error NotForSale();
    // Thrown when not authorized.
    error NotAuthorized();
    // Thrown when the sequencer uptime oracle is not reverting.
    error OracleNotReverting();
    // Thrown when the sequencer uptime oracle is reverting.
    error OracleReverting();
    // Thrown when the sequencer is down.
    error SequencerDown();
}

library DebtTokenErrors {
    // Thrown when function called has not be implemented.
    error FunctionNotImplemented();
    // Thrown when amount of asset would represent zero shares.
    error ZeroShares();
}

library LendingPoolErrors {
    // Thrown when amount available to withdraw of an asset is less than amount requested to withdraw.
    error AmountExceedsBalance();
    // Thrown when an auction is in process.
    error AuctionOngoing();
    // Thrown when an Account would become unhealthy OR the creditor of the Account is not the specific lending pool OR the Account version would not be valid.
    error InvalidVersion();
    // Thrown when account specified is not an Arcadia Account.
    error IsNotAnAccount();
    // Thrown when an account has zero debt.
    error IsNotAnAccountWithDebt();
    // Thrown when liquidation weights are above maximum value.
    error LiquidationWeightsTooHigh();
    // Thrown when a specific tranche does not exist.
    error NonExistingTranche();
    // Thrown when address has an open position
    error OpenPositionNonZero();
    // Thrown when the tranche of the lending pool already exists.
    error TrancheAlreadyExists();
    // Thrown when caller is not authorized.
    error Unauthorized();
    // Thrown when asset amount in input is zero.
    error ZeroAmount();
}

library TrancheErrors {
    // Thrown when a tranche is locked.
    error Locked();
    // Thrown when amount of shares would represent zero assets.
    error ZeroAssets();
    // Thrown when an auction is in process.
    error AuctionOngoing();
    // Thrown when caller is not valid.
    error Unauthorized();
    // Thrown when amount of asset would represent zero shares.
    error ZeroShares();
}
合同源代码
文件 9 的 20:FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)
library FixedPointMathLib {
    /*//////////////////////////////////////////////////////////////
                    SIMPLIFIED FIXED POINT OPERATIONS
    //////////////////////////////////////////////////////////////*/

    uint256 internal constant MAX_UINT256 = 2 ** 256 - 1;

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

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

    function mulDivDown(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) }

            // Divide x * y by the denominator.
            z := div(mul(x, y), denominator)
        }
    }

    function mulDivUp(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) { revert(0, 0) }

            // If x * y modulo the denominator is strictly greater than 0,
            // 1 is added to round up the division of x * y by the denominator.
            z := add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/
}
合同源代码
文件 10 的 20:IAccount.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity 0.8.22;

import { AssetValueAndRiskFactors } from "../../lib/accounts-v2/src/libraries/AssetValuationLib.sol";

interface IAccount {
    /**
     * @notice Returns the address of the owner of the Account.
     */
    function owner() external view returns (address);

    /**
     * @notice Calculates the total collateral value (MTM discounted with a haircut) of the Account.
     * @return collateralValue The collateral value, returned in the decimal precision of the Numeraire.
     */
    function getCollateralValue() external view returns (uint256);

    /**
     * @notice Returns the used margin of the Account.
     * @return usedMargin The total amount of Margin that is currently in use to back liabilities.
     */
    function getUsedMargin() external view returns (uint256);

    /**
     * @notice Checks if the Account is still healthy for an updated open position.
     * @param openPosition The new open position.
     * @return accountVersion The current Account version.
     */
    function increaseOpenPosition(uint256 openPosition) external returns (uint256);

    /**
     * @notice Executes a flash action initiated by the Creditor.
     * @param actionTarget The contract address of the flashAction.
     * @param actionData A bytes object containing three structs and two bytes objects.
     * The first struct contains the info about the assets to withdraw from this Account to the actionTarget.
     * The second struct contains the info about the owner's assets that need to be transferred from the owner to the actionTarget.
     * The third struct contains the permit for the Permit2 transfer.
     * The first bytes object contains the signature for the Permit2 transfer.
     * The second bytes object contains the encoded input for the actionTarget.
     * @return accountVersion The current Account version.
     */
    function flashActionByCreditor(bytes calldata callbackData, address actionTarget, bytes calldata actionData)
        external
        returns (uint256);

    /**
     * @notice Checks if an Account is liquidatable and continues the liquidation flow.
     * @param initiator The address of the liquidation initiator.
     * @return assetAddresses Array of the contract addresses of the assets in Account.
     * @return assetIds Array of the IDs of the assets in Account.
     * @return assetAmounts Array with the amounts of the assets in Account.
     * @return creditor_ The contract address of the Creditor.
     * @return minimumMargin_ The minimum margin.
     * @return openPosition The open position (liabilities) issued against the Account.
     * @return assetAndRiskValues Array of asset values and corresponding collateral and liquidation factors.
     */
    function startLiquidation(address initiator)
        external
        returns (
            address[] memory,
            uint256[] memory,
            uint256[] memory,
            address,
            uint96,
            uint256,
            AssetValueAndRiskFactors[] memory
        );

    /**
     * @notice Transfers the asset bought by a bidder during a liquidation event.
     * @param assetAddresses Array of the contract addresses of the assets.
     * @param assetIds Array of the IDs of the assets.
     * @param assetAmounts Array with the amounts of the assets.
     * @param bidder The address of the bidder.
     * @return assetAmounts_ Array with the actual transferred amounts of assets.
     */
    function auctionBid(
        address[] memory assetAddresses,
        uint256[] memory assetIds,
        uint256[] memory assetAmounts,
        address bidder
    ) external returns (uint256[] memory);

    /**
     * @notice Transfers all assets of the Account in case the auction did not end successful (= Bought In).
     * @param to The recipient's address to receive the assets, set by the Creditor.
     */
    function auctionBoughtIn(address to) external;

    /**
     * @notice Sets the "inAuction" flag to false when an auction ends.
     */
    function endAuction() external;
}
合同源代码
文件 11 的 20:ICreditor.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity 0.8.22;

/**
 * @title Creditor implementation.
 * @author Pragma Labs
 * @notice This contract contains the minimum functionality a Creditor, interacting with Arcadia Accounts, needs to implement.
 * @dev For the implementation of Arcadia Accounts, see: https://github.com/arcadia-finance/accounts-v2.
 */
interface ICreditor {
    /**
     * @notice Checks if Account fulfills all requirements and returns Creditor parameters.
     * @param accountVersion The version of the Arcadia Account.
     * @return success Bool indicating if all requirements are met.
     * @return numeraire The Numeraire of the Creditor.
     * @return liquidator The liquidator of the Creditor.
     * @return minimumMargin The minimum amount of collateral that must be held in the Account before a position can be opened,
     * denominated in the numeraire.
     */
    function openMarginAccount(uint256 accountVersion) external returns (bool, address, address, uint256);

    /**
     * @notice Checks if Account can be closed.
     * @param account The Account address.
     */
    function closeMarginAccount(address account) external;

    /**
     * @notice Returns the open position of the Account.
     * @param account The Account address.
     * @return openPosition The open position of the Account.
     */
    function getOpenPosition(address account) external view returns (uint256);

    /**
     * @notice Returns the Risk Manager of the creditor.
     * @return riskManager The Risk Manager of the creditor.
     */
    function riskManager() external view returns (address riskManager);

    /**
     * @notice Callback of the Account during a flashAction.
     * @param callbackData The data for the actions that have to be executed by the Creditor during a flashAction.
     */
    function flashActionCallback(bytes calldata callbackData) external;

    /**
     * @notice Starts the liquidation of an account and returns the open position of the Account.
     * @param initiator The address of the liquidation initiator.
     * @param minimumMargin The minimum margin of the Account.
     * @return openPosition the open position of the Account.
     */
    function startLiquidation(address initiator, uint256 minimumMargin) external returns (uint256);
}
合同源代码
文件 12 的 20:IFactory.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity 0.8.22;

interface IFactory {
    /**
     * @notice View function returning if an address is a Account.
     * @param account The address to be checked.
     * @return bool Whether the address is a Account or not.
     */
    function isAccount(address account) external view returns (bool);

    /**
     * @notice Returns the owner of a Account.
     * @param account The Account address.
     * @return owner The Account owner.
     */
    function ownerOfAccount(address account) external view returns (address);

    /**
     * @notice Function used to transfer a Account between users.
     * @param from The sender.
     * @param to The target.
     * @param account The address of the Account that is transferred.
     */
    function safeTransferFrom(address from, address to, address account) external;
}
合同源代码
文件 13 的 20:ILendingPool.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity 0.8.22;

interface ILendingPool {
    /**
     * @notice Returns the total redeemable amount of liquidity in the underlying asset.
     * @return totalLiquidity The total redeemable amount of liquidity in the underlying asset.
     */
    function totalLiquidity() external view returns (uint256);

    /**
     * @notice Deposit assets in the Lending Pool.
     * @param assets The amount of assets of the underlying ERC-20 token being deposited.
     * @param from The address of the Liquidity Provider who deposits the underlying ERC-20 token via a Tranche.
     */
    function depositInLendingPool(uint256 assets, address from) external;

    /**
     * @notice Withdraw assets from the Lending Pool.
     * @param assets The amount of assets of the underlying ERC-20 tokens being withdrawn.
     * @param receiver The address of the receiver of the underlying ERC-20 tokens.
     */
    function withdrawFromLendingPool(uint256 assets, address receiver) external;

    /**
     * @notice Returns the redeemable amount of liquidity in the underlying asset of an address.
     * @param owner The address of the liquidity provider.
     * @return assets The redeemable amount of liquidity in the underlying asset.
     */
    function liquidityOf(address owner) external view returns (uint256);

    /**
     * @notice liquidityOf, but syncs the unrealised interest first.
     * @param owner The address of the liquidity provider.
     * @return assets The redeemable amount of liquidity in the underlying asset.
     */
    function liquidityOfAndSync(address owner) external returns (uint256);

    /**
     * @notice Repays debt via an auction.
     * @param startDebt The amount of debt of the Account the moment the liquidation was initiated.
     * @param minimumMargin The minimum margin of the Account.
     * @param minimumMargin The minimum margin of the Account.
     * @param amount The amount of debt repaid by a bidder during the auction.
     * @param account The contract address of the Arcadia Account backing the loan.
     * @param bidder The address of the bidder.
     * @return earlyTerminate Bool indicating of the full amount of debt was repaid.
     */
    function auctionRepay(uint256 startDebt, uint256 minimumMargin, uint256 amount, address account, address bidder)
        external
        returns (bool);

    /**
     * @notice Settles the liquidation process for a specific Account.
     * @param account The address of the Account undergoing liquidation settlement.
     * @param startDebt The initial debt amount of the liquidated Account.
     * @param minimumMargin The minimum margin of the Account.
     * @param terminator The address of the liquidation terminator.
     */
    function settleLiquidationHappyFlow(address account, uint256 startDebt, uint256 minimumMargin, address terminator)
        external;

    /**
     * @notice Settles the liquidation process for a specific Account.
     * @param account The address of the Account undergoing liquidation settlement.
     * @param startDebt The initial debt amount of the liquidated Account.
     * @param minimumMargin The minimum margin of the Account.
     * @param terminator The address of the liquidation terminator.
     */
    function settleLiquidationUnhappyFlow(address account, uint256 startDebt, uint256 minimumMargin, address terminator)
        external;
}
合同源代码
文件 14 的 20:ITranche.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: MIT
 */
pragma solidity 0.8.22;

interface ITranche {
    /**
     * @notice Locks the tranche in case all liquidity of the tranche is written of due to bad debt.
     */
    function lock() external;

    /**
     * @notice Locks the tranche while an auction is in progress.
     * @param auctionInProgress Flag indicating if there are auctions in progress.
     */
    function setAuctionInProgress(bool auctionInProgress) external;
}
合同源代码
文件 15 的 20:LendingPool.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity 0.8.22;

import { Creditor } from "../lib/accounts-v2/src/abstracts/Creditor.sol";
import { DebtToken, ERC20, ERC4626 } from "./DebtToken.sol";
import { FixedPointMathLib } from "../lib/solmate/src/utils/FixedPointMathLib.sol";
import { IAccount } from "./interfaces/IAccount.sol";
import { IFactory } from "./interfaces/IFactory.sol";
import { ILendingPool } from "./interfaces/ILendingPool.sol";
import { ITranche } from "./interfaces/ITranche.sol";
import { LendingPoolErrors } from "./libraries/Errors.sol";
import { LendingPoolGuardian } from "./guardians/LendingPoolGuardian.sol";
import { LogExpMath } from "./libraries/LogExpMath.sol";
import { SafeCastLib } from "../lib/solmate/src/utils/SafeCastLib.sol";
import { SafeTransferLib } from "../lib/solmate/src/utils/SafeTransferLib.sol";

/**
 * @title Arcadia LendingPool.
 * @author Pragma Labs
 * @notice The Lending pool is responsible for the:
 * - Accounting of the liabilities of borrowers via the debtTokens (ERC4626).
 * - Accounting of the liquidity of the Liquidity Providers, via one or more Tranche(s) (ERC4626).
 * - Management of issuing and repaying debt.
 * - Management of interest payments.
 * - Settlement of liquidations and default events.
 */
contract LendingPool is LendingPoolGuardian, Creditor, DebtToken, ILendingPool {
    using FixedPointMathLib for uint256;
    using SafeTransferLib for ERC20;

    /* //////////////////////////////////////////////////////////////
                                CONSTANTS
    ////////////////////////////////////////////////////////////// */

    // Seconds per year, leap years ignored.
    uint256 internal constant YEARLY_SECONDS = 31_536_000;
    // Contract address of the Arcadia Account Factory.
    address internal immutable ACCOUNT_FACTORY;
    // Contract address of the Liquidator contract.
    address internal immutable LIQUIDATOR;
    // The unit for fixed point numbers with 4 decimals precision.
    uint256 internal constant ONE_4 = 10_000;
    // Maximum total liquidation penalty, 4 decimal precision.
    uint256 internal constant MAX_TOTAL_PENALTY = 1100;

    /* //////////////////////////////////////////////////////////////
                                STORAGE
    ////////////////////////////////////////////////////////////// */

    // The current interest rate, 18 decimals precision.
    uint80 public interestRate;
    // The interest rate when utilisation is 0.
    // 18 decimals precision.
    uint72 internal baseRatePerYear;
    // The slope of the first curve, defined as the delta in interest rate for a delta in utilisation of 100%.
    // 18 decimals precision.
    uint72 internal lowSlopePerYear;
    // The slope of the second curve, defined as the delta in interest rate for a delta in utilisation of 100%.
    // 18 decimals precision.
    uint72 internal highSlopePerYear;
    // The optimal capital utilisation, where we go from the first curve to the steeper second curve.
    // 4 decimal precision.
    uint16 internal utilisationThreshold;
    // Last timestamp that interests were realized.
    uint32 internal lastSyncedTimestamp;
    // Fee issued upon taking debt, 4 decimals precision (10 equals 0.001 or 0.1%), capped at 255 (2.55%).
    uint8 public originationFee;
    // Sum of all the interest weights of the tranches + treasury.
    uint24 internal totalInterestWeight;
    // Fraction (interestWeightTreasury / totalInterestWeight) of the interest fees that go to the treasury.
    uint16 internal interestWeightTreasury;
    // Fraction (liquidationWeightTreasury / totalLiquidationWeight) of the liquidation fees that goes to the treasury.
    uint16 internal liquidationWeightTreasury;
    // Fraction (liquidationWeightTranche / totalLiquidationWeight) of the liquidation fees that goes to the most Junior Tranche.
    uint16 internal liquidationWeightTranche;

    // Total amount of `underlying asset` that is claimable by the LPs. Does not take into account pending interests.
    uint128 internal totalRealisedLiquidity;
    // The minimum amount of collateral that must be held in an Account before a position can be opened.
    uint96 internal minimumMargin;

    // Address of the protocol treasury.
    address internal treasury;
    // Number of auctions that are currently in progress.
    uint16 internal auctionsInProgress;
    // Maximum amount of `underlying asset` that is paid as fee to the initiator/terminator of a liquidation.
    uint80 internal maxReward;
    // Minimum initiation and termination reward, relative to the minimumMargin, 4 decimal precision.
    uint16 internal minRewardWeight;
    // Fee paid to the Liquidation Initiator.
    // Defined as a fraction of the openDebt with 4 decimals precision.
    // Absolute fee can be further capped to a max amount by the creditor.
    uint16 internal initiationWeight;
    // Penalty the Account owner has to pay to the Creditor on top of the open Debt for being liquidated.
    // Defined as a fraction of the openDebt with 4 decimals precision.
    uint16 internal penaltyWeight;
    // Fee paid to the address that is ending an auction.
    // Defined as a fraction of the openDebt with 4 decimals precision.
    uint16 internal terminationWeight;

    // Array of the interest weights of each Tranche.
    // Fraction (interestWeightTranches[i] / totalInterestWeight) of the interest fees that go to Tranche i.
    uint16[] internal interestWeightTranches;
    // Array of the contract addresses of the Tranches.
    address[] internal tranches;

    // Map tranche => status.
    mapping(address => bool) internal isTranche;
    // Map tranche => interestWeight.
    // Fraction (interestWeightTranches[i] / totalInterestWeight) of the interest fees that go to Tranche i.
    mapping(address => uint256) internal interestWeight;
    // Map tranche => realisedLiquidity.
    // Amount of `underlying asset` that is claimable by the liquidity providers.
    // Does not take into account pending interests.
    mapping(address => uint256) internal realisedLiquidityOf;
    // Map Account => owner => beneficiary => amount.
    // Stores the credit allowances for a beneficiary per Account and per Owner.
    mapping(address => mapping(address => mapping(address => uint256))) public creditAllowance;

    /* //////////////////////////////////////////////////////////////
                                EVENTS
    ////////////////////////////////////////////////////////////// */

    event AuctionStarted(address indexed account, address indexed creditor, uint128 openDebt);
    event AuctionFinished(
        address indexed account,
        address indexed creditor,
        uint256 startDebt,
        uint256 initiationReward,
        uint256 terminationReward,
        uint256 penalty,
        uint256 badDebt,
        uint256 surplus
    );
    event Borrow(
        address indexed account, address indexed by, address to, uint256 amount, uint256 fee, bytes3 indexed referrer
    );
    event CreditApproval(address indexed account, address indexed owner, address indexed beneficiary, uint256 amount);
    event InterestSynced(uint256 interest);
    event InterestWeightTrancheUpdated(address indexed tranche, uint8 indexed trancheIndex, uint16 interestWeight);
    event LiquidationWeightTrancheUpdated(uint16 liquidationWeight);
    event PoolStateUpdated(uint256 totalDebt, uint256 totalLiquidity, uint80 interestRate);
    event Repay(address indexed account, address indexed from, uint256 amount);
    event TranchePopped(address tranche);
    event TreasuryWeightsUpdated(uint16 interestWeight, uint16 liquidationWeight);

    /* //////////////////////////////////////////////////////////////
                                MODIFIERS
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Checks if caller is the Liquidator.
     */
    modifier onlyLiquidator() {
        if (LIQUIDATOR != msg.sender) revert LendingPoolErrors.Unauthorized();
        _;
    }

    /**
     * @notice Checks if caller is a Tranche.
     */
    modifier onlyTranche() {
        if (!isTranche[msg.sender]) revert LendingPoolErrors.Unauthorized();
        _;
    }

    /**
     * @notice Syncs interest to LPs and treasury and updates the interest rate.
     */
    modifier processInterests() {
        _syncInterests();
        _;
        // _updateInterestRate() modifies the state (effect), but can safely be called after interactions.
        // Cannot be exploited by re-entrancy attack.
        _updateInterestRate(realisedDebt, totalRealisedLiquidity);
    }

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

    /**
     * @notice The constructor for a lending pool.
     * @param riskManager_ The address of the new Risk Manager.
     * @param asset_ The underlying ERC20 token of the Lending Pool.
     * @param treasury_ The address of the protocol treasury.
     * @param accountFactory The contract address of the Arcadia Account Factory.
     * @param liquidator The contract address of the Liquidator.
     * @dev The name and symbol of the DebtToken are automatically generated, based on the name and symbol of the underlying token.
     */
    constructor(address riskManager_, ERC20 asset_, address treasury_, address accountFactory, address liquidator)
        LendingPoolGuardian()
        Creditor(riskManager_)
        DebtToken(asset_)
    {
        treasury = treasury_;
        ACCOUNT_FACTORY = accountFactory;
        LIQUIDATOR = liquidator;
    }

    /* //////////////////////////////////////////////////////////////
                            TRANCHES LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Adds a tranche to the Lending Pool.
     * @param tranche The address of the Tranche.
     * @param interestWeight_ The interest weight of the specific Tranche.
     * @dev The order of the tranches is important, the most senior tranche is added first at index 0, the most junior at the last index.
     * @dev Each Tranche is an ERC4626 contract.
     * @dev The interest weight of each Tranche determines the relative share of the yield (interest payments) that goes to its Liquidity providers.
     */
    function addTranche(address tranche, uint16 interestWeight_) external onlyOwner processInterests {
        if (auctionsInProgress > 0) revert LendingPoolErrors.AuctionOngoing();
        if (isTranche[tranche]) revert LendingPoolErrors.TrancheAlreadyExists();

        totalInterestWeight += interestWeight_;
        interestWeightTranches.push(interestWeight_);
        interestWeight[tranche] = interestWeight_;

        uint8 trancheIndex = uint8(tranches.length);
        tranches.push(tranche);
        isTranche[tranche] = true;

        emit InterestWeightTrancheUpdated(tranche, trancheIndex, interestWeight_);
    }

    /**
     * @notice Changes the interest weight of a specific Tranche.
     * @param index The index of the Tranche for which a new interest weight is being set.
     * @param interestWeight_ The new interest weight of the Tranche at the index.
     * @dev The interest weight of each Tranche determines the relative share of yield (interest payments) that goes to its Liquidity providers.
     */
    function setInterestWeightTranche(uint256 index, uint16 interestWeight_) external onlyOwner processInterests {
        if (index >= tranches.length) revert LendingPoolErrors.NonExistingTranche();
        totalInterestWeight = totalInterestWeight - interestWeightTranches[index] + interestWeight_;
        interestWeightTranches[index] = interestWeight_;
        address tranche = tranches[index];
        interestWeight[tranche] = interestWeight_;

        emit InterestWeightTrancheUpdated(tranche, uint8(index), interestWeight_);
    }

    /**
     * @notice Changes the liquidation weight of the most Junior Tranche.
     * @param liquidationWeight The new liquidation weight of the Tranche at the highest index.
     * @dev The liquidation weight determines the relative share of liquidation fees that goes to the most Junior Tranche.
     */
    function setLiquidationWeightTranche(uint16 liquidationWeight) external onlyOwner {
        emit LiquidationWeightTrancheUpdated(liquidationWeightTranche = liquidationWeight);
    }

    /**
     * @notice Removes the Tranche at the last index (most junior).
     * @param index The index of the last Tranche.
     * @param tranche The address of the last Tranche.
     * @dev This function can only be called by the function _processDefault(uint256 assets),
     * when there is a default as big as (or bigger than) the complete amount of liquidity of the most junior Tranche.
     * @dev Passing the input parameters to the function saves gas compared to reading the address and index of the last Tranche from storage.
     * No need to check if index and Tranche are indeed of the last tranche since function is only called by _processDefault.
     */
    function _popTranche(uint256 index, address tranche) internal {
        unchecked {
            totalInterestWeight -= interestWeightTranches[index];
        }
        isTranche[tranche] = false;
        interestWeightTranches.pop();
        tranches.pop();
        interestWeight[tranche] = 0;

        emit TranchePopped(tranche);
    }

    /* ///////////////////////////////////////////////////////////////
                    TREASURY FEE CONFIGURATION
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Changes the interest and liquidation weight of the Treasury.
     * @param interestWeight_ The new interestWeight of the treasury.
     * @param liquidationWeight The new liquidationWeight of the treasury.
     * @dev The interestWeight determines the relative share of the yield (interest payments) that goes to the protocol treasury.
     * @dev Setting interestWeightTreasury to a very high value will cause the treasury to collect all interest fees from that moment on.
     * Although this will affect the future profits of liquidity providers, no funds nor realized interest are at risk for LPs.
     */
    function setTreasuryWeights(uint16 interestWeight_, uint16 liquidationWeight) external onlyOwner processInterests {
        totalInterestWeight = totalInterestWeight - interestWeightTreasury + interestWeight_;
        interestWeight[treasury] = interestWeight_;

        emit TreasuryWeightsUpdated(
            interestWeightTreasury = interestWeight_, liquidationWeightTreasury = liquidationWeight
        );
    }

    /**
     * @notice Sets new treasury address.
     * @param treasury_ The new address of the treasury.
     */
    function setTreasury(address treasury_) external onlyOwner {
        treasury = treasury_;
    }

    /**
     * @notice Sets the new origination fee.
     * @param originationFee_ The new origination fee.
     * @dev originationFee is limited by being a uint8 -> max value is 2.55%
     * 4 decimal precision (10 = 0.1%).
     */
    function setOriginationFee(uint8 originationFee_) external onlyOwner {
        originationFee = originationFee_;
    }

    /* //////////////////////////////////////////////////////////////
                        DEPOSIT/WITHDRAWAL LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Deposit assets in the Lending Pool.
     * @param assets The amount of assets of the underlying ERC20 tokens being deposited.
     * @param from The address of the Liquidity Provider who deposits the underlying ERC20 token via a Tranche.
     * @dev This function can only be called by Tranches.
     */
    function depositInLendingPool(uint256 assets, address from)
        external
        whenDepositNotPaused
        onlyTranche
        processInterests
    {
        // Need to transfer before minting or ERC777s could reenter.
        // Address(this) is trusted -> no risk on re-entrancy attack after transfer.
        asset.safeTransferFrom(from, address(this), assets);

        unchecked {
            realisedLiquidityOf[msg.sender] += assets;
            totalRealisedLiquidity = SafeCastLib.safeCastTo128(assets + totalRealisedLiquidity);
        }
    }

    /**
     * @notice Donate assets to the Lending Pool.
     * @param trancheIndex The index of the tranche to donate to.
     * @param assets The amount of assets of the underlying ERC20 tokens being deposited.
     * @dev Can be used by anyone to donate assets to the Lending Pool.
     * It is supposed to serve as a way to compensate the jrTranche after an
     * auction didn't get sold and was manually liquidated after cutoffTime.
     * @dev Inflation attacks by the first depositor in the Tranches have to be prevented with virtual assets/shares.
     */
    function donateToTranche(uint256 trancheIndex, uint256 assets) external whenDepositNotPaused processInterests {
        if (assets == 0) revert LendingPoolErrors.ZeroAmount();

        address tranche = tranches[trancheIndex];

        // Need to transfer before donating or ERC777s could reenter.
        // Address(this) is trusted -> no risk on re-entrancy attack after transfer.
        asset.safeTransferFrom(msg.sender, address(this), assets);

        unchecked {
            realisedLiquidityOf[tranche] += assets; //[̲̅$̲̅(̲̅ ͡° ͜ʖ ͡°̲̅)̲̅$̲̅]
            totalRealisedLiquidity = SafeCastLib.safeCastTo128(assets + totalRealisedLiquidity);
        }
    }

    /**
     * @notice Withdraw assets from the Lending Pool.
     * @param assets The amount of assets of the underlying ERC20 tokens being withdrawn.
     * @param receiver The address of the receiver of the underlying ERC20 tokens.
     * @dev This function can be called by anyone with an open balance (realisedLiquidityOf[address] bigger than 0),
     * which can be both Tranches as other address (treasury, Liquidation Initiators, Liquidated Account Owner...).
     */
    function withdrawFromLendingPool(uint256 assets, address receiver)
        external
        whenWithdrawNotPaused
        processInterests
    {
        if (realisedLiquidityOf[msg.sender] < assets) revert LendingPoolErrors.AmountExceedsBalance();

        unchecked {
            realisedLiquidityOf[msg.sender] -= assets;
            totalRealisedLiquidity = SafeCastLib.safeCastTo128(totalRealisedLiquidity - assets);
        }

        asset.safeTransfer(receiver, assets);
    }

    /* //////////////////////////////////////////////////////////////
                            LENDING LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Approve a beneficiary to take out debt against an Arcadia Account.
     * @param beneficiary The address of the beneficiary who can take out debt backed by an Arcadia Account.
     * @param amount The amount of underlying ERC20 tokens to be lent out.
     * @param account The address of the Arcadia Account backing the debt.
     */
    function approveBeneficiary(address beneficiary, uint256 amount, address account) external {
        // If Account is not an actual address of an Arcadia Account, ownerOfAccount(address) will return the zero address.
        if (IFactory(ACCOUNT_FACTORY).ownerOfAccount(account) != msg.sender) revert LendingPoolErrors.Unauthorized();

        creditAllowance[account][msg.sender][beneficiary] = amount;

        emit CreditApproval(account, msg.sender, beneficiary, amount);
    }

    /**
     * @notice Takes out debt backed by collateral in an Arcadia Account.
     * @param amount The amount of underlying ERC20 tokens to be lent out.
     * @param account The address of the Arcadia Account backing the debt.
     * @param to The address who receives the lent out underlying tokens.
     * @param referrer A unique identifier of the referrer, who will receive part of the fees generated by this transaction.
     * @dev The sender might be different than the owner if they have the proper allowances.
     */
    function borrow(uint256 amount, address account, address to, bytes3 referrer)
        external
        whenBorrowNotPaused
        processInterests
    {
        if (amount == 0) revert LendingPoolErrors.ZeroAmount();

        // If Account is not an actual address of an Account, ownerOfAccount(address) will return the zero address.
        address accountOwner = IFactory(ACCOUNT_FACTORY).ownerOfAccount(account);
        if (accountOwner == address(0)) revert LendingPoolErrors.IsNotAnAccount();

        uint256 amountWithFee = amount + amount.mulDivUp(originationFee, ONE_4);

        // Check allowances to take debt.
        if (accountOwner != msg.sender) {
            uint256 allowed = creditAllowance[account][accountOwner][msg.sender];
            if (allowed != type(uint256).max) {
                creditAllowance[account][accountOwner][msg.sender] = allowed - amountWithFee;
            }
        }

        // Mint debt tokens to the Account.
        _deposit(amountWithFee, account);

        // Add origination fee to the treasury.
        unchecked {
            if (amountWithFee - amount > 0) {
                totalRealisedLiquidity = SafeCastLib.safeCastTo128(amountWithFee + totalRealisedLiquidity - amount);
                realisedLiquidityOf[treasury] += amountWithFee - amount;
            }
        }

        // UpdateOpenPosition checks that the Account indeed has opened a margin account for this Lending Pool and
        // checks that it is still healthy after the debt is increased with amountWithFee.
        // Reverts in Account if one of the checks fails.
        uint256 accountVersion = IAccount(account).increaseOpenPosition(maxWithdraw(account));
        if (!isValidVersion[accountVersion]) revert LendingPoolErrors.InvalidVersion();

        // Transfer fails if there is insufficient liquidity in the pool.
        asset.safeTransfer(to, amount);

        emit Borrow(account, msg.sender, to, amount, amountWithFee - amount, referrer);
    }

    /**
     * @notice Repays debt.
     * @param amount The amount of underlying ERC20 tokens to be repaid.
     * @param account The contract address of the Arcadia Account backing the debt.
     * @dev if Account is not an actual address of an Arcadia Account, maxWithdraw(account) will always return 0.
     * Function will not revert, but amount is always 0.
     * @dev Anyone (EOAs and contracts) can repay debt in the name of an Account.
     */
    function repay(uint256 amount, address account) external whenRepayNotPaused processInterests {
        uint256 accountDebt = maxWithdraw(account);
        amount = accountDebt > amount ? amount : accountDebt;

        // Need to transfer before burning debt or ERC777s could reenter.
        // Address(this) is trusted -> no risk on re-entrancy attack after transfer.
        asset.safeTransferFrom(msg.sender, address(this), amount);

        _withdraw(amount, address(this), account);

        emit Repay(account, msg.sender, amount);
    }

    /**
     * @notice Repays debt via an auction.
     * @param startDebt The amount of debt of the Account the moment the liquidation was initiated.
     * @param minimumMargin_ The minimum margin of the Account.
     * @param amount The amount repaid by a bidder during the auction.
     * @param account The contract address of the Arcadia Account backing the debt.
     * @param bidder The address of the bidder.
     * @return earlyTerminate Bool indicating whether the full amount of debt was repaid.
     * @dev This function allows a liquidator to repay a specified amount of debt for a user.
     */
    function auctionRepay(uint256 startDebt, uint256 minimumMargin_, uint256 amount, address account, address bidder)
        external
        whenLiquidationNotPaused
        onlyLiquidator
        processInterests
        returns (bool earlyTerminate)
    {
        // Need to transfer before burning debt or ERC777s could reenter.
        // Address(this) is trusted -> no risk on re-entrancy attack after transfer.
        asset.safeTransferFrom(bidder, address(this), amount);

        uint256 accountDebt = maxWithdraw(account);
        if (accountDebt == 0) revert LendingPoolErrors.IsNotAnAccountWithDebt();
        if (accountDebt <= amount) {
            // The amount recovered by selling assets during the auction is bigger than the total debt of the Account.
            // -> Terminate the auction and make the surplus available to the Account-Owner.
            earlyTerminate = true;
            unchecked {
                _settleLiquidationHappyFlow(account, startDebt, minimumMargin_, bidder, (amount - accountDebt));
            }
            amount = accountDebt;
        }

        _withdraw(amount, address(this), account);

        emit Repay(account, bidder, amount);
    }

    /* //////////////////////////////////////////////////////////////
                        LEVERAGED ACTIONS LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Execute and interact with external logic on leverage.
     * @param amountBorrowed The amount of underlying ERC20 tokens to be lent out.
     * @param account The address of the Arcadia Account backing the debt.
     * @param actionTarget The address of the Action Target to call.
     * @param actionData A bytes object containing three actionAssetData structs, an address array and a bytes array.
     * @param referrer A unique identifier of the referrer, who will receive part of the fees generated by this transaction.
     * @dev The sender might be different than the owner if they have the proper allowances.
     * @dev accountManagementAction() works similar to flash loans, this function optimistically calls external logic and checks for the Account state at the very end.
     */
    function flashAction(
        uint256 amountBorrowed,
        address account,
        address actionTarget,
        bytes calldata actionData,
        bytes3 referrer
    ) external whenBorrowNotPaused processInterests {
        // If Account is not an actual address of a Account, ownerOfAccount(address) will return the zero address.
        address accountOwner = IFactory(ACCOUNT_FACTORY).ownerOfAccount(account);
        if (accountOwner == address(0)) revert LendingPoolErrors.IsNotAnAccount();

        // Check allowances to take debt.
        if (accountOwner != msg.sender) {
            // Calling flashActionByCreditor() gives the sender full control over all assets in the Account,
            // Only Beneficiaries with maximum allowance can call the flashAction function.
            if (creditAllowance[account][accountOwner][msg.sender] != type(uint256).max) {
                revert LendingPoolErrors.Unauthorized();
            }
        }

        // Prepare callback of the Account.
        // The borrowing of funds during a flashAction has to be executed via a callback of the Account.
        // As such the Account cannot be reentered in between the time that funds are borrowed and the final health check is done.
        callbackAccount = account;
        bytes memory callbackData = abi.encode(amountBorrowed, actionTarget, msg.sender, referrer);

        // The Action Target will use the borrowed funds (optionally with additional assets withdrawn from the Account)
        // to execute one or more actions (swap, deposit, mint...).
        // Next the action Target will deposit any of the remaining funds or any of the recipient token
        // resulting from the actions back into the Account.
        // As last step, after all assets are deposited back into the Account a final health check is done:
        // The Collateral Value of all assets in the Account must be bigger than the total liabilities against the Account (including the debt taken during this function).
        // flashActionByCreditor also checks that the Account indeed has opened a margin account for this Lending Pool.
        {
            uint256 accountVersion = IAccount(account).flashActionByCreditor(callbackData, actionTarget, actionData);
            if (!isValidVersion[accountVersion]) revert LendingPoolErrors.InvalidVersion();
        }
    }

    /**
     * @notice Callback of the Account during a flashAction.
     * @param account The contract address of the Arcadia Account.
     * @param callbackData The data for the borrow during a flashAction.
     * @dev During the callback, the Account cannot be reentered and the actionTimestamp is updated.
     */
    function _flashActionCallback(address account, bytes calldata callbackData) internal override {
        (uint256 amountBorrowed, address actionTarget, address sender, bytes3 referrer) =
            abi.decode(callbackData, (uint256, address, address, bytes3));

        uint256 amountBorrowedWithFee = amountBorrowed + amountBorrowed.mulDivUp(originationFee, ONE_4);

        // Mint debt tokens to the Account.
        _deposit(amountBorrowedWithFee, account);

        // Add origination fee to the treasury.
        unchecked {
            if (amountBorrowedWithFee - amountBorrowed > 0) {
                totalRealisedLiquidity += SafeCastLib.safeCastTo128(amountBorrowedWithFee - amountBorrowed);
                realisedLiquidityOf[treasury] += amountBorrowedWithFee - amountBorrowed;
            }
        }

        // Send Borrowed funds to the actionTarget.
        // Transfer fails if there is insufficient liquidity in the pool.
        asset.safeTransfer(actionTarget, amountBorrowed);

        unchecked {
            emit Borrow(account, sender, actionTarget, amountBorrowed, amountBorrowedWithFee - amountBorrowed, referrer);
        }
    }

    /* //////////////////////////////////////////////////////////////
                            ACCOUNTING LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Returns the total amount of outstanding debt in the underlying asset.
     * @return totalDebt The total debt in underlying assets.
     */
    function totalAssets() public view override returns (uint256 totalDebt) {
        // Avoid a second calculation of unrealised debt (expensive)
        // if interests are already synced this block.
        if (lastSyncedTimestamp != uint32(block.timestamp)) {
            totalDebt = realisedDebt + calcUnrealisedDebt();
        } else {
            totalDebt = realisedDebt;
        }
    }

    /**
     * @notice Returns the total redeemable amount of liquidity in the underlying asset.
     * @return totalLiquidity_ The total redeemable amount of liquidity in the underlying asset.
     */
    function totalLiquidity() external view returns (uint256 totalLiquidity_) {
        // Avoid a second calculation of unrealised debt (expensive)
        // if interests are already synced this block.
        if (lastSyncedTimestamp != uint32(block.timestamp)) {
            // The total liquidity equals the sum of the realised liquidity, and the pending interests.
            unchecked {
                totalLiquidity_ = totalRealisedLiquidity + calcUnrealisedDebt();
            }
        } else {
            totalLiquidity_ = totalRealisedLiquidity;
        }
    }

    /**
     * @notice Returns the redeemable amount of liquidity in the underlying asset of an address.
     * @param owner_ The address of the liquidity provider.
     * @return assets The redeemable amount of liquidity in the underlying asset.
     * @dev This function syncs the interests to prevent calculating UnrealisedDebt twice when depositing/withdrawing through the Tranches.
     * @dev After calling this function, the interest rate will not be updated until the next processInterests() call.
     */
    function liquidityOfAndSync(address owner_) external returns (uint256 assets) {
        _syncInterests();
        assets = realisedLiquidityOf[owner_];
    }

    /**
     * @notice Returns the redeemable amount of liquidity in the underlying asset of an address.
     * @param owner_ The address of the liquidity provider.
     * @return assets The redeemable amount of liquidity in the underlying asset.
     *  ¨* @dev liquidityOf() might deviate from the actual liquidityOf after interests are synced in the following situations:
     *   - liquidityOf() the treasury will be slightly underestimated, since all rounding errors of all tranches will go to the treasury.
     *   - Tranches with 0 liquidity don't earn any interests, interests will go to treasury instead.
     *   - If totalInterestWeight is 0 (will never happen) all interests will go to treasury instead.
     */
    function liquidityOf(address owner_) external view returns (uint256 assets) {
        // Avoid a second calculation of unrealised debt (expensive).
        // if interests are already synced this block.
        if (lastSyncedTimestamp != uint32(block.timestamp)) {
            // The total liquidity of a tranche equals the sum of the realised liquidity
            // of the tranche, and its pending interests.
            uint256 interest = calcUnrealisedDebt().mulDivDown(interestWeight[owner_], totalInterestWeight);
            unchecked {
                assets = realisedLiquidityOf[owner_] + interest;
            }
        } else {
            assets = realisedLiquidityOf[owner_];
        }
    }

    /**
     * @notice Skims any surplus funds in the LendingPool to the treasury.
     * @dev In normal conditions (when there are no ongoing auctions), the total Claimable Liquidity should be equal
     * to the sum of the available funds (the balanceOf() the underlying asset) in the pool and the total open debt.
     * In practice the actual sum of available funds and total open debt will always be bigger than the total Claimable Liquidity.
     * This because of the rounding errors of the ERC4626 calculations (conversions between assets and shares),
     * or because someone accidentally sent funds directly to the pool instead of depositing via a Tranche.
     * This functions makes the surplus available to the Treasury (otherwise they would be lost forever).
     * @dev In case you accidentally sent funds to the pool, contact the current treasury manager.
     */
    function skim() external processInterests {
        // During auction initiation, debt tokens representing the liquidation incentives are minted at start of the auction
        // yet not accounted for in the totalRealisedLiquidity.
        // -> skim function must be blocked during auctions.
        if (auctionsInProgress != 0) revert LendingPoolErrors.AuctionOngoing();

        // Pending interests are synced via the processInterests modifier.
        uint256 delta = asset.balanceOf(address(this)) + realisedDebt - totalRealisedLiquidity;

        // Add difference to the treasury.
        unchecked {
            totalRealisedLiquidity = SafeCastLib.safeCastTo128(delta + totalRealisedLiquidity);
            realisedLiquidityOf[treasury] += delta;
        }
    }

    /* //////////////////////////////////////////////////////////////
                            INTERESTS LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Syncs all unrealised debt (= interest for LP and treasury).
     * @dev Calculates the unrealised debt since last sync, and realises it by minting an equal amount of
     * debt tokens to all debt holders and interests to LPs and the treasury.
     */
    function _syncInterests() internal {
        // Only Sync interests once per block.
        if (lastSyncedTimestamp != uint32(block.timestamp)) {
            uint256 unrealisedDebt = calcUnrealisedDebt();
            lastSyncedTimestamp = uint32(block.timestamp);

            // Sync interests for borrowers.
            unchecked {
                realisedDebt += unrealisedDebt;
            }

            // Sync interests for LPs and Protocol Treasury.
            _syncInterestsToLiquidityProviders(unrealisedDebt);

            emit InterestSynced(unrealisedDebt);
        }
    }

    /**
     * @notice Calculates the unrealised debt (interests).
     * @return unrealisedDebt The unrealised debt.
     * @dev To calculate the unrealised debt over an amount of time, you need to calculate D[(1+r)^x-1].
     * The base of the exponential: 1 + r, is a 18 decimals fixed point number
     * with r the yearly interest rate.
     * The exponent of the exponential: x, is a 18 decimals fixed point number.
     * The exponent x is calculated as: the amount of seconds passed since last sync timestamp divided by
     * the average of seconds per year.
     */
    function calcUnrealisedDebt() public view returns (uint256 unrealisedDebt) {
        unchecked {
            //gas: Can't overflow for reasonable interest rates.
            uint256 base = 1e18 + interestRate;

            // gas: Only overflows when (block.timestamp - lastSyncedBlockTimestamp) > 1e59
            // in practice: exponent in LogExpMath lib is limited to 130e18,
            // Corresponding to a delta of timestamps of 4099680000 (or 130 years),
            // much bigger than any realistic time difference between two syncs.
            uint256 exponent = ((block.timestamp - lastSyncedTimestamp) * 1e18) / YEARLY_SECONDS;

            // gas: Taking an imaginary worst-case scenario with max interest of 1000%
            // over a period of 5 years.
            // This won't overflow as long as openDebt < 3402823669209384912995114146594816
            // which is 3.4 million billion *10**18 decimals.
            unrealisedDebt = (realisedDebt * (LogExpMath.pow(base, exponent) - 1e18)) / 1e18;
        }

        return SafeCastLib.safeCastTo128(unrealisedDebt);
    }

    /**
     * @notice Syncs interest payments to the liquidity providers and the treasury.
     * @param assets The total amount of underlying assets to be paid out as interests.
     * @dev The interest weight of each Tranche determines the relative share of yield (interest payments)
     * that goes to its liquidity providers.
     * @dev If the total interest weight is 0, all interests will go to the treasury.
     */
    function _syncInterestsToLiquidityProviders(uint256 assets) internal {
        uint256 remainingAssets = assets;

        uint256 totalInterestWeight_ = totalInterestWeight;
        if (totalInterestWeight_ > 0) {
            uint256 realisedLiquidity;
            uint256 trancheShare;
            uint256 trancheLength = tranches.length;
            for (uint256 i; i < trancheLength; ++i) {
                realisedLiquidity = realisedLiquidityOf[tranches[i]];
                // Don't pay interests to Tranches without liquidity.
                // Interests will go to treasury instead.
                if (realisedLiquidity == 0) continue;
                trancheShare = assets.mulDivDown(interestWeightTranches[i], totalInterestWeight_);
                unchecked {
                    realisedLiquidityOf[tranches[i]] = realisedLiquidity + trancheShare;
                    remainingAssets -= trancheShare;
                }
            }
        }
        unchecked {
            totalRealisedLiquidity = SafeCastLib.safeCastTo128(totalRealisedLiquidity + assets);

            // Add the remainingAssets to the treasury balance.
            realisedLiquidityOf[treasury] += remainingAssets;
        }
    }

    /* //////////////////////////////////////////////////////////////
                        INTEREST RATE LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Sets the interest configuration parameters.
     * @param baseRatePerYear_ The base interest rate per year.
     * @param lowSlopePerYear_ The slope of the interest rate per year when the utilization rate is below the utilization threshold.
     * @param highSlopePerYear_ The slope of the interest rate per year when the utilization rate exceeds the utilization threshold.
     * @param utilisationThreshold_ The utilization threshold for determining the interest rate slope change.
     * @dev We cannot use a struct to store all variables, since this would cause the contract size to exceed the maximum size.
     */
    function setInterestParameters(
        uint72 baseRatePerYear_,
        uint72 lowSlopePerYear_,
        uint72 highSlopePerYear_,
        uint16 utilisationThreshold_
    ) external processInterests onlyOwner {
        baseRatePerYear = baseRatePerYear_;
        lowSlopePerYear = lowSlopePerYear_;
        highSlopePerYear = highSlopePerYear_;
        utilisationThreshold = utilisationThreshold_;
    }

    /**
     * @notice Updates the interest rate.
     * @dev Any address can call this, it will sync unrealised interests and update the interest rate.
     */
    function updateInterestRate() external processInterests { }

    /**
     * @notice Updates the interest rate.
     * @param totalDebt Total amount of debt.
     * @param totalLiquidity_ Total amount of Liquidity (sum of borrowed out assets and assets still available in the Lending Pool).
     */
    function _updateInterestRate(uint256 totalDebt, uint256 totalLiquidity_) internal {
        uint256 utilisation; // 4 decimals precision
        unchecked {
            // This doesn't overflow since totalDebt is a uint128: uint128 * 10_000 < type(uint256).max.
            if (totalLiquidity_ > 0) utilisation = totalDebt * ONE_4 / totalLiquidity_;
        }
        // Cap utilisation to 100%.
        utilisation = utilisation <= ONE_4 ? utilisation : ONE_4;

        emit PoolStateUpdated(totalDebt, totalLiquidity_, interestRate = _calculateInterestRate(utilisation));
    }

    /**
     * @notice Calculates the interest rate.
     * @param utilisation Utilisation rate, 4 decimal precision.
     * @return interestRate_ The current interest rate, 18 decimal precision.
     * @dev The interest rate is a function of the utilisation of the Lending Pool.
     * We use two linear curves: one below the optimal utilisation with low slope and a steep one above.
     */
    function _calculateInterestRate(uint256 utilisation) internal view returns (uint80 interestRate_) {
        // While repays are paused, interest rate is set to 0.
        if (repayPaused) return 0;

        unchecked {
            if (utilisation >= utilisationThreshold) {
                // lsIR (1e22) = uT (1e4) * ls (1e18).
                uint256 lowSlopeInterest = uint256(utilisationThreshold) * lowSlopePerYear;
                // hsIR (1e22) = (u - uT) (1e4) * hs (e18).
                uint256 highSlopeInterest = uint256(utilisation - utilisationThreshold) * highSlopePerYear;
                // i (1e18) =  (lsIR (e22) + hsIR (1e22)) / 1e4 + bs (1e18).
                interestRate_ = uint80((lowSlopeInterest + highSlopeInterest) / ONE_4 + baseRatePerYear);
            } else {
                // i (1e18) = (u (1e4) * ls (1e18)) / 1e4 + br (1e18).
                interestRate_ = uint80(utilisation * lowSlopePerYear / ONE_4 + baseRatePerYear);
            }
        }
    }

    /* //////////////////////////////////////////////////////////////
                        LIQUIDATION LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Initiates the liquidation process for an Account.
     * @param initiator The address of the liquidation initiator.
     * @param minimumMargin_ The minimum margin of the Account.
     * @return startDebt The initial debt of the liquidated Account.
     * @dev This function is only callable by an Arcadia Account with debt.
     * The liquidation process involves assessing the Account's debt and calculating liquidation incentives,
     * which are considered as extra debt.
     * The extra debt is then minted towards the Account to encourage the liquidation process and bring the Account to a healthy state.
     * @dev Only Accounts with non-zero balances can have debt, and debtTokens are non-transferrable.
     * @dev If the provided Account has a debt balance of 0, the function reverts with the error "IsNotAnAccountWithDebt."
     */
    function startLiquidation(address initiator, uint256 minimumMargin_)
        external
        override
        whenLiquidationNotPaused
        processInterests
        returns (uint256 startDebt)
    {
        // Only Accounts can have debt, and debtTokens are non-transferrable.
        // Hence by checking that the balance of the msg.sender is not 0,
        // we know that the sender is indeed an Account and has debt.
        startDebt = maxWithdraw(msg.sender);
        if (startDebt == 0) revert LendingPoolErrors.IsNotAnAccountWithDebt();

        // Calculate liquidation incentives which have to be paid by the Account owner and are minted
        // as extra debt to the Account.
        (uint256 initiationReward, uint256 terminationReward, uint256 liquidationPenalty) =
            _calculateRewards(startDebt, minimumMargin_);

        // Mint the liquidation incentives as extra debt towards the Account.
        _deposit(initiationReward + liquidationPenalty + terminationReward, msg.sender);

        // Increase the realised liquidity for the initiator.
        // The other incentives will only be added as realised liquidity for the respective actors
        // after the auction is finished.
        realisedLiquidityOf[initiator] += initiationReward;
        totalRealisedLiquidity = SafeCastLib.safeCastTo128(totalRealisedLiquidity + initiationReward);

        // If this is the sole ongoing auction, prevent any deposits and withdrawals in the most jr tranche
        if (auctionsInProgress == 0 && tranches.length > 0) {
            unchecked {
                ITranche(tranches[tranches.length - 1]).setAuctionInProgress(true);
            }
        }

        unchecked {
            ++auctionsInProgress;
        }

        // Emit event
        emit AuctionStarted(msg.sender, address(this), uint128(startDebt));
    }

    /**
     * @notice Ends the liquidation process for a specific Account and settles the liquidation incentives.
     * @param account The address of the Account undergoing liquidation settlement.
     * @param startDebt The initial debt amount of the liquidated Account.
     * @param minimumMargin_ The minimum margin of the Account.
     * @param terminator The address of the liquidation terminator.
     * @dev In the happy flow, the auction proceeds are sufficient to pay off enough debt
     *  to bring the Account in a healthy position, and pay out all liquidation incentives to the
     *  relevant actors.
     */
    function settleLiquidationHappyFlow(address account, uint256 startDebt, uint256 minimumMargin_, address terminator)
        external
        whenLiquidationNotPaused
        onlyLiquidator
        processInterests
    {
        _settleLiquidationHappyFlow(account, startDebt, minimumMargin_, terminator, 0);
    }

    /**
     * @notice Ends the liquidation process for a specific Account and settles the liquidation incentives.
     * @param account The address of the Account undergoing liquidation settlement.
     * @param startDebt The initial debt amount of the liquidated Account.
     * @param minimumMargin_ The minimum margin of the Account.
     * @param terminator The address of the liquidation terminator.
     * @param surplus The surplus amount obtained from the liquidation process.
     * @dev In the happy flow, the auction proceeds are sufficient to pay off enough debt
     *  to bring the Account in a healthy position, and pay out all liquidation incentives to the
     *  relevant actors.
     * @dev The following pending incentives are made claimable:
     *   - The "terminationReward", going towards the terminator of the auction.
     *   - The "liquidationFee", going towards LPs and the Treasury.
     *   - If there are still remaining assets after paying off all debt and incentives,
     *   the surplus goes towards the owner of the account.
     */
    function _settleLiquidationHappyFlow(
        address account,
        uint256 startDebt,
        uint256 minimumMargin_,
        address terminator,
        uint256 surplus
    ) internal {
        (uint256 initiationReward, uint256 terminationReward, uint256 liquidationPenalty) =
            _calculateRewards(startDebt, minimumMargin_);

        // Pay out the "liquidationPenalty" to the most Junior Tranche and Treasury.
        _syncLiquidationFee(liquidationPenalty);

        totalRealisedLiquidity =
            SafeCastLib.safeCastTo128(totalRealisedLiquidity + terminationReward + liquidationPenalty + surplus);

        unchecked {
            // Pay out any surplus to the current Account Owner.
            if (surplus > 0) realisedLiquidityOf[IAccount(account).owner()] += surplus;
            // Pay out the "terminationReward" to the "terminator".
            realisedLiquidityOf[terminator] += terminationReward;
        }

        _endLiquidation();

        emit AuctionFinished(
            account, address(this), startDebt, initiationReward, terminationReward, liquidationPenalty, 0, surplus
        );
    }

    /**
     * @notice Ends the liquidation process for a specific Account and settles the liquidation incentives/bad debt.
     * @param account The address of the Account undergoing liquidation settlement.
     * @param startDebt The initial debt amount of the liquidated Account.
     * @param minimumMargin_ The minimum margin of the Account.
     * @param terminator The address of the auction terminator.
     * @dev In the unhappy flow, the auction proceeds are not sufficient to pay out all liquidation incentives
     *  and maybe not even to pay off all debt.
     * @dev The order in which incentives are not paid out/ bad debt is settled is fixed:
     *   - First, the "liquidationFee", going towards LPs and the Treasury is not paid out.
     *   - Next, the "terminationReward", going towards the terminator of the auction is not paid out.
     *   - Next, the underlying assets of LPs in the most junior Tranche are written off pro rata.
     *   - Next, the underlying assets of LPs in the second most junior Tranche are written off pro rata.
     *   - etc.
     */
    function settleLiquidationUnhappyFlow(
        address account,
        uint256 startDebt,
        uint256 minimumMargin_,
        address terminator
    ) external whenLiquidationNotPaused onlyLiquidator processInterests {
        (uint256 initiationReward, uint256 terminationReward, uint256 liquidationPenalty) =
            _calculateRewards(startDebt, minimumMargin_);

        // Any remaining debt that was not recovered during the auction must be written off.
        // Depending on the size of the remaining debt, different stakeholders will be impacted.
        uint256 debtShares = balanceOf[account];
        uint256 openDebt = convertToAssets(debtShares);
        uint256 badDebt;
        if (openDebt > terminationReward + liquidationPenalty) {
            // "openDebt" is bigger than pending liquidation incentives.
            // No incentives will be paid out, and a default event is triggered.
            unchecked {
                badDebt = openDebt - terminationReward - liquidationPenalty;
            }

            totalRealisedLiquidity = uint128(totalRealisedLiquidity - badDebt);
            _processDefault(badDebt);
        } else {
            uint256 remainder = liquidationPenalty + terminationReward - openDebt;
            if (openDebt >= liquidationPenalty) {
                // "openDebt" is bigger than the "liquidationPenalty" but smaller than the total pending liquidation incentives.
                // Don't pay out the "liquidationPenalty" to Lps, partially pay out the "terminator".
                realisedLiquidityOf[terminator] += remainder;
            } else {
                // "openDebt" is smaller than the "liquidationPenalty".
                // Fully pay out the "terminator" and partially pay out the "liquidationPenalty".
                realisedLiquidityOf[terminator] += terminationReward;
                _syncLiquidationFee(remainder - terminationReward);
            }
            totalRealisedLiquidity = SafeCastLib.safeCastTo128(totalRealisedLiquidity + remainder);
        }

        // Remove the remaining debt from the Account now that it is written off from the liquidation incentives/Liquidity Providers.
        _burn(account, debtShares);
        realisedDebt -= openDebt;
        emit Withdraw(msg.sender, account, account, openDebt, debtShares);

        _endLiquidation();

        emit AuctionFinished(
            account, address(this), startDebt, initiationReward, terminationReward, liquidationPenalty, badDebt, 0
        );
    }

    /**
     * @notice Ends the liquidation.
     * @dev Unlocks the most junior Tranche if there are no other liquidations ongoing.
     */
    function _endLiquidation() internal {
        // Decrement the number of auctions in progress.
        unchecked {
            --auctionsInProgress;
        }

        // Hook to the most junior Tranche.
        if (auctionsInProgress == 0 && tranches.length > 0) {
            unchecked {
                ITranche(tranches[tranches.length - 1]).setAuctionInProgress(false);
            }
        }
    }

    /**
     * @notice Handles the accounting in case of bad debt (Account became undercollateralised).
     * @param badDebt The total amount of underlying assets that need to be written off as bad debt.
     * @dev The order of the Tranches is important, the most senior Tranche is at index 0, the most junior at the last index.
     * @dev The most junior tranche will lose its underlying assets first. If all liquidity of a certain Tranche is written off,
     * the complete tranche is locked and removed. If there is still remaining bad debt, the next Tranche starts losing capital.
     * @dev If all Tranches are written off and there is still remaining badDebt, the accounting of the pool no longer holds
     * (sum of all realisedLiquidityOf() balances is bigger then totalRealisedLiquidity).
     * In this case no new Tranches should be added to restart the LendingPool and any remaining funds should be withdrawn.
     */
    function _processDefault(uint256 badDebt) internal {
        address tranche;
        uint256 maxBurnable;
        uint256 length = tranches.length;
        for (uint256 i = length; i > 0;) {
            unchecked {
                --i;
            }
            tranche = tranches[i];
            maxBurnable = realisedLiquidityOf[tranche];
            if (badDebt < maxBurnable) {
                // Deduct badDebt from the balance of the most junior Tranche.
                unchecked {
                    realisedLiquidityOf[tranche] -= badDebt;
                }
                break;
            } else {
                // Unhappy flow, should never occur in practice!
                // badDebt is bigger than the balance of most junior Tranche -> tranche is completely wiped out
                // and temporarily locked (no new deposits or withdraws possible).
                // DAO or insurance might refund (Part of) the losses, and add Tranche back.
                realisedLiquidityOf[tranche] = 0;
                _popTranche(i, tranche);
                unchecked {
                    badDebt -= maxBurnable;
                }
                ITranche(tranche).lock();
                // Hook to the new most junior Tranche to inform that auctions are ongoing.
                if (i != 0) ITranche(tranches[i - 1]).setAuctionInProgress(true);
            }
        }
    }

    /**
     * @notice Syncs liquidation penalties to the most Junior Tranche and the treasury.
     * @param assets The total amount of underlying assets to be paid out as liquidation fee.
     * @dev The liquidationWeightTranche and liquidationWeightTreasury determines the relative share of yield (liquidation penalties)
     * that goes to the most Junior Tranche and the treasury.
     * @dev If the total liquidation weight is 0, the liquidation fee is added to the treasury.
     */
    function _syncLiquidationFee(uint256 assets) internal {
        // Cache storage variables.
        uint256 length = tranches.length;
        uint256 weightTranche = liquidationWeightTranche;
        uint256 totalWeight;
        unchecked {
            totalWeight = weightTranche + liquidationWeightTreasury;
        }

        // Sync fee to the most Junior Tranche (last index).
        if (totalWeight > 0 && length > 0) {
            uint256 realisedLiquidity = realisedLiquidityOf[tranches[length - 1]];
            // Don't pay fees to a Tranche without liquidity.
            // Interests will go to treasury instead.
            if (realisedLiquidity > 0) {
                uint256 trancheFee = assets.mulDivDown(weightTranche, totalWeight);
                unchecked {
                    realisedLiquidityOf[tranches[length - 1]] = realisedLiquidity + trancheFee;
                    assets -= trancheFee;
                }
            }
        }

        // Add the remaining fee to the treasury balance.
        unchecked {
            realisedLiquidityOf[treasury] += assets;
        }
    }

    /**
     * @notice Calculates the rewards and penalties for the liquidation process based on the given debt amount.
     * @param debt The debt amount of the Account at the time of liquidation initiation.
     * @param minimumMargin_ The minimum margin of the Account.
     * @return initiationReward The reward for the liquidation initiator, capped by the maximum initiator reward.
     * @return terminationReward The reward for closing the liquidation process, capped by the maximum termination reward.
     * @return liquidationPenalty The penalty paid by the Account owner towards the liquidity providers and the protocol treasury.
     * @dev The rewards for the initiator and terminator should at least cover the gas costs.
     * -> minimumMargin should be set big enough such that "minimumMargin * minRewardWeight" can cover any possible gas cost to initiate/terminate the liquidation.
     * @dev Since the initiation/termination costs do not increase with position size, the initiator and terminator rewards can be capped to a maximum value.
     */
    function _calculateRewards(uint256 debt, uint256 minimumMargin_)
        internal
        view
        returns (uint256 initiationReward, uint256 terminationReward, uint256 liquidationPenalty)
    {
        uint256 maxReward_ = maxReward;
        // The minimum reward, for both the initiation- and terminationReward, is defined as a fixed percentage of the minimumMargin.
        uint256 minReward = minimumMargin_.mulDivUp(minRewardWeight, ONE_4);

        // Initiation reward must be between minReward and maxReward.
        initiationReward = debt.mulDivDown(initiationWeight, ONE_4);
        initiationReward = initiationReward > minReward ? initiationReward : minReward;
        initiationReward = initiationReward > maxReward_ ? maxReward_ : initiationReward;

        // Termination reward must be between minReward and maxReward.
        terminationReward = debt.mulDivDown(terminationWeight, ONE_4);
        terminationReward = terminationReward > minReward ? terminationReward : minReward;
        terminationReward = terminationReward > maxReward_ ? maxReward_ : terminationReward;

        liquidationPenalty = debt.mulDivUp(penaltyWeight, ONE_4);
    }

    /*///////////////////////////////////////////////////////////////
                        MANAGE AUCTION SETTINGS
    ///////////////////////////////////////////////////////////////*/

    /**
     * @notice Sets the liquidation parameters.
     * @param initiationWeight_ Reward paid to the Liquidation Initiator.
     * @param penaltyWeight_ Penalty paid by the Account owner to the Creditor.
     * @param terminationWeight_ Reward paid to the Liquidation closer.
     * @param minRewardWeight_ The minimum reward that is paid to the initiator/terminator of a liquidation.
     * @param maxReward_ The maximum reward that is paid to the initiator/terminator of a liquidation.
     * @dev Each weight has 4 decimals precision (50 equals 0,005 or 0,5%).
     * @dev Each weight sets the % of the debt that is paid as reward to the initiator and terminator of a liquidation.
     * This reward is capped in absolute value by the maxReward respectively maxReward.
     * @dev We cannot use a struct to store all variables, since this would cause the contract size to exceed the maximum size.
     */
    function setLiquidationParameters(
        uint16 initiationWeight_,
        uint16 penaltyWeight_,
        uint16 terminationWeight_,
        uint16 minRewardWeight_,
        uint80 maxReward_
    ) external onlyOwner {
        // When auctions are ongoing, it is not allowed to modify the auction parameters,
        // as that would corrupt the rewards and penalties calculated by _calculateRewards().
        if (auctionsInProgress != 0) revert LendingPoolErrors.AuctionOngoing();

        // Total penalties/rewards, paid by the Account cannot exceed MAX_TOTAL_PENALTY.
        if (uint256(initiationWeight_) + penaltyWeight_ + terminationWeight_ > MAX_TOTAL_PENALTY) {
            revert LendingPoolErrors.LiquidationWeightsTooHigh();
        }

        // Sum of the initiationReward and terminationReward cannot exceed minimumMargin of the Account.
        // -> minRewardWeight is capped to 50%.
        if (minRewardWeight_ > 5000) revert LendingPoolErrors.LiquidationWeightsTooHigh();

        // Store new parameters.
        initiationWeight = initiationWeight_;
        penaltyWeight = penaltyWeight_;
        terminationWeight = terminationWeight_;
        minRewardWeight = minRewardWeight_;
        maxReward = maxReward_;
    }

    /**
     * @notice Sets the minimum amount of collateral that must be held in an Account before a position can be opened.
     * @param minimumMargin_ The new minimumMargin.
     * @dev The minimum margin should be a conservative upper estimate of the maximal gas cost to liquidate a position (fixed cost, independent of openDebt).
     * The minimumMargin prevents dusting attacks, and ensures that upon liquidations positions are big enough to cover
     * network transaction costs while remaining attractive to liquidate.
     */
    function setMinimumMargin(uint96 minimumMargin_) external onlyOwner {
        minimumMargin = minimumMargin_;
    }

    /* //////////////////////////////////////////////////////////////
                            ACCOUNT LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @notice Sets a new Risk Manager.
     * @param riskManager_ The address of the new Risk Manager.
     */
    function setRiskManager(address riskManager_) external onlyOwner {
        _setRiskManager(riskManager_);
    }

    /**
     * @notice Enables or disables a certain Account version to be used as margin account.
     * @param accountVersion the Account version to be enabled/disabled.
     * @param valid The validity of the respective accountVersion.
     */
    function setAccountVersion(uint256 accountVersion, bool valid) external onlyOwner {
        _setAccountVersion(accountVersion, valid);
    }

    /**
     * @inheritdoc Creditor
     */
    function openMarginAccount(uint256 accountVersion)
        external
        view
        override
        returns (bool success, address numeraire, address liquidator_, uint256 minimumMargin_)
    {
        if (isValidVersion[accountVersion]) {
            success = true;
            numeraire = address(asset);
            liquidator_ = LIQUIDATOR;
            minimumMargin_ = minimumMargin;
        }
    }

    /**
     * @inheritdoc Creditor
     */
    function closeMarginAccount(address account) external view override {
        if (maxWithdraw(account) != 0) revert LendingPoolErrors.OpenPositionNonZero();
    }

    /**
     * @inheritdoc Creditor
     */
    function getOpenPosition(address account) external view override returns (uint256 openPosition) {
        openPosition = maxWithdraw(account);
    }

    /* //////////////////////////////////////////////////////////////
                            HELPER FUNCTIONS
    ////////////////////////////////////////////////////////////// */
    /**
     * @notice Returns the configuration of the interest rate slopes.
     * @return baseRatePerYear The base interest rate per year.
     * @return lowSlopePerYear The slope of the interest rate per year when the utilization rate is below the utilization threshold.
     * @return highSlopePerYear The slope of the interest rate per year when the utilization rate exceeds the utilization threshold.
     * @return utilisationThreshold The utilization threshold for determining the interest rate slope change.
     */
    function getInterestRateConfig() external view returns (uint72, uint72, uint72, uint16) {
        return (baseRatePerYear, lowSlopePerYear, highSlopePerYear, utilisationThreshold);
    }

    /**
     * @notice Returns the liquidation parameters.
     * @return initiationWeight Reward paid to the Liquidation Initiator.
     * @return penaltyWeight Penalty paid by the Account owner to the Creditor.
     * @return terminationWeight Reward paid to the Liquidation closer.
     * @return minRewardWeight The minimum reward that is paid to the initiator/terminator of a liquidation.
     * @return maxReward The maximum reward that is paid to the initiator/terminator of a liquidation.
     */
    function getLiquidationParameters() external view returns (uint16, uint16, uint16, uint16, uint80) {
        return (initiationWeight, penaltyWeight, terminationWeight, minRewardWeight, maxReward);
    }
}
合同源代码
文件 16 的 20:LendingPoolGuardian.sol
/**
 * Created by Pragma Labs
 * SPDX-License-Identifier: BUSL-1.1
 */
pragma solidity 0.8.22;

import { BaseGuardian, GuardianErrors } from "../../lib/accounts-v2/src/guardians/BaseGuardian.sol";

/**
 * @title LendingPool Guardian.
 * @author Pragma Labs
 * @notice Logic inherited by the LendingPool that allows:
 * - An authorized guardian to trigger an emergency stop.
 * - The protocol owner to unpause functionalities one-by-one.
 * - Anyone to unpause all functionalities after a fixed cool-down period.
 */
abstract contract LendingPoolGuardian is BaseGuardian {
    /* //////////////////////////////////////////////////////////////
                                STORAGE
    ////////////////////////////////////////////////////////////// */

    // Flag indicating if the repay() function is paused.
    bool public repayPaused;
    // Flag indicating if the withdraw() function is paused.
    bool public withdrawPaused;
    // Flag indicating if the borrow() function is paused.
    bool public borrowPaused;
    // Flag indicating if the deposit() function is paused.
    bool public depositPaused;
    // Flag indicating if the liquidation() function is paused.
    bool public liquidationPaused;

    /* //////////////////////////////////////////////////////////////
                                EVENTS
    ////////////////////////////////////////////////////////////// */

    event PauseFlagsUpdated(
        bool repayPauseFlagsUpdated,
        bool withdrawPauseFlagsUpdated,
        bool borrowPauseFlagsUpdated,
        bool depositPauseFlagsUpdated,
        bool liquidationPauseFlagsUpdated
    );

    /* //////////////////////////////////////////////////////////////
                                MODIFIERS
    ////////////////////////////////////////////////////////////// */

    /**
     * @dev Throws if the repay functionality is paused.
     */
    modifier whenRepayNotPaused() {
        if (repayPaused) revert GuardianErrors.FunctionIsPaused();
        _;
    }

    /**
     * @dev Throws if the withdraw functionality is paused.
     */
    modifier whenWithdrawNotPaused() {
        if (withdrawPaused) revert GuardianErrors.FunctionIsPaused();
        _;
    }

    /**
     * @dev Throws if the borrow functionality is paused.
     */
    modifier whenBorrowNotPaused() {
        if (borrowPaused) revert GuardianErrors.FunctionIsPaused();
        _;
    }

    /**
     * @dev Throws if the deposit functionality is paused.
     */
    modifier whenDepositNotPaused() {
        if (depositPaused) revert GuardianErrors.FunctionIsPaused();
        _;
    }

    /**
     * @dev Throws if the liquidation functionality is paused.
     */
    modifier whenLiquidationNotPaused() {
        if (liquidationPaused) revert GuardianErrors.FunctionIsPaused();
        _;
    }

    /* //////////////////////////////////////////////////////////////
                            PAUSING LOGIC
    ////////////////////////////////////////////////////////////// */

    /**
     * @inheritdoc BaseGuardian
     * @dev This function will pause the functionality to:
     * - Repay debt.
     * - Withdraw liquidity.
     * - Borrow.
     * - Deposit liquidity.
     * - Liquidate positions.
     */
    function pause() external override onlyGuardian afterCoolDownOf(32 days) {
        pauseTimestamp = uint96(block.timestamp);

        emit PauseFlagsUpdated(
            repayPaused = true,
            withdrawPaused = true,
            borrowPaused = true,
            depositPaused = true,
            liquidationPaused = true
        );
    }

    /**
     * @notice This function is used to unpause one or more flags.
     * @param repayPaused_ False when repay functionality should be unPaused.
     * @param withdrawPaused_ False when withdraw functionality should be unPaused.
     * @param borrowPaused_ False when borrow functionality should be unPaused.
     * @param depositPaused_ False when deposit functionality should be unPaused.
     * @param liquidationPaused_ False when liquidation functionality should be unPaused.
     * @dev This function can unPause repay, withdraw, borrow, and deposit individually.
     * @dev Can only update flags from paused (true) to unPaused (false), cannot be used the other way around
     * (to set unPaused flags to paused).
     */
    function unpause(
        bool repayPaused_,
        bool withdrawPaused_,
        bool borrowPaused_,
        bool depositPaused_,
        bool liquidationPaused_
    ) external onlyOwner {
        emit PauseFlagsUpdated(
            repayPaused = repayPaused && repayPaused_,
            withdrawPaused = withdrawPaused && withdrawPaused_,
            borrowPaused = borrowPaused && borrowPaused_,
            depositPaused = depositPaused && depositPaused_,
            liquidationPaused = liquidationPaused && liquidationPaused_
        );
    }

    /**
     * @inheritdoc BaseGuardian
     * @dev This function will unpause the functionality to:
     * - Repay debt.
     * - Withdraw liquidity.
     * - Liquidate positions.
     */
    function unpause() external override afterCoolDownOf(30 days) {
        emit PauseFlagsUpdated(
            repayPaused = false, withdrawPaused = false, borrowPaused, depositPaused, liquidationPaused = false
        );
    }
}
合同源代码
文件 17 的 20:LogExpMath.sol
// SPDX-License-Identifier: MIT
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.

// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

pragma solidity 0.8.22;

import "./BalancerErrors.sol";

/* solhint-disable */

/**
 * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument).
 *
 * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural
 * exponentiation and logarithm (where the base is Euler's number).
 *
 * @author Fernando Martinelli - @fernandomartinelli
 * @author Sergio Yuhjtman - @sergioyuhjtman
 * @author Daniel Fernandez - @dmf7z
 */
library LogExpMath {
    // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying
    // two numbers, and multiply by ONE when dividing them.

    // All arguments and return values are 18 decimal fixed point numbers.
    int256 constant ONE_18 = 1e18;

    // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the
    // case of ln36, 36 decimals.
    int256 constant ONE_20 = 1e20;
    int256 constant ONE_36 = 1e36;

    // The domain of natural exponentiation is bound by the word size and number of decimals used.
    //
    // Because internally the result will be stored using 20 decimals, the largest possible result is
    // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221.
    // The smallest possible result is 10^(-18), which makes largest negative argument
    // ln(10^(-18)) = -41.446531673892822312.
    // We use 130.0 and -41.0 to have some safety margin.
    int256 constant MAX_NATURAL_EXPONENT = 130e18;
    int256 constant MIN_NATURAL_EXPONENT = -41e18;

    // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point
    // 256 bit integer.
    int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17;
    int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17;

    uint256 constant MILD_EXPONENT_BOUND = 2 ** 254 / uint256(ONE_20);

    // 18 decimal constants
    int256 constant x0 = 128_000_000_000_000_000_000; // 2ˆ7
    int256 constant a0 = 38_877_084_059_945_950_922_200_000_000_000_000_000_000_000_000_000_000_000; // eˆ(x0) (no decimals)
    int256 constant x1 = 64_000_000_000_000_000_000; // 2ˆ6
    int256 constant a1 = 6_235_149_080_811_616_882_910_000_000; // eˆ(x1) (no decimals)

    // 20 decimal constants
    int256 constant x2 = 3_200_000_000_000_000_000_000; // 2ˆ5
    int256 constant a2 = 7_896_296_018_268_069_516_100_000_000_000_000; // eˆ(x2)
    int256 constant x3 = 1_600_000_000_000_000_000_000; // 2ˆ4
    int256 constant a3 = 888_611_052_050_787_263_676_000_000; // eˆ(x3)
    int256 constant x4 = 800_000_000_000_000_000_000; // 2ˆ3
    int256 constant a4 = 298_095_798_704_172_827_474_000; // eˆ(x4)
    int256 constant x5 = 400_000_000_000_000_000_000; // 2ˆ2
    int256 constant a5 = 5_459_815_003_314_423_907_810; // eˆ(x5)
    int256 constant x6 = 200_000_000_000_000_000_000; // 2ˆ1
    int256 constant a6 = 738_905_609_893_065_022_723; // eˆ(x6)
    int256 constant x7 = 100_000_000_000_000_000_000; // 2ˆ0
    int256 constant a7 = 271_828_182_845_904_523_536; // eˆ(x7)
    int256 constant x8 = 50_000_000_000_000_000_000; // 2ˆ-1
    int256 constant a8 = 164_872_127_070_012_814_685; // eˆ(x8)
    int256 constant x9 = 25_000_000_000_000_000_000; // 2ˆ-2
    int256 constant a9 = 128_402_541_668_774_148_407; // eˆ(x9)
    int256 constant x10 = 12_500_000_000_000_000_000; // 2ˆ-3
    int256 constant a10 = 113_314_845_306_682_631_683; // eˆ(x10)
    int256 constant x11 = 6_250_000_000_000_000_000; // 2ˆ-4
    int256 constant a11 = 106_449_445_891_785_942_956; // eˆ(x11)

    /**
     * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent.
     *
     * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function pow(uint256 x, uint256 y) internal pure returns (uint256) {
        if (y == 0) {
            // We solve the 0^0 indetermination by making it equal one.
            return uint256(ONE_18);
        }

        if (x == 0) {
            return 0;
        }

        // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to
        // arrive at that result. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means
        // x^y = exp(y * ln(x)).

        // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range.
        _require(x < 2 ** 255, Errors.X_OUT_OF_BOUNDS);
        int256 x_int256 = int256(x);

        // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In
        // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end.

        // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range.
        _require(y < MILD_EXPONENT_BOUND, Errors.Y_OUT_OF_BOUNDS);
        int256 y_int256 = int256(y);

        int256 logx_times_y;
        if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) {
            int256 ln_36_x = _ln_36(x_int256);

            // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just
            // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal
            // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the
            // (downscaled) last 18 decimals.
            logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18);
        } else {
            logx_times_y = _ln(x_int256) * y_int256;
        }
        logx_times_y /= ONE_18;

        // Finally, we compute exp(y * ln(x)) to arrive at x^y
        _require(
            MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT, Errors.PRODUCT_OUT_OF_BOUNDS
        );

        return uint256(exp(logx_times_y));
    }

    /**
     * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent.
     *
     * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function exp(int256 x) internal pure returns (int256) {
        _require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, Errors.INVALID_EXPONENT);

        if (x < 0) {
            // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it
            // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT).
            // Fixed point division requires multiplying by ONE_18.
            return ((ONE_18 * ONE_18) / exp(-x));
        }

        // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n,
        // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7
        // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the
        // decomposition.
        // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this
        // decomposition, which will be lower than the smallest x_n.
        // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1.
        // We mutate x by subtracting x_n, making it the remainder of the decomposition.

        // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause
        // intermediate overflows. Instead we store them as plain integers, with 0 decimals.
        // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the
        // decomposition.

        // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct
        // it and compute the accumulated product.

        int256 firstAN;
        if (x >= x0) {
            x -= x0;
            firstAN = a0;
        } else if (x >= x1) {
            x -= x1;
            firstAN = a1;
        } else {
            firstAN = 1; // One with no decimal places
        }

        // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the
        // smaller terms.
        x *= 100;

        // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point
        // one. Recall that fixed point multiplication requires dividing by ONE_20.
        int256 product = ONE_20;

        if (x >= x2) {
            x -= x2;
            product = (product * a2) / ONE_20;
        }
        if (x >= x3) {
            x -= x3;
            product = (product * a3) / ONE_20;
        }
        if (x >= x4) {
            x -= x4;
            product = (product * a4) / ONE_20;
        }
        if (x >= x5) {
            x -= x5;
            product = (product * a5) / ONE_20;
        }
        if (x >= x6) {
            x -= x6;
            product = (product * a6) / ONE_20;
        }
        if (x >= x7) {
            x -= x7;
            product = (product * a7) / ONE_20;
        }
        if (x >= x8) {
            x -= x8;
            product = (product * a8) / ONE_20;
        }
        if (x >= x9) {
            x -= x9;
            product = (product * a9) / ONE_20;
        }

        // x10 and x11 are unnecessary here since we have high enough precision already.

        // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series
        // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!).

        int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places.
        int256 term; // Each term in the sum, where the nth term is (x^n / n!).

        // The first term is simply x.
        term = x;
        seriesSum += term;

        // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number,
        // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not.

        term = ((term * x) / ONE_20) / 2;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 3;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 4;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 5;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 6;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 7;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 8;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 9;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 10;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 11;
        seriesSum += term;

        term = ((term * x) / ONE_20) / 12;
        seriesSum += term;

        // 12 Taylor terms are sufficient for 18 decimal precision.

        // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor
        // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply
        // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication),
        // and then drop two digits to return an 18 decimal value.

        return (((product * seriesSum) / ONE_20) * firstAN) / 100;
    }

    /**
     * @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
     */
    function _ln(int256 a) private pure returns (int256) {
        if (a < ONE_18) {
            // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less
            // than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call.
            // Fixed point division requires multiplying by ONE_18.
            return (-_ln((ONE_18 * ONE_18) / a));
        }

        // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which
        // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is,
        // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot
        // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a.
        // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this
        // decomposition, which will be lower than the smallest a_n.
        // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1.
        // We mutate a by subtracting a_n, making it the remainder of the decomposition.

        // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point
        // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by
        // ONE_18 to convert them to fixed point.
        // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide
        // by it and compute the accumulated sum.

        int256 sum = 0;
        if (a >= a0 * ONE_18) {
            a /= a0; // Integer, not fixed point division
            sum += x0;
        }

        if (a >= a1 * ONE_18) {
            a /= a1; // Integer, not fixed point division
            sum += x1;
        }

        // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format.
        sum *= 100;
        a *= 100;

        // Because further a_n are  20 digit fixed point numbers, we multiply by ONE_20 when dividing by them.

        if (a >= a2) {
            a = (a * ONE_20) / a2;
            sum += x2;
        }

        if (a >= a3) {
            a = (a * ONE_20) / a3;
            sum += x3;
        }

        if (a >= a4) {
            a = (a * ONE_20) / a4;
            sum += x4;
        }

        if (a >= a5) {
            a = (a * ONE_20) / a5;
            sum += x5;
        }

        if (a >= a6) {
            a = (a * ONE_20) / a6;
            sum += x6;
        }

        if (a >= a7) {
            a = (a * ONE_20) / a7;
            sum += x7;
        }

        if (a >= a8) {
            a = (a * ONE_20) / a8;
            sum += x8;
        }

        if (a >= a9) {
            a = (a * ONE_20) / a9;
            sum += x9;
        }

        if (a >= a10) {
            a = (a * ONE_20) / a10;
            sum += x10;
        }

        if (a >= a11) {
            a = (a * ONE_20) / a11;
            sum += x11;
        }

        // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series
        // that converges rapidly for values of `a` close to one - the same one used in ln_36.
        // Let z = (a - 1) / (a + 1).
        // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

        // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires
        // division by ONE_20.
        int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20);
        int256 z_squared = (z * z) / ONE_20;

        // num is the numerator of the series: the z^(2 * n + 1) term
        int256 num = z;

        // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
        int256 seriesSum = num;

        // In each step, the numerator is multiplied by z^2
        num = (num * z_squared) / ONE_20;
        seriesSum += num / 3;

        num = (num * z_squared) / ONE_20;
        seriesSum += num / 5;

        num = (num * z_squared) / ONE_20;
        seriesSum += num / 7;

        num = (num * z_squared) / ONE_20;
        seriesSum += num / 9;

        num = (num * z_squared) / ONE_20;
        seriesSum += num / 11;

        // 6 Taylor terms are sufficient for 36 decimal precision.

        // Finally, we multiply by 2 (non fixed point) to compute ln(remainder)
        seriesSum *= 2;

        // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both
        // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal
        // value.

        return (sum + seriesSum) / 100;
    }

    /**
     * @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument,
     * for x close to one.
     *
     * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND.
     */
    function _ln_36(int256 x) private pure returns (int256) {
        // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits
        // worthwhile.

        // First, we transform x to a 36 digit fixed point value.
        x *= ONE_18;

        // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1).
        // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

        // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires
        // division by ONE_36.
        int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36);
        int256 z_squared = (z * z) / ONE_36;

        // num is the numerator of the series: the z^(2 * n + 1) term
        int256 num = z;

        // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
        int256 seriesSum = num;

        // In each step, the numerator is multiplied by z^2
        num = (num * z_squared) / ONE_36;
        seriesSum += num / 3;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 5;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 7;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 9;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 11;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 13;

        num = (num * z_squared) / ONE_36;
        seriesSum += num / 15;

        // 8 Taylor terms are sufficient for 36 decimal precision.

        // All that remains is multiplying by 2 (non fixed point).
        return seriesSum * 2;
    }
}
合同源代码
文件 18 的 20:Owned.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Simple single owner authorization mixin.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol)
abstract contract Owned {
    /*//////////////////////////////////////////////////////////////
                                 EVENTS
    //////////////////////////////////////////////////////////////*/

    event OwnershipTransferred(address indexed user, address indexed newOwner);

    /*//////////////////////////////////////////////////////////////
                            OWNERSHIP STORAGE
    //////////////////////////////////////////////////////////////*/

    address public owner;

    modifier onlyOwner() virtual {
        require(msg.sender == owner, "UNAUTHORIZED");

        _;
    }

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

    constructor(address _owner) {
        owner = _owner;

        emit OwnershipTransferred(address(0), _owner);
    }

    /*//////////////////////////////////////////////////////////////
                             OWNERSHIP LOGIC
    //////////////////////////////////////////////////////////////*/

    function transferOwnership(address newOwner) public virtual onlyOwner {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}
合同源代码
文件 19 的 20:SafeCastLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;

/// @notice Safe unsigned integer casting library that reverts on overflow.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeCastLib.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol)
library SafeCastLib {
    function safeCastTo128(uint256 x) internal pure returns (uint128 y) {
        require(x < 1 << 128);

        y = uint128(x);
    }
}
合同源代码
文件 20 的 20: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/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
/// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.
library SafeTransferLib {
    /*//////////////////////////////////////////////////////////////
                             ETH OPERATIONS
    //////////////////////////////////////////////////////////////*/

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

        /// @solidity memory-safe-assembly
        assembly {
            // Transfer the ETH and store if it succeeded or not.
            success := call(gas(), to, amount, 0, 0, 0, 0)
        }

        require(success, "ETH_TRANSFER_FAILED");
    }

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

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
            mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
            )
        }

        require(success, "TRANSFER_FROM_FAILED");
    }

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "TRANSFER_FAILED");
    }

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

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

            // Write the abi-encoded calldata into memory, beginning with the function selector.
            mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
            mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.

            success := and(
                // Set success to whether the call reverted, if not we check it either
                // returned exactly 1 (can't just be non-zero data), or had no return data.
                or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
                // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
                // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
                // Counterintuitively, this call must be positioned second to the or() call in the
                // surrounding and() call or else returndatasize() will be zero during the computation.
                call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
            )
        }

        require(success, "APPROVE_FAILED");
    }
}
设置
{
  "compilationTarget": {
    "src/LendingPool.sol": "LendingPool"
  },
  "evmVersion": "shanghai",
  "libraries": {},
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "remappings": [
    ":@openzeppelin/=lib/accounts-v2/lib/openzeppelin-contracts/",
    ":@uniswap/v3-core/contracts/=lib/accounts-v2/lib/v3-core/contracts/",
    ":accounts-v2/=lib/accounts-v2/",
    ":ds-test/=lib/forge-std/lib/ds-test/src/",
    ":forge-std/=lib/forge-std/src/",
    ":openzeppelin-contracts/=lib/accounts-v2/lib/openzeppelin-contracts/contracts/",
    ":solmate/=lib/solmate/src/",
    ":v3-core/=lib/accounts-v2/lib/v3-core/",
    ":v3-periphery/=lib/accounts-v2/lib/v3-periphery/contracts/"
  ]
}
ABI
[{"inputs":[{"internalType":"address","name":"riskManager_","type":"address"},{"internalType":"contract ERC20","name":"asset_","type":"address"},{"internalType":"address","name":"treasury_","type":"address"},{"internalType":"address","name":"accountFactory","type":"address"},{"internalType":"address","name":"liquidator","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AmountExceedsBalance","type":"error"},{"inputs":[],"name":"AuctionOngoing","type":"error"},{"inputs":[],"name":"CoolDownPeriodNotPassed","type":"error"},{"inputs":[],"name":"FunctionIsPaused","type":"error"},{"inputs":[],"name":"FunctionNotImplemented","type":"error"},{"inputs":[],"name":"InvalidVersion","type":"error"},{"inputs":[],"name":"IsNotAnAccount","type":"error"},{"inputs":[],"name":"IsNotAnAccountWithDebt","type":"error"},{"inputs":[],"name":"LiquidationWeightsTooHigh","type":"error"},{"inputs":[],"name":"NonExistingTranche","type":"error"},{"inputs":[],"name":"OnlyGuardian","type":"error"},{"inputs":[],"name":"OpenPositionNonZero","type":"error"},{"inputs":[],"name":"TrancheAlreadyExists","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"inputs":[],"name":"ZeroShares","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":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"creditor","type":"address"},{"indexed":false,"internalType":"uint256","name":"startDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"initiationReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"terminationReward","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"penalty","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"badDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"surplus","type":"uint256"}],"name":"AuctionFinished","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"creditor","type":"address"},{"indexed":false,"internalType":"uint128","name":"openDebt","type":"uint128"}],"name":"AuctionStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":true,"internalType":"bytes3","name":"referrer","type":"bytes3"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CreditApproval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newGuardian","type":"address"}],"name":"GuardianChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"}],"name":"InterestSynced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"tranche","type":"address"},{"indexed":true,"internalType":"uint8","name":"trancheIndex","type":"uint8"},{"indexed":false,"internalType":"uint16","name":"interestWeight","type":"uint16"}],"name":"InterestWeightTrancheUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint16","name":"liquidationWeight","type":"uint16"}],"name":"LiquidationWeightTrancheUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"repayPauseFlagsUpdated","type":"bool"},{"indexed":false,"internalType":"bool","name":"withdrawPauseFlagsUpdated","type":"bool"},{"indexed":false,"internalType":"bool","name":"borrowPauseFlagsUpdated","type":"bool"},{"indexed":false,"internalType":"bool","name":"depositPauseFlagsUpdated","type":"bool"},{"indexed":false,"internalType":"bool","name":"liquidationPauseFlagsUpdated","type":"bool"}],"name":"PauseFlagsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"totalDebt","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalLiquidity","type":"uint256"},{"indexed":false,"internalType":"uint80","name":"interestRate","type":"uint80"}],"name":"PoolStateUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Repay","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"riskManager","type":"address"}],"name":"RiskManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"tranche","type":"address"}],"name":"TranchePopped","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":"uint16","name":"interestWeight","type":"uint16"},{"indexed":false,"internalType":"uint16","name":"liquidationWeight","type":"uint16"}],"name":"TreasuryWeightsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"accountVersion","type":"uint256"},{"indexed":false,"internalType":"bool","name":"valid","type":"bool"}],"name":"ValidAccountVersionsUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Withdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"tranche","type":"address"},{"internalType":"uint16","name":"interestWeight_","type":"uint16"}],"name":"addTranche","outputs":[],"stateMutability":"nonpayable","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":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"beneficiary","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"approveBeneficiary","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startDebt","type":"uint256"},{"internalType":"uint256","name":"minimumMargin_","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"bidder","type":"address"}],"name":"auctionRepay","outputs":[{"internalType":"bool","name":"earlyTerminate","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"bytes3","name":"referrer","type":"bytes3"}],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"calcUnrealisedDebt","outputs":[{"internalType":"uint256","name":"unrealisedDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardian_","type":"address"}],"name":"changeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"closeMarginAccount","outputs":[],"stateMutability":"view","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":"assets","type":"uint256"}],"name":"convertToShares","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"creditAllowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"from","type":"address"}],"name":"depositInLendingPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"trancheIndex","type":"uint256"},{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"donateToTranche","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountBorrowed","type":"uint256"},{"internalType":"address","name":"account","type":"address"},{"internalType":"address","name":"actionTarget","type":"address"},{"internalType":"bytes","name":"actionData","type":"bytes"},{"internalType":"bytes3","name":"referrer","type":"bytes3"}],"name":"flashAction","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"callbackData","type":"bytes"}],"name":"flashActionCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getInterestRateConfig","outputs":[{"internalType":"uint72","name":"","type":"uint72"},{"internalType":"uint72","name":"","type":"uint72"},{"internalType":"uint72","name":"","type":"uint72"},{"internalType":"uint16","name":"","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getLiquidationParameters","outputs":[{"internalType":"uint16","name":"","type":"uint16"},{"internalType":"uint16","name":"","type":"uint16"},{"internalType":"uint16","name":"","type":"uint16"},{"internalType":"uint16","name":"","type":"uint16"},{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"getOpenPosition","outputs":[{"internalType":"uint256","name":"openPosition","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardian","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRate","outputs":[{"internalType":"uint80","name":"","type":"uint80"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"isValidVersion","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"liquidationPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"liquidityOf","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner_","type":"address"}],"name":"liquidityOfAndSync","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"maxWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","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":"uint256","name":"accountVersion","type":"uint256"}],"name":"openMarginAccount","outputs":[{"internalType":"bool","name":"success","type":"bool"},{"internalType":"address","name":"numeraire","type":"address"},{"internalType":"address","name":"liquidator_","type":"address"},{"internalType":"uint256","name":"minimumMargin_","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"originationFee","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseTimestamp","outputs":[{"internalType":"uint96","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"previewRedeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"previewWithdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"account","type":"address"}],"name":"repay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repayPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"riskManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"accountVersion","type":"uint256"},{"internalType":"bool","name":"valid","type":"bool"}],"name":"setAccountVersion","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint72","name":"baseRatePerYear_","type":"uint72"},{"internalType":"uint72","name":"lowSlopePerYear_","type":"uint72"},{"internalType":"uint72","name":"highSlopePerYear_","type":"uint72"},{"internalType":"uint16","name":"utilisationThreshold_","type":"uint16"}],"name":"setInterestParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint16","name":"interestWeight_","type":"uint16"}],"name":"setInterestWeightTranche","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"initiationWeight_","type":"uint16"},{"internalType":"uint16","name":"penaltyWeight_","type":"uint16"},{"internalType":"uint16","name":"terminationWeight_","type":"uint16"},{"internalType":"uint16","name":"minRewardWeight_","type":"uint16"},{"internalType":"uint80","name":"maxReward_","type":"uint80"}],"name":"setLiquidationParameters","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"liquidationWeight","type":"uint16"}],"name":"setLiquidationWeightTranche","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint96","name":"minimumMargin_","type":"uint96"}],"name":"setMinimumMargin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"originationFee_","type":"uint8"}],"name":"setOriginationFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"riskManager_","type":"address"}],"name":"setRiskManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"treasury_","type":"address"}],"name":"setTreasury","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint16","name":"interestWeight_","type":"uint16"},{"internalType":"uint16","name":"liquidationWeight","type":"uint16"}],"name":"setTreasuryWeights","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"startDebt","type":"uint256"},{"internalType":"uint256","name":"minimumMargin_","type":"uint256"},{"internalType":"address","name":"terminator","type":"address"}],"name":"settleLiquidationHappyFlow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"startDebt","type":"uint256"},{"internalType":"uint256","name":"minimumMargin_","type":"uint256"},{"internalType":"address","name":"terminator","type":"address"}],"name":"settleLiquidationUnhappyFlow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"skim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"initiator","type":"address"},{"internalType":"uint256","name":"minimumMargin_","type":"uint256"}],"name":"startLiquidation","outputs":[{"internalType":"uint256","name":"startDebt","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalLiquidity","outputs":[{"internalType":"uint256","name":"totalLiquidity_","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":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"repayPaused_","type":"bool"},{"internalType":"bool","name":"withdrawPaused_","type":"bool"},{"internalType":"bool","name":"borrowPaused_","type":"bool"},{"internalType":"bool","name":"depositPaused_","type":"bool"},{"internalType":"bool","name":"liquidationPaused_","type":"bool"}],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateInterestRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"withdrawFromLendingPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"withdrawPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"}]