ERC-20
Overview
Max Total Supply
756.020781 ERC20 ***
Holders
252
Market
Price
$0.00 @ 0.000000 ETH
Onchain Market Cap
$0.00
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 6 Decimals)
Balance
0.499979 ERC20 ***Value
$0.00Loading...
Loading
Loading...
Loading
Loading...
Loading
Similar Match Source Code This contract matches the deployed Bytecode of the Source Code for Contract 0x38d378d9...9f12f938B The constructor portion of the code might be different and could alter the actual behaviour of the contract
Contract Name:
CygnusBorrow
Compiler Version
v0.8.17+commit.8df45f5f
Optimization Enabled:
Yes with 800 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0-or-later // // CygnusBorrow.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════ █████████ 🛸 🛸 . ███░░░░░███ 📡 🌔 ███ ░░░ █████ ████ ███████ ████████ █████ ████ █████ ⠀ ░███ ░░███ ░███ ███░░███░░███░░███ ░░███ ░███ ███░░ . .⠀ 🛰️ . ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░░█████ ⠀ ░░███ ███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░░░░███ . . ░░█████████ ░░███████ ░░███████ ████ █████ ░░████████ ██████ -----========*⠀ ░░░░░░░░░ ░░░░░███ ░░░░░███░░░░ ░░░░░ ░░░░░░░░ ░░░░░░ . . ███ ░███ ███ ░███ . . 🛸 ⠀ . * ░░██████ ░░██████ . 🛰️ . . ░░░░░░ ░░░░░░ ⠀ . . . ------======* . BORROWABLE (CygUSD) - https://cygnusdao.finance . . ═══════════════════════════════════════════════════════════════════════════════════════════════════════════ Smart contracts to lend stablecoins to liquidity providers. Deposit USD, earn USD. Structure of all Cygnus Contracts: Contract ⠀Interface ├ 1. Libraries ├ 1. Custom Errors ├ 2. Storage ├ 2. Custom Events │ ├ Private ⠀ ├ 3. Constant Functions ⠀ │ ├ Internal │ ├ Public ⠀ │ └ Public │ └ External ⠀⠀⠀ ├ 3. Constructor └ 4. Non-Constant Functions ├ 4. Modifiers ⠀ ├ Public ├ 5. Constant Functions ⠀ └ External │ ├ Private ⠀ │ ├ Internal │ ├ Public │ └ External └ 6. Non-Constant Functions ├ Private ├ Internal ├ Public └ External @dev: Inspired by Impermax, follows similar architecture and code but with significant edits. It should only be tested with Solidity >=0.8 as some functions don't check for overflow/underflow and all errors are handled with the new `custom errors` feature among other small things... */ pragma solidity >=0.8.17; // Dependencies import {ICygnusBorrow} from "./interfaces/ICygnusBorrow.sol"; import {CygnusBorrowVoid} from "./CygnusBorrowVoid.sol"; // Libraries import {SafeTransferLib} from "./libraries/SafeTransferLib.sol"; import {FixedPointMathLib} from "./libraries/FixedPointMathLib.sol"; // Interfaces import {ICygnusCollateral} from "./interfaces/ICygnusCollateral.sol"; import {ICygnusAltairCall} from "./interfaces/ICygnusAltairCall.sol"; // Overrides import {ERC20} from "./ERC20.sol"; /** * @title CygnusBorrow Main borrow contract for Cygnus which handles borrows, repays, liquidations & flash liqudiations * @author CygnusDAO * @notice This is the main Borrow contract which is used for borrowing stablecoins and liquidating shortfall * positions. * * The `borrow` function allows anyone to borrow or leverage USD to buy more LP Tokens. If calldata is * passed, then the function calls the `altairBorrow` function on the sender, which should be used to * leverage positions. If there is no calldata, the user can simply borrow instead of leveraging. The * same borrow function is used to repay a loan, by checking the totalBalance held of underlying. * The function also allows anyone to perform a flash loan, as long as the amount repaid is greater * than or equal the borrowed amount. * * The `liquidate` function allows anyone to liquidate or flash liquidate a position. When using * this function with no calldata then the liquidator must have sent an amount of USDC to repay the loan. * When using the function with calldata it allows the user to liquidate a position, and pass this data * to a periphery contract, where it should implement the logic to sell the collateral to the market, * receive USDC and finally repay this contract. The function does the check at the end to ensure * we have received a correct amount of USDC. */ contract CygnusBorrow is ICygnusBorrow, CygnusBorrowVoid { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. LIBRARIES ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @custom:library SafeTransferLib ERC20 transfer library that gracefully handles missing return values. */ using SafeTransferLib for address; /** * @custom:library FixedPointMathLib Arithmetic library with operations for fixed-point numbers */ using FixedPointMathLib for uint256; /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 6. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @notice ERC20 Override * @notice Tracks lender's position after any transfer/mint/burn/etc. CygUSD is the lender token and should always * be tracked at core and updated on any interaction. */ function _afterTokenTransfer(address from, address to, uint256) internal override(ERC20) { // Check for zero address (in case of mints) if (from != address(0)) trackLender(from); // Check for zero address (in case of burns) if (to != address(0)) trackLender(to); } /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @dev This low level function should be called from a periphery contract only * @inheritdoc ICygnusBorrow * @custom:security non-reentrant */ function borrow( address borrower, address receiver, uint256 borrowAmount, bytes calldata data ) external override nonReentrant update returns (uint256 liquidity) { // Check if msg.sender can borrow on behalf of borrower, we use the same spend allowance as redeem if (borrower != msg.sender) _spendAllowance(borrower, msg.sender, borrowAmount); // ────────── 1. Check amount and optimistically send `borrowAmount` to `receiver` // We optimistically transfer borrow amounts and check in step 5 if borrower has enough liquidity to borrow. if (borrowAmount > 0) { // Withdraw `borrowAmount` from strategy _beforeWithdraw(borrowAmount); // Transfer stablecoin to receiver underlying.safeTransfer(receiver, borrowAmount); } // ────────── 2. Pass data to the router if needed // Check data for leverage transaction, if any pass data to router. `liquidity` is the amount of LP received. // Useful return var to work with a router, has no effect on this function itself. if (data.length > 0) liquidity = ICygnusAltairCall(msg.sender).altairBorrow_O9E(msg.sender, borrowAmount, data); // ────────── 3. Get the repay amount (if any) // Borrow/Repay use this same function. To repay the loan the user must have sent back stablecoins to this contract. // Any stablecoin sent directly here is not deposited in the strategy yet. uint256 repayAmount = _checkBalance(underlying); // ────────── 4. Update borrow internally with borrowAmount and repayAmount // IMPORTANT: During tests we want to keep track here of what was actually withdrawn from the strategy // and what was the borrowAmount passed. Always check that withdrawing is correct and ensure // there is no rounding errors. // Update internal record for `borrower` with borrow and repay amount uint256 accountBorrows = _updateBorrow(borrower, borrowAmount, repayAmount); // ────────── 5. Do checks for borrow and repay transactions // Borrow transaction. Check that the borrower has sufficient collateral after borrowing `borrowAmount` by // passing `accountBorrows` to the collateral contract if (borrowAmount > repayAmount) { // Check borrower's current liquidity/shortfall bool userCanBorrow = ICygnusCollateral(twinstar).canBorrow(borrower, accountBorrows); /// @custom:error InsufficientLiquidity Avoid if borrower has insufficient liquidity for this amount if (!userCanBorrow) revert CygnusBorrow__InsufficientLiquidity(); } // Deposit repay amount (if any) in strategy if (repayAmount > 0) _afterDeposit(repayAmount); /// @custom:event Borrow emit Borrow(msg.sender, borrower, receiver, borrowAmount, repayAmount); } /** * @dev This low level function should be called from a periphery contract only * @inheritdoc ICygnusBorrow * @custom:security non-reentrant */ function liquidate( address borrower, address receiver, uint256 repayAmount, bytes calldata data ) external override nonReentrant update returns (uint256 amountUsd) { // ────────── 1. Get borrower's USD debt // Latest borrow balance, already accrued (, uint256 borrowBalance) = _latestBorrowBalance(borrower, false); // Adjust declared amount to max liquidatable, this is the actual repaid amount uint256 max = borrowBalance < repayAmount ? borrowBalance : repayAmount; // ────────── 2. Seize CygLP from borrower // CygLP = (max * liq. incentive) / lp price. // Reverts at Collateral if: // - `max` is 0. // - `borrower`'s position is not in liquidatable state uint256 cygLPAmount = ICygnusCollateral(twinstar).seizeCygLP(receiver, borrower, max); // ────────── 3. Check for data length in case sender sells the collateral to market // If the `receiver` was the router used to flash liquidate then we call the router with the data passed, // allowing the collateral to be sold to the market if (data.length > 0) ICygnusAltairCall(msg.sender).altairLiquidate_f2x(msg.sender, cygLPAmount, max, data); // ────────── 4. Get the repaid amount of USD // Current balance of USD not deposited in strategy (if sell to market then router must have sent back USD). // The amount received back would have to be equal at least to `max`, allowing liquidator to keep the liquidation incentive amountUsd = _checkBalance(underlying); /// @custom:error InsufficientUsdReceived Avoid liquidating if we received less usd than declared if (amountUsd < max) revert CygnusBorrow__InsufficientUsdReceived(); // ────────── 5. Update borrow internally with 0 borrow amount and the amount of usd received // Pass to CygnusBorrowModel _updateBorrow(borrower, 0, amountUsd); // ────────── 6. Deposit in strategy // Deposit underlying in strategy, if 0 then would've reverted by now _afterDeposit(amountUsd); /// @custom:event Liquidate emit Liquidate(msg.sender, borrower, receiver, cygLPAmount, max, amountUsd); } }
// SPDX-License-Identifier: AGPL-3.0-or-later // // CygnusBorrowControl.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusBorrowControl} from "./interfaces/ICygnusBorrowControl.sol"; import {CygnusTerminal} from "./CygnusTerminal.sol"; // Libraries import {FixedPointMathLib} from "./libraries/FixedPointMathLib.sol"; import {SafeCastLib} from "./libraries/SafeCastLib.sol"; // Interfaces import {IERC20} from "./interfaces/IERC20.sol"; // Overrides import {ERC20} from "./ERC20.sol"; /** * @title CygnusBorrowControl Contract for controlling borrow settings * @author CygnusDAO * @notice Initializes Borrow Arm. Assigns name, symbol and decimals to CygnusTerminal for the CygUSD Token. * This contract should be the only contract the Admin has control of (along with the strateyg contract) * specifically to set the CYG rewarder, reserve factor and interest rate model for this pool. */ contract CygnusBorrowControl is ICygnusBorrowControl, CygnusTerminal { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. LIBRARIES ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @custom:library FixedPointMathLib Arithmetic library with operations for fixed-point numbers */ using FixedPointMathLib for uint256; /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. STORAGE ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Private ──────────────────────────────────────────────── */ // ────────────────────── Min/Max this pool allows /** * @notice Maximum base interest rate allowed (10%) */ uint256 private constant BASE_RATE_MAX = 0.10e18; /** * @notice Maximum reserve factor that the protocol can keep as reserves (20%) */ uint256 private constant RESERVE_FACTOR_MAX = 0.20e18; /** * @notice Minimum kink utilization point allowed (70%) */ uint256 private constant KINK_UTILIZATION_RATE_MIN = 0.70e18; /** * @notice Maximum Kink point allowed (99%) */ uint256 private constant KINK_UTILIZATION_RATE_MAX = 0.99e18; /** * @notice Maximum Kink multiplier */ uint256 private constant KINK_MULTIPLIER_MAX = 40; /** * @notice Used to calculate the per second interest rates */ uint256 private constant SECONDS_PER_YEAR = 31536000; /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @inheritdoc ICygnusBorrowControl */ InterestRateModel public override interestRateModel; /** * @inheritdoc ICygnusBorrowControl */ address public override pillarsOfCreation; /** * @inheritdoc ICygnusBorrowControl */ uint256 public override reserveFactor = 0.2e18; /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 5. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Private ──────────────────────────────────────────────── */ /** * @notice Checks if new parameter is within range when updating borrowable settings * @param min The minimum value allowed for the parameter that is being updated * @param max The maximum value allowed for the parameter that is being updated * @param value The value of the parameter that is being updated */ function _validRange(uint256 min, uint256 max, uint256 value) private pure { /// @custom:error ParameterNotInRange Avoid setting important variables outside range if (value < min || value > max) revert CygnusBorrowControl__ParameterNotInRange(); } /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @notice Overrides the name function from the ERC20 * @inheritdoc ERC20 */ function name() public pure override(ERC20, IERC20) returns (string memory) { // Name of the borrowable arm return "Cygnus: Borrowable"; } /** * @notice Overrides the symbol function to represent the underlying stablecoin (ie 'CygUSD: USDC') * @inheritdoc ERC20 */ function symbol() public view override(ERC20, IERC20) returns (string memory) { // Symbol of the Borrowable (ie `CygUSD: USDC`) return string(abi.encodePacked("CygUSD: ", IERC20(underlying).symbol())); } /** * @notice Overrides the decimal function to use the same decimals as underlying * @inheritdoc ERC20 */ function decimals() public view override(ERC20, IERC20) returns (uint8) { // Override decimals for stablecoin return IERC20(underlying).decimals(); } /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @inheritdoc ICygnusBorrowControl */ function collateral() external view override returns (address) { // Read the stored internal variable from terminal return twinstar; } /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 6. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Private ──────────────────────────────────────────────── */ /** * @notice Updates the parameters of the interest rate model and writes to storage * @dev Does necessary checks internally. Reverts in case of failing checks * @param baseRatePerYear_ The approximate target base APR, as a mantissa (scaled by 1e18) * @param multiplierPerYear_ The rate of increase in interest rate wrt utilization (scaled by 1e18) * @param kinkMultiplier_ The increase to farmApy once kink utilization is reached * @param kinkUtilizationRate_ The point at which the jump multiplier takes effect */ function _interestRateModel( uint256 baseRatePerYear_, uint256 multiplierPerYear_, uint256 kinkMultiplier_, uint256 kinkUtilizationRate_ ) private { // Internal parameter check for base rate _validRange(0, BASE_RATE_MAX, baseRatePerYear_); // Internal parameter check for kink rate _validRange(KINK_UTILIZATION_RATE_MIN, KINK_UTILIZATION_RATE_MAX, kinkUtilizationRate_); // Internal parameter check for kink multiplier _validRange(2, KINK_MULTIPLIER_MAX, kinkMultiplier_); // Slope of the interest rate curve in seconds uint256 slope = multiplierPerYear_.divWad(SECONDS_PER_YEAR * kinkUtilizationRate_); // Update the interest rate model interestRateModel = InterestRateModel({ // Calculate the Base Rate per second and update to storage baseRatePerSecond: SafeCastLib.toUint64(baseRatePerYear_ / SECONDS_PER_YEAR), // Calculate the Multiplier per second and update to storage multiplierPerSecond: SafeCastLib.toUint64(slope), // Calculate the Jump Multiplier per second and update to storage jumpMultiplierPerSecond: SafeCastLib.toUint64(slope * kinkMultiplier_), // Update kink utilization rate kink: SafeCastLib.toUint64(kinkUtilizationRate_) }); /// @custom:event NewInterestParameter emit NewInterestRateParameters(baseRatePerYear_, multiplierPerYear_, kinkMultiplier_, kinkUtilizationRate_); } /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @inheritdoc ICygnusBorrowControl * @custom:security only-admin 👽 */ function setInterestRateModel( uint256 baseRatePerYear_, uint256 multiplierPerYear_, uint256 kinkMultiplier_, uint256 kink_ ) external override cygnusAdmin { // Update interest rate model with per second rates _interestRateModel(baseRatePerYear_, multiplierPerYear_, kinkMultiplier_, kink_); } /** * @inheritdoc ICygnusBorrowControl * @custom:security only-admin 👽 */ function setPillarsOfCreation(address newCygRewarder) external override cygnusAdmin { // Need the option of setting to address(0) as child contract checks for 0 address in case it's inactive // Pillars address until now address oldCygRewarder = pillarsOfCreation; /// @custom:event NewPillarsOfCreation emit NewPillarsOfCreation(oldCygRewarder, pillarsOfCreation = newCygRewarder); } /** * @inheritdoc ICygnusBorrowControl * @custom:security only-admin 👽 */ function setReserveFactor(uint256 newReserveFactor) external override cygnusAdmin { // Checks if parameter is within bounds _validRange(0, RESERVE_FACTOR_MAX, newReserveFactor); // Reserve factor until now uint256 oldReserveFactor = reserveFactor; /// @custom:event NewReserveFactor emit NewReserveFactor(oldReserveFactor, reserveFactor = newReserveFactor); } }
// SPDX-License-Identifier: AGPL-3.0-or-later // // CygnusBorrowModel.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this prograinterestRateModel. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusBorrowModel} from "./interfaces/ICygnusBorrowModel.sol"; import {CygnusBorrowControl} from "./CygnusBorrowControl.sol"; // Libraries import {FixedPointMathLib} from "./libraries/FixedPointMathLib.sol"; import {SafeCastLib} from "./libraries/SafeCastLib.sol"; // Interfaces import {IPillarsOfCreation} from "./interfaces/IPillarsOfCreation.sol"; // Overrides import {CygnusTerminal, ICygnusTerminal} from "./CygnusTerminal.sol"; /** * @title CygnusBorrowModel Contract that accrues interest and stores borrow data of each user * @author CygnusDAO * @notice Contract that accrues interest and tracks borrows for this shuttle. It accrues interest on any borrow, * liquidation, repay, deposit or redeem. This contract is also used by CygnusCollateral contracts to get * the latest borrow balance of a borrower to calculate current debt ratio, liquidity or shortfall. * * The interest accrual mechanism is similar to Compound Finance's with the exception of reserves. * If the reserveRate is set (> 0) then the contract mints the vault token (CygUSD) to the daoReserves * contract set at the factory. * * There's also 2 functions `trackLender` & `trackBorrower` which are used to give out rewards to lenders * and borrowers respectively. The way rewards are calculated is by querying the latest balance of * CygUSD for lenders and the latest borrow balance for borrowers. See the `_afterTokenTransfer` function * in CygnusBorrow.sol. After any token transfer of CygUSD (including mint/burn) we pass the balance of * CygUSD of the `from` and `to` address. After any borrow, repay or liquidate we track the latest borrow * balance of the borrower. */ contract CygnusBorrowModel is ICygnusBorrowModel, CygnusBorrowControl { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. LIBRARIES ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @custom:library FixedPointMathLib Arithmetic library with operations for fixed-point numbers */ using FixedPointMathLib for uint256; /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. STORAGE ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @notice Snapshot of borrowers which includes the principal and the borrow balance (principal + interests) */ mapping(address => BorrowSnapshot) internal borrowBalances; // 1 memory slot per accrual /** * @notice The stored total borrows as per last state changing action */ uint144 internal _totalBorrows; /** * @notice The stored borrow index as per last state changing action */ uint80 internal _borrowIndex; /** * @notice The stored timestamp of the last interest accrual */ uint32 internal _accrualTimestamp; /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTRUCTOR ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @notice Constructs the borrowable model contract */ constructor() { // Set initial borrow index to 1 _borrowIndex = 1e18; // Set last accrual timestamp to deployment time _accrualTimestamp = uint32(block.timestamp); } /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. MODIFIERS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @notice Overrides the previous modifier from CygnusTerminal to accrue interest before any interaction * @notice CygnusTerminal override * @custom:modifier update Accrues interest to total borrows */ modifier update() override(CygnusTerminal) { // Accrue interest before any state changing action (ie. Deposit/Redeem/Borrow/Repay/Liquidate) _accrueInterest(); // Update balance of the underlying before to prevent deposit spam for yield bearing tokens _update(); _; // Update balance of the underlying after _update(); } /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 5. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ───────────────────────────────────────────── Internal ──────────────────────────────────────────────── */ /** * @notice Previews the total assets owned by the vault (cash + borrows). * @notice The deposit and redeem functions always use this function to calculate the shares and assets * respectively passing `false` since interest and balance is synced (since deposit/redeem use * update modifier), stopping extra SLOADs. If called externally then we always simulate accrual. * @param accrue Whether we should simulate accrual or not. * @return The total underlying assets we own (total balance + borrows) * @inheritdoc CygnusTerminal */ function _totalAssets(bool accrue) internal view override returns (uint256) { // Current stored cash uint256 balance = totalBalance; // Current borrows stored uint256 borrows = _totalBorrows; // If we should accrue then get the latest borrows with interest accrued from the borrow indices. if (accrue) (, borrows, , , ) = _borrowIndices(); // Return total cash + total borrows return balance + borrows; } /** * @notice Get the latest borrow indices * @return cash The total amount of underlying currently deposited in the strategy * @return borrows The latest borrows with interest accruals * @return index The latest borrow index with interst accruals * @return timeElapsed The time elapsed since the last accrual timestamp * @return interest The interest accrued since last accrual */ function _borrowIndices() internal view returns (uint256 cash, uint256 borrows, uint256 index, uint256 timeElapsed, uint256 interest) { // ──────────────────── Load values from storage ──────────────────────── // Total balance of the underlying deposited in the strategy cash = totalBalance; // Total borrows stored borrows = _totalBorrows; // Borrow index stored index = _borrowIndex; // Time elapsed between present timestamp and last accrued period timeElapsed = block.timestamp - _accrualTimestamp; // Return cash, stored borrows and stored index if no time elapsed since last accrual and thus no interest if (timeElapsed == 0) return (cash, borrows, index, 0, 0); // ────────────────────────────────────────────────────────────────────── // 1. Get latest per-second BorrowRate with current cash and stored borrows uint256 latestBorrowRate = _latestBorrowRate(cash, borrows); // 2. The borrow rate by the time elapsed uint256 interestFactor = latestBorrowRate * timeElapsed; // 3. Calculate the interest accumulated in time elapsed with the stored borrows interest = interestFactor.mulWad(borrows); // 4. Add the interest accumulated to the stored borrows borrows += interest; // 5. Latest borrow index (new_index = index + (interestfactor * index / 1e18)) index += interestFactor.mulWad(index); } /** * @notice Calculates the current utilization rate given cash and borrows * @param cash Total current balance of assets this contract holds * @param borrows Total amount of borrowed funds * @return The current utilization rate for the lending pool */ function _utilizationRate(uint256 cash, uint256 borrows) internal pure returns (uint256) { // Utilization rate = borrows / (cash + borrows). We don't take into account reserves since we mint CygUSD return borrows == 0 ? 0 : borrows.divWad(cash + borrows); } /** * @notice Get the latest borrow balance for `borrower`. If accrue is false it means that we have already accrued interest * (through the `update` modifier) within the transaction and there is no need to load the borrow indices. * @param borrower The address of the borrower * @param accrue Whether we should simulate accrue or not * @return principal The original borrowed amount without interest * @return borrowBalance The borrowed amount with interest */ function _latestBorrowBalance(address borrower, bool accrue) internal view returns (uint256 principal, uint256 borrowBalance) { // Load user struct to storage (gas savings when called from Collateral.sol) BorrowSnapshot storage borrowSnapshot = borrowBalances[borrower]; // If interestIndex = 0 then user has no borrows if (borrowSnapshot.interestIndex == 0) return (0, 0); // The original loaned amount without interest accruals principal = borrowSnapshot.principal; // Get the current index. If called in `_updateBorrow` (after any liquidation or borrow) then we use the // stored borrow index as we have accrued before and this is the latest borrow index. uint256 index = _borrowIndex; /// If accrue then get the latest borrow index with interest accrued if (accrue) (, , index, , ) = _borrowIndices(); // Calculate borrow balance with latest borrow index borrowBalance = principal.fullMulDiv(index, borrowSnapshot.interestIndex); } /** * @notice Calculates the current borrow rate given cash and borrows * @param cash Total current balance of assets this contract holds * @param borrows Total amount of borrowed funds * @return The latest per second borrow rate */ function _latestBorrowRate(uint256 cash, uint256 borrows) internal view returns (uint256) { // Current utilization rate (can be 0) uint256 util = _utilizationRate(cash, borrows); // If util is under then return normal rate if (util <= interestRateModel.kink) { return util.mulWad(interestRateModel.multiplierPerSecond) + interestRateModel.baseRatePerSecond; } // Else return normal rate + kink rate uint256 normalRate = uint256(interestRateModel.kink).mulWad(interestRateModel.multiplierPerSecond) + interestRateModel.baseRatePerSecond; // Get the excess utilization rate uint256 excessUtil = util - interestRateModel.kink; // Return per second borrow rate return excessUtil.mulWad(interestRateModel.jumpMultiplierPerSecond) + normalRate; } /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ // Stored variables /** * @inheritdoc ICygnusBorrowModel */ function totalBorrows() external view override returns (uint256 borrows) { // Latest borrows with interest accrued (, borrows, , , ) = _borrowIndices(); } /** * @inheritdoc ICygnusBorrowModel */ function borrowIndex() external view override returns (uint256 index) { // Latest borrow index with interest accrued (, , index, , ) = _borrowIndices(); } /** * @inheritdoc ICygnusBorrowModel */ function lastAccrualTimestamp() external view override returns (uint256) { // Last accrual timestamp return _accrualTimestamp; } /** * @inheritdoc ICygnusBorrowModel */ function getUsdPrice() public view override returns (uint256) { // Return price of the denom token in 18 decimals, kept here for reporting purposes only return nebula.denominationTokenPrice(); } // Interest rate model /** * @inheritdoc ICygnusBorrowModel */ function utilizationRate() external view override returns (uint256) { /// Get the latest borrows with interest accrued (uint256 cash, uint256 borrows, , , ) = _borrowIndices(); // Current utilization rate return _utilizationRate(cash, borrows); } /** * @inheritdoc ICygnusBorrowModel */ function borrowRate() external view override returns (uint256) { // Get the current borrows with interest accrued (uint256 cash, uint256 borrows, , , ) = _borrowIndices(); // Calculates the latest borrow rate with the new increased borrows return _latestBorrowRate(cash, borrows); } /** * @inheritdoc ICygnusBorrowModel */ function supplyRate() external view override returns (uint256) { // Get the latest borrows with interest accrued (uint256 cash, uint256 borrows, , , ) = _borrowIndices(); // Latest per second borrow rate with updated borrows uint256 latestBorrowRate = _latestBorrowRate(cash, borrows); // Current burrow rate taking into account the reserve factor uint256 rateToPool = latestBorrowRate.mulWad(1e18 - reserveFactor); // Current utilization rate uint256 util = _utilizationRate(cash, borrows); // Return pool supply rate return util.mulWad(rateToPool); } // Latest user positions /** * @dev It is used by CygnusCollateral contract to check a borrower's position * @inheritdoc ICygnusBorrowModel */ function getBorrowBalance(address borrower) external view override returns (uint256 principal, uint256 borrowBalance) { // Return using latest borrow indices always if called externally return _latestBorrowBalance(borrower, true); } /** * @inheritdoc ICygnusBorrowModel */ function getLenderPosition(address lender) external view override returns (uint256 usdBalance, uint256 positionUsd) { // Balance of underlying - Exchange rate uses latest borrow indices (ie `_totalAssets(true)`) usdBalance = balanceOf(lender).mulWad(exchangeRate()); // Position in USD = Underlying balance * Underlying price positionUsd = usdBalance.mulWad(getUsdPrice()); } /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 6. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Private ──────────────────────────────────────────────── */ /** * @notice Track borrows and lending rewards * @param account The address of the lender or borrower * @param balance Record of this borrower's total borrows up to this point * @param collateral Whether the position is a lend or borrow position */ function trackRewardsPrivate(address account, uint256 balance, address collateral) private { // Latest pillars of creation address address rewarder = pillarsOfCreation; // If pillars of creation is set then track reward if (rewarder != address(0)) IPillarsOfCreation(rewarder).trackRewards(account, balance, collateral); } /** * @notice Mints reserves to the DAO based on the interest accumulated * @param cash Total cash in the strategy * @param borrows Total latest borrows with interest accrued * @param interest The total interest we have accrued during this accrual * @return newReserves The amount of CygUSD minted based on `interestAccumulated and the current exchangeRate */ function mintReservesPrivate(uint256 cash, uint256 borrows, uint256 interest) private returns (uint256 newReserves) { // Calculate the reserves to keep from the total interest accrued (interest * reserveFactor) newReserves = interest.mulWad(reserveFactor); if (newReserves > 0) { // Calculate the amount of CygUSD to mint - Same as convert to shares but allow for 0 shares uint256 cygUsdReserves = newReserves.fullMulDiv(totalSupply(), (cash + borrows - newReserves)); // Get the DAO Reserves current address, DAO reserves is never zero address daoReserves = hangar18.daoReserves(); // Mint to Hangar18's latest `daoReserves` _mint(daoReserves, cygUsdReserves); } } /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @notice Applies accrued interest to total borrows and reserves * @notice Calculates the interest accumulated during the time elapsed since the last accrual and mints reserves accordingly. */ function _accrueInterest() internal { // Get borrow indices (uint256 cash, uint256 borrows, uint256 index, uint256 timeElapsed, uint256 interest) = _borrowIndices(); // Escape if no time has past since last accrue if (timeElapsed == 0) return; // Try mint reserves before updating storage uint256 newReserves = mintReservesPrivate(cash, borrows, interest); // ──────────────────── Store values: 1 memory slot ───────────────────── // Store total borrows with interests _totalBorrows = SafeCastLib.toUint144(borrows); // Store latest borrow index _borrowIndex = SafeCastLib.toUint80(index); // This accruals' timestamp _accrualTimestamp = SafeCastLib.toUint32(block.timestamp); /// @custom:event AccrueInterest emit AccrueInterest(cash, borrows, interest, newReserves); } /** * @notice Updates the borrow balance of a borrower and the total borrows of the protocol. * @param borrower The address of the borrower whose borrow balance is being updated. * @param borrowAmount The amount of tokens being borrowed by the borrower. * @param repayAmount The amount of tokens being repaid by the borrower. * @return accountBorrows The borrower's updated borrow balance */ function _updateBorrow(address borrower, uint256 borrowAmount, uint256 repayAmount) internal returns (uint256 accountBorrows) { // This is always guaranteed to be the borrower's latest borrow balance since we have updated and accured before, // so we can avoid borrow indices and save on SLOADs. (, uint256 borrowBalance) = _latestBorrowBalance(borrower, false); // Return current borrow balance if (borrowAmount == repayAmount) return borrowBalance; // Get the current borrow index uint256 index = _borrowIndex; // Load borrower snapshot BorrowSnapshot storage borrowSnapshot = borrowBalances[borrower]; // Increase the borrower's borrow balance if the borrow amount is greater than the repay amount if (borrowAmount > repayAmount) { // Amount to increase uint256 increaseBorrowAmount; // Never underflows unchecked { // Calculate the actual amount to increase the borrow balance by increaseBorrowAmount = borrowAmount - repayAmount; } // Calculate the borrower's updated borrow balance accountBorrows = borrowBalance + increaseBorrowAmount; // Update the snapshot record of the borrower's principal borrowSnapshot.principal = SafeCastLib.toUint128(accountBorrows); // Update the snapshot record of the present borrow index borrowSnapshot.interestIndex = SafeCastLib.toUint128(index); // Total borrows of the protocol uint256 borrows = _totalBorrows + increaseBorrowAmount; // Update total borrows to storage _totalBorrows = SafeCastLib.toUint144(borrows); } // Decrease the borrower's borrow balance if the repay amount is greater than the borrow amount else { // Never underflows unchecked { // Calculate the actual amount to decrease the borrow balance by uint256 decreaseBorrowAmount = repayAmount - borrowAmount; // Calculate the borrower's updated borrow balance accountBorrows = borrowBalance > decreaseBorrowAmount ? borrowBalance - decreaseBorrowAmount : 0; } // Update the snapshot record of the borrower's principal borrowSnapshot.principal = SafeCastLib.toUint128(accountBorrows); // Update the snapshot record of the borrower's interest index, if no borrows then interest index is 0 borrowSnapshot.interestIndex = accountBorrows == 0 ? 0 : SafeCastLib.toUint128(index); // Calculate the actual decrease amount uint256 actualDecreaseAmount = borrowBalance - accountBorrows; // Total protocol borrows and gas savings uint256 borrows = _totalBorrows; // Never underflows unchecked { // Condition check to update protocols total borrows borrows = borrows > actualDecreaseAmount ? borrows - actualDecreaseAmount : 0; } // Update total protocol borrows _totalBorrows = SafeCastLib.toUint144(borrows); } // Track borrower trackRewardsPrivate(borrower, accountBorrows, twinstar); } /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @inheritdoc ICygnusBorrowModel */ function trackLender(address lender) public override { // Get latest CygUSD balance uint256 balance = balanceOf(lender); // Pass balance with address(0) as collateral trackRewardsPrivate(lender, balance, address(0)); } /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @inheritdoc ICygnusBorrowModel */ function accrueInterest() external override { // Accrue interest, increasing `borrowIndex`, `totalBorrows` and `accrualTimestamp` _accrueInterest(); } /** * @inheritdoc ICygnusBorrowModel */ function trackBorrower(address borrower) external override { // Rewards are paid out on borrower's principal (original borrowed amount), so no need to accrue. (uint256 principal, ) = _latestBorrowBalance(borrower, false); // Pass borrower info to the Rewarder (if any) trackRewardsPrivate(borrower, principal, twinstar); } }
// SPDX-License-Identifier: AGPL-3.0-or-later // // CygnusBorrowVoid.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusBorrowVoid} from "./interfaces/ICygnusBorrowVoid.sol"; import {CygnusBorrowModel} from "./CygnusBorrowModel.sol"; // Libraries import {SafeTransferLib} from "./libraries/SafeTransferLib.sol"; // Interfaces import {IERC20} from "./interfaces/IERC20.sol"; // Strategy import {IZToken} from "./interfaces/BorrowableVoid/IZToken.sol"; import {IUniTroller} from "./interfaces/BorrowableVoid/IUniTroller.sol"; // Overrides import {CygnusTerminal} from "./CygnusTerminal.sol"; /** * @title CygnusBorrowVoid The strategy contract for the underlying stablecoin * @author CygnusDAO * @notice Strategy for the underlying stablecoin deposits. */ contract CygnusBorrowVoid is ICygnusBorrowVoid, CygnusBorrowModel { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. LIBRARIES ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @custom:library SafeTransferLib ERC20 transfer library that gracefully handles missing return values. */ using SafeTransferLib for address; /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. STORAGE ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Private ──────────────────────────────────────────────── */ /* ──────── Strategy ──────── */ /** * @notice Keom USDC.e cToken */ IZToken private constant KEOM_USDC = IZToken(0x4ce75412DAFceBb421E90E42b3fac6dB795e4f85); /** * @notice Comptroller to enter markets, claim rewards, etc. */ IUniTroller private constant COMPTROLLER = IUniTroller(0x6EA32f626e3A5c41547235ebBdf861526e11f482); /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @inheritdoc ICygnusBorrowVoid */ address[] public override allRewardTokens; /** * @inheritdoc ICygnusBorrowVoid */ address public override harvester; /** * @inheritdoc ICygnusBorrowVoid */ uint256 public override lastHarvest; /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTRUCTOR ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @notice Constructs the Cygnus Void contract which handles the strategy for the borrowable`s underlying. */ constructor() {} /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 5. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @notice Preview our balance of USDC.e from Keom * @notice Cygnus Terminal Override * @inheritdoc CygnusTerminal */ function _previewTotalBalance() internal override(CygnusTerminal) returns (uint256 balance) { // Return our latest balance of USD balance = KEOM_USDC.balanceOfUnderlying(address(this)); } /** * @notice Checks the `token` balance of this contract * @param token The token to view balance of * @return amount This contract's `token` balance */ function _checkBalance(address token) internal view returns (uint256) { // Our balance of `token` return token.balanceOf(address(this)); } /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @inheritdoc ICygnusBorrowVoid */ function rewarder() external pure override returns (address) { // Return the contract that rewards us with `rewardsToken` return address(KEOM_USDC); } /** * @inheritdoc ICygnusBorrowVoid */ function rewardTokensLength() external view override returns (uint256) { // Return total reward tokens length return allRewardTokens.length; } /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 6. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Private ──────────────────────────────────────────────── */ /** * @notice Harvest the rewards from the strategy */ function harvestRewardsPrivate() private { // Assign cToken address[] memory holders = new address[](1); // Make the markets array to claim from the Comptroller address[] memory markets = new address[](1); // This vault and the cToken only holders[0] = address(this); markets[0] = address(KEOM_USDC); // Claim only supplying, gas savings COMPTROLLER.claimRewards(holders, markets, false, true); } /** * @notice Harvest and return the pending reward tokens and mounts interally, used by reinvest function. * @return tokens Array of reward token addresses * @return amounts Array of reward token amounts */ function getRewardsPrivate() private returns (address[] memory tokens, uint256[] memory amounts) { // Harvest the rewards from the strategy harvestRewardsPrivate(); // Assign reward tokens and gas savings tokens = allRewardTokens; // Create array of amounts amounts = new uint256[](tokens.length); // Loop over each reward token and return balance for (uint256 i = 0; i < tokens.length; ) { // Assign balance of reward token `i` amounts[i] = _checkBalance(tokens[i]); // Next iteration unchecked { ++i; } } /// @custom:event RechargeVoid emit RechargeVoid(msg.sender, tokens, amounts, lastHarvest = block.timestamp); } /** * @notice Removes allowances from the harvester * @param _harvester The address of the harvester * @param tokens The old reward tokens */ function removeHarvesterPrivate(address _harvester, address[] memory tokens) private { // If no harvester then return if (_harvester == address(0)) return; // Loop through each token for (uint256 i = 0; i < tokens.length; i++) { // Remove the harvester's allowance of old tokens tokens[i].safeApprove(_harvester, 0); } } /** * @notice Add allowances to the new harvester * @param _harvester The address of the new harvester * @param tokens The new reward tokens */ function addHarvesterPrivate(address _harvester, address[] calldata tokens) private { // If no harvester then return if (_harvester == address(0)) return; // Loop through each token for (uint256 i = 0; i < tokens.length; i++) { // Check for underlying/strategy token if (tokens[i] != underlying && tokens[i] != address(KEOM_USDC)) { // Approve harvester tokens[i].safeApprove(_harvester, type(uint256).max); } } } /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @notice Deposits underlying assets in the strategy * @notice Cygnus Terminal Override * @inheritdoc CygnusTerminal */ function _afterDeposit(uint256 assets) internal override(CygnusTerminal) { // Supply USD to Keom KEOM_USDC.mint(assets); } /** * @notice Withdraws underlying assets from the strategy * @notice Cygnus Terminal Override * @inheritdoc CygnusTerminal */ function _beforeWithdraw(uint256 assets) internal override(CygnusTerminal) { // Withdraw USD from Keom KEOM_USDC.redeemUnderlying(assets); } /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @inheritdoc ICygnusBorrowVoid * @custom:security non-reentrant */ function getRewards() external override nonReentrant returns (address[] memory tokens, uint256[] memory amounts) { // The harvester contract calls this function to harvest the rewards. Anyone can call // this function, but the rewards can only be moved by the harvester contract itself return getRewardsPrivate(); } /** * @notice Updates `_totalBalance` increasing amount of underlying liquidity tokens we own * @inheritdoc ICygnusBorrowVoid * @custom:security non-reentrant only-harvester */ function reinvestRewards_y7b(uint256 liquidity) external override nonReentrant update { /// @custom:error OnlyHarvesterAllowed Avoid call if msg.sender is not the harvester if (msg.sender != harvester) revert CygnusBorrowVoid__OnlyHarvesterAllowed(); // After deposit hook, doesn't mint any shares. The contract should have already received // the underlying stablecoin _afterDeposit(liquidity); } /* ────────── Admin ───────── */ /** * @inheritdoc ICygnusBorrowVoid * @custom:security only-admin 👽 */ function chargeVoid() external override cygnusAdmin { // Allow Keom cToken contract to use our USDC underlying.safeApprove(address(KEOM_USDC), type(uint256).max); /// @custom:event ChargeVoid emit ChargeVoid(underlying, shuttleId, address(KEOM_USDC)); } /** * @inheritdoc ICygnusBorrowVoid * @custom:security only-admin 👽 */ function setHarvester(address newHarvester, address[] calldata rewardTokens) external override cygnusAdmin { // Old harvester address oldHarvester = harvester; // Remove allowances from the harvester for `allRewardTokens` up to this point removeHarvesterPrivate(oldHarvester, allRewardTokens); // Allow new harvester to access the new reward tokens passed addHarvesterPrivate(newHarvester, rewardTokens); /// @custom:event NewHarvester emit NewHarvester(oldHarvester, harvester = newHarvester, allRewardTokens = rewardTokens); } /** * @inheritdoc ICygnusBorrowVoid * @custom:security only-admin 👽 */ function sweepToken(address token, address to) external override cygnusAdmin { /// @custom;error CantSweepUnderlying Avoid sweeping underlying if (token == underlying || token == address(KEOM_USDC)) revert CygnusBorrowVoid__TokenIsUnderlying(); // Get balance of token uint256 balance = _checkBalance(token); // Transfer token balance to `to` token.safeTransfer(to, balance); } }
// SPDX-License-Identifier: AGPL-3.0-or-later // // CygnusTerminal.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════════ █████████ 🛸 🛸 🛸 . 🛸 ███░░░░░███ 📡 🌔 ███ ░░░ █████ ████ ███████ ████████ █████ ████ █████ ⠀ ░███ ░░███ ░███ ███░░███░░███░░███ ░░███ ░███ ███░░ . .⠀ 🛰️ . ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░░█████ ⠀ ░░███ ███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░███ ░░░░███ . . ░░█████████ ░░███████ ░░███████ ████ █████ ░░████████ ██████ -----========*⠀ ░░░░░░░░░ ░░░░░███ ░░░░░███░░░░ ░░░░░ ░░░░░░░░ ░░░░░░ . . ███ ░███ ███ ░███ . . 🛸 ⠀ . 🛸* ░░██████ ░░██████ . 🛸 🛰️ -----=========* ░░░░░░ ░░░░░░ 🛸 ⠀ . . . 🛰️ . POOL TOKEN (CygUSD/CygLP) - https://cygnusdao.finance . . ═══════════════════════════════════════════════════════════════════════════════════════════════════════════ */ pragma solidity >=0.8.17; // Dependencies import {ICygnusTerminal} from "./interfaces/ICygnusTerminal.sol"; import {ERC20} from "./ERC20.sol"; import {ReentrancyGuard} from "./utils/ReentrancyGuard.sol"; // Libraries import {SafeCastLib} from "./libraries/SafeCastLib.sol"; import {SafeTransferLib} from "./libraries/SafeTransferLib.sol"; import {FixedPointMathLib} from "./libraries/FixedPointMathLib.sol"; // Interfaces import {IOrbiter} from "./interfaces/IOrbiter.sol"; import {IHangar18} from "./interfaces/IHangar18.sol"; import {ICygnusNebula} from "./interfaces/ICygnusNebula.sol"; import {IAllowanceTransfer} from "./interfaces/IAllowanceTransfer.sol"; /** * @title CygnusTerminal * @author CygnusDAO * @notice Contract used to mint Collateral and Borrow tokens. Both Collateral/Borrow arms of Cygnus mint here * to get the vault token (CygUSD for stablecoin deposits and CygLP for Liquidity deposits). * @notice As the borrowable arm is a stablecoin vault which has assets deposited in strategies, the exchange * rate for CygUSD should be the cash deposited in the strategy + current borrows. Therefore we use an * internal `_totalAssets(bool)` to take into account latest borrows. The bool dictates whether we should * simulate accruals or not, helpful for the contract to always display data in real time. * * @notice Functions overridden in Strategy contracts (CygnusBorrowVoid.sol & CygnusCollateralVoid.sol): * _afterDeposit - borrowable/collateral - Underlying deposits into the strategy * _beforeWithdraw - borrowable/collateral - Underlying withdrawals from the strategy * _previewTotalBalance - borrowable/collateral - The balance of USDC/LP deposited in the strategy * * Functions overriden to include Borrows and accrue intersest in borrowable (CygnusBorrowModel.sol) * _totalAssets - borrowable - Includes total borrows + total balance * update (modifier) - borrowable - Interest accruals before any payable actions */ abstract contract CygnusTerminal is ICygnusTerminal, ERC20, ReentrancyGuard { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. LIBRARIES ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @custom:library SafeTransferLib ERC20 transfer library that gracefully handles missing return values. */ using SafeTransferLib for address; /** * @custom:library FixedPointMathLib Arithmetic library with operations for fixed-point numbers. */ using FixedPointMathLib for uint256; /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. STORAGE ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @notice The address of this contract`s opposite arm. For collateral pools, this is the borrowable address. * For borrowable pools, this is the collateral address. Getters in child contract. */ address internal immutable twinstar; /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @inheritdoc ICygnusTerminal */ IAllowanceTransfer public constant override PERMIT2 = IAllowanceTransfer(0x000000000022D473030F116dDEE9F6B43aC78BA3); /** * @inheritdoc ICygnusTerminal */ IHangar18 public immutable override hangar18; /** * @inheritdoc ICygnusTerminal */ address public immutable override underlying; /** * @inheritdoc ICygnusTerminal */ ICygnusNebula public immutable override nebula; /** * @inheritdoc ICygnusTerminal */ uint256 public immutable override shuttleId; /** * @notice The total balance held of the underlying in the strategy (USD for borrowable, LP for collateral) * @inheritdoc ICygnusTerminal */ uint160 public override totalBalance; /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTRUCTOR ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @notice Constructs tokens for both Collateral and Borrow arms */ constructor() { // Get immutables from deployer contracts (AlbireoOrbiter for Borrowable, DenebOrbiter for Collateral) // Factory, asset, borrow/collateral, oracle, lending pool ID (hangar18, underlying, twinstar, nebula, shuttleId) = IOrbiter(msg.sender).shuttleParameters(); } /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. MODIFIERS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @custom:modifier cygnusAdmin Controls important parameters in both Collateral and Borrow contracts 👽 */ modifier cygnusAdmin() { _checkAdmin(); _; } /** * @notice We override in borrowable arm to accrue interest before any state changing action. * @custom:modifier update Updates `totalBalance` in terms of its underlying * @custom:override CygnusBorrowModel */ modifier update() virtual { _; _update(); } /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 5. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Private ──────────────────────────────────────────────── */ /** * @notice Checks that the msg.sender is Hangar18's current admin 👽 */ function _checkAdmin() private view { // Current admin from the factory address admin = hangar18.admin(); /// @custom:error MsgSenderNotAdmin Avoid unless caller is Cygnus Admin if (msg.sender != admin) revert CygnusTerminal__MsgSenderNotAdmin(); } /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @notice The total assets owned by the vault. Overridden by the borrowable arm to include borrows. * @notice The bool argument is to check if we should simulate interest accrual or not in borrowable. If the * contract is in sync with the latest balance and it has already accrued, we use false. For Collateral * this has no effect. * @custom:override CygnusBorrowModel */ function _totalAssets(bool) internal view virtual returns (uint256) { return totalBalance; } /** * @notice Converts assets to shares * @notice We always pass false to `_totalAssets()` to not simulate accrual and to avoid extra SLOADs. This is * because stored variables are in sync since deposit/redeem use the `update` modifier which updates * balances and accrue interest (see `update` modifier above). */ function _convertToShares(uint256 assets) internal view returns (uint256) { // Gas savings if non-zero uint256 _totalSupply = totalSupply(); // Compute shares given an amount of stablecoin or LP token assets return _totalSupply == 0 ? assets : assets.fullMulDiv(_totalSupply, _totalAssets(false)); } /** * @notice Convert shares to assets. Same as above, pass false to `_totalAssets()` as balances are in sync and * we have already accrued. */ function _convertToAssets(uint256 shares) internal view returns (uint256) { // Gas savings if non-zero uint256 _totalSupply = totalSupply(); // Compute assets given an amount of CygUSD or CygLP shares return _totalSupply == 0 ? shares : shares.fullMulDiv(_totalAssets(false), _totalSupply); } /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @notice Computes the exchange rate between 1 unit of the vault token and the underlying asset * @inheritdoc ICygnusTerminal */ function exchangeRate() public view override returns (uint256) { // Gas savings if non-zero uint256 _totalSupply = totalSupply(); // Return the exchange rate between 1 unit of CygUSD/CygLP and underlying - Always simulate accruals. // This is kept here for reporting purposes. return _totalSupply == 0 ? 1e18 : totalAssets().divWad(_totalSupply); } /** * @notice Total assets managed by the vault. For borrowable this is the stablecoin balance deposited in * the strategy + the current borrows (simulates accruals). For collateral this is the LP token * balance deposited in the strategy. * @inheritdoc ICygnusTerminal */ function totalAssets() public view override returns (uint256) { // Always simulate accrual for borrowable when called externally, for collateral this has no effect and // reads cached balance. return _totalAssets(true); } /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 6. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @notice Updates this contract's balance in terms of its underlying, triggered after any payable function. */ function _update() internal { // Preview the total assets of stablecoin or LP we own uint256 balance = _previewTotalBalance(); /// @custom:event Sync emit Sync(totalBalance = SafeCastLib.toUint160(balance)); } // Should always be overridden in strategy contracts /** * @notice Previews our balance of the underlying asset in the strategy, does not update totalBalance * @notice Not marked as view as some strategies require state update (for example cToken's `balanceOfUnderlying`) * @custom:override CygnusBorrowVoid * @custom:override CygnusCollateralVoid */ function _previewTotalBalance() internal virtual returns (uint256) {} /** * @notice Internal hook for deposits into strategies * @param assets The amount of assets to deposit in the strategy * @custom:override CygnusBorrowVoid * @custom:override CygnusCollateralVoid */ function _afterDeposit(uint256 assets) internal virtual {} /** * @notice Internal hook for withdrawals from strategies * @param assets The amount of assets to withdraw from the strategy * @custom:override CygnusBorrowVoid * @custom:override CygnusCollateralVoid */ function _beforeWithdraw(uint256 assets) internal virtual {} /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @inheritdoc ICygnusTerminal * @custom:security non-reentrant */ function deposit( uint256 assets, address recipient, IAllowanceTransfer.PermitSingle calldata _permit, bytes calldata signature ) external override nonReentrant update returns (uint256 shares) { // Convert assets deposited into shares shares = _convertToShares(assets); /// @custom:error CantMintZeroShares Avoid minting 0 shares if (shares == 0) revert CygnusTerminal__CantMintZeroShares(); // Check for permit to approve and deposit in 1 tx. Users can just approve this contract in // permit2 and skip this by passing an empty signature). if (signature.length > 0) { // Set allowance using permit PERMIT2.permit( // The owner of the tokens being approved. // We only allow the owner of the tokens to be the depositor, but // recipient can be set to another address msg.sender, // Data signed over by the owner specifying the terms of approval _permit, // The owner's signature over the permit data that was the result // of signing the EIP712 hash of `_permit` signature ); } // Transfer underlying to vault PERMIT2.transferFrom(msg.sender, address(this), SafeCastLib.toUint160(assets), underlying); // Avoid inflation attack on the vault - This is only for the first pool depositor as after there will always // be 1000 shares locked in zero address if (totalSupply() == 0) { // Update shares for first depositor shares -= 1000; // Lock initial tokens _mint(address(0), 1000); } // Mint shares and emit Transfer event _mint(recipient, shares); // Deposit assets in the strategy _afterDeposit(assets); /// @custom:event Deposit emit Deposit(msg.sender, recipient, assets, shares); } /** * @inheritdoc ICygnusTerminal * @custom:security non-reentrant */ function redeem(uint256 shares, address recipient, address owner) external override nonReentrant update returns (uint256 assets) { // Withdraw flow if (msg.sender != owner) _spendAllowance(owner, msg.sender, shares); // Convert shares redeemed into underlying assets assets = _convertToAssets(shares); /// @custom:error CantRedeemZeroAssets Avoid redeeming 0 assets if (assets == 0) revert CygnusTerminal__CantRedeemZeroAssets(); // Withdraw assets from the strategy _beforeWithdraw(assets); // Burn shares and emit transfer event _burn(owner, shares); // Transfer assets to recipient underlying.safeTransfer(recipient, assets); /// @custom:event Withdraw emit Withdraw(msg.sender, recipient, owner, assets, shares); } /** * @notice Manually updates `totalBalance` in terms of its underlying, and accrues interest in borrowable. * @inheritdoc ICygnusTerminal * @custom:security non-reentrant */ function sync() external override nonReentrant update {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {IERC20Permit} from "./interfaces/IERC20Permit.sol"; // IMPORTANT: - Removed all hooks from the internal `_transfer` function (used to seize collateral from underwater accounts) // - Removed the `_beforeTokenTransfer` from `_mint` to save gas on `canRedeem` /// @notice Simple ERC20 + EIP-2612 implementation. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol) /// /// @dev Note: /// The ERC20 standard allows minting and transferring to and from the zero address, /// minting and transferring zero tokens, as well as self-approvals. /// For performance, this implementation WILL NOT revert for such actions. /// Please add any checks with overrides if desired. abstract contract ERC20 is IERC20Permit { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The total supply has overflowed. error TotalSupplyOverflow(); /// @dev The allowance has overflowed. error AllowanceOverflow(); /// @dev The allowance has underflowed. error AllowanceUnderflow(); /// @dev Insufficient balance. error InsufficientBalance(); /// @dev Insufficient allowance. error InsufficientAllowance(); /// @dev The permit is invalid. error InvalidPermit(); /// @dev The permit has expired. error PermitExpired(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EVENTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Emitted when `amount` tokens is transferred from `from` to `to`. event Transfer(address indexed from, address indexed to, uint256 amount); /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`. event Approval(address indexed owner, address indexed spender, uint256 amount); /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`. uint256 private constant _TRANSFER_EVENT_SIGNATURE = 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef; /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`. uint256 private constant _APPROVAL_EVENT_SIGNATURE = 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* STORAGE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The storage slot for the total supply. uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c; /// @dev The balance slot of `owner` is given by: /// ``` /// mstore(0x0c, _BALANCE_SLOT_SEED) /// mstore(0x00, owner) /// let balanceSlot := keccak256(0x0c, 0x20) /// ``` uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2; /// @dev The allowance slot of (`owner`, `spender`) is given by: /// ``` /// mstore(0x20, spender) /// mstore(0x0c, _ALLOWANCE_SLOT_SEED) /// mstore(0x00, owner) /// let allowanceSlot := keccak256(0x0c, 0x34) /// ``` uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20; /// @dev The nonce slot of `owner` is given by: /// ``` /// mstore(0x0c, _NONCES_SLOT_SEED) /// mstore(0x00, owner) /// let nonceSlot := keccak256(0x0c, 0x20) /// ``` uint256 private constant _NONCES_SLOT_SEED = 0x38377508; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`. uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901; /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`. bytes32 private constant _DOMAIN_TYPEHASH = 0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f; /// @dev `keccak256("1")`. bytes32 private constant _VERSION_HASH = 0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6; /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`. bytes32 private constant _PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 METADATA */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the name of the token. function name() public view virtual returns (string memory); /// @dev Returns the symbol of the token. function symbol() public view virtual returns (string memory); /// @dev Returns the decimals places of the token. function decimals() public view virtual returns (uint8) { return 18; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns the amount of tokens in existence. function totalSupply() public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { result := sload(_TOTAL_SUPPLY_SLOT) } } /// @dev Returns the amount of tokens owned by `owner`. function balanceOf(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x20)) } } /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`. function allowance(address owner, address spender) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x34)) } } /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens. /// /// Emits a {Approval} event. function approve(address spender, uint256 amount) public virtual returns (bool) { /// @solidity memory-safe-assembly assembly { // Compute the allowance slot and store the amount. mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, caller()) sstore(keccak256(0x0c, 0x34), amount) // Emit the {Approval} event. mstore(0x00, amount) log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c))) } return true; } /// @dev Transfer `amount` tokens from the caller to `to`. /// /// Requirements: /// - `from` must at least have `amount`. /// /// Emits a {Transfer} event. function transfer(address to, uint256 amount) public virtual returns (bool) { _beforeTokenTransfer(msg.sender, to, amount); /// @solidity memory-safe-assembly assembly { // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, caller()) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c))) } _afterTokenTransfer(msg.sender, to, amount); return true; } /// @dev Transfers `amount` tokens from `from` to `to`. /// /// Note: Does not update the allowance if it is the maximum uint256 value. /// /// Requirements: /// - `from` must at least have `amount`. /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`. /// /// Emits a {Transfer} event. function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) { _beforeTokenTransfer(from, to, amount); /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) // Compute the allowance slot and load its value. mstore(0x20, caller()) mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED)) let allowanceSlot := keccak256(0x0c, 0x34) let allowance_ := sload(allowanceSlot) // If the allowance is not the maximum uint256 value. if add(allowance_, 1) { // Revert if the amount to be transferred exceeds the allowance. if gt(amount, allowance_) { mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. revert(0x1c, 0x04) } // Subtract and store the updated allowance. sstore(allowanceSlot, sub(allowance_, amount)) } // Compute the balance slot and load its value. mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) } _afterTokenTransfer(from, to, amount); return true; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* EIP-2612 */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev For more performance, override to return the constant value /// of `keccak256(bytes(name()))` if `name()` will never change. function _constantNameHash() internal view virtual returns (bytes32 result) {} /// @dev Returns the current nonce for `owner`. /// This value is used to compute the signature for EIP-2612 permit. function nonces(address owner) public view virtual returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // Compute the nonce slot and load its value. mstore(0x0c, _NONCES_SLOT_SEED) mstore(0x00, owner) result := sload(keccak256(0x0c, 0x20)) } } /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`, /// authorized by a signed approval by `owner`. /// /// Emits a {Approval} event. function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public virtual { bytes32 nameHash = _constantNameHash(); // We simply calculate it on-the-fly to allow for cases where the `name` may change. if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); /// @solidity memory-safe-assembly assembly { // Revert if the block timestamp is greater than `deadline`. if gt(timestamp(), deadline) { mstore(0x00, 0x1a15a3cc) // `PermitExpired()`. revert(0x1c, 0x04) } let m := mload(0x40) // Grab the free memory pointer. // Clean the upper 96 bits. owner := shr(96, shl(96, owner)) spender := shr(96, shl(96, spender)) // Compute the nonce slot and load its value. mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX) mstore(0x00, owner) let nonceSlot := keccak256(0x0c, 0x20) let nonceValue := sload(nonceSlot) // Prepare the domain separator. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), _VERSION_HASH) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) mstore(0x2e, keccak256(m, 0xa0)) // Prepare the struct hash. mstore(m, _PERMIT_TYPEHASH) mstore(add(m, 0x20), owner) mstore(add(m, 0x40), spender) mstore(add(m, 0x60), value) mstore(add(m, 0x80), nonceValue) mstore(add(m, 0xa0), deadline) mstore(0x4e, keccak256(m, 0xc0)) // Prepare the ecrecover calldata. mstore(0x00, keccak256(0x2c, 0x42)) mstore(0x20, and(0xff, v)) mstore(0x40, r) mstore(0x60, s) let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20) // If the ecrecover fails, the returndatasize will be 0x00, // `owner` will be checked if it equals the hash at 0x00, // which evaluates to false (i.e. 0), and we will revert. // If the ecrecover succeeds, the returndatasize will be 0x20, // `owner` will be compared against the returned address at 0x20. if iszero(eq(mload(returndatasize()), owner)) { mstore(0x00, 0xddafbaef) // `InvalidPermit()`. revert(0x1c, 0x04) } // Increment and store the updated nonce. sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds. // Compute the allowance slot and store the value. // The `owner` is already at slot 0x20. mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender)) sstore(keccak256(0x2c, 0x34), value) // Emit the {Approval} event. log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender) mstore(0x40, m) // Restore the free memory pointer. mstore(0x60, 0) // Restore the zero pointer. } } /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit. function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) { bytes32 nameHash = _constantNameHash(); // We simply calculate it on-the-fly to allow for cases where the `name` may change. if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name())); /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Grab the free memory pointer. mstore(m, _DOMAIN_TYPEHASH) mstore(add(m, 0x20), nameHash) mstore(add(m, 0x40), _VERSION_HASH) mstore(add(m, 0x60), chainid()) mstore(add(m, 0x80), address()) result := keccak256(m, 0xa0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL MINT FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Mints `amount` tokens to `to`, increasing the total supply. /// /// Emits a {Transfer} event. function _mint(address to, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT) let totalSupplyAfter := add(totalSupplyBefore, amount) // Revert if the total supply overflows. if lt(totalSupplyAfter, totalSupplyBefore) { mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`. revert(0x1c, 0x04) } // Store the updated total supply. sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter) // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c))) } _afterTokenTransfer(address(0), to, amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL BURN FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Burns `amount` tokens from `from`, reducing the total supply. /// /// Emits a {Transfer} event. function _burn(address from, uint256 amount) internal virtual { _beforeTokenTransfer(from, address(0), amount); /// @solidity memory-safe-assembly assembly { // Compute the balance slot and load its value. mstore(0x0c, _BALANCE_SLOT_SEED) mstore(0x00, from) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Subtract and store the updated total supply. sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount)) // Emit the {Transfer} event. mstore(0x00, amount) log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0) } _afterTokenTransfer(from, address(0), amount); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL TRANSFER FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Moves `amount` of tokens from `from` to `to`. function _transfer(address from, address to, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { let from_ := shl(96, from) // Compute the balance slot and load its value. mstore(0x0c, or(from_, _BALANCE_SLOT_SEED)) let fromBalanceSlot := keccak256(0x0c, 0x20) let fromBalance := sload(fromBalanceSlot) // Revert if insufficient balance. if gt(amount, fromBalance) { mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`. revert(0x1c, 0x04) } // Subtract and store the updated balance. sstore(fromBalanceSlot, sub(fromBalance, amount)) // Compute the balance slot of `to`. mstore(0x00, to) let toBalanceSlot := keccak256(0x0c, 0x20) // Add and store the updated balance of `to`. // Will not overflow because the sum of all user balances // cannot exceed the maximum uint256 value. sstore(toBalanceSlot, add(sload(toBalanceSlot), amount)) // Emit the {Transfer} event. mstore(0x20, amount) log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* INTERNAL ALLOWANCE FUNCTIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`. function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { // Compute the allowance slot and load its value. mstore(0x20, spender) mstore(0x0c, _ALLOWANCE_SLOT_SEED) mstore(0x00, owner) let allowanceSlot := keccak256(0x0c, 0x34) let allowance_ := sload(allowanceSlot) // If the allowance is not the maximum uint256 value. if add(allowance_, 1) { // Revert if the amount to be transferred exceeds the allowance. if gt(amount, allowance_) { mstore(0x00, 0x13be252b) // `InsufficientAllowance()`. revert(0x1c, 0x04) } // Subtract and store the updated allowance. sstore(allowanceSlot, sub(allowance_, amount)) } } } /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`. /// /// Emits a {Approval} event. function _approve(address owner, address spender, uint256 amount) internal virtual { /// @solidity memory-safe-assembly assembly { let owner_ := shl(96, owner) // Compute the allowance slot and store the amount. mstore(0x20, spender) mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED)) sstore(keccak256(0x0c, 0x34), amount) // Emit the {Approval} event. mstore(0x00, amount) log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c))) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* HOOKS TO OVERRIDE */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Hook that is called before any transfer of tokens. /// This includes minting and burning. function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /// @dev Hook that is called after any transfer of tokens. /// This includes minting and burning. function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); // getRoundData and latestRoundData should both raise "No data present" // if they do not have data to report, instead of returning unset values // which could be misinterpreted as actual reported values. function getRoundData( uint80 _roundId ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.8.17; // Keom Unitroller interface IUniTroller { /** * @notice Claim all the reward accrued by holder in all markets * @param holder The address to claim Reward for */ function claimReward(address holder) external returns (uint256); /** * @notice Claim all the reward accrued by holder in the specified markets * @param holder The address to claim Reward for * @param oTokens The list of markets to claim Reward in */ function claimRewards(address holder, address[] memory oTokens) external; /** * @notice Claim all reward accrued by the holders * @param holders The addresses to claim Reward for * @param oTokens The list of markets to claim Reward in * @param borrowers Whether or not to claim Reward earned by borrowing * @param suppliers Whether or not to claim Reward earned by supplying */ function claimRewards(address[] memory holders, address[] memory oTokens, bool borrowers, bool suppliers) external; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity >=0.8.17; /** * @title Keom's zToken (ie. cToken) */ interface IZToken { function mint(uint256 mintAmount) external; function redeem(uint256 redeemTokens) external; function redeemUnderlying(uint256 redeemAmount) external; function balanceOf(address owner) external view returns (uint); function balanceOfUnderlying(address owner) external returns (uint); }
// SPDX-License-Identifier: AGPL-3.0-or-later // // IAlbireoOrbiter.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; /** * @title ICygnusAlbireo The interface the Cygnus borrow deployer * @notice A contract that constructs a Cygnus borrow pool must implement this to pass arguments to the pool */ interface IAlbireoOrbiter { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @notice Passing the struct parameters to the borrow contracts avoids setting constructor parameters * @return factory The address of the Cygnus factory assigned to `Hangar18` * @return underlying The address of the underlying borrow token (address of USDC) * @return collateral The address of the Cygnus collateral contract for this borrow contract * @return oracle The address of the oracle for this lending pool * @return shuttleId The lending pool ID */ function shuttleParameters() external returns (address factory, address underlying, address collateral, address oracle, uint256 shuttleId); /** * @return The init code hash of the borrow contract for this deployer */ function borrowableInitCodeHash() external view returns (bytes32); /** * @notice Function to deploy the borrow contract of a lending pool * @param underlying The address of the underlying borrow token (address of USDc) * @param collateral The address of the Cygnus collateral contract for this borrow contract * @param shuttleId The ID of the shuttle we are deploying (shared by borrow and collateral) * @return borrowable The address of the new borrow contract */ function deployAlbireo(address underlying, address collateral, address oracle, uint256 shuttleId) external returns (address borrowable); }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.17; /// @title AllowanceTransfer /// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts /// @dev Requires user's token approval on the Permit2 contract interface IAllowanceTransfer { /// @notice Thrown when an allowance on a token has expired. /// @param deadline The timestamp at which the allowed amount is no longer valid error AllowanceExpired(uint256 deadline); /// @notice Thrown when an allowance on a token has been depleted. /// @param amount The maximum amount allowed error InsufficientAllowance(uint256 amount); /// @notice Thrown when too many nonces are invalidated. error ExcessiveInvalidation(); /// @notice Emits an event when the owner successfully invalidates an ordered nonce. event NonceInvalidation(address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce); /// @notice Emits an event when the owner successfully sets permissions on a token for the spender. event Approval(address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration); /// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender. event Permit(address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration, uint48 nonce); /// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function. event Lockdown(address indexed owner, address token, address spender); /// @notice The permit data for a token struct PermitDetails { // ERC20 token address address token; // the maximum amount allowed to spend uint160 amount; // timestamp at which a spender's token allowances become invalid uint48 expiration; // an incrementing value indexed per owner,token,and spender for each signature uint48 nonce; } /// @notice The permit message signed for a single token allownce struct PermitSingle { // the permit data for a single token alownce PermitDetails details; // address permissioned on the allowed tokens address spender; // deadline on the permit signature uint256 sigDeadline; } /// @notice The permit message signed for multiple token allowances struct PermitBatch { // the permit data for multiple token allowances PermitDetails[] details; // address permissioned on the allowed tokens address spender; // deadline on the permit signature uint256 sigDeadline; } /// @notice The saved permissions /// @dev This info is saved per owner, per token, per spender and all signed over in the permit message /// @dev Setting amount to type(uint160).max sets an unlimited approval struct PackedAllowance { // amount allowed uint160 amount; // permission expiry uint48 expiration; // an incrementing value indexed per owner,token,and spender for each signature uint48 nonce; } /// @notice A token spender pair. struct TokenSpenderPair { // the token the spender is approved address token; // the spender address address spender; } /// @notice Details for a token transfer. struct AllowanceTransferDetails { // the owner of the token address from; // the recipient of the token address to; // the amount of the token uint160 amount; // the token to be transferred address token; } /// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval. /// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress] /// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals. function allowance(address, address, address) external view returns (uint160, uint48, uint48); /// @notice Approves the spender to use up to amount of the specified token up until the expiration /// @param token The token to approve /// @param spender The spender address to approve /// @param amount The approved amount of the token /// @param expiration The timestamp at which the approval is no longer valid /// @dev The packed allowance also holds a nonce, which will stay unchanged in approve /// @dev Setting amount to type(uint160).max sets an unlimited approval function approve(address token, address spender, uint160 amount, uint48 expiration) external; /// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce /// @param owner The owner of the tokens being approved /// @param permitSingle Data signed over by the owner specifying the terms of approval /// @param signature The owner's signature over the permit data function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external; /// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature /// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce /// @param owner The owner of the tokens being approved /// @param permitBatch Data signed over by the owner specifying the terms of approval /// @param signature The owner's signature over the permit data function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external; /// @notice Transfer approved tokens from one address to another /// @param from The address to transfer from /// @param to The address of the recipient /// @param amount The amount of the token to transfer /// @param token The token address to transfer /// @dev Requires the from address to have approved at least the desired amount /// of tokens to msg.sender. function transferFrom(address from, address to, uint160 amount, address token) external; /// @notice Transfer approved tokens in a batch /// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers /// @dev Requires the from addresses to have approved at least the desired amount /// of tokens to msg.sender. function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external; /// @notice Enables performing a "lockdown" of the sender's Permit2 identity /// by batch revoking approvals /// @param approvals Array of approvals to revoke. function lockdown(TokenSpenderPair[] calldata approvals) external; /// @notice Invalidate nonces for a given (token, spender) pair /// @param token The token to invalidate nonces for /// @param spender The spender to invalidate nonces for /// @param newNonce The new nonce to set. Invalidates all nonces less than it. /// @dev Can't invalidate more than 2**16 nonces per transaction. function invalidateNonces(address token, address spender, uint48 newNonce) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusAltairCall.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; /** * @title ICygnusAltairCall * @notice Simple callee contract for leverage, deleverage and flash liquidations */ interface ICygnusAltairCall { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Function that is called by the CygnusBorrow contract and decodes data to carry out the leverage * @notice Will only succeed if: Caller is borrow contract & Borrow contract was called by router * * @param sender Address of the contract that initialized the borrow transaction (address of the router) * @param borrowAmount The amount to leverage * @param data The encoded byte data passed from the CygnusBorrow contract to the router */ function altairBorrow_O9E(address sender, uint256 borrowAmount, bytes calldata data) external returns (uint256); /** * @notice Function that is called by the CygnusCollateral contract and decodes data to carry out the deleverage * @notice Will only succeed if: Caller is collateral contract & collateral contract was called by router * * @param sender Address of the contract that initialized the redeem transaction (address of the router) * @param redeemAmount The amount to deleverage * @param data The encoded byte data passed from the CygnusCollateral contract to the router */ function altairRedeem_u91A(address sender, uint256 redeemAmount, bytes calldata data) external returns (uint256); /** * @notice Function that is called by the CygnusBorrow contract and decodes data to carry out the liquidation * @notice Will only succeed if: Caller is borrow contract & Borrow contract was called by router * * @param sender Address of the contract that initialized the borrow transaction (address of the router) * @param cygLPAmount The cygLP Amount seized * @param actualRepayAmount The usd amount the contract must have for the liquidate function to finish * @param data The encoded byte data passed from the CygnusBorrow contract to the router */ function altairLiquidate_f2x(address sender, uint256 cygLPAmount, uint256 actualRepayAmount, bytes calldata data) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusBorrow.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusBorrowVoid} from "./ICygnusBorrowVoid.sol"; // Overrides import {ICygnusTerminal} from "./ICygnusTerminal.sol"; /** * @title ICygnusBorrow Interface for the main Borrow contract which handles borrows/liquidations * @notice Main interface to borrow against collateral or liquidate positions */ interface ICygnusBorrow is ICygnusBorrowVoid { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts if the borrower has insufficient liquidity for this borrow * @custom:error InsufficientLiquidity */ error CygnusBorrow__InsufficientLiquidity(); /** * @dev Reverts if usd received is less than repaid after liquidating * @custom:error InsufficientUsdReceived */ error CygnusBorrow__InsufficientUsdReceived(); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when a borrower borrows or repays a loan. * @param sender Indexed address of msg.sender * @param borrower Indexed address of the borrower * @param receiver Indexed address of receiver * @param borrowAmount The amount of stablecoins borrowed (if any) * @param repayAmount The amount of stablecoins repaid (if any) * @custom:event Borrow */ event Borrow(address indexed sender, address indexed borrower, address indexed receiver, uint256 borrowAmount, uint256 repayAmount); /** * @dev Logs when a liquidator repays and seizes collateral * @param sender Indexed address of msg.sender (should be `Altair` address) * @param borrower Indexed address of the borrower * @param receiver Indexed address of receiver * @param repayAmount The amount of USD repaid * @param cygLPAmount The amount of CygLP seized * @param usdAmount The total amount of underlying deposited * @custom:event Liquidate */ event Liquidate( address indexed sender, address indexed borrower, address indexed receiver, uint256 repayAmount, uint256 cygLPAmount, uint256 usdAmount ); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice This low level function should be called from a periphery contract only * @notice Main function used to borrow stablecoins or repay loans. * @param borrower The address of the borrower * @param receiver The address of the receiver of the borrow amount. * @param borrowAmount The amount of the underlying asset to borrow. * @param data Calldata passed to a router contract * @custom:security non-reentrant */ function borrow(address borrower, address receiver, uint256 borrowAmount, bytes calldata data) external returns (uint256); /** * @notice This low level function should be called from a periphery contract only * @notice Main function used to liquidate or flash liquidation positions. * @param borrower The address of the borrower being liquidated * @param receiver The address of the receiver of the collateral * @param repayAmount USD amount covering the loan * @param data Calldata passed to a router contract * @return usdAmount The amount of USD deposited after taking into account liq. incentive * @custom:security non-reentrant */ function liquidate(address borrower, address receiver, uint256 repayAmount, bytes calldata data) external returns (uint256 usdAmount); }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusBorrowControl.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusTerminal} from "./ICygnusTerminal.sol"; /** * @title ICygnusBorrowControl Interface for the control of borrow contracts (interest rate params, reserves, etc.) * @notice Admin contract for Cygnus Borrow contract 👽 */ interface ICygnusBorrowControl is ICygnusTerminal { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts when attempting to set a parameter outside the min/max ranges allowed in the Control contract * @custom:error ParameterNotInRange */ error CygnusBorrowControl__ParameterNotInRange(); /** * @dev Reverts when setting the collateral if the msg.sender is not the hangar18 contract * @custom:error MsgSenderNotHangar */ error CygnusBorrowControl__MsgSenderNotHangar(); /** * @dev Reverts wehn attempting to set a collateral that has already been set * @custom:error CollateralAlreadySet */ error CygnusBorrowControl__CollateralAlreadySet(); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when a new interest rate curve is set for this shuttle * @param baseRatePerYear The approximate target base APR, as a mantissa (scaled by 1e18) * @param multiplierPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18) * @param kinkMultiplier_ The increase to multiplier per year once kink utilization is reached * @param kinkUtilizationRate_ The rate at which the jump interest rate takes effect * custom:event NewInterestRateParameters */ event NewInterestRateParameters( uint256 baseRatePerYear, uint256 multiplierPerYear, uint256 kinkMultiplier_, uint256 kinkUtilizationRate_ ); /** * @dev Logs when a new rewarder contract is set to reward borrowers and lenders. * @param oldRewarder The address of the rewarder up until this point used for CYG distribution * @param newRewarder The address of the new rewarder * @custom:event NewCygnusBorrowRewarder */ event NewPillarsOfCreation(address oldRewarder, address newRewarder); /** * @dev Logs when a new reserve factor is set. * @param oldReserveFactor The reserve factor used in this shuttle until this point * @param newReserveFactor The new reserve factor set * @custom:event NewReserveFactor */ event NewReserveFactor(uint256 oldReserveFactor, uint256 newReserveFactor); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @custom:struct InterestRateModel The interest rate params for this pool * @custom:member baseRatePerSecond The base interest rate which is the y-intercept when utilization rate is 0 * @custom:member multiplierPerSecond The multiplier of utilization rate that gives the slope of the interest rate * @custom:member jumpMultiplierPerSecond The multiplierPerSecond after hitting a specified utilization point * @custom:member kink The utilization point at which the jump multiplier is applied */ struct InterestRateModel { uint64 baseRatePerSecond; uint64 multiplierPerSecond; uint64 jumpMultiplierPerSecond; uint64 kink; } /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @return The address of the Cygnus Collateral contract for this borrowable */ function collateral() external view returns (address); /** * @return Address that rewards borrowers and lenders in CYG */ function pillarsOfCreation() external view returns (address); /** * @notice The current interest rate params set for this pool * @return baseRatePerSecond The base interest rate which is the y-intercept when utilization rate is 0 * @return multiplierPerSecond The multiplier of utilization rate that gives the slope of the interest rate * @return jumpMultiplierPerSecond The multiplierPerSecond after hitting a specified utilization point * @return kink The utilization point at which the jump multiplier is applied */ function interestRateModel() external view returns (uint64 baseRatePerSecond, uint64 multiplierPerSecond, uint64 jumpMultiplierPerSecond, uint64 kink); /** * @return reserveFactor Percentage of interest that is routed to this market's Reserve Pool */ function reserveFactor() external view returns (uint256); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Admin 👽 * @notice Updates the interest rate model with annualized rates, scaled by 1e18 (ie 1% = 0.01e18) * @param baseRatePerYear The new annualized base rate * @param multiplierPerYear The new annualized rate of increase in interest rate wrt utilization * @param kinkMultiplier_ The increase to the slope once kink utilization is reached * @param kinkUtilizationRate_ The new max utilization rate * @custom:security only-admin */ function setInterestRateModel( uint256 baseRatePerYear, uint256 multiplierPerYear, uint256 kinkMultiplier_, uint256 kinkUtilizationRate_ ) external; /** * @notice Admin 👽 * @notice Updates the reserve factor * @param newReserveFactor The new reserve factor for this shuttle * @custom:security only-admin */ function setReserveFactor(uint256 newReserveFactor) external; /** * @notice Admin 👽 * @notice Updates the pillars of creation contract. This can be updated to the zero address in case we need * to remove rewards from pools saving us gas instead of calling the contract. * @param newPillars The address of the new CYG rewarder * @custom:security only-admin */ function setPillarsOfCreation(address newPillars) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusBorrowModel.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusBorrowControl} from "./ICygnusBorrowControl.sol"; /** * @title ICygnusBorrowModel Interface of the contract that implements the interest rate model and interest accruals */ interface ICygnusBorrowModel is ICygnusBorrowControl { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when interest is accrued to borrows and reserves * @param cash Total balance of the underlying in the strategy * @param borrows Latest total borrows stored * @param interest Interest accumulated since last accrual * @param reserves The amount of CygUSD minted to the DAO * @custom:event AccrueInterest */ event AccrueInterest(uint256 cash, uint256 borrows, uint256 interest, uint256 reserves); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @custom:struct BorrowSnapshot Container for individual user's borrow balance information * @custom:member principal The total borrowed amount without interest accrued * @custom:member interestIndex Borrow index as of the most recent balance-changing action */ struct BorrowSnapshot { uint128 principal; uint128 interestIndex; } /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @return Total borrows of the lending pool (uses borrow indices to simulate interest rate accruals) */ function totalBorrows() external view returns (uint256); /** * @return Borrow index stored of this lending pool (uses borrow indices) */ function borrowIndex() external view returns (uint256); /** * @return The unix timestamp stored of the last interest rate accrual */ function lastAccrualTimestamp() external view returns (uint256); /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @return The price of the denomination token in 18 decimals, used for reporting purposes only */ function getUsdPrice() external view returns (uint256); /** * @return The total amount of borrowed funds divided by the total vault assets */ function utilizationRate() external view returns (uint256); /** * @return The current per-second borrow rate */ function borrowRate() external view returns (uint256); /** * @return The current per-second supply rate */ function supplyRate() external view returns (uint256); /** * @notice Function used to get the borrow balance of users and their principal. * @param borrower The address whose balance should be calculated * @return principal The stablecoin amount borrowed without interests * @return borrowBalance The stablecoin amount borrowed with interests (ie. what borrowers must pay back) */ function getBorrowBalance(address borrower) external view returns (uint256 principal, uint256 borrowBalance); /** * @notice Gets the lender`s full position * @param lender The address of the lender * @return usdBalance The amount of stablecoins that the lender owns * @return positionInUsd The position of the lender in USD */ function getLenderPosition(address lender) external view returns (uint256 usdBalance, uint256 positionInUsd); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Applies interest accruals to borrows and reserves */ function accrueInterest() external; /** * @notice Manually track the user's CygUSD shares to pass to the rewarder contract * @param lender The address of the lender */ function trackLender(address lender) external; /** * @notice Manually track the user's borrows * @param borrower The address of the borrower */ function trackBorrower(address borrower) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusBorrowVoid.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusBorrowModel} from "./ICygnusBorrowModel.sol"; /** * @title ICygnusBorrowVoid * @notice Interface for `CygnusBorrowVoid` which is in charge of connecting the stablecoin Token with * a specified strategy (for example connect to a rewarder contract to stake the USDC, etc.) */ interface ICygnusBorrowVoid is ICygnusBorrowModel { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts if msg.sender is not the harvester * * @custom:error OnlyHarvesterAllowed */ error CygnusBorrowVoid__OnlyHarvesterAllowed(); /** * @dev Reverts if the token we are sweeping is underlying * * @custom:error TokenIsUnderlying */ error CygnusBorrowVoid__TokenIsUnderlying(); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when the strategy is first initialized or re-approves contracts * * @param underlying The address of the underlying LP * @param shuttleId The unique ID of the lending pool * @param whitelisted The contract we approved to use our underlying * * @custom:event ChargeVoid */ event ChargeVoid(address underlying, uint256 shuttleId, address whitelisted); /** * @dev Logs when rewards are harvested * * @param sender The address of the caller who harvested the rewards * @param tokens Total reward tokens harvested * @param amounts Amounts of reward tokens harvested * @param timestamp The timestamp of the harvest * * @custom:event RechargeVoid */ event RechargeVoid(address indexed sender, address[] tokens, uint256[] amounts, uint256 timestamp); /** * @dev Logs when admin sets a new harvester to reinvest rewards * * @param oldHarvester The address of the old harvester * @param newHarvester The address of the new harvester * @param rewardTokens The reward tokens added for the new harvester * * @custom:event NewHarvester */ event NewHarvester(address oldHarvester, address newHarvester, address[] rewardTokens); /** * @dev Logs when admin sets a new reward token for the harvester (if needed) * * @param _token Address of the token we are allowing the harvester to move * @param _harvester Address of the harvester * * @custom:event NewBonusHarvesterToken */ event NewBonusHarvesterToken(address _token, address _harvester); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @return harvester The address of the harvester contract */ function harvester() external view returns (address); /** * @return lastHarvest Timestamp of the last reinvest performed by the harvester contract */ function lastHarvest() external view returns (uint256); /** * @notice Array of reward tokens for this pool * @param index The index of the token in the array * @return rewardToken The reward token */ function allRewardTokens(uint256 index) external view returns (address rewardToken); /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @return rewarder The address of the rewarder contract */ function rewarder() external view returns (address); /** * @return rewardTokensLength Length of reward tokens */ function rewardTokensLength() external view returns (uint256); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Get the pending rewards manually - helpful to get rewards through static calls * * @return tokens The addresses of the reward tokens earned by harvesting rewards * @return amounts The amounts of each token received * * @custom:security non-reentrant */ function getRewards() external returns (address[] memory tokens, uint256[] memory amounts); /** * @notice Only the harvester can reinvest * @notice Reinvests all rewards from the rewarder to buy more USD to then deposit back into the rewarder * This makes underlying balance increase in this contract, increasing the exchangeRate between * CygUSD and underlying and thus lowering utilization rate and borrow rate * * @custom:security non-reentrant only-harvester */ function reinvestRewards_y7b(uint256 liquidity) external; /** * @notice Admin 👽 * @notice Charges approvals needed for deposits and withdrawals, and any other function * needed to get the vault started. ie, setting a pool ID from a MasterChef, a gauge, etc. * * @custom:security only-admin */ function chargeVoid() external; /** * @notice Admin 👽 * @notice Sets the harvester address to harvest and reinvest rewards into more underlying * * @param _harvester The address of the new harvester contract * @param rewardTokens Array of reward tokens * * @custom:security only-admin */ function setHarvester(address _harvester, address[] calldata rewardTokens) external; /** * @notice Admin 👽 * @notice Sweeps a token that was sent to this address by mistake, or a bonus reward token we are not tracking. Cannot * sweep the underlying USD or USD LP token (like Comp USDC, etc.) * * @custom:security only-admin */ function sweepToken(address token, address to) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusCollateral.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusCollateralVoid} from "./ICygnusCollateralVoid.sol"; /** * @title ICygnusCollateral * @notice Interface for the main collateral contract which handles collateral seizes and flash redeems */ interface ICygnusCollateral is ICygnusCollateralVoid { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts when the user doesn't have enough liquidity to redeem * @custom:error InsufficientLiquidity */ error CygnusCollateral__InsufficientLiquidity(); /** * @dev Reverts when the msg.sender of the liquidation is not this contract`s borrowable * @custom:error MsgSenderNotBorrowable */ error CygnusCollateral__MsgSenderNotBorrowable(); /** * @dev Reverts when the repayAmount in a liquidation is 0 * @custom:error CantLiquidateZero */ error CygnusCollateral__CantLiquidateZero(); /** * @dev Reverts when trying to redeem 0 tokens * @custom:error CantRedeemZero */ error CygnusCollateral__CantRedeemZero(); /** * @dev Reverts when liquidating an account that has no shortfall * @custom:error NotLiquidatable */ error CygnusCollateral__NotLiquidatable(); /** * @dev Reverts when redeeming more shares than CygLP in this contract * @custom:error InsufficientRedeemAmount */ error CygnusCollateral__InsufficientCygLPReceived(); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when collateral is seized from the borrower and sent to the liquidator * @param liquidator The address of the liquidator * @param borrower The address of the borrower being liquidated * @param cygLPAmount The amount of CygLP seized without taking into account incentive or fee * @param liquidatorAmount The aamount of CygLP seized sent to the liquidator (with the liq. incentive) * @param daoFee The amount of CygLP sent to the DAO Reserves * @param totalSeized The total amount of CygLP seized from the borrower */ event SeizeCygLP( address indexed liquidator, address indexed borrower, uint256 cygLPAmount, uint256 liquidatorAmount, uint256 daoFee, uint256 totalSeized ); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Seizes CygLP from borrower and adds it to the liquidator's account. This function can only be called * from the borrowable contract, else it reverts. * @param liquidator The address repaying the borrow amount and seizing the collateral * @param borrower The address of the borrower * @param repayAmount The number of collateral tokens to seize * @return liquidatorAmount The amount of CygLP that the liquidator received for liquidating the position * @custom:security non-reentrant */ function seizeCygLP(address liquidator, address borrower, uint256 repayAmount) external returns (uint256 liquidatorAmount); /** * @notice Flash redeems the underlying LP Token - Low level function which should only be called by a router. * @param redeemer The address redeeming the tokens (Altair contract) * @param assets The amount of the underlying assets to redeem * @param data Calldata passed from and back to router contract * @custom:security non-reentrant */ function flashRedeemAltair(address redeemer, uint256 assets, bytes calldata data) external returns (uint256 usdAmount); }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusCollateralControl.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusTerminal} from "./ICygnusTerminal.sol"; /** * @title ICygnusCollateralControl Interface for the admin control of collateral contracts (incentives, debt ratios) * @notice Admin contract for Cygnus Collateral contract 👽 */ interface ICygnusCollateralControl is ICygnusTerminal { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts when attempting to set a parameter outside the min/max ranges allowed in the Control contract * @custom:error ParameterNotInRange */ error CygnusCollateralControl__ParameterNotInRange(); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when the max debt ratio is updated for this shuttle * @param oldDebtRatio The old debt ratio at which the collateral was liquidatable in this shuttle * @param newDebtRatio The new debt ratio for this shuttle * @custom:event NewDebtRatio */ event NewDebtRatio(uint256 oldDebtRatio, uint256 newDebtRatio); /** * @dev Logs when a new liquidation incentive is set for liquidators * @param oldLiquidationIncentive The old incentive for liquidators taken from the collateral * @param newLiquidationIncentive The new liquidation incentive for this shuttle * @custom:event NewLiquidationIncentive */ event NewLiquidationIncentive(uint256 oldLiquidationIncentive, uint256 newLiquidationIncentive); /** * @dev Logs when a new liquidation fee is set, which the protocol keeps from each liquidation * @param oldLiquidationFee The previous fee the protocol kept as reserves from each liquidation * @param newLiquidationFee The new liquidation fee for this shuttle * @custom:event NewLiquidationFee */ event NewLiquidationFee(uint256 oldLiquidationFee, uint256 newLiquidationFee); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @return The address of the Cygnus Borrow contract for this collateral */ function borrowable() external view returns (address); /** * @return The current max debt ratio for this shuttle, after which positions become liquidatable */ function debtRatio() external view returns (uint256); /** * @return The current liquidation incentive for this shuttle */ function liquidationIncentive() external view returns (uint256); /** * @return The current liquidation fee the protocol keeps from each liquidation */ function liquidationFee() external view returns (uint256); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 5. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Admin 👽 * @notice Updates the shuttle's debt ratio * @param newDebtRatio The new max debt ratio at which positions become liquidatable. * @custom:security only-admin */ function setDebtRatio(uint256 newDebtRatio) external; /** * @notice Admin 👽 * @notice Updates the liquidation bonus for liquidators * @param newLiquidationIncentive The new incentive that the liquidators receive for liquidating positions. * @custom:security only-admin */ function setLiquidationIncentive(uint256 newLiquidationIncentive) external; /** * @notice Admin 👽 * @notice Updates the liquidation fee that the protocol keeps for every liquidation * @param newLiquidationFee The new fee that the protocol keeps from every liquidation. * @custom:security only-admin */ function setLiquidationFee(uint256 newLiquidationFee) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusCollateralModel.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusCollateralControl} from "./ICygnusCollateralControl.sol"; /** * @title ICygnusCollateralModel The contract that implements the collateralization model */ interface ICygnusCollateralModel is ICygnusCollateralControl { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts when the borrower is the zero address or this collateral * @custom:error InvalidBorrower */ error CygnusCollateralModel__InvalidBorrower(); /** * @dev Reverts when the price returned from the oracle is 0 * @custom:error PriceCantBeZero */ error CygnusCollateralModel__PriceCantBeZero(); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @notice Checks if the given user is able to redeem the specified amount of LP tokens. * @param borrower The address of the user to check. * @param redeemAmount The amount of LP tokens to be redeemed. * @return True if the user can redeem, false otherwise. * */ function canRedeem(address borrower, uint256 redeemAmount) external view returns (bool); /** * @notice Get the price of 1 amount of the underlying in stablecoins. Note: It returns the price in the borrowable`s * decimals. ie If USDC, returns price in 6 deicmals, if DAI/BUSD in 18 * @notice Calls the oracle to return the price of 1 unit of the underlying LP Token of this shuttle * @return The price of 1 LP Token denominated in the Borrowable's underlying stablecoin */ function getLPTokenPrice() external view returns (uint256); /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Gets an account's liquidity or shortfall * @param borrower The address of the borrower * @return liquidity The account's liquidity denominated in the borrowable's underlying stablecoin. * @return shortfall The account's shortfall denominated in the borrowable's underlying stablecoin. If positive * then the account can be liquidated. */ function getAccountLiquidity(address borrower) external view returns (uint256 liquidity, uint256 shortfall); /** * @notice Check if a borrower can borrow a specified amount of stablecoins from the borrowable contract. * @param borrower The address of the borrower * @param borrowAmount The amount of stablecoins that borrower wants to borrow. * @return A boolean indicating whether the borrower can borrow the specified amount */ function canBorrow(address borrower, uint256 borrowAmount) external view returns (bool); /** * @notice Quick view function to get a borrower's latest position * @param borrower The address of the borrower * @return lpBalance The borrower`s position in LP Tokens * @return positionUsd The borrower's position in USD (ie. CygLP Balance * Exchange Rate * LP Token Price) * @return health The user's current loan health (once it reaches 100% the user becomes liquidatable) */ function getBorrowerPosition(address borrower) external view returns (uint256 lpBalance, uint256 positionUsd, uint256 health); }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusCollateralVoid.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {ICygnusCollateralModel} from "./ICygnusCollateralModel.sol"; /** * @title ICygnusCollateralVoid * @notice Interface for `CygnusCollateralVoid` which is in charge of connecting the collateral LP Tokens with * a specified strategy (for example connect to a rewarder contract to stake the LP Token, etc.) */ interface ICygnusCollateralVoid is ICygnusCollateralModel { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts if msg.sender is not the harvester * * @custom:error OnlyHarvesterAllowed */ error CygnusCollateralVoid__OnlyHarvesterAllowed(); /** * @dev Reverts if the token we are sweeping is the underlying LP * * @custom:error TokenIsUnderlying */ error CygnusCollateralVoid__TokenIsUnderlying(); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when the strategy is first initialized or re-approves contracts * * @param underlying The address of the underlying stablecoin * @param shuttleId The unique ID of the lending pool * @param whitelisted The contract we approved to use our underlying * * @custom:event ChargeVoid */ event ChargeVoid(address underlying, uint256 shuttleId, address whitelisted); /** * @dev Logs when rewards are harvested * * @param sender The address of the caller who harvested the rewards * @param tokens Total reward tokens harvested * @param amounts Amounts of reward tokens harvested * @param timestamp The timestamp of the harvest * * @custom:event RechargeVoid */ event RechargeVoid(address indexed sender, address[] tokens, uint256[] amounts, uint256 timestamp); /** * @dev Logs when admin sets a new harvester to reinvest rewards * * @param oldHarvester The address of the old harvester * @param newHarvester The address of the new harvester * @param rewardTokens The reward tokens added for the new harvester * * @custom:event NewHarvester */ event NewHarvester(address oldHarvester, address newHarvester, address[] rewardTokens); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @return harvester The address of the harvester contract */ function harvester() external view returns (address); /** * @return lastHarvest Timestamp of the last harvest performed by the harvester contract */ function lastHarvest() external view returns (uint256); /** * @notice Array of reward tokens for this pool * @param index The index of the token in the array * @return rewardToken The reward token */ function allRewardTokens(uint256 index) external view returns (address rewardToken); /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @return rewarder The address of the rewarder contract */ function rewarder() external view returns (address); /** * @return rewardTokensLength Length of reward tokens */ function rewardTokensLength() external view returns (uint256); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Get the pending rewards manually - helpful to get rewards through static calls * * @return tokens The addresses of the reward tokens earned by harvesting rewards * @return amounts The amounts of each token received * * @custom:security non-reentrant */ function getRewards( address[] calldata _tokens, uint256[] calldata _amounts, bytes32[][] calldata _proofs ) external returns (address[] memory tokens, uint256[] memory amounts); /** * @notice Only the harvester can reinvest * @notice Reinvests all rewards from the rewarder to buy more LP to then deposit back into the rewarder * This makes underlying LP balance increase in this contract, increasing the exchangeRate between * CygLP and underlying and thus lowering debt ratio for all borrwers in the pool as they own more LP. * * @custom:security non-reentrant only-harvester */ function reinvestRewards_y7b(uint256 liquidity) external; /** * @notice Admin 👽 * @notice Charges approvals needed for deposits and withdrawals, and any other function * needed to get the vault started. ie, setting a pool ID from a MasterChef, a gauge, etc. * * @custom:security only-admin */ function chargeVoid() external; /** * @notice Admin 👽 * @notice Sets the harvester address to harvest and reinvest rewards into more underlying * * @param _harvester The address of the new harvester contract * * @custom:security only-admin */ function setHarvester(address _harvester, address[] calldata rewardTokens) external; /** * @notice Admin 👽 * @notice Sweeps a token that was sent to this address by mistake, or a bonus reward token we are not tracking. Cannot * sweep the underlying LP * * @custom:security only-admin */ function sweepToken(address token, address to) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // CygnusNebulaOracle.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity ^0.8.17; // Interfaces import {AggregatorV3Interface} from "./AggregatorV3Interface.sol"; import {IERC20} from "./IERC20.sol"; /** * @title ICygnusNebula Interface to interact with Cygnus' LP Oracle * @author CygnusDAO */ interface ICygnusNebula { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts when attempting to initialize an already initialized LP Token * * @param lpTokenPair The address of the LP Token we are initializing * * @custom:error PairIsInitialized */ error CygnusNebulaOracle__PairAlreadyInitialized(address lpTokenPair); /** * @dev Reverts when attempting to get the price of an LP Token that is not initialized * * @param lpTokenPair THe address of the LP Token we are getting the price for * * @custom:error PairNotInitialized */ error CygnusNebulaOracle__PairNotInitialized(address lpTokenPair); /** * @dev Reverts when attempting to access admin only methods * * @param sender The address of msg.sender * * @custom:error MsgSenderNotAdmin */ error CygnusNebulaOracle__MsgSenderNotAdmin(address sender); /** * @dev Reverts when attempting to set the admin if the pending admin is the zero address * * @param pendingAdmin The address of the pending oracle admin * * @custom:error AdminCantBeZero */ error CygnusNebulaOracle__AdminCantBeZero(address pendingAdmin); /** * @dev Reverts when attempting to set the same pending admin twice * * @param pendingAdmin The address of the pending oracle admin * * @custom:error PendingAdminAlreadySet */ error CygnusNebulaOracle__PendingAdminAlreadySet(address pendingAdmin); /** * @dev Reverts when getting a record if not initialized * * @param lpTokenPair The address of the LP Token for the record * * @custom:error NebulaRecordNotInitialized */ error CygnusNebulaOracle__NebulaRecordNotInitialized(address lpTokenPair); /** * @dev Reverts when re-initializing a record * * @param lpTokenPair The address of the LP Token for the record * * @custom:error NebulaRecordAlreadyInitialized */ error CygnusNebulaOracle__NebulaRecordAlreadyInitialized(address lpTokenPair); /** * @dev Reverts when the price of an initialized `lpTokenPair` is 0 * * @param lpTokenPair The address of the LP Token for the record * * @custom:error NebulaRecordAlreadyInitialized */ error CygnusNebulaOracle__PriceCantBeZero(address lpTokenPair); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when an LP Token pair's price starts being tracked * * @param initialized Whether or not the LP Token is initialized * @param oracleId The ID for this oracle * @param lpTokenPair The address of the LP Token * @param poolTokens The addresses of the tokens for this LP Token * @param poolTokensDecimals The decimals of each pool token * @param priceFeeds The addresses of the price feeds for the tokens * @param priceFeedsDecimals The decimals of each price feed * * @custom:event InitializeCygnusNebula */ event InitializeNebulaOracle( bool initialized, uint88 oracleId, address lpTokenPair, IERC20[] poolTokens, uint256[] poolTokensDecimals, AggregatorV3Interface[] priceFeeds, uint256[] priceFeedsDecimals ); /** * @dev Logs when a new pending admin is set, to be accepted by admin * * @param oracleCurrentAdmin The address of the current oracle admin * @param oraclePendingAdmin The address of the pending oracle admin * * @custom:event NewNebulaPendingAdmin */ event NewOraclePendingAdmin(address oracleCurrentAdmin, address oraclePendingAdmin); /** * @dev Logs when the pending admin is confirmed as the new oracle admin * * @param oracleOldAdmin The address of the old oracle admin * @param oracleNewAdmin The address of the new oracle admin * * @custom:event NewNebulaAdmin */ event NewOracleAdmin(address oracleOldAdmin, address oracleNewAdmin); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @notice The struct record of each oracle used by Cygnus * @custom:member initialized Whether an LP Token is being tracked or not * @custom:member oracleId The ID of the LP Token tracked by the oracle * @custom:member name User friendly name of the underlying * @custom:member underlying The address of the LP Token * @custom:member poolTokens Array of all the pool tokens * @custom:member poolTokensDecimals Array of the decimals of each pool token * @custom:member priceFeeds Array of all the Chainlink price feeds for the pool tokens * @custom:member priceFeedsDecimals Array of the decimals of each price feed */ struct NebulaOracle { bool initialized; uint88 oracleId; string name; address underlying; IERC20[] poolTokens; uint256[] poolTokensDecimals; AggregatorV3Interface[] priceFeeds; uint256[] priceFeedsDecimals; } /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @notice Returns the struct record of each oracle used by Cygnus * * @param lpTokenPair The address of the LP Token * @return nebulaOracle Struct of the oracle for the LP Token */ function getNebulaOracle(address lpTokenPair) external view returns (NebulaOracle memory nebulaOracle); /** * @notice Gets the address of the LP Token that (if) is being tracked by this oracle * * @param id The ID of each LP Token that is being tracked by this oracle * @return The address of the LP Token if it is being tracked by this oracle, else returns address zero */ function allNebulas(uint256 id) external view returns (address); /** * @return The name for this Cygnus-Chainlink Nebula oracle */ function name() external view returns (string memory); /** * @return The address of the Cygnus admin */ function admin() external view returns (address); /** * @return The address of the new requested admin */ function pendingAdmin() external view returns (address); /** * @return The version of this oracle */ function version() external view returns (string memory); /** * @return SECONDS_PER_YEAR The number of seconds in year assumed by the oracle */ function SECONDS_PER_YEAR() external view returns (uint256); /** * @notice We use a constant to set the chainlink aggregator decimals. As stated by chainlink all decimals for tokens * denominated in USD are 8 decimals. And all decimals for tokens denominated in ETH are 18 decimals. We use * tokens denominated in USD, so we set the constant to 8 decimals. * @return AGGREGATOR_DECIMALS The decimals used by Chainlink (8 for all tokens priced in USD, 18 for priced in ETH) */ function AGGREGATOR_DECIMALS() external pure returns (uint256); /** * @return The scalar used to price the token in 18 decimals (ie. 10 ** (18 - AGGREGATOR_DECIMALS)) */ function AGGREGATOR_SCALAR() external pure returns (uint256); /** * @return How many LP Token pairs' prices are being tracked by this oracle */ function nebulaSize() external view returns (uint88); /** * @return The denomination token this oracle returns the price in */ function denominationToken() external view returns (IERC20); /** * @return The decimals for this Cygnus-Chainlink Nebula oracle */ function decimals() external view returns (uint8); /** * @return The address of Chainlink's denomination oracle */ function denominationAggregator() external view returns (AggregatorV3Interface); /** * @return nebulaRegistry The address of the nebula registry */ function nebulaRegistry() external view returns (address); /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @return The price of the denomination token in oracle decimals */ function denominationTokenPrice() external view returns (uint256); /** * @notice Get the APR given 2 exchange rates and the time elapsed between them. This is helpful for tokens * that meet x*y=k such as UniswapV2 since exchange rates should never decrease (else LPs lose cash). * Uses the natural log to avoid overflowing when we annualize the log difference. * * @param exchangeRateLast The previous exchange rate * @param exchangeRateNow The current exchange rate * @param timeElapsed Time elapsed between the exchange rates * @return apr The estimated base rate (APR excluding any token rewards) */ function getAnnualizedBaseRate( uint256 exchangeRateLast, uint256 exchangeRateNow, uint256 timeElapsed ) external pure returns (uint256 apr); /** * @notice Gets the latest price of the LP Token denominated in denomination token * @notice LP Token pair must be initialized, else reverts with custom error * * @param lpTokenPair The address of the LP Token * @return lpTokenPrice The price of the LP Token denominated in denomination token */ function lpTokenPriceUsd(address lpTokenPair) external view returns (uint256 lpTokenPrice); /** * @notice Gets the latest price of the LP Token's token0 and token1 denominated in denomination token * @notice Used by Cygnus Altair contract to calculate optimal amount of leverage * * @param lpTokenPair The address of the LP Token * @return Array of the LP's asset prices */ function assetPricesUsd(address lpTokenPair) external view returns (uint256[] memory); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Admin 👽 * @notice Initialize an LP Token pair, only admin * * @param lpTokenPair The contract address of the LP Token * @param aggregators Array of Chainlink aggregators for this LP token's tokens * * @custom:security non-reentrant only-admin */ function initializeNebulaOracle(address lpTokenPair, AggregatorV3Interface[] calldata aggregators) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // ICygnusTerminal.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Dependencies import {IERC20Permit} from "./IERC20Permit.sol"; // Interfaces import {IHangar18} from "./IHangar18.sol"; import {IAllowanceTransfer} from "./IAllowanceTransfer.sol"; import {ICygnusNebula} from "./ICygnusNebula.sol"; /** * @title ICygnusTerminal * @notice The interface to mint/redeem pool tokens (CygLP and CygUSD) */ interface ICygnusTerminal is IERC20Permit { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts when attempting to mint zero shares * @custom:error CantMintZeroShares */ error CygnusTerminal__CantMintZeroShares(); /** * @dev Reverts when attempting to redeem zero assets * @custom:error CantBurnZeroAssets */ error CygnusTerminal__CantRedeemZeroAssets(); /** * @dev Reverts when attempting to call Admin-only functions * @custom:error MsgSenderNotAdmin */ error CygnusTerminal__MsgSenderNotAdmin(); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when totalBalance syncs with the underlying contract's balanceOf. * @param totalBalance Total balance in terms of the underlying * @custom:event Sync */ event Sync(uint160 totalBalance); /** * @dev Logs when CygLP or CygUSD pool tokens are minted * @param sender The address of `CygnusAltair` or the sender of the function call * @param recipient Address of the minter * @param assets Amount of assets being deposited * @param shares Amount of pool tokens being minted * @custom:event Mint */ event Deposit(address indexed sender, address indexed recipient, uint256 assets, uint256 shares); /** * @dev Logs when CygLP or CygUSD are redeemed * @param sender The address of the redeemer of the shares * @param recipient The address of the recipient of assets * @param owner The address of the owner of the pool tokens * @param assets The amount of assets to redeem * @param shares The amount of pool tokens burnt * @custom:event Redeem */ event Withdraw(address indexed sender, address indexed recipient, address indexed owner, uint256 assets, uint256 shares); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @return Address of the Permit2 router on this chain. We use the AllowanceTransfer instead of SignatureTransfer * to allow deposits from other smart contracts. * */ function PERMIT2() external view returns (IAllowanceTransfer); /** * @return The address of the Cygnus Factory contract used to deploy this shuttle */ function hangar18() external view returns (IHangar18); /** * @return The address of the underlying asset (stablecoin for Borrowable, LP Token for collateral) */ function underlying() external view returns (address); /** * @return The address of the oracle for this lending pool */ function nebula() external view returns (ICygnusNebula); /** * @return The unique ID of the lending pool, shared by Borrowable and Collateral */ function shuttleId() external view returns (uint256); /** * @return Total available cash deposited in the strategy (stablecoin for Borrowable, LP Token for collateral) */ function totalBalance() external view returns (uint160); /** * @return The total assets owned by the vault. Same as total balance, but includes total borrows for Borrowable. */ function totalAssets() external view returns (uint256); /** * @return The exchange rate between 1 vault share (CygUSD/CygLP) and the underlying asset */ function exchangeRate() external view returns (uint256); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Deposits the underlying asset into the vault (stablecoins for borrowable, LP tokens for collateral). * Users must approve the Permit2 router in the underlying before depositing. Users can bypass * the permit and signature arguments by also approving the vault contract in the Permit2 router * and pass an empty permit and signature. * @param assets Amount of the underlying asset to deposit. * @param recipient Address that will receive the corresponding amount of shares. * @param _permit Data signed over by the owner specifying the terms of approval * @param _signature The owner's signature over the permit data * @return shares Amount of Cygnus Vault shares minted and transferred to the `recipient`. * @custom:security non-reentrant */ function deposit( uint256 assets, address recipient, IAllowanceTransfer.PermitSingle calldata _permit, bytes calldata _signature ) external returns (uint256 shares); /** * @notice Redeems vault shares and transfers out assets (stablecoins for borrowable, LP tokens for collateral). * @param shares The number of shares to redeem for the underlying asset. * @param recipient The address that will receive the underlying asset. * @param owner The address that owns the shares. * @return assets The amount of underlying assets received by the `recipient`. * @custom:security non-reentrant */ function redeem(uint256 shares, address recipient, address owner) external returns (uint256 assets); /** * @notice Syncs `totalBalance` in terms of its underlying * @custom:security non-reentrant */ function sync() external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // IDenebOrbiter.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; /** * @title ICygnusDeneb The interface for a contract that is capable of deploying Cygnus collateral pools * @notice A contract that constructs a Cygnus collateral pool must implement this to pass arguments to the pool */ interface IDenebOrbiter { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @notice Passing the struct parameters to the collateral contract avoids setting constructor * @return factory The address of the Cygnus factory * @return underlying The address of the underlying LP Token * @return borrowable The address of the Cygnus borrow contract for this collateral * @return oracle The address of the oracle for this lending pool * @return shuttleId The ID of the lending pool */ function shuttleParameters() external returns (address factory, address underlying, address borrowable, address oracle, uint256 shuttleId); /** * @return The init code hash of the collateral contract for this deployer */ function collateralInitCodeHash() external view returns (bytes32); /** * @notice Function to deploy the collateral contract of a lending pool * @param underlying The address of the underlying LP Token * @param borrowable The address of the Cygnus borrow contract for this collateral * @param oracle The address of the oracle for this lending pool * @param shuttleId The ID of the lending pool * @return collateral The address of the new deployed Cygnus collateral contract */ function deployDeneb(address underlying, address borrowable, address oracle, uint256 shuttleId) external returns (address collateral); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity >=0.8.17; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity >=0.8.17; import {IERC20} from "./IERC20.sol"; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit is IERC20 { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. */ function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: AGPL-3.0-or-later // // IHangar18.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; // Orbiters import {IDenebOrbiter} from "./IDenebOrbiter.sol"; import {IAlbireoOrbiter} from "./IAlbireoOrbiter.sol"; // Oracles /** * @title The interface for the Cygnus Factory * @notice The Cygnus factory facilitates creation of collateral and borrow pools */ interface IHangar18 { /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 1. CUSTOM ERRORS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Reverts when caller is not Admin * * @param sender The address of the account that invoked the function and caused the error * @param admin The address of the Admin that is allowed to perform the function * * @custom:error CygnusAdminOnly */ error Hangar18__CygnusAdminOnly(address sender, address admin); /** * @dev Reverts when the orbiter pair already exists * * @custom:error OrbitersAlreadySet */ error Hangar18__OrbitersAlreadySet(); /** * @dev Reverts when trying to deploy a shuttle that already exists * * @custom:error ShuttleAlreadyDeployed */ error Hangar18__ShuttleAlreadyDeployed(); /** * @dev Reverts when deploying a shuttle with orbiters that are inactive or dont exist * * @custom:error OrbitersAreInactive */ error Hangar18__OrbitersAreInactive(); /** * @dev Reverts when predicted collateral address doesn't match with deployed * * @custom:error CollateralAddressMismatch */ error Hangar18__CollateralAddressMismatch(); /** * @dev Reverts when trying to deploy a shuttle with an unsupported LP Pair * * @custom:error LiquidityTokenNotSupported */ error Hangar18__LiquidityTokenNotSupported(); /** * @dev Reverts when the CYG rewarder contract is zero * * @custom:error PillarsCantBeZero */ error Hangar18__PillarsCantBeZero(); /** * @dev Reverts when the CYG rewarder contract is zero * * @custom:error PillarsCantBeZero */ error Hangar18__AltairCantBeZero(); /** * @dev Reverts when the oracle set is the same as the new one we are assigning * * @param priceOracle The address of the existing price oracle * @param newPriceOracle The address of the new price oracle that was attempted to be set * * @custom:error CygnusNebulaAlreadySet */ error Hangar18__CygnusNebulaAlreadySet(address priceOracle, address newPriceOracle); /** * @dev Reverts when the admin is the same as the new one we are assigning * * @custom:error AdminAlreadySet */ error Hangar18__AdminAlreadySet(); /** * @dev Reverts when the pending admin is the same as the new one we are assigning * * @param newPendingAdmin The address of the new pending admin * @param pendingAdmin The address of the existing pending admin * * @custom:error PendingAdminAlreadySet */ error Hangar18__PendingAdminAlreadySet(address newPendingAdmin, address pendingAdmin); /** * @dev Reverts when the pending dao reserves is already the dao reserves * * @custom:error DaoReservesAlreadySet */ error Hangar18__DaoReservesAlreadySet(); /** * @dev Reverts when the pending address is the same as the new pending * * @custom:error PendingDaoReservesAlreadySet */ error Hangar18__PendingDaoReservesAlreadySet(); /** * @dev Reverts when msg.sender is not the pending admin * * @custom:error SenderNotPendingAdmin */ error Hangar18__SenderNotPendingAdmin(); /** * @dev Reverts when pending reserves contract address is the zero address * * @custom:error DaoReservesCantBeZero */ error Hangar18__DaoReservesCantBeZero(); /** * @dev Reverts when setting a new vault as the 0 address * * @custom:error X1VaultCantBeZero */ error Hangar18__X1VaultCantBeZero(); /** * @dev Reverts when deploying a pool with an inactive orbiter * * @custom:error OrbiterInactive */ error Hangar18__OrbiterInactive(); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 2. CUSTOM EVENTS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /** * @dev Logs when a new lending pool is launched * * @param lpTokenPair The address of the LP Token pair * @param orbiterId The ID of the orbiter used to deploy this lending pool * @param shuttleId The ID of the lending pool * @param borrowable The address of the Cygnus borrow contract * @param collateral The address of the Cygnus collateral contract * * @custom:event NewShuttle */ event NewShuttle(address indexed lpTokenPair, uint256 indexed orbiterId, uint256 shuttleId, address borrowable, address collateral); /** * @dev Logs when a new Cygnus admin is requested * * @param pendingAdmin Address of the requested admin * @param _admin Address of the present admin * * @custom:event NewPendingCygnusAdmin */ event NewPendingCygnusAdmin(address pendingAdmin, address _admin); /** * @dev Logs when a new Cygnus admin is confirmed * * @param oldAdmin Address of the old admin * @param newAdmin Address of the new confirmed admin * * @custom:event NewCygnusAdmin */ event NewCygnusAdmin(address oldAdmin, address newAdmin); /** * @dev Logs when a new implementation contract is requested * * @param oldPendingdaoReservesContract Address of the current `daoReserves` contract * @param newPendingdaoReservesContract Address of the requested new `daoReserves` contract * * @custom:event NewPendingDaoReserves */ event NewPendingDaoReserves(address oldPendingdaoReservesContract, address newPendingdaoReservesContract); /** * @dev Logs when a new implementation contract is confirmed * * @param oldDaoReserves Address of old `daoReserves` contract * @param daoReserves Address of the new confirmed `daoReserves` contract * * @custom:event NewDaoReserves */ event NewDaoReserves(address oldDaoReserves, address daoReserves); /** * @dev Logs when a new pillars is confirmed * * @param oldPillars Address of old `pillars` contract * @param newPillars Address of the new pillars contract * * @custom:event NewPillarsOfCreation */ event NewPillarsOfCreation(address oldPillars, address newPillars); /** * @dev Logs when a new router is confirmed * * @param oldRouter Address of the old base router contract * @param newRouter Address of the new router contract * * @custom:event NewAltairRouter */ event NewAltairRouter(address oldRouter, address newRouter); /** * @dev Logs when orbiters are initialized in the factory * * @param status Whether or not these orbiters are active and usable * @param orbitersLength How many orbiter pairs we have (equals the amount of Dexes cygnus is using) * @param borrowOrbiter The address of the borrow orbiter for this dex * @param denebOrbiter The address of the collateral orbiter for this dex * @param orbitersName The name of the dex for these orbiters * @param uniqueHash The keccack256 hash of the collateral init code hash and borrowable init code hash * * @custom:event InitializeOrbiters */ event InitializeOrbiters( bool status, uint256 orbitersLength, IAlbireoOrbiter borrowOrbiter, IDenebOrbiter denebOrbiter, bytes32 uniqueHash, string orbitersName ); /** * @dev Logs when admins switch orbiters off for future deployments * * @param status Bool representing whether or not these orbiters are usable * @param orbiterId The ID of the collateral & borrow orbiters * @param albireoOrbiter The address of the deleted borrow orbiter * @param denebOrbiter The address of the deleted collateral orbiter * @param orbiterName The name of the dex these orbiters were for * * @custom:event SwitchOrbiterStatus */ event SwitchOrbiterStatus( bool status, uint256 orbiterId, IAlbireoOrbiter albireoOrbiter, IDenebOrbiter denebOrbiter, string orbiterName ); /** * @dev Logs when a new vault is set which accumulates rewards from lending pools * * @param oldVault The address of the old vault * @param newVault The address of the new vault * * @custom:event NewX1Vault */ event NewX1Vault(address oldVault, address newVault); /** * @dev Logs when an owner allows or disallows spender to borrow on their behalf * * @param owner The address of msg.sender (owner of the CygLP) * @param spender The address of the user the owner is allowing/disallowing * @param status Whether or not the spender can borrow after this transaction * * @custom:event NewMasterBorrowApproval */ event NewMasterBorrowApproval(address owner, address spender, bool status); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 3. CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── Internal ─────────────────────────────────────────────── */ /** * @custom:struct Official record of all collateral and borrow deployer contracts, unique per dex * @custom:member status Whether or not these orbiters are active and usable * @custom:member orbiterId The ID for this pair of orbiters * @custom:member albireoOrbiter The address of the borrow deployer contract * @custom:member denebOrbiter The address of the collateral deployer contract * @custom:member borrowableInitCodeHash The hash of the borrowable contract's initialization code * @custom:member collateralInitCodeHash The hash of the collateral contract's initialization code * @custom:member uniqueHash The unique hash of the orbiter * @custom:member orbiterName Huamn friendly name for the orbiters */ struct Orbiter { bool status; uint88 orbiterId; IAlbireoOrbiter albireoOrbiter; IDenebOrbiter denebOrbiter; bytes32 borrowableInitCodeHash; bytes32 collateralInitCodeHash; bytes32 uniqueHash; string orbiterName; } /** * @custom:struct Shuttle Official record of pools deployed by this factory * @custom:member launched Whether or not the lending pool is initialized * @custom:member shuttleId The ID of the lending pool * @custom:member borrowable The address of the borrowing contract * @custom:member collateral The address of the Cygnus collateral * @custom:member orbiterId The ID of the orbiters used to deploy lending pool */ struct Shuttle { bool launched; uint88 shuttleId; address borrowable; address collateral; uint96 orbiterId; } /* ─────────────────────────────────────────────── Public ──────────────────────────────────────────────── */ /** * @notice Array of structs containing all orbiters deployed * @param _orbiterId The ID of the orbiter pair * @return status Whether or not these orbiters are active and usable * @return orbiterId The ID for these orbiters (ideally should be 1 per dex) * @return albireoOrbiter The address of the borrow deployer contract * @return denebOrbiter The address of the collateral deployer contract * @return borrowableInitCodeHash The init code hash of the borrowable * @return collateralInitCodeHash The init code hash of the collateral * @return uniqueHash The keccak256 hash of collateralInitCodeHash and borrowableInitCodeHash * @return orbiterName The name of the dex */ function allOrbiters( uint256 _orbiterId ) external view returns ( bool status, uint88 orbiterId, IAlbireoOrbiter albireoOrbiter, IDenebOrbiter denebOrbiter, bytes32 borrowableInitCodeHash, bytes32 collateralInitCodeHash, bytes32 uniqueHash, string memory orbiterName ); /** * @notice Array of LP Token pairs deployed * @param _shuttleId The ID of the shuttle deployed * @return launched Whether this pair exists or not * @return shuttleId The ID of this shuttle * @return borrowable The address of the borrow contract * @return collateral The address of the collateral contract * @return orbiterId The ID of the orbiters used to deploy this lending pool */ function allShuttles( uint256 _shuttleId ) external view returns (bool launched, uint88 shuttleId, address borrowable, address collateral, uint96 orbiterId); /** * @notice Checks if a pair of orbiters has been added to the Hangar * @param orbiterHash The keccak hash of the creation code of each orbiter * @return Whether this par of orbiters has been added or not */ function orbitersExist(bytes32 orbiterHash) external view returns (bool); /** * @notice Official record of all lending pools deployed * @param _lpTokenPair The address of the LP Token * @param _orbiterId The ID of the orbiter for this LP Token * @return launched Whether this pair exists or not * @return shuttleId The ID of this shuttle * @return borrowable The address of the borrow contract * @return collateral The address of the collateral contract * @return orbiterId The ID of the orbiters used to deploy this lending pool */ function getShuttles( address _lpTokenPair, uint256 _orbiterId ) external view returns (bool launched, uint88 shuttleId, address borrowable, address collateral, uint96 orbiterId); /** * @return Human friendly name for this contract */ function name() external view returns (string memory); /** * @return The version of this contract */ function version() external view returns (string memory); /** * @return usd The address of the borrowable token (stablecoin) */ function usd() external view returns (address); /** * @return nativeToken The address of the chain's native token */ function nativeToken() external view returns (address); /** * @notice The address of the nebula registry on this chain */ function nebulaRegistry() external view returns (address); /** * @return admin The address of the Cygnus Admin which grants special permissions in collateral/borrow contracts */ function admin() external view returns (address); /** * @return pendingAdmin The address of the requested account to be the new Cygnus Admin */ function pendingAdmin() external view returns (address); /** * @return daoReserves The address that handles Cygnus reserves from all pools */ function daoReserves() external view returns (address); /** * @dev Returns the address of the CygnusDAO revenue vault. * @return cygnusX1Vault The address of the CygnusDAO revenue vault. */ function cygnusX1Vault() external view returns (address); /** * @dev Returns the address of the CygnusDAO base router. * @return cygnusAltair Latest address of the base router on this chain. */ function cygnusAltair() external view returns (address); /** * @dev Returns the address of the CYG rewarder * @return cygnusPillars The address of the CYG rewarder on this chain */ function cygnusPillars() external view returns (address); /** * @dev Returns the total number of orbiter pairs deployed (1 collateral + 1 borrow = 1 orbiter). * @return orbitersDeployed The total number of orbiter pairs deployed. */ function orbitersDeployed() external view returns (uint256); /** * @dev Returns the total number of shuttles deployed. * @return shuttlesDeployed The total number of shuttles deployed. */ function shuttlesDeployed() external view returns (uint256); /** * @dev Returns the chain ID */ function chainId() external view returns (uint256); /** * @dev Returns the borrowable TVL (Total Value Locked) in USD for a specific shuttle. * @param shuttleId The ID of the shuttle for which the borrowable TVL is requested. * @return The borrowable TVL in USD for the specified shuttle. */ function borrowableTvlUsd(uint256 shuttleId) external view returns (uint256); /** * @dev Returns the collateral TVL (Total Value Locked) in USD for a specific shuttle. * @param shuttleId The ID of the shuttle for which the collateral TVL is requested. * @return The collateral TVL in USD for the specified shuttle. */ function collateralTvlUsd(uint256 shuttleId) external view returns (uint256); /** * @dev Returns the total TVL (Total Value Locked) in USD for a specific shuttle. * @param shuttleId The ID of the shuttle for which the total TVL is requested. * @return The total TVL in USD for the specified shuttle. */ function shuttleTvlUsd(uint256 shuttleId) external view returns (uint256); /** * @dev Returns the total borrowable TVL in USD for all shuttles. * @return The total borrowable TVL in USD. */ function allBorrowablesTvlUsd() external view returns (uint256); /** * @dev Returns the total collateral TVL in USD for all shuttles. * @return The total collateral TVL in USD. */ function allCollateralsTvlUsd() external view returns (uint256); /** * @dev Returns the USD value of the DAO Cyg USD reserves. * @return The USD value of the DAO Cyg USD reserves. */ function daoCygUsdReservesUsd() external view returns (uint256); /** * @dev Returns the USD value of the DAO Cyg LP reserves. * @return The USD value of the DAO Cyg LP reserves. */ function daoCygLPReservesUsd() external view returns (uint256); /** * @dev Returns the total USD value of CygnusDAO reserves. * @return The total USD value of CygnusDAO reserves. */ function cygnusTotalReservesUsd() external view returns (uint256); /** * @dev Returns the total TVL in USD for CygnusDAO. * @return The total TVL in USD for CygnusDAO. */ function cygnusTvlUsd() external view returns (uint256); /** * @dev Returns the total amount borrowed for all shuttles * @return The total amount borrowed in USD. */ function cygnusTotalBorrows() external view returns (uint256); /* ═══════════════════════════════════════════════════════════════════════════════════════════════════════ 4. NON-CONSTANT FUNCTIONS ═══════════════════════════════════════════════════════════════════════════════════════════════════════ */ /* ────────────────────────────────────────────── External ─────────────────────────────────────────────── */ /** * @notice Admin 👽 * @notice Turns off orbiters making them not able for deployment of pools * * @param orbiterId The ID of the orbiter pairs we want to switch the status of * * @custom:security only-admin */ function switchOrbiterStatus(uint256 orbiterId) external; /** * @notice Admin 👽 * @notice Initializes both Borrow arms and the collateral arm * * @param lpTokenPair The address of the underlying LP Token this pool is for * @param orbiterId The ID of the orbiters we want to deploy to (= dex Id) * @return borrowable The address of the Cygnus borrow contract for this pool * @return collateral The address of the Cygnus collateral contract for both borrow tokens * * @custom:security non-reentrant only-admin 👽 */ function deployShuttle(address lpTokenPair, uint256 orbiterId) external returns (address borrowable, address collateral); /** * @notice Admin 👽 * @notice Sets the new orbiters to deploy collateral and borrow contracts and stores orbiters in storage * * @param name The name of the strategy OR the dex these orbiters are for * @param albireoOrbiter the address of this orbiter's borrow deployer * @param denebOrbiter The address of this orbiter's collateral deployer * * @custom:security non-reentrant only-admin */ function initializeOrbiter(string memory name, IAlbireoOrbiter albireoOrbiter, IDenebOrbiter denebOrbiter) external; /** * @notice Admin 👽 * @notice Sets a new pending admin for Cygnus * * @param newCygnusAdmin Address of the requested Cygnus admin * * @custom:security only-admin */ function setPendingAdmin(address newCygnusAdmin) external; /** * @notice Approves the pending admin and is the new Cygnus admin * * @custom:security only-pending-admin */ function acceptCygnusAdmin() external; /** * @notice Admin 👽 * @notice Accepts the new implementation contract * * @param newReserves The address of the new DAO reserves * * @custom:security only-admin */ function setDaoReserves(address newReserves) external; /** * @notice Admin 👽 * @notice Sets the address of the new x1 vault which accumulates rewards over time * * @custom:security only-admin */ function setCygnusX1Vault(address newX1Vault) external; /** * @notice Admin 👽 * @notice Sets the address of the new pillars of creation * * @custom:security only-admin */ function setCygnusPillars(address newPillars) external; /** * @notice Admin 👽 * @notice Sets the address of the new base router * * @custom:security only-admin */ function setCygnusAltair(address newAltair) external; }
// SPDX-License-Identifier: AGPL-3.0-or-later // // IOrbiter.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; import {IHangar18} from "./IHangar18.sol"; import {ICygnusNebula} from "./ICygnusNebula.sol"; /** * @notice Interface used by core contracts to read variables from the deployers (AlbireoOrbiter.sol and * DenebOrbiter.sol) */ interface IOrbiter { /** * @notice Simple interface of both borrow/collateral orbiters that gets read during deployment of pools * in the constructor of CygnusTerminal.sol * @return factory The address of the Cygnus factory-like contract, assigned to `hangar18` * @return underlying The address of the underlying borrow token (stablecoin) or collateral token (LP Token) * @return twinstar The opposite contract to the one being deployed. IE. If collateral is being deployed, * then it is the address of the borrowable. If borrowable is being deployed, it is the * address of the collateral. * @return oracle The address of the oracle * @return shuttleId The lending pool ID */ function shuttleParameters() external returns (IHangar18 factory, address underlying, address twinstar, ICygnusNebula oracle, uint256 shuttleId); }
// SPDX-License-Identifier: AGPL-3.0-or-later // // IPillarsOfCreation.sol // // Copyright (C) 2023 CygnusDAO // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see <https://www.gnu.org/licenses/>. pragma solidity >=0.8.17; /** * @notice Interface to interact with CYG rewards */ interface IPillarsOfCreation { /** * @dev Tracks rewards for lenders and borrowers. * * @param account The address of the lender or borrower * @param balance The updated balance of the account * @param collateral The address of the Collateral (Zero Address for lenders) * * Effects: * - Updates the shares and reward debt of the borrower in the borrowable asset's pool. * - Updates the total shares of the borrowable asset's pool. */ function trackRewards(address account, uint256 balance, address collateral) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) library FixedPointMathLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The operation failed, as the output exceeds the maximum value of uint256. error ExpOverflow(); /// @dev The operation failed, as the output exceeds the maximum value of uint256. error FactorialOverflow(); /// @dev The operation failed, due to an multiplication overflow. error MulWadFailed(); /// @dev The operation failed, either due to a /// multiplication overflow, or a division by a zero. error DivWadFailed(); /// @dev The multiply-divide operation failed, either due to a /// multiplication overflow, or a division by a zero. error MulDivFailed(); /// @dev The division failed, as the denominator is zero. error DivFailed(); /// @dev The full precision multiply-divide operation failed, either due /// to the result being larger than 256 bits, or a division by a zero. error FullMulDivFailed(); /// @dev The output is undefined, as the input is less-than-or-equal to zero. error LnWadUndefined(); /// @dev The output is undefined, as the input is zero. error Log2Undefined(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The scalar of ETH and most ERC20s. uint256 internal constant WAD = 1e18; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SIMPLIFIED FIXED POINT OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `(x * y) / WAD` rounded down. function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if mul(y, gt(x, div(not(0), y))) { // Store the function selector of `MulWadFailed()`. mstore(0x00, 0xbac65e5b) // Revert with (offset, size). revert(0x1c, 0x04) } z := div(mul(x, y), WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded up. function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if mul(y, gt(x, div(not(0), y))) { // Store the function selector of `MulWadFailed()`. mstore(0x00, 0xbac65e5b) // Revert with (offset, size). revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD)) } } /// @dev Equivalent to `(x * WAD) / y` rounded down. function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { // Store the function selector of `DivWadFailed()`. mstore(0x00, 0x7c5f487d) // Revert with (offset, size). revert(0x1c, 0x04) } z := div(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded up. function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { // Store the function selector of `DivWadFailed()`. mstore(0x00, 0x7c5f487d) // Revert with (offset, size). revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y)) } } /// @dev Equivalent to `x` to the power of `y`. /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`. function powWad(int256 x, int256 y) internal pure returns (int256) { // Using `ln(x)` means `x` must be greater than 0. return expWad((lnWad(x) * y) / int256(WAD)); } /// @dev Returns `exp(x)`, denominated in `WAD`. function expWad(int256 x) internal pure returns (int256 r) { unchecked { // When the result is < 0.5 we return zero. This happens when // x <= floor(log(0.5e18) * 1e18) ~ -42e18 if (x <= -42139678854452767551) return r; /// @solidity memory-safe-assembly assembly { // When the result is > (2**255 - 1) / 1e18 we can not represent it as an // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135. if iszero(slt(x, 135305999368893231589)) { // Store the function selector of `ExpOverflow()`. mstore(0x00, 0xa37bfec9) // Revert with (offset, size). revert(0x1c, 0x04) } } // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96 // for more intermediate precision and a binary basis. This base conversion // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. x = (x << 78) / 5 ** 18; // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers // of two such that exp(x) = exp(x') * 2**k, where k is an integer. // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96; x = x - k * 54916777467707473351141471128; // k is in the range [-61, 195]. // Evaluate using a (6, 7)-term rational approximation. // p is made monic, we'll multiply by a scale factor later. int256 y = x + 1346386616545796478920950773328; y = ((y * x) >> 96) + 57155421227552351082224309758442; int256 p = y + x - 94201549194550492254356042504812; p = ((p * y) >> 96) + 28719021644029726153956944680412240; p = p * x + (4385272521454847904659076985693276 << 96); // We leave p in 2**192 basis so we don't need to scale it back up for the division. int256 q = x - 2855989394907223263936484059900; q = ((q * x) >> 96) + 50020603652535783019961831881945; q = ((q * x) >> 96) - 533845033583426703283633433725380; q = ((q * x) >> 96) + 3604857256930695427073651918091429; q = ((q * x) >> 96) - 14423608567350463180887372962807573; q = ((q * x) >> 96) + 26449188498355588339934803723976023; /// @solidity memory-safe-assembly assembly { // Div in assembly because solidity adds a zero check despite the unchecked. // The q polynomial won't have zeros in the domain as all its roots are complex. // No scaling is necessary because p is already 2**96 too large. r := sdiv(p, q) } // r should be in the range (0.09, 0.25) * 2**96. // We now need to multiply r by: // * the scale factor s = ~6.031367120. // * the 2**k factor from the range reduction. // * the 1e18 / 2**96 factor for base conversion. // We do this all at once, with an intermediate result in 2**213 // basis, so the final right shift is always by a positive amount. r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)); } } /// @dev Returns `ln(x)`, denominated in `WAD`. function lnWad(int256 x) internal pure returns (int256 r) { unchecked { /// @solidity memory-safe-assembly assembly { if iszero(sgt(x, 0)) { // Store the function selector of `LnWadUndefined()`. mstore(0x00, 0x1615e638) // Revert with (offset, size). revert(0x1c, 0x04) } } // We want to convert x from 10**18 fixed point to 2**96 fixed point. // We do this by multiplying by 2**96 / 10**18. But since // ln(x * C) = ln(x) + ln(C), we can simply do nothing here // and add ln(2**96 / 10**18) at the end. // Compute k = log2(x) - 96. int256 k; /// @solidity memory-safe-assembly assembly { let v := x k := shl(7, lt(0xffffffffffffffffffffffffffffffff, v)) k := or(k, shl(6, lt(0xffffffffffffffff, shr(k, v)))) k := or(k, shl(5, lt(0xffffffff, shr(k, v)))) // For the remaining 32 bits, use a De Bruijn lookup. // See: https://graphics.stanford.edu/~seander/bithacks.html v := shr(k, v) v := or(v, shr(1, v)) v := or(v, shr(2, v)) v := or(v, shr(4, v)) v := or(v, shr(8, v)) v := or(v, shr(16, v)) // forgefmt: disable-next-item k := sub( or(k, byte(shr(251, mul(v, shl(224, 0x07c4acdd))), 0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f)), 96 ) } // Reduce range of x to (1, 2) * 2**96 // ln(2^k * x) = k * ln(2) + ln(x) x <<= uint256(159 - k); x = int256(uint256(x) >> 159); // Evaluate using a (8, 8)-term rational approximation. // p is made monic, we will multiply by a scale factor later. int256 p = x + 3273285459638523848632254066296; p = ((p * x) >> 96) + 24828157081833163892658089445524; p = ((p * x) >> 96) + 43456485725739037958740375743393; p = ((p * x) >> 96) - 11111509109440967052023855526967; p = ((p * x) >> 96) - 45023709667254063763336534515857; p = ((p * x) >> 96) - 14706773417378608786704636184526; p = p * x - (795164235651350426258249787498 << 96); // We leave p in 2**192 basis so we don't need to scale it back up for the division. // q is monic by convention. int256 q = x + 5573035233440673466300451813936; q = ((q * x) >> 96) + 71694874799317883764090561454958; q = ((q * x) >> 96) + 283447036172924575727196451306956; q = ((q * x) >> 96) + 401686690394027663651624208769553; q = ((q * x) >> 96) + 204048457590392012362485061816622; q = ((q * x) >> 96) + 31853899698501571402653359427138; q = ((q * x) >> 96) + 909429971244387300277376558375; /// @solidity memory-safe-assembly assembly { // Div in assembly because solidity adds a zero check despite the unchecked. // The q polynomial is known not to have zeros in the domain. // No scaling required because p is already 2**96 too large. r := sdiv(p, q) } // r is in the range (0, 0.125) * 2**96 // Finalization, we need to: // * multiply by the scale factor s = 5.549… // * add ln(2**96 / 10**18) // * add k * ln(2) // * multiply by 10**18 / 2**96 = 5**18 >> 78 // mul s * 5e18 * 2**96, base is now 5**18 * 2**192 r *= 1677202110996718588342820967067443963516166; // add ln(2) * k * 5e18 * 2**192 r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k; // add ln(2**96 / 10**18) * 5e18 * 2**192 r += 600920179829731861736702779321621459595472258049074101567377883020018308; // base conversion: mul 2**18 / 2**192 r >>= 174; } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* GENERAL NUMBER UTILITIES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Calculates `floor(a * b / d)` with full precision. /// Throws if result overflows a uint256 or when `d` is zero. /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // forgefmt: disable-next-item for { } 1 { } { // 512-bit multiply `[prod1 prod0] = x * y`. // Compute the product mod `2**256` and mod `2**256 - 1` // then 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`. // Least significant 256 bits of the product. let prod0 := mul(x, y) let mm := mulmod(x, y, not(0)) // Most significant 256 bits of the product. let prod1 := sub(mm, add(prod0, lt(mm, prod0))) // Handle non-overflow cases, 256 by 256 division. if iszero(prod1) { if iszero(d) { // Store the function selector of `FullMulDivFailed()`. mstore(0x00, 0xae47f702) // Revert with (offset, size). revert(0x1c, 0x04) } result := div(prod0, d) break } // Make sure the result is less than `2**256`. // Also prevents `d == 0`. if iszero(gt(d, prod1)) { // Store the function selector of `FullMulDivFailed()`. mstore(0x00, 0xae47f702) // Revert with (offset, size). revert(0x1c, 0x04) } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from `[prod1 prod0]`. // Compute remainder using mulmod. let remainder := mulmod(x, y, d) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) // Factor powers of two out of `d`. // Compute largest power of two divisor of `d`. // Always greater or equal to 1. let twos := and(d, sub(0, d)) // Divide d by power of two. d := div(d, twos) // Divide [prod1 prod0] by the factors of two. prod0 := div(prod0, twos) // Shift in bits from `prod1` into `prod0`. For this we need // to flip `twos` such that it is `2**256 / twos`. // If `twos` is zero, then it becomes one. prod0 := or(prod0, mul(prod1, add(div(sub(0, twos), twos), 1))) // Invert `d mod 2**256` // Now that `d` is an odd number, it has an inverse // modulo `2**256` such that `d * inv = 1 mod 2**256`. // Compute the inverse by starting with a seed that is correct // correct for four bits. That is, `d * inv = 1 mod 2**4`. let inv := xor(mul(3, d), 2) // Now use 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. inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128 result := mul(prod0, mul(inv, sub(2, mul(d, inv)))) // inverse mod 2**256 break } } } /// @dev Calculates `floor(x * y / d)` with full precision, rounded up. /// Throws if result overflows a uint256 or when `d` is zero. /// Credit to Uniswap-v3-core under MIT license: /// https://github.com/Uniswap/v3-core/blob/contracts/libraries/FullMath.sol function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { result = fullMulDiv(x, y, d); /// @solidity memory-safe-assembly assembly { if mulmod(x, y, d) { if iszero(add(result, 1)) { // Store the function selector of `FullMulDivFailed()`. mstore(0x00, 0xae47f702) // Revert with (offset, size). revert(0x1c, 0x04) } result := add(result, 1) } } } /// @dev Returns `floor(x * y / d)`. /// Reverts if `x * y` overflows, or `d` is zero. function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { // Store the function selector of `MulDivFailed()`. mstore(0x00, 0xad251c27) // Revert with (offset, size). revert(0x1c, 0x04) } z := div(mul(x, y), d) } } /// @dev Returns `ceil(x * y / d)`. /// Reverts if `x * y` overflows, or `d` is zero. function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { // Store the function selector of `MulDivFailed()`. mstore(0x00, 0xad251c27) // Revert with (offset, size). revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d)) } } /// @dev Returns `ceil(x / d)`. /// Reverts if `d` is zero. function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { if iszero(d) { // Store the function selector of `DivFailed()`. mstore(0x00, 0x65244e4e) // Revert with (offset, size). revert(0x1c, 0x04) } z := add(iszero(iszero(mod(x, d))), div(x, d)) } } /// @dev Returns `max(0, x - y)`. function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mul(gt(x, y), sub(x, y)) } } /// @dev Returns the square root of `x`. function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`. 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. // Let `y = x / 2**r`. // We check `y >= 2**(k + 8)` but shift right by `k` bits // each branch to ensure that if `x >= 256`, then `y >= 256`. let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffffff, shr(r, x)))) z := shl(shr(1, r), 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(shr(r, x), 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)) } } /// @dev Returns the factorial of `x`. function factorial(uint256 x) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for { } 1 { } { if iszero(lt(10, x)) { // forgefmt: disable-next-item result := and(shr(mul(22, x), 0x375f0016260009d80004ec0002d00001e0000180000180000200000400001), 0x3fffff) break } if iszero(lt(57, x)) { let end := 31 result := 8222838654177922817725562880000000 if iszero(lt(end, x)) { end := 10 result := 3628800 } for { let w := not(0) } 1 { } { result := mul(result, x) x := add(x, w) if eq(x, end) { break } } break } // Store the function selector of `FactorialOverflow()`. mstore(0x00, 0xaba0f2a2) // Revert with (offset, size). revert(0x1c, 0x04) } } } /// @dev Returns the log2 of `x`. /// Equivalent to computing the index of the most significant bit (MSB) of `x`. function log2(uint256 x) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { if iszero(x) { // Store the function selector of `Log2Undefined()`. mstore(0x00, 0x5be3aa5c) // Revert with (offset, size). revert(0x1c, 0x04) } r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) // For the remaining 32 bits, use a De Bruijn lookup. // See: https://graphics.stanford.edu/~seander/bithacks.html x := shr(r, x) x := or(x, shr(1, x)) x := or(x, shr(2, x)) x := or(x, shr(4, x)) x := or(x, shr(8, x)) x := or(x, shr(16, x)) // forgefmt: disable-next-item r := or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))), 0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f)) } } /// @dev Returns the log2 of `x`, rounded up. function log2Up(uint256 x) internal pure returns (uint256 r) { unchecked { uint256 isNotPo2; assembly { isNotPo2 := iszero(iszero(and(x, sub(x, 1)))) } return log2(x) + isNotPo2; } } /// @dev Returns the average of `x` and `y`. function avg(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = (x & y) + ((x ^ y) >> 1); } } /// @dev Returns the average of `x` and `y`. function avg(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = (x >> 1) + (y >> 1) + (((x & 1) + (y & 1)) >> 1); } } /// @dev Returns the absolute value of `x`. function abs(int256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let mask := sub(0, shr(255, x)) z := xor(mask, add(mask, x)) } } /// @dev Returns the absolute distance between `x` and `y`. function dist(int256 x, int256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let a := sub(y, x) z := xor(a, mul(xor(a, sub(x, y)), sgt(x, y))) } } /// @dev Returns the minimum of `x` and `y`. function min(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), lt(y, x))) } } /// @dev Returns the minimum of `x` and `y`. function min(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), slt(y, x))) } } /// @dev Returns the maximum of `x` and `y`. function max(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), gt(y, x))) } } /// @dev Returns the maximum of `x` and `y`. function max(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), sgt(y, x))) } } /// @dev Returns `x`, bounded to `minValue` and `maxValue`. function clamp(uint256 x, uint256 minValue, uint256 maxValue) internal pure returns (uint256 z) { z = min(max(x, minValue), maxValue); } /// @dev Returns `x`, bounded to `minValue` and `maxValue`. function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) { z = min(max(x, minValue), maxValue); } /// @dev Returns greatest common divisor of `x` and `y`. function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // forgefmt: disable-next-item for { z := x } y { } { let t := y y := mod(z, y) z := t } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RAW NUMBER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns `x + y`, without checking for overflow. function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x + y; } } /// @dev Returns `x + y`, without checking for overflow. function rawAdd(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x + y; } } /// @dev Returns `x - y`, without checking for underflow. function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x - y; } } /// @dev Returns `x - y`, without checking for underflow. function rawSub(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x - y; } } /// @dev Returns `x * y`, without checking for overflow. function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x * y; } } /// @dev Returns `x * y`, without checking for overflow. function rawMul(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x * y; } } /// @dev Returns `x / y`, returning 0 if `y` is zero. function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := div(x, y) } } /// @dev Returns `x / y`, returning 0 if `y` is zero. function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := sdiv(x, y) } } /// @dev Returns `x % y`, returning 0 if `y` is zero. function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mod(x, y) } } /// @dev Returns `x % y`, returning 0 if `y` is zero. function rawSMod(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := smod(x, y) } } /// @dev Returns `(x + y) % d`, return 0 if `d` if zero. function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := addmod(x, y, d) } } /// @dev Returns `(x * y) % d`, return 0 if `d` if zero. function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mulmod(x, y, d) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe integer casting library that reverts on overflow. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeCastLib.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/math/SafeCast.sol) library SafeCastLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ error Overflow(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* UNSIGNED INTEGER SAFE CASTING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function toUint8(uint256 x) internal pure returns (uint8) { if (x >= 1 << 8) _revertOverflow(); return uint8(x); } function toUint16(uint256 x) internal pure returns (uint16) { if (x >= 1 << 16) _revertOverflow(); return uint16(x); } function toUint24(uint256 x) internal pure returns (uint24) { if (x >= 1 << 24) _revertOverflow(); return uint24(x); } function toUint32(uint256 x) internal pure returns (uint32) { if (x >= 1 << 32) _revertOverflow(); return uint32(x); } function toUint40(uint256 x) internal pure returns (uint40) { if (x >= 1 << 40) _revertOverflow(); return uint40(x); } function toUint48(uint256 x) internal pure returns (uint48) { if (x >= 1 << 48) _revertOverflow(); return uint48(x); } function toUint56(uint256 x) internal pure returns (uint56) { if (x >= 1 << 56) _revertOverflow(); return uint56(x); } function toUint64(uint256 x) internal pure returns (uint64) { if (x >= 1 << 64) _revertOverflow(); return uint64(x); } function toUint72(uint256 x) internal pure returns (uint72) { if (x >= 1 << 72) _revertOverflow(); return uint72(x); } function toUint80(uint256 x) internal pure returns (uint80) { if (x >= 1 << 80) _revertOverflow(); return uint80(x); } function toUint88(uint256 x) internal pure returns (uint88) { if (x >= 1 << 88) _revertOverflow(); return uint88(x); } function toUint96(uint256 x) internal pure returns (uint96) { if (x >= 1 << 96) _revertOverflow(); return uint96(x); } function toUint104(uint256 x) internal pure returns (uint104) { if (x >= 1 << 104) _revertOverflow(); return uint104(x); } function toUint112(uint256 x) internal pure returns (uint112) { if (x >= 1 << 112) _revertOverflow(); return uint112(x); } function toUint120(uint256 x) internal pure returns (uint120) { if (x >= 1 << 120) _revertOverflow(); return uint120(x); } function toUint128(uint256 x) internal pure returns (uint128) { if (x >= 1 << 128) _revertOverflow(); return uint128(x); } function toUint136(uint256 x) internal pure returns (uint136) { if (x >= 1 << 136) _revertOverflow(); return uint136(x); } function toUint144(uint256 x) internal pure returns (uint144) { if (x >= 1 << 144) _revertOverflow(); return uint144(x); } function toUint152(uint256 x) internal pure returns (uint152) { if (x >= 1 << 152) _revertOverflow(); return uint152(x); } function toUint160(uint256 x) internal pure returns (uint160) { if (x >= 1 << 160) _revertOverflow(); return uint160(x); } function toUint168(uint256 x) internal pure returns (uint168) { if (x >= 1 << 168) _revertOverflow(); return uint168(x); } function toUint176(uint256 x) internal pure returns (uint176) { if (x >= 1 << 176) _revertOverflow(); return uint176(x); } function toUint184(uint256 x) internal pure returns (uint184) { if (x >= 1 << 184) _revertOverflow(); return uint184(x); } function toUint192(uint256 x) internal pure returns (uint192) { if (x >= 1 << 192) _revertOverflow(); return uint192(x); } function toUint200(uint256 x) internal pure returns (uint200) { if (x >= 1 << 200) _revertOverflow(); return uint200(x); } function toUint208(uint256 x) internal pure returns (uint208) { if (x >= 1 << 208) _revertOverflow(); return uint208(x); } function toUint216(uint256 x) internal pure returns (uint216) { if (x >= 1 << 216) _revertOverflow(); return uint216(x); } function toUint224(uint256 x) internal pure returns (uint224) { if (x >= 1 << 224) _revertOverflow(); return uint224(x); } function toUint232(uint256 x) internal pure returns (uint232) { if (x >= 1 << 232) _revertOverflow(); return uint232(x); } function toUint240(uint256 x) internal pure returns (uint240) { if (x >= 1 << 240) _revertOverflow(); return uint240(x); } function toUint248(uint256 x) internal pure returns (uint248) { if (x >= 1 << 248) _revertOverflow(); return uint248(x); } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SIGNED INTEGER SAFE CASTING OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function toInt8(int256 x) internal pure returns (int8) { int8 y = int8(x); if (x != y) _revertOverflow(); return y; } function toInt16(int256 x) internal pure returns (int16) { int16 y = int16(x); if (x != y) _revertOverflow(); return y; } function toInt24(int256 x) internal pure returns (int24) { int24 y = int24(x); if (x != y) _revertOverflow(); return y; } function toInt32(int256 x) internal pure returns (int32) { int32 y = int32(x); if (x != y) _revertOverflow(); return y; } function toInt40(int256 x) internal pure returns (int40) { int40 y = int40(x); if (x != y) _revertOverflow(); return y; } function toInt48(int256 x) internal pure returns (int48) { int48 y = int48(x); if (x != y) _revertOverflow(); return y; } function toInt56(int256 x) internal pure returns (int56) { int56 y = int56(x); if (x != y) _revertOverflow(); return y; } function toInt64(int256 x) internal pure returns (int64) { int64 y = int64(x); if (x != y) _revertOverflow(); return y; } function toInt72(int256 x) internal pure returns (int72) { int72 y = int72(x); if (x != y) _revertOverflow(); return y; } function toInt80(int256 x) internal pure returns (int80) { int80 y = int80(x); if (x != y) _revertOverflow(); return y; } function toInt88(int256 x) internal pure returns (int88) { int88 y = int88(x); if (x != y) _revertOverflow(); return y; } function toInt96(int256 x) internal pure returns (int96) { int96 y = int96(x); if (x != y) _revertOverflow(); return y; } function toInt104(int256 x) internal pure returns (int104) { int104 y = int104(x); if (x != y) _revertOverflow(); return y; } function toInt112(int256 x) internal pure returns (int112) { int112 y = int112(x); if (x != y) _revertOverflow(); return y; } function toInt120(int256 x) internal pure returns (int120) { int120 y = int120(x); if (x != y) _revertOverflow(); return y; } function toInt128(int256 x) internal pure returns (int128) { int128 y = int128(x); if (x != y) _revertOverflow(); return y; } function toInt136(int256 x) internal pure returns (int136) { int136 y = int136(x); if (x != y) _revertOverflow(); return y; } function toInt144(int256 x) internal pure returns (int144) { int144 y = int144(x); if (x != y) _revertOverflow(); return y; } function toInt152(int256 x) internal pure returns (int152) { int152 y = int152(x); if (x != y) _revertOverflow(); return y; } function toInt160(int256 x) internal pure returns (int160) { int160 y = int160(x); if (x != y) _revertOverflow(); return y; } function toInt168(int256 x) internal pure returns (int168) { int168 y = int168(x); if (x != y) _revertOverflow(); return y; } function toInt176(int256 x) internal pure returns (int176) { int176 y = int176(x); if (x != y) _revertOverflow(); return y; } function toInt184(int256 x) internal pure returns (int184) { int184 y = int184(x); if (x != y) _revertOverflow(); return y; } function toInt192(int256 x) internal pure returns (int192) { int192 y = int192(x); if (x != y) _revertOverflow(); return y; } function toInt200(int256 x) internal pure returns (int200) { int200 y = int200(x); if (x != y) _revertOverflow(); return y; } function toInt208(int256 x) internal pure returns (int208) { int208 y = int208(x); if (x != y) _revertOverflow(); return y; } function toInt216(int256 x) internal pure returns (int216) { int216 y = int216(x); if (x != y) _revertOverflow(); return y; } function toInt224(int256 x) internal pure returns (int224) { int224 y = int224(x); if (x != y) _revertOverflow(); return y; } function toInt232(int256 x) internal pure returns (int232) { int232 y = int232(x); if (x != y) _revertOverflow(); return y; } function toInt240(int256 x) internal pure returns (int240) { int240 y = int240(x); if (x != y) _revertOverflow(); return y; } function toInt248(int256 x) internal pure returns (int248) { int248 y = int248(x); if (x != y) _revertOverflow(); return y; } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* PRIVATE HELPERS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ function _revertOverflow() private pure { /// @solidity memory-safe-assembly assembly { // Store the function selector of `Overflow()`. mstore(0x00, 0x35278d12) // Revert with (offset, size). revert(0x1c, 0x04) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller. library SafeTransferLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ETH transfer has failed. error ETHTransferFailed(); /// @dev The ERC20 `transferFrom` has failed. error TransferFromFailed(); /// @dev The ERC20 `transfer` has failed. error TransferFailed(); /// @dev The ERC20 `approve` has failed. error ApproveFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Suggested gas stipend for contract receiving ETH /// that disallows any storage writes. uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300; /// @dev Suggested gas stipend for contract receiving ETH to perform a few /// storage reads and writes, but low enough to prevent griefing. /// Multiply by a small constant (e.g. 2), if needed. uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ETH OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` (in wei) ETH to `to`. /// Reverts upon failure. function safeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { // Transfer the ETH and check if it succeeded or not. if iszero(call(gas(), to, amount, 0, 0, 0, 0)) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. /// The `gasStipend` can be set to a low enough value to prevent /// storage writes or gas griefing. /// /// If sending via the normal procedure fails, force sends the ETH by /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. /// /// Reverts if the current contract has insufficient balance. function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { // If insufficient balance, revert. if lt(selfbalance(), amount) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } // Transfer the ETH and check if it succeeded or not. if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. // We can directly use `SELFDESTRUCT` in the contract creation. // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 if iszero(create(amount, 0x0b, 0x16)) { // For better gas estimation. if iszero(gt(gas(), 1000000)) { revert(0, 0) } } } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend /// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default /// for 99% of cases and can be overriden with the three-argument version of this /// function if necessary. /// /// If sending via the normal procedure fails, force sends the ETH by /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. /// /// Reverts if the current contract has insufficient balance. function forceSafeTransferETH(address to, uint256 amount) internal { // Manually inlined because the compiler doesn't inline functions with branches. /// @solidity memory-safe-assembly assembly { // If insufficient balance, revert. if lt(selfbalance(), amount) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } // Transfer the ETH and check if it succeeded or not. if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. // We can directly use `SELFDESTRUCT` in the contract creation. // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 if iszero(create(amount, 0x0b, 0x16)) { // For better gas estimation. if iszero(gt(gas(), 1000000)) { revert(0, 0) } } } } } /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. /// The `gasStipend` can be set to a low enough value to prevent /// storage writes or gas griefing. /// /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend. /// /// Note: Does NOT revert upon failure. /// Returns whether the transfer of ETH is successful instead. function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { // Transfer the ETH and check if it succeeded or not. success := call(gasStipend, to, amount, 0, 0, 0, 0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x60, amount) // Store the `amount` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x0c, 0x23b872dd000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // 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(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends all of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x40, to) // Store the `to` argument. mstore(0x2c, shl(96, from)) // Store the `from` argument. // Store the function selector of `balanceOf(address)`. mstore(0x0c, 0x70a08231000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x00, 0x23b872dd) // The `amount` argument is already written to the memory word at 0x6c. amount := mload(0x60) if iszero( and( // The arguments of `and` are evaluated from right to left. // 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(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransfer(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. // Store the function selector of `transfer(address,uint256)`. mstore(0x00, 0xa9059cbb000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // 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(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten. mstore(0x34, 0) } } /// @dev Sends all of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransferAll(address token, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, address()) // Store the address of the current contract. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x14, to) // Store the `to` argument. // The `amount` argument is already written to the memory word at 0x34. amount := mload(0x34) // Store the function selector of `transfer(address,uint256)`. mstore(0x00, 0xa9059cbb000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // 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(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten. mstore(0x34, 0) } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// Reverts upon failure. function safeApprove(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x14, to) // Store the `to` argument. mstore(0x34, amount) // Store the `amount` argument. // Store the function selector of `approve(address,uint256)`. mstore(0x00, 0x095ea7b3000000000000000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // 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(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `ApproveFailed()`. mstore(0x00, 0x3e3f8f73) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten. mstore(0x34, 0) } } /// @dev Returns the amount of ERC20 `token` owned by `account`. /// Returns zero if the `token` does not exist. function balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x14, account) // Store the `account` argument. // Store the function selector of `balanceOf(address)`. mstore(0x00, 0x70a08231000000000000000000000000) amount := mul( mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20) ) ) } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @title ReentrancyGuard /// @author Paul Razvan Berg /// @notice Contract module that helps prevent reentrant calls to a function. /// /// Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier available, which can be applied /// to functions to make sure there are no nested (reentrant) calls to them. /// /// Note that because there is a single `nonReentrant` guard, functions marked as `nonReentrant` may not /// call one another. This can be worked around by making those functions `private`, and then adding /// `external` `nonReentrant` entry points to them. /// /// @dev Forked from OpenZeppelin /// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v3.4.0/contracts/utils/ReentrancyGuard.sol abstract contract ReentrancyGuard { /*////////////////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////////////////*/ /// @notice Thrown when there is a reentrancy call. error ReentrantCall(); /*////////////////////////////////////////////////////////////////////////// PRIVATE STORAGE //////////////////////////////////////////////////////////////////////////*/ bool private notEntered; /*////////////////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////////////////*/ /// Storing an initial non-zero value makes deployment a bit more expensive but in exchange the /// refund on every call to nonReentrant will be lower in amount. Since refunds are capped to a /// percentage of the total transaction's gas, it is best to keep them low in cases like this one, /// to increase the likelihood of the full refund coming into effect. constructor() { notEntered = true; } /*////////////////////////////////////////////////////////////////////////// MODIFIERS //////////////////////////////////////////////////////////////////////////*/ /// @notice Prevents a contract from calling itself, directly or indirectly. /// @dev Calling a `nonReentrant` function from another `nonReentrant` function /// is not supported. It is possible to prevent this from happening by making /// the `nonReentrant` function external, and make it call a `private` /// function that does the actual work. modifier nonReentrant() { // On the first call to nonReentrant, notEntered will be true. if (!notEntered) { revert ReentrantCall(); } // Any calls to nonReentrant after this point will fail. notEntered = false; _; // By storing the original value once again, a refund is triggered (https://eips.ethereum.org/EIPS/eip-2200). notEntered = true; } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 800, "details": { "peephole": true, "inliner": true, "jumpdestRemover": true, "orderLiterals": true, "deduplicate": true, "cse": true, "constantOptimizer": true, "yulDetails": { "stackAllocation": true, "optimizerSteps": "dhfoDgvulfnTUtnIf[xa[r]EscLMcCTUtTOntnfDIulLculVcul[j]Tpeulxa[rul]xa[r]cLgvifCTUca[r]LSsTOtfDnca[r]Iulc]jmul[jul]VcTOculjmul" } } }, "metadata": { "bytecodeHash": "none" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"name":"AllowanceOverflow","type":"error"},{"inputs":[],"name":"AllowanceUnderflow","type":"error"},{"inputs":[],"name":"CygnusBorrowControl__CollateralAlreadySet","type":"error"},{"inputs":[],"name":"CygnusBorrowControl__MsgSenderNotHangar","type":"error"},{"inputs":[],"name":"CygnusBorrowControl__ParameterNotInRange","type":"error"},{"inputs":[],"name":"CygnusBorrowVoid__OnlyHarvesterAllowed","type":"error"},{"inputs":[],"name":"CygnusBorrowVoid__TokenIsUnderlying","type":"error"},{"inputs":[],"name":"CygnusBorrow__InsufficientLiquidity","type":"error"},{"inputs":[],"name":"CygnusBorrow__InsufficientUsdReceived","type":"error"},{"inputs":[],"name":"CygnusTerminal__CantMintZeroShares","type":"error"},{"inputs":[],"name":"CygnusTerminal__CantRedeemZeroAssets","type":"error"},{"inputs":[],"name":"CygnusTerminal__MsgSenderNotAdmin","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidPermit","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"ReentrantCall","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"cash","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrows","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interest","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reserves","type":"uint256"}],"name":"AccrueInterest","type":"event"},{"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":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"}],"name":"Borrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"underlying","type":"address"},{"indexed":false,"internalType":"uint256","name":"shuttleId","type":"uint256"},{"indexed":false,"internalType":"address","name":"whitelisted","type":"address"}],"name":"ChargeVoid","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"shares","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"repayAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"cygLPAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"usdAmount","type":"uint256"}],"name":"Liquidate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_token","type":"address"},{"indexed":false,"internalType":"address","name":"_harvester","type":"address"}],"name":"NewBonusHarvesterToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldHarvester","type":"address"},{"indexed":false,"internalType":"address","name":"newHarvester","type":"address"},{"indexed":false,"internalType":"address[]","name":"rewardTokens","type":"address[]"}],"name":"NewHarvester","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"baseRatePerYear","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"multiplierPerYear","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"kinkMultiplier_","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"kinkUtilizationRate_","type":"uint256"}],"name":"NewInterestRateParameters","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldRewarder","type":"address"},{"indexed":false,"internalType":"address","name":"newRewarder","type":"address"}],"name":"NewPillarsOfCreation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"oldReserveFactor","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newReserveFactor","type":"uint256"}],"name":"NewReserveFactor","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"}],"name":"RechargeVoid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint160","name":"totalBalance","type":"uint160"}],"name":"Sync","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":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","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":"result","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT2","outputs":[{"internalType":"contract IAllowanceTransfer","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrueInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"allRewardTokens","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"borrowAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowIndex","outputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"chargeVoid","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"components":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint160","name":"amount","type":"uint160"},{"internalType":"uint48","name":"expiration","type":"uint48"},{"internalType":"uint48","name":"nonce","type":"uint48"}],"internalType":"struct IAllowanceTransfer.PermitDetails","name":"details","type":"tuple"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"sigDeadline","type":"uint256"}],"internalType":"struct IAllowanceTransfer.PermitSingle","name":"_permit","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"exchangeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"}],"name":"getBorrowBalance","outputs":[{"internalType":"uint256","name":"principal","type":"uint256"},{"internalType":"uint256","name":"borrowBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lender","type":"address"}],"name":"getLenderPosition","outputs":[{"internalType":"uint256","name":"usdBalance","type":"uint256"},{"internalType":"uint256","name":"positionUsd","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRewards","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getUsdPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"hangar18","outputs":[{"internalType":"contract IHangar18","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvester","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"interestRateModel","outputs":[{"internalType":"uint64","name":"baseRatePerSecond","type":"uint64"},{"internalType":"uint64","name":"multiplierPerSecond","type":"uint64"},{"internalType":"uint64","name":"jumpMultiplierPerSecond","type":"uint64"},{"internalType":"uint64","name":"kink","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastAccrualTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastHarvest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"uint256","name":"repayAmount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"liquidate","outputs":[{"internalType":"uint256","name":"amountUsd","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"nebula","outputs":[{"internalType":"contract ICygnusNebula","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"result","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":[],"name":"pillarsOfCreation","outputs":[{"internalType":"address","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":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"liquidity","type":"uint256"}],"name":"reinvestRewards_y7b","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reserveFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewardTokensLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rewarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newHarvester","type":"address"},{"internalType":"address[]","name":"rewardTokens","type":"address[]"}],"name":"setHarvester","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseRatePerYear_","type":"uint256"},{"internalType":"uint256","name":"multiplierPerYear_","type":"uint256"},{"internalType":"uint256","name":"kinkMultiplier_","type":"uint256"},{"internalType":"uint256","name":"kink_","type":"uint256"}],"name":"setInterestRateModel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newCygRewarder","type":"address"}],"name":"setPillarsOfCreation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newReserveFactor","type":"uint256"}],"name":"setReserveFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"shuttleId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"sweepToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sync","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBalance","outputs":[{"internalType":"uint160","name":"","type":"uint160"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalBorrows","outputs":[{"internalType":"uint256","name":"borrows","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"borrower","type":"address"}],"name":"trackBorrower","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lender","type":"address"}],"name":"trackLender","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","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":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"underlying","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"utilizationRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
610120806040523462000207576000805460ff19166001178155635a412e5360e01b825260a09190828260048184335af1918215620001fa57818091818091819662000146575b505061010094855260e05260805260c05282526702c68af0bb1400006003556005546503782dace9d960a21b9063ffffffff60e01b4260e01b169060018060901b03161717600555604051906132ee9283620002228439608051838181610608015281816113ef015281816116b601528181611b01015261299901525182818161037d015281816128160152612db1015260c0518281816108bd01528181610a2d01528181610d1c01528181610f39015281816112b10152818161159a015281816116f50152818161188001528181611a5501528181611c280152612ce8015260e051828181611434015261267c01525181818161091001526113400152f35b94509450505050823d8411620001f2575b601f8101601f191682016001600160401b03811183821017620001de578491839160405281010312620001da578051916001600160a01b038084168403620001da57620001a7602084016200020c565b92620001b6604082016200020c565b9260608201519283168303620001d757506080015193929190388062000046565b80fd5b5080fd5b634e487b7160e01b84526041600452602484fd5b503d62000157565b50604051903d90823e3d90fd5b600080fd5b51906001600160a01b0382168203620002075756fe6040608081526004908136101561001557600080fd5b600091823560e01c806301e1d1141461224257806304c607fd146121c25780630572b0cc14611f8457806306fdde0314611f5b578063095ea7b314611ee857806318160ddd14611ec15780631acf558214611ea45780631c44698314611e305780631e44b7fc14611c6f5780631e7dcc0d146119ea57806323b872dd146118f2578063258836fe1461184057806327ce385414611624578063313ce5671461156d5780633644e515146114e55780633ba0b9a9146114c85780634322b714146114a957806347bd3718146114805780634bdaeac1146114585780634ecde849146114145780635b322bbe146113ba5780635d413fa2146113635780635f77f891146113285780636afdd850146112fe5780636c321c8a146112d55780636f307dc31461129157806370a08231146112565780637d6af0791461121a5780637ecebe00146111df57806381f5b6f4146111695780638808e71c14610e4357806395d89b4114610cf05780639ee80c5c14610c99578063a6afed9514610c80578063a9059cbb14610bbc578063aa5af0fd14610b94578063ad2961a314610b1e578063ad7a672f14610af5578063ba0876521461093b578063bcbc33201461087d578063bf199e621461085e578063c0789dcb1461083c578063c818cd3a1461080c578063c914b437146107dc578063d505accf1461062c578063d8dfeb45146105e8578063d959a01614610420578063dcc3e06e146103f1578063dd62ed3e146103a1578063e8a24e1d1461035d578063f1a392da1461033e578063f3fdb15a146102f9578063f4e8aaff146102cd5763fff6cae91461027357600080fd5b346102c957826003193601126102c95782549160ff8316156102bc578360018460ff1980911683556102a36126f2565b6102ab612e99565b6102b3612e99565b82541617815580f35b516306fda65d60e31b8152fd5b8280fd5b5050346102f557816003193601126102f5576020906001600160a01b03600254169051908152f35b5080fd5b5050346102f557816003193601126102f55760809060015481519167ffffffffffffffff80831684528083831c16602085015282851c169083015260c01c6060820152f35b5050346102f557816003193601126102f5576020906008549051908152f35b5050346102f557816003193601126102f557602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b50346102c957816003193601126102c957356001600160a01b039283821682036103ec5760243593841684036103ec576020938452637f5e9f20600c52526034600c20549051908152f35b600080fd5b5050346102f557816003193601126102f55760209051734ce75412dafcebb421e90e42b3fac6db795e4f858152f35b5090346102c957806003193601126102c9578135916001600160a01b03808416918285036103ec576024359067ffffffffffffffff928383116105e457366023840112156105e457828201359384116105e4576024830192602436918660051b0101116105e45783836104c092610495612d93565b60075416986104bb89516104b5816104ae816006612bc4565b03826123c6565b8b612c6f565b612cd3565b8373ffffffffffffffffffffffffffffffffffffffff1960075416176007556801000000000000000083116105d1575060065482600655808310610591575b5060068652855b82811061055657867fddd57fd5151dbcab176b5c0606de5d7aa76cbbce6df7e771c62b17b56e97486c87606088888151938452602084015282015280610550606082016006612bc4565b0390a180f35b600190602061056484612c4c565b930192817ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f015501610506565b827ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f91820191015b8181106105c657506104ff565b8781556001016105b9565b634e487b7160e01b875260419052602486fd5b8780fd5b5050346102f557816003193601126102f557602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b509190346102f55760e03660031901126102f5578235906001600160a01b038083168093036103ec576024359081168091036103ec576064359260443560843560ff811681036107d85761067e61243b565b918251602080940120918742116107cc5786519765383775081901600e52858a5284600c20918254917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8b52868b01958652828b8a8c82017fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc681528b606084019a468c527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9608086019530875260a08120602e525252528688525260a08b015260c08a20604e526042602c208b5260ff16855260a435885260c435606052848060808c60015afa90863d51036107c057019055777f5e9f200000000000000000000000000000000000000000851786526034602c20557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a3528060605280f35b8b63ddafbaef8c52601cfd5b89631a15a3cc8a52601cfd5b8680fd5b5050346102f557816003193601126102f5576020906108056107fc612537565b505050906125c1565b9051908152f35b8382346102f55760203660031901126102f557356001600160a01b03811681036103ec5761083990612b08565b80f35b5050346102f557816003193601126102f55760209060055460e01c9051908152f35b5050346102f557816003193601126102f5576020906006549051908152f35b5050346102f557816003193601126102f55760607f98ccebae89f16f152c93c6a0cdc0d99913b9e62a9fc612471c21ffc299c389fd916108bb612d93565b7f000000000000000000000000000000000000000000000000000000000000000090734ce75412dafcebb421e90e42b3fac6db795e4f85906109006000198385613269565b6001600160a01b038151931683527f00000000000000000000000000000000000000000000000000000000000000006020840152820152a180f35b5090346102c95760603660031901126102c9576001600160a01b039260243584811684358183036103ec57604435968716928388036103ec5784549760ff891615610ae55760ff1998891686556109906126f2565b610998612e99565b843303610ad5575b6805345cdf77eb68f44c805480610abc575083985b8915610aae576109c48a612b7a565b6387a211a2600c528288526020600c2090815490818711610aa35750938593610a51938c9360019a9860209f9a98039055858154039055848a5289877fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8f83a386610a94575b507f0000000000000000000000000000000000000000000000000000000000000000613220565b865190888252898201527ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db873392a4610a88612e99565b82541617905551908152f35b610a9d90612b08565b38610a2a565b63f4d678b88a52601cfd5b885163e3e8d29760e01b8152fd5b610acf90610ac989612e5f565b866130ef565b986109b5565b610ae0833383613043565b6109a0565b86516306fda65d60e31b81528890fd5b5050346102f557816003193601126102f5576001600160a01b036020925460081c169051908152f35b509134610b915780600319360112610b9157610b38612537565b5050509091610b4782846125c1565b60035491670de0b6b3a7640000928303928311610b7e5760208661080587610b7988610b738989613081565b92612647565b613081565b634e487b7160e01b815260118752602490fd5b80fd5b5050346102f557816003193601126102f557602090610bb1612537565b505092519283525050f35b509134610b915781600319360112610b91578235906001600160a01b038216908183036103ec57602435906387a211a2600c5233815260209586600c2090815490818511610c75575090838693920390555284600c208181540190558452600c5160601c337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8680a333610c67575b610c58575b505160018152f35b610c6190612b08565b38610c50565b610c7033612b08565b610c4b565b63f4d678b88452601cfd5b8334610b915780600319360112610b91576108396126f2565b50346102c95760203660031901126102c957356006548110156102c9576001600160a01b03906006602094527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0154169051908152f35b5090346102c957826003193601126102c95782815180936395d89b4160e01b8252816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa918215610e39578392610db1575b610dad8383610da36028825180947f4379675553443a200000000000000000000000000000000000000000000000006020830152610d9381518092602086860191016122ff565b81010360088101855201836123c6565b5191829182612322565b0390f35b9091503d8084833e610dc381836123c6565b810190602081830312610e315780519067ffffffffffffffff8211610e35570181601f82011215610e3157805193610dfa8561241f565b92610e07855194856123c6565b85845260208684010111610b915750610dad93610e2a91602080850191016122ff565b9038610d4c565b8380fd5b8480fd5b81513d85823e3d90fd5b5090346102c9576101203660031901126102c957602435926001600160a01b0380851684358187036103ec5760c0366043190112610e315761010495863567ffffffffffffffff811161116557610e9d903690830161234e565b979086549560ff8716156111555760ff199687168855610ebb6126f2565b610ec3612e99565b6805345cdf77eb68f44c918254801560001461113b575085935b849b851561112b5780611039575b505050610ef7856131f7565b6e22d473030f116ddee9f6b43ac78ba3803b156110355760848a928385938e519687958694631b63c28b60e11b8652338d8701523060248701521660448501527f00000000000000000000000000000000000000000000000000000000000000001660648401525af1801561102b57908891611016575b50505415610fca575b50509060019291610f8a87602099612fbb565b610f9381612b25565b855190815286888201527fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7863392a3610a88612e99565b909796506103e7198101908111611003576020975090610f8a60019493928098610ff66103e889612fbb565b9950508192939450610f77565b634e487b7160e01b855260118852602485fd5b81611020916123c6565b6107d8578638610f6e565b89513d8a823e3d90fd5b8980fd5b6e22d473030f116ddee9f6b43ac78ba3803b15611127578c516302b67b5760e41b8152338982015293604435868116908190036103ec57602486015260643586811680910361112357604486015260843565ffffffffffff908181168091036103ec57606487015260a4359081168091036103ec57608486015260c435928684168094036103ec57856110ec8f96938793839795849660a486015260e43560c486015261010060e48601528401916123fe565b03925af1801561111957908991611104575b80610eeb565b8161110e916123c6565b6105e45787386110fe565b8a513d8b823e3d90fd5b8d80fd5b8b80fd5b8b5163f1ca48b160e01b81528790fd5b61114f906111488b612e5f565b90886130ef565b93610edd565b88516306fda65d60e31b81528490fd5b8580fd5b509190346102f55760203660031901126102f55781549060ff8216156111d05760ff1991821683556111996126f2565b6111a1612e99565b6001600160a01b036007541633036111c157506102ab6001929335612b25565b51630dd8de6360e11b81528390fd5b516306fda65d60e31b81528390fd5b50346102c95760203660031901126102c957356001600160a01b03811681036103ec576020926338377508600c525281600c20549051908152f35b508234610b91576020366003190112610b915750356001600160a01b03811681036103ec57600161124a916124a8565b82519182526020820152f35b50346102c95760203660031901126102c957356001600160a01b03811681036103ec576020926387a211a2600c525281600c20549051908152f35b5050346102f557816003193601126102f557602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5050346102f557816003193601126102f5576020906108056112f5612537565b50505090612647565b5050346102f557816003193601126102f557602090516e22d473030f116ddee9f6b43ac78ba38152f35b5050346102f557816003193601126102f557602090517f00000000000000000000000000000000000000000000000000000000000000008152f35b508290346102f55760203660031901126102f55735906001600160a01b03821682036103ec576387a211a2600c52526113a96020600c20546113a3612e1d565b90613081565b61124a6113b4612667565b82613081565b8382346102f55760203660031901126102f557356001600160a01b03811681036103ec57806113ec83610839936124a8565b507f000000000000000000000000000000000000000000000000000000000000000091612a90565b5050346102f557816003193601126102f557602090516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b5050346102f557816003193601126102f5576020906001600160a01b03600754169051908152f35b5050346102f557816003193601126102f55760209061149d612537565b50505091905051908152f35b5050346102f557816003193601126102f5576020906003549051908152f35b5050346102f557816003193601126102f557602090610805612e1d565b5050346102f557816003193601126102f55760209060a061150461243b565b8381519101208251907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252848201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc683820152466060820152306080820152209051908152f35b509134610b915780600319360112610b915760208251809463313ce56760e01b8252816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9283156116185781936115d7575b60208460ff855191168152f35b9092506020813d8211611610575b816115f2602093836123c6565b810103126102c957519160ff83168303610b91575060ff60206115ca565b3d91506115e5565b509051903d90823e3d90fd5b508290346102f557611636368261237c565b91929086979497549360ff8516156118305760ff19948516885587949392919061165e6126f2565b611666612e99565b611670868b6124a8565b9190508082101561182957505b865191632d3aef6960e21b83526001600160a01b0380941694858a850152848c16948560248601528360448601528460648160209e8f947f0000000000000000000000000000000000000000000000000000000000000000165af193841561102b5788946117fa575b5080611787575b5050611719307f00000000000000000000000000000000000000000000000000000000000000006132b2565b9781891061177957506117338887600198999a9b9c6128b0565b5061173d89612b25565b87519182528982015287878201527fb2b14447b2fb5b250428a70cf03fd19f8ff7ee7e04a46ebe7824df2a0c91a9e360603392a4610a88612e99565b87516307b1bd6f60e21b8152fd5b333b156105e45787908a6117c78b51948593849363e95471c760e01b855233908501528860248501528760448501526080606485015260848401916123fe565b038183335af180156117f057908791156116ed57816117e5916123c6565b61116557858b6116ed565b88513d89823e3d90fd5b9093508a81813d8311611822575b61181281836123c6565b810103126103ec5751928c6116e6565b503d611808565b905061167d565b85516306fda65d60e31b81528790fd5b509190346102f557806003193601126102f5578235906001600160a01b03908183168084036103ec576024359280841684036103ec5761187e612d93565b7f00000000000000000000000000000000000000000000000000000000000000001681149081156118d4575b506118c5575090610839916118bf30836132b2565b91613220565b516307544c5360e11b81528490fd5b734ce75412dafcebb421e90e42b3fac6db795e4f85915014386118aa565b509134610b91576060366003190112610b91576001600160a01b039083358281168082036103ec57602435938416928385036103ec576044358360601b91602098338a52600c93637f5e9f2081178552603485208054600181016119c7575b50506387a211a217845289842090815490818511610c755750839003905586905287822080548201905587525160601c817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8880a36119b8575b50610c5857505160018152f35b6119c190612b08565b386119ab565b8086116119de5785900390556387a211a238611951565b836313be252b8652601cfd5b509190346102f5576119fc368461237c565b9192869496549760ff891615611c615760ff199889168655611a1c6126f2565b611a24612e99565b85976001600160a01b039384821695338703611c51575b87611c18575b80611b85575b505090611a818692611a79307f00000000000000000000000000000000000000000000000000000000000000006132b2565b9384916128b0565b828711611ae1575b50509160209893918360019694611ad3575b88519485528a85015216917f96558a334f4759f0e7c423d68c84721860bd8fbf94ddc4e55158ecb125ad04b5873392a4610a88612e99565b611adc81612b25565b611a9b565b8851906333e3231160e21b825286838301526024820152602081604481887f0000000000000000000000000000000000000000000000000000000000000000165afa90811561102b578891611b4b575b5015611b3d5780611a89565b875163efd5188560e01b8152fd5b90506020813d8211611b7d575b81611b65602093836123c6565b810103126105e4575180151581036105e45738611b31565b3d9150611b58565b8951638f53d74f60e01b815233858201526024810189905260606044820152929a5060209183918291611bbc9160648401916123fe565b03818a335af19081156117f057908692918891611be1575b50611a8190999192611a47565b9250506020823d8211611c10575b81611bfc602093836123c6565b810103126103ec57611a8186925190611bd4565b3d9150611bef565b611c2188612b7a565b611c4c88867f0000000000000000000000000000000000000000000000000000000000000000613220565b611a41565b611c5c883385613043565b611a3b565b86516306fda65d60e31b8152fd5b509190346102f55760803660031901126102f55782356024359160443560643591611c98612d93565b67016345785d8a00008411611e06576709b6e64a8ec6000083108015611e1f575b611e0657600282108015611e15575b611e06576301e133808381028181048503611df357916105509391611d1c611d14899897957ff58b8cc439edada25fd9495b7364aeb1d737dbef59be1cf62e0cc4001c44a3cd9a6130ac565b91870461318d565b77ffffffffffffffff00000000000000000000000000000000611d50611d4b86611d458661318d565b9561247f565b61318d565b916fffffffffffffffff00000000000000007fffffffffffffffff000000000000000000000000000000000000000000000000611d8c8961318d565b8751611d996080826123c6565b606067ffffffffffffffff80961695868352808a1660208401528089168b840152831691015260c01b1694861b16179160801b16171760015551948594859094939260609260808301968352602083015260408201520152565b634e487b7160e01b885260118952602488fd5b51630e49b3b360e41b81528690fd5b5060288211611cc8565b50670dbd2fc137a300008311611cb9565b50346102c95760203660031901126102c957803590611e4d612d93565b6702c68af0bb1400008211611e965750907faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146091600354908060035582519182526020820152a180f35b8251630e49b3b360e41b8152fd5b5050346102f557816003193601126102f557602090610805612667565b5050346102f557816003193601126102f5576020906805345cdf77eb68f44c549051908152f35b50346102c957816003193601126102c95735916001600160a01b03831683036103ec57602092602435908452637f5e9f20600c52338252806034600c20558152602c5160601c907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925843392a35160018152f35b5050346102f557816003193601126102f557610dad90611f7961243b565b905191829182612322565b509190346102f5576003199282843601126102c957825460ff8116156121b35760ff1990811684558251611fb884826123c6565b600195868252602093843681850137855191611fd487846123c6565b88835285368185013730611fe88986612c22565b52734ce75412dafcebb421e90e42b3fac6db795e4f856120088985612c22565b52736ea32f626e3a5c41547235ebbdf861526e11f48292833b156121af578861205595936120648296946080948c51998a9889978896632a75bb7d60e11b8852870152608486019061228e565b9184830301602485015261228e565b8260448301528c606483015203925af180156121a557612191575b5082519361209985612092816006612bc4565b03866123c6565b8451916120a583612c0a565b926120b2865194856123c6565b8084526120c1601f1991612c0a565b01368585013781875b612151575b5095610dad949596426008558651606081527fc5252ef97d1913a56f636a69fa3df5248e702e398f142464175250bb7e9ec1e061211f612112606084018c61228e565b83810389850152876122cb565b91428a8201528033930390a2825416179055612144845195858796875286019061228e565b91848303908501526122cb565b865181101561218c5787809161217a306001600160a01b03612173848d612c22565b51166132b2565b6121848288612c22565b5201906120ca565b6120cf565b8461219e919592956123c6565b923861207f565b84513d87823e3d90fd5b8880fd5b5090516306fda65d60e31b8152fd5b50346102c95760203660031901126102c95735906001600160a01b03808316809303610e31577fb0e815c8557c523830a73bb1eae59599a6588c298c1a2ed253c9261bc65cee2892612212612d93565b600254918173ffffffffffffffffffffffffffffffffffffffff198416176002558351921682526020820152a180f35b5050346102f557816003193601126102f5576108056001600160a01b036020935460081c1661226f612537565b50505091905061252a565b35906001600160a01b03821682036103ec57565b90815180825260208080930193019160005b8281106122ae575050505090565b83516001600160a01b0316855293810193928101926001016122a0565b90815180825260208080930193019160005b8281106122eb575050505090565b8351855293810193928101926001016122dd565b60005b8381106123125750506000910152565b8181015183820152602001612302565b6040916020825261234281518092816020860152602086860191016122ff565b601f01601f1916010190565b9181601f840112156103ec5782359167ffffffffffffffff83116103ec57602083818601950101116103ec57565b906080828203126103ec576123908261227a565b9261239d6020840161227a565b92604081013592606082013567ffffffffffffffff81116103ec576123c2920161234e565b9091565b90601f8019910116810190811067ffffffffffffffff8211176123e857604052565b634e487b7160e01b600052604160045260246000fd5b908060209392818452848401376000828201840152601f01601f1916010190565b67ffffffffffffffff81116123e857601f01601f191660200190565b612445601261241f565b9061245360405192836123c6565b601282527f4379676e75733a20426f72726f7761626c6500000000000000000000000000006020830152565b8181029291811591840414171561249257565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b03909291921660005260046020526040600020548060801c90811561251157906001600160801b036124fb92169369ffffffffffffffffffff60055460901c16906124fe575b846130ef565b90565b50612507612537565b50509150506124f5565b50509050600090600090565b9190820391821161249257565b9190820180921161249257565b6001600160a01b0360005460081c1690600554906001600160901b03821661257269ffffffffffffffffffff8460901c169360e01c4261251d565b9081156125b7576125b461258f8361258a84896125c1565b61247f565b946125ae816125a86125a1868a613081565b809661252a565b97613081565b9061252a565b92565b9291600091508190565b906125cb91612647565b6001548060c01c80831115612621576124fb929161261c9161261167ffffffffffffffff9161260b612602848660401c1683613081565b8486169061252a565b9561251d565b9160801c1690613081565b61252a565b506124fb9161263f67ffffffffffffffff91828460401c1690613081565b91169061252a565b9080612654575050600090565b612661816124fb9361252a565b906130ac565b60405163cadbcc2160e01b81526020816004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa9081156126e6576000916126b8575090565b906020823d82116126de575b816126d1602093836123c6565b81010312610b9157505190565b3d91506126c4565b6040513d6000823e3d90fd5b6126fa612537565b909391931561288b5761270f60035482613081565b806127d8575b6001600160901b03612726856131d0565b16916a01000000000000000000008610156127ca576401000000004210156127ca577f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04956127c5937bffffffffffffffffffff00000000000000000000000000000000000063ffffffff60e01b4260e01b169260901b161717600555604051948594859094939260609260808301968352602083015260408201520152565b0390a1565b6335278d126000526004601cfd5b6128006805345cdf77eb68f44c546127f9836127f4888861252a565b61251d565b90836130ef565b604051630b60d15f60e31b8152906020826004817f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03165afa80156126e6576128589260009161285d575b50612fbb565b612715565b61287e915060203d8111612884575b61287681836123c6565b810190612891565b38612852565b503d61286c565b50505050565b908160209103126103ec57516001600160a01b03811681036103ec5790565b600093916128be85836124a8565b9050848214612a87576129be93949569ffffffffffffffffffff60055460901c16966001600160a01b0385168252600460205260408220918185116000146129c0575061291561293c91612965949503809561252a565b976001600160801b036129278a6131ab565b166001600160801b03198454161783556131ab565b81546001600160801b031660809190911b6fffffffffffffffffffffffffffffffff1916179055565b600554906129856129806001600160901b039283851661252a565b6131d0565b16906001600160901b031916176005555b837f000000000000000000000000000000000000000000000000000000000000000091612a90565b565b93612a3293910380821115612a7c576127f4908203925b83996001600160801b036129ea866131ab565b83546fffffffffffffffffffffffffffffffff1916911617825584612a73575080546001600160801b0316608087901b6fffffffffffffffffffffffffffffffff1916179055565b600554916001600160901b0391612a56918385169082821115612a6b5750036131d0565b16906001600160901b03191617600555612996565b9150506131d0565b61293c906131ab565b506127f484926129d7565b93505050915090565b9190916001600160a01b039081600254169081612aaf575b5050505050565b813b156103ec57600060649284829660405198899788966334beddab60e11b885216600487015260248601521660448401525af180156126e657612af7575b80808080612aa8565b6000612b02916123c6565b38612aee565b60006129be916387a211a2600c528082526020600c205490612a90565b734ce75412dafcebb421e90e42b3fac6db795e4f8590813b156103ec5760009160248392604051948593849263140e25ad60e31b845260048401525af180156126e657612b6f5750565b60006129be916123c6565b734ce75412dafcebb421e90e42b3fac6db795e4f8590813b156103ec5760009160248392604051948593849263852a12e360e01b845260048401525af180156126e657612b6f5750565b90815480825260208092019260005281600020916000905b828210612bea575050505090565b83546001600160a01b031685529384019360019384019390910190612bdc565b67ffffffffffffffff81116123e85760051b60200190565b8051821015612c365760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b356001600160a01b03811681036103ec5790565b60001981146124925760010190565b9190916001600160a01b0380821615612cbd57600093845b8151811015612cb55780612cab878686612ca4612cb09688612c22565b5116613269565b612c60565b612c87565b505050509050565b50509050565b9190811015612c365760051b0190565b91906001600160a01b03928381161561288b577f000000000000000000000000000000000000000000000000000000000000000084169360005b848110612d1c57505050505050565b808683612d35612d30612d47958a8a612cc3565b612c4c565b16141580612d66575b612d4c57612c60565b612d0d565b612cab60001985612d61612d30858b8b612cc3565b613269565b50734ce75412dafcebb421e90e42b3fac6db795e4f8583612d8b612d30848a8a612cc3565b161415612d3e565b6040516303e1469160e61b81526001600160a01b03906020816004817f000000000000000000000000000000000000000000000000000000000000000086165afa9081156126e657600091612dff575b50163303612ded57565b6040516331d63ad160e11b8152600490fd5b612e17915060203d81116128845761287681836123c6565b38612de3565b6805345cdf77eb68f44c5460009080612e3e575050670de0b6b3a764000090565b612e5a6001600160a01b036124fb935460081c1661226f612537565b6130ac565b6124fb906001600160a01b0360005460081c166001600160901b0360055416911561252a579050612e8e612537565b50505090509061252a565b604051633af9e66960e01b815230600482015260009060208160248185734ce75412dafcebb421e90e42b3fac6db795e4f855af1908115612fb0578291612f5f575b507f3145c2b643834867f2c0a8cac8a48218307687911615c0ed01412a09dd163b6691612f096020926131f7565b9080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff008460081b1691161790556001600160a01b0360405191168152a1565b90506020813d8211612fa8575b81612f79602093836123c6565b810103126102f557517f3145c2b643834867f2c0a8cac8a48218307687911615c0ed01412a09dd163b66612edb565b3d9150612f6c565b6040513d84823e3d90fd5b906805345cdf77eb68f44c80549082820191821061303557556387a211a2600c52816000526020600c20818154019055602052600c5160601c60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a36001600160a01b03811661302c5750565b6129be90612b08565b63e5cfe9576000526004601cfd5b90602052637f5e9f20600c526000526034600c209081546001810161306757505050565b80821161307357039055565b6313be252b6000526004601cfd5b9080600019048211810261309e57670de0b6b3a764000091020490565b63bac65e5b6000526004601cfd5b670de0b6b3a7640000907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f2181118202158302156130e157020490565b637c5f487d6000526004601cfd5b91600092828102926000198183099184831085018084039314613171578284111561316457908391099060018386038416808080960460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029703040190848311900302920304170290565b63ae47f70286526004601cfd5b50505091821561318057500490565b63ae47f70290526004601cfd5b680100000000000000008110156127ca5767ffffffffffffffff1690565b7001000000000000000000000000000000008110156127ca576001600160801b031690565b72010000000000000000000000000000000000008110156127ca576001600160901b031690565b740100000000000000000000000000000000000000008110156127ca576001600160a01b031690565b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d15600183511417161561325c57603452565b6390b8ec1890526004601cfd5b60109260209260145260345260446000938480936f095ea7b300000000000000000000000082525af13d1560018351141716156132a557603452565b633e3f8f7390526004601cfd5b602460106020939284936014526f70a082310000000000000000000000006000525afa601f3d1116602051029056fea164736f6c6343000811000a
Deployed Bytecode
0x6040608081526004908136101561001557600080fd5b600091823560e01c806301e1d1141461224257806304c607fd146121c25780630572b0cc14611f8457806306fdde0314611f5b578063095ea7b314611ee857806318160ddd14611ec15780631acf558214611ea45780631c44698314611e305780631e44b7fc14611c6f5780631e7dcc0d146119ea57806323b872dd146118f2578063258836fe1461184057806327ce385414611624578063313ce5671461156d5780633644e515146114e55780633ba0b9a9146114c85780634322b714146114a957806347bd3718146114805780634bdaeac1146114585780634ecde849146114145780635b322bbe146113ba5780635d413fa2146113635780635f77f891146113285780636afdd850146112fe5780636c321c8a146112d55780636f307dc31461129157806370a08231146112565780637d6af0791461121a5780637ecebe00146111df57806381f5b6f4146111695780638808e71c14610e4357806395d89b4114610cf05780639ee80c5c14610c99578063a6afed9514610c80578063a9059cbb14610bbc578063aa5af0fd14610b94578063ad2961a314610b1e578063ad7a672f14610af5578063ba0876521461093b578063bcbc33201461087d578063bf199e621461085e578063c0789dcb1461083c578063c818cd3a1461080c578063c914b437146107dc578063d505accf1461062c578063d8dfeb45146105e8578063d959a01614610420578063dcc3e06e146103f1578063dd62ed3e146103a1578063e8a24e1d1461035d578063f1a392da1461033e578063f3fdb15a146102f9578063f4e8aaff146102cd5763fff6cae91461027357600080fd5b346102c957826003193601126102c95782549160ff8316156102bc578360018460ff1980911683556102a36126f2565b6102ab612e99565b6102b3612e99565b82541617815580f35b516306fda65d60e31b8152fd5b8280fd5b5050346102f557816003193601126102f5576020906001600160a01b03600254169051908152f35b5080fd5b5050346102f557816003193601126102f55760809060015481519167ffffffffffffffff80831684528083831c16602085015282851c169083015260c01c6060820152f35b5050346102f557816003193601126102f5576020906008549051908152f35b5050346102f557816003193601126102f557602090516001600160a01b037f0000000000000000000000008796747946871b6b8ea495cce8d7814b17959296168152f35b50346102c957816003193601126102c957356001600160a01b039283821682036103ec5760243593841684036103ec576020938452637f5e9f20600c52526034600c20549051908152f35b600080fd5b5050346102f557816003193601126102f55760209051734ce75412dafcebb421e90e42b3fac6db795e4f858152f35b5090346102c957806003193601126102c9578135916001600160a01b03808416918285036103ec576024359067ffffffffffffffff928383116105e457366023840112156105e457828201359384116105e4576024830192602436918660051b0101116105e45783836104c092610495612d93565b60075416986104bb89516104b5816104ae816006612bc4565b03826123c6565b8b612c6f565b612cd3565b8373ffffffffffffffffffffffffffffffffffffffff1960075416176007556801000000000000000083116105d1575060065482600655808310610591575b5060068652855b82811061055657867fddd57fd5151dbcab176b5c0606de5d7aa76cbbce6df7e771c62b17b56e97486c87606088888151938452602084015282015280610550606082016006612bc4565b0390a180f35b600190602061056484612c4c565b930192817ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f015501610506565b827ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f91820191015b8181106105c657506104ff565b8781556001016105b9565b634e487b7160e01b875260419052602486fd5b8780fd5b5050346102f557816003193601126102f557602090516001600160a01b037f0000000000000000000000009222e903087877dbd96ea48ec489118d8f8d9519168152f35b509190346102f55760e03660031901126102f5578235906001600160a01b038083168093036103ec576024359081168091036103ec576064359260443560843560ff811681036107d85761067e61243b565b918251602080940120918742116107cc5786519765383775081901600e52858a5284600c20918254917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8b52868b01958652828b8a8c82017fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc681528b606084019a468c527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9608086019530875260a08120602e525252528688525260a08b015260c08a20604e526042602c208b5260ff16855260a435885260c435606052848060808c60015afa90863d51036107c057019055777f5e9f200000000000000000000000000000000000000000851786526034602c20557f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259190a3528060605280f35b8b63ddafbaef8c52601cfd5b89631a15a3cc8a52601cfd5b8680fd5b5050346102f557816003193601126102f5576020906108056107fc612537565b505050906125c1565b9051908152f35b8382346102f55760203660031901126102f557356001600160a01b03811681036103ec5761083990612b08565b80f35b5050346102f557816003193601126102f55760209060055460e01c9051908152f35b5050346102f557816003193601126102f5576020906006549051908152f35b5050346102f557816003193601126102f55760607f98ccebae89f16f152c93c6a0cdc0d99913b9e62a9fc612471c21ffc299c389fd916108bb612d93565b7f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e590734ce75412dafcebb421e90e42b3fac6db795e4f85906109006000198385613269565b6001600160a01b038151931683527f00000000000000000000000000000000000000000000000000000000000000046020840152820152a180f35b5090346102c95760603660031901126102c9576001600160a01b039260243584811684358183036103ec57604435968716928388036103ec5784549760ff891615610ae55760ff1998891686556109906126f2565b610998612e99565b843303610ad5575b6805345cdf77eb68f44c805480610abc575083985b8915610aae576109c48a612b7a565b6387a211a2600c528288526020600c2090815490818711610aa35750938593610a51938c9360019a9860209f9a98039055858154039055848a5289877fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8f83a386610a94575b507f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5613220565b865190888252898201527ffbde797d201c681b91056529119e0b02407c7bb96a4a2c75c01fc9667232c8db873392a4610a88612e99565b82541617905551908152f35b610a9d90612b08565b38610a2a565b63f4d678b88a52601cfd5b885163e3e8d29760e01b8152fd5b610acf90610ac989612e5f565b866130ef565b986109b5565b610ae0833383613043565b6109a0565b86516306fda65d60e31b81528890fd5b5050346102f557816003193601126102f5576001600160a01b036020925460081c169051908152f35b509134610b915780600319360112610b9157610b38612537565b5050509091610b4782846125c1565b60035491670de0b6b3a7640000928303928311610b7e5760208661080587610b7988610b738989613081565b92612647565b613081565b634e487b7160e01b815260118752602490fd5b80fd5b5050346102f557816003193601126102f557602090610bb1612537565b505092519283525050f35b509134610b915781600319360112610b91578235906001600160a01b038216908183036103ec57602435906387a211a2600c5233815260209586600c2090815490818511610c75575090838693920390555284600c208181540190558452600c5160601c337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8680a333610c67575b610c58575b505160018152f35b610c6190612b08565b38610c50565b610c7033612b08565b610c4b565b63f4d678b88452601cfd5b8334610b915780600319360112610b91576108396126f2565b50346102c95760203660031901126102c957356006548110156102c9576001600160a01b03906006602094527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f0154169051908152f35b5090346102c957826003193601126102c95782815180936395d89b4160e01b8252816001600160a01b037f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5165afa918215610e39578392610db1575b610dad8383610da36028825180947f4379675553443a200000000000000000000000000000000000000000000000006020830152610d9381518092602086860191016122ff565b81010360088101855201836123c6565b5191829182612322565b0390f35b9091503d8084833e610dc381836123c6565b810190602081830312610e315780519067ffffffffffffffff8211610e35570181601f82011215610e3157805193610dfa8561241f565b92610e07855194856123c6565b85845260208684010111610b915750610dad93610e2a91602080850191016122ff565b9038610d4c565b8380fd5b8480fd5b81513d85823e3d90fd5b5090346102c9576101203660031901126102c957602435926001600160a01b0380851684358187036103ec5760c0366043190112610e315761010495863567ffffffffffffffff811161116557610e9d903690830161234e565b979086549560ff8716156111555760ff199687168855610ebb6126f2565b610ec3612e99565b6805345cdf77eb68f44c918254801560001461113b575085935b849b851561112b5780611039575b505050610ef7856131f7565b6e22d473030f116ddee9f6b43ac78ba3803b156110355760848a928385938e519687958694631b63c28b60e11b8652338d8701523060248701521660448501527f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e51660648401525af1801561102b57908891611016575b50505415610fca575b50509060019291610f8a87602099612fbb565b610f9381612b25565b855190815286888201527fdcbc1c05240f31ff3ad067ef1ee35ce4997762752e3a095284754544f4c709d7863392a3610a88612e99565b909796506103e7198101908111611003576020975090610f8a60019493928098610ff66103e889612fbb565b9950508192939450610f77565b634e487b7160e01b855260118852602485fd5b81611020916123c6565b6107d8578638610f6e565b89513d8a823e3d90fd5b8980fd5b6e22d473030f116ddee9f6b43ac78ba3803b15611127578c516302b67b5760e41b8152338982015293604435868116908190036103ec57602486015260643586811680910361112357604486015260843565ffffffffffff908181168091036103ec57606487015260a4359081168091036103ec57608486015260c435928684168094036103ec57856110ec8f96938793839795849660a486015260e43560c486015261010060e48601528401916123fe565b03925af1801561111957908991611104575b80610eeb565b8161110e916123c6565b6105e45787386110fe565b8a513d8b823e3d90fd5b8d80fd5b8b80fd5b8b5163f1ca48b160e01b81528790fd5b61114f906111488b612e5f565b90886130ef565b93610edd565b88516306fda65d60e31b81528490fd5b8580fd5b509190346102f55760203660031901126102f55781549060ff8216156111d05760ff1991821683556111996126f2565b6111a1612e99565b6001600160a01b036007541633036111c157506102ab6001929335612b25565b51630dd8de6360e11b81528390fd5b516306fda65d60e31b81528390fd5b50346102c95760203660031901126102c957356001600160a01b03811681036103ec576020926338377508600c525281600c20549051908152f35b508234610b91576020366003190112610b915750356001600160a01b03811681036103ec57600161124a916124a8565b82519182526020820152f35b50346102c95760203660031901126102c957356001600160a01b03811681036103ec576020926387a211a2600c525281600c20549051908152f35b5050346102f557816003193601126102f557602090516001600160a01b037f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5168152f35b5050346102f557816003193601126102f5576020906108056112f5612537565b50505090612647565b5050346102f557816003193601126102f557602090516e22d473030f116ddee9f6b43ac78ba38152f35b5050346102f557816003193601126102f557602090517f00000000000000000000000000000000000000000000000000000000000000048152f35b508290346102f55760203660031901126102f55735906001600160a01b03821682036103ec576387a211a2600c52526113a96020600c20546113a3612e1d565b90613081565b61124a6113b4612667565b82613081565b8382346102f55760203660031901126102f557356001600160a01b03811681036103ec57806113ec83610839936124a8565b507f0000000000000000000000009222e903087877dbd96ea48ec489118d8f8d951991612a90565b5050346102f557816003193601126102f557602090516001600160a01b037f0000000000000000000000004d26dff79b745472ff436407f634b14d1d9571ff168152f35b5050346102f557816003193601126102f5576020906001600160a01b03600754169051908152f35b5050346102f557816003193601126102f55760209061149d612537565b50505091905051908152f35b5050346102f557816003193601126102f5576020906003549051908152f35b5050346102f557816003193601126102f557602090610805612e1d565b5050346102f557816003193601126102f55760209060a061150461243b565b8381519101208251907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252848201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc683820152466060820152306080820152209051908152f35b509134610b915780600319360112610b915760208251809463313ce56760e01b8252816001600160a01b037f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5165afa9283156116185781936115d7575b60208460ff855191168152f35b9092506020813d8211611610575b816115f2602093836123c6565b810103126102c957519160ff83168303610b91575060ff60206115ca565b3d91506115e5565b509051903d90823e3d90fd5b508290346102f557611636368261237c565b91929086979497549360ff8516156118305760ff19948516885587949392919061165e6126f2565b611666612e99565b611670868b6124a8565b9190508082101561182957505b865191632d3aef6960e21b83526001600160a01b0380941694858a850152848c16948560248601528360448601528460648160209e8f947f0000000000000000000000009222e903087877dbd96ea48ec489118d8f8d9519165af193841561102b5788946117fa575b5080611787575b5050611719307f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e56132b2565b9781891061177957506117338887600198999a9b9c6128b0565b5061173d89612b25565b87519182528982015287878201527fb2b14447b2fb5b250428a70cf03fd19f8ff7ee7e04a46ebe7824df2a0c91a9e360603392a4610a88612e99565b87516307b1bd6f60e21b8152fd5b333b156105e45787908a6117c78b51948593849363e95471c760e01b855233908501528860248501528760448501526080606485015260848401916123fe565b038183335af180156117f057908791156116ed57816117e5916123c6565b61116557858b6116ed565b88513d89823e3d90fd5b9093508a81813d8311611822575b61181281836123c6565b810103126103ec5751928c6116e6565b503d611808565b905061167d565b85516306fda65d60e31b81528790fd5b509190346102f557806003193601126102f5578235906001600160a01b03908183168084036103ec576024359280841684036103ec5761187e612d93565b7f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e51681149081156118d4575b506118c5575090610839916118bf30836132b2565b91613220565b516307544c5360e11b81528490fd5b734ce75412dafcebb421e90e42b3fac6db795e4f85915014386118aa565b509134610b91576060366003190112610b91576001600160a01b039083358281168082036103ec57602435938416928385036103ec576044358360601b91602098338a52600c93637f5e9f2081178552603485208054600181016119c7575b50506387a211a217845289842090815490818511610c755750839003905586905287822080548201905587525160601c817fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8880a36119b8575b50610c5857505160018152f35b6119c190612b08565b386119ab565b8086116119de5785900390556387a211a238611951565b836313be252b8652601cfd5b509190346102f5576119fc368461237c565b9192869496549760ff891615611c615760ff199889168655611a1c6126f2565b611a24612e99565b85976001600160a01b039384821695338703611c51575b87611c18575b80611b85575b505090611a818692611a79307f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e56132b2565b9384916128b0565b828711611ae1575b50509160209893918360019694611ad3575b88519485528a85015216917f96558a334f4759f0e7c423d68c84721860bd8fbf94ddc4e55158ecb125ad04b5873392a4610a88612e99565b611adc81612b25565b611a9b565b8851906333e3231160e21b825286838301526024820152602081604481887f0000000000000000000000009222e903087877dbd96ea48ec489118d8f8d9519165afa90811561102b578891611b4b575b5015611b3d5780611a89565b875163efd5188560e01b8152fd5b90506020813d8211611b7d575b81611b65602093836123c6565b810103126105e4575180151581036105e45738611b31565b3d9150611b58565b8951638f53d74f60e01b815233858201526024810189905260606044820152929a5060209183918291611bbc9160648401916123fe565b03818a335af19081156117f057908692918891611be1575b50611a8190999192611a47565b9250506020823d8211611c10575b81611bfc602093836123c6565b810103126103ec57611a8186925190611bd4565b3d9150611bef565b611c2188612b7a565b611c4c88867f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e5613220565b611a41565b611c5c883385613043565b611a3b565b86516306fda65d60e31b8152fd5b509190346102f55760803660031901126102f55782356024359160443560643591611c98612d93565b67016345785d8a00008411611e06576709b6e64a8ec6000083108015611e1f575b611e0657600282108015611e15575b611e06576301e133808381028181048503611df357916105509391611d1c611d14899897957ff58b8cc439edada25fd9495b7364aeb1d737dbef59be1cf62e0cc4001c44a3cd9a6130ac565b91870461318d565b77ffffffffffffffff00000000000000000000000000000000611d50611d4b86611d458661318d565b9561247f565b61318d565b916fffffffffffffffff00000000000000007fffffffffffffffff000000000000000000000000000000000000000000000000611d8c8961318d565b8751611d996080826123c6565b606067ffffffffffffffff80961695868352808a1660208401528089168b840152831691015260c01b1694861b16179160801b16171760015551948594859094939260609260808301968352602083015260408201520152565b634e487b7160e01b885260118952602488fd5b51630e49b3b360e41b81528690fd5b5060288211611cc8565b50670dbd2fc137a300008311611cb9565b50346102c95760203660031901126102c957803590611e4d612d93565b6702c68af0bb1400008211611e965750907faaa68312e2ea9d50e16af5068410ab56e1a1fd06037b1a35664812c30f82146091600354908060035582519182526020820152a180f35b8251630e49b3b360e41b8152fd5b5050346102f557816003193601126102f557602090610805612667565b5050346102f557816003193601126102f5576020906805345cdf77eb68f44c549051908152f35b50346102c957816003193601126102c95735916001600160a01b03831683036103ec57602092602435908452637f5e9f20600c52338252806034600c20558152602c5160601c907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925843392a35160018152f35b5050346102f557816003193601126102f557610dad90611f7961243b565b905191829182612322565b509190346102f5576003199282843601126102c957825460ff8116156121b35760ff1990811684558251611fb884826123c6565b600195868252602093843681850137855191611fd487846123c6565b88835285368185013730611fe88986612c22565b52734ce75412dafcebb421e90e42b3fac6db795e4f856120088985612c22565b52736ea32f626e3a5c41547235ebbdf861526e11f48292833b156121af578861205595936120648296946080948c51998a9889978896632a75bb7d60e11b8852870152608486019061228e565b9184830301602485015261228e565b8260448301528c606483015203925af180156121a557612191575b5082519361209985612092816006612bc4565b03866123c6565b8451916120a583612c0a565b926120b2865194856123c6565b8084526120c1601f1991612c0a565b01368585013781875b612151575b5095610dad949596426008558651606081527fc5252ef97d1913a56f636a69fa3df5248e702e398f142464175250bb7e9ec1e061211f612112606084018c61228e565b83810389850152876122cb565b91428a8201528033930390a2825416179055612144845195858796875286019061228e565b91848303908501526122cb565b865181101561218c5787809161217a306001600160a01b03612173848d612c22565b51166132b2565b6121848288612c22565b5201906120ca565b6120cf565b8461219e919592956123c6565b923861207f565b84513d87823e3d90fd5b8880fd5b5090516306fda65d60e31b8152fd5b50346102c95760203660031901126102c95735906001600160a01b03808316809303610e31577fb0e815c8557c523830a73bb1eae59599a6588c298c1a2ed253c9261bc65cee2892612212612d93565b600254918173ffffffffffffffffffffffffffffffffffffffff198416176002558351921682526020820152a180f35b5050346102f557816003193601126102f5576108056001600160a01b036020935460081c1661226f612537565b50505091905061252a565b35906001600160a01b03821682036103ec57565b90815180825260208080930193019160005b8281106122ae575050505090565b83516001600160a01b0316855293810193928101926001016122a0565b90815180825260208080930193019160005b8281106122eb575050505090565b8351855293810193928101926001016122dd565b60005b8381106123125750506000910152565b8181015183820152602001612302565b6040916020825261234281518092816020860152602086860191016122ff565b601f01601f1916010190565b9181601f840112156103ec5782359167ffffffffffffffff83116103ec57602083818601950101116103ec57565b906080828203126103ec576123908261227a565b9261239d6020840161227a565b92604081013592606082013567ffffffffffffffff81116103ec576123c2920161234e565b9091565b90601f8019910116810190811067ffffffffffffffff8211176123e857604052565b634e487b7160e01b600052604160045260246000fd5b908060209392818452848401376000828201840152601f01601f1916010190565b67ffffffffffffffff81116123e857601f01601f191660200190565b612445601261241f565b9061245360405192836123c6565b601282527f4379676e75733a20426f72726f7761626c6500000000000000000000000000006020830152565b8181029291811591840414171561249257565b634e487b7160e01b600052601160045260246000fd5b6001600160a01b03909291921660005260046020526040600020548060801c90811561251157906001600160801b036124fb92169369ffffffffffffffffffff60055460901c16906124fe575b846130ef565b90565b50612507612537565b50509150506124f5565b50509050600090600090565b9190820391821161249257565b9190820180921161249257565b6001600160a01b0360005460081c1690600554906001600160901b03821661257269ffffffffffffffffffff8460901c169360e01c4261251d565b9081156125b7576125b461258f8361258a84896125c1565b61247f565b946125ae816125a86125a1868a613081565b809661252a565b97613081565b9061252a565b92565b9291600091508190565b906125cb91612647565b6001548060c01c80831115612621576124fb929161261c9161261167ffffffffffffffff9161260b612602848660401c1683613081565b8486169061252a565b9561251d565b9160801c1690613081565b61252a565b506124fb9161263f67ffffffffffffffff91828460401c1690613081565b91169061252a565b9080612654575050600090565b612661816124fb9361252a565b906130ac565b60405163cadbcc2160e01b81526020816004817f0000000000000000000000004d26dff79b745472ff436407f634b14d1d9571ff6001600160a01b03165afa9081156126e6576000916126b8575090565b906020823d82116126de575b816126d1602093836123c6565b81010312610b9157505190565b3d91506126c4565b6040513d6000823e3d90fd5b6126fa612537565b909391931561288b5761270f60035482613081565b806127d8575b6001600160901b03612726856131d0565b16916a01000000000000000000008610156127ca576401000000004210156127ca577f4dec04e750ca11537cabcd8a9eab06494de08da3735bc8871cd41250e190bc04956127c5937bffffffffffffffffffff00000000000000000000000000000000000063ffffffff60e01b4260e01b169260901b161717600555604051948594859094939260609260808301968352602083015260408201520152565b0390a1565b6335278d126000526004601cfd5b6128006805345cdf77eb68f44c546127f9836127f4888861252a565b61251d565b90836130ef565b604051630b60d15f60e31b8152906020826004817f0000000000000000000000008796747946871b6b8ea495cce8d7814b179592966001600160a01b03165afa80156126e6576128589260009161285d575b50612fbb565b612715565b61287e915060203d8111612884575b61287681836123c6565b810190612891565b38612852565b503d61286c565b50505050565b908160209103126103ec57516001600160a01b03811681036103ec5790565b600093916128be85836124a8565b9050848214612a87576129be93949569ffffffffffffffffffff60055460901c16966001600160a01b0385168252600460205260408220918185116000146129c0575061291561293c91612965949503809561252a565b976001600160801b036129278a6131ab565b166001600160801b03198454161783556131ab565b81546001600160801b031660809190911b6fffffffffffffffffffffffffffffffff1916179055565b600554906129856129806001600160901b039283851661252a565b6131d0565b16906001600160901b031916176005555b837f0000000000000000000000009222e903087877dbd96ea48ec489118d8f8d951991612a90565b565b93612a3293910380821115612a7c576127f4908203925b83996001600160801b036129ea866131ab565b83546fffffffffffffffffffffffffffffffff1916911617825584612a73575080546001600160801b0316608087901b6fffffffffffffffffffffffffffffffff1916179055565b600554916001600160901b0391612a56918385169082821115612a6b5750036131d0565b16906001600160901b03191617600555612996565b9150506131d0565b61293c906131ab565b506127f484926129d7565b93505050915090565b9190916001600160a01b039081600254169081612aaf575b5050505050565b813b156103ec57600060649284829660405198899788966334beddab60e11b885216600487015260248601521660448401525af180156126e657612af7575b80808080612aa8565b6000612b02916123c6565b38612aee565b60006129be916387a211a2600c528082526020600c205490612a90565b734ce75412dafcebb421e90e42b3fac6db795e4f8590813b156103ec5760009160248392604051948593849263140e25ad60e31b845260048401525af180156126e657612b6f5750565b60006129be916123c6565b734ce75412dafcebb421e90e42b3fac6db795e4f8590813b156103ec5760009160248392604051948593849263852a12e360e01b845260048401525af180156126e657612b6f5750565b90815480825260208092019260005281600020916000905b828210612bea575050505090565b83546001600160a01b031685529384019360019384019390910190612bdc565b67ffffffffffffffff81116123e85760051b60200190565b8051821015612c365760209160051b010190565b634e487b7160e01b600052603260045260246000fd5b356001600160a01b03811681036103ec5790565b60001981146124925760010190565b9190916001600160a01b0380821615612cbd57600093845b8151811015612cb55780612cab878686612ca4612cb09688612c22565b5116613269565b612c60565b612c87565b505050509050565b50509050565b9190811015612c365760051b0190565b91906001600160a01b03928381161561288b577f00000000000000000000000037eaa0ef3549a5bb7d431be78a3d99bd360d19e584169360005b848110612d1c57505050505050565b808683612d35612d30612d47958a8a612cc3565b612c4c565b16141580612d66575b612d4c57612c60565b612d0d565b612cab60001985612d61612d30858b8b612cc3565b613269565b50734ce75412dafcebb421e90e42b3fac6db795e4f8583612d8b612d30848a8a612cc3565b161415612d3e565b6040516303e1469160e61b81526001600160a01b03906020816004817f0000000000000000000000008796747946871b6b8ea495cce8d7814b1795929686165afa9081156126e657600091612dff575b50163303612ded57565b6040516331d63ad160e11b8152600490fd5b612e17915060203d81116128845761287681836123c6565b38612de3565b6805345cdf77eb68f44c5460009080612e3e575050670de0b6b3a764000090565b612e5a6001600160a01b036124fb935460081c1661226f612537565b6130ac565b6124fb906001600160a01b0360005460081c166001600160901b0360055416911561252a579050612e8e612537565b50505090509061252a565b604051633af9e66960e01b815230600482015260009060208160248185734ce75412dafcebb421e90e42b3fac6db795e4f855af1908115612fb0578291612f5f575b507f3145c2b643834867f2c0a8cac8a48218307687911615c0ed01412a09dd163b6691612f096020926131f7565b9080547fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff008460081b1691161790556001600160a01b0360405191168152a1565b90506020813d8211612fa8575b81612f79602093836123c6565b810103126102f557517f3145c2b643834867f2c0a8cac8a48218307687911615c0ed01412a09dd163b66612edb565b3d9150612f6c565b6040513d84823e3d90fd5b906805345cdf77eb68f44c80549082820191821061303557556387a211a2600c52816000526020600c20818154019055602052600c5160601c60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a36001600160a01b03811661302c5750565b6129be90612b08565b63e5cfe9576000526004601cfd5b90602052637f5e9f20600c526000526034600c209081546001810161306757505050565b80821161307357039055565b6313be252b6000526004601cfd5b9080600019048211810261309e57670de0b6b3a764000091020490565b63bac65e5b6000526004601cfd5b670de0b6b3a7640000907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f2181118202158302156130e157020490565b637c5f487d6000526004601cfd5b91600092828102926000198183099184831085018084039314613171578284111561316457908391099060018386038416808080960460028082600302188083028203028083028203028083028203028083028203028083028203028092029003029703040190848311900302920304170290565b63ae47f70286526004601cfd5b50505091821561318057500490565b63ae47f70290526004601cfd5b680100000000000000008110156127ca5767ffffffffffffffff1690565b7001000000000000000000000000000000008110156127ca576001600160801b031690565b72010000000000000000000000000000000000008110156127ca576001600160901b031690565b740100000000000000000000000000000000000000008110156127ca576001600160a01b031690565b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d15600183511417161561325c57603452565b6390b8ec1890526004601cfd5b60109260209260145260345260446000938480936f095ea7b300000000000000000000000082525af13d1560018351141716156132a557603452565b633e3f8f7390526004601cfd5b602460106020939284936014526f70a082310000000000000000000000006000525afa601f3d1116602051029056fea164736f6c6343000811000a
[ Download: CSV Export ]
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.