Source Code
Overview
MON Balance
MON Value
$0.00Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Set Linked Token | 48577070 | 14 days ago | IN | 0 MON | 0.00976497 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
DynamicIRM
Compiler Version
v0.8.28+commit.7893614a
Contract Source Code (Solidity)
/**
*Submitted for verification at monadscan.com on 2026-01-15
*/
// SPDX-License-Identifier: BUSL-1.1
pragma solidity =0.8.28;
// contracts/interfaces/ICentralRegistry.sol
/// TYPES ///
/// @notice Configuration data for a separately supported blockchain.
/// @param isSupported Whether the chain is supported or not.
/// @param messagingChainId Messaging Chain ID where this address authorized.
/// @param domain Domain for the chain.
/// @param messagingHub Messaging Hub address on the chain.
/// @param votingHub Voting Hub address on the chain.
/// @param cveAddress CVE address on the chain.
/// @param feeTokenAddress Fee token address on the chain.
/// @param crosschainRelayer Crosschain relayer address on the chain.
struct ChainConfig {
bool isSupported;
uint16 messagingChainId;
uint32 domain;
address messagingHub;
address votingHub;
address cveAddress;
address feeTokenAddress;
address crosschainRelayer;
}
interface ICentralRegistry {
/// @notice The length of one protocol epoch, in seconds.
function EPOCH_DURATION() external view returns (uint256);
/// @notice Sequencer uptime oracle on this chain (for L2s).
function SEQUENCER_ORACLE() external view returns (address);
/// @notice Returns Genesis Epoch Timestamp of Curvance.
function genesisEpoch() external view returns (uint256);
/// @notice Returns Protocol DAO address.
function daoAddress() external view returns (address);
/// @notice Returns Protocol Emergency Council address.
function emergencyCouncil() external view returns (address);
/// @notice Indicates if address has DAO permissions or not.
function hasDaoPermissions(
address addressToCheck
) external view returns (bool);
/// @notice Indicates if address has elevated DAO permissions or not.
function hasElevatedPermissions(
address addressToCheck
) external view returns (bool);
/// @notice Indicates if address has lock creation permissions or not.
function hasLockingPermissions(
address addressToCheck
) external view returns (bool);
/// @notice Indicates if address has auction permissions or not.
function hasAuctionPermissions(
address addressToCheck
) external view returns (bool);
/// @notice Indicates if address has market permissions or not.
function hasMarketPermissions(
address addressToCheck
) external view returns (bool);
/// @notice Indicates if address has harvest permissions or not.
function hasHarvestPermissions(
address addressToCheck
) external view returns (bool);
/// @notice Returns Reward Manager address.
function rewardManager() external view returns (address);
/// @notice Returns Gauge Manager address.
function gaugeManager() external view returns (address);
/// @notice Returns CVE address.
function cve() external view returns (address);
/// @notice Returns veCVE address.
function veCVE() external view returns (address);
/// @notice Returns Voting Hub address.
function votingHub() external view returns (address);
/// @notice Returns Messaging Hub address.
function messagingHub() external view returns (address);
/// @notice Returns Oracle Manager address.
function oracleManager() external view returns (address);
/// @notice Returns Fee Manager address.
function feeManager() external view returns (address);
/// @notice Returns Fee Token address.
function feeToken() external view returns (address);
/// @notice Returns Crosschain Core contract address.
function crosschainCore() external view returns (address);
/// @notice Returns Crosschain Relayer contract address.
function crosschainRelayer() external view returns (address);
/// @notice Returns Token Messenger contract address.
function tokenMessager() external view returns (address);
/// @notice Returns Messenger Transmitter contract address.
function messageTransmitter() external view returns (address);
/// @notice Returns domain value.
function domain() external view returns (uint32);
/// @notice Returns protocol gas fee on harvest, in `BPS`.
function protocolCompoundFee() external view returns (uint256);
/// @notice Returns protocol yield fee on strategy harvest, in `BPS`.
function protocolYieldFee() external view returns (uint256);
/// @notice Returns protocol yield + gas fee on strategy harvest,
/// in `BPS`.
function protocolHarvestFee() external view returns (uint256);
/// @notice Returns protocol fee on leverage actions, in `BPS`.
function protocolLeverageFee() external view returns (uint256);
/// @notice Returns default fee on interest generated from active loans,
/// in `BPS`.
function defaultProtocolInterestFee() external view returns (uint256);
/// @notice Returns earlyUnlockPenaltyMultiplier value, in `BPS`.
function earlyUnlockPenaltyMultiplier() external view returns (uint256);
/// @notice Returns voteBoostMultiplier value, in `BPS`.
function voteBoostMultiplier() external view returns (uint256);
/// @notice Returns lockBoostMultiplier value, in `BPS`.
function lockBoostMultiplier() external view returns (uint256);
/// @notice Returns swap slippage limit, in `BPS`.
function slippageLimit() external view returns (uint256);
/// @notice Returns an array of Chain IDs recorded in the Crosschain
/// Protocol's Chain ID format.
function foreignChainIds() external view returns (uint256[] memory);
/// @notice Returns an array of Curvance markets on this chain.
function marketManagers() external view returns (address[] memory);
/// @notice Increments a caller's approval index.
/// @dev By incrementing their approval index, a user's delegates will all
/// have their delegation authority revoked across all Curvance
/// contracts.
/// Emits an {ApprovalIndexIncremented} event.
function incrementApprovalIndex() external;
/// @notice Returns `user`'s approval index.
/// @param user The user to check approval index for.
function userApprovalIndex(address user) external view returns (uint256);
/// @notice Returns whether a user has delegation disabled.
/// @param user The user to check delegation status for.
function checkNewDelegationDisabled(
address user
) external view returns (bool);
/// @notice Returns whether a particular GETH chainId is supported.
/// ChainId => messagingHub address, 2 = supported; 1 = unsupported.
function chainConfig(
uint256 chainId
) external view returns (ChainConfig memory);
/// @notice Returns the GETH chainId corresponding chainId corresponding
/// to Crosschain Messaging Protocol's `chainId`.
/// @param chainId The Crosschain Messaging Protocol's chainId.
/// @return The GETH chainId corresponding chainId corresponding to
/// Crosschain Messaging Protocol's `chainId`.
function messagingToGETHChainId(
uint16 chainId
) external view returns (uint256);
/// @notice Returns the Crosschain Messaging Protocol's ChainId
/// corresponding to the GETH `chainId`.
/// @param chainId The GETH chainId.
/// @return The Crosschain Messaging Protocol's ChainId
/// corresponding to the GETH `chainId`.
function GETHToMessagingChainId(
uint256 chainId
) external view returns (uint256);
/// @notice Indicates if an address is a market manager or not.
function isMarketManager(
address addressToCheck
) external view returns (bool);
/// @notice Maps an intent target address to the contract that will
/// inspect provided external calldata.
function externalCalldataChecker(
address addressToCheck
) external view returns (address);
/// @notice Maps a Multicall target address to the contract that will
/// inspect provided multicall calldata.
function multicallChecker(
address addressToCheck
) external view returns (address);
/// @notice Indicates the amount of token rewards allocated on this chain,
/// for an epoch.
function emissionsAllocatedByEpoch(
uint256 epoch
) external view returns (uint256);
/// @notice Indicates the amount of token rewards allocated across all
/// chains, for an era. An era is a particular period in time in
/// which rewards are constant, before a halvening event moves the
/// protocol to a new era.
function targetEmissionAllocationByEra(
uint256 era
) external view returns (uint256);
/// @notice Unlocks a market to process auction-based liquidations.
/// @param marketToUnlock The address of the market manager to unlock
/// auction-based liquidations with a specific
/// liquidation bonus.
function unlockAuctionForMarket(address marketToUnlock) external;
/// @notice Checks if a market is unlocked for auction operations.
/// @return Whether the caller is an unlocked market, approved for
/// auction-based liquidations.
function isMarketUnlocked() external view returns (bool);
/// @notice Sets the amount of token rewards allocated on this chain,
/// for an epoch.
/// @dev Only callable by the Voting Hub.
/// @param epoch The epoch having its token emission values set.
/// @param emissionsAllocated The amount of token rewards allocated on
/// this chain, for an epoch.
function setEmissionsAllocatedByEpoch(
uint256 epoch,
uint256 emissionsAllocated
) external;
/// @notice Checks whether `user` has transferability enabled or disabled
/// for their tokens.
/// @dev This is inherited from ActionRegistry portion of centralRegistry.
/// @param user The address to check whether transferability is enabled or
/// disabled for.
/// @return result Indicates whether `user` has transferability disabled
/// or not, true = disabled, false = not disabled.
function checkTransfersDisabled(
address user
) external view returns (bool result);
}
// contracts/interfaces/IDynamicIRM.sol
interface IDynamicIRM {
/// @notice Returns the interval at which interest rates are adjusted.
/// @notice The interval at which interest rates are adjusted,
/// in seconds.
function ADJUSTMENT_RATE() external view returns (uint256);
/// @notice The borrowable token linked to this interest rate model
/// contract.
function linkedToken() external view returns (address);
/// @notice Calculates the current borrow rate, per second.
/// @dev This function's intention is for frontend data querying and
/// should not be used for onchain execution.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param debt The amount of outstanding debt in the pool.
/// @return result The borrow interest rate percentage, per second,
/// in `WAD`.
function borrowRate(
uint256 assetsHeld,
uint256 debt
) external view returns (uint256 result);
/// @notice Calculates the current borrow rate per second,
/// with updated vertex multiplier applied.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param debt The amount of outstanding debt in the pool.
/// @return result The borrow rate percentage per second, in `WAD`.
function predictedBorrowRate(
uint256 assetsHeld,
uint256 debt
) external view returns (uint256 result);
/// @notice Calculates the current supply rate, per second.
/// @dev This function's intention is for frontend data querying and
/// should not be used for onchain execution.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param debt The amount of outstanding debt in the pool.
/// @param interestFee The current interest rate protocol fee
/// for the market token.
/// @return result The supply interest rate percentage, per second,
/// in `WAD`.
function supplyRate(
uint256 assetsHeld,
uint256 debt,
uint256 interestFee
) external view returns (uint256 result);
/// @notice Calculates the interest rate paid per second by borrowers,
/// in percentage paid, per second, in `WAD`, and updates
/// `vertexMultiplier` if necessary.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param debt The amount of outstanding debt in the pool.
/// @return ratePerSecond The interest rate paid per second by borrowers,
/// in percentage paid, per second, in `WAD`.
/// @return adjustmentRate The period of time at which interest rates are
/// adjusted, in seconds.
function adjustedBorrowRate(
uint256 assetsHeld,
uint256 debt
) external returns (uint256 ratePerSecond, uint256 adjustmentRate);
/// @notice Calculates the borrow utilization rate of the market.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param outstandingDebt The amount of outstanding debt in the pool.
/// @return The utilization rate between [0, WAD].
function utilizationRate(
uint256 assetsHeld,
uint256 outstandingDebt
) external view returns (uint256);
}
// contracts/interfaces/IERC165.sol
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(
bytes4 interfaceId
) external view returns (bool);
}
// contracts/interfaces/IERC20.sol
// @dev Interface of the ERC20 standard
interface IERC20 {
// @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 of the token.
function decimals() external view returns (uint8);
// @dev Emitted when `value` tokens are moved from one account (`from`) to
// another (`to`).
// Note that `value` may be zero.
event Transfer(address indexed from, address indexed to, uint256 value);
// @dev Emitted when the allowance of a `spender` for an `owner` is set by
// a call to {approve}. `value` is the new allowance.
event Approval(
address indexed owner,
address indexed spender,
uint256 value
);
// @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 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 remaining number of tokens that `spender` will be
// allowed to spend on behalf of `owner` through {transferFrom}. This
// is zero by default.
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);
}
// contracts/interfaces/IMarketManager.sol
interface IMarketManager {
/// TYPES ///
/// @notice Data structure passed communicating the intended liquidation
/// scenario to review based on current liquidity levels.
/// @param collateralToken The token which was used as collateral
/// by `account` and may be seized.
/// @param debtToken The token to potentially repay which has
/// outstanding debt by `account`.
/// @param numAccounts The number of accounts to be, potentially,
/// liquidated.
/// @param liquidateExact Whether the liquidator desires a specific
/// liquidation amount.
/// @param liquidatedShares Empty variable slot to store how much
/// `collateralToken` will be seized as
/// part of a particular liquidation.
/// @param debtRepaid Empty variable slot to store how much
/// `debtToken` will be repaid as part of a
/// particular liquidation.
/// @param badDebt Empty variable slot to store how much bad debt will
/// be realized by lenders as part of a particular
/// liquidation.
struct LiqAction {
address collateralToken;
address debtToken;
uint256 numAccounts;
bool liquidateExact;
uint256 liquidatedShares;
uint256 debtRepaid;
uint256 badDebt;
}
/// @notice Data structure returned communicating outcome of a liquidity
/// scenario review based on current liquidity levels.
/// @param liquidatedShares An array containing the collateral amounts to
/// liquidate from accounts, in shares.
/// @param debtRepaid The total amount of debt to repay from accounts,
/// in assets.
/// @param badDebtRealized The total amount of debt to realize as losses
/// for lenders, in assets.
struct LiqResult {
uint256[] liquidatedShares;
uint256 debtRepaid;
uint256 badDebtRealized;
}
/// @notice Returns whether minting, collateralization, borrowing of
/// `cToken` is paused.
/// @param cToken The address of the Curvance token to return
/// action statuses of.
/// @return bool Whether minting `cToken` is paused or not.
/// @return bool Whether collateralization `cToken` is paused or not.
/// @return bool Whether borrowing `cToken` is paused or not.
function actionsPaused(
address cToken
) external view returns (bool, bool, bool);
/// @notice Returns the current collateralization configuration
/// of `cToken`.
/// @param cToken The address of the Curvance token to return
/// collateralization configuration of.
/// @return The ratio at which this token can be borrowed against
/// when collateralized.
/// @return The collateral requirement where dipping below this
/// will cause a soft liquidation.
/// @return The collateral requirement where dipping below
/// this will cause a hard liquidation.
function collConfig(address cToken) external view returns (
uint256, uint256, uint256
);
/// @notice Returns the current liquidation configuration
/// of `cToken`.
/// @param cToken The address of the Curvance token to return
/// liquidation configuration of.
/// @return The base ratio at which this token will be
/// compensated on soft liquidation.
/// @return The liquidation incentive curve length between soft
/// liquidation to hard liquidation, in `WAD`. e.g. 5% base
/// incentive with 8% curve length results in 13% liquidation
/// incentive on hard liquidation.
/// @return The minimum possible liquidation incentive for during an
/// auction, in `WAD`.
/// @return The maximum possible liquidation incentive for during an
/// auction, in `WAD`.
/// @return Maximum % that a liquidator can repay when soft
/// liquidating an account, in `WAD`.
/// @return Curve length between soft liquidation and hard liquidation,
/// should be equal to 100% - `closeFactorBase`, in `WAD`.
/// @return The minimum possible close factor for during an auction,
/// in `WAD`.
/// @return The maximum possible close factor for during an auction,
/// in `WAD`.
function liquidationConfig(address cToken) external view returns (
uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256
);
/// @notice Enables an auction-based liquidation, potentially with a dynamic
/// close factor and liquidation penalty values in transient storage.
/// @dev Transient storage enforces any liquidator outside auction-based
/// liquidations uses the default risk parameters.
/// @param cToken The Curvance token to configure liquidations for during
/// an auction-based liquidation.
/// @param incentive The auction liquidation incentive value, in `BPS`.
/// @param closeFactor The auction close factor value, in `BPS`.
function setTransientLiquidationConfig(
address cToken,
uint256 incentive,
uint256 closeFactor
) external;
/// @notice Called from the AuctionManager as a post hook after liquidations
/// are tried to enable all collateral to be liquidated outside
/// an Auction tx.
/// @notice Resets the liquidation risk parameters in transient storage to
/// zero.
/// @dev This is redundant since the transient values will be reset after
/// the liquidation transaction, but can be useful during meta calls
/// with multiple liquidations during a single transaction.
function resetTransientLiquidationConfig() external;
/// @notice Checks if the account should be allowed to mint tokens
/// in the given market.
/// @param cToken The token to verify mints against.
function canMint(address cToken) external;
/// @notice Checks if the account should be allowed to collateralize
/// their shares of the given market.
/// Prunes unused positions in `account` data.
/// @dev May emit a {PositionUpdated} event.
/// @param cToken The token to verify collateralization of.
/// @param account The account which would collateralize the asset.
/// @param newNetCollateral The amount of shares that would be
/// collateralized in total if allowed.
function canCollateralize(
address cToken,
address account,
uint256 newNetCollateral
) external;
/// @notice Checks if the account should be allowed to redeem tokens
/// in the given market, and then redeems.
/// @dev This can only be called by the cToken itself.
/// @param cToken The token to verify the redemption against.
/// @param shares The number of cToken shares to redeem for the
/// underlying asset in the market.
/// @param account The account which would redeem `shares`.
/// @param balanceOf The current cToken share balance of `account`.
/// @param collateralPosted The current cToken shares posted as
/// collateral by `account`.
/// @param forceRedeemCollateral Whether the collateral should be always
/// reduced.
function canRedeemWithCollateralRemoval(
address cToken,
uint256 shares,
address account,
uint256 balanceOf,
uint256 collateralPosted,
bool forceRedeemCollateral
) external returns (uint256);
/// @notice Checks if the account should be allowed to borrow
/// the underlying asset of the given market.
/// Prunes unused positions in `account` data.
/// @dev May emit a {PositionUpdated} event.
/// @param cToken The token to verify borrowability of.
/// @param assets The amount of underlying assets `account` would borrow.
/// @param account The account which would borrow the asset.
/// @param newNetDebt The amount of assets that would be
/// outstanding debt in total if allowed.
function canBorrow(
address cToken,
uint256 assets,
address account,
uint256 newNetDebt
) external;
/// @notice Checks if the account should be allowed to borrow
/// the underlying asset of the given market,
/// and notifies the market of the borrow.
/// @dev This can only be called by the market itself.
/// @param cToken The market to verify the borrow against.
/// @param assets The amount of underlying assets `account` would borrow.
/// @param account The account which would borrow the asset.
/// @param newNetDebt The amount of assets that would be
/// outstanding debt in total if allowed.
function canBorrowWithNotify(
address cToken,
uint256 assets,
address account,
uint256 newNetDebt
) external;
/// @notice Checks if the account should be allowed to repay a borrow
/// in the given market, may clean up positions.
/// @param cToken The Curvance token to verify the repayment of.
/// @param newNetDebt The new debt amount owed by `account` after
/// repayment.
/// @param debtAsset The debt asset being repaid to `cToken`.
/// @param decimals The decimals that `debtToken` is measured in.
/// @param account The account who will have their loan repaid.
function canRepayWithReview(
address cToken,
uint256 newNetDebt,
address debtAsset,
uint256 decimals,
address account
) external view;
/// @notice Checks if the liquidation should be allowed to occur,
/// and returns how many collateralized shares should be seized
/// on liquidation.
/// @param debtAmounts The amounts of outstanding debt the liquidator
/// wishes to repay, in underlying assets, empty if
/// intention is to liquidate maximum amount possible
/// for each account.
/// @param liquidator The address of the account trying to liquidate
/// `accounts`.
/// @param accounts The addresses of the accounts to be liquidated.
/// @param action A LiqAction struct containing:
/// collateralToken The token which is used as collateral
/// by `account` and may be seized.
/// debtToken The token to potentially repay which has
/// outstanding debt by `account`.
/// numAccounts The number of accounts to be, potentially,
/// liquidated.
/// liquidateExact Whether the liquidator desires a
/// specific liquidation amount.
/// collateralLiquidated Empty variable slot to store how
/// much `collateralToken` will be
/// seized as part of a particular
/// liquidation.
/// debtRepaid Empty variable slot to store how much
/// `debtToken` will be repaid as part of a
/// particular liquidation.
/// badDebt Empty variable slot to store how much bad debt
/// will be realized as part of a particular
/// liquidation.
/// @return result A LiqResult struct containing:
/// liquidatedShares An array containing the collateral
/// amounts to liquidate from
/// `accounts`.
/// debtRepaid The total amount of debt to repay from
/// `accounts`.
/// badDebtRealized The total amount of debt to realize as
/// losses for lenders inside this market.
/// @return An array containing the debt amounts to repay from
/// `accounts`, in assets.
function canLiquidate(
uint256[] memory debtAmounts,
address liquidator,
address[] calldata accounts,
IMarketManager.LiqAction memory action
) external returns (LiqResult memory, uint256[] memory);
/// @notice Checks if the seizing of `collateralToken` by repayment of
/// `debtToken` should be allowed.
/// @param collateralToken The Curvance token which was used as collateral
/// and will be seized.
/// @param debtToken The Curvance token which has outstanding debt to and
/// would be repaid during `collateralToken` seizure.
function canSeize(address collateralToken, address debtToken) external;
/// @notice Checks if the account should be allowed to transfer collateral
/// tokens in the given market.
/// @param cToken The Curvance token to verify the transfer of.
/// @param shares The amount of `cToken` to transfer.
/// @param account The account which will transfer `shares`.
/// @param balanceOf The current balance that `account` has of `cToken`
/// shares.
/// @param collateralPosted The amount of `cToken` shares posted as
/// collateral by `account`.
/// @param isCollateral Boolean indicating whether the token is currently
/// being used as collateral.
function canTransfer(
address cToken,
uint256 shares,
address account,
uint256 balanceOf,
uint256 collateralPosted,
bool isCollateral
) external returns (uint256);
/// @notice Updates `account` cooldownTimestamp to the current block
/// timestamp.
/// @dev The caller must be a listed cToken in the `markets` mapping.
/// @param cToken The address of the token that the account is borrowing.
/// @param account The address of the account that has just borrowed.
function notifyBorrow(address cToken, address account) external;
/// @notice A list of all tokens inside this market for
/// offchain querying.
function queryTokensListed() external view returns (address[] memory);
/// @notice Returns whether `cToken` is listed in the lending market.
/// @param cToken market token address.
function isListed(address cToken) external view returns (bool);
/// @notice The total amount of `cToken` that can be posted as collateral,
/// in shares.
function collateralCaps(address cToken) external view returns (uint256);
/// @notice The total amount of `cToken` underlying that can be borrowed,
/// in assets.
function debtCaps(address cToken) external view returns (uint256);
/// @notice Returns whether `addressToCheck` is an approved position
/// manager or not.
/// @param addressToCheck Address to check for position management
/// authority.
function isPositionManager(
address addressToCheck
) external view returns (bool);
/// @notice Returns the assets an account has entered.
/// @param account The address of the account to pull assets for.
/// @return A dynamic list with the assets `account` has entered.
function assetsOf(
address account
) external view returns (address[] memory);
/// @notice Determine `account`'s current status between collateral,
/// debt, and additional liquidity.
/// @param account The account to determine liquidity for.
/// @return The current total collateral amount of `account`.
/// @return The maximum debt amount of `account` can take out with
/// their current collateral.
/// @return The current total borrow amount of `account`.
function statusOf(
address account
) external returns (uint256, uint256, uint256);
}
// contracts/interfaces/IMulticallChecker.sol
interface IMulticallChecker {
function checkCalldata(
address caller,
address target,
bytes memory data
) external;
}
// contracts/libraries/ConstantsLib.sol
/// @dev Scalar for math. `WAD` * `WAD`.
uint256 constant WAD_SQUARED = 1e36;
/// @dev Scalar for math. `WAD` * `WAD` / `BPS`.
/// 1e18 * 1e18 / 1e4
uint256 constant WAD_SQUARED_BPS_OFFSET = 1e32;
/// @dev Scalar for math. Increased precision when WAD is insufficient
/// but WAD_SQUARED runs the risk of overflow.
uint256 constant RAY = 1e27;
/// @dev Scalar for math. `WAD` * `BPS`.
uint256 constant WAD_BPS = 1e22;
/// @dev Scalar for math. Base precision matching ether.
uint256 constant WAD = 1e18;
/// @dev Scalar for math. Represents basis points typically used in TradFi.
uint256 constant BPS = 1e4;
/// @dev Return value indicating no price returned at all.
uint256 constant BAD_SOURCE = 2;
/// @dev Return value indicating price divergence or a missing price feed.
uint256 constant CAUTION = 1;
/// @dev Return value indicating no price error.
uint256 constant NO_ERROR = 0;
/// @dev Extra time added to top end Oracle feed heartbeat incase of
/// transaction congestion delaying an update.
uint256 constant HEARTBEAT_GRACE_PERIOD = 120;
/// @dev Unix time has 31,536,000 seconds per year.
/// All my homies hate leap seconds and leap years.
uint256 constant SECONDS_PER_YEAR = 31_536_000;
// contracts/libraries/LowLevelCallsHelper.sol
library LowLevelCallsHelper {
/// ERRORS ///
error LowLevelCallsHelper__InsufficientBalance();
error LowLevelCallsHelper__CallFailed();
/// INTERNAL FUNCTIONS ///
/// @notice Executes a low level .call() and validates that execution
/// was safely performed.
/// @dev Bubbles up errors and reverts if anything along the execution
/// path failed or was a red flag.
/// @param target The target contract address to execute .call() at.
/// @param data The bytecode data to attach to the .call() execution
/// including the function signature hash and function call
/// parameters.
function _call(
address target,
bytes memory data
) internal returns (bytes memory) {
(bool success, bytes memory returnData) = target.call{value: 0}(data);
return _verifyResult(target, success, returnData);
}
/// @notice Executes a low level .call(), with attached native token
/// and validates that execution was safely performed.
/// @dev Bubbles up errors and reverts if anything along the execution
/// path failed or was a red flag.
/// @param target The target contract address to execute .call() at.
/// @param data The bytecode data to attach to the .call() execution
/// including the function signature hash and function call
/// parameters.
/// @param value The amount of native token to attach to the low level
/// call.
function _callWithNative(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
if (address(this).balance < value) {
revert LowLevelCallsHelper__InsufficientBalance();
}
(bool success, bytes memory returnData) = target.call{
value: value
}(data);
return _verifyResult(target, success, returnData);
}
/// @notice Executes a low level .delegatecall() and validates that
/// execution was safely performed.
/// @dev Bubbles up errors and reverts if anything along the execution
/// path failed or was a red flag.
/// @param target The target contract address to execute .delegatecall()
/// at.
/// @param data The bytecode data to attach to the .call() execution
/// including the function signature hash and function call
/// parameters.
function _delegateCall(
address target,
bytes memory data
) internal returns (bytes memory) {
(bool success, bytes memory returnData) = target.delegatecall(data);
return _verifyResult(target, success, returnData);
}
/// @dev Validates whether the low level call or delegate call was
/// successful and reverts in cases of bubbled up revert messages
/// or if `target` was not actually a contract.
function _verifyResult(
address target,
bool success,
bytes memory returnData
) internal view returns (bytes memory) {
_propagateError(success, returnData);
// If the call was successful but there was no return data we need
// to make sure a contract was actually called as expected.
if (returnData.length == 0 && target.code.length == 0) {
revert LowLevelCallsHelper__CallFailed();
}
return returnData;
}
/// @dev Propagates an error message, if necessary.
/// @param success If transaction was successful.
/// @param resultData The transaction result data.
function _propagateError(
bool success,
bytes memory resultData
) internal pure {
if (!success) {
if (resultData.length == 0) {
revert LowLevelCallsHelper__CallFailed();
}
// Bubble up error if there was one given.
assembly {
revert(add(32, resultData), mload(resultData))
}
}
}
}
// contracts/libraries/external/FixedPointMathLib.sol
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @dev Reduced function scope from full FixedPointMathLib library to only what is needed for Curvance.
/// @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, either due to a multiplication overflow, or a division by a zero.
error MulDivFailed();
/// @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();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* GENERAL NUMBER UTILITIES */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
/// @dev Calculates `floor(x * y / 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 {
for {} 1 {} {
// 512-bit multiply `[p1 p0] = 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 = p1 * 2**256 + p0`.
// Least significant 256 bits of the product.
result := mul(x, y) // Temporarily use `result` as `p0` to save gas.
let mm := mulmod(x, y, not(0))
// Most significant 256 bits of the product.
let p1 := sub(mm, add(result, lt(mm, result)))
// Handle non-overflow cases, 256 by 256 division.
if iszero(p1) {
if iszero(d) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
result := div(result, d)
break
}
// Make sure the result is less than `2**256`. Also prevents `d == 0`.
if iszero(gt(d, p1)) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
/*------------------- 512 by 256 division --------------------*/
// Make division exact by subtracting the remainder from `[p1 p0]`.
// Compute remainder using mulmod.
let r := mulmod(x, y, d)
// `t` is the least significant bit of `d`.
// Always greater or equal to 1.
let t := and(d, sub(0, d))
// Divide `d` by `t`, which is a power of two.
d := div(d, t)
// 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(2, mul(3, d))
// 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(
// Divide [p1 p0] by the factors of two.
// Shift in bits from `p1` into `p0`. For this we need
// to flip `t` such that it is `2**256 / t`.
or(
mul(sub(p1, gt(r, result)), add(div(sub(0, t), t), 1)),
div(sub(result, r), t)
),
// inverse mod 2**256
mul(inv, sub(2, mul(d, inv)))
)
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) {
result := add(result, 1)
if iszero(result) {
mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
revert(0x1c, 0x04)
}
}
}
}
/// @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)))))) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
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)))))) {
mstore(0x00, 0xad251c27) // `MulDivFailed()`.
revert(0x1c, 0x04)
}
z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d))
}
}
/// @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 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
z := sub(z, lt(div(x, z), z))
}
}
}
// contracts/libraries/external/SafeTransferLib.sol
/// @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 Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation 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.
uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* ETH OPERATIONS */
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/
// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
//
// The regular variants:
// - Forwards all remaining gas to the target.
// - Reverts if the target reverts.
// - Reverts if the current contract has insufficient balance.
//
// The force variants:
// - Forwards with an optional gas stipend
// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
// - If the target reverts, or if the gas stipend is exhausted,
// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
// - Reverts if the current contract has insufficient balance.
//
// The try variants:
// - Forwards with a mandatory gas stipend.
// - Instead of reverting, returns whether the transfer succeeded.
/// @dev Sends `amount` (in wei) ETH to `to`.
function safeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.
function safeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// Transfer all the ETH and check if it succeeded or not.
if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
/// @solidity memory-safe-assembly
assembly {
if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferETH(address to, uint256 amount) internal {
/// @solidity memory-safe-assembly
assembly {
if lt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
revert(0x1c, 0x04)
}
if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
function forceSafeTransferAllETH(address to) internal {
/// @solidity memory-safe-assembly
assembly {
// forgefmt: disable-next-item
if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.
mstore8(0x0b, 0x73) // Opcode `PUSH20`.
mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
function trySafeTransferAllETH(address to, uint256 gasStipend)
internal
returns (bool success)
{
/// @solidity memory-safe-assembly
assembly {
success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
/* 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.
mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
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 their entire balance 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.
mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
// Read the balance, reverting upon failure.
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)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
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.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @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.
// Read the balance, reverting upon failure.
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)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
// Perform the transfer, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @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.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, reverting upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
/// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
/// then retries the approval again (some tokens, e.g. USDT, requires this).
/// Reverts upon failure.
function safeApproveWithRetry(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.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
// Perform the approval, retrying upon failure.
if iszero(
and( // The arguments of `and` are evaluated from right to left.
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.
mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
mstore(0x34, amount) // Store back the original `amount`.
// Retry the approval, reverting upon failure.
if iszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @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.
mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
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)
)
)
}
}
}
// contracts/libraries/external/ERC165.sol
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// contracts/libraries/external/ERC165Checker.sol
// @author Modified from OpenZeppelin Contracts (utils/introspection/ERC165Checker.sol)
/**
* @dev Library used to query support of an interface declared via {IERC165}.
*
* Note that these functions return the actual result of the query: they do not
* `revert` if an interface is not supported. It is up to the caller to decide
* what to do in these cases.
*/
library ERC165Checker {
// As per the EIP-165 spec, no interface should ever match 0xffffffff
bytes4 private constant _INTERFACE_ID_INVALID = 0xffffffff;
/**
* @dev Returns true if `account` supports the {IERC165} interface.
*/
function supportsERC165(address account) internal view returns (bool) {
// Any contract that implements ERC165 must explicitly indicate support of
// InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
return
supportsERC165InterfaceUnchecked(
account,
type(IERC165).interfaceId
) &&
!supportsERC165InterfaceUnchecked(account, _INTERFACE_ID_INVALID);
}
/**
* @dev Returns true if `account` supports the interface defined by
* `interfaceId`. Support for {IERC165} itself is queried automatically.
*
* See {IERC165-supportsInterface}.
*/
function supportsInterface(
address account,
bytes4 interfaceId
) internal view returns (bool) {
// query support of both ERC165 as per the spec and support of _interfaceId
return
supportsERC165(account) &&
supportsERC165InterfaceUnchecked(account, interfaceId);
}
/**
* @notice Query if a contract implements an interface, does not check ERC165 support
* @param account The address of the contract to query for support of an interface
* @param interfaceId The interface identifier, as specified in ERC-165
* @return true if the contract at account indicates support of the interface with
* identifier interfaceId, false otherwise
* @dev Assumes that account contains a contract that supports ERC165, otherwise
* the behavior of this method is undefined. This precondition can be checked
* with {supportsERC165}.
*
* Some precompiled contracts will falsely indicate support for a given interface, so caution
* should be exercised when using this function.
*
* Interface identification is specified in ERC-165.
*/
function supportsERC165InterfaceUnchecked(
address account,
bytes4 interfaceId
) internal view returns (bool) {
// prepare call
bytes memory encodedParams = abi.encodeCall(
IERC165.supportsInterface,
(interfaceId)
);
// perform static call
bool success;
uint256 returnSize;
uint256 returnValue;
assembly {
success := staticcall(
30000,
account,
add(encodedParams, 0x20),
mload(encodedParams),
0x00,
0x20
)
returnSize := returndatasize()
returnValue := mload(0x00)
}
return success && returnSize >= 0x20 && returnValue > 0;
}
}
// contracts/libraries/CentralRegistryLib.sol
/// @title Curvance Central Registry Validation Library
/// @notice For validating that Central Registry is configured properly
/// on launch.
library CentralRegistryLib {
/// ERRORS ///
error CentralRegistryLib__InvalidCentralRegistry();
/// INTERNAL FUNCTIONS ///
/// @notice Validates that `cr` is the Protocol Central Registry.
/// @param cr The proposed Protocol Central Registry.
function _isCentralRegistry(ICentralRegistry cr) internal view {
if (
!ERC165Checker.supportsInterface(
address(cr),
type(ICentralRegistry).interfaceId
)
) {
revert CentralRegistryLib__InvalidCentralRegistry();
}
}
}
// contracts/libraries/Multicall.sol
/// @title Curvance Multicall helper.
/// @notice Multicall implementation to support pull based oracles and
/// other chained actions within Curvance.
abstract contract Multicall {
/// TYPES ///
/// @notice Struct containing information on the desired
/// multicall action to execute.
/// @param target The address of the target contract to execute the call at.
/// @param isPriceUpdate Boolean indicating if the call is a price update.
/// @param data The data to attach to the call.
struct MulticallAction {
address target;
bool isPriceUpdate;
bytes data;
}
/// ERRORS ///
error Multicall__InvalidTarget();
error Multicall__UnknownCalldata();
/// EXTERNAL FUNCTIONS ///
/// @notice Executes multiple calls in a single transaction.
/// This can be used to update oracle prices before
/// a liquidity dependent action.
function multicall(
MulticallAction[] calldata calls
) external returns (bytes[] memory results) {
ICentralRegistry cr = _getCentralRegistry();
uint256 numCalls = calls.length;
results = new bytes[](numCalls);
MulticallAction memory cachedCall;
for (uint256 i; i < numCalls; ++i) {
cachedCall = calls[i];
if (cachedCall.isPriceUpdate) {
// CASE: We need to update a pull based price oracle and we
// need a direct call to the target address.
address checker = cr.multicallChecker(cachedCall.target);
// Validate we know how to verify this calldata.
if (checker == address(0)) {
revert Multicall__UnknownCalldata();
}
IMulticallChecker(checker).checkCalldata(
msg.sender,
cachedCall.target,
cachedCall.data
);
results[i] = LowLevelCallsHelper.
_call(cachedCall.target, cachedCall.data);
continue;
}
// CASE: Not a price update and we need delegate the call to the
// current address.
if (address(this) != cachedCall.target) {
revert Multicall__InvalidTarget();
}
results[i] = LowLevelCallsHelper.
_delegateCall(address(this), cachedCall.data);
}
}
/// @notice Returns the Protocol Central Registry contract in interface
/// form.
/// @dev MUST be overridden in every multicallable contract's
/// implementation.
function _getCentralRegistry()
internal
view
virtual
returns (ICentralRegistry);
}
// contracts/interfaces/IBorrowableCToken.sol
interface IBorrowableCToken {
/// ICToken GENERIC FUNCTIONS ///
/// @notice Starts a cToken market, executed via marketManager.
/// @dev This initial mint is a failsafe against rounding exploits,
/// although, we protect against them in many ways,
/// better safe than sorry.
/// @param by The account initializing deposits.
function initializeDeposits(address by) external returns (bool);
/// @notice Returns the decimals of the cToken.
/// @dev We pull directly from underlying incase its a proxy contract,
/// and changes decimals on us.
/// @return The number of decimals for this cToken,
/// matching the underlying token.
function decimals() external view returns (uint8);
/// @notice Returns whether the underlying token can be borrowed.
/// @dev true = Borrowable; false = Not Borrowable.
/// @return Whether this token is borrowable or not.
function isBorrowable() external view returns (bool);
/// @notice The token balance of an account.
/// @dev Account address => account token balance.
/// @param account The address of the account to query token balance for.
function balanceOf(address account) external view returns (uint256);
/// @notice Returns the address of the underlying asset.
/// @return The address of the underlying asset.
function asset() external view returns (address);
/// @notice Returns the current vesting yield information.
/// @return vestingRate % per second in `asset()`.
/// @return vestingEnd When the current vesting period ends and interest
/// rates paid will update.
/// @return lastVestingClaim Last time pending vested yield was claimed.
/// @return debtIndex The current market debt index.
function getYieldInformation() external view returns (
uint256 vestingRate,
uint256 vestingEnd,
uint256 lastVestingClaim,
uint256 debtIndex
);
/// @notice Get a snapshot of `account` data in this Curvance token.
/// @dev Used by marketManager to more efficiently perform
/// liquidity checks.
/// NOTE: Does not accrue pending interest as part of the call.
/// @param account The address of the account to snapshot.
/// @return The account snapshot of `account`.
function getSnapshot(
address account
) external view returns (AccountSnapshot memory);
/// @notice Total number of cTokens in circulation.
function totalSupply() external view returns (uint256);
/// @notice Returns the total amount of assets held by the market.
/// @return The total amount of assets held by the market.
function totalAssets() external view returns (uint256);
/// @notice Address of the Market Manager linked to this contract.
function marketManager() external view returns (IMarketManager);
/// @notice Returns the amount of assets that would be exchanged
/// by the vault for `shares` provided.
/// @param shares The number of shares to theoretically use
/// for conversion to assets.
/// @return The number of assets a user would receive for converting
/// `shares`.
function convertToAssets(uint256 shares) external view returns (uint256);
/// @notice Returns share -> asset exchange rate, in `WAD`.
/// @dev Oracle Manager calculates cToken value from this exchange rate.
function exchangeRate() external view returns (uint256);
/// @notice Executes multiple calls in a single transaction.
/// This can be used to update oracle prices before
/// a liquidity dependent action.
function multicall(
Multicall.MulticallAction[] memory calls
) external returns (bytes[] memory results);
/// @notice Caller deposits `assets` into the market and `receiver`
/// receives shares.
/// @param assets The amount of the underlying assets to deposit.
/// @param receiver The account that should receive the shares.
/// @return shares The amount of shares received by `receiver`.
function deposit(
uint256 assets,
address receiver
) external returns (uint256 shares);
/// @notice Caller deposits `assets` into the market, `receiver` receives
/// shares, and collateralization of `assets` is enabled.
/// @dev The caller must be depositing for themselves, or be managing
/// their position through a Position Manager contract.
/// If the caller is not approved to collateralize the function will
/// simply deposit assets on behalf of `receiver`.
/// @param assets The amount of the underlying assets to deposit.
/// @param receiver The account that should receive the shares.
/// @return shares The amount of shares received by `receiver`.
function depositAsCollateral(
uint256 assets,
address receiver
) external returns (uint256 shares);
/// @notice Caller deposits `assets` into the market, `receiver` receives
/// shares, and collateralization of `assets` is enabled.
/// @dev Requires that `receiver` approves the caller prior to
/// collateralize on their behalf.
/// NOTE: Be careful who you approve here!
/// They can delay redemption of assets through repeated
/// collateralization preventing withdrawal.
/// If the caller is not approved to collateralize the function will
/// simply deposit assets on behalf of `receiver`.
/// @param assets The amount of the underlying assets to deposit.
/// @param receiver The account that should receive the shares.
/// @return shares The amount of shares received by `receiver`.
function depositAsCollateralFor(
uint256 assets,
address receiver
) external returns (uint256 shares);
/// @notice Withdraws assets, quoted in `shares` from the market,
/// and burns `owner` shares.
/// @dev Does not force collateral to be withdrawn.
/// @param shares The amount of shares to be redeemed.
/// @param receiver The account that should receive the assets.
/// @param owner The account that will burn their shares to withdraw
/// assets.
/// @return assets The amount of assets redeemed by `owner`.
function redeem(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
/// @notice Withdraws assets, quoted in `shares` from the market,
/// and burns `owner` shares, sending assets to `receiver`.
/// @dev Does not force collateral to be withdrawn.
/// @param shares The amount of shares to redeemed.
/// @param receiver The account that should receive the assets.
/// @param owner The account that will burn their shares to withdraw
/// assets.
/// @return assets The amount of assets redeemed by `owner` and sent to
/// `receiver`.
function redeemFor(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
/// @notice Caller withdraws assets from the market and burns their
/// shares, on behalf of `owner`.
/// @dev Forces collateral to be withdrawn from `owner` collateralPosted.
/// @param shares The amount of shares to redeemed.
/// @param receiver The account that should receive the assets.
/// @param owner The account that will burn their shares to withdraw
/// assets.
/// @return assets The amount of assets redeemed by `owner` and sent to
/// `receiver`.
function redeemCollateralFor(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
/// @notice Used by a Position Manager contract to redeem assets from
/// collateralized shares by `account` to perform a complex
/// action.
/// @param assets The amount of the underlying assets to redeem.
/// @param owner The owner address of assets to redeem.
/// @param action Instructions for a deleverage action containing:
/// cToken Address of the cToken that will be redeemed from
/// and assets swapped into `borrowableCToken` asset.
/// collateralAssets The amount of `cToken` that will be
/// deleveraged, in assets.
/// borrowableCToken Address of the borrowableCToken that
/// will have its debt paid.
/// repayAssets The amount of `borrowableCToken` asset that
/// will be repaid to lenders.
/// swapActions Swap actions instructions converting
/// collateral asset into debt asset to
/// facilitate deleveraging.
/// auxData Optional auxiliary data for execution of a
/// deleverage action.
function withdrawByPositionManager(
uint256 assets,
address owner,
IPositionManager.DeleverageAction memory action
) external;
/// @notice Amount of tokens that has been posted as collateral,
/// in shares.
function marketCollateralPosted() external view returns (uint256);
/// @notice Shares of this token that an account has posted as collateral.
/// @param account The address of the account to check collateral posted
/// of.
function collateralPosted(address account) external view returns (uint256);
/// @notice Transfers tokens from `account` to `liquidator`.
/// @dev Will fail unless called by a cToken during the process
/// of liquidation.
/// @param shares An array containing the number of cToken shares
/// to seize.
/// @param liquidator The account receiving seized cTokens.
/// @param accounts An array containing the accounts having
/// collateral seized.
function seize(
uint256[] calldata shares,
address liquidator,
address[] calldata accounts
) external;
/// @notice Allows users to simulate the effects of their deposit at
/// the current block.
/// @param assets The number of assets to preview a deposit call.
/// @return The shares received for depositing `assets`.
function previewDeposit(uint256 assets) external view returns (uint256);
/// IBorrowableCToken SPECIFIC FUNCTIONS ///
/// @notice Address of the current Dynamic IRM.
function IRM() external view returns (IDynamicIRM);
/// @notice Fee that goes to protocol for interested generated for
/// lenders, in `WAD`.
function interestFee() external view returns (uint256);
/// @notice Can accrue interest yield, configure next interest accrual
/// period, and updates vesting data, if needed.
/// @dev May emit a {RatesAdjusted} event.
function accrueIfNeeded() external;
/// @notice The amount of tokens that has been borrowed as debt,
/// in assets.
function marketOutstandingDebt() external view returns (uint256);
/// @notice Returns the current debt balance for `account`.
/// @dev Note: Pending interest is not applied in this calculation.
/// @param account The address whose debt balance should be calculated.
/// @return result The current outstanding debt balance of `account`.
function debtBalance(
address account
) external view returns (uint256 result);
/// @notice Updates pending interest and returns the current outstanding
/// debt owed by `account`.
/// @param account The address whose debt balance should be calculated.
/// @return result The current outstanding debt of `account`, with pending
/// interest applied.
function debtBalanceUpdated(
address account
) external returns (uint256 result);
/// @notice Updates pending interest and returns the up-to-date exchange
/// rate from the underlying to the BorrowableCToken.
/// @return result The share -> asset exchange rate, in `WAD`.
function exchangeRateUpdated() external returns (uint256);
/// @notice Used by a delegated user to borrow underlying tokens
/// from lenders, based on collateral posted inside this market
/// by `account`.
/// @dev Updates pending interest before executing the borrow.
/// NOTE: Be careful who you approve here!
/// Not only can they take borrowed funds, but, they can delay
/// repayment through repeated borrows preventing withdrawal.
/// @param assets The amount of the underlying asset to borrow.
/// @param receiver The account who will receive the borrowed assets.
/// @param owner The account who will have their assets borrowed
/// against.
function borrowFor(
uint256 assets,
address receiver,
address owner
) external;
/// @notice Used by a Position Manager contract to borrow assets from
/// lenders, based on collateralized shares by `account` to
/// perform a complex action.
/// @param assets The amount of the underlying assets to borrow.
/// @param owner The account address to borrow on behalf of.
/// @param action Instructions for a leverage action containing:
/// borrowableCToken Address of the borrowableCToken that
/// will be borrowed from and assets
/// swapped into `cToken` asset.
/// borrowAssets The amount borrowed from
/// `borrowableCToken`, in assets.
/// cToken Curvance token assets that borrowed funds will be
/// swapped into.
/// swapAction Swap action instructions converting debt
/// asset into collateral asset to facilitate
/// leveraging.
/// auxData Optional auxiliary data for execution of a
/// leverage action.
function borrowForPositionManager(
uint256 assets,
address owner,
IPositionManager.LeverageAction memory action
) external;
/// @notice Repays underlying tokens to lenders, on behalf of `account`,
/// freeing up their collateral posted inside this market.
/// @dev Updates pending interest before executing the repay.
/// @param assets The amount to repay, or 0 for the full outstanding
/// amount.
/// @param owner The account address to repay on behalf of.
function repayFor(uint256 assets, address owner) external;
/// @notice Gets balance of this contract, in terms of the underlying.
/// @dev This excludes changes in underlying token balance by the
/// current transaction, if any.
/// @return The quantity of underlying tokens held by the market.
function assetsHeld() external view returns (uint256);
}
// contracts/interfaces/ICToken.sol
struct AccountSnapshot {
address asset;
address underlying;
uint8 decimals;
bool isCollateral;
uint256 collateralPosted;
uint256 debtBalance;
}
interface ICToken {
/// @notice Starts a cToken market, executed via marketManager.
/// @dev This initial mint is a failsafe against rounding exploits,
/// although, we protect against them in many ways,
/// better safe than sorry.
/// @param by The account initializing deposits.
function initializeDeposits(address by) external returns (bool);
/// @notice Returns the decimals of the cToken.
/// @dev We pull directly from underlying incase its a proxy contract,
/// and changes decimals on us.
/// @return The number of decimals for this cToken,
/// matching the underlying token.
function decimals() external view returns (uint8);
function name() external view returns (string memory);
function symbol() external view returns (string memory);
/// @notice Returns whether the underlying token can be borrowed.
/// @dev true = Borrowable; false = Not Borrowable.
/// @return Whether this token is borrowable or not.
function isBorrowable() external view returns (bool);
/// @notice The token balance of an account.
/// @dev Account address => account token balance.
/// @param account The address of the account to query token balance for.
function balanceOf(address account) external view returns (uint256);
/// @notice Returns the address of the underlying asset.
/// @return The address of the underlying asset.
function asset() external view returns (address);
/// @notice Updates pending assets and returns a snapshot of the cToken
/// and `account` data.
/// @dev Used by marketManager to more efficiently perform
/// liquidity checks.
/// @return result The snapshot of the cToken and `account` data.
function getSnapshotUpdated(
address account
) external returns (AccountSnapshot memory result);
/// @notice Get a snapshot of `account` data in this Curvance token.
/// @dev NOTE: Does not accrue pending assets as part of the call.
/// @param account The address of the account to snapshot.
/// @return The account snapshot of `account`.
function getSnapshot(
address account
) external view returns (AccountSnapshot memory);
/// @notice Total number of cTokens in circulation.
function totalSupply() external view returns (uint256);
/// @notice Returns the total amount of assets held by the market.
/// @return The total amount of assets held by the market.
function totalAssets() external view returns (uint256);
/// @notice Address of the Market Manager linked to this contract.
function marketManager() external view returns (IMarketManager);
/// @notice Returns the amount of assets that would be exchanged
/// by the vault for `shares` provided.
/// @param shares The number of shares to theoretically use
/// for conversion to assets.
/// @return The number of assets a user would receive for converting
/// `shares`.
function convertToAssets(uint256 shares) external view returns (uint256);
/// @notice Updates pending assets and returns the up-to-date exchange
/// rate from the underlying to the BorrowableCToken.
/// @dev Oracle Manager calculates cToken value from this exchange rate.
/// @return The share -> asset exchange rate, in `WAD`.
function exchangeRateUpdated() external returns (uint256) ;
/// @notice Returns the up-to-date exchange rate from the underlying to
/// the BorrowableCToken.
/// @dev Oracle Manager calculates cToken value from this exchange rate.
/// @return The share -> asset exchange rate, in `WAD`.
function exchangeRate() external view returns (uint256);
/// @notice Executes multiple calls in a single transaction.
/// This can be used to update oracle prices before
/// a liquidity dependent action.
function multicall(
Multicall.MulticallAction[] memory calls
) external returns (bytes[] memory results);
/// @notice Caller deposits `assets` into the market and `receiver`
/// receives shares.
/// @param assets The amount of the underlying assets to deposit.
/// @param receiver The account that should receive the shares.
/// @return shares The amount of shares received by `receiver`.
function deposit(
uint256 assets,
address receiver
) external returns (uint256 shares);
/// @notice Caller deposits `assets` into the market, `receiver` receives
/// shares, and collateralization of `assets` is enabled.
/// @dev The caller must be depositing for themselves, or be managing
/// their position through a Position Manager contract.
/// @param assets The amount of the underlying assets to deposit.
/// @param receiver The account that should receive the shares.
/// @return shares The amount of shares received by `receiver`.
function depositAsCollateral(
uint256 assets,
address receiver
) external returns (uint256 shares);
/// @notice Caller deposits `assets` into the market, `receiver` receives
/// shares, and collateralization of `assets` is enabled.
/// @dev Requires that `receiver` approves the caller prior to
/// collateralize on their behalf.
/// NOTE: Be careful who you approve here!
/// They can delay redemption of assets through repeated
/// collateralization preventing withdrawal.
/// @param assets The amount of the underlying assets to deposit.
/// @param receiver The account that should receive the shares.
/// @return shares The amount of shares received by `receiver`.
function depositAsCollateralFor(
uint256 assets,
address receiver
) external returns (uint256 shares);
/// @notice Withdraws assets, quoted in `shares` from the market,
/// and burns `owner` shares.
/// @dev Does not force collateral to be withdrawn.
/// @param shares The amount of shares to be redeemed.
/// @param receiver The account that should receive the assets.
/// @param owner The account that will burn their shares to withdraw
/// assets.
/// @return assets The amount of assets redeemed by `owner`.
function redeem(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
/// @notice Withdraws assets, quoted in `shares` from the market,
/// and burns `owner` shares, sending assets to `receiver`.
/// @dev Does not force collateral to be withdrawn.
/// @param shares The amount of shares to redeemed.
/// @param receiver The account that should receive the assets.
/// @param owner The account that will burn their shares to withdraw
/// assets.
/// @return assets The amount of assets redeemed by `owner` and sent to
/// `receiver`.
function redeemFor(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
/// @notice Caller withdraws assets from the market and burns their
/// shares, on behalf of `owner`.
/// @dev Forces collateral to be withdrawn from `owner` collateralPosted.
/// @param shares The amount of shares to redeemed.
/// @param receiver The account that should receive the assets.
/// @param owner The account that will burn their shares to withdraw
/// assets.
/// @return assets The amount of assets redeemed by `owner` and sent to
/// `receiver`.
function redeemCollateralFor(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
/// @notice Used by a Position Manager contract to redeem assets from
/// collateralized shares by `account` to perform a complex
/// action.
/// @param assets The amount of the underlying assets to redeem.
/// @param owner The owner address of assets to redeem.
/// @param action Instructions for a deleverage action containing:
/// cToken Address of the cToken that will be redeemed from
/// and assets swapped into `borrowableCToken` asset.
/// collateralAssets The amount of `cToken` that will be
/// deleveraged, in assets.
/// borrowableCToken Address of the borrowableCToken that
/// will have its debt paid.
/// repayAssets The amount of `borrowableCToken` asset that
/// will be repaid to lenders.
/// swapActions Swap actions instructions converting
/// collateral asset into debt asset to
/// facilitate deleveraging.
/// auxData Optional auxiliary data for execution of a
/// deleverage action.
function withdrawByPositionManager(
uint256 assets,
address owner,
IPositionManager.DeleverageAction memory action
) external;
/// @notice Amount of tokens that has been posted as collateral,
/// in shares.
function marketCollateralPosted() external view returns (uint256);
/// @notice Shares of this token that an account has posted as collateral.
/// @param account The address of the account to check collateral posted
/// of.
function collateralPosted(address account) external view returns (uint256);
/// @notice Transfers tokens from `account` to `liquidator`.
/// @dev Will fail unless called by a cToken during the process
/// of liquidation.
/// @param shares An array containing the number of cToken shares
/// to seize.
/// @param liquidator The account receiving seized cTokens.
/// @param accounts An array containing the accounts having
/// collateral seized.
function seize(
uint256[] calldata shares,
address liquidator,
address[] calldata accounts
) external;
/// @notice Allows users to simulate the effects of their deposit at
/// the current block.
/// @param assets The number of assets to preview a deposit call.
/// @return The shares received for depositing `assets`.
function previewDeposit(uint256 assets) external view returns (uint256);
}
// contracts/interfaces/IExternalCalldataChecker.sol
interface IExternalCalldataChecker {
/// @notice Inspects calldata for compliance with other swap instruction
/// parameters.
/// @dev Used on swap to inspect and validate calldata safety.
/// @param swapAction Swap action instructions including both direct
/// parameters and decodeable calldata.
/// @param expectedRecipient Address who will receive proceeds of
/// `swapAction`.
function checkCalldata(
SwapperLib.Swap memory swapAction,
address expectedRecipient
) external returns (uint256);
}
// contracts/interfaces/IOracleManager.sol
interface IOracleManager {
/// @notice Retrieves the price of a specified asset from either single
/// or dual oracles.
/// @dev If the asset has one oracle, it fetches the price from a single feed.
/// If it has two or more oracles, it fetches the price from both feeds.
/// @param asset The address of the asset to retrieve the price for.
/// @param inUSD Whether the price should be returned in USD or ETH.
/// @param getLower Whether the lower or higher price should be returned
/// if two feeds are available.
/// @return price The current price of `asset`.
/// @return errorCode An error code related to fetching the price:
/// '0' indicates no error fetching price.
/// '1' indicates that price should be taken with
/// caution.
/// '2' indicates a complete failure in receiving
/// a price.
function getPrice(
address asset,
bool inUSD,
bool getLower
) external view returns (uint256 price, uint256 errorCode);
/// @notice Retrieves the prices of a collateral token and debt token
/// underlyings.
/// @param collateralToken The cToken currently collateralized to price.
/// @param debtToken The cToken borrowed from to price.
/// @param errorCodeBreakpoint The error code that will cause liquidity
/// operations to revert.
/// @return collateralUnderlyingPrice The current price of
/// `collateralToken` underlying.
/// @return debtUnderlyingPrice The current price of `debtToken`
/// underlying.
function getPriceIsolatedPair(
address collateralToken,
address debtToken,
uint256 errorCodeBreakpoint
) external returns (uint256, uint256);
/// @notice Retrieves the prices and account data of multiple assets
/// inside a Curvance Market.
/// @param account The account to retrieve data for.
/// @param assets An array of asset addresses to retrieve the prices for.
/// @param errorCodeBreakpoint The error code that will cause liquidity
/// operations to revert.
/// @return AccountSnapshot[] Contains `assets` data for `account`
/// @return uint256[] Contains prices for `assets`.
/// @return uint256 The number of assets `account` is in.
function getPricesForMarket(
address account,
address[] calldata assets,
uint256 errorCodeBreakpoint
)
external
returns (AccountSnapshot[] memory, uint256[] memory, uint256);
/// @notice Potentially removes the dependency on pricing from `adaptor`
/// for `asset`, triggered by an adaptor's notification of a price
/// feed's removal.
/// @notice Removes a pricing adaptor for `asset` triggered by an
/// adaptor's notification of a price feed's removal.
/// @dev Requires that the adaptor is currently being used for pricing
/// for `asset`.
/// NOTE: This intentionally does not modify asset deviation values
/// because they simply wont be used if there are less than two
/// pricing adaptors in use, so no reason to delete data as
/// when a second pricing adaptor is configured the deviation
/// has the opportunity be to reconfigured anyway.
/// @param asset The address of the asset to potentially remove the
/// pricing adaptor dependency from depending on current
/// `asset` configuration.
function notifyFeedRemoval(address asset) external;
/// @notice Returns the adaptors used for pricing `asset`.
/// @param asset The address of the asset to get pricing adaptors for.
/// @return The current adaptor(s) used for pricing `asset`.
function getPricingAdaptors(
address asset
) external view returns(address[] memory);
/// @notice Address => Adaptor approval status.
/// @param adaptor The address of the adaptor to check.
/// @return True if the adaptor is supported, false otherwise.
function isApprovedAdaptor(address adaptor) external view returns (bool);
/// @notice Whether a token is recognized as a Curvance token or not,
/// if it is, will return its underlying asset address instead
/// of address (0).
/// @return The cToken's underlying asset, or address(0) if not a cToken.
function cTokens(address cToken) external view returns (address);
/// @notice Checks if a given asset is supported by the Oracle Manager.
/// @dev An asset is considered supported if it has one
/// or more associated price feeds.
/// @param asset The address of the asset to check.
/// @return True if the asset is supported, false otherwise.
function isSupportedAsset(address asset) external view returns (bool);
/// @notice Check whether L2 sequencer is valid or down.
/// @return True if sequencer is valid.
function isSequencerValid() external view returns (bool);
}
// contracts/interfaces/IPositionManager.sol
interface IPositionManager {
/// TYPES ///
/// @notice Instructions for a leverage action.
/// @param borrowableCToken Address of the borrowableCToken that will be
/// borrowed from and assets swapped into `cToken`
/// asset.
/// @param borrowAssets The amount borrowed from `borrowableCToken`,
/// in assets.
/// @param cToken Curvance token assets that borrowed funds will be
/// swapped into.
/// @param expectedShares The expected shares received from depositing
/// into `cToken` with swapped `borrowAssets`.
/// @param swapAction Swap action instructions converting debt asset into
/// collateral asset to facilitate leveraging.
/// @param auxData Optional auxiliary data for execution of a leverage
/// action.
struct LeverageAction {
IBorrowableCToken borrowableCToken;
uint256 borrowAssets;
ICToken cToken;
uint256 expectedShares;
SwapperLib.Swap swapAction;
bytes auxData;
}
/// @notice Instructions for a deleverage action.
/// @param cToken Address of the cToken that will be redeemed from and
/// assets swapped into `borrowableCToken` asset.
/// @param collateralAssets The amount of `cToken` that will be
/// deleveraged, in assets.
/// @param borrowableCToken Address of the borrowableCToken that will
/// have its debt paid.
/// @param repayAssets The amount of `borrowableCToken` asset that will
/// be repaid to lenders.
/// @param swapActions Swap actions instructions converting collateral
/// asset into debt asset to facilitate deleveraging.
/// @param auxData Optional auxiliary data for execution of a deleverage
/// action.
struct DeleverageAction {
ICToken cToken;
uint256 collateralAssets;
IBorrowableCToken borrowableCToken;
uint256 repayAssets;
SwapperLib.Swap[] swapActions;
bytes auxData;
}
/// @notice Callback function to execute post borrow of
/// `borrowableCToken`'s asset and swap it to deposit
/// new collateralized shares for `owner`.
/// @dev Measures slippage after this callback validating that `owner`
/// is still within acceptable liquidity requirements.
/// @param borrowableCToken The borrowableCToken borrowed from.
/// @param borrowAssets The amount of `borrowableCToken`'s asset borrowed.
/// @param owner The account borrowing that will be swapped into
/// collateral assets deposited into Curvance.
/// @param action Instructions for a leverage action containing:
/// borrowableCToken Address of the borrowableCToken that
/// will be borrowed from and assets
/// swapped into `cToken` asset.
/// borrowAssets The amount borrowed from
/// `borrowableCToken`, in assets.
/// cToken Curvance token assets that borrowed funds will be
/// swapped into.
/// swapAction Swap action instructions converting debt
/// asset into collateral asset to facilitate
/// leveraging.
/// auxData Optional auxiliary data for execution of a
/// leverage action.
function onBorrow(
address borrowableCToken,
uint256 borrowAssets,
address owner,
LeverageAction memory action
) external;
/// @notice Callback function to execute post redemption of `cToken`'s
/// asset and swap it to repay outstanding debt for `owner`.
/// @dev Measures slippage after this callback validating that `owner`
/// is still within acceptable liquidity requirements.
/// @param cToken The Curvance token redeemed for its underlying.
/// @param collateralAssets The amount of `cToken` underlying redeemed.
/// @param owner The account redeeming collateral that will be used to
/// repay their active debt.
/// @param action Instructions for a deleverage action containing:
/// cToken Address of the cToken that will be redeemed from
/// and assets swapped into `borrowableCToken` asset.
/// collateralAssets The amount of `cToken` that will be
/// deleveraged, in assets.
/// borrowableCToken Address of the borrowableCToken that
/// will have its debt paid.
/// repayAssets The amount of `borrowableCToken` asset that
/// will be repaid to lenders.
/// swapAction Swap actions instructions converting
/// collateral asset into debt asset to
/// facilitate deleveraging.
/// auxData Optional auxiliary data for execution of a
/// deleverage action.
function onRedeem(
address cToken,
uint256 collateralAssets,
address owner,
DeleverageAction memory action
) external;
}
// contracts/libraries/CommonLib.sol
/// @title Curvance Common Library
/// @notice A utility library for common functions used throughout the
/// Curvance Protocol.
library CommonLib {
/// @notice Returns whether `tokenA` matches `tokenB` or not.
/// @param tokenA The first token address to compare.
/// @param tokenB The second token address to compare.
/// @return Whether `tokenA` matches `tokenB` or not.
function _isMatchingToken(
address tokenA,
address tokenB
) internal pure returns (bool) {
if (tokenA == tokenB) {
return true;
}
if (_isNative(tokenA) && _isNative(tokenB)) {
return true;
}
return false;
}
/// @notice Returns whether `token` is referring to network gas token
/// or not.
/// @param token The address to review.
/// @return result Whether `token` is referring to network gas token or
/// not.
function _isNative(address token) internal pure returns (bool result) {
result = token == address(0) ||
token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
}
/// @notice Returns balance of `token` for this contract.
/// @param token The token address to query balance of.
/// @return b The balance of `token` inside address(this).
function _balanceOf(address token) internal view returns (uint256 b) {
b = _isNative(token) ? address(this).balance :
IERC20(token).balanceOf(address(this));
}
/// @notice Returns the Oracle Manager in interface form from `cr`.
/// @param cr The address of the Protocol Central Registry.
/// @return oracleManager The Oracle Manager in interface form.
function _oracleManager(
ICentralRegistry cr
) internal view returns (IOracleManager oracleManager) {
oracleManager = IOracleManager(cr.oracleManager());
}
}
// contracts/libraries/SwapperLib.sol
/// @title Curvance Swapper Library.
/// @notice Helper Library for performing composable swaps with varying
/// degrees of slippage tolerance. "Unsafe" swaps perform a standard
/// slippage check whereas "safe" swaps not only check for standard
/// slippage but also check against the Oracle Manager's prices as
/// well.
/// NOTE: This library does not intend to provide support for fee on
/// transfer tokens though support may be built in the future.
library SwapperLib {
/// TYPES ///
/// @notice Instructions to execute a swap, selling `inputToken` for
/// `outputToken`.
/// @param inputToken Address of input token to swap from.
/// @param inputAmount The amount of `inputToken` to swap.
/// @param outputToken Address of token to swap into.
/// @param target Address of the swapper, usually an aggregator.
/// @param slippage The amount of value-loss acceptable from swapping
/// between tokens.
/// @param call Swap instruction calldata.
struct Swap {
address inputToken;
uint256 inputAmount;
address outputToken;
address target;
uint256 slippage;
bytes call;
}
/// @notice Address identifying a chain's native token.
address public constant native =
0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// ERRORS ///
error SwapperLib__UnknownCalldata();
error SwapperLib__TokenPrice(address inputToken);
error SwapperLib__Slippage(uint256 slippage);
error SwapperLib__SameTokens();
/// INTERNAL FUNCTIONS ///
/// @notice Swaps `action.inputToken` into a `action.outputToken`
/// without an extra slippage check.
/// @param cr The address of the Protocol Central Registry to pull
/// addresses from.
/// @param action Instructions for a swap action containing:
/// inputToken Address of input token to swap from.
/// inputAmount The amount of `inputToken` to swap.
/// outputToken Address of token to swap into.
/// target Address of the swapper, usually an aggregator.
/// slippage The amount of value-loss acceptable from
/// swapping between tokens.
/// call Swap instruction calldata.
/// @return outAmount The output amount received from swapping.
function _swapUnsafe(
ICentralRegistry cr,
Swap memory action
) internal returns (uint256 outAmount) {
address outputToken = action.outputToken;
address inputToken = action.inputToken;
// Do not use this library if the tokens are the same.
if (CommonLib._isMatchingToken(inputToken, outputToken)) {
revert SwapperLib__SameTokens();
}
address callDataChecker = cr.externalCalldataChecker(action.target);
// Validate we know how to verify this calldata.
if (callDataChecker == address(0)) {
revert SwapperLib__UnknownCalldata();
}
// Verify calldata integrity.
uint256 minOutAmount = IExternalCalldataChecker(callDataChecker)
.checkCalldata(action, address(this));
// Approve `action.inputToken` to target contract, if necessary.
_approveIfNeeded(inputToken, action.target, action.inputAmount);
// Cache output token from struct for easier querying.
uint256 balanceBefore = CommonLib._balanceOf(outputToken);
uint256 callValue = CommonLib._isNative(inputToken) ?
action.inputAmount : 0;
// Execute the swap.
LowLevelCallsHelper._callWithNative(
action.target,
action.call,
callValue
);
// Remove any excess approval.
_removeApprovalIfNeeded(inputToken, action.target);
outAmount = CommonLib._balanceOf(outputToken) - balanceBefore;
if (outAmount < minOutAmount) {
revert SwapperLib__Slippage(minOutAmount);
}
}
/// @notice Swaps `action.inputToken` into a `action.outputToken`
/// with an extra slippage check.
/// @param cr The address of the Protocol Central Registry to pull
/// addresses from.
/// @param action Instructions for a swap action containing:
/// inputToken Address of input token to swap from.
/// inputAmount The amount of `inputToken` to swap.
/// outputToken Address of token to swap into.
/// target Address of the swapper, usually an
/// aggregator.
/// slippage The amount of value-loss acceptable from
/// swapping between tokens.
/// call Swap instruction calldata.
/// @return outAmount The output amount received from swapping.
function _swapSafe(
ICentralRegistry cr,
Swap memory action
) internal returns (uint256 outAmount) {
if (action.slippage >= WAD) {
revert SwapperLib__Slippage(action.slippage);
}
outAmount = _swapUnsafe(cr, action);
IOracleManager om = CommonLib._oracleManager(cr);
uint256 valueIn = _getValue(om, action.inputToken, action.inputAmount);
uint256 valueOut = _getValue(om, action.outputToken, outAmount);
// Check if swap received positive slippage.
if (valueOut > valueIn) {
return outAmount;
}
// Calculate % slippage from executed swap.
uint256 slippage = FixedPointMathLib.mulDivUp(
valueIn - valueOut,
WAD,
valueIn
);
if (slippage > action.slippage) {
revert SwapperLib__Slippage(slippage);
}
}
/// @notice Get the value of a token amount.
/// @notice Approves `token` spending allowance, if needed.
/// @param om The Oracle Manager address to call for pricing `token`.
/// @param token The token address to get the value of.
/// @param amount The amount of `token` to get the value of.
function _getValue(
IOracleManager om,
address token,
uint256 amount
) internal view returns (uint256 result) {
// If token is native, normalize to address(0) so it is compatible
// with the Oracle Manager.
if (token == address(0)) {
token = native;
}
(uint256 price, uint256 errorCode) = om.getPrice(token, true, true);
if (errorCode != NO_ERROR) {
revert SwapperLib__TokenPrice(token);
}
// Return price in WAD form.
result = FixedPointMathLib.mulDiv(
price,
amount,
10 ** (CommonLib._isNative(token) ? 18 : IERC20(token).decimals())
);
}
/// @notice Approves `token` spending allowance, if needed.
/// @param token The token address to approve.
/// @param spender The spender address.
/// @param amount The approval amount.
function _approveIfNeeded(
address token,
address spender,
uint256 amount
) internal {
if (!CommonLib._isNative(token)) {
SafeTransferLib.safeApprove(token, spender, amount);
}
}
/// @notice Removes `token` spending allowance, if needed.
/// @param token The token address to remove approval.
/// @param spender The spender address.
function _removeApprovalIfNeeded(address token, address spender) internal {
if (!CommonLib._isNative(token)) {
if (IERC20(token).allowance(address(this), spender) > 0) {
SafeTransferLib.safeApprove(token, spender, 0);
}
}
}
}
// contracts/market/DynamicIRM.sol
/// @title Curvance Dynamic Interest Rate Model.
/// @notice Manages borrow and lending interest rates for borrowable Curvance
/// tokens.
/// @dev A dynamically adjusting interest rate model built to incentivize
/// growth, and minimize liquidity crunches.
///
/// At its core the Curvance Dynamic Interest Rate Model uses two
/// different interest rates:
/// The "baseRatePerSecond" linearly increases interest until
/// `vertexStart` is reached, where `vertexRatePerSecond` then is
/// used instead. This behaves very similar to the classic "Jump Rate"
/// interest rate model just without the risk-free rate.
///
/// This model then builds on top of the previous systems by introducing
/// a dynamic "Vertex Multiplier" which increases the skew of
/// `vertexRatePerSecond`. The Vertex Multiplier is adjusted upward or
/// downward based on the utilization of liquidity inside the
/// borrowableCToken pool.
///
/// This means that if utilization remains elevated during update
/// periods, the interest rate paid by borrowers will continually
/// increase. This will, in theory, attract new lenders who require
/// a higher yield to provide liquidity to a particular market.
/// At the same time, higher borrow rates incentivize interest rate
/// sensitive borrowers to repay their outstanding debt.
/// These two actions both will decrease net liquidity utilization,
/// decreasing borrow rates, and with a heavy enough drop, begin
/// decreasing the Vertex Multiplier.
///
/// This process is optimized by the introduction of a decay mechanism.
/// When the Vertex Multiplier is elevated, the decay rate naturally
/// reduces the excess skew overtime. This has the effect of creating a
/// "downward sloping" interest rate model. From a mathematical sense,
/// this means when the multiplier value is elevated, a constant negative
/// velocity is applied to it, regardless of positive or negative
/// acceleration applied due to liquidity utilization. By having a
/// naturally decreasing interest rate model users are incentivized to
/// continually borrow from the borrowableCToken market over other
/// solutions. Then, when liquidity dries up, the interest rate model
/// attracts new lenders. The combination of these two forces should,
/// in theory, create an efficient system that naturally stimulates
/// market growth while also decreasing the risk of liquidity crunches.
///
/// The Vertex Multiplier adjustment logic is as follows:
///
/// When utilization is below `vertexStart`:
/// If the utilization is below the 'decreaseThresholdEnd',
/// decay rate and maximum adjustment velocity is applied.
/// For higher utilizations (but still below the vertex),
/// a new multiplier is calculated by applying a negative curve value
/// to the adjustment. This new multiplier is also subjected to the
/// decay multiplier.
///
/// When utilization is above `vertexStart`:
/// If the utilization rate is below the 'increaseThresholdStart',
/// it simply applies the decay to the current multiplier.
/// If the utilization is higher, it calculates a new multiplier by
/// applying a positive curve value to the adjustment. This adjustment
/// is also subjected to the decay multiplier.
///
/// NOTE: The Dynamic Interest Rate model will not be able to update its
/// modifier until a borrowable Curvance token is properly linked
/// to it via setlinkedToken().
///
/// If a borrowable Curvance token updates to another dynamic
/// interest rate model contract then this contract theoretically
/// can still be called by it afterwards if the smart contract was
/// malformed, this does not really have any tangible impact but
/// for developers who may adapt this smart contract in the future,
/// I figure its worth mentioning.
///
contract DynamicIRM is IDynamicIRM, ERC165 {
/// TYPES ///
/// @title Rates Configuration.
/// @notice Stores configuration data for current Dynamic Interest
/// Rate Model.
/// @dev Once this token is set it can never be changed, like an immutable
/// variable, this IRM will also be depreciated if that token ever
/// switches IRMs.
/// @param baseRatePerSecond Rate at which interest is accumulated,
/// before `vertexStart`, per second, in `WAD`.
/// @param vertexRatePerSecond Rate at which interest is
/// accumulated, after `vertexStart`,
/// per second, in `WAD`.
/// @param vertexStart Utilization rate point where vertex rate
/// is used, instead of base rate, in `WAD`.
/// @param increaseThresholdStart The utilization rate at which the vertex
/// multiplier will begin to increase,
/// in 10 * `BPS`.
/// @param decreaseThresholdEnd The utilization rate at which the vertex
/// multiplier negative velocity will max out,
/// in 10 * `BPS`.
/// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
/// is adjusted per `adjustmentRate`, in `BPS`.
/// @param decayPerAdjustment The rate at which `vertexMultiplier` will
/// decay back down per `adjustmentRate`,
/// in `BPS`.
/// @param vertexMultiplierMax The maximum value that `vertexMultiplier`
/// can be, in `multiplier` denomination
/// aka `WAD`.
/// @param linkedToken The borrowable Curvance token linked to this
/// interest rate model contract.
struct RatesConfig {
uint64 baseRatePerSecond;
uint64 vertexRatePerSecond;
uint64 vertexStart;
uint24 increaseThresholdStart;
uint24 decreaseThresholdEnd;
uint16 adjustmentVelocity;
uint16 decayPerAdjustment;
uint80 vertexMultiplierMax;
address linkedToken;
}
/// CONSTANTS ///
/// @notice The interval at which interest rates are adjusted, in seconds.
/// @dev 10 minutes = 600 seconds.
/// The 10 minute adjustment rate is set so that borrowers experience
/// on average 2 interest rate adjustments from a standard borrow
/// action (20 minute minimum holding period).
uint256 public constant ADJUSTMENT_RATE = 10 minutes;
/// @notice Curvance DAO hub.
ICentralRegistry public immutable centralRegistry;
/// @notice The maximum value that the annual base interest rate can
/// be set to, in `WAD`.
/// E.g. 1.5 * WAD = 150% Base Interest Rate value at
/// `vertexStart` % borrowing utilization.
uint256 internal constant _MAX_BASE_INTEREST_RATE_PER_YEAR = 1.5e18;
/// @notice The maximum value that the annual vertex interest rate can
/// be set to, in `WAD`.
/// E.g. 2 * WAD = 200% Vertex Interest Rate value at
/// 100% borrowing utilization.
uint256 internal constant _MAX_VERTEX_INTEREST_RATE_PER_YEAR = 2e18;
/// @notice The maximum value that the vertex interest rate can
/// be set to begin at, in `WAD`.
/// E.g. 0.99 * WAD = Vertex rate begins at 99% utilization.
uint256 internal constant _MAX_VERTEX_START = 0.99e18;
/// @notice The minimum value that the vertex interest rate can
/// be set to begin at, in `WAD`.
/// E.g. 0.50 * WAD = Vertex rate begins at 50% utilization.
uint256 internal constant _MIN_VERTEX_START = 0.5e18;
/// @notice The maximum rate at which `vertexMultiplier` is adjusted,
/// in BPS on top of base rate (1 `BPS`).
/// E.g. 1 * BPS = 120% multiplied to vertex interest rate per
/// `ADJUSTMENT_RATE` at 100% utilization.
uint256 internal constant _MAX_VERTEX_ADJUSTMENT_VELOCITY = 2000;
/// @notice The minimum rate at which `vertexMultiplier` is adjusted,
/// in BPS on top of base rate (1 `BPS`).
/// E.g. 0.01 * BPS = 101% multiplied to vertex interest rate per
/// `ADJUSTMENT_RATE` at 100% utilization.
uint256 internal constant _MIN_VERTEX_ADJUSTMENT_VELOCITY = 100;
/// @notice Maximum Rate at which `vertexMultiplier` will
/// decay per `ADJUSTMENT_RATE`, in `BPS`.
/// @dev 200 = 2%.
uint256 internal constant _MAX_VERTEX_DECAY_RATE = 200;
/// @notice The maximum value that `vertexMultiplierMax` can be set
/// to, in `WAD`.
/// E.g. 1 * WAD = 100% Maximum `vertexMultiplierMax` value.
uint256 internal constant _MAXIMUM_VERTEX_MULTIPLIER_MAX = 50e18;
/// @notice The minimum value that `vertexMultiplierMax` can be set to,
/// in `WAD`.
/// E.g. 1 * WAD = 100% Minimum `vertexMultiplierMax` value.
uint256 internal constant _MINIMUM_VERTEX_MULTIPLIER_MAX = WAD;
/// @notice Very small adjustments in utilization can lose 1 decimal of
/// precision when calculating inflection points with `BPS`
/// denomination so we use 10 * BPS instead for
/// `increaseThresholdStart` and `decreaseThresholdEnd`.
uint256 internal constant _INFLECTION_DENOMINATION = 1e13;
/// STORAGE ///
/// @notice The dynamic value applied to `vertexRatePerSecond`, increasing
/// it as `utilizationRate` remains elevated over time, adjusted
/// every `ADJUSTMENT_RATE`, in `WAD`.
uint256 public vertexMultiplier;
/// @notice Struct containing current configuration data for the
/// dynamic interest rate model.
RatesConfig public ratesConfig;
/// EVENTS ///
event NewIRM(RatesConfig config);
event TokenLinked(address borrowableCToken);
/// ERRORS ///
error DynamicIRM__Unauthorized();
error DynamicIRM__InvalidToken();
error DynamicIRM__InvalidUtilizationStart();
error DynamicIRM__InvalidInterestRatePerYear();
error DynamicIRM__InvalidAdjustmentVelocity();
error DynamicIRM__InvalidDecayRate();
error DynamicIRM__InvalidMultiplierMax();
/// CONSTRUCTOR ///
/// @param cr The address of the Protocol Central Registry.
/// @param baseRatePerYear Rate at which interest is accumulated,
/// before `vertexStart`, per year,
/// in `BPS`.
/// @param vertexRatePerYear Rate at which interest is accumulated,
/// after `vertexStart`, per year,
/// in `BPS`.
/// @param vertexStart The utilization point at which the vertex
/// rate is applied, in `BPS`.
/// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
/// is adjusted per `adjustmentRate`, in `BPS`.
/// @param decayPerAdjustment The rate at which `vertexMultiplier` will
/// decay back down per `adjustmentRate`,
/// in `BPS`.
/// @param vertexMultiplierMax The maximum value that `vertexMultiplier`
/// can be, in `BPS`.
constructor(
ICentralRegistry cr,
uint256 baseRatePerYear,
uint256 vertexRatePerYear,
uint256 vertexStart,
uint256 adjustmentVelocity,
uint256 decayPerAdjustment,
uint256 vertexMultiplierMax
) {
CentralRegistryLib._isCentralRegistry(cr);
centralRegistry = cr;
_updateDynamicIRM(
baseRatePerYear,
vertexRatePerYear,
vertexStart,
adjustmentVelocity,
decayPerAdjustment,
vertexMultiplierMax,
true
);
}
/// EXTERNAL FUNCTIONS ///
/// @notice The borrowable Curvance token linked to this interest rate
/// model contract.
/// @dev Once this token is set it can never be changed, like an immutable
/// variable, this IRM will also be depreciated if that token ever
/// switches IRMs.
/// @return result The linked borrowableCToken address.
function linkedToken() external view returns(address result) {
result = ratesConfig.linkedToken;
}
/// @notice Sets the dynamic interest rate model's linked borrowable
/// Curvance token (cToken) which interest rates this contract
/// will manage.
/// @dev Once this function is properly it can never be called again.
/// @param cTokenAddress The address of the token to be linked
/// to this interest rate model contract.
function setLinkedToken(address cTokenAddress) external {
if (!centralRegistry.hasDaoPermissions(msg.sender)) {
revert DynamicIRM__Unauthorized();
}
RatesConfig storage c = ratesConfig;
// Validate that a borrowable Curvance token has not already been
// linked to this smart contract.
if (c.linkedToken != address(0)) {
revert DynamicIRM__Unauthorized();
}
// Validate that the token being linked is actually a borrowable token
// if the token is not a Curvance token this will also natively fail,
// which is fine too.
if (!IBorrowableCToken(cTokenAddress).isBorrowable()) {
revert DynamicIRM__InvalidToken();
}
// Validate that the token is actually expecting this interest
// rate model to be linked to it.
if (address(IBorrowableCToken(cTokenAddress).IRM()) != address(this)) {
revert DynamicIRM__InvalidToken();
}
c.linkedToken = cTokenAddress;
emit TokenLinked(cTokenAddress);
}
/// @notice Updates the dynamic interest rate model's configuration
/// for calculating interest payment for `linkedToken`.
/// @param baseRatePerYear Rate at which interest is accumulated,
/// before `vertexStart`, per year, in `BPS`.
/// @param vertexRatePerYear Rate at which interest is accumulated,
/// after `vertexStart`, per year, in `BPS`.
/// @param vertexStart The utilization point at which the vertex
/// rate is applied, in `BPS`.
/// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
/// is adjusted per `adjustmentRate`, in `BPS`.
/// @param decayPerAdjustment The rate at which `vertexMultiplier` will
/// decay back down per `adjustmentRate`,
/// in `BPS`.
/// @param vertexMultiplierMax The maximum value that `vertexMultiplier`
/// can be, in `BPS`.
/// @param vertexReset Whether `vertexMultiplier` should be reset back
/// to its default value.
function updateDynamicIRM(
uint256 baseRatePerYear,
uint256 vertexRatePerYear,
uint256 vertexStart,
uint256 adjustmentVelocity,
uint256 decayPerAdjustment,
uint256 vertexMultiplierMax,
bool vertexReset
) external {
_checkMarketPermissions();
address linkedCToken = ratesConfig.linkedToken;
if (linkedCToken != address(0)) {
IBorrowableCToken(linkedCToken).accrueIfNeeded();
}
_updateDynamicIRM(
baseRatePerYear,
vertexRatePerYear,
vertexStart,
adjustmentVelocity,
decayPerAdjustment,
vertexMultiplierMax,
vertexReset
);
}
/// @notice Calculates the interest rate paid per second by borrowers,
/// in percentage paid, per second, in `WAD`, and updates
/// `vertexMultiplier` if necessary.
/// @dev NOTE: Updates to `vertexMultiplier` are applied to `ratesPerSecond`
/// AFTER the next adjustment, this is to prevent someone flash
/// borrowing/lending a ton of capital and moving rates.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param debt The amount of outstanding debt in the pool.
/// @return ratePerSecond The interest rate paid per second by borrowers,
/// in percentage paid, per second, in `WAD`.
/// @return adjustmentRate The period of time at which interest rates are
/// adjusted, in seconds.
function adjustedBorrowRate(
uint256 assetsHeld,
uint256 debt
) external returns (uint256 ratePerSecond, uint256 adjustmentRate) {
RatesConfig memory c = ratesConfig;
// Validate that the linked token itself is calling to update
// its interest accrued.
if (msg.sender != c.linkedToken) {
revert DynamicIRM__Unauthorized();
}
uint256 util = utilizationRate(assetsHeld, debt);
uint256 vertexPoint = c.vertexStart;
uint256 multiplier = vertexMultiplier;
bool belowVertex = (util <= vertexPoint);
// Pull current interest rate.
if (belowVertex) {
ratePerSecond = _baseRate(util, c.baseRatePerSecond);
} else {
ratePerSecond = _vertexRate(
util,
c.baseRatePerSecond,
c.vertexRatePerSecond,
vertexPoint,
multiplier
);
}
// If `vertexMultiplier` is already at its minimum,
// and would decrease more, can break here.
// Convert `increaseThresholdStart` to `WAD` to be in same terms as
// `util`, no precision loss as a result.
if (
multiplier == WAD &&
util < (_INFLECTION_DENOMINATION * uint256(c.increaseThresholdStart))
) {
return (ratePerSecond, ADJUSTMENT_RATE);
}
/// Vertex Multiplier adjustment cases below:
/// Case 1: Vertex Multiplier downward adjustment.
if (belowVertex) {
vertexMultiplier = _updateBelowVertex(c, util, multiplier);
return (ratePerSecond, ADJUSTMENT_RATE);
}
/// Case 2: Vertex Multiplier upward adjustment.
vertexMultiplier = _updateAboveVertex(c, util, multiplier);
adjustmentRate = ADJUSTMENT_RATE;
}
/// PUBLIC FUNCTIONS ///
/// @notice Calculates the current borrow rate per second,
/// with updated `vertexMultiplier` applied.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param debt The amount of outstanding debt in the pool.
/// @return result The borrow rate percentage per second, in `WAD`.
function predictedBorrowRate(
uint256 assetsHeld,
uint256 debt
) public view returns (uint256 result) {
uint256 util = utilizationRate(assetsHeld, debt);
RatesConfig memory c = ratesConfig;
uint256 vertexStart = c.vertexStart;
// Directly pull interest rate since `vertexMultiplier` is irrelevant
// with util <= vertexStart.
if (util <= vertexStart) {
return _baseRate(util, c.baseRatePerSecond);
}
uint256 multiplier = vertexMultiplier;
// Vertex multiplier is not going to change so we can pull interest
// rate with current `vertexMultiplier`.
// Convert `increaseThresholdStart` to `WAD` to be in same terms as
// `util`, no precision loss as a result.
if (
multiplier == WAD &&
util < (_INFLECTION_DENOMINATION * uint256(c.increaseThresholdStart))
) {
return _vertexRate(
util,
c.baseRatePerSecond,
c.vertexRatePerSecond,
vertexStart,
multiplier
);
}
// Get updated vertex multiplier then pull interest rate with new
// vertex multiplier.
result = _vertexRate(
util,
c.baseRatePerSecond,
c.vertexRatePerSecond,
vertexStart,
_updateAboveVertex(c, util, multiplier)
);
}
/// @notice Calculates the current borrow rate, per second.
/// @dev This function's intention is for frontend data querying and
/// should not be used for onchain execution.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param debt The amount of outstanding debt in the pool.
/// @return r The borrow interest rate percentage, per second, in `WAD`.
function borrowRate(
uint256 assetsHeld,
uint256 debt
) public view returns (uint256 r) {
uint256 util = utilizationRate(assetsHeld, debt);
// Cache from storage since we only need to query a new config values.
RatesConfig storage c = ratesConfig;
uint256 vertexStart = c.vertexStart;
if (util <= vertexStart) {
return _baseRate(util, c.baseRatePerSecond);
}
r = _vertexRate(
util,
c.baseRatePerSecond,
c.vertexRatePerSecond,
vertexStart,
vertexMultiplier
);
}
/// @notice Calculates the current supply rate, per second.
/// @dev This function's intention is for frontend data querying and
/// should not be used for onchain execution.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param debt The amount of outstanding debt in the pool.
/// @param interestFee The current interest rate protocol fee
/// for the market token, in `BPS`.
/// @return r The supply interest rate percentage, per second, in `WAD`.
function supplyRate(
uint256 assetsHeld,
uint256 debt,
uint256 interestFee
) public view returns (uint256 r) {
// feeAdjustedBorrowRate = (borrowRate * (1 - Interest Fee)) / BPS.
uint256 feeAdjustedBorrowRate = _mulDiv(
borrowRate(assetsHeld, debt),
BPS - interestFee,
BPS
);
// Supply Rate = (utilizationRate * feeAdjustedBorrowRate) / WAD.
r = _mulDiv(
utilizationRate(assetsHeld, debt),
feeAdjustedBorrowRate,
WAD
);
}
/// @notice Calculates the borrow utilization rate of the market.
/// @param assetsHeld The amount of underlying assets held in the pool.
/// @param debt The amount of outstanding debt in the pool.
/// @return r The utilization rate between [0, WAD].
function utilizationRate(
uint256 assetsHeld,
uint256 debt
) public pure returns (uint256 r) {
// Utilization rate is 0 when there are no outstanding debt.
r = debt == 0 ? 0 : _mulDiv(debt, WAD, assetsHeld + debt);
}
/// @inheritdoc ERC165
/// @param interfaceId The interface ID to check.
/// @return result Whether the contract implements the interface.
function supportsInterface(
bytes4 interfaceId
) public view override returns (bool result) {
result = interfaceId == type(IDynamicIRM).interfaceId ||
super.supportsInterface(interfaceId);
}
/// INTERNAL FUNCTIONS ///
/// @notice Calculates the interest rate for `util` market utilization.
/// @param util The utilization rate of the market, in `WAD`.
/// @return r The calculated base interest rate, in `WAD`.
function _baseRate(
uint256 util,
uint256 baseRatePerSecond
) internal pure returns (uint256 r) {
r = _mulDiv(util, baseRatePerSecond, WAD);
}
/// @notice Calculates the interest rate under `vertexRatePerSecond`
/// conditions, e.g. `util` > `vertex ` based on market
/// utilization.
/// @param util The utilization rate of the market above
/// `vertexStart`, in `WAD`.
/// @param vertexStart The point at which utilization begins
/// calculating interest rate paid off
/// `vertexRatePerSecond` * `multiplier`.
/// @param multiplier The multiplicative value applied to
/// `vertexRatePerSecond`, in `WAD`.
/// @return r The calculated vertex interest rate, in `WAD`.
function _vertexRate(
uint256 util,
uint256 baseRatePerSecond,
uint256 vertexRatePerSecond,
uint256 vertexStart,
uint256 multiplier
) internal pure returns (uint256 r) {
r = _mulDiv(vertexStart, baseRatePerSecond, WAD) +
_mulDiv(
util - vertexStart,
vertexRatePerSecond * multiplier,
WAD_SQUARED
);
}
/// @notice Updates the parameters of the dynamic interest rate model
/// used in the market.
/// @dev This function sets various parameters for the dynamic interest
/// rate model, adjusting how interest rates are calculated based
/// on system utilization.
/// Emits a {NewIRM} event.
/// @param baseRatePerYear Rate at which interest is accumulated,
/// before `vertexStart`, per year,
/// in `BPS`.
/// @param vertexRatePerYear Rate at which interest is accumulated,
/// after `vertexStart`, per year,
/// in `BPS`.
/// @param vertexStart The utilization point at which the vertex
/// rate is applied, in `BPS`.
/// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
/// is adjusted per `adjustmentRate`, in `BPS`.
/// @param decayPerAdjustment The rate at which `vertexMultiplier` will
/// decay back down per `adjustmentRate`,
/// in `BPS`.
/// @param vertexMultiplierMax The maximum value that `vertexMultiplier`
/// can be, in `BPS`.
/// @param vertexReset A boolean flag indicating whether `vertexMultiplier`
/// should be reset to `WAD`.
function _updateDynamicIRM(
uint256 baseRatePerYear,
uint256 vertexRatePerYear,
uint256 vertexStart,
uint256 adjustmentVelocity,
uint256 decayPerAdjustment,
uint256 vertexMultiplierMax,
bool vertexReset
) internal {
// Convert the parameters from `BPS` to `WAD` format.
// While inefficient, we want to minimize potential human error
// as much as possible, even if it costs a bit extra gas on config.
baseRatePerYear = _bpToWad(baseRatePerYear);
vertexRatePerYear = _bpToWad(vertexRatePerYear);
vertexStart = _bpToWad(vertexStart);
vertexMultiplierMax = _bpToWad(vertexMultiplierMax);
/// Validate config values are within allowed constraints.
if (
vertexStart > _MAX_VERTEX_START ||
vertexStart < _MIN_VERTEX_START
) {
revert DynamicIRM__InvalidUtilizationStart();
}
if (
baseRatePerYear > _MAX_BASE_INTEREST_RATE_PER_YEAR ||
vertexRatePerYear > _MAX_VERTEX_INTEREST_RATE_PER_YEAR
) {
revert DynamicIRM__InvalidInterestRatePerYear();
}
if (decayPerAdjustment > _MAX_VERTEX_DECAY_RATE) {
revert DynamicIRM__InvalidDecayRate();
}
if (
adjustmentVelocity > _MAX_VERTEX_ADJUSTMENT_VELOCITY ||
adjustmentVelocity < _MIN_VERTEX_ADJUSTMENT_VELOCITY
) {
revert DynamicIRM__InvalidAdjustmentVelocity();
}
if (
vertexMultiplierMax > _MAXIMUM_VERTEX_MULTIPLIER_MAX ||
vertexMultiplierMax < _MINIMUM_VERTEX_MULTIPLIER_MAX
) {
revert DynamicIRM__InvalidMultiplierMax();
}
RatesConfig storage config = ratesConfig;
config.baseRatePerSecond = uint64(_mulDiv(
baseRatePerYear,
WAD,
SECONDS_PER_YEAR * vertexStart
));
config.vertexRatePerSecond = uint64(_mulDiv(
vertexRatePerYear,
WAD,
SECONDS_PER_YEAR * (WAD - vertexStart)
));
config.vertexStart = uint64(vertexStart);
config.adjustmentVelocity = uint16(adjustmentVelocity);
config.decayPerAdjustment = uint16(decayPerAdjustment);
config.vertexMultiplierMax = uint80(vertexMultiplierMax);
if (vertexReset) {
vertexMultiplier = WAD;
} else {
uint256 cachedVertexMultiplier = vertexMultiplier;
// If the new `vertexMultiplierMax` is below `vertexMultiplier`
// we should clamp it down to the new maximum.
if (vertexMultiplierMax < cachedVertexMultiplier) {
vertexMultiplier = vertexMultiplierMax;
}
}
// Dynamic rates start increasing halfway between desired
// utilization and 100% utilization, in `WAD`.
uint256 thresholdLength = (WAD - vertexStart) / 2;
// Dynamic rates start increasing as soon as we are halfway between
// `vertexStart` and 100% utilization. Convert to `BPS` form to save a
// storage slot and `vertexStart` is set in `BPS` so we shouldnt lose
// precision.
config.increaseThresholdStart =
uint24((vertexStart + thresholdLength) / _INFLECTION_DENOMINATION);
// Dynamic rates start decreasing as soon as we are below desired
// utilization (vertexStart) and maximizes an equal utilization down
// from where increase threshold starts upward. Convert to `BPS` form
// to save a storage slot and `vertexStart` is set in `BPS` so we
// shouldnt lose precision.
config.decreaseThresholdEnd =
uint24((vertexStart - thresholdLength) / _INFLECTION_DENOMINATION);
emit NewIRM(config);
}
/// @notice Calculates and returns the updated multiplier for scenarios
/// where the utilization rate is above the vertex.
/// @dev This function is used to adjust `vertexMultiplier` based on
/// the borrowableCToken's current borrow utilization.
/// A decay mechanism is incorporated to gradually decrease the
/// multiplier, and ensures the multiplier does not fall below 1,
/// in WAD.
/// NOTE: The multiplier is updated with the following logic:
/// If the utilization is below the 'increaseThresholdStart',
/// it simply applies the decay to the current multiplier.
/// If the utilization is higher, it calculates a new multiplier by
/// applying a positive curve value to the adjustment.
/// This adjustment is also subjected to the decay multiplier.
/// @param c The cached version of the current `RatesConfig`.
/// @param util The current utilization value, used to determine how the
/// multiplier should be adjusted.
/// @param multiplier The dynamic value applied to `vertexRatePerSecond`
/// that will be adjusted, in `WAD`.
/// @return newMultiplier The updated multiplier after applying decay
/// and adjustments based on the current utilization
/// level.
function _updateAboveVertex(
RatesConfig memory c,
uint256 util,
uint256 multiplier
) internal pure returns (uint256 newMultiplier) {
// Calculate decay rate.
uint256 decay = _mulDiv(multiplier, c.decayPerAdjustment, BPS);
if (util <= (_INFLECTION_DENOMINATION * uint256(c.increaseThresholdStart))) {
newMultiplier = multiplier - decay;
// Check if decay rate sends new rate below 1.
return newMultiplier < WAD ? WAD : newMultiplier;
}
// Apply a positive multiplier to the current multiplier based on
// `util` vs `increaseThresholdStart` and `WAD`.
// Then apply decay effect.
newMultiplier = _positiveShift(
multiplier, // `multiplier` in `WAD`.
c.adjustmentVelocity, // `adjustmentVelocity` in `BPS`.
decay, // `decay` in `multiplier` aka `WAD`.
util, // `current` in `WAD`.
_INFLECTION_DENOMINATION * uint256(c.increaseThresholdStart) // `start` convert to WAD to match util.
);
// Update and return with adjustment and decay rate applied.
// Its theorectically possible for the multiplier to be below 1
// due to decay, so we need to check like in below vertex.
if (newMultiplier < WAD) {
return WAD;
}
// Make sure `newMultiplier` is not above `vertexMultiplierMax`.
newMultiplier = newMultiplier < c.vertexMultiplierMax
? newMultiplier
: c.vertexMultiplierMax;
}
/// @notice Calculates and returns the updated multiplier for scenarios
/// where the utilization rate is below the vertex.
/// @dev This function is used to adjust `vertexMultiplier` based on
/// the borrowableCToken's current borrow utilization.
/// A decay mechanism is incorporated to gradually decrease the
/// multiplier, and ensures the multiplier does not fall below 1,
/// in WAD.
/// NOTE: The multiplier is updated with the following logic:
/// If the utilization is below the 'decreaseThresholdEnd',
/// decay rate and maximum adjustment velocity is applied.
/// For higher utilizations (but still below the vertex),
/// a new multiplier is calculated by applying a negative curve value
/// to the adjustment. This new multiplier is also subjected to the
/// decay multiplier.
/// @param c The cached version of the current `RatesConfig`.
/// @param util The current utilization value, used to determine how the
/// multiplier should be adjusted.
/// @param multiplier The dynamic value applied to `vertexRatePerSecond`
/// that will be adjusted, in `WAD`.
/// @return newMultiplier The updated multiplier after applying decay
/// and adjustments based on the current utilization
/// level.
function _updateBelowVertex(
RatesConfig memory c,
uint256 util,
uint256 multiplier
) internal pure returns (uint256 newMultiplier) {
// Calculate decay rate.
uint256 decay = _mulDiv(multiplier, c.decayPerAdjustment, BPS);
// Convert `decreaseThresholdEnd` to `WAD` to be in same terms as
// `util`, no precision loss as a result.
if (util <= (_INFLECTION_DENOMINATION * uint256(c.decreaseThresholdEnd))) {
// Apply maximum adjustVelocity reduction (shift = 1).
// We only need to adjust for `BPS` precision since `shift`
// is not used here.
// currentMultiplier / (1 + adjustmentVelocity) = newMultiplier.
newMultiplier = _mulDiv(
multiplier,
BPS,
BPS + c.adjustmentVelocity
) - decay;
// Check if decay rate sends multiplier below 1.
return newMultiplier < WAD ? WAD : newMultiplier;
}
// Apply a negative multiplier to the current multiplier based on
// `util` vs `vertexStart` and `decreaseThresholdEnd`.
// Then apply decay effect.
newMultiplier = _negativeShift(
multiplier, // `multiplier` in `WAD`.
c.adjustmentVelocity, // `adjustmentVelocity` in `BPS`.
decay, // `decay` in `multiplier` aka `WAD`.
util, // `current` in `WAD`.
c.vertexStart, // `start` in `WAD`.
_INFLECTION_DENOMINATION * uint256(c.decreaseThresholdEnd) // `end` convert to WAD to match util/vertexStart.
);
// Update and return with adjustment and decay rate applied.
// But first check if new rate sends multiplier below 1.
return newMultiplier < WAD ? WAD : newMultiplier;
}
/// @notice Calculates positive shift value based on `current`,
/// `start`, and `end` values. Then applies the linear curve
/// effect to the adjustment velocity, then applies the
/// adjustment velocity and decay effect to `multiplier`.
/// @dev The shift is scaled by current, start, and end values and
/// multiplied by `WAD` to maintain precision. This results in 1,
/// (in `WAD`) if the current value is greater than or equal to
/// `end`. The terminal shift is then applied to the
/// adjustmentVelocity in determining the growth of `multiplier` for
/// this period, then the decay rate is applied.
/// @param multiplier The dynamic value applied to `vertexRatePerSecond`
/// that will be adjusted, in `WAD`.
/// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
/// is adjusted per `adjustmentRate`, in `BPS`.
/// @param decay The current decay rate, calculated as a negative % value,
/// in `multiplier` denomination, aka `WAD` form.
/// @param current The current value, representing a point on the curve.
/// @param start The start value of the curve, marking the beginning of
/// the calculation range.
/// @return result The new multiplier with the calculated shift value,
/// and decay rate applied.
function _positiveShift(
uint256 multiplier,
uint256 adjustmentVelocity,
uint256 decay,
uint256 current, // `util`.
uint256 start // `increaseThresholdStart`.
) internal pure returns (uint256 result) {
// We do not need to check for current >= end, since we know util is
// the absolute maximum utilization is 100%, and thus current == end.
// Which will result in WAD result for `shift`.
// Thus, this will be bound between [0, WAD].
uint256 shift = _mulDiv(current - start, WAD, WAD - start);
// Apply `shift` result to adjustment velocity.
// Then add 100% on top for final adjustment value to `multiplier`.
// We use WAD_BPS here since `shift` is in `WAD` for max precision but
// `adjustmentVelocity` is in BPS since it does not need greater
// precision due to being configured in `BPS`.
shift = WAD_BPS + (shift * adjustmentVelocity);
// Apply positive `shift` effect to `currentMultiplier`, and
// adjust for 1e36 precision. Then apply decay effect.
result = _mulDiv(multiplier, shift, WAD_BPS) - decay;
}
/// @notice Calculates negative shift value based on `current`,
/// `start`, and `end` values. Then applies the linear curve
/// effect to the adjustment velocity, then applies the
/// adjustment velocity and decay effect to `multiplier`.
/// @dev The shift is scaled by current, start, and end values and
/// multiplied by `WAD` to maintain precision. This results in 1,
/// (in `WAD`) if the current value is less than or equal to
/// `end`. The terminal shift is then applied to the
/// adjustmentVelocity in determining the reduction of `multiplier`
/// for this period, then the decay rate is applied.
/// @param multiplier The dynamic value applied to `vertexRatePerSecond`
/// that will be adjusted, in `WAD`.
/// @param adjustmentVelocity The maximum rate at which `vertexMultiplier`
/// is adjusted per `adjustmentRate`, in `BPS`.
/// @param decay The current decay rate, calculated as a negative % value,
/// in `multiplier` denomination, aka `WAD` form.
/// @param current The current value, representing a point on the curve.
/// @param start The start value of the curve, marking the beginning of
/// the calculation range, equal to `vertexStart`.
/// @param end The end value of the curve, marking the end of the
/// calculation range.
/// @return result The new multiplier with the calculated shift value,
/// and decay rate applied.
function _negativeShift(
uint256 multiplier,
uint256 adjustmentVelocity,
uint256 decay,
uint256 current, // `util`.
uint256 start, // `vertexStart`.
uint256 end // `decreaseThresholdEnd`.
) internal pure returns (uint256 result) {
// Calculate linear curve multiplier. We know that current > end,
// based on pre conditional checks.
// Thus, this will be bound between [0, WAD].
uint256 shift = _mulDiv(start - current, WAD, start - end);
// Apply `shift` result to adjustment velocity.
// Then add 100% on top for final adjustment value to `multiplier`.
// We use WAD_BPS here since `shift` is in `WAD` for max precision but
// `adjustmentVelocity` is in BPS since it does not need greater
// precision due to being configured in `BPS`.
shift = WAD_BPS + (shift * adjustmentVelocity);
// Apply negative `shift` effect to `currentMultiplier`, and
// adjust for 1e36 precision. Then apply decay effect.
result = _mulDiv(multiplier, WAD_BPS, shift) - decay;
}
/// @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) {
z = FixedPointMathLib.mulDiv(x, y, d);
}
/// @dev Checks whether the caller has sufficient permissioning.
function _checkMarketPermissions() internal view virtual {
if (!centralRegistry.hasMarketPermissions(msg.sender)) {
revert DynamicIRM__Unauthorized();
}
}
/// @notice Multiplies `value` by 1e14 to convert it from `BPS`
/// to `WAD`.
/// @dev Internal helper function for easily converting between scalars.
/// @param value The value to convert from `BPS` to `WAD`.
/// @return result The value, in `WAD`.
function _bpToWad(uint256 value) internal pure returns (uint256 result) {
result = value * 1e14;
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"contract ICentralRegistry","name":"cr","type":"address"},{"internalType":"uint256","name":"baseRatePerYear","type":"uint256"},{"internalType":"uint256","name":"vertexRatePerYear","type":"uint256"},{"internalType":"uint256","name":"vertexStart","type":"uint256"},{"internalType":"uint256","name":"adjustmentVelocity","type":"uint256"},{"internalType":"uint256","name":"decayPerAdjustment","type":"uint256"},{"internalType":"uint256","name":"vertexMultiplierMax","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CentralRegistryLib__InvalidCentralRegistry","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidAdjustmentVelocity","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidDecayRate","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidInterestRatePerYear","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidMultiplierMax","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidToken","type":"error"},{"inputs":[],"name":"DynamicIRM__InvalidUtilizationStart","type":"error"},{"inputs":[],"name":"DynamicIRM__Unauthorized","type":"error"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint64","name":"baseRatePerSecond","type":"uint64"},{"internalType":"uint64","name":"vertexRatePerSecond","type":"uint64"},{"internalType":"uint64","name":"vertexStart","type":"uint64"},{"internalType":"uint24","name":"increaseThresholdStart","type":"uint24"},{"internalType":"uint24","name":"decreaseThresholdEnd","type":"uint24"},{"internalType":"uint16","name":"adjustmentVelocity","type":"uint16"},{"internalType":"uint16","name":"decayPerAdjustment","type":"uint16"},{"internalType":"uint80","name":"vertexMultiplierMax","type":"uint80"},{"internalType":"address","name":"linkedToken","type":"address"}],"indexed":false,"internalType":"struct DynamicIRM.RatesConfig","name":"config","type":"tuple"}],"name":"NewIRM","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"borrowableCToken","type":"address"}],"name":"TokenLinked","type":"event"},{"inputs":[],"name":"ADJUSTMENT_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"name":"adjustedBorrowRate","outputs":[{"internalType":"uint256","name":"ratePerSecond","type":"uint256"},{"internalType":"uint256","name":"adjustmentRate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"name":"borrowRate","outputs":[{"internalType":"uint256","name":"r","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"centralRegistry","outputs":[{"internalType":"contract ICentralRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"linkedToken","outputs":[{"internalType":"address","name":"result","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"name":"predictedBorrowRate","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ratesConfig","outputs":[{"internalType":"uint64","name":"baseRatePerSecond","type":"uint64"},{"internalType":"uint64","name":"vertexRatePerSecond","type":"uint64"},{"internalType":"uint64","name":"vertexStart","type":"uint64"},{"internalType":"uint24","name":"increaseThresholdStart","type":"uint24"},{"internalType":"uint24","name":"decreaseThresholdEnd","type":"uint24"},{"internalType":"uint16","name":"adjustmentVelocity","type":"uint16"},{"internalType":"uint16","name":"decayPerAdjustment","type":"uint16"},{"internalType":"uint80","name":"vertexMultiplierMax","type":"uint80"},{"internalType":"address","name":"linkedToken","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"cTokenAddress","type":"address"}],"name":"setLinkedToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"},{"internalType":"uint256","name":"interestFee","type":"uint256"}],"name":"supplyRate","outputs":[{"internalType":"uint256","name":"r","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"result","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseRatePerYear","type":"uint256"},{"internalType":"uint256","name":"vertexRatePerYear","type":"uint256"},{"internalType":"uint256","name":"vertexStart","type":"uint256"},{"internalType":"uint256","name":"adjustmentVelocity","type":"uint256"},{"internalType":"uint256","name":"decayPerAdjustment","type":"uint256"},{"internalType":"uint256","name":"vertexMultiplierMax","type":"uint256"},{"internalType":"bool","name":"vertexReset","type":"bool"}],"name":"updateDynamicIRM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetsHeld","type":"uint256"},{"internalType":"uint256","name":"debt","type":"uint256"}],"name":"utilizationRate","outputs":[{"internalType":"uint256","name":"r","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"vertexMultiplier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
60a060405234801561000f575f5ffd5b506040516119df3803806119df83398101604081905261002e9161055e565b61003787610060565b6001600160a01b0387166080526100548686868686866001610091565b50505050505050610631565b610071816399011ef160e01b610439565b61008e576040516369b5e45b60e11b815260040160405180910390fd5b50565b61009a8761045d565b96506100a58661045d565b95506100b08561045d565b94506100bb8261045d565b9150670dbd2fc137a300008511806100da57506706f05b59d3b2000085105b156100f8576040516348436e1760e11b815260040160405180910390fd5b6714d1120d7b1600008711806101155750671bc16d674ec8000086115b15610133576040516332defd0160e01b815260040160405180910390fd5b60c88311156101555760405163c709b1f760e01b815260040160405180910390fd5b6107d08411806101655750606484105b1561018357604051635d3a72f960e01b815260040160405180910390fd5b6802b5e3af16b18800008211806101a15750670de0b6b3a764000082105b156101bf5760405163872fb21d60e01b815260040160405180910390fd5b60016101e188670de0b6b3a76400006101dc896301e133806105d5565b61046e565b81546001600160401b0319166001600160401b039190911617815561022187670de0b6b3a764000061021389826105ec565b6101dc906301e133806105d5565b8154600160401b600160c01b031916680100000000000000006001600160401b0392831602600160801b600160c01b03191617600160801b91881691909102176001600160f01b0316600160f01b61ffff878116919091029190911782556001820180549186166001600160601b031990921691909117620100006001600160501b0386160217905581156102c057670de0b6b3a76400005f556102d1565b5f54808410156102cf575f8490555b505b5f60026102e688670de0b6b3a76400006105ec565b6102f091906105ff565b90506509184e72a000610303828961061e565b61030d91906105ff565b825462ffffff91909116600160c01b0262ffffff60c01b199091161782556509184e72a00061033c82896105ec565b61034691906105ff565b825462ffffff91909116600160d81b0262ffffff60d81b199091161782556040517f6569529c6bf3ee50edf766abe0c799ad401376f650fa7a725b452bcbcbc91db69061042690849081546001600160401b03811682526101208201906001600160401b03604082901c1660208401526001600160401b03608082901c16604084015260c081901c62ffffff16606084015260d881901c62ffffff16608084015260f081901c60a084015250600183015461ffff811660c08401526001600160501b03601082901c1660e0840152606081901c6101008401525092915050565b60405180910390a1505050505050505050565b5f61044383610482565b8015610454575061045483836104b4565b90505b92915050565b5f61045782655af3107a40006105d5565b5f61047a84848461053a565b949350505050565b5f610494826301ffc9a760e01b6104b4565b801561045757506104ad826001600160e01b03196104b4565b1592915050565b6040516001600160e01b0319821660248201525f90819060440160408051601f19818403018152919052602080820180516001600160e01b03166301ffc9a760e01b17815282519293505f9283928392909183918a617530fa92503d91505f519050828015610524575060208210155b801561052f57505f81115b979650505050505050565b5f825f1904841183021582026105575763ad251c275f526004601cfd5b5091020490565b5f5f5f5f5f5f5f60e0888a031215610574575f5ffd5b87516001600160a01b038116811461058a575f5ffd5b602089015160408a015160608b015160808c015160a08d015160c0909d0151949e939d50919b909a50909850965090945092505050565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610457576104576105c1565b81810381811115610457576104576105c1565b5f8261061957634e487b7160e01b5f52601260045260245ffd5b500490565b80820180821115610457576104576105c1565b6080516113886106575f395f8181610158015281816103400152610c5601526113885ff3fe608060405234801561000f575f5ffd5b50600436106100cb575f3560e01c8063ad58472611610088578063e46023e211610063578063e46023e2146101cb578063ea12489f146101de578063ec2de40c146101f6578063f3129fac14610209575f5ffd5b8063ad58472614610192578063c0b36044146101a5578063da6008f4146101b8575f5ffd5b806301ffc9a7146100cf5780630e53bfc4146100f75780632c2650721461010d578063589dc77b1461012257806367e860cd1461014a5780638f73dcfa14610153575b5f5ffd5b6100e26100dd3660046111a2565b6102f5565b60405190151581526020015b60405180910390f35b6100ff5f5481565b6040519081526020016100ee565b61012061011b3660046111e0565b61032b565b005b6101356101303660046111fb565b610574565b604080519283526020830191909152016100ee565b6100ff61025881565b61017a7f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016100ee565b6100ff6101a03660046111fb565b61072e565b6100ff6101b336600461121b565b6107a9565b6100ff6101c63660046111fb565b6107f3565b6101206101d9366004611251565b61095f565b600254600160601b90046001600160a01b031661017a565b6100ff6102043660046111fb565b6109e8565b600154600254610283916001600160401b0380821692600160401b8304821692600160801b81049092169162ffffff600160c01b8204811692600160d81b83049091169161ffff600160f01b909104811691908116906001600160501b0362010000820416906001600160a01b03600160601b9091041689565b604080516001600160401b039a8b168152988a1660208a0152969098169587019590955262ffffff938416606087015291909216608085015261ffff91821660a08501521660c08301526001600160501b031660e08201526001600160a01b03909116610100820152610120016100ee565b5f6001600160e01b03198216638ec124b360e01b148061032557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6040516355eaece960e11b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063abd5d9d290602401602060405180830381865afa15801561038d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103b191906112ac565b6103ce5760405163ce8f8fcb60e01b815260040160405180910390fd5b600254600190600160601b90046001600160a01b0316156104025760405163ce8f8fcb60e01b815260040160405180910390fd5b816001600160a01b03166345d7b97a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561043e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061046291906112ac565b61047f57604051630b613edd60e21b815260040160405180910390fd5b306001600160a01b0316826001600160a01b0316631e75db166040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104c5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104e991906112c7565b6001600160a01b03161461051057604051630b613edd60e21b815260040160405180910390fd5b6001810180546bffffffffffffffffffffffff16600160601b6001600160a01b038516908102919091179091556040519081527fcaec6e139653a20e6ea7c3862d384166d94b04bd44cd59e9357b8ac1c28182fc9060200160405180910390a15050565b60408051610120810182526001546001600160401b038082168352600160401b820481166020840152600160801b82041692820192909252600160c01b820462ffffff9081166060830152600160d81b8304166080820152600160f01b90910461ffff90811660a083015260025490811660c08301526201000081046001600160501b031660e0830152600160601b90046001600160a01b031661010082018190525f91829133146106395760405163ce8f8fcb60e01b815260040160405180910390fd5b5f61064486866109e8565b60408301515f549192506001600160401b03169081831180159061067f5761067884865f01516001600160401b0316610a19565b96506106a8565b6106a584865f01516001600160401b031687602001516001600160401b03168686610a2d565b96505b670de0b6b3a7640000821480156106d7575060608501516106d49062ffffff166509184e72a0006112f6565b84105b156106ec575061025894506107279350505050565b801561070f576106fd858584610a77565b5f555061025894506107279350505050565b61071a858584610b68565b5f55506102589450505050505b9250929050565b5f5f61073a84846109e8565b6001805491925090600160801b90046001600160401b031680831161077957815461076f9084906001600160401b0316610a19565b9350505050610325565b81545f5461079f9185916001600160401b0380831692600160401b900416908590610a2d565b9695505050505050565b5f5f6107cc6107b8868661072e565b6107c48561271061130d565b612710610c2d565b90506107ea6107db86866109e8565b82670de0b6b3a7640000610c2d565b95945050505050565b5f5f6107ff84846109e8565b60408051610120810182526001546001600160401b038082168352600160401b820481166020840152600160801b82041692820183905262ffffff600160c01b820481166060840152600160d81b820416608083015261ffff600160f01b909104811660a083015260025490811660c08301526001600160501b036201000082041660e08301526001600160a01b03600160601b909104166101008201529192508083116108bd5761076f83835f01516001600160401b0316610a19565b5f54670de0b6b3a7640000811480156108ee575060608301516108eb9062ffffff166509184e72a0006112f6565b84105b156109245761091984845f01516001600160401b031685602001516001600160401b03168585610a2d565b945050505050610325565b61095484845f01516001600160401b031685602001516001600160401b03168561094f888a88610b68565b610a2d565b979650505050505050565b610967610c41565b600254600160601b90046001600160a01b031680156109cf57806001600160a01b031663775a814a6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156109b8575f5ffd5b505af11580156109ca573d5f5f3e3d5ffd5b505050505b6109de88888888888888610ce6565b5050505050505050565b5f8115610a1057610a0b82670de0b6b3a7640000610a068287611320565b610c2d565b610a12565b5f5b9392505050565b5f610a128383670de0b6b3a7640000610c2d565b5f610a5a610a3b848861130d565b610a4584876112f6565b6ec097ce7bc90715b34b9f1000000000610c2d565b610a6d8487670de0b6b3a7640000610c2d565b61079f9190611320565b5f5f610a8e838660c0015161ffff16612710610c2d565b9050846080015162ffffff166509184e72a000610aab91906112f6565b8411610b025780610acf846127108860a0015161ffff16612710610a069190611320565b610ad9919061130d565b9150670de0b6b3a76400008210610af05781610afa565b670de0b6b3a76400005b915050610a12565b610b3f838660a0015161ffff16838789604001516001600160401b03168a6080015162ffffff166509184e72a000610b3a91906112f6565b611099565b9150670de0b6b3a76400008210610b5657816107ea565b50670de0b6b3a7640000949350505050565b5f5f610b7f838660c0015161ffff16612710610c2d565b9050846060015162ffffff166509184e72a000610b9c91906112f6565b8411610bac57610ad9818461130d565b610bdb838660a0015161ffff168387896060015162ffffff166509184e72a000610bd691906112f6565b611109565b9150670de0b6b3a7640000821015610bfe57670de0b6b3a7640000915050610a12565b8460e001516001600160501b03168210610c25578460e001516001600160501b03166107ea565b509392505050565b5f610c3984848461116d565b949350505050565b604051630225e24360e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630225e24390602401602060405180830381865afa158015610ca3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cc791906112ac565b610ce45760405163ce8f8fcb60e01b815260040160405180910390fd5b565b610cef87611191565b9650610cfa86611191565b9550610d0585611191565b9450610d1082611191565b9150670dbd2fc137a30000851180610d2f57506706f05b59d3b2000085105b15610d4d576040516348436e1760e11b815260040160405180910390fd5b6714d1120d7b160000871180610d6a5750671bc16d674ec8000086115b15610d88576040516332defd0160e01b815260040160405180910390fd5b60c8831115610daa5760405163c709b1f760e01b815260040160405180910390fd5b6107d0841180610dba5750606484105b15610dd857604051635d3a72f960e01b815260040160405180910390fd5b6802b5e3af16b1880000821180610df65750670de0b6b3a764000082105b15610e145760405163872fb21d60e01b815260040160405180910390fd5b6001610e3188670de0b6b3a7640000610a06896301e133806112f6565b815467ffffffffffffffff19166001600160401b0391909116178155610e7287670de0b6b3a7640000610e64898261130d565b610a06906301e133806112f6565b815477ffffffffffffffffffffffffffffffff00000000000000001916600160401b6001600160401b039283160267ffffffffffffffff60801b191617600160801b91881691909102176001600160f01b0316600160f01b61ffff878116919091029190911782556001820180549186166bffffffffffffffffffffffff1990921691909117620100006001600160501b038616021790558115610f2057670de0b6b3a76400005f55610f31565b5f5480841015610f2f575f8490555b505b5f6002610f4688670de0b6b3a764000061130d565b610f509190611333565b90506509184e72a000610f638289611320565b610f6d9190611333565b825462ffffff91909116600160c01b0262ffffff60c01b199091161782556509184e72a000610f9c828961130d565b610fa69190611333565b825462ffffff91909116600160d81b0262ffffff60d81b199091161782556040517f6569529c6bf3ee50edf766abe0c799ad401376f650fa7a725b452bcbcbc91db69061108690849081546001600160401b0381168252610120820190604081901c6001600160401b03166020840152608081901c6001600160401b0316604084015260c081901c62ffffff16606084015260d881901c62ffffff16608084015260f081901c60a084015250600183015461ffff811660c0840152601081901c6001600160501b031660e0840152606081901c6101008401525092915050565b60405180910390a1505050505050505050565b5f806110bb6110a8868661130d565b670de0b6b3a7640000610a06868861130d565b90506110c787826112f6565b6110db9069021e19e0c9bab2400000611320565b9050856110f38969021e19e0c9bab240000084610c2d565b6110fd919061130d565b98975050505050505050565b5f8061112b611118848661130d565b670de0b6b3a7640000610a06868261130d565b905061113786826112f6565b61114b9069021e19e0c9bab2400000611320565b905084611163888369021e19e0c9bab2400000610c2d565b610954919061130d565b5f825f19048411830215820261118a5763ad251c275f526004601cfd5b5091020490565b5f61032582655af3107a40006112f6565b5f602082840312156111b2575f5ffd5b81356001600160e01b031981168114610a12575f5ffd5b6001600160a01b03811681146111dd575f5ffd5b50565b5f602082840312156111f0575f5ffd5b8135610a12816111c9565b5f5f6040838503121561120c575f5ffd5b50508035926020909101359150565b5f5f5f6060848603121561122d575f5ffd5b505081359360208301359350604090920135919050565b80151581146111dd575f5ffd5b5f5f5f5f5f5f5f60e0888a031215611267575f5ffd5b873596506020880135955060408801359450606088013593506080880135925060a0880135915060c088013561129c81611244565b8091505092959891949750929550565b5f602082840312156112bc575f5ffd5b8151610a1281611244565b5f602082840312156112d7575f5ffd5b8151610a12816111c9565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610325576103256112e2565b81810381811115610325576103256112e2565b80820180821115610325576103256112e2565b5f8261134d57634e487b7160e01b5f52601260045260245ffd5b50049056fea26469706673582212201c58c4f2a95bab6e3826f528a8fd40d37a3abe92ffdd0e7a48b3c6163038d86a64736f6c634300081c00330000000000000000000000001310f352f1389969ece6741671c4b919523912ff00000000000000000000000000000000000000000000000000000000000005780000000000000000000000000000000000000000000000000000000000000834000000000000000000000000000000000000000000000000000000000000232800000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000186a0
Deployed Bytecode
0x608060405234801561000f575f5ffd5b50600436106100cb575f3560e01c8063ad58472611610088578063e46023e211610063578063e46023e2146101cb578063ea12489f146101de578063ec2de40c146101f6578063f3129fac14610209575f5ffd5b8063ad58472614610192578063c0b36044146101a5578063da6008f4146101b8575f5ffd5b806301ffc9a7146100cf5780630e53bfc4146100f75780632c2650721461010d578063589dc77b1461012257806367e860cd1461014a5780638f73dcfa14610153575b5f5ffd5b6100e26100dd3660046111a2565b6102f5565b60405190151581526020015b60405180910390f35b6100ff5f5481565b6040519081526020016100ee565b61012061011b3660046111e0565b61032b565b005b6101356101303660046111fb565b610574565b604080519283526020830191909152016100ee565b6100ff61025881565b61017a7f0000000000000000000000001310f352f1389969ece6741671c4b919523912ff81565b6040516001600160a01b0390911681526020016100ee565b6100ff6101a03660046111fb565b61072e565b6100ff6101b336600461121b565b6107a9565b6100ff6101c63660046111fb565b6107f3565b6101206101d9366004611251565b61095f565b600254600160601b90046001600160a01b031661017a565b6100ff6102043660046111fb565b6109e8565b600154600254610283916001600160401b0380821692600160401b8304821692600160801b81049092169162ffffff600160c01b8204811692600160d81b83049091169161ffff600160f01b909104811691908116906001600160501b0362010000820416906001600160a01b03600160601b9091041689565b604080516001600160401b039a8b168152988a1660208a0152969098169587019590955262ffffff938416606087015291909216608085015261ffff91821660a08501521660c08301526001600160501b031660e08201526001600160a01b03909116610100820152610120016100ee565b5f6001600160e01b03198216638ec124b360e01b148061032557506301ffc9a760e01b6001600160e01b03198316145b92915050565b6040516355eaece960e11b81523360048201527f0000000000000000000000001310f352f1389969ece6741671c4b919523912ff6001600160a01b03169063abd5d9d290602401602060405180830381865afa15801561038d573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103b191906112ac565b6103ce5760405163ce8f8fcb60e01b815260040160405180910390fd5b600254600190600160601b90046001600160a01b0316156104025760405163ce8f8fcb60e01b815260040160405180910390fd5b816001600160a01b03166345d7b97a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561043e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061046291906112ac565b61047f57604051630b613edd60e21b815260040160405180910390fd5b306001600160a01b0316826001600160a01b0316631e75db166040518163ffffffff1660e01b8152600401602060405180830381865afa1580156104c5573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104e991906112c7565b6001600160a01b03161461051057604051630b613edd60e21b815260040160405180910390fd5b6001810180546bffffffffffffffffffffffff16600160601b6001600160a01b038516908102919091179091556040519081527fcaec6e139653a20e6ea7c3862d384166d94b04bd44cd59e9357b8ac1c28182fc9060200160405180910390a15050565b60408051610120810182526001546001600160401b038082168352600160401b820481166020840152600160801b82041692820192909252600160c01b820462ffffff9081166060830152600160d81b8304166080820152600160f01b90910461ffff90811660a083015260025490811660c08301526201000081046001600160501b031660e0830152600160601b90046001600160a01b031661010082018190525f91829133146106395760405163ce8f8fcb60e01b815260040160405180910390fd5b5f61064486866109e8565b60408301515f549192506001600160401b03169081831180159061067f5761067884865f01516001600160401b0316610a19565b96506106a8565b6106a584865f01516001600160401b031687602001516001600160401b03168686610a2d565b96505b670de0b6b3a7640000821480156106d7575060608501516106d49062ffffff166509184e72a0006112f6565b84105b156106ec575061025894506107279350505050565b801561070f576106fd858584610a77565b5f555061025894506107279350505050565b61071a858584610b68565b5f55506102589450505050505b9250929050565b5f5f61073a84846109e8565b6001805491925090600160801b90046001600160401b031680831161077957815461076f9084906001600160401b0316610a19565b9350505050610325565b81545f5461079f9185916001600160401b0380831692600160401b900416908590610a2d565b9695505050505050565b5f5f6107cc6107b8868661072e565b6107c48561271061130d565b612710610c2d565b90506107ea6107db86866109e8565b82670de0b6b3a7640000610c2d565b95945050505050565b5f5f6107ff84846109e8565b60408051610120810182526001546001600160401b038082168352600160401b820481166020840152600160801b82041692820183905262ffffff600160c01b820481166060840152600160d81b820416608083015261ffff600160f01b909104811660a083015260025490811660c08301526001600160501b036201000082041660e08301526001600160a01b03600160601b909104166101008201529192508083116108bd5761076f83835f01516001600160401b0316610a19565b5f54670de0b6b3a7640000811480156108ee575060608301516108eb9062ffffff166509184e72a0006112f6565b84105b156109245761091984845f01516001600160401b031685602001516001600160401b03168585610a2d565b945050505050610325565b61095484845f01516001600160401b031685602001516001600160401b03168561094f888a88610b68565b610a2d565b979650505050505050565b610967610c41565b600254600160601b90046001600160a01b031680156109cf57806001600160a01b031663775a814a6040518163ffffffff1660e01b81526004015f604051808303815f87803b1580156109b8575f5ffd5b505af11580156109ca573d5f5f3e3d5ffd5b505050505b6109de88888888888888610ce6565b5050505050505050565b5f8115610a1057610a0b82670de0b6b3a7640000610a068287611320565b610c2d565b610a12565b5f5b9392505050565b5f610a128383670de0b6b3a7640000610c2d565b5f610a5a610a3b848861130d565b610a4584876112f6565b6ec097ce7bc90715b34b9f1000000000610c2d565b610a6d8487670de0b6b3a7640000610c2d565b61079f9190611320565b5f5f610a8e838660c0015161ffff16612710610c2d565b9050846080015162ffffff166509184e72a000610aab91906112f6565b8411610b025780610acf846127108860a0015161ffff16612710610a069190611320565b610ad9919061130d565b9150670de0b6b3a76400008210610af05781610afa565b670de0b6b3a76400005b915050610a12565b610b3f838660a0015161ffff16838789604001516001600160401b03168a6080015162ffffff166509184e72a000610b3a91906112f6565b611099565b9150670de0b6b3a76400008210610b5657816107ea565b50670de0b6b3a7640000949350505050565b5f5f610b7f838660c0015161ffff16612710610c2d565b9050846060015162ffffff166509184e72a000610b9c91906112f6565b8411610bac57610ad9818461130d565b610bdb838660a0015161ffff168387896060015162ffffff166509184e72a000610bd691906112f6565b611109565b9150670de0b6b3a7640000821015610bfe57670de0b6b3a7640000915050610a12565b8460e001516001600160501b03168210610c25578460e001516001600160501b03166107ea565b509392505050565b5f610c3984848461116d565b949350505050565b604051630225e24360e01b81523360048201527f0000000000000000000000001310f352f1389969ece6741671c4b919523912ff6001600160a01b031690630225e24390602401602060405180830381865afa158015610ca3573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610cc791906112ac565b610ce45760405163ce8f8fcb60e01b815260040160405180910390fd5b565b610cef87611191565b9650610cfa86611191565b9550610d0585611191565b9450610d1082611191565b9150670dbd2fc137a30000851180610d2f57506706f05b59d3b2000085105b15610d4d576040516348436e1760e11b815260040160405180910390fd5b6714d1120d7b160000871180610d6a5750671bc16d674ec8000086115b15610d88576040516332defd0160e01b815260040160405180910390fd5b60c8831115610daa5760405163c709b1f760e01b815260040160405180910390fd5b6107d0841180610dba5750606484105b15610dd857604051635d3a72f960e01b815260040160405180910390fd5b6802b5e3af16b1880000821180610df65750670de0b6b3a764000082105b15610e145760405163872fb21d60e01b815260040160405180910390fd5b6001610e3188670de0b6b3a7640000610a06896301e133806112f6565b815467ffffffffffffffff19166001600160401b0391909116178155610e7287670de0b6b3a7640000610e64898261130d565b610a06906301e133806112f6565b815477ffffffffffffffffffffffffffffffff00000000000000001916600160401b6001600160401b039283160267ffffffffffffffff60801b191617600160801b91881691909102176001600160f01b0316600160f01b61ffff878116919091029190911782556001820180549186166bffffffffffffffffffffffff1990921691909117620100006001600160501b038616021790558115610f2057670de0b6b3a76400005f55610f31565b5f5480841015610f2f575f8490555b505b5f6002610f4688670de0b6b3a764000061130d565b610f509190611333565b90506509184e72a000610f638289611320565b610f6d9190611333565b825462ffffff91909116600160c01b0262ffffff60c01b199091161782556509184e72a000610f9c828961130d565b610fa69190611333565b825462ffffff91909116600160d81b0262ffffff60d81b199091161782556040517f6569529c6bf3ee50edf766abe0c799ad401376f650fa7a725b452bcbcbc91db69061108690849081546001600160401b0381168252610120820190604081901c6001600160401b03166020840152608081901c6001600160401b0316604084015260c081901c62ffffff16606084015260d881901c62ffffff16608084015260f081901c60a084015250600183015461ffff811660c0840152601081901c6001600160501b031660e0840152606081901c6101008401525092915050565b60405180910390a1505050505050505050565b5f806110bb6110a8868661130d565b670de0b6b3a7640000610a06868861130d565b90506110c787826112f6565b6110db9069021e19e0c9bab2400000611320565b9050856110f38969021e19e0c9bab240000084610c2d565b6110fd919061130d565b98975050505050505050565b5f8061112b611118848661130d565b670de0b6b3a7640000610a06868261130d565b905061113786826112f6565b61114b9069021e19e0c9bab2400000611320565b905084611163888369021e19e0c9bab2400000610c2d565b610954919061130d565b5f825f19048411830215820261118a5763ad251c275f526004601cfd5b5091020490565b5f61032582655af3107a40006112f6565b5f602082840312156111b2575f5ffd5b81356001600160e01b031981168114610a12575f5ffd5b6001600160a01b03811681146111dd575f5ffd5b50565b5f602082840312156111f0575f5ffd5b8135610a12816111c9565b5f5f6040838503121561120c575f5ffd5b50508035926020909101359150565b5f5f5f6060848603121561122d575f5ffd5b505081359360208301359350604090920135919050565b80151581146111dd575f5ffd5b5f5f5f5f5f5f5f60e0888a031215611267575f5ffd5b873596506020880135955060408801359450606088013593506080880135925060a0880135915060c088013561129c81611244565b8091505092959891949750929550565b5f602082840312156112bc575f5ffd5b8151610a1281611244565b5f602082840312156112d7575f5ffd5b8151610a12816111c9565b634e487b7160e01b5f52601160045260245ffd5b8082028115828204841417610325576103256112e2565b81810381811115610325576103256112e2565b80820180821115610325576103256112e2565b5f8261134d57634e487b7160e01b5f52601260045260245ffd5b50049056fea26469706673582212201c58c4f2a95bab6e3826f528a8fd40d37a3abe92ffdd0e7a48b3c6163038d86a64736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000001310f352f1389969ece6741671c4b919523912ff00000000000000000000000000000000000000000000000000000000000005780000000000000000000000000000000000000000000000000000000000000834000000000000000000000000000000000000000000000000000000000000232800000000000000000000000000000000000000000000000000000000000001f400000000000000000000000000000000000000000000000000000000000000c800000000000000000000000000000000000000000000000000000000000186a0
-----Decoded View---------------
Arg [0] : cr (address): 0x1310f352f1389969Ece6741671c4B919523912fF
Arg [1] : baseRatePerYear (uint256): 1400
Arg [2] : vertexRatePerYear (uint256): 2100
Arg [3] : vertexStart (uint256): 9000
Arg [4] : adjustmentVelocity (uint256): 500
Arg [5] : decayPerAdjustment (uint256): 200
Arg [6] : vertexMultiplierMax (uint256): 100000
-----Encoded View---------------
7 Constructor Arguments found :
Arg [0] : 0000000000000000000000001310f352f1389969ece6741671c4b919523912ff
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000578
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000834
Arg [3] : 0000000000000000000000000000000000000000000000000000000000002328
Arg [4] : 00000000000000000000000000000000000000000000000000000000000001f4
Arg [5] : 00000000000000000000000000000000000000000000000000000000000000c8
Arg [6] : 00000000000000000000000000000000000000000000000000000000000186a0
Deployed Bytecode Sourcemap
129529:39656:0:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;149280:231;;;;;;:::i;:::-;;:::i;:::-;;;470:14:1;;463:22;445:41;;433:2;418:18;149280:231:0;;;;;;;;135433:31;;;;;;;;;643:25:1;;;631:2;616:18;135433:31:0;497:177:1;138659:1116:0;;;;;;:::i;:::-;;:::i;:::-;;142578:1922;;;;;;:::i;:::-;;:::i;:::-;;;;1592:25:1;;;1648:2;1633:18;;1626:34;;;;1565:18;142578:1922:0;1418:248:1;132215:52:0;;132257:10;132215:52;;132315:49;;;;;;;;-1:-1:-1;;;;;1859:32:1;;;1841:51;;1829:2;1814:18;132315:49:0;1671:227:1;146793:642:0;;;;;;:::i;:::-;;:::i;147984:593::-;;;;;;:::i;:::-;;:::i;144877:1494::-;;;;;;:::i;:::-;;:::i;140955:756::-;;;;;;:::i;:::-;;:::i;138148:112::-;138229:23;;-1:-1:-1;;;138229:23:0;;-1:-1:-1;;;;;138229:23:0;138148:112;;148856:262;;;;;;:::i;:::-;;:::i;135589:30::-;;;;;;;-1:-1:-1;;;;;135589:30:0;;;;-1:-1:-1;;;135589:30:0;;;;;-1:-1:-1;;;135589:30:0;;;;;;;-1:-1:-1;;;135589:30:0;;;;;-1:-1:-1;;;135589:30:0;;;;;;;-1:-1:-1;;;135589:30:0;;;;;;;;;;-1:-1:-1;;;;;135589:30:0;;;;;-1:-1:-1;;;;;;;;135589:30:0;;;;;;;;;;-1:-1:-1;;;;;4567:31:1;;;4549:50;;4635:31;;;4630:2;4615:18;;4608:59;4703:31;;;;4683:18;;;4676:59;;;;4783:8;4771:21;;;4766:2;4751:18;;4744:49;4830:21;;;;4824:3;4809:19;;4802:50;4901:6;4889:19;;;4883:3;4868:19;;4861:48;4946:19;4940:3;4925:19;;4918:48;-1:-1:-1;;;;;5003:35:1;4997:3;4982:19;;4975:64;-1:-1:-1;;;;;5076:32:1;;;5070:3;5055:19;;5048:61;4536:3;4521:19;135589:30:0;4194:921:1;149280:231:0;149373:11;-1:-1:-1;;;;;;149406:44:0;;-1:-1:-1;;;149406:44:0;;:97;;-1:-1:-1;;;;;;;;;;69699:40:0;;;149467:36;149397:106;149280:231;-1:-1:-1;;149280:231:0:o;138659:1116::-;138731:45;;-1:-1:-1;;;138731:45:0;;138765:10;138731:45;;;1841:51:1;138731:15:0;-1:-1:-1;;;;;138731:33:0;;;;1814:18:1;;138731:45:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;138726:112;;138800:26;;-1:-1:-1;;;138800:26:0;;;;;;;;;;;138726:112;139020:13;;138874:11;;-1:-1:-1;;;139020:13:0;;-1:-1:-1;;;;;139020:13:0;:27;139016:93;;139071:26;;-1:-1:-1;;;139071:26:0;;;;;;;;;;;139016:93;139334:13;-1:-1:-1;;;;;139316:45:0;;:47;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;139311:114;;139387:26;;-1:-1:-1;;;139387:26:0;;;;;;;;;;;139311:114;139615:4;-1:-1:-1;;;;;139556:64:0;139582:13;-1:-1:-1;;;;;139564:36:0;;:38;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;139556:64:0;;139552:130;;139644:26;;-1:-1:-1;;;139644:26:0;;;;;;;;;;;139552:130;139694:13;;;:29;;;;-1:-1:-1;;;;;;;;139694:29:0;;;;;;;;;;;;139741:26;;1841:51:1;;;139741:26:0;;1829:2:1;1814:18;139741:26:0;;;;;;;138715:1060;138659:1116;:::o;142578:1922::-;142741:34;;;;;;;;142764:11;142741:34;-1:-1:-1;;;;;142741:34:0;;;;;-1:-1:-1;;;142741:34:0;;;;;;;;-1:-1:-1;;;142741:34:0;;;;;;;;;;-1:-1:-1;;;142741:34:0;;;;;;;;;;-1:-1:-1;;;142741:34:0;;;;;;;-1:-1:-1;;;142741:34:0;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;142741:34:0;;;;;-1:-1:-1;;;142741:34:0;;-1:-1:-1;;;;;142741:34:0;;;;;;;-1:-1:-1;;;;142897:10:0;:27;142893:93;;142948:26;;-1:-1:-1;;;142948:26:0;;;;;;;;;;;142893:93;142998:12;143013:33;143029:10;143041:4;143013:15;:33::i;:::-;143079:13;;;;143057:19;143124:16;142998:48;;-1:-1:-1;;;;;;143057:35:0;;143171:19;;;;;;143252:331;;143300:36;143310:4;143316:1;:19;;;-1:-1:-1;;;;;143300:36:0;:9;:36::i;:::-;143284:52;;143252:331;;;143385:186;143415:4;143438:1;:19;;;-1:-1:-1;;;;;143385:186:0;143476:1;:21;;;-1:-1:-1;;;;;143385:186:0;143516:11;143546:10;143385:11;:186::i;:::-;143369:202;;143252:331;35282:4;143855:10;:17;:103;;;;-1:-1:-1;143932:24:0;;;;143897:60;;143924:33;;135189:4;143897:60;:::i;:::-;143889:4;:69;143855:103;143837:199;;;-1:-1:-1;132257:10:0;;-1:-1:-1;143985:39:0;;-1:-1:-1;;;;143985:39:0;143837:199;144169:11;144165:156;;;144216:39;144235:1;144238:4;144244:10;144216:18;:39::i;:::-;144197:16;:58;-1:-1:-1;132257:10:0;;-1:-1:-1;144270:39:0;;-1:-1:-1;;;;144270:39:0;144165:156;144410:39;144429:1;144432:4;144438:10;144410:18;:39::i;:::-;144391:16;:58;-1:-1:-1;132257:10:0;;-1:-1:-1;;;;;142578:1922:0;;;;;;:::o;146793:642::-;146893:9;146915:12;146930:33;146946:10;146958:4;146930:15;:33::i;:::-;147078:11;147122:13;;146915:48;;-1:-1:-1;147078:11:0;-1:-1:-1;;;147122:13:0;;-1:-1:-1;;;;;147122:13:0;147152:19;;;147148:95;;147211:19;;147195:36;;147205:4;;-1:-1:-1;;;;;147211:19:0;147195:9;:36::i;:::-;147188:43;;;;;;;147148:95;147304:19;;;147400:16;147259:168;;147285:4;;-1:-1:-1;;;;;147304:19:0;;;;-1:-1:-1;;;147338:21:0;;;;147374:11;;147259;:168::i;:::-;147255:172;146793:642;-1:-1:-1;;;;;;146793:642:0:o;147984:593::-;148114:9;148213:29;148246:111;148268:28;148279:10;148291:4;148268:10;:28::i;:::-;148311:17;148317:11;35391:3;148311:17;:::i;:::-;35391:3;148246:7;:111::i;:::-;148213:144;;148449:120;148471:33;148487:10;148499:4;148471:15;:33::i;:::-;148519:21;35282:4;148449:7;:120::i;:::-;148445:124;147984:593;-1:-1:-1;;;;;147984:593:0:o;144877:1494::-;144986:14;145013:12;145028:33;145044:10;145056:4;145028:15;:33::i;:::-;145072:34;;;;;;;;145095:11;145072:34;-1:-1:-1;;;;;145072:34:0;;;;;-1:-1:-1;;;145072:34:0;;;;;;;;-1:-1:-1;;;145072:34:0;;;;;;;;;;-1:-1:-1;;;145072:34:0;;;;;;;;-1:-1:-1;;;145072:34:0;;;;;;;;-1:-1:-1;;;145072:34:0;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;145072:34:0;;;;;;;;-1:-1:-1;;;;;;;;145072:34:0;;;;;;;;145013:48;;-1:-1:-1;145286:19:0;;;145282:95;;145329:36;145339:4;145345:1;:19;;;-1:-1:-1;;;;;145329:36:0;:9;:36::i;145282:95::-;145389:18;145410:16;35282:4;145710:17;;:103;;;;-1:-1:-1;145787:24:0;;;;145752:60;;145779:33;;135189:4;145752:60;:::i;:::-;145744:4;:69;145710:103;145692:353;;;145847:186;145877:4;145900:1;:19;;;-1:-1:-1;;;;;145847:186:0;145938:1;:21;;;-1:-1:-1;;;;;145847:186:0;145978:11;146008:10;145847:11;:186::i;:::-;145840:193;;;;;;;;145692:353;146172:191;146198:4;146217:1;:19;;;-1:-1:-1;;;;;146172:191:0;146251:1;:21;;;-1:-1:-1;;;;;146172:191:0;146287:11;146313:39;146332:1;146335:4;146341:10;146313:18;:39::i;:::-;146172:11;:191::i;:::-;146163:200;144877:1494;-1:-1:-1;;;;;;;144877:1494:0:o;140955:756::-;141247:25;:23;:25::i;:::-;141308:23;;-1:-1:-1;;;141308:23:0;;-1:-1:-1;;;;;141308:23:0;141346:26;;141342:107;;141407:12;-1:-1:-1;;;;;141389:46:0;;:48;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;141342:107;141461:242;141493:15;141523:17;141555:11;141581:18;141614;141647:19;141681:11;141461:17;:242::i;:::-;141236:475;140955:756;;;;;;;:::o;148856:262::-;148961:9;149057;;:53;;149073:37;149081:4;35282;149092:17;149081:4;149092:10;:17;:::i;:::-;149073:7;:37::i;:::-;149057:53;;;149069:1;149057:53;149053:57;148856:262;-1:-1:-1;;;148856:262:0:o;149761:179::-;149869:9;149895:37;149903:4;149909:17;35282:4;149895:7;:37::i;150613:425::-;150820:9;150906:124;150928:18;150935:11;150928:4;:18;:::i;:::-;150961:32;150983:10;150961:19;:32;:::i;:::-;34828:4;150906:7;:124::i;:::-;150846:44;150854:11;150867:17;35282:4;150846:7;:44::i;:::-;:184;;;;:::i;160930:1860::-;161071:21;161139:13;161155:46;161163:10;161175:1;:20;;;161155:46;;35391:3;161155:7;:46::i;:::-;161139:62;;161388:1;:22;;;161380:31;;135189:4;161353:58;;;;:::i;:::-;161344:4;:68;161340:623;;161819:5;161698:118;161724:10;35391:3;161781:1;:20;;;161775:26;;35391:3;161775:26;;;;:::i;161698:118::-;:126;;;;:::i;:::-;161682:142;;35282:4;161910:13;:19;:41;;161938:13;161910:41;;;35282:4;161910:41;161903:48;;;;;161340:623;162167:418;162196:10;162247:1;:20;;;162167:418;;162316:5;162374:4;162416:1;:13;;;-1:-1:-1;;;;;162167:418:0;162500:1;:22;;;162492:31;;135189:4;162465:58;;;;:::i;:::-;162167:14;:418::i;:::-;162151:434;;35282:4;162741:13;:19;:41;;162769:13;162741:41;;;-1:-1:-1;35282:4:0;;160930:1860;-1:-1:-1;;;;160930:1860:0:o;157868:1604::-;158009:21;158077:13;158093:46;158101:10;158113:1;:20;;;158093:46;;35391:3;158093:7;:46::i;:::-;158077:62;;158200:1;:24;;;158192:33;;135189:4;158165:60;;;;:::i;:::-;158156:4;:70;158152:262;;158259:18;158272:5;158259:10;:18;:::i;158152:262::-;158612:361;158641:10;158692:1;:20;;;158612:361;;158761:5;158819:4;158896:1;:24;;;158888:33;;135189:4;158861:60;;;;:::i;:::-;158612:14;:361::i;:::-;158596:377;;35282:4;159201:13;:19;159197:62;;;35282:4;159237:10;;;;;159197:62;159377:1;:21;;;-1:-1:-1;;;;;159361:37:0;:13;:37;:103;;159443:1;:21;;;-1:-1:-1;;;;;159361:103:0;;;;158032:1440;157868:1604;;;;;:::o;168337:174::-;168444:9;168470:33;168495:1;168498;168501;168470:24;:33::i;:::-;168466:37;168337:174;-1:-1:-1;;;;168337:174:0:o;168589:190::-;168662:48;;-1:-1:-1;;;168662:48:0;;168699:10;168662:48;;;1841:51:1;168662:15:0;-1:-1:-1;;;;;168662:36:0;;;;1814:18:1;;168662:48:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;168657:115;;168734:26;;-1:-1:-1;;;168734:26:0;;;;;;;;;;;168657:115;168589:190::o;152499:3951::-;153023:25;153032:15;153023:8;:25::i;:::-;153005:43;;153079:27;153088:17;153079:8;:27::i;:::-;153059:47;;153131:21;153140:11;153131:8;:21::i;:::-;153117:35;;153185:29;153194:19;153185:8;:29::i;:::-;153163:51;;133218:7;153313:11;:31;:79;;;;133468:6;153361:11;:31;153313:79;153295:180;;;153426:37;;-1:-1:-1;;;153426:37:0;;;;;;;;;;;153295:180;132669:6;153505:15;:50;:121;;;;132971:4;153572:17;:54;153505:121;153487:225;;;153660:40;;-1:-1:-1;;;153660:40:0;;;;;;;;;;;153487:225;134347:3;153728:18;:43;153724:113;;;153795:30;;-1:-1:-1;;;153795:30:0;;;;;;;;;;;153724:113;133803:4;153875:18;:52;:121;;;;134139:3;153944:18;:52;153875:121;153857:224;;;154030:39;;-1:-1:-1;;;154030:39:0;;;;;;;;;;;153857:224;134594:5;154111:19;:52;:121;;;;35282:4;154180:19;:52;154111:121;154093:219;;;154266:34;;-1:-1:-1;;;154266:34:0;;;;;;;;;;;154093:219;154353:11;154411:111;154433:15;35282:4;154481:30;154500:11;36008:10;154481:30;:::i;154411:111::-;154377:146;;-1:-1:-1;;154377:146:0;-1:-1:-1;;;;;154377:146:0;;;;;;;154572:121;154594:17;35282:4;154664:17;154670:11;35282:4;154664:17;:::i;:::-;154644:38;;36008:10;154644:38;:::i;154572:121::-;154536:158;;-1:-1:-1;;154707:40:0;-1:-1:-1;;;;;;;;154536:158:0;;;;-1:-1:-1;;;;154707:40:0;;-1:-1:-1;;;154707:40:0;;;;;;;;-1:-1:-1;;;;;154758:54:0;-1:-1:-1;;;154758:54:0;;;;;;;;;;;;;;-1:-1:-1;154823:25:0;;:54;;;;;-1:-1:-1;;154888:56:0;;;;;;;;-1:-1:-1;;;;;154888:56:0;;;;;;154955:422;;;;35282:4;154987:16;:22;154955:422;;;155042:30;155075:16;155247:44;;;155243:123;;;155312:16;:38;;;155243:123;155027:350;154955:422;155512:23;155560:1;155539:17;155545:11;35282:4;155539:17;:::i;:::-;155538:23;;;;:::i;:::-;155512:49;-1:-1:-1;135189:4:0;155886:29;155512:49;155886:11;:29;:::i;:::-;155885:58;;;;:::i;:::-;155833:111;;;;;;;-1:-1:-1;;;155833:111:0;-1:-1:-1;;;;155833:111:0;;;;;;135189:4;156352:29;156366:15;156352:11;:29;:::i;:::-;156351:58;;;;:::i;:::-;156301:109;;;;;;;-1:-1:-1;;;156301:109:0;-1:-1:-1;;;;156301:109:0;;;;;;156428:14;;;;;;156301:6;;6658:13:1;;-1:-1:-1;;;;;6698:34:1;;3837:43;;6628:3;6613:19;;;6779:2;6775:18;;;-1:-1:-1;;;;;6771:43:1;6831:4;6816:20;;3837:43;6872:3;6868:19;;;-1:-1:-1;;;;;6864:44:1;6925:2;6910:18;;3837:43;6964:3;6960:19;;;6981:8;6956:34;7007:4;6992:20;;3944:33;7048:3;7044:19;;;7065:8;7040:34;7091:3;7076:19;;3944:33;7127:3;7123:19;;;7159:4;7144:20;;4041:31;-1:-1:-1;7211:4:1;7199:17;;7193:24;7261:6;7244:24;;7285:3;7270:19;;4041:31;7325:2;7321:20;;;-1:-1:-1;;;;;7317:49:1;7383:4;7368:20;;4136:47;7421:4;7417:22;;;7456:6;7441:22;;3521:44;7398:66;6435:1035;;;;;156428:14:0;;;;;;;;152781:3669;;152499:3951;;;;;;;:::o;167089:1143::-;167361:14;;167579:42;167587:15;167595:7;167587:5;:15;:::i;:::-;35282:4;167609:11;167617:3;167609:5;:11;:::i;167579:42::-;167563:58;-1:-1:-1;167997:26:0;168005:18;167563:58;167997:26;:::i;:::-;167986:38;;35192:4;167986:38;:::i;:::-;167978:46;;168219:5;168180:35;168188:10;35192:4;168209:5;168180:7;:35::i;:::-;:44;;;;:::i;:::-;168171:53;167089:1143;-1:-1:-1;;;;;;;;167089:1143:0:o;164273:1199::-;164507:14;;164819:42;164827:15;164837:5;164827:7;:15;:::i;:::-;35282:4;164849:11;164855:5;35282:4;164849:11;:::i;164819:42::-;164803:58;-1:-1:-1;165237:26:0;165245:18;164803:58;165237:26;:::i;:::-;165226:38;;35192:4;165226:38;:::i;:::-;165218:46;;165459:5;165420:35;165428:10;165440:5;35192:4;165420:7;:35::i;:::-;:44;;;;:::i;46339:476::-;46411:9;46638:1;46634;46630:6;46626:14;46623:1;46620:21;46617:1;46613:29;46606:37;46603:1;46599:45;46589:172;;46678:10;46672:4;46665:24;46741:4;46735;46728:18;46589:172;-1:-1:-1;46784:9:0;;46780:17;;46339:476::o;169070:112::-;169126:14;169162:12;:5;169170:4;169162:12;:::i;14:286:1:-;72:6;125:2;113:9;104:7;100:23;96:32;93:52;;;141:1;138;131:12;93:52;167:23;;-1:-1:-1;;;;;;219:32:1;;209:43;;199:71;;266:1;263;256:12;679:131;-1:-1:-1;;;;;754:31:1;;744:42;;734:70;;800:1;797;790:12;734:70;679:131;:::o;815:247::-;874:6;927:2;915:9;906:7;902:23;898:32;895:52;;;943:1;940;933:12;895:52;982:9;969:23;1001:31;1026:5;1001:31;:::i;1067:346::-;1135:6;1143;1196:2;1184:9;1175:7;1171:23;1167:32;1164:52;;;1212:1;1209;1202:12;1164:52;-1:-1:-1;;1257:23:1;;;1377:2;1362:18;;;1349:32;;-1:-1:-1;1067:346:1:o;1903:466::-;1980:6;1988;1996;2049:2;2037:9;2028:7;2024:23;2020:32;2017:52;;;2065:1;2062;2055:12;2017:52;-1:-1:-1;;2110:23:1;;;2230:2;2215:18;;2202:32;;-1:-1:-1;2333:2:1;2318:18;;;2305:32;;1903:466;-1:-1:-1;1903:466:1:o;2374:118::-;2460:5;2453:13;2446:21;2439:5;2436:32;2426:60;;2482:1;2479;2472:12;2497:965;2607:6;2615;2623;2631;2639;2647;2655;2708:3;2696:9;2687:7;2683:23;2679:33;2676:53;;;2725:1;2722;2715:12;2676:53;2770:23;;;-1:-1:-1;2890:2:1;2875:18;;2862:32;;-1:-1:-1;2993:2:1;2978:18;;2965:32;;-1:-1:-1;3096:2:1;3081:18;;3068:32;;-1:-1:-1;3199:3:1;3184:19;;3171:33;;-1:-1:-1;3303:3:1;3288:19;;3275:33;;-1:-1:-1;3386:3:1;3371:19;;3358:33;3400:30;3358:33;3400:30;:::i;:::-;3449:7;3439:17;;;2497:965;;;;;;;;;;:::o;5120:245::-;5187:6;5240:2;5228:9;5219:7;5215:23;5211:32;5208:52;;;5256:1;5253;5246:12;5208:52;5288:9;5282:16;5307:28;5329:5;5307:28;:::i;5370:270::-;5459:6;5512:2;5500:9;5491:7;5487:23;5483:32;5480:52;;;5528:1;5525;5518:12;5480:52;5560:9;5554:16;5579:31;5604:5;5579:31;:::i;5645:127::-;5706:10;5701:3;5697:20;5694:1;5687:31;5737:4;5734:1;5727:15;5761:4;5758:1;5751:15;5777:168;5850:9;;;5881;;5898:15;;;5892:22;;5878:37;5868:71;;5919:18;;:::i;5950:128::-;6017:9;;;6038:11;;;6035:37;;;6052:18;;:::i;6083:125::-;6148:9;;;6169:10;;;6166:36;;;6182:18;;:::i;6213:217::-;6253:1;6279;6269:132;;6323:10;6318:3;6314:20;6311:1;6304:31;6358:4;6355:1;6348:15;6386:4;6383:1;6376:15;6269:132;-1:-1:-1;6415:9:1;;6213:217::o
Swarm Source
ipfs://1c58c4f2a95bab6e3826f528a8fd40d37a3abe92ffdd0e7a48b3c6163038d86a
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in MON
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.