Contract
0x62eAa8b180faebfBb0627dBd07E23f27379c147e
1
Contract Overview
Balance:
0 ETH
EtherValue:
$0.00
My Name Tag:
Not Available, login to update
Latest 25 internal transaction
[ Download CSV Export ]
Contract Name:
Lender
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// 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)))) } } }
// 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); } } }
// 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); } }
// 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)) } } }
// 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"); } }
// 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); } }
// 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 } }
// 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; } } }
// 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); } }
// 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;
// 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;
{ "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
- No Contract Security Audit Submitted- Submit Audit Here
[{"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"}]
Contract Creation Code
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
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.