Contract 0x62eAa8b180faebfBb0627dBd07E23f27379c147e 1

 

Txn Hash Method
Block
From
To
Value
Latest 25 internal transaction
Parent Txn Hash Block From To Value
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x4f673be970e6aebeade659fcbd0861b30e75009f0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0xc1c5852bcfe51837f23e8f052c5238901f55a2380x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0xdb4fa8456039f92cc61852958a339d01c014290e0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x4ac8ca80b1c5e8d38d0176b611ab6013ea5505860x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x4b8b5e04122103c34e60e70737a017b3107e1e230x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x6966d8c6e0fbe8695bffa98a6fba2f278ed17e600x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x9a84aa0a58c6e2cf01b9f411a430ab08b05996b00x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x25ae9b8c22a5d5b9a4f216be23c2d101244da69e0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x1c3d0020f1ecca8a931b219b490dfb41499ca1520x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0xd193873e6d9c6ba76e60f7e430aa74d02032e44d0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0xdfcb933a125be8963c97b64fdb5f83de69ee8e100x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x65e8d70ca547159f2bfdfc879ea297e600415cda0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x1dc7e9815f32ba367be4557ebde63feeba44e8f90x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0xc10bc6733de7628037e9d51600d05ca24f21d6fb0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x50c3f2ddef7d84e29b2e3cbb933b703def9c3d690x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x2a207c66016b4a16ac1a79ddfcad6ab81658fedb0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x57296e9ad47cd5dd4a2237cb4b9df23c7e6fd1200x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x3bebea3bcedfc62ac9e75f7163cd57a92906b8db0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x44a924b7669f96d0df5444051145564ec63db7f60x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x7f28a4afcba2d9588ed6209c1544cf933cd240ba0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0x878928ad2c58187258919e32c4a4cd51b208211e0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x936ff48c4aceed06751e207839f094de3ef00c04bb8a9e54272cd9d709948f7a1075403152023-07-30 1:10:0760 days 13 hrs ago 0xde5aab4ceb875ac8406c506bae07c715a0d004c20x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x7e71e150aa9e9a77b79c6a45ca4a1ac853ba250997a842a56f79f8c14da5c12b1073922062023-07-26 14:53:0963 days 23 hrs ago 0x44a924b7669f96d0df5444051145564ec63db7f60x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0xf7e2da73b75a05d4d50e1a21d9d1101e07103f832c4fca2309cb54088f675ecc1073921652023-07-26 14:51:4763 days 23 hrs ago 0xd193873e6d9c6ba76e60f7e430aa74d02032e44d0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
0x84aecde2e40e7895a1e6b2a72e3377e460d47457cd46b61da8ac577c22c318a61072379842023-07-23 1:12:2567 days 13 hrs ago 0x4f673be970e6aebeade659fcbd0861b30e75009f0x62eaa8b180faebfbb0627dbd07e23f27379c147e0 ETH
[ Download CSV Export 
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Lender

Compiler Version
v0.8.17+commit.8df45f5f

Optimization Enabled:
Yes with 1000000 runs

Other Settings:
default evmVersion
File 1 of 11 : ImmutableArgs.sol
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.15;

/// @title ImmutableArgs
/// @author zefram.eth, Saw-mon & Natalie
/// @notice Provides helper functions for reading immutable args from calldata
library ImmutableArgs {
    function addr() internal pure returns (address arg) {
        assembly {
            arg := shr(0x60, calldataload(sub(calldatasize(), 22)))
        }
    }

    /// @notice Reads an immutable arg with type address
    /// @param offset The offset of the arg in the packed data
    /// @return arg The arg value
    function addressAt(uint256 offset) internal pure returns (address arg) {
        uint256 start = _startOfImmutableArgs();
        assembly {
            arg := shr(0x60, calldataload(add(start, offset)))
        }
    }

    /// @notice Reads an immutable arg with type uint256
    /// @param offset The offset of the arg in the packed data
    /// @return arg The arg value
    function uint256At(uint256 offset) internal pure returns (uint256 arg) {
        uint256 start = _startOfImmutableArgs();
        assembly {
            arg := calldataload(add(start, offset))
        }
    }

    function all() internal pure returns (bytes memory args) {
        uint256 start = _startOfImmutableArgs();
        unchecked {
            args = msg.data[start:msg.data.length - 2];
        }
    }

    /// @return offset The offset of the packed immutable args in calldata
    function _startOfImmutableArgs() private pure returns (uint256 offset) {
        assembly {
            //                                      read final 2 bytes of calldata, i.e. `extraLength`
            offset := sub(calldatasize(), shr(0xf0, calldataload(sub(calldatasize(), 2))))
        }
    }
}

File 2 of 11 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator,
        Rounding rounding
    ) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10**64) {
                value /= 10**64;
                result += 64;
            }
            if (value >= 10**32) {
                value /= 10**32;
                result += 32;
            }
            if (value >= 10**16) {
                value /= 10**16;
                result += 16;
            }
            if (value >= 10**8) {
                value /= 10**8;
                result += 8;
            }
            if (value >= 10**4) {
                value /= 10**4;
                result += 4;
            }
            if (value >= 10**2) {
                value /= 10**2;
                result += 2;
            }
            if (value >= 10**1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

File 3 of 11 : 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);
    }
}

File 4 of 11 : 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.

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

    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
    }

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

    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
        return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
    }

    /*//////////////////////////////////////////////////////////////
                    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))
        }
    }

    function rpow(
        uint256 x,
        uint256 n,
        uint256 scalar
    ) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            switch x
            case 0 {
                switch n
                case 0 {
                    // 0 ** 0 = 1
                    z := scalar
                }
                default {
                    // 0 ** n = 0
                    z := 0
                }
            }
            default {
                switch mod(n, 2)
                case 0 {
                    // If n is even, store scalar in z for now.
                    z := scalar
                }
                default {
                    // If n is odd, store x in z for now.
                    z := x
                }

                // Shifting right by 1 is like dividing by 2.
                let half := shr(1, scalar)

                for {
                    // Shift n right by 1 before looping to halve it.
                    n := shr(1, n)
                } n {
                    // Shift n right by 1 each iteration to halve it.
                    n := shr(1, n)
                } {
                    // Revert immediately if x ** 2 would overflow.
                    // Equivalent to iszero(eq(div(xx, x), x)) here.
                    if shr(128, x) {
                        revert(0, 0)
                    }

                    // Store x squared.
                    let xx := mul(x, x)

                    // Round to the nearest number.
                    let xxRound := add(xx, half)

                    // Revert if xx + half overflowed.
                    if lt(xxRound, xx) {
                        revert(0, 0)
                    }

                    // Set x to scaled xxRound.
                    x := div(xxRound, scalar)

                    // If n is even:
                    if mod(n, 2) {
                        // Compute z * x.
                        let zx := mul(z, x)

                        // If z * x overflowed:
                        if iszero(eq(div(zx, x), z)) {
                            // Revert if x is non-zero.
                            if iszero(iszero(x)) {
                                revert(0, 0)
                            }
                        }

                        // Round to the nearest number.
                        let zxRound := add(zx, half)

                        // Revert if zx + half overflowed.
                        if lt(zxRound, zx) {
                            revert(0, 0)
                        }

                        // Return properly scaled zxRound.
                        z := div(zxRound, scalar)
                    }
                }
            }
        }
    }

    /*//////////////////////////////////////////////////////////////
                        GENERAL NUMBER UTILITIES
    //////////////////////////////////////////////////////////////*/

    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let y := x // We start y at x, which will help us make our initial estimate.

            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // We check y >= 2^(k + 8) but shift right by k bits
            // each branch to ensure that if x >= 256, then y >= 256.
            if iszero(lt(y, 0x10000000000000000000000000000000000)) {
                y := shr(128, y)
                z := shl(64, z)
            }
            if iszero(lt(y, 0x1000000000000000000)) {
                y := shr(64, y)
                z := shl(32, z)
            }
            if iszero(lt(y, 0x10000000000)) {
                y := shr(32, y)
                z := shl(16, z)
            }
            if iszero(lt(y, 0x1000000)) {
                y := shr(16, y)
                z := shl(8, z)
            }

            // Goal was to get z*z*y within a small factor of x. More iterations could
            // get y in a tighter range. Currently, we will have y in [256, 256*2^16).
            // We ensured y >= 256 so that the relative difference between y and y+1 is small.
            // That's not possible if x < 256 but we can just verify those cases exhaustively.

            // Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
            // Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
            // Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.

            // For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
            // (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.

            // Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
            // sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.

            // There is no overflow risk here since y < 2^136 after the first branch above.
            z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If x+1 is a perfect square, the Babylonian method cycles between
            // floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
            // If you don't care whether the floor or ceil square root is returned, you can remove this statement.
            z := sub(z, lt(div(x, z), z))
        }
    }

    function unsafeMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Mod x by y. Note this will return
            // 0 instead of reverting if y is zero.
            z := mod(x, y)
        }
    }

    function unsafeDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // Divide x by y. Note this will return
            // 0 instead of reverting if y is zero.
            r := div(x, y)
        }
    }

    function unsafeDivUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Add 1 to x * y if x % y > 0. Note this will
            // return 0 instead of reverting if y is zero.
            z := add(gt(mod(x, y), 0), div(x, y))
        }
    }
}

File 5 of 11 : 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), from) // Append the "from" argument.
            mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.

            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), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            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), to) // Append the "to" argument.
            mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.

            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");
    }
}

File 6 of 11 : Ledger.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

import {ImmutableArgs} from "clones-with-immutable-args/ImmutableArgs.sol";
import {Math} from "openzeppelin-contracts/utils/math/Math.sol";
import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";
import {ERC20} from "solmate/tokens/ERC20.sol";

import {BORROWS_SCALER, ONE} from "./libraries/constants/Constants.sol";
import {Q112} from "./libraries/constants/Q.sol";

import {RateModel} from "./RateModel.sol";

contract Ledger {
    using FixedPointMathLib for uint256;

    address public immutable FACTORY;

    address public immutable RESERVE;

    struct Cache {
        uint256 totalSupply;
        uint256 lastBalance;
        uint256 lastAccrualTime;
        uint256 borrowBase;
        uint256 borrowIndex;
    }

    /*//////////////////////////////////////////////////////////////
                             LENDER STORAGE
    //////////////////////////////////////////////////////////////*/

    uint112 public totalSupply;

    uint112 public lastBalance;

    uint32 public lastAccrualTime;

    uint184 public borrowBase;

    uint72 public borrowIndex;

    mapping(address => uint256) public borrows;

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

    /// @dev Highest 32 bits are the referral code, next 112 are the principle, lowest 112 are the shares.
    mapping(address => uint256) public balances;

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

    /*//////////////////////////////////////////////////////////////
                            ERC2612 STORAGE
    //////////////////////////////////////////////////////////////*/

    bytes32 internal initialDomainSeparator;

    uint256 internal initialChainId;

    mapping(address => uint256) public nonces;

    /*//////////////////////////////////////////////////////////////
                           INCENTIVE STORAGE
    //////////////////////////////////////////////////////////////*/

    struct Courier {
        address wallet;
        uint16 cut;
    }

    mapping(uint32 => Courier) public couriers;

    /*//////////////////////////////////////////////////////////////
                         GOVERNABLE PARAMETERS
    //////////////////////////////////////////////////////////////*/

    RateModel public rateModel;

    uint8 public reserveFactor;

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

    constructor(address reserve) {
        FACTORY = msg.sender;
        RESERVE = reserve;
    }

    /// @notice The name of the banknote.
    function name() external view returns (string memory) {
        return string.concat("Aloe II ", asset().name());
    }

    /// @notice The symbol of the banknote.
    function symbol() external view returns (string memory) {
        return string.concat(asset().symbol(), "+");
    }

    /// @notice The number of decimals the banknote uses. Matches the underlying token.
    function decimals() external view returns (uint8) {
        return asset().decimals();
    }

    /// @notice The address of the underlying token.
    function asset() public pure returns (ERC20) {
        return ERC20(ImmutableArgs.addr());
    }

    function DOMAIN_SEPARATOR() public view returns (bytes32) {
        return block.chainid == initialChainId ? initialDomainSeparator : _computeDomainSeparator();
    }

    /**
     * @notice Gets basic lending statistics as if `accrueInterest` were just called.
     * @return The updated `borrowIndex`
     * @return The sum of all banknote balances, in underlying units
     * @return The sum of all outstanding debts, in underlying units
     * @return The sum of all banknote balances. Will differ from `totalSupply()` due to reserves inflation
     */
    function stats() external view returns (uint72, uint256, uint256, uint256) {
        (Cache memory cache, uint256 inventory, uint256 newTotalSupply) = _previewInterest(_getCache());

        unchecked {
            return (
                uint72(cache.borrowIndex),
                inventory,
                (cache.borrowBase * cache.borrowIndex) / BORROWS_SCALER,
                newTotalSupply
            );
        }
    }

    function courierOf(address account) external view returns (uint32) {
        return uint32(balances[account] >> 224);
    }

    function principleOf(address account) external view returns (uint256) {
        return (balances[account] >> 112) % Q112;
    }

    /// @notice The number of shares held by `account`
    function balanceOf(address account) external view returns (uint256) {
        return balances[account] % Q112;
    }

    function underlyingBalance(address account) external view returns (uint256) {
        (, uint256 inventory, uint256 newTotalSupply) = _previewInterest(_getCache());
        return _nominalAssets(account, inventory, newTotalSupply);
    }

    function underlyingBalanceStored(address account) external view returns (uint256) {
        unchecked {
            return
                _nominalAssets({
                    account: account,
                    inventory: lastBalance + (uint256(borrowBase) * borrowIndex) / BORROWS_SCALER,
                    totalSupply_: totalSupply
                });
        }
    }

    function borrowBalance(address account) external view returns (uint256) {
        uint256 b = borrows[account];
        if (b == 0) return 0;

        (Cache memory cache, , ) = _previewInterest(_getCache());
        unchecked {
            return ((b - 1) * cache.borrowIndex) / BORROWS_SCALER;
        }
    }

    function borrowBalanceStored(address account) external view returns (uint256) {
        uint256 b = borrows[account];
        if (b == 0) return 0;

        unchecked {
            return ((b - 1) * borrowIndex) / BORROWS_SCALER;
        }
    }

    /*//////////////////////////////////////////////////////////////
                           ERC4626 ACCOUNTING
    //////////////////////////////////////////////////////////////*/

    function totalAssets() external view returns (uint256) {
        (, uint256 inventory, ) = _previewInterest(_getCache());
        return inventory;
    }

    function convertToShares(uint256 assets) public view returns (uint256) {
        (, uint256 inventory, uint256 newTotalSupply) = _previewInterest(_getCache());
        return _convertToShares(assets, inventory, newTotalSupply, /* roundUp: */ false);
    }

    function convertToAssets(uint256 shares) public view returns (uint256) {
        (, uint256 inventory, uint256 newTotalSupply) = _previewInterest(_getCache());
        return _convertToAssets(shares, inventory, newTotalSupply, /* roundUp: */ false);
    }

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

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

    function previewMint(uint256 shares) public view returns (uint256) {
        (, uint256 inventory, uint256 newTotalSupply) = _previewInterest(_getCache());
        return _convertToAssets(shares, inventory, newTotalSupply, /* roundUp: */ true);
    }

    function previewWithdraw(uint256 assets) public view returns (uint256) {
        (, uint256 inventory, uint256 newTotalSupply) = _previewInterest(_getCache());
        return _convertToShares(assets, inventory, newTotalSupply, /* roundUp: */ true);
    }

    /*//////////////////////////////////////////////////////////////
                    ERC4626 DEPOSIT/WITHDRAWAL LIMITS
    //////////////////////////////////////////////////////////////*/

    /**
     * @notice Returns a conservative estimate of the maximum amount of `asset()` that can be deposited into the
     * Vault for `receiver`, through a deposit call.
     * @return The maximum amount of `asset()` that can be deposited
     *
     * @dev Should return the *precise* maximum. In this case that'd be on the order of 2**112 with weird constraints
     * coming from both `lastBalance` and `totalSupply`, which changes during interest accrual. Instead of doing
     * complicated math, we provide a constant conservative estimate of 2**96.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address) external pure returns (uint256) {
        return 1 << 96;
    }

    /**
     * @notice Returns a conservative estimate of the maximum number of Vault shares that can be minted for `receiver`,
     * through a mint call.
     * @return The maximum number of Vault shares that can be minted
     *
     * @dev Should return the *precise* maximum. In this case that'd be on the order of 2**112 with weird constraints
     * coming from both `lastBalance` and `totalSupply`, which changes during interest accrual. Instead of doing
     * complicated math, we provide a constant conservative estimate of 2**96.
     *
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum number of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address) external pure returns (uint256) {
        return 1 << 96;
    }

    /**
     * @notice Returns the maximum amount of `asset()` that can be withdrawn from the Vault by `owner`, through a
     * withdraw call.
     * @param owner The address that would burn Vault shares when withdrawing
     * @return The maximum amount of `asset()` that can be withdrawn
     *
     * @dev
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256) {
        (Cache memory cache, uint256 inventory, uint256 newTotalSupply) = _previewInterest(_getCache());

        uint256 a = _nominalAssets(owner, inventory, newTotalSupply);
        uint256 b = cache.lastBalance;

        return a < b ? a : b;
    }

    /**
     * @notice Returns the maximum number of Vault shares that can be redeemed in the Vault by `owner`, through a
     * redeem call.
     * @param owner The address that would burn Vault shares when redeeming
     * @return The maximum number of Vault shares that can be redeemed
     *
     * @dev
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256) {
        (Cache memory cache, uint256 inventory, uint256 newTotalSupply) = _previewInterest(_getCache());

        uint256 a = _nominalShares(owner, inventory, newTotalSupply);
        uint256 b = _convertToShares(cache.lastBalance, inventory, newTotalSupply, false);

        return a < b ? a : b;
    }

    /*//////////////////////////////////////////////////////////////
                                 HELPERS
    //////////////////////////////////////////////////////////////*/

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

    function _previewInterest(Cache memory cache) internal view returns (Cache memory, uint256, uint256) {
        unchecked {
            uint256 oldBorrows = (cache.borrowBase * cache.borrowIndex) / BORROWS_SCALER;
            uint256 oldInventory = cache.lastBalance + oldBorrows;

            if (cache.lastAccrualTime == block.timestamp || oldBorrows == 0) {
                return (cache, oldInventory, cache.totalSupply);
            }

            uint8 rf = reserveFactor;
            uint256 accrualFactor = rateModel.getAccrualFactor({
                elapsedTime: block.timestamp - cache.lastAccrualTime,
                utilization: Math.mulDiv(1e18, oldBorrows, oldInventory)
            });

            cache.borrowIndex = (cache.borrowIndex * accrualFactor) / ONE;
            cache.lastAccrualTime = 0; // 0 in storage means locked to reentrancy; 0 in `cache` means `borrowIndex` was updated

            uint256 newInventory = cache.lastBalance + (cache.borrowBase * cache.borrowIndex) / BORROWS_SCALER;
            uint256 newTotalSupply = Math.mulDiv(
                cache.totalSupply,
                newInventory,
                newInventory - (newInventory - oldInventory) / rf
            );
            return (cache, newInventory, newTotalSupply);
        }
    }

    function _convertToShares(
        uint256 assets,
        uint256 inventory,
        uint256 totalSupply_,
        bool roundUp
    ) internal pure returns (uint256) {
        if (totalSupply_ == 0) return assets;
        return roundUp ? assets.mulDivUp(totalSupply_, inventory) : assets.mulDivDown(totalSupply_, inventory);
    }

    function _convertToAssets(
        uint256 shares,
        uint256 inventory,
        uint256 totalSupply_,
        bool roundUp
    ) internal pure returns (uint256) {
        if (totalSupply_ == 0) return shares;
        return roundUp ? shares.mulDivUp(inventory, totalSupply_) : shares.mulDivDown(inventory, totalSupply_);
    }

    function _nominalShares(
        address account,
        uint256 inventory,
        uint256 totalSupply_
    ) private view returns (uint256 shares) {
        unchecked {
            uint256 data = balances[account];
            shares = data % Q112;

            uint32 id = uint32(data >> 224);
            if (id != 0) {
                uint256 principle = _convertToShares((data >> 112) % Q112, inventory, totalSupply_, true);

                if (shares > principle) {
                    shares -= ((shares - principle) * couriers[id].cut) / 10_000;
                }
            }
        }
    }

    function _nominalAssets(
        address account,
        uint256 inventory,
        uint256 totalSupply_
    ) private view returns (uint256 assets) {
        unchecked {
            uint256 data = balances[account];
            assets = _convertToAssets(data % Q112, inventory, totalSupply_, false);

            uint32 id = uint32(data >> 224);
            if (id != 0) {
                uint256 principle = (data >> 112) % Q112;

                if (assets > principle) {
                    assets -= ((assets - principle) * couriers[id].cut) / 10_000;
                }
            }
        }
    }

    function _getCache() private view returns (Cache memory) {
        return Cache(totalSupply, lastBalance, lastAccrualTime, borrowBase, borrowIndex);
    }
}

File 7 of 11 : Lender.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

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

import {BORROWS_SCALER, ONE, MIN_RESERVE_FACTOR, MAX_RESERVE_FACTOR} from "./libraries/constants/Constants.sol";
import {Q112} from "./libraries/constants/Q.sol";
import {SafeCastLib} from "./libraries/SafeCastLib.sol";

import {Ledger} from "./Ledger.sol";
import {RateModel} from "./RateModel.sol";

interface IFlashBorrower {
    function onFlashLoan(address initiator, uint256 amount, bytes calldata data) external;
}

/// @title Lender
/// @author Aloe Labs, Inc.
/// @dev "Test everything; hold fast what is good." - 1 Thessalonians 5:21
contract Lender is Ledger {
    using FixedPointMathLib for uint256;
    using SafeCastLib for uint256;
    using SafeTransferLib for ERC20;

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

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

    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
    );

    event Borrow(address indexed caller, address indexed recipient, uint256 amount, uint256 units);

    event Repay(address indexed caller, address indexed beneficiary, uint256 amount, uint256 units);

    event EnrollCourier(uint32 indexed id, address indexed wallet, uint16 cut);

    event CreditCourier(uint32 indexed id, address indexed account);

    /*//////////////////////////////////////////////////////////////
                       CONSTRUCTOR & INITIALIZER
    //////////////////////////////////////////////////////////////*/

    constructor(address reserve) Ledger(reserve) {}

    function initialize(RateModel rateModel_, uint8 reserveFactor_) external {
        require(borrowIndex == 0);
        borrowIndex = uint72(ONE);
        lastAccrualTime = uint32(block.timestamp);

        initialDomainSeparator = _computeDomainSeparator();
        initialChainId = block.chainid;

        rateModel = rateModel_;
        require(MIN_RESERVE_FACTOR <= reserveFactor_ && reserveFactor_ <= MAX_RESERVE_FACTOR);
        reserveFactor = reserveFactor_;
    }

    function whitelist(address borrower) external {
        // Requirements:
        // - `msg.sender == FACTORY` so that only the factory can whitelist borrowers
        // - `borrows[borrower] == 0` ensures we don't accidentally erase debt
        require(msg.sender == FACTORY && borrows[borrower] == 0);

        // `borrow` and `repay` have to read the `borrows` mapping anyway, so setting this to 1
        // allows them to efficiently check whether a given borrower is whitelisted. This extra
        // unit of debt won't accrue interest or impact solvency calculations.
        borrows[borrower] = 1;
    }

    function enrollCourier(uint32 id, address wallet, uint16 cut) external {
        // Requirements:
        // - `id != 0` because 0 is reserved as the no-courier case
        // - `cut != 0 && cut < 10_000` just means between 0 and 100%
        require(id != 0 && cut != 0 && cut < 10_000);
        // Once an `id` has been enrolled, its info can't be changed
        require(couriers[id].cut == 0);

        couriers[id] = Courier(wallet, cut);

        emit EnrollCourier(id, wallet, cut);
    }

    function creditCourier(uint32 id, address account) external {
        // Callers are free to set their own courier, but they need permission to mess with others'
        require(msg.sender == account || allowance[account][msg.sender] != 0);

        // Prevent `RESERVE` from having a courier, since its principle wouldn't be tracked properly
        require(account != RESERVE);

        // Payout logic can't handle self-reference, so don't let accounts credit themselves
        Courier memory courier = couriers[id];
        require(courier.cut != 0 && courier.wallet != account);

        // Only set courier if account balance is 0. Otherwise a previous courier may
        // be cheated out of their fees.
        require(balances[account] % Q112 == 0);
        balances[account] = uint256(id) << 224;

        emit CreditCourier(id, account);
    }

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

    function deposit(uint256 amount, address beneficiary) external returns (uint256 shares) {
        // Guard against reentrancy, accrue interest, and update reserves
        (Cache memory cache, uint256 inventory) = _load();

        shares = _convertToShares(amount, inventory, cache.totalSupply, /* roundUp: */ false);
        require(shares != 0, "Aloe: zero impact");

        // Ensure tokens were transferred
        cache.lastBalance += amount;
        require(cache.lastBalance <= asset().balanceOf(address(this)), "Aloe: insufficient pre-pay");

        // Mint shares and (if applicable) handle courier accounting
        _unsafeMint(beneficiary, shares, amount);
        cache.totalSupply += shares;

        // Save state to storage (thus far, only mappings have been updated, so we must address everything else)
        _save(cache, /* didChangeBorrowBase: */ false);

        emit Deposit(msg.sender, beneficiary, amount, shares);
    }

    function redeem(uint256 shares, address recipient, address owner) external returns (uint256 amount) {
        // Guard against reentrancy, accrue interest, and update reserves
        (Cache memory cache, uint256 inventory) = _load();

        amount = _convertToAssets(shares, inventory, cache.totalSupply, /* roundUp: */ false);
        require(amount != 0, "Aloe: zero impact");

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

        // Burn shares and (if applicable) handle courier accounting
        _unsafeBurn(owner, shares, inventory, cache.totalSupply);
        unchecked {
            cache.totalSupply -= shares;
        }

        // Transfer tokens
        cache.lastBalance -= amount;
        asset().safeTransfer(recipient, amount);

        // Save state to storage (thus far, only mappings have been updated, so we must address everything else)
        _save(cache, /* didChangeBorrowBase: */ false);

        emit Withdraw(msg.sender, recipient, owner, amount, shares);
    }

    /*//////////////////////////////////////////////////////////////
                           BORROW/REPAY LOGIC
    //////////////////////////////////////////////////////////////*/

    function borrow(uint256 amount, address recipient) external returns (uint256 units) {
        uint256 b = borrows[msg.sender];
        require(b != 0, "Aloe: not a borrower");

        // Guard against reentrancy, accrue interest, and update reserves
        (Cache memory cache, ) = _load();

        units = amount.mulDivUp(BORROWS_SCALER, cache.borrowIndex);
        cache.borrowBase += units;
        borrows[msg.sender] = b + units;

        // Transfer tokens
        cache.lastBalance -= amount;
        asset().safeTransfer(recipient, amount);

        // Save state to storage (thus far, only mappings have been updated, so we must address everything else)
        _save(cache, /* didChangeBorrowBase: */ true);

        emit Borrow(msg.sender, recipient, amount, units);
    }

    function repay(uint256 amount, address beneficiary) external returns (uint256 units) {
        uint256 b = borrows[beneficiary];

        // Guard against reentrancy, accrue interest, and update reserves
        (Cache memory cache, ) = _load();

        unchecked {
            units = (amount * BORROWS_SCALER) / cache.borrowIndex;
            require(units < b, "Aloe: repay too much");

            borrows[beneficiary] = b - units;
            cache.borrowBase -= units;
        }

        // Ensure tokens were transferred
        cache.lastBalance += amount;
        require(cache.lastBalance <= asset().balanceOf(address(this)), "Aloe: insufficient pre-pay");

        // Save state to storage (thus far, only mappings have been updated, so we must address everything else)
        _save(cache, /* didChangeBorrowBase: */ true);

        emit Repay(msg.sender, beneficiary, amount, units);
    }

    function accrueInterest() external returns (uint72) {
        (Cache memory cache, ) = _load();
        _save(cache, /* didChangeBorrowBase: */ false);
        return uint72(cache.borrowIndex);
    }

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

    function approve(address spender, uint256 shares) external returns (bool) {
        allowance[msg.sender][spender] = shares;

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

        return true;
    }

    function transfer(address to, uint256 shares) external returns (bool) {
        _transfer(msg.sender, to, shares);

        return true;
    }

    function transferFrom(address from, address to, uint256 shares) external returns (bool) {
        uint256 allowed = allowance[from][msg.sender];
        if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - shares;

        _transfer(from, to, shares);

        return true;
    }

    /*//////////////////////////////////////////////////////////////
                             ERC2612 LOGIC
    //////////////////////////////////////////////////////////////*/

    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external {
        require(deadline >= block.timestamp, "Aloe: permit 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, "Aloe: permit invalid");

            allowance[recoveredAddress][spender] = value;
        }

        emit Approval(owner, spender, value);
    }

    /*//////////////////////////////////////////////////////////////
                                HELPERS
    //////////////////////////////////////////////////////////////*/

    function _transfer(address from, address to, uint256 shares) private {
        unchecked {
            // From most to least significant...
            // -------------------------------
            // | courier id       | 32 bits  |
            // | user's principle | 112 bits |
            // | user's balance   | 112 bits |
            // -------------------------------
            uint256 data;

            data = balances[from];
            require(data >> 224 == 0 && shares <= data % Q112);
            balances[from] = data - shares;

            data = balances[to];
            require(data >> 224 == 0);
            balances[to] = data + shares;
        }

        emit Transfer(from, to, shares);
    }

    /// @dev You must do `totalSupply += shares` separately. Do so in a checked context.
    function _unsafeMint(address to, uint256 shares, uint256 amount) private {
        unchecked {
            // From most to least significant...
            // -------------------------------
            // | courier id       | 32 bits  |
            // | user's principle | 112 bits |
            // | user's balance   | 112 bits |
            // -------------------------------
            uint256 data = balances[to];

            if (data >> 224 != 0) {
                // Keep track of principle iff courier deserves credit
                require(amount + ((data >> 112) % Q112) < Q112);
                data += amount << 112;
            }

            // Keep track of balance regardless of courier.
            // Since `totalSupply` fits in uint112, the user's balance will too. No need to check here.
            balances[to] = data + shares;
        }

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

    /// @dev You must do `totalSupply -= shares` separately. Do so in an unchecked context.
    function _unsafeBurn(address from, uint256 shares, uint256 inventory, uint256 totalSupply_) private {
        unchecked {
            // From most to least significant...
            // -------------------------------
            // | courier id       | 32 bits  |
            // | user's principle | 112 bits |
            // | user's balance   | 112 bits |
            // -------------------------------
            uint256 data = balances[from];
            uint256 balance = data % Q112;

            uint32 id = uint32(data >> 224);
            if (id != 0) {
                uint256 principleAssets = (data >> 112) % Q112;
                uint256 principleShares = principleAssets.mulDivUp(totalSupply_, inventory);

                if (balance > principleShares) {
                    Courier memory courier = couriers[id];

                    // Compute total fee owed to courier. Take it out of balance so that
                    // comparison is correct (`shares <= balance`)
                    uint256 fee = ((balance - principleShares) * courier.cut) / 10_000;
                    balance -= fee;

                    // Compute portion of fee to pay out during this burn.
                    fee = (fee * shares) / balance;

                    // Send `fee` from `from` to `courier.wallet`. NOTE: We skip principle
                    // update on courier, so if couriers credit each other, 100% of `fee`
                    // is treated as profit.
                    data -= fee;
                    balances[courier.wallet] += fee;
                    emit Transfer(from, courier.wallet, fee);
                }

                // Update principle
                data -= ((principleAssets * shares) / balance) << 112;
            }

            require(shares <= balance);
            balances[from] = data - shares;
        }

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

    function _load() private returns (Cache memory cache, uint256 inventory) {
        cache = Cache(totalSupply, lastBalance, lastAccrualTime, borrowBase, borrowIndex);
        // Guard against reentrancy
        require(cache.lastAccrualTime != 0, "Aloe: locked");
        lastAccrualTime = 0;

        // Accrue interest (only in memory)
        uint256 newTotalSupply;
        (cache, inventory, newTotalSupply) = _previewInterest(cache);

        // Update reserves (new `totalSupply` is only in memory, but `balanceOf` is updated in storage)
        if (newTotalSupply > cache.totalSupply) {
            _unsafeMint(RESERVE, newTotalSupply - cache.totalSupply, 0);
            cache.totalSupply = newTotalSupply;
        }
    }

    function _save(Cache memory cache, bool didChangeBorrowBase) private {
        if (cache.lastAccrualTime == 0) {
            // `cache.lastAccrualTime == 0` implies that `cache.borrowIndex` was updated.
            // `cache.borrowBase` MAY also have been updated, so we store both components of the slot.
            borrowBase = cache.borrowBase.safeCastTo184();
            borrowIndex = cache.borrowIndex.safeCastTo72();
            // Now that we've read the flag, we can update `cache.lastAccrualTime` to the real, appropriate value
            cache.lastAccrualTime = block.timestamp;
        } else if (didChangeBorrowBase) {
            // Here, `cache.lastAccrualTime` is a real timestamp (could be `block.timestamp` or older). We can infer
            // that `cache.borrowIndex` was *not* updated. So we only have to store `cache.borrowBase`.
            borrowBase = cache.borrowBase.safeCastTo184();
        }

        totalSupply = cache.totalSupply.safeCastTo112();
        lastBalance = cache.lastBalance.safeCastTo112();
        lastAccrualTime = cache.lastAccrualTime.safeCastTo32(); // Disables reentrancy guard
    }
}

File 8 of 11 : RateModel.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol";

/// @title RateModel
/// @author Aloe Labs, Inc.
/// @dev "Test everything; hold fast what is good." - 1 Thessalonians 5:21
contract RateModel {
    uint256 private constant A = 6.1010463348e20;

    uint256 private constant B = 1e12 - A / 1e18;

    function getAccrualFactor(uint256 elapsedTime, uint256 utilization) external pure returns (uint256) {
        unchecked {
            uint256 rate = computeYieldPerSecond(utilization);

            if (elapsedTime > 1 weeks) elapsedTime = 1 weeks;

            return FixedPointMathLib.rpow(rate, elapsedTime, 1e12);
        }
    }

    function computeYieldPerSecond(uint256 utilization) public pure returns (uint256) {
        unchecked {
            return (utilization < 0.99e18) ? B + A / (1e18 - utilization) : 1000000060400;
        }
    }
}

File 9 of 11 : SafeCastLib.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

/// @title SafeCastLib
/// @notice Safe unsigned integer casting library that reverts on overflow.
/// @author Aloe Labs, Inc.
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeCastLib.sol)
library SafeCastLib {
    function safeCastTo248(uint256 x) internal pure returns (uint248 y) {
        require(x < 1 << 248);

        y = uint248(x);
    }

    function safeCastTo224(uint256 x) internal pure returns (uint224 y) {
        require(x < 1 << 224);

        y = uint224(x);
    }

    function safeCastTo192(uint256 x) internal pure returns (uint192 y) {
        require(x < 1 << 192);

        y = uint192(x);
    }

    function safeCastTo184(uint256 x) internal pure returns (uint184 y) {
        require(x < 1 << 184);

        y = uint184(x);
    }

    function safeCastTo160(uint256 x) internal pure returns (uint160 y) {
        require(x < 1 << 160);

        y = uint160(x);
    }

    function safeCastTo128(uint256 x) internal pure returns (uint128 y) {
        require(x < 1 << 128);

        y = uint128(x);
    }

    function safeCastTo112(uint256 x) internal pure returns (uint112 y) {
        require(x < 1 << 112);

        y = uint112(x);
    }

    function safeCastTo96(uint256 x) internal pure returns (uint96 y) {
        require(x < 1 << 96);

        y = uint96(x);
    }

    function safeCastTo72(uint256 x) internal pure returns (uint72 y) {
        require(x < 1 << 72);

        y = uint72(x);
    }

    function safeCastTo64(uint256 x) internal pure returns (uint64 y) {
        require(x < 1 << 64);

        y = uint64(x);
    }

    function safeCastTo32(uint256 x) internal pure returns (uint32 y) {
        require(x < 1 << 32);

        y = uint32(x);
    }

    function safeCastTo24(uint256 x) internal pure returns (uint24 y) {
        require(x < 1 << 24);

        y = uint24(x);
    }

    function safeCastTo16(uint256 x) internal pure returns (uint16 y) {
        require(x < 1 << 16);

        y = uint16(x);
    }

    function safeCastTo8(uint256 x) internal pure returns (uint8 y) {
        require(x < 1 << 8);

        y = uint8(x);
    }
}

File 10 of 11 : Constants.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

uint256 constant ONE = 1e12;

uint256 constant BORROWS_SCALER = type(uint72).max * ONE; // uint72 is from the type of borrowIndex in `Ledger`

uint256 constant MIN_SIGMA = 0.01e18;

// To avoid underflow in `BalanceSheet.computeProbePrices`, ensure that `MAX_SIGMA * Borrower.B <= 1e18`
uint256 constant MAX_SIGMA = 0.18e18;

uint256 constant MIN_RESERVE_FACTOR = 4; // Expressed as reciprocal, e.g. 4 --> 25%

uint256 constant MAX_RESERVE_FACTOR = 20; // Expressed as reciprocal, e.g. 20 --> 5%

// 1 + 1 / MAX_LEVERAGE should correspond to the maximum feasible single-block accrualFactor so that liquidators have time to respond to interest updates
uint256 constant MAX_LEVERAGE = 200;

uint256 constant LIQUIDATION_INCENTIVE = 20; // Expressed as reciprocal, e.g. 20 --> 5%

uint256 constant LIQUIDATION_GRACE_PERIOD = 2 minutes;

uint256 constant IV_SCALE = 24 hours;

uint256 constant IV_CHANGE_PER_SECOND = 5e12;

uint256 constant FEE_GROWTH_GLOBALS_SAMPLE_PERIOD = 1 minutes;

uint32 constant ORACLE_LOOKBACK = 20 minutes;

File 11 of 11 : Q.sol
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.17;

uint256 constant Q8 = 1 << 8;

uint256 constant Q16 = 1 << 16;

uint256 constant Q24 = 1 << 24;

uint256 constant Q32 = 1 << 32;

uint256 constant Q40 = 1 << 40;

uint256 constant Q48 = 1 << 48;

uint256 constant Q56 = 1 << 56;

uint256 constant Q64 = 1 << 64;

uint256 constant Q72 = 1 << 72;

uint256 constant Q80 = 1 << 80;

uint256 constant Q88 = 1 << 88;

uint256 constant Q96 = 1 << 96;

uint256 constant Q104 = 1 << 104;

uint256 constant Q112 = 1 << 112;

uint256 constant Q120 = 1 << 120;

uint256 constant Q128 = 1 << 128;

Settings
{
  "remappings": [
    "clones-with-immutable-args/=lib/clones-with-immutable-args/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
    "solmate/=lib/solmate/src/",
    "v3-core/=lib/v3-core/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 1000000
  },
  "metadata": {
    "bytecodeHash": "ipfs"
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "london",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"address","name":"reserve","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"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":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"id","type":"uint32"},{"indexed":true,"internalType":"address","name":"account","type":"address"}],"name":"CreditCourier","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":"uint32","name":"id","type":"uint32"},{"indexed":true,"internalType":"address","name":"wallet","type":"address"},{"indexed":false,"internalType":"uint16","name":"cut","type":"uint16"}],"name":"EnrollCourier","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"caller","type":"address"},{"indexed":true,"internalType":"address","name":"beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"}],"name":"Repay","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":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":[],"name":"FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"RESERVE","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[{"internalType":"uint72","name":"","type":"uint72"}],"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":"shares","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"asset","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"balances","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"units","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"borrowBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowBase","outputs":[{"internalType":"uint184","name":"","type":"uint184"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint72","name":"","type":"uint72"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"borrows","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"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":"account","type":"address"}],"name":"courierOf","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"couriers","outputs":[{"internalType":"address","name":"wallet","type":"address"},{"internalType":"uint16","name":"cut","type":"uint16"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"account","type":"address"}],"name":"creditCourier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"internalType":"address","name":"wallet","type":"address"},{"internalType":"uint16","name":"cut","type":"uint16"}],"name":"enrollCourier","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract RateModel","name":"rateModel_","type":"address"},{"internalType":"uint8","name":"reserveFactor_","type":"uint8"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lastAccrualTime","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastBalance","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"maxMint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","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":[],"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":"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":"address","name":"account","type":"address"}],"name":"principleOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rateModel","outputs":[{"internalType":"contract RateModel","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"beneficiary","type":"address"}],"name":"repay","outputs":[{"internalType":"uint256","name":"units","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveFactor","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stats","outputs":[{"internalType":"uint72","name":"","type":"uint72"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint112","name":"","type":"uint112"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"shares","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"underlyingBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"underlyingBalanceStored","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"}],"name":"whitelist","outputs":[],"stateMutability":"nonpayable","type":"function"}]

60c03461008e57601f6200331738819003918201601f19168301916001600160401b038311848410176100935780849260209460405283398101031261008e57516001600160a01b038116810361008e573360805260a05260405161326d9081620000aa82396080518181816111be0152611fdb015260a0518181816102c0015281816111480152612f510152f35b600080fd5b634e487b7160e01b600052604160045260246000fdfe60406080815260048036101561001457600080fd5b60009160e0908335821c806301e1d1141461236c57806306fdde031461226657806307a2d13a14611b75578063095ea7b3146121ca5780630a28a4771461217c57806318160ddd1461213157806323b872dd1461206157806327e235e314611fff5780632dd3100014611f90578063313ce56714611ea45780633644e51514611e6957806338d52e0f14611e0a5780633e64257514611db7578063402d267d14610a7d5780634322b71414611d745780634b3fd14814611bc35780634cdad50614611b755780634d73e9ba14611b3157806354a5706f14611acf5780635f8e2c9a14611a5b5780636e553f651461185557806370a08231146117e35780637ecebe00146117815780637f3fc7a51461170c5780638e36f12d146115c35780638f1c56bd1461157557806391f9b9ef146114e1578063943b24b21461137b57806395d89b411461125557806395dd9193146112115780639b19251a1461116c5780639d2cc436146110fd578063a1088459146110aa578063a6afed951461104f578063a9059cbb14611000578063aa5af0fd14610fc0578063acb7081514610db6578063b3d7f6b914610d68578063ba08765214610a8a578063c63d75b614610a7d578063c6e6f592146103cb578063caf0d5b114610a18578063ce96cb77146109b0578063d505accf146106b6578063d7e7270814610678578063d80528ae146105f9578063d905777e146104fb578063dd62ed3e14610482578063dd9eb1ef14610428578063ef8b30f7146103cb5763fb12cbba14610254575b600080fd5b346103c757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103c75761028a6124b6565b9173ffffffffffffffffffffffffffffffffffffffff92836102aa612443565b16948533149081156103aa575b501561039b57837f000000000000000000000000000000000000000000000000000000000000000016851461039b5763ffffffff811693848752600860205285602085892061ffff87519161030b836124c9565b549485169485835260a01c1691829101521515918261039f575b50501561039b5784865260036020526dffffffffffffffffffffffffffff838720541661039b577fffffffff00000000000000000000000000000000000000000000000000000000911b16908420557f208d401500198504074f2394690b20925b5aeaf9e414d19999c1fd2b3dddd5768380a380f35b8580fd5b141590508538610325565b9050858752602052828620338752602052828620541515386102b7565b8380fd5b838286346104255760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610425575061041e60209261041461040f612ba4565b6127c7565b9290915035612a75565b9051908152f35b80fd5b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209076ffffffffffffffffffffffffffffffffffffffffffffff600154169051908152f35b5080fd5b848285346104f757817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f75760209282916104c0612420565b6104c8612443565b9173ffffffffffffffffffffffffffffffffffffffff8092168452865283832091168252845220549051908152f35b8280fd5b509150346104f75760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f75791602092610539612420565b9173ffffffffffffffffffffffffffffffffffffffff61055a61040f612ba4565b9290919516835260038752858320546dffffffffffffffffffffffffffff81818116961c91826105b1575b505050866105969394950151612a75565b9050808210156105a95750905b51908152f35b9050906105a3565b836105c192869260701c16612a60565b908186116105d0575b80610585565b8452600888528684205461271091860360a09190911c61ffff16020490930392866105966105ca565b50833461042557807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104255760806060836de8d4a50fffffffffff172b5af00061064861040f612ba4565b939186819492940151958691015102049168ffffffffffffffffff82519516855260208501528301526060820152f35b509150346104f757827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f75760209254901c9051908152f35b50929192346104f757817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f7576106f0612420565b6106f8612443565b9160443590606435926084359460ff86168096036109ac5742851061094f5761071f6125fb565b9473ffffffffffffffffffffffffffffffffffffffff80931696878a5260209660078852858b20998a549a60018c019055865193868a8601967f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c988528c8a880152169b8c606087015289608087015260a086015260c085015260c08452830167ffffffffffffffff948482108683111761092257818852845190206101008501927f1901000000000000000000000000000000000000000000000000000000000000845261010286015261012285015260428152610160840194818610908611176108f657848752519020835261018082015260a4356101a082015260c4356101c0909101528780528490889060809060015afa156108ec5786511696871515806108e3575b156108885786977f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259596975283528087208688528352818188205551908152a380f35b8360649251917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601460248201527f416c6f653a207065726d697420696e76616c69640000000000000000000000006044820152fd5b50848814610845565b81513d88823e3d90fd5b60248c60418f7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b5060248c60418f7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b60648960208551917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601460248201527f416c6f653a207065726d697420657870697265640000000000000000000000006044820152fd5b8780fd5b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576020916109ec612420565b9083610a036109fc61040f612ba4565b9195612b13565b9201519050808210156105a957509051908152f35b509150346104f75760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f7578160209373ffffffffffffffffffffffffffffffffffffffff610a6b612420565b168152600385522054901c9051908152f35b505050505061024f612466565b5090346103c75760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103c757823593610ac6612443565b6044359273ffffffffffffffffffffffffffffffffffffffff92838516809503610425579087939291610af7612e79565b9092610b0584518389612af1565b99610b118b1515612c10565b883303610cff575b508351908884528360209c8d93600385528c8c81852054968c6dffffffffffffffffffffffffffff89818116991c9081610c1e575b5050505050505091929350501161047e579085610be7939288835260038c520388822055867fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8b8a51898152a3848151038152888101610baf898251612d16565b9052610be288847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea36013560601c612d23565b613198565b8451928684528784015216907ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db843392a451908152f35b899b610c34918c60709d9c9b9d1c169788612ab5565b90818911610c64575b505050505050610c4f93945002612612565b60701b9003818388388c8f8d908c8480610b4e565b91610c4f999186610cd26127108b9c8f807fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9a9986925260088a52209461ffff855196610cb0886124c9565b54888116885260a01c1691828a8801520302049e8f90039e8f9c8d9102612612565b8094039c838351168152600386522083815401905551169451908152a3869493508c8f8d908c3880610c3d565b8884528060205289842033855260205289842054887fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610d43575b5050610b19565b610d4c91612d16565b9089855260205289842033855260205289842055388088610d3c565b838286346104255760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610425575061041e602092610dac61040f612ba4565b9290915035612b02565b8385833461047e57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e57823573ffffffffffffffffffffffffffffffffffffffff610e06612443565b1690818452602094600286528385205494610e1f612e79565b5091610e3f60808401516de8d4a50fffffffffff172b5af0008602612612565b9680881015610f6457879086845260028a5203868320556060830187815103905287830190610e6f858351612c75565b8092528651907f70a08231000000000000000000000000000000000000000000000000000000008252309082015288816024817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea36013560601c5afa928315610f59578093610f25575b5050610ef09291610eeb911115612cb1565b613046565b825190815283858201527fe4a1ae657f49cb1fb1c7d3a94ae6093565c4c8c0e03de488f79c377c3c3a24e0833392a351908152f35b909192508882813d8311610f52575b610f3e8183612530565b810103126104255750519080610ef0610ed9565b503d610f34565b8751903d90823e3d90fd5b6064828a8951917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601460248201527f416c6f653a20726570617920746f6f206d7563680000000000000000000000006044820152fd5b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209060015460b81c9051908152f35b84823461047e57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209061104861103e612420565b6024359033612dd4565b5160018152f35b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209068ffffffffffffffffff6080611096612e79565b506110a081613198565b0151169051908152f35b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209073ffffffffffffffffffffffffffffffffffffffff600954169051908152f35b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576111a5612420565b9073ffffffffffffffffffffffffffffffffffffffff807f0000000000000000000000000000000000000000000000000000000000000000163314806111fc575b156103c757600192168352600260205282205580f35b508083168452600260205281842054156111e6565b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209061041e611250612420565b6126c4565b509050346104f757827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f75782815180937f95d89b41000000000000000000000000000000000000000000000000000000008252817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea36013560601c5afa9182156113715783611348949361134c575b505061133e60218251846113088296518092602080860191016123b3565b81017f2b000000000000000000000000000000000000000000000000000000000000006020820152036001810185520183612530565b51918291826123d6565b0390f35b6113699293503d8091833e6113618183612530565b810190612571565b9038806112ea565b81513d85823e3d90fd5b50346103c7577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f75781359073ffffffffffffffffffffffffffffffffffffffff82168092036103c7576024359060ff82169081830361039b576001548060b81c6114dd5776ffffffffffffffffffffffffffffffffffffffffffffff167be8d4a5100000000000000000000000000000000000000000000000001760015585547bffffffffffffffffffffffffffffffffffffffffffffffffffffffff164290911b7fffffffff000000000000000000000000000000000000000000000000000000001617855561147061272c565b600555466006558060095494111590816114d1575b50156103c75774ff00000000000000000000000000000000000000007fffffffffffffffffffffff0000000000000000000000000000000000000000009160a01b169216171760095580f35b60149150111538611485565b8680fd5b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5761041e602092611520612420565b905490600154916dffffffffffffffffffffffffffff6de8d4a50fffffffffff172b5af0008183169476ffffffffffffffffffffffffffffffffffffffffffffff8160b81c911602049160701c160190612b13565b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576dffffffffffffffffffffffffffff6020925460701c169051908152f35b84823461047e5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576115fc6124b6565b611604612443565b916044359061ffff9283831680930361039b5763ffffffff169283151580611703575b806116f8575b1561039b5783865260086020528186205460a01c166116f4577f9038af148c4bb36b89ff46ec17f02b27c5ab1e0ca181c0f0981b986a78b002d5918160209251611676816124c9565b73ffffffffffffffffffffffffffffffffffffffff8097169687825284820190848252878a5260088652838a209251167fffffffffffffffffffff0000000000000000000000000000000000000000000075ffff00000000000000000000000000000000000000008454935160a01b1692161717905551908152a380f35b8480fd5b50612710831061162d565b50821515611627565b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576dffffffffffffffffffffffffffff8160209373ffffffffffffffffffffffffffffffffffffffff61176d612420565b16815260038552205460701c169051908152f35b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e578060209273ffffffffffffffffffffffffffffffffffffffff6117d3612420565b1681526007845220549051908152f35b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576dffffffffffffffffffffffffffff8160209373ffffffffffffffffffffffffffffffffffffffff611844612420565b168152600385522054169051908152f35b50833461042557817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261042557833561188f612443565b936118a661189b612e79565b819291519085612a75565b956118b2871515612c10565b6020978883016118c3868251612c75565b8091528751917f70a08231000000000000000000000000000000000000000000000000000000008352309083015289826024817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea36013560601c5afa918215611a51578792611a20575b5073ffffffffffffffffffffffffffffffffffffffff9291611950911115612cb1565b16848180965260038952878782205480951c6119df575b6119aa9401878220557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8988518a8152a36119a3868251612c75565b8152613198565b825190815283858201527fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7833392a351908152f35b50919290506e0100000000000000000000000000006dffffffffffffffffffffffffffff8260701c168501101561047e57607084901b019190849087611967565b9091508981813d8311611a4a575b611a388183612530565b810103126114dd57519061195061192d565b503d611a2e565b88513d89823e3d90fd5b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e57809163ffffffff611a9b6124b6565b1681526008602052205461ffff82519173ffffffffffffffffffffffffffffffffffffffff8116835260a01c166020820152f35b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e578060209273ffffffffffffffffffffffffffffffffffffffff611b21612420565b1681526002845220549051908152f35b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209061041e611b70612420565b61264b565b838286346104255760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610425575061041e602092611bb961040f612ba4565b9290915035612af1565b509050346104f757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f7578135611bfe612443565b3385526020946002865283812054948515611d185750611c1c612e79565b509060808201516de8d4a50fffffffffff172b5af000720119799812dea11197f39889069b3b2cb9832586118102158202156104f7579186611c9673ffffffffffffffffffffffffffffffffffffffff969593611ce1958902908082049106151501809a60608601611c8f838251612c75565b9052612c75565b9133815260028b522055878101611cae868251612d16565b9052610eeb85847fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea36013560601c612d23565b8351928352848684015216907fc1561b330e73faa7d5d1ac03c968d8f359b0191ccdb9cc002cf7d8eb6ae038cb833392a351908152f35b606490878651917f08c379a0000000000000000000000000000000000000000000000000000000008352820152601460248201527f416c6f653a206e6f74206120626f72726f7765720000000000000000000000006044820152fd5b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209060ff60095460a01c169051908152f35b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209061041e611df6612420565b611e0161040f612ba4565b92909150612b13565b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e57602090517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea36013560601c8152f35b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e5760209061041e6125fb565b8382863461042557807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610425576020825180947f313ce567000000000000000000000000000000000000000000000000000000008252817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea36013560601c5afa928315611f84578193611f43575b60208460ff855191168152f35b9092506020813d8211611f7c575b81611f5e60209383612530565b810103126104f757519160ff83168303610425575060ff6020611f36565b3d9150611f51565b509051903d90823e3d90fd5b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b84823461047e5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e578060209273ffffffffffffffffffffffffffffffffffffffff612051612420565b1681526003845220549051908152f35b848285346104f75760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f757602092611048916120a2612420565b6120aa612443565b91856044359473ffffffffffffffffffffffffffffffffffffffff841692838252808a528282203383528a5282822054877fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff820361210e575b505050505050612dd4565b61211791612d16565b938252895281812033825289522055868581808087612103565b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576dffffffffffffffffffffffffffff60209254169051908152f35b838286346104255760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610425575061041e6020926121c061040f612ba4565b9290915035612a60565b848285346104f757817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f757602092612206612420565b9183602435928392338252875273ffffffffffffffffffffffffffffffffffffffff8282209516948582528752205582519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925843392a35160018152f35b509050346104f757827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126104f75782815180937f06fdde03000000000000000000000000000000000000000000000000000000008252817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffea36013560601c5afa9182156113715783611348949361234f575b505061133e6028825180947f416c6f6520494920000000000000000000000000000000000000000000000000602083015261233f81518092602086860191016123b3565b8101036008810185520183612530565b6123649293503d8091833e6113618183612530565b9038806122fb565b84823461047e57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261047e576020906123aa61040f612ba4565b50915191825250f35b60005b8381106123c65750506000910152565b81810151838201526020016123b6565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f6040936020845261241981518092816020880152602088880191016123b3565b0116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361024f57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361024f57565b503461024f5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261024f5761249e612420565b5060206040516c010000000000000000000000008152f35b6004359063ffffffff8216820361024f57565b6040810190811067ffffffffffffffff8211176124e557604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b60a0810190811067ffffffffffffffff8211176124e557604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff8211176124e557604052565b60208183031261024f57805167ffffffffffffffff9182821161024f57019082601f8301121561024f5781519081116124e557604051926125da60207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501160185612530565b8184526020828401011161024f576125f891602080850191016123b3565b90565b600654460361260a5760055490565b6125f861272c565b811561261c570490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b73ffffffffffffffffffffffffffffffffffffffff16600052600260205260406000205480156126be576de8d4a50fffffffffff172b5af000907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60806126b361040f612ba4565b505001519101020490565b50600090565b73ffffffffffffffffffffffffffffffffffffffff16600052600260205260406000205480156126be576de8d4a50fffffffffff172b5af000907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60015460b81c9101020490565b60405160208101907f2aef22f9d7df5f9d21c56d14029233f3fdaa91917727e1eb68e504d27072d6cd82527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660408201524660608201523060808201526080815261279681612514565b51902090565b604051906127a982612514565b60006080838281528260208201528260408201528260608201520152565b6127cf61279c565b506060810191825160808301936de8d4a50fffffffffff172b5af0008092865102049160208501958387510196879460408801908151428114801561291a575b61290a5760209060446128266009549d8e95612922565b9173ffffffffffffffffffffffffffffffffffffffff60405195869485937f5e555353000000000000000000000000000000000000000000000000000000008552420360048501526024840152165afa9081156128fe576000916128cc575b50916000849264e8d4a510006128c69997956128be9d99975102048094525251925102040195869160ff87519460a01c16908303612612565b8103916129cc565b91929190565b906020823d82116128f6575b816128e560209383612530565b810103126104255750516000612885565b3d91506128d8565b6040513d6000823e3d90fd5b5050505050505050815191929190565b50811561280f565b670de0b6b3a7640000917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82840992828102928380861095039480860395146129bf578483111561024f578291096001821901821680920460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029360018380600003040190848311900302920304170290565b5050906125f89250612612565b917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82840992828102928380861095039480860395146129bf578483111561024f578291096001821901821680920460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029360018380600003040190848311900302920304170290565b918015612a70576125f892612ab5565b505090565b918015612a70576125f8925b817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04811182021583021561024f57020490565b817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff04811182021583021561024f570290808204910615150190565b91908115612a70576125f892612a81565b91908115612a70576125f892612ab5565b73ffffffffffffffffffffffffffffffffffffffff90929192166000526003602052612b57604060002054916dffffffffffffffffffffffffffff93848416612af1565b918160e01c9182612b69575b50505090565b60701c1690818311612b7c575b80612b63565b9061271091600052600860205261ffff60406000205460a01c16908303020490033880612b76565b612bac61279c565b5060005460015460405191612bc083612514565b6dffffffffffffffffffffffffffff80821684528160701c16602084015260e01c604083015276ffffffffffffffffffffffffffffffffffffffffffffff8116606083015260b81c608082015290565b15612c1757565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f416c6f653a207a65726f20696d706163740000000000000000000000000000006044820152fd5b91908201809211612c8257565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b15612cb857565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f416c6f653a20696e73756666696369656e74207072652d7061790000000000006044820152fd5b91908203918211612c8257565b60009182604492602095604051937fa9059cbb000000000000000000000000000000000000000000000000000000008552600485015260248401525af13d15601f3d1160016000511416171615612d7657565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f5452414e534645525f4641494c454400000000000000000000000000000000006044820152fd5b909173ffffffffffffffffffffffffffffffffffffffff80921691600093838552600360205260408520548060e01c1580612e5f575b1561039b578390036040862055169283815260408120548060e01c61047e57916020916040827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9501912055604051908152a3565b506dffffffffffffffffffffffffffff8116841115612e0a565b612e8161279c565b5060008054918260e01c60015490604094855191612e9e83612514565b6dffffffffffffffffffffffffffff938483168452848360701c166020850152818885015276ffffffffffffffffffffffffffffffffffffffffffffff8116606085015260b81c608084015215612fe9577bffffffffffffffffffffffffffffffffffffffffffffffffffffffff168455612f18906127c7565b92909482968351808611612f2f575b505050505050565b612f399086612d16565b9073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001693848452600360205281842054908160e01c612fc6575b5091602091817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef94018186205551908152a352388080808080612f27565b6e010000000000000000000000000000908260701c1610156103c7576020612f88565b606486517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f416c6f653a206c6f636b656400000000000000000000000000000000000000006044820152fd5b60408101908151156000146131445776ffffffffffffffffffffffffffffffffffffffffffffff61307a60608301516131d1565b166080820151690100000000000000000081101561024f5760b81b7fffffffffffffffffff000000000000000000000000000000000000000000000016176001554282525b7bffffffffffffffffffffffffffff000000000000000000000000000061310360206dffffffffffffffffffffffffffff6130fa855161320d565b1693015161320d565b60701b16915164010000000081101561024f577fffffffff000000000000000000000000000000000000000000000000000000009060e01b16911717600055565b76ffffffffffffffffffffffffffffffffffffffffffffff61316960608301516131d1565b167fffffffffffffffffff000000000000000000000000000000000000000000000060015416176001556130bf565b60408101908151156000146131cc5776ffffffffffffffffffffffffffffffffffffffffffffff61307a60608301516131d1565b6130bf565b7701000000000000000000000000000000000000000000000081101561024f5776ffffffffffffffffffffffffffffffffffffffffffffff1690565b6e01000000000000000000000000000081101561024f576dffffffffffffffffffffffffffff169056fea2646970667358221220f8ab74faad616e0a0e4b5d9cec90aa7fd099a56952f96d99c698566c589a690d64736f6c6343000811003300000000000000000000000095110c9806833d3d3c250112fac73c5a6f631e80

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

00000000000000000000000095110c9806833d3d3c250112fac73c5a6f631e80

-----Decoded View---------------
Arg [0] : reserve (address): 0x95110C9806833d3D3C250112fac73c5A6f631E80

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000095110c9806833d3d3c250112fac73c5a6f631e80


Block Transaction Difficulty Gas Used Reward
Block Uncle Number Difficulty Gas Used Reward
Loading
Loading
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.