Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 1 from a total of 1 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x60a06040 | 21108883 | 20 days ago | IN | 0 ETH | 0.01102013 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Name:
PayloadIGP48
Compiler Version
v0.8.21+commit.d9974bed
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity ^0.8.21; pragma experimental ABIEncoderV2; import {LiquidityCalcs} from "../libraries/liquidityCalcs.sol"; import {LiquiditySlotsLink} from "../libraries/liquiditySlotsLink.sol"; interface IGovernorBravo { function _acceptAdmin() external; function _setVotingDelay(uint256 newVotingDelay) external; function _setVotingPeriod(uint256 newVotingPeriod) external; function _acceptAdminOnTimelock() external; function _setImplementation(address implementation_) external; function propose( address[] memory targets, uint256[] memory values, string[] memory signatures, bytes[] memory calldatas, string memory description ) external returns (uint256); function admin() external view returns (address); function pendingAdmin() external view returns (address); function timelock() external view returns (address); function votingDelay() external view returns (uint256); function votingPeriod() external view returns (uint256); } interface ITimelock { function acceptAdmin() external; function setDelay(uint256 delay_) external; function setPendingAdmin(address pendingAdmin_) external; function queueTransaction( address target, uint256 value, string memory signature, bytes memory data, uint256 eta ) external returns (bytes32); function executeTransaction( address target, uint256 value, string memory signature, bytes memory data, uint256 eta ) external payable returns (bytes memory); function pendingAdmin() external view returns (address); function admin() external view returns (address); function delay() external view returns (uint256); } interface AdminModuleStructs { struct AddressBool { address addr; bool value; } struct AddressUint256 { address addr; uint256 value; } struct RateDataV1Params { address token; uint256 kink; uint256 rateAtUtilizationZero; uint256 rateAtUtilizationKink; uint256 rateAtUtilizationMax; } struct RateDataV2Params { address token; uint256 kink1; uint256 kink2; uint256 rateAtUtilizationZero; uint256 rateAtUtilizationKink1; uint256 rateAtUtilizationKink2; uint256 rateAtUtilizationMax; } struct TokenConfig { address token; uint256 fee; uint256 threshold; } struct UserSupplyConfig { address user; address token; uint8 mode; uint256 expandPercent; uint256 expandDuration; uint256 baseWithdrawalLimit; } struct UserBorrowConfig { address user; address token; uint8 mode; uint256 expandPercent; uint256 expandDuration; uint256 baseDebtCeiling; uint256 maxDebtCeiling; } } interface IFluidLiquidityAdmin { /// @notice adds/removes auths. Auths generally could be contracts which can have restricted actions defined on contract. /// auths can be helpful in reducing governance overhead where it's not needed. /// @param authsStatus_ array of structs setting allowed status for an address. /// status true => add auth, false => remove auth function updateAuths( AdminModuleStructs.AddressBool[] calldata authsStatus_ ) external; /// @notice adds/removes guardians. Only callable by Governance. /// @param guardiansStatus_ array of structs setting allowed status for an address. /// status true => add guardian, false => remove guardian function updateGuardians( AdminModuleStructs.AddressBool[] calldata guardiansStatus_ ) external; /// @notice changes the revenue collector address (contract that is sent revenue). Only callable by Governance. /// @param revenueCollector_ new revenue collector address function updateRevenueCollector(address revenueCollector_) external; /// @notice changes current status, e.g. for pausing or unpausing all user operations. Only callable by Auths. /// @param newStatus_ new status /// status = 2 -> pause, status = 1 -> resume. function changeStatus(uint256 newStatus_) external; /// @notice update tokens rate data version 1. Only callable by Auths. /// @param tokensRateData_ array of RateDataV1Params with rate data to set for each token function updateRateDataV1s( AdminModuleStructs.RateDataV1Params[] calldata tokensRateData_ ) external; /// @notice update tokens rate data version 2. Only callable by Auths. /// @param tokensRateData_ array of RateDataV2Params with rate data to set for each token function updateRateDataV2s( AdminModuleStructs.RateDataV2Params[] calldata tokensRateData_ ) external; /// @notice updates token configs: fee charge on borrowers interest & storage update utilization threshold. /// Only callable by Auths. /// @param tokenConfigs_ contains token address, fee & utilization threshold function updateTokenConfigs( AdminModuleStructs.TokenConfig[] calldata tokenConfigs_ ) external; /// @notice updates user classes: 0 is for new protocols, 1 is for established protocols. /// Only callable by Auths. /// @param userClasses_ struct array of uint256 value to assign for each user address function updateUserClasses( AdminModuleStructs.AddressUint256[] calldata userClasses_ ) external; /// @notice sets user supply configs per token basis. Eg: with interest or interest-free and automated limits. /// Only callable by Auths. /// @param userSupplyConfigs_ struct array containing user supply config, see `UserSupplyConfig` struct for more info function updateUserSupplyConfigs( AdminModuleStructs.UserSupplyConfig[] memory userSupplyConfigs_ ) external; /// @notice setting user borrow configs per token basis. Eg: with interest or interest-free and automated limits. /// Only callable by Auths. /// @param userBorrowConfigs_ struct array containing user borrow config, see `UserBorrowConfig` struct for more info function updateUserBorrowConfigs( AdminModuleStructs.UserBorrowConfig[] memory userBorrowConfigs_ ) external; /// @notice pause operations for a particular user in class 0 (class 1 users can't be paused by guardians). /// Only callable by Guardians. /// @param user_ address of user to pause operations for /// @param supplyTokens_ token addresses to pause withdrawals for /// @param borrowTokens_ token addresses to pause borrowings for function pauseUser( address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_ ) external; /// @notice unpause operations for a particular user in class 0 (class 1 users can't be paused by guardians). /// Only callable by Guardians. /// @param user_ address of user to unpause operations for /// @param supplyTokens_ token addresses to unpause withdrawals for /// @param borrowTokens_ token addresses to unpause borrowings for function unpauseUser( address user_, address[] calldata supplyTokens_, address[] calldata borrowTokens_ ) external; /// @notice collects revenue for tokens to configured revenueCollector address. /// @param tokens_ array of tokens to collect revenue for /// @dev Note that this can revert if token balance is < revenueAmount (utilization > 100%) function collectRevenue(address[] calldata tokens_) external; /// @notice gets the current updated exchange prices for n tokens and updates all prices, rates related data in storage. /// @param tokens_ tokens to update exchange prices for /// @return supplyExchangePrices_ new supply rates of overall system for each token /// @return borrowExchangePrices_ new borrow rates of overall system for each token function updateExchangePrices( address[] calldata tokens_ ) external returns ( uint256[] memory supplyExchangePrices_, uint256[] memory borrowExchangePrices_ ); function readFromStorage( bytes32 slot_ ) external view returns (uint256 result_); } interface FluidVaultFactory { /// @notice Sets an address as allowed vault deployment logic (`deploymentLogic_`) contract or not. /// This function can only be called by the owner. /// @param deploymentLogic_ The address of the vault deployment logic contract to be set. /// @param allowed_ A boolean indicating whether the specified address is allowed to deploy new type of vault. function setVaultDeploymentLogic( address deploymentLogic_, bool allowed_ ) external; /// @notice Sets an address (`vaultAuth_`) as allowed vault authorization or not for a specific vault (`vault_`). /// This function can only be called by the owner. /// @param vault_ The address of the vault for which the authorization is being set. /// @param vaultAuth_ The address to be set as vault authorization. /// @param allowed_ A boolean indicating whether the specified address is allowed to update the specific vault config. function setVaultAuth( address vault_, address vaultAuth_, bool allowed_ ) external; /// @notice Computes the address of a vault based on its given ID (`vaultId_`). /// @param vaultId_ The ID of the vault. /// @return vault_ Returns the computed address of the vault. function getVaultAddress( uint256 vaultId_ ) external view returns (address vault_); } interface IFluidVaultT1 { /// @notice updates the Vault oracle to `newOracle_`. Must implement the FluidOracle interface. function updateOracle(address newOracle_) external; /// @notice updates the all Vault core settings according to input params. /// All input values are expected in 1e2 (1% = 100, 100% = 10_000). function updateCoreSettings( uint256 supplyRateMagnifier_, uint256 borrowRateMagnifier_, uint256 collateralFactor_, uint256 liquidationThreshold_, uint256 liquidationMaxLimit_, uint256 withdrawGap_, uint256 liquidationPenalty_, uint256 borrowFee_ ) external; /// @notice updates the allowed rebalancer to `newRebalancer_`. function updateRebalancer(address newRebalancer_) external; /// @notice updates the supply rate magnifier to `supplyRateMagnifier_`. Input in 1e2 (1% = 100, 100% = 10_000). function updateSupplyRateMagnifier(uint supplyRateMagnifier_) external; /// @notice updates the borrow rate magnifier to `borrowRateMagnifier_`. Input in 1e2 (1% = 100, 100% = 10_000). function updateBorrowRateMagnifier(uint borrowRateMagnifier_) external; /// @notice updates the collateral factor to `collateralFactor_`. Input in 1e2 (1% = 100, 100% = 10_000). function updateCollateralFactor(uint collateralFactor_) external; /// @notice updates the liquidation threshold to `liquidationThreshold_`. Input in 1e2 (1% = 100, 100% = 10_000). function updateLiquidationThreshold(uint liquidationThreshold_) external; /// @notice updates the liquidation max limit to `liquidationMaxLimit_`. Input in 1e2 (1% = 100, 100% = 10_000). function updateLiquidationMaxLimit(uint liquidationMaxLimit_) external; /// @notice updates the withdrawal gap to `withdrawGap_`. Input in 1e2 (1% = 100, 100% = 10_000). function updateWithdrawGap(uint withdrawGap_) external; /// @notice updates the liquidation penalty to `liquidationPenalty_`. Input in 1e2 (1% = 100, 100% = 10_000). function updateLiquidationPenalty(uint liquidationPenalty_) external; /// @notice updates the borrow fee to `borrowFee_`. Input in 1e2 (1% = 100, 100% = 10_000). function updateBorrowFee(uint borrowFee_) external; } interface IFluidReserveContract { function isRebalancer(address user) external returns (bool); function rebalanceFToken(address protocol_) external; function rebalanceVault(address protocol_) external; function transferFunds(address token_) external; function getProtocolTokens(address protocol_) external; function updateAuth(address auth_, bool isAuth_) external; function updateRebalancer(address rebalancer_, bool isRebalancer_) external; function approve( address[] memory protocols_, address[] memory tokens_, uint256[] memory amounts_ ) external; function revoke( address[] memory protocols_, address[] memory tokens_ ) external; } interface IFTokenAdmin { /// @notice updates the rewards rate model contract. /// Only callable by LendingFactory auths. /// @param rewardsRateModel_ the new rewards rate model contract address. /// can be set to address(0) to set no rewards (to save gas) function updateRewards(address rewardsRateModel_) external; /// @notice Balances out the difference between fToken supply at Liquidity vs totalAssets(). /// Deposits underlying from rebalancer address into Liquidity but doesn't mint any shares /// -> thus making deposit available as rewards. /// Only callable by rebalancer. /// @return assets_ amount deposited to Liquidity function rebalance() external payable returns (uint256 assets_); /// @notice gets the liquidity exchange price of the underlying asset, calculates the updated exchange price (with reward rates) /// and writes those values to storage. /// Callable by anyone. /// @return tokenExchangePrice_ exchange price of fToken share to underlying asset /// @return liquidityExchangePrice_ exchange price at Liquidity for the underlying asset function updateRates() external returns (uint256 tokenExchangePrice_, uint256 liquidityExchangePrice_); /// @notice sends any potentially stuck funds to Liquidity contract. Only callable by LendingFactory auths. function rescueFunds(address token_) external; /// @notice Updates the rebalancer address (ReserveContract). Only callable by LendingFactory auths. function updateRebalancer(address rebalancer_) external; } interface IERC20 { function allowance( address spender, address caller ) external view returns (uint256); function balanceOf(address account) external view returns (uint256); } interface FluidDexFactory { /// @notice Computes the address of a dex based on its given ID (`dexId_`). /// @param dexId_ The ID of the dex. /// @return dex_ Returns the computed address of the dex. function getDexAddress(uint256 dexId_) external view returns (address dex_); function setDexAuth(address dex_, address dexAuth_, bool allowed_) external; function owner() external view returns (address); } contract PayloadIGP48 { uint256 public constant PROPOSAL_ID = 48; address public constant PROPOSER = 0xA45f7bD6A5Ff45D31aaCE6bCD3d426D9328cea01; address public constant PROPOSER_AVO_MULTISIG = 0x059a94a72451c0ae1Cc1cE4bf0Db52421Bbe8210; address public constant PROPOSER_AVO_MULTISIG_2 = 0x9efdE135CA4832AbF0408c44c6f5f370eB0f35e8; address public constant PROPOSER_AVO_MULTISIG_3 = 0x5C43AAC965ff230AC1cF63e924D0153291D78BaD; IGovernorBravo public constant GOVERNOR = IGovernorBravo(0x0204Cd037B2ec03605CFdFe482D8e257C765fA1B); ITimelock public constant TIMELOCK = ITimelock(0x2386DC45AdDed673317eF068992F19421B481F4c); address public constant TEAM_MULTISIG = 0x4F6F977aCDD1177DCD81aB83074855EcB9C2D49e; address public immutable ADDRESS_THIS; IFluidLiquidityAdmin public constant LIQUIDITY = IFluidLiquidityAdmin(0x52Aa899454998Be5b000Ad077a46Bbe360F4e497); IFluidReserveContract public constant FLUID_RESERVE = IFluidReserveContract(0x264786EF916af64a1DB19F513F24a3681734ce92); FluidVaultFactory public constant VAULT_FACTORY = FluidVaultFactory(0x324c5Dc1fC42c7a4D43d92df1eBA58a54d13Bf2d); FluidDexFactory public constant DEX_FACTORY = FluidDexFactory(0x91716C4EDA1Fb55e84Bf8b4c7085f84285c19085); address internal constant ETH_ADDRESS = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; address internal constant wstETH_ADDRESS = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; address internal constant weETH_ADDRESS = 0xCd5fE23C85820F7B72D0926FC9b05b43E359b7ee; address internal constant USDC_ADDRESS = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; address internal constant USDT_ADDRESS = 0xdAC17F958D2ee523a2206206994597C13D831ec7; address internal constant sUSDe_ADDRESS = 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497; address internal constant sUSDs_ADDRESS = 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD; address internal constant USDe_ADDRESS = 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3; address internal constant GHO_ADDRESS = 0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f; address internal constant WBTC_ADDRESS = 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599; address internal constant cbBTC_ADDRESS = 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf; address internal constant F_GHO_ADDRESS = 0x6A29A46E21C730DcA1d8b23d637c101cec605C5B; struct Dex { address dex; address tokenA; address tokenB; bool smartCollateral; bool smartDebt; uint256 baseWithdrawalLimitInUSD; uint256 baseBorrowLimitInUSD; uint256 maxBorrowLimitInUSD; } enum TYPE { TYPE_2, TYPE_3, TYPE_4 } struct Vault { address vault; TYPE vaultType; address supplyToken; address borrowToken; } constructor() { ADDRESS_THIS = address(this); } function propose(string memory description) external { require( msg.sender == PROPOSER || msg.sender == TEAM_MULTISIG || address(this) == PROPOSER_AVO_MULTISIG || address(this) == PROPOSER_AVO_MULTISIG_2 || address(PROPOSER_AVO_MULTISIG_3) == PROPOSER_AVO_MULTISIG_3, "msg.sender-not-allowed" ); uint256 totalActions = 1; address[] memory targets = new address[](totalActions); uint256[] memory values = new uint256[](totalActions); string[] memory signatures = new string[](totalActions); bytes[] memory calldatas = new bytes[](totalActions); // Action 1: call executePayload on timelock contract to execute payload related to Fluid and Lite targets[0] = address(TIMELOCK); values[0] = 0; signatures[0] = "executePayload(address,string,bytes)"; calldatas[0] = abi.encode(ADDRESS_THIS, "execute()", abi.encode()); uint256 proposedId = GOVERNOR.propose( targets, values, signatures, calldatas, description ); require(proposedId == PROPOSAL_ID, "PROPOSAL_IS_NOT_SAME"); } function execute() external { require(address(this) == address(TIMELOCK), "not-valid-caller"); // Action 1: Set USDe token config and market rate curve on liquidity. action1(); // Action 2: Set GHO-USDC Dex pool allowance. action2(); // Action 3: Set Other Dex pools dust allowance. action3(); // Action 4: set fGHO rewards handler action4(); // Action 5: Adjust Reserve allowances on wBTC/stables vaults action5(); // Action 6: Add Team Multisig as auth for newly deployed Vaults action6(); } function verifyProposal() external view {} /** * | * | Proposal Payload Actions | * |__________________________________ */ /// @notice Action 1: Set USDe token config and market rate curve on liquidity. function action1() internal { { AdminModuleStructs.RateDataV1Params[] memory params_ = new AdminModuleStructs.RateDataV1Params[](1); params_[0] = AdminModuleStructs.RateDataV1Params({ token: USDe_ADDRESS, // USDe kink: 93 * 1e2, // 93% rateAtUtilizationZero: 0, // 0% rateAtUtilizationKink: 7.5 * 1e2, // 7.5% rateAtUtilizationMax: 25 * 1e2 // 25% }); LIQUIDITY.updateRateDataV1s(params_); } { // Remove Team Multisig as auth for GHO-USDC Vault. VAULT_FACTORY.setVaultAuth( getVaultAddress(61), TEAM_MULTISIG, false ); // Remove Team Multisig as auth for GHO-USDC Dex pool. if (DEX_FACTORY.owner() == address(this)) { DEX_FACTORY.setDexAuth(getDexAddress(4), TEAM_MULTISIG, false); } } } /// @notice Action 2: Set GHO-USDC Dex pool allowance. function action2() internal { { // GHO-USDC Dex memory DEX_GHO_USDC = Dex({ dex: getDexAddress(4), tokenA: GHO_ADDRESS, tokenB: USDC_ADDRESS, smartCollateral: true, smartDebt: true, baseWithdrawalLimitInUSD: 7_500_000, // $7.5M baseBorrowLimitInUSD: 5_000_000, // $5M maxBorrowLimitInUSD: 6_000_000 // $6M }); setDexLimits(DEX_GHO_USDC); // Smart Collateral & Smart Debt } } /// @notice Action 3: Set Other Dex pools dust allowance. function action3() internal { { // ETH-USDC Dex memory DEX_ETH_USDC = Dex({ dex: getDexAddress(5), tokenA: ETH_ADDRESS, tokenB: USDC_ADDRESS, smartCollateral: true, smartDebt: true, baseWithdrawalLimitInUSD: 50_000, // $50k baseBorrowLimitInUSD: 40_000, // $40k maxBorrowLimitInUSD: 50_000 // $50k }); setDexLimits(DEX_ETH_USDC); // Smart Collateral & Smart Debt } { // WBTC-ETH Dex memory DEX_WBTC_ETH = Dex({ dex: getDexAddress(6), tokenA: WBTC_ADDRESS, tokenB: ETH_ADDRESS, smartCollateral: true, smartDebt: true, baseWithdrawalLimitInUSD: 50_000, // $50k baseBorrowLimitInUSD: 40_000, // $40k maxBorrowLimitInUSD: 50_000 // $50k }); setDexLimits(DEX_WBTC_ETH); // Smart Collateral & Smart Debt } { // cbBTC-ETH Dex memory DEX_cbBTC_ETH = Dex({ dex: getDexAddress(7), tokenA: cbBTC_ADDRESS, tokenB: ETH_ADDRESS, smartCollateral: true, smartDebt: true, baseWithdrawalLimitInUSD: 50_000, // $50k baseBorrowLimitInUSD: 40_000, // $40k maxBorrowLimitInUSD: 50_000 // $50k }); setDexLimits(DEX_cbBTC_ETH); // Smart Collateral & Smart Debt } { // USDe-USDC Dex memory DEX_USDe_USDC = Dex({ dex: getDexAddress(8), tokenA: USDe_ADDRESS, tokenB: USDC_ADDRESS, smartCollateral: true, smartDebt: true, baseWithdrawalLimitInUSD: 50_000, // $50k baseBorrowLimitInUSD: 40_000, // $40k maxBorrowLimitInUSD: 50_000 // $50k }); setDexLimits(DEX_USDe_USDC); // Smart Collateral & Smart Debt } } /// @notice Action 4: set fGHO rewards handler function action4() internal { address[] memory protocols = new address[](1); address[] memory tokens = new address[](1); uint256[] memory amounts = new uint256[](1); { /// fGHO IFTokenAdmin(F_GHO_ADDRESS).updateRewards( address(0xE85eb0acB7281fAf00810d167C7dE14bB070B480) ); uint256 allowance = IERC20(GHO_ADDRESS).allowance( address(FLUID_RESERVE), F_GHO_ADDRESS ); protocols[0] = F_GHO_ADDRESS; tokens[0] = GHO_ADDRESS; amounts[0] = allowance + (73_000 * 1e18); } FLUID_RESERVE.approve(protocols, tokens, amounts); } /// @notice Action 5: Adjust Reserve allowances on wBTC/stables and cbBTC/stables vaults function action5() internal { address[] memory protocols = new address[](6); address[] memory tokens = new address[](6); uint256[] memory amounts = new uint256[](6); address wBTC_USDC_VAULT = getVaultAddress(21); address wBTC_USDT_VAULT = getVaultAddress(22); address cbBTC_USDC_VAULT = getVaultAddress(29); address cbBTC_USDT_VAULT = getVaultAddress(30); { // Supply Side wBTC-USDC protocols[0] = wBTC_USDC_VAULT; tokens[0] = WBTC_ADDRESS; amounts[0] = 0.0015 * 1e8; } { // Supply Side wBTC-USDT protocols[1] = wBTC_USDT_VAULT; tokens[1] = WBTC_ADDRESS; amounts[1] = 0.0015 * 1e8; } { // Borrow Side wBTC-USDC uint256 allowance = IERC20(USDC_ADDRESS).allowance( address(FLUID_RESERVE), wBTC_USDC_VAULT ); protocols[2] = wBTC_USDC_VAULT; tokens[2] = USDC_ADDRESS; amounts[2] = allowance + (26_000 * 1e6); } { // Borrow Side wBTC-USDT uint256 allowance = IERC20(USDT_ADDRESS).allowance( address(FLUID_RESERVE), wBTC_USDT_VAULT ); protocols[3] = wBTC_USDT_VAULT; tokens[3] = USDT_ADDRESS; amounts[3] = allowance + (26_000 * 1e6); } { // Borrow Side cbBTC-USDC uint256 allowance = IERC20(USDC_ADDRESS).allowance( address(FLUID_RESERVE), cbBTC_USDC_VAULT ); protocols[4] = cbBTC_USDC_VAULT; tokens[4] = USDC_ADDRESS; amounts[4] = allowance + (26_000 * 1e6); } { // Borrow Side cbBTC-USDT uint256 allowance = IERC20(USDT_ADDRESS).allowance( address(FLUID_RESERVE), cbBTC_USDT_VAULT ); protocols[5] = cbBTC_USDT_VAULT; tokens[5] = USDT_ADDRESS; amounts[5] = allowance + (26_000 * 1e6); } FLUID_RESERVE.approve(protocols, tokens, amounts); } /// @notice Action 6: Add Team Multisig as auth for newly deployed Vaults function action6() internal { VAULT_FACTORY.setVaultAuth(getVaultAddress(62), TEAM_MULTISIG, true); VAULT_FACTORY.setVaultAuth(getVaultAddress(63), TEAM_MULTISIG, true); VAULT_FACTORY.setVaultAuth(getVaultAddress(64), TEAM_MULTISIG, true); VAULT_FACTORY.setVaultAuth(getVaultAddress(65), TEAM_MULTISIG, true); } /** * | * | Proposal Payload Helpers | * |__________________________________ */ function getVaultAddress(uint256 vaultId_) public view returns (address) { return VAULT_FACTORY.getVaultAddress(vaultId_); } function getDexAddress(uint256 dexId_) public view returns (address) { return DEX_FACTORY.getDexAddress(dexId_); } struct SupplyProtocolConfig { address protocol; address supplyToken; uint256 expandPercent; uint256 expandDuration; uint256 baseWithdrawalLimitInUSD; } struct BorrowProtocolConfig { address protocol; address borrowToken; uint256 expandPercent; uint256 expandDuration; uint256 baseBorrowLimitInUSD; uint256 maxBorrowLimitInUSD; } function setDexLimits(Dex memory dex_) internal { // Smart Collateral if (dex_.smartCollateral) { SupplyProtocolConfig memory protocolConfigTokenA_ = SupplyProtocolConfig({ protocol: dex_.dex, supplyToken: dex_.tokenA, expandPercent: 50 * 1e2, // 50% expandDuration: 1 hours, // 1 hour baseWithdrawalLimitInUSD: dex_.baseWithdrawalLimitInUSD }); setSupplyProtocolLimits(protocolConfigTokenA_); SupplyProtocolConfig memory protocolConfigTokenB_ = SupplyProtocolConfig({ protocol: dex_.dex, supplyToken: dex_.tokenB, expandPercent: 50 * 1e2, // 50% expandDuration: 1 hours, // 1 hour baseWithdrawalLimitInUSD: dex_.baseWithdrawalLimitInUSD }); setSupplyProtocolLimits(protocolConfigTokenB_); } // Smart Debt if (dex_.smartDebt) { BorrowProtocolConfig memory protocolConfigTokenA_ = BorrowProtocolConfig({ protocol: dex_.dex, borrowToken: dex_.tokenA, expandPercent: 50 * 1e2, // 50% expandDuration: 1 hours, // 1 hour baseBorrowLimitInUSD: dex_.baseBorrowLimitInUSD, maxBorrowLimitInUSD: dex_.maxBorrowLimitInUSD }); setBorrowProtocolLimits(protocolConfigTokenA_); BorrowProtocolConfig memory protocolConfigTokenB_ = BorrowProtocolConfig({ protocol: dex_.dex, borrowToken: dex_.tokenB, expandPercent: 50 * 1e2, // 50% expandDuration: 1 hours, // 1 hour baseBorrowLimitInUSD: dex_.baseBorrowLimitInUSD, maxBorrowLimitInUSD: dex_.maxBorrowLimitInUSD }); setBorrowProtocolLimits(protocolConfigTokenB_); } } function setSupplyProtocolLimits( SupplyProtocolConfig memory protocolConfig_ ) internal { { // Supply Limits AdminModuleStructs.UserSupplyConfig[] memory configs_ = new AdminModuleStructs.UserSupplyConfig[](1); configs_[0] = AdminModuleStructs.UserSupplyConfig({ user: address(protocolConfig_.protocol), token: protocolConfig_.supplyToken, mode: 1, expandPercent: protocolConfig_.expandPercent, expandDuration: protocolConfig_.expandDuration, baseWithdrawalLimit: getRawAmount( protocolConfig_.supplyToken, 0, protocolConfig_.baseWithdrawalLimitInUSD, true ) }); LIQUIDITY.updateUserSupplyConfigs(configs_); } } function setBorrowProtocolLimits( BorrowProtocolConfig memory protocolConfig_ ) internal { { // Borrow Limits AdminModuleStructs.UserBorrowConfig[] memory configs_ = new AdminModuleStructs.UserBorrowConfig[](1); configs_[0] = AdminModuleStructs.UserBorrowConfig({ user: address(protocolConfig_.protocol), token: protocolConfig_.borrowToken, mode: 1, expandPercent: protocolConfig_.expandPercent, expandDuration: protocolConfig_.expandDuration, baseDebtCeiling: getRawAmount( protocolConfig_.borrowToken, 0, protocolConfig_.baseBorrowLimitInUSD, false ), maxDebtCeiling: getRawAmount( protocolConfig_.borrowToken, 0, protocolConfig_.maxBorrowLimitInUSD, false ) }); LIQUIDITY.updateUserBorrowConfigs(configs_); } } function getRawAmount( address token, uint256 amount, uint256 amountInUSD, bool isSupply ) public view returns (uint256) { if (amount > 0 && amountInUSD > 0) { revert("both usd and amount are not zero"); } uint256 exchangePriceAndConfig_ = LIQUIDITY.readFromStorage( LiquiditySlotsLink.calculateMappingStorageSlot( LiquiditySlotsLink.LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT, token ) ); ( uint256 supplyExchangePrice, uint256 borrowExchangePrice ) = LiquidityCalcs.calcExchangePrices(exchangePriceAndConfig_); uint256 usdPrice = 0; uint256 decimals = 18; if (token == ETH_ADDRESS) { usdPrice = 2_500 * 1e2; decimals = 18; } else if (token == wstETH_ADDRESS) { usdPrice = 2_970 * 1e2; decimals = 18; } else if (token == weETH_ADDRESS) { usdPrice = 2_650 * 1e2; decimals = 18; } else if (token == cbBTC_ADDRESS || token == WBTC_ADDRESS) { usdPrice = 67_750 * 1e2; decimals = 8; } else if (token == USDC_ADDRESS || token == USDT_ADDRESS) { usdPrice = 1 * 1e2; decimals = 6; } else if (token == sUSDe_ADDRESS) { usdPrice = 1.11 * 1e2; decimals = 18; } else if (token == sUSDs_ADDRESS) { usdPrice = 1.12 * 1e2; decimals = 18; } else if (token == GHO_ADDRESS || token == USDe_ADDRESS) { usdPrice = 1 * 1e2; decimals = 18; } else { revert("not-found"); } uint256 exchangePrice = isSupply ? supplyExchangePrice : borrowExchangePrice; if (amount > 0) { return (amount * 1e12) / exchangePrice; } else { return (amountInUSD * 1e12 * (10 ** decimals)) / ((usdPrice * exchangePrice) / 1e2); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; /// @title library that represents a number in BigNumber(coefficient and exponent) format to store in smaller bits. /// @notice the number is divided into two parts: a coefficient and an exponent. This comes at a cost of losing some precision /// at the end of the number because the exponent simply fills it with zeroes. This precision is oftentimes negligible and can /// result in significant gas cost reduction due to storage space reduction. /// Also note, a valid big number is as follows: if the exponent is > 0, then coefficient last bits should be occupied to have max precision. /// @dev roundUp is more like a increase 1, which happens everytime for the same number. /// roundDown simply sets trailing digits after coefficientSize to zero (floor), only once for the same number. library BigMathMinified { /// @dev constants to use for `roundUp` input param to increase readability bool internal constant ROUND_DOWN = false; bool internal constant ROUND_UP = true; /// @dev converts `normal` number to BigNumber with `exponent` and `coefficient` (or precision). /// e.g.: /// 5035703444687813576399599 (normal) = (coefficient[32bits], exponent[8bits])[40bits] /// 5035703444687813576399599 (decimal) => 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 (binary) /// => 10000101010010110100000011111011000000000000000000000000000000000000000000000000000 /// ^-------------------- 51(exponent) -------------- ^ /// coefficient = 1000,0101,0100,1011,0100,0000,1111,1011 (2236301563) /// exponent = 0011,0011 (51) /// bigNumber = 1000,0101,0100,1011,0100,0000,1111,1011,0011,0011 (572493200179) /// /// @param normal number which needs to be converted into Big Number /// @param coefficientSize at max how many bits of precision there should be (64 = uint64 (64 bits precision)) /// @param exponentSize at max how many bits of exponent there should be (8 = uint8 (8 bits exponent)) /// @param roundUp signals if result should be rounded down or up /// @return bigNumber converted bigNumber (coefficient << exponent) function toBigNumber( uint256 normal, uint256 coefficientSize, uint256 exponentSize, bool roundUp ) internal pure returns (uint256 bigNumber) { assembly { let lastBit_ let number_ := normal if gt(number_, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) { number_ := shr(0x80, number_) lastBit_ := 0x80 } if gt(number_, 0xFFFFFFFFFFFFFFFF) { number_ := shr(0x40, number_) lastBit_ := add(lastBit_, 0x40) } if gt(number_, 0xFFFFFFFF) { number_ := shr(0x20, number_) lastBit_ := add(lastBit_, 0x20) } if gt(number_, 0xFFFF) { number_ := shr(0x10, number_) lastBit_ := add(lastBit_, 0x10) } if gt(number_, 0xFF) { number_ := shr(0x8, number_) lastBit_ := add(lastBit_, 0x8) } if gt(number_, 0xF) { number_ := shr(0x4, number_) lastBit_ := add(lastBit_, 0x4) } if gt(number_, 0x3) { number_ := shr(0x2, number_) lastBit_ := add(lastBit_, 0x2) } if gt(number_, 0x1) { lastBit_ := add(lastBit_, 1) } if gt(number_, 0) { lastBit_ := add(lastBit_, 1) } if lt(lastBit_, coefficientSize) { // for throw exception lastBit_ := coefficientSize } let exponent := sub(lastBit_, coefficientSize) let coefficient := shr(exponent, normal) if and(roundUp, gt(exponent, 0)) { // rounding up is only needed if exponent is > 0, as otherwise the coefficient fully holds the original number coefficient := add(coefficient, 1) if eq(shl(coefficientSize, 1), coefficient) { // case were coefficient was e.g. 111, with adding 1 it became 1000 (in binary) and coefficientSize 3 bits // final coefficient would exceed it's size. -> reduce coefficent to 100 and increase exponent by 1. coefficient := shl(sub(coefficientSize, 1), 1) exponent := add(exponent, 1) } } if iszero(lt(exponent, shl(exponentSize, 1))) { // if exponent is >= exponentSize, the normal number is too big to fit within // BigNumber with too small sizes for coefficient and exponent revert(0, 0) } bigNumber := shl(exponentSize, coefficient) bigNumber := add(bigNumber, exponent) } } /// @dev get `normal` number from `bigNumber`, `exponentSize` and `exponentMask` function fromBigNumber( uint256 bigNumber, uint256 exponentSize, uint256 exponentMask ) internal pure returns (uint256 normal) { assembly { let coefficient := shr(exponentSize, bigNumber) let exponent := and(bigNumber, exponentMask) normal := shl(exponent, coefficient) } } /// @dev gets the most significant bit `lastBit` of a `normal` number (length of given number of binary format). /// e.g. /// 5035703444687813576399599 = 10000101010010110100000011111011110010100110100000000011100101001101001101011101111 /// lastBit = ^--------------------------------- 83 ----------------------------------------^ function mostSignificantBit(uint256 normal) internal pure returns (uint lastBit) { assembly { let number_ := normal if gt(normal, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) { number_ := shr(0x80, number_) lastBit := 0x80 } if gt(number_, 0xFFFFFFFFFFFFFFFF) { number_ := shr(0x40, number_) lastBit := add(lastBit, 0x40) } if gt(number_, 0xFFFFFFFF) { number_ := shr(0x20, number_) lastBit := add(lastBit, 0x20) } if gt(number_, 0xFFFF) { number_ := shr(0x10, number_) lastBit := add(lastBit, 0x10) } if gt(number_, 0xFF) { number_ := shr(0x8, number_) lastBit := add(lastBit, 0x8) } if gt(number_, 0xF) { number_ := shr(0x4, number_) lastBit := add(lastBit, 0x4) } if gt(number_, 0x3) { number_ := shr(0x2, number_) lastBit := add(lastBit, 0x2) } if gt(number_, 0x1) { lastBit := add(lastBit, 1) } if gt(number_, 0) { lastBit := add(lastBit, 1) } } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; library LibsErrorTypes { /***********************************| | LiquidityCalcs | |__________________________________*/ /// @notice thrown when supply or borrow exchange price is zero at calc token data (token not configured yet) uint256 internal constant LiquidityCalcs__ExchangePriceZero = 70001; /// @notice thrown when rate data is set to a version that is not implemented uint256 internal constant LiquidityCalcs__UnsupportedRateVersion = 70002; /// @notice thrown when the calculated borrow rate turns negative. This should never happen. uint256 internal constant LiquidityCalcs__BorrowRateNegative = 70003; /***********************************| | SafeTransfer | |__________________________________*/ /// @notice thrown when safe transfer from for an ERC20 fails uint256 internal constant SafeTransfer__TransferFromFailed = 71001; /// @notice thrown when safe transfer for an ERC20 fails uint256 internal constant SafeTransfer__TransferFailed = 71002; }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; import { LibsErrorTypes as ErrorTypes } from "./errorTypes.sol"; import { LiquiditySlotsLink } from "./liquiditySlotsLink.sol"; import { BigMathMinified } from "./bigMathMinified.sol"; /// @notice implements calculation methods used for Fluid liquidity such as updated exchange prices, /// borrow rate, withdrawal / borrow limits, revenue amount. library LiquidityCalcs { error FluidLiquidityCalcsError(uint256 errorId_); /// @notice emitted if the calculated borrow rate surpassed max borrow rate (16 bits) and was capped at maximum value 65535 event BorrowRateMaxCap(); /// @dev constants as from Liquidity variables.sol uint256 internal constant EXCHANGE_PRICES_PRECISION = 1e12; /// @dev Ignoring leap years uint256 internal constant SECONDS_PER_YEAR = 365 days; // constants used for BigMath conversion from and to storage uint256 internal constant DEFAULT_EXPONENT_SIZE = 8; uint256 internal constant DEFAULT_EXPONENT_MASK = 0xFF; uint256 internal constant FOUR_DECIMALS = 1e4; uint256 internal constant TWELVE_DECIMALS = 1e12; uint256 internal constant X14 = 0x3fff; uint256 internal constant X15 = 0x7fff; uint256 internal constant X16 = 0xffff; uint256 internal constant X18 = 0x3ffff; uint256 internal constant X24 = 0xffffff; uint256 internal constant X33 = 0x1ffffffff; uint256 internal constant X64 = 0xffffffffffffffff; /////////////////////////////////////////////////////////////////////////// ////////// CALC EXCHANGE PRICES ///////// /////////////////////////////////////////////////////////////////////////// /// @dev calculates interest (exchange prices) for a token given its' exchangePricesAndConfig from storage. /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage /// @return supplyExchangePrice_ updated supplyExchangePrice /// @return borrowExchangePrice_ updated borrowExchangePrice function calcExchangePrices( uint256 exchangePricesAndConfig_ ) internal view returns (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) { // Extracting exchange prices supplyExchangePrice_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE) & X64; borrowExchangePrice_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE) & X64; if (supplyExchangePrice_ == 0 || borrowExchangePrice_ == 0) { revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__ExchangePriceZero); } uint256 temp_ = exchangePricesAndConfig_ & X16; // temp_ = borrowRate unchecked { // last timestamp can not be > current timestamp uint256 secondsSinceLastUpdate_ = block.timestamp - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_LAST_TIMESTAMP) & X33); uint256 borrowRatio_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_BORROW_RATIO) & X15; if (secondsSinceLastUpdate_ == 0 || temp_ == 0 || borrowRatio_ == 1) { // if no time passed, borrow rate is 0, or no raw borrowings: no exchange price update needed // (if borrowRatio_ == 1 means there is only borrowInterestFree, as first bit is 1 and rest is 0) return (supplyExchangePrice_, borrowExchangePrice_); } // calculate new borrow exchange price. // formula borrowExchangePriceIncrease: previous price * borrow rate * secondsSinceLastUpdate_. // nominator is max uint112 (uint64 * uint16 * uint32). Divisor can not be 0. borrowExchangePrice_ += (borrowExchangePrice_ * temp_ * secondsSinceLastUpdate_) / (SECONDS_PER_YEAR * FOUR_DECIMALS); // FOR SUPPLY EXCHANGE PRICE: // all yield paid by borrowers (in mode with interest) goes to suppliers in mode with interest. // formula: previous price * supply rate * secondsSinceLastUpdate_. // where supply rate = (borrow rate - revenueFee%) * ratioSupplyYield. And // ratioSupplyYield = utilization * supplyRatio * borrowRatio // // Example: // supplyRawInterest is 80, supplyInterestFree is 20. totalSupply is 100. BorrowedRawInterest is 50. // BorrowInterestFree is 10. TotalBorrow is 60. borrow rate 40%, revenueFee 10%. // yield is 10 (so half a year must have passed). // supplyRawInterest must become worth 89. totalSupply must become 109. BorrowedRawInterest must become 60. // borrowInterestFree must still be 10. supplyInterestFree still 20. totalBorrow 70. // supplyExchangePrice would have to go from 1 to 1,125 (+ 0.125). borrowExchangePrice from 1 to 1,2 (+0.2). // utilization is 60%. supplyRatio = 20 / 80 = 25% (only 80% of lenders receiving yield). // borrowRatio = 10 / 50 = 20% (only 83,333% of borrowers paying yield): // x of borrowers paying yield = 100% - (20 / (100 + 20)) = 100% - 16.6666666% = 83,333%. // ratioSupplyYield = 60% * 83,33333% * (100% + 20%) = 62,5% // supplyRate = (40% * (100% - 10%)) * = 36% * 62,5% = 22.5% // increase in supplyExchangePrice, assuming 100 as previous price. // 100 * 22,5% * 1/2 (half a year) = 0,1125. // cross-check supplyRawInterest worth = 80 * 1.1125 = 89. totalSupply worth = 89 + 20. // -------------- 1. calculate ratioSupplyYield -------------------------------- // step1: utilization * supplyRatio (or actually part of lenders receiving yield) // temp_ => supplyRatio (in 1e2: 100% = 10_000; 1% = 100 -> max value 16_383) // if first bit 0 then ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger) // else ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger) temp_ = (exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_SUPPLY_RATIO) & X15; if (temp_ == 1) { // if no raw supply: no exchange price update needed // (if supplyRatio_ == 1 means there is only supplyInterestFree, as first bit is 1 and rest is 0) return (supplyExchangePrice_, borrowExchangePrice_); } // ratioSupplyYield precision is 1e27 as 100% for increased precision when supplyInterestFree > supplyWithInterest if (temp_ & 1 == 1) { // ratio is supplyWithInterest / supplyInterestFree (supplyInterestFree is bigger) temp_ = temp_ >> 1; // Note: case where temp_ == 0 (only supplyInterestFree, no yield) already covered by early return // in the if statement a little above. // based on above example but supplyRawInterest is 20, supplyInterestFree is 80. no fee. // supplyRawInterest must become worth 30. totalSupply must become 110. // supplyExchangePrice would have to go from 1 to 1,5. borrowExchangePrice from 1 to 1,2. // so ratioSupplyYield must come out as 2.5 (250%). // supplyRatio would be (20 * 10_000 / 80) = 2500. but must be inverted. temp_ = (1e27 * FOUR_DECIMALS) / temp_; // e.g. 1e31 / 2500 = 4e27. (* 1e27 for precision) // e.g. 5_000 * (1e27 + 4e27) / 1e27 = 25_000 (=250%). temp_ = // utilization * (100% + 100% / supplyRatio) (((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * (1e27 + temp_)) / // extract utilization (max 16_383 so there is no way this can overflow). (FOUR_DECIMALS); // max possible value of temp_ here is 16383 * (1e27 + 1e31) / 1e4 = ~1.64e31 } else { // ratio is supplyInterestFree / supplyWithInterest (supplyWithInterest is bigger) temp_ = temp_ >> 1; // if temp_ == 0 then only supplyWithInterest => full yield. temp_ is already 0 // e.g. 5_000 * 10_000 + (20 * 10_000 / 80) / 10_000 = 5000 * 12500 / 10000 = 6250 (=62.5%). temp_ = // 1e27 * utilization * (100% + supplyRatio) / 100% (1e27 * ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_UTILIZATION) & X14) * // extract utilization (max 16_383 so there is no way this can overflow). (FOUR_DECIMALS + temp_)) / (FOUR_DECIMALS * FOUR_DECIMALS); // max possible temp_ value: 1e27 * 16383 * 2e4 / 1e8 = 3.2766e27 } // from here temp_ => ratioSupplyYield (utilization * supplyRatio part) scaled by 1e27. max possible value ~1.64e31 // step2 of ratioSupplyYield: add borrowRatio (only x% of borrowers paying yield) if (borrowRatio_ & 1 == 1) { // ratio is borrowWithInterest / borrowInterestFree (borrowInterestFree is bigger) borrowRatio_ = borrowRatio_ >> 1; // borrowRatio_ => x of total bororwers paying yield. scale to 1e27. // Note: case where borrowRatio_ == 0 (only borrowInterestFree, no yield) already covered // at the beginning of the method by early return if `borrowRatio_ == 1`. // based on above example but borrowRawInterest is 10, borrowInterestFree is 50. no fee. borrowRatio = 20%. // so only 16.66% of borrowers are paying yield. so the 100% - part of the formula is not needed. // x of borrowers paying yield = (borrowRatio / (100 + borrowRatio)) = 16.6666666% // borrowRatio_ => x of total bororwers paying yield. scale to 1e27. borrowRatio_ = (borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_); // max value here for borrowRatio_ is (1e31 / (1e4 + 1e4))= 5e26 (= 50% of borrowers paying yield). } else { // ratio is borrowInterestFree / borrowWithInterest (borrowWithInterest is bigger) borrowRatio_ = borrowRatio_ >> 1; // borrowRatio_ => x of total bororwers paying yield. scale to 1e27. // x of borrowers paying yield = 100% - (borrowRatio / (100 + borrowRatio)) = 100% - 16.6666666% = 83,333%. borrowRatio_ = (1e27 - ((borrowRatio_ * 1e27) / (FOUR_DECIMALS + borrowRatio_))); // borrowRatio can never be > 100%. so max subtraction can be 100% - 100% / 200%. // or if borrowRatio_ is 0 -> 100% - 0. or if borrowRatio_ is 1 -> 100% - 1 / 101. // max value here for borrowRatio_ is 1e27 - 0 = 1e27 (= 100% of borrowers paying yield). } // temp_ => ratioSupplyYield. scaled down from 1e25 = 1% each to normal percent precision 1e2 = 1%. // max nominator value is ~1.64e31 * 1e27 = 1.64e58. max result = 1.64e8 temp_ = (FOUR_DECIMALS * temp_ * borrowRatio_) / 1e54; // 2. calculate supply rate // temp_ => supply rate (borrow rate - revenueFee%) * ratioSupplyYield. // division part is done in next step to increase precision. (divided by 2x FOUR_DECIMALS, fee + borrowRate) // Note that all calculation divisions for supplyExchangePrice are rounded down. // Note supply rate can be bigger than the borrowRate, e.g. if there are only few lenders with interest // but more suppliers not earning interest. temp_ = ((exchangePricesAndConfig_ & X16) * // borrow rate temp_ * // ratioSupplyYield (FOUR_DECIMALS - ((exchangePricesAndConfig_ >> LiquiditySlotsLink.BITS_EXCHANGE_PRICES_FEE) & X14))); // revenueFee // fee can not be > 100%. max possible = 65535 * ~1.64e8 * 1e4 =~1.074774e17. // 3. calculate increase in supply exchange price supplyExchangePrice_ += ((supplyExchangePrice_ * temp_ * secondsSinceLastUpdate_) / (SECONDS_PER_YEAR * FOUR_DECIMALS * FOUR_DECIMALS * FOUR_DECIMALS)); // max possible nominator = max uint 64 * 1.074774e17 * max uint32 = ~8.52e45. Denominator can not be 0. } } /////////////////////////////////////////////////////////////////////////// ////////// CALC REVENUE ///////// /////////////////////////////////////////////////////////////////////////// /// @dev gets the `revenueAmount_` for a token given its' totalAmounts and exchangePricesAndConfig from storage /// and the current balance of the Fluid liquidity contract for the token. /// @param totalAmounts_ total amounts packed uint256 read from storage /// @param exchangePricesAndConfig_ exchange prices and config packed uint256 read from storage /// @param liquidityTokenBalance_ current balance of Liquidity contract (IERC20(token_).balanceOf(address(this))) /// @return revenueAmount_ collectable revenue amount function calcRevenue( uint256 totalAmounts_, uint256 exchangePricesAndConfig_, uint256 liquidityTokenBalance_ ) internal view returns (uint256 revenueAmount_) { // @dev no need to super-optimize this method as it is only used by admin // calculate the new exchange prices based on earned interest (uint256 supplyExchangePrice_, uint256 borrowExchangePrice_) = calcExchangePrices(exchangePricesAndConfig_); // total supply = interest free + with interest converted from raw uint256 totalSupply_ = getTotalSupply(totalAmounts_, supplyExchangePrice_); if (totalSupply_ > 0) { // available revenue: balanceOf(token) + totalBorrowings - totalLendings. revenueAmount_ = liquidityTokenBalance_ + getTotalBorrow(totalAmounts_, borrowExchangePrice_); // ensure there is no possible case because of rounding etc. where this would revert, // explicitly check if > revenueAmount_ = revenueAmount_ > totalSupply_ ? revenueAmount_ - totalSupply_ : 0; // Note: if utilization > 100% (totalSupply < totalBorrow), then all the amount above 100% utilization // can only be revenue. } else { // if supply is 0, then rest of balance can be withdrawn as revenue so that no amounts get stuck revenueAmount_ = liquidityTokenBalance_; } } /////////////////////////////////////////////////////////////////////////// ////////// CALC LIMITS ///////// /////////////////////////////////////////////////////////////////////////// /// @dev calculates withdrawal limit before an operate execution: /// amount of user supply that must stay supplied (not amount that can be withdrawn). /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M /// @param userSupplyData_ user supply data packed uint256 from storage /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and converted from BigMath /// @return currentWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction. /// returned value is in raw for with interest mode, normal amount for interest free mode! function calcWithdrawalLimitBeforeOperate( uint256 userSupplyData_, uint256 userSupply_ ) internal view returns (uint256 currentWithdrawalLimit_) { // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet). // first tx where timestamp is 0 will enter `if (lastWithdrawalLimit_ == 0)` because lastWithdrawalLimit_ is not set yet. // returning max withdrawal allowed, which is not exactly right but doesn't matter because the first interaction must be // a deposit anyway. Important is that it would not revert. // Note the first time a deposit brings the user supply amount to above the base withdrawal limit, the active limit // is the fully expanded limit immediately. // extract last set withdrawal limit uint256 lastWithdrawalLimit_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT) & X64; lastWithdrawalLimit_ = (lastWithdrawalLimit_ >> DEFAULT_EXPONENT_SIZE) << (lastWithdrawalLimit_ & DEFAULT_EXPONENT_MASK); if (lastWithdrawalLimit_ == 0) { // withdrawal limit is not activated. Max withdrawal allowed return 0; } uint256 maxWithdrawableLimit_; uint256 temp_; unchecked { // extract max withdrawable percent of user supply and // calculate maximum withdrawable amount expandPercentage of user supply at full expansion duration elapsed // e.g.: if 10% expandPercentage, meaning 10% is withdrawable after full expandDuration has elapsed. // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible). maxWithdrawableLimit_ = (((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14) * userSupply_) / FOUR_DECIMALS; // time elapsed since last withdrawal limit was set (in seconds) // @dev last process timestamp is guaranteed to exist for withdrawal, as a supply must have happened before. // last timestamp can not be > current timestamp temp_ = block.timestamp - ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP) & X33); } // calculate withdrawable amount of expandPercent that is elapsed of expandDuration. // e.g. if 60% of expandDuration has elapsed, then user should be able to withdraw 6% of user supply, down to 94%. // Note: no explicit check for this needed, it is covered by setting minWithdrawalLimit_ if needed. temp_ = (maxWithdrawableLimit_ * temp_) / // extract expand duration: After this, decrement won't happen (user can withdraw 100% of withdraw limit) ((userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_DURATION) & X24); // expand duration can never be 0 // calculate expanded withdrawal limit: last withdrawal limit - withdrawable amount. // Note: withdrawable amount here can grow bigger than userSupply if timeElapsed is a lot bigger than expandDuration, // which would cause the subtraction `lastWithdrawalLimit_ - withdrawableAmount_` to revert. In that case, set 0 // which will cause minimum (fully expanded) withdrawal limit to be set in lines below. unchecked { // underflow explicitly checked & handled currentWithdrawalLimit_ = lastWithdrawalLimit_ > temp_ ? lastWithdrawalLimit_ - temp_ : 0; // calculate minimum withdrawal limit: minimum amount of user supply that must stay supplied at full expansion. // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_ temp_ = userSupply_ - maxWithdrawableLimit_; } // if withdrawal limit is decreased below minimum then set minimum // (e.g. when more than expandDuration time has elapsed) if (temp_ > currentWithdrawalLimit_) { currentWithdrawalLimit_ = temp_; } } /// @dev calculates withdrawal limit after an operate execution: /// amount of user supply that must stay supplied (not amount that can be withdrawn). /// i.e. if user has supplied 100m and can withdraw 5M, this method returns the 95M, not the withdrawable amount 5M /// @param userSupplyData_ user supply data packed uint256 from storage /// @param userSupply_ current user supply amount already extracted from `userSupplyData_` and added / subtracted with the executed operate amount /// @param newWithdrawalLimit_ current withdrawal limit updated for expansion since last interaction, result from `calcWithdrawalLimitBeforeOperate` /// @return withdrawalLimit_ updated withdrawal limit that should be written to storage. returned value is in /// raw for with interest mode, normal amount for interest free mode! function calcWithdrawalLimitAfterOperate( uint256 userSupplyData_, uint256 userSupply_, uint256 newWithdrawalLimit_ ) internal pure returns (uint256) { // temp_ => base withdrawal limit. below this, maximum withdrawals are allowed uint256 temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT) & X18; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); // if user supply is below base limit then max withdrawals are allowed if (userSupply_ < temp_) { return 0; } // temp_ => withdrawal limit expandPercent (is in 1e2 decimals) temp_ = (userSupplyData_ >> LiquiditySlotsLink.BITS_USER_SUPPLY_EXPAND_PERCENT) & X14; unchecked { // temp_ => minimum withdrawal limit: userSupply - max withdrawable limit (userSupply * expandPercent)) // userSupply_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible). // subtraction can not underflow as maxWithdrawableLimit_ is a percentage amount (<=100%) of userSupply_ temp_ = userSupply_ - ((userSupply_ * temp_) / FOUR_DECIMALS); } // if new (before operation) withdrawal limit is less than minimum limit then set minimum limit. // e.g. can happen on new deposits. withdrawal limit is instantly fully expanded in a scenario where // increased deposit amount outpaces withrawals. if (temp_ > newWithdrawalLimit_) { return temp_; } return newWithdrawalLimit_; } /// @dev calculates borrow limit before an operate execution: /// total amount user borrow can reach (not borrowable amount in current operation). /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M /// @param userBorrowData_ user borrow data packed uint256 from storage /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` /// @return currentBorrowLimit_ current borrow limit updated for expansion since last interaction. returned value is in /// raw for with interest mode, normal amount for interest free mode! function calcBorrowLimitBeforeOperate( uint256 userBorrowData_, uint256 userBorrow_ ) internal view returns (uint256 currentBorrowLimit_) { // @dev must support handling the case where timestamp is 0 (config is set but no interactions yet) -> base limit. // first tx where timestamp is 0 will enter `if (maxExpandedBorrowLimit_ < baseBorrowLimit_)` because `userBorrow_` and thus // `maxExpansionLimit_` and thus `maxExpandedBorrowLimit_` is 0 and `baseBorrowLimit_` can not be 0. // temp_ = extract borrow expand percent (is in 1e2 decimals) uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; uint256 maxExpansionLimit_; uint256 maxExpandedBorrowLimit_; unchecked { // calculate max expansion limit: Max amount limit can expand to since last interaction // userBorrow_ needs to be atleast 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible). maxExpansionLimit_ = ((userBorrow_ * temp_) / FOUR_DECIMALS); // calculate max borrow limit: Max point limit can increase to since last interaction maxExpandedBorrowLimit_ = userBorrow_ + maxExpansionLimit_; } // currentBorrowLimit_ = extract base borrow limit currentBorrowLimit_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18; currentBorrowLimit_ = (currentBorrowLimit_ >> DEFAULT_EXPONENT_SIZE) << (currentBorrowLimit_ & DEFAULT_EXPONENT_MASK); if (maxExpandedBorrowLimit_ < currentBorrowLimit_) { return currentBorrowLimit_; } // time elapsed since last borrow limit was set (in seconds) unchecked { // temp_ = timeElapsed_ (last timestamp can not be > current timestamp) temp_ = block.timestamp - ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP) & X33); // extract last update timestamp } // currentBorrowLimit_ = expandedBorrowableAmount + extract last set borrow limit currentBorrowLimit_ = // calculate borrow limit expansion since last interaction for `expandPercent` that is elapsed of `expandDuration`. // divisor is extract expand duration (after this, full expansion to expandPercentage happened). ((maxExpansionLimit_ * temp_) / ((userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_DURATION) & X24)) + // expand duration can never be 0 // extract last set borrow limit BigMathMinified.fromBigNumber( (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT) & X64, DEFAULT_EXPONENT_SIZE, DEFAULT_EXPONENT_MASK ); // if timeElapsed is bigger than expandDuration, new borrow limit would be > max expansion, // so set to `maxExpandedBorrowLimit_` in that case. // also covers the case where last process timestamp = 0 (timeElapsed would simply be very big) if (currentBorrowLimit_ > maxExpandedBorrowLimit_) { currentBorrowLimit_ = maxExpandedBorrowLimit_; } // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above) temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); if (currentBorrowLimit_ > temp_) { currentBorrowLimit_ = temp_; } } /// @dev calculates borrow limit after an operate execution: /// total amount user borrow can reach (not borrowable amount in current operation). /// i.e. if user has borrowed 50M and can still borrow 5M, this method returns the total 55M, not the borrowable amount 5M /// @param userBorrowData_ user borrow data packed uint256 from storage /// @param userBorrow_ current user borrow amount already extracted from `userBorrowData_` and added / subtracted with the executed operate amount /// @param newBorrowLimit_ current borrow limit updated for expansion since last interaction, result from `calcBorrowLimitBeforeOperate` /// @return borrowLimit_ updated borrow limit that should be written to storage. /// returned value is in raw for with interest mode, normal amount for interest free mode! function calcBorrowLimitAfterOperate( uint256 userBorrowData_, uint256 userBorrow_, uint256 newBorrowLimit_ ) internal pure returns (uint256 borrowLimit_) { // temp_ = extract borrow expand percent uint256 temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_EXPAND_PERCENT) & X14; // (is in 1e2 decimals) unchecked { // borrowLimit_ = calculate maximum borrow limit at full expansion. // userBorrow_ needs to be at least 1e73 to overflow max limit of ~1e77 in uint256 (no token in existence where this is possible). borrowLimit_ = userBorrow_ + ((userBorrow_ * temp_) / FOUR_DECIMALS); } // temp_ = extract base borrow limit temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_BASE_BORROW_LIMIT) & X18; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); if (borrowLimit_ < temp_) { // below base limit, borrow limit is always base limit return temp_; } // temp_ = extract hard max borrow limit. Above this user can never borrow (not expandable above) temp_ = (userBorrowData_ >> LiquiditySlotsLink.BITS_USER_BORROW_MAX_BORROW_LIMIT) & X18; temp_ = (temp_ >> DEFAULT_EXPONENT_SIZE) << (temp_ & DEFAULT_EXPONENT_MASK); // make sure fully expanded borrow limit is not above hard max borrow limit if (borrowLimit_ > temp_) { borrowLimit_ = temp_; } // if new borrow limit (from before operate) is > max borrow limit, set max borrow limit. // (e.g. on a repay shrinking instantly to fully expanded borrow limit from new borrow amount. shrinking is instant) if (newBorrowLimit_ > borrowLimit_) { return borrowLimit_; } return newBorrowLimit_; } /////////////////////////////////////////////////////////////////////////// ////////// CALC RATES ///////// /////////////////////////////////////////////////////////////////////////// /// @dev Calculates new borrow rate from utilization for a token /// @param rateData_ rate data packed uint256 from storage for the token /// @param utilization_ totalBorrow / totalSupply. 1e4 = 100% utilization /// @return rate_ rate for that particular token in 1e2 precision (e.g. 5% rate = 500) function calcBorrowRateFromUtilization(uint256 rateData_, uint256 utilization_) internal returns (uint256 rate_) { // extract rate version: 4 bits (0xF) starting from bit 0 uint256 rateVersion_ = (rateData_ & 0xF); if (rateVersion_ == 1) { rate_ = calcRateV1(rateData_, utilization_); } else if (rateVersion_ == 2) { rate_ = calcRateV2(rateData_, utilization_); } else { revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__UnsupportedRateVersion); } if (rate_ > X16) { // hard cap for borrow rate at maximum value 16 bits (65535) to make sure it does not overflow storage space. // this is unlikely to ever happen if configs stay within expected levels. rate_ = X16; // emit event to more easily become aware emit BorrowRateMaxCap(); } } /// @dev calculates the borrow rate based on utilization for rate data version 1 (with one kink) in 1e2 precision /// @param rateData_ rate data packed uint256 from storage for the token /// @param utilization_ in 1e2 (100% = 1e4) /// @return rate_ rate in 1e2 precision function calcRateV1(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) { /// For rate v1 (one kink) ------------------------------------------------------ /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 52- 67 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Last 188 bits => 68-255 => blank, might come in use in future // y = mx + c. // y is borrow rate // x is utilization // m = slope (m can also be negative for declining rates) // c is constant (c can be negative) uint256 y1_; uint256 y2_; uint256 x1_; uint256 x2_; // extract kink1: 16 bits (0xFFFF) starting from bit 20 // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_UTILIZATION_AT_KINK) & X16; if (utilization_ < kink1_) { // if utilization is less than kink y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16; x1_ = 0; // 0% x2_ = kink1_; } else { // else utilization is greater than kink y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX) & X16; x1_ = kink1_; x2_ = FOUR_DECIMALS; // 100% } int256 constant_; int256 slope_; unchecked { // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1). // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor) // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_)); // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx. // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256 // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12; // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256 // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256 constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_)); // calculating new borrow rate // - slope_ max value is 65535 * 1e12, // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply) // - constant max value is 65535 * 1e12 // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256 // divisor TWELVE_DECIMALS can not be 0 slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings if (slope_ < 0) { revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative); } rate_ = uint256(slope_) / TWELVE_DECIMALS; } } /// @dev calculates the borrow rate based on utilization for rate data version 2 (with two kinks) in 1e4 precision /// @param rateData_ rate data packed uint256 from storage for the token /// @param utilization_ in 1e2 (100% = 1e4) /// @return rate_ rate in 1e4 precision function calcRateV2(uint256 rateData_, uint256 utilization_) internal pure returns (uint256 rate_) { /// For rate v2 (two kinks) ----------------------------------------------------- /// Next 16 bits => 4 - 19 => Rate at utilization 0% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 20- 35 => Utilization at kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 36- 51 => Rate at utilization kink1 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 52- 67 => Utilization at kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 68- 83 => Rate at utilization kink2 (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Next 16 bits => 84- 99 => Rate at utilization 100% (in 1e2: 100% = 10_000; 1% = 100 -> max value 65535) /// Last 156 bits => 100-255 => blank, might come in use in future // y = mx + c. // y is borrow rate // x is utilization // m = slope (m can also be negative for declining rates) // c is constant (c can be negative) uint256 y1_; uint256 y2_; uint256 x1_; uint256 x2_; // extract kink1: 16 bits (0xFFFF) starting from bit 20 // kink is in 1e2, same as utilization, so no conversion needed for direct comparison of the two uint256 kink1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1) & X16; if (utilization_ < kink1_) { // if utilization is less than kink1 y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16; x1_ = 0; // 0% x2_ = kink1_; } else { // extract kink2: 16 bits (0xFFFF) starting from bit 52 uint256 kink2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2) & X16; if (utilization_ < kink2_) { // if utilization is less than kink2 y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16; x1_ = kink1_; x2_ = kink2_; } else { // else utilization is greater than kink2 y1_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2) & X16; y2_ = (rateData_ >> LiquiditySlotsLink.BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX) & X16; x1_ = kink2_; x2_ = FOUR_DECIMALS; } } int256 constant_; int256 slope_; unchecked { // calculating slope with twelve decimal precision. m = (y2 - y1) / (x2 - x1). // utilization of x2 can not be <= utilization of x1 (so no underflow or 0 divisor) // y is in 1e2 so can not overflow when multiplied with TWELVE_DECIMALS slope_ = (int256(y2_ - y1_) * int256(TWELVE_DECIMALS)) / int256((x2_ - x1_)); // calculating constant at 12 decimal precision. slope is already in 12 decimal hence only multiple with y1. c = y - mx. // maximum y1_ value is 65535. 65535 * 1e12 can not overflow int256 // maximum slope is 65535 - 0 * TWELVE_DECIMALS / 1 = 65535 * 1e12; // maximum x1_ is 100% (9_999 actually) => slope_ * x1_ can not overflow int256 // subtraction most extreme case would be 0 - max value slope_ * x1_ => can not underflow int256 constant_ = int256(y1_ * TWELVE_DECIMALS) - (slope_ * int256(x1_)); // calculating new borrow rate // - slope_ max value is 65535 * 1e12, // - utilization max value is let's say 500% (extreme case where borrow rate increases borrow amount without new supply) // - constant max value is 65535 * 1e12 // so max values are 65535 * 1e12 * 50_000 + 65535 * 1e12 -> 3.2768*10^21, which easily fits int256 // divisor TWELVE_DECIMALS can not be 0 slope_ = (slope_ * int256(utilization_)) + constant_; // reusing `slope_` as variable for gas savings if (slope_ < 0) { revert FluidLiquidityCalcsError(ErrorTypes.LiquidityCalcs__BorrowRateNegative); } rate_ = uint256(slope_) / TWELVE_DECIMALS; } } /// @dev reads the total supply out of Liquidity packed storage `totalAmounts_` for `supplyExchangePrice_` function getTotalSupply( uint256 totalAmounts_, uint256 supplyExchangePrice_ ) internal pure returns (uint256 totalSupply_) { // totalSupply_ => supplyInterestFree totalSupply_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE) & X64; totalSupply_ = (totalSupply_ >> DEFAULT_EXPONENT_SIZE) << (totalSupply_ & DEFAULT_EXPONENT_MASK); uint256 totalSupplyRaw_ = totalAmounts_ & X64; // no shifting as supplyRaw is first 64 bits totalSupplyRaw_ = (totalSupplyRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalSupplyRaw_ & DEFAULT_EXPONENT_MASK); // totalSupply = supplyInterestFree + supplyRawInterest normalized from raw totalSupply_ += ((totalSupplyRaw_ * supplyExchangePrice_) / EXCHANGE_PRICES_PRECISION); } /// @dev reads the total borrow out of Liquidity packed storage `totalAmounts_` for `borrowExchangePrice_` function getTotalBorrow( uint256 totalAmounts_, uint256 borrowExchangePrice_ ) internal pure returns (uint256 totalBorrow_) { // totalBorrow_ => borrowInterestFree // no & mask needed for borrow interest free as it occupies the last bits in the storage slot totalBorrow_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE); totalBorrow_ = (totalBorrow_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrow_ & DEFAULT_EXPONENT_MASK); uint256 totalBorrowRaw_ = (totalAmounts_ >> LiquiditySlotsLink.BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST) & X64; totalBorrowRaw_ = (totalBorrowRaw_ >> DEFAULT_EXPONENT_SIZE) << (totalBorrowRaw_ & DEFAULT_EXPONENT_MASK); // totalBorrow = borrowInterestFree + borrowRawInterest normalized from raw totalBorrow_ += ((totalBorrowRaw_ * borrowExchangePrice_) / EXCHANGE_PRICES_PRECISION); } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity 0.8.21; /// @notice library that helps in reading / working with storage slot data of Fluid Liquidity. /// @dev as all data for Fluid Liquidity is internal, any data must be fetched directly through manual /// slot reading through this library or, if gas usage is less important, through the FluidLiquidityResolver. library LiquiditySlotsLink { /// @dev storage slot for status at Liquidity uint256 internal constant LIQUIDITY_STATUS_SLOT = 1; /// @dev storage slot for auths mapping at Liquidity uint256 internal constant LIQUIDITY_AUTHS_MAPPING_SLOT = 2; /// @dev storage slot for guardians mapping at Liquidity uint256 internal constant LIQUIDITY_GUARDIANS_MAPPING_SLOT = 3; /// @dev storage slot for user class mapping at Liquidity uint256 internal constant LIQUIDITY_USER_CLASS_MAPPING_SLOT = 4; /// @dev storage slot for exchangePricesAndConfig mapping at Liquidity uint256 internal constant LIQUIDITY_EXCHANGE_PRICES_MAPPING_SLOT = 5; /// @dev storage slot for rateData mapping at Liquidity uint256 internal constant LIQUIDITY_RATE_DATA_MAPPING_SLOT = 6; /// @dev storage slot for totalAmounts mapping at Liquidity uint256 internal constant LIQUIDITY_TOTAL_AMOUNTS_MAPPING_SLOT = 7; /// @dev storage slot for user supply double mapping at Liquidity uint256 internal constant LIQUIDITY_USER_SUPPLY_DOUBLE_MAPPING_SLOT = 8; /// @dev storage slot for user borrow double mapping at Liquidity uint256 internal constant LIQUIDITY_USER_BORROW_DOUBLE_MAPPING_SLOT = 9; /// @dev storage slot for listed tokens array at Liquidity uint256 internal constant LIQUIDITY_LISTED_TOKENS_ARRAY_SLOT = 10; // -------------------------------- // @dev stacked uint256 storage slots bits position data for each: // ExchangePricesAndConfig uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATE = 0; uint256 internal constant BITS_EXCHANGE_PRICES_FEE = 16; uint256 internal constant BITS_EXCHANGE_PRICES_UTILIZATION = 30; uint256 internal constant BITS_EXCHANGE_PRICES_UPDATE_THRESHOLD = 44; uint256 internal constant BITS_EXCHANGE_PRICES_LAST_TIMESTAMP = 58; uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_EXCHANGE_PRICE = 91; uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_EXCHANGE_PRICE = 155; uint256 internal constant BITS_EXCHANGE_PRICES_SUPPLY_RATIO = 219; uint256 internal constant BITS_EXCHANGE_PRICES_BORROW_RATIO = 234; // RateData: uint256 internal constant BITS_RATE_DATA_VERSION = 0; // RateData: V1 uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_ZERO = 4; uint256 internal constant BITS_RATE_DATA_V1_UTILIZATION_AT_KINK = 20; uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_KINK = 36; uint256 internal constant BITS_RATE_DATA_V1_RATE_AT_UTILIZATION_MAX = 52; // RateData: V2 uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_ZERO = 4; uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK1 = 20; uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK1 = 36; uint256 internal constant BITS_RATE_DATA_V2_UTILIZATION_AT_KINK2 = 52; uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_KINK2 = 68; uint256 internal constant BITS_RATE_DATA_V2_RATE_AT_UTILIZATION_MAX = 84; // TotalAmounts uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_WITH_INTEREST = 0; uint256 internal constant BITS_TOTAL_AMOUNTS_SUPPLY_INTEREST_FREE = 64; uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_WITH_INTEREST = 128; uint256 internal constant BITS_TOTAL_AMOUNTS_BORROW_INTEREST_FREE = 192; // UserSupplyData uint256 internal constant BITS_USER_SUPPLY_MODE = 0; uint256 internal constant BITS_USER_SUPPLY_AMOUNT = 1; uint256 internal constant BITS_USER_SUPPLY_PREVIOUS_WITHDRAWAL_LIMIT = 65; uint256 internal constant BITS_USER_SUPPLY_LAST_UPDATE_TIMESTAMP = 129; uint256 internal constant BITS_USER_SUPPLY_EXPAND_PERCENT = 162; uint256 internal constant BITS_USER_SUPPLY_EXPAND_DURATION = 176; uint256 internal constant BITS_USER_SUPPLY_BASE_WITHDRAWAL_LIMIT = 200; uint256 internal constant BITS_USER_SUPPLY_IS_PAUSED = 255; // UserBorrowData uint256 internal constant BITS_USER_BORROW_MODE = 0; uint256 internal constant BITS_USER_BORROW_AMOUNT = 1; uint256 internal constant BITS_USER_BORROW_PREVIOUS_BORROW_LIMIT = 65; uint256 internal constant BITS_USER_BORROW_LAST_UPDATE_TIMESTAMP = 129; uint256 internal constant BITS_USER_BORROW_EXPAND_PERCENT = 162; uint256 internal constant BITS_USER_BORROW_EXPAND_DURATION = 176; uint256 internal constant BITS_USER_BORROW_BASE_BORROW_LIMIT = 200; uint256 internal constant BITS_USER_BORROW_MAX_BORROW_LIMIT = 218; uint256 internal constant BITS_USER_BORROW_IS_PAUSED = 255; // -------------------------------- /// @notice Calculating the slot ID for Liquidity contract for single mapping at `slot_` for `key_` function calculateMappingStorageSlot(uint256 slot_, address key_) internal pure returns (bytes32) { return keccak256(abi.encode(key_, slot_)); } /// @notice Calculating the slot ID for Liquidity contract for double mapping at `slot_` for `key1_` and `key2_` function calculateDoubleMappingStorageSlot( uint256 slot_, address key1_, address key2_ ) internal pure returns (bytes32) { bytes32 intermediateSlot_ = keccak256(abi.encode(key1_, slot_)); return keccak256(abi.encode(key2_, intermediateSlot_)); } }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"errorId_","type":"uint256"}],"name":"FluidLiquidityCalcsError","type":"error"},{"inputs":[],"name":"ADDRESS_THIS","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEX_FACTORY","outputs":[{"internalType":"contract FluidDexFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FLUID_RESERVE","outputs":[{"internalType":"contract IFluidReserveContract","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GOVERNOR","outputs":[{"internalType":"contract IGovernorBravo","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQUIDITY","outputs":[{"internalType":"contract IFluidLiquidityAdmin","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROPOSAL_ID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROPOSER","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROPOSER_AVO_MULTISIG","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROPOSER_AVO_MULTISIG_2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PROPOSER_AVO_MULTISIG_3","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TEAM_MULTISIG","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TIMELOCK","outputs":[{"internalType":"contract ITimelock","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"VAULT_FACTORY","outputs":[{"internalType":"contract FluidVaultFactory","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"execute","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"dexId_","type":"uint256"}],"name":"getDexAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"amountInUSD","type":"uint256"},{"internalType":"bool","name":"isSupply","type":"bool"}],"name":"getRawAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultId_","type":"uint256"}],"name":"getVaultAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"description","type":"string"}],"name":"propose","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"verifyProposal","outputs":[],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a060405234801561001057600080fd5b5030608052608051612a9c610037600039600081816102ca0152610a630152612a9c6000f3fe608060405234801561001057600080fd5b50600436106101215760003560e01c80637aadef8b116100ad578063b696649511610071578063b696649514610274578063b788f3a11461028f578063bffa7f0f146102aa578063cc025f7c146102c5578063e6bd26a2146102ec57600080fd5b80637aadef8b146101fd5780637e2f35fa1461021857806397b87b4a146102335780639d3d2a781461024e578063aa98df391461026157600080fd5b80632861c7d1116100f45780632861c7d1146101a2578063588c77e6146101bd57806361461954146101d857806366760d7d146101e05780636dc0ae22146101e257600080fd5b80630bc9136e14610126578063103f29071461014157806312e366aa1461017457806325ad7f4d14610187575b600080fd5b61012e603081565b6040519081526020015b60405180910390f35b61015c73324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d81565b6040516001600160a01b039091168152602001610138565b61015c610182366004612375565b6102ff565b61015c739efde135ca4832abf0408c44c6f5f370eb0f35e881565b61015c7352aa899454998be5b000ad077a46bbe360f4e49781565b61015c735c43aac965ff230ac1cf63e924d0153291d78bad81565b6101e061037d565b005b61015c730204cd037b2ec03605cfdfe482d8e257c765fa1b81565b61015c732386dc45added673317ef068992f19421b481f4c81565b61015c73059a94a72451c0ae1cc1ce4bf0db52421bbe821081565b61015c7391716c4eda1fb55e84bf8b4c7085f84285c1908581565b61012e61025c3660046123a3565b61040a565b6101e061026f366004612408565b6107e1565b61015c73264786ef916af64a1db19f513f24a3681734ce9281565b61015c734f6f977acdd1177dcd81ab83074855ecb9c2d49e81565b61015c73a45f7bd6a5ff45d31aace6bcd3d426d9328cea0181565b61015c7f000000000000000000000000000000000000000000000000000000000000000081565b61015c6102fa366004612375565b610b8e565b604051630971b35560e11b8152600481018290526000907391716c4eda1fb55e84bf8b4c7085f84285c19085906312e366aa906024015b602060405180830381865afa158015610353573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061037791906124b9565b92915050565b30732386dc45added673317ef068992f19421b481f4c146103d85760405162461bcd60e51b815260206004820152601060248201526f3737ba16bb30b634b216b1b0b63632b960811b60448201526064015b60405180910390fd5b6103e0610bc9565b6103e8610eb2565b6103f0610f41565b6103f8611164565b61040061140f565b610408611b59565b565b6000808411801561041b5750600083115b156104685760405162461bcd60e51b815260206004820181905260248201527f626f74682075736420616e6420616d6f756e7420617265206e6f74207a65726f60448201526064016103cf565b604080516001600160a01b03871660208083019190915260058284015282518083038401815260608301938490528051910120632d71cdb960e21b90925260648101919091526000907352aa899454998be5b000ad077a46bbe360f4e4979063b5c736e490608401602060405180830381865afa1580156104ed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061051191906124dd565b905060008061051f83611d6e565b90925090506000601273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038b160161055b57506203d09090506012610761565b737f39c581f595b53c5cb19bd0b3f8da6c935e2c9f196001600160a01b038b160161058e57506204882890506012610761565b73cd5fe23c85820f7b72d0926fc9b05b43e359b7ed196001600160a01b038b16016105c1575062040b2890506012610761565b6001600160a01b038a1673cbb7c0000ab88b473b1f5afd9ef808440eed33bf148061060857506001600160a01b038a16732260fac5e5542a773aa44fbcfedf7c193bc2c599145b1561061b5750626760d890506008610761565b6001600160a01b038a1673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48148061066257506001600160a01b038a1673dac17f958d2ee523a2206206994597c13d831ec7145b156106735750606490506006610761565b739d39a5de30e57443bff2a8307a4256c8797a3496196001600160a01b038b16016106a45750606f90506012610761565b73a3931d71877c0e7a3148cb7eb4463524fec27fbc196001600160a01b038b16016106d55750607090506012610761565b6001600160a01b038a167340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f148061071c57506001600160a01b038a16734c9edd5852cd905f086c759e8383e09bff1e68b3145b1561072d5750606490506012610761565b60405162461bcd60e51b81526020600482015260096024820152681b9bdd0b599bdd5b9960ba1b60448201526064016103cf565b60008761076e5783610770565b845b9050891561079f57806107888b64e8d4a5100061250c565b6107929190612539565b96505050505050506107d9565b60646107ab828561250c565b6107b59190612539565b6107c083600a61263f565b6107cf8b64e8d4a5100061250c565b610788919061250c565b949350505050565b3373a45f7bd6a5ff45d31aace6bcd3d426d9328cea011480610816575033734f6f977acdd1177dcd81ab83074855ecb9c2d49e145b8061083457503073059a94a72451c0ae1cc1ce4bf0db52421bbe8210145b80610852575030739efde135ca4832abf0408c44c6f5f370eb0f35e8145b8061085b575060015b6108a05760405162461bcd60e51b81526020600482015260166024820152751b5cd9cb9cd95b99195c8b5b9bdd0b585b1b1bddd95960521b60448201526064016103cf565b604080516001808252818301909252600090826020808301908036833701905050905060008267ffffffffffffffff8111156108de576108de6123f2565b604051908082528060200260200182016040528015610907578160200160208202803683370190505b50905060008367ffffffffffffffff811115610925576109256123f2565b60405190808252806020026020018201604052801561095857816020015b60608152602001906001900390816109435790505b50905060008467ffffffffffffffff811115610976576109766123f2565b6040519080825280602002602001820160405280156109a957816020015b60608152602001906001900390816109945790505b509050732386dc45added673317ef068992f19421b481f4c846000815181106109d4576109d461264b565b60200260200101906001600160a01b031690816001600160a01b031681525050600083600081518110610a0957610a0961264b565b602002602001018181525050604051806060016040528060248152602001612a436024913982600081518110610a4157610a4161264b565b60209081029190910181019190915260408051600081529182018152610a8a917f00000000000000000000000000000000000000000000000000000000000000009181016126a7565b60405160208183030381529060405281600081518110610aac57610aac61264b565b6020908102919091010152604051636d4ab48d60e11b8152600090730204cd037b2ec03605cfdfe482d8e257c765fa1b9063da95691a90610af99088908890889088908e906004016127b1565b6020604051808303816000875af1158015610b18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3c91906124dd565b905060308114610b855760405162461bcd60e51b815260206004820152601460248201527350524f504f53414c5f49535f4e4f545f53414d4560601b60448201526064016103cf565b50505050505050565b60405163735e935160e11b81526004810182905260009073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d9063e6bd26a290602401610336565b604080516001808252818301909252600091816020015b610c1b6040518060a0016040528060006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b815260200190600190039081610be05790505090506040518060a00160405280734c9edd5852cd905f086c759e8383e09bff1e68b36001600160a01b031681526020016124548152602001600081526020016102ee81526020016109c481525081600081518110610c8e57610c8e61264b565b6020908102919091010152604051630feb5dff60e21b81527352aa899454998be5b000ad077a46bbe360f4e49790633fad77fc90610cd0908490600401612860565b600060405180830381600087803b158015610cea57600080fd5b505af1158015610cfe573d6000803e3d6000fd5b505050505073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d6001600160a01b0316637faa1d21610d30603d610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60006040518463ffffffff1660e01b8152600401610d65939291906128ca565b600060405180830381600087803b158015610d7f57600080fd5b505af1158015610d93573d6000803e3d6000fd5b50505050306001600160a01b03167391716c4eda1fb55e84bf8b4c7085f84285c190856001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1791906124b9565b6001600160a01b031603610408577391716c4eda1fb55e84bf8b4c7085f84285c190856378c7e138610e4960046102ff565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60006040518463ffffffff1660e01b8152600401610e7e939291906128ca565b600060405180830381600087803b158015610e9857600080fd5b505af1158015610eac573d6000803e3d6000fd5b50505050565b6000604051806101000160405280610eca60046102ff565b6001600160a01b031681527340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f602082015273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4860408201526001606082018190526080820152627270e060a0820152624c4b4060c0820152625b8d8060e0909101529050610f3e81611f63565b50565b6000604051806101000160405280610f5960056102ff565b6001600160a01b0316815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee602082015273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486040820152600160608201819052608082015261c35060a08201819052619c4060c083015260e0909101529050610fc981611f63565b506000604051806101000160405280610fe260066102ff565b6001600160a01b03168152732260fac5e5542a773aa44fbcfedf7c193bc2c599602082015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6040820152600160608201819052608082015261c35060a08201819052619c4060c083015260e090910152905061105281611f63565b50600060405180610100016040528061106b60076102ff565b6001600160a01b0316815273cbb7c0000ab88b473b1f5afd9ef808440eed33bf602082015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6040820152600160608201819052608082015261c35060a08201819052619c4060c083015260e09091015290506110db81611f63565b5060006040518061010001604052806110f460086102ff565b6001600160a01b03168152734c9edd5852cd905f086c759e8383e09bff1e68b3602082015273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486040820152600160608201819052608082015261c35060a08201819052619c4060c083015260e0909101529050610f3e81611f63565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337505060408051600180825281830190925292935060009291506020808301908036833701905050604051635fd6196560e01b815273e85eb0acb7281faf00810d167c7de14bb070b4806004820152909150736a29a46e21c730dca1d8b23d637c101cec605c5b90635fd6196590602401600060405180830381600087803b15801561122d57600080fd5b505af1158015611241573d6000803e3d6000fd5b5050604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce926004820152736a29a46e21c730dca1d8b23d637c101cec605c5b6024820152600092507340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f915063dd62ed3e90604401602060405180830381865afa1580156112c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e991906124dd565b9050736a29a46e21c730dca1d8b23d637c101cec605c5b846000815181106113135761131361264b565b60200260200101906001600160a01b031690816001600160a01b0316815250507340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f8360008151811061135b5761135b61264b565b6001600160a01b039092166020928302919091019091015261138781690f7556828d6c7ba000006128ee565b8260008151811061139a5761139a61264b565b6020908102919091010152506040516303e8046160e01b815273264786ef916af64a1db19f513f24a3681734ce92906303e80461906113e190869086908690600401612901565b600060405180830381600087803b1580156113fb57600080fd5b505af1158015610b85573d6000803e3d6000fd5b60408051600680825260e082019092526000916020820160c080368337505060408051600680825260e0820190925292935060009291506020820160c080368337505060408051600680825260e0820190925292935060009291506020820160c08036833701905050905060006114866015610b8e565b905060006114946016610b8e565b905060006114a2601d610b8e565b905060006114b0601e610b8e565b905083876000815181106114c6576114c661264b565b60200260200101906001600160a01b031690816001600160a01b031681525050732260fac5e5542a773aa44fbcfedf7c193bc2c5998660008151811061150e5761150e61264b565b60200260200101906001600160a01b031690816001600160a01b031681525050620249f0856000815181106115455761154561264b565b60200260200101818152505082876001815181106115655761156561264b565b60200260200101906001600160a01b031690816001600160a01b031681525050732260fac5e5542a773aa44fbcfedf7c193bc2c599866001815181106115ad576115ad61264b565b60200260200101906001600160a01b031690816001600160a01b031681525050620249f0856001815181106115e4576115e461264b565b6020908102919091010152604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce9260048201526001600160a01b038516602482015260009073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489063dd62ed3e90604401602060405180830381865afa158015611664573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168891906124dd565b9050848860028151811061169e5761169e61264b565b60200260200101906001600160a01b031690816001600160a01b03168152505073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48876002815181106116e6576116e661264b565b6001600160a01b039092166020928302919091019091015261170d8164060db884006128ee565b866002815181106117205761172061264b565b602090810291909101015250604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce9260048201526001600160a01b038416602482015260009073dac17f958d2ee523a2206206994597c13d831ec79063dd62ed3e90604401602060405180830381865afa1580156117a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c591906124dd565b905083886003815181106117db576117db61264b565b60200260200101906001600160a01b031690816001600160a01b03168152505073dac17f958d2ee523a2206206994597c13d831ec7876003815181106118235761182361264b565b6001600160a01b039092166020928302919091019091015261184a8164060db884006128ee565b8660038151811061185d5761185d61264b565b602090810291909101015250604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce9260048201526001600160a01b038316602482015260009073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489063dd62ed3e90604401602060405180830381865afa1580156118de573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061190291906124dd565b905082886004815181106119185761191861264b565b60200260200101906001600160a01b031690816001600160a01b03168152505073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48876004815181106119605761196061264b565b6001600160a01b03909216602092830291909101909101526119878164060db884006128ee565b8660048151811061199a5761199a61264b565b602090810291909101015250604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce9260048201526001600160a01b038216602482015260009073dac17f958d2ee523a2206206994597c13d831ec79063dd62ed3e90604401602060405180830381865afa158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3f91906124dd565b90508188600581518110611a5557611a5561264b565b60200260200101906001600160a01b031690816001600160a01b03168152505073dac17f958d2ee523a2206206994597c13d831ec787600581518110611a9d57611a9d61264b565b6001600160a01b0390921660209283029190910190910152611ac48164060db884006128ee565b86600581518110611ad757611ad761264b565b6020908102919091010152506040516303e8046160e01b815273264786ef916af64a1db19f513f24a3681734ce92906303e8046190611b1e908a908a908a90600401612901565b600060405180830381600087803b158015611b3857600080fd5b505af1158015611b4c573d6000803e3d6000fd5b5050505050505050505050565b73324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d637faa1d21611b7d603e610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60016040518463ffffffff1660e01b8152600401611bb2939291906128ca565b600060405180830381600087803b158015611bcc57600080fd5b505af1158015611be0573d6000803e3d6000fd5b5050505073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d6001600160a01b0316637faa1d21611c11603f610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60016040518463ffffffff1660e01b8152600401611c46939291906128ca565b600060405180830381600087803b158015611c6057600080fd5b505af1158015611c74573d6000803e3d6000fd5b5050505073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d6001600160a01b0316637faa1d21611ca56040610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60016040518463ffffffff1660e01b8152600401611cda939291906128ca565b600060405180830381600087803b158015611cf457600080fd5b505af1158015611d08573d6000803e3d6000fd5b5050505073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d6001600160a01b0316637faa1d21611d396041610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60016040518463ffffffff1660e01b8152600401610e7e939291906128ca565b67ffffffffffffffff605b82901c811690609b83901c16811580611d90575080155b15611db357604051636a86ba8960e11b81526201117160048201526024016103cf565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480611ddd575082155b80611de85750806001145b15611df557505050915091565b64496cebb80084840283020484019350617fff60db87901c16925082600103611e2057505050915091565b82600116600103611e755760019290921c91826c7e37be2022c0914b268000000081611e4e57611e4e612523565b049250612710601e87901c613fff166b033b2e3c9fd0803ce8000000850102049250611ea2565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b80600116600103611ed95760011c61271081016b033b2e3c9fd0803ce8000000820281611ed157611ed1612523565b049050611f0f565b60011c61271081016b033b2e3c9fd0803ce8000000820281611efd57611efd612523565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b80606001511561200e5760006040518060a0016040528083600001516001600160a01b0316815260200183602001516001600160a01b031681526020016113888152602001610e1081526020018360a001518152509050611fc3816120cf565b6040805160a0808201835284516001600160a01b0390811683528584015116602083015261138892820192909252610e10606082015290830151608082015261200b816120cf565b50505b806080015115610f3e5760006040518060c0016040528083600001516001600160a01b0316815260200183602001516001600160a01b031681526020016113888152602001610e1081526020018360c0015181526020018360e0015181525090506120788161221d565b6040805160c0808201835284516001600160a01b0390811683528584015116602083015261138892820192909252610e10606082015290830151608082015260e083015160a08201526120ca8161221d565b505050565b604080516001808252818301909252600091816020015b6040805160c08101825260008082526020808301829052928201819052606082018190526080820181905260a082015282526000199092019101816120e65790505090506040518060c0016040528083600001516001600160a01b0316815260200183602001516001600160a01b03168152602001600160ff168152602001836040015181526020018360600151815260200161218f846020015160008660800151600161040a565b815250816000815181106121a5576121a561264b565b60209081029190910101526040516301635db560e11b81527352aa899454998be5b000ad077a46bbe360f4e497906302c6bb6a906121e7908490600401612944565b600060405180830381600087803b15801561220157600080fd5b505af1158015612215573d6000803e3d6000fd5b505050505050565b604080516001808252818301909252600091816020015b6122896040518060e0016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600060ff168152602001600081526020016000815260200160008152602001600081525090565b8152602001906001900390816122345790505090506040518060e0016040528083600001516001600160a01b0316815260200183602001516001600160a01b03168152602001600160ff1681526020018360400151815260200183606001518152602001612303846020015160008660800151600061040a565b815260200161231e846020015160008660a00151600061040a565b815250816000815181106123345761233461264b565b602090810291909101015260405162dc47c360e11b81527352aa899454998be5b000ad077a46bbe360f4e497906301b88f86906121e79084906004016129be565b60006020828403121561238757600080fd5b5035919050565b6001600160a01b0381168114610f3e57600080fd5b600080600080608085870312156123b957600080fd5b84356123c48161238e565b93506020850135925060408501359150606085013580151581146123e757600080fd5b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561241a57600080fd5b813567ffffffffffffffff8082111561243257600080fd5b818401915084601f83011261244657600080fd5b813581811115612458576124586123f2565b604051601f8201601f19908116603f01168101908382118183101715612480576124806123f2565b8160405282815287602084870101111561249957600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000602082840312156124cb57600080fd5b81516124d68161238e565b9392505050565b6000602082840312156124ef57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610377576103776124f6565b634e487b7160e01b600052601260045260246000fd5b60008261255657634e487b7160e01b600052601260045260246000fd5b500490565b600181815b8085111561259657816000190482111561257c5761257c6124f6565b8085161561258957918102915b93841c9390800290612560565b509250929050565b6000826125ad57506001610377565b816125ba57506000610377565b81600181146125d057600281146125da576125f6565b6001915050610377565b60ff8411156125eb576125eb6124f6565b50506001821b610377565b5060208310610133831016604e8410600b8410161715612619575081810a610377565b612623838361255b565b8060001904821115612637576126376124f6565b029392505050565b60006124d6838361259e565b634e487b7160e01b600052603260045260246000fd5b6000815180845260005b818110156126875760208185018101518683018201520161266b565b506000602082860101526020601f19601f83011685010191505092915050565b60018060a01b038316815260606020820152600960608201526865786563757465282960b81b608082015260a0604082015260006107d960a0830184612661565b600081518084526020808501945080840160005b838110156127215781516001600160a01b0316875295820195908201906001016126fc565b509495945050505050565b600081518084526020808501945080840160005b8381101561272157815187529582019590820190600101612740565b600081518084526020808501808196508360051b8101915082860160005b858110156127a4578284038952612792848351612661565b9885019893509084019060010161277a565b5091979650505050505050565b60a0815260006127c460a08301886126e8565b6020838203818501526127d7828961272c565b915083820360408501528187518084528284019150828160051b850101838a0160005b8381101561282857601f19878403018552612816838351612661565b948601949250908501906001016127fa565b5050868103606088015261283c818a61275c565b94505050505082810360808401526128548185612661565b98975050505050505050565b602080825282518282018190526000919060409081850190868401855b828110156127a457815180516001600160a01b0316855286810151878601528581015186860152606080820151908601526080908101519085015260a0909301929085019060010161287d565b6001600160a01b039384168152919092166020820152901515604082015260600190565b80820180821115610377576103776124f6565b60608152600061291460608301866126e8565b828103602084015261292681866126e8565b9050828103604084015261293a818561272c565b9695505050505050565b602080825282518282018190526000919060409081850190868401855b828110156127a457815180516001600160a01b0390811686528782015116878601528581015160ff1686860152606080820151908601526080808201519086015260a0908101519085015260c09093019290850190600101612961565b602080825282518282018190526000919060409081850190868401855b828110156127a457815180516001600160a01b0390811686528782015116878601528581015160ff1686860152606080820151908601526080808201519086015260a0808201519086015260c0908101519085015260e090930192908501906001016129db56fe657865637574655061796c6f616428616464726573732c737472696e672c627974657329a2646970667358221220637aa95cde9bde1a825b9d67574be05d823775c37237b681b0ed26f2296289f664736f6c63430008150033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101215760003560e01c80637aadef8b116100ad578063b696649511610071578063b696649514610274578063b788f3a11461028f578063bffa7f0f146102aa578063cc025f7c146102c5578063e6bd26a2146102ec57600080fd5b80637aadef8b146101fd5780637e2f35fa1461021857806397b87b4a146102335780639d3d2a781461024e578063aa98df391461026157600080fd5b80632861c7d1116100f45780632861c7d1146101a2578063588c77e6146101bd57806361461954146101d857806366760d7d146101e05780636dc0ae22146101e257600080fd5b80630bc9136e14610126578063103f29071461014157806312e366aa1461017457806325ad7f4d14610187575b600080fd5b61012e603081565b6040519081526020015b60405180910390f35b61015c73324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d81565b6040516001600160a01b039091168152602001610138565b61015c610182366004612375565b6102ff565b61015c739efde135ca4832abf0408c44c6f5f370eb0f35e881565b61015c7352aa899454998be5b000ad077a46bbe360f4e49781565b61015c735c43aac965ff230ac1cf63e924d0153291d78bad81565b6101e061037d565b005b61015c730204cd037b2ec03605cfdfe482d8e257c765fa1b81565b61015c732386dc45added673317ef068992f19421b481f4c81565b61015c73059a94a72451c0ae1cc1ce4bf0db52421bbe821081565b61015c7391716c4eda1fb55e84bf8b4c7085f84285c1908581565b61012e61025c3660046123a3565b61040a565b6101e061026f366004612408565b6107e1565b61015c73264786ef916af64a1db19f513f24a3681734ce9281565b61015c734f6f977acdd1177dcd81ab83074855ecb9c2d49e81565b61015c73a45f7bd6a5ff45d31aace6bcd3d426d9328cea0181565b61015c7f000000000000000000000000038642fe64cdbd4de681dfa86652890bfc740b9681565b61015c6102fa366004612375565b610b8e565b604051630971b35560e11b8152600481018290526000907391716c4eda1fb55e84bf8b4c7085f84285c19085906312e366aa906024015b602060405180830381865afa158015610353573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061037791906124b9565b92915050565b30732386dc45added673317ef068992f19421b481f4c146103d85760405162461bcd60e51b815260206004820152601060248201526f3737ba16bb30b634b216b1b0b63632b960811b60448201526064015b60405180910390fd5b6103e0610bc9565b6103e8610eb2565b6103f0610f41565b6103f8611164565b61040061140f565b610408611b59565b565b6000808411801561041b5750600083115b156104685760405162461bcd60e51b815260206004820181905260248201527f626f74682075736420616e6420616d6f756e7420617265206e6f74207a65726f60448201526064016103cf565b604080516001600160a01b03871660208083019190915260058284015282518083038401815260608301938490528051910120632d71cdb960e21b90925260648101919091526000907352aa899454998be5b000ad077a46bbe360f4e4979063b5c736e490608401602060405180830381865afa1580156104ed573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061051191906124dd565b905060008061051f83611d6e565b90925090506000601273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b038b160161055b57506203d09090506012610761565b737f39c581f595b53c5cb19bd0b3f8da6c935e2c9f196001600160a01b038b160161058e57506204882890506012610761565b73cd5fe23c85820f7b72d0926fc9b05b43e359b7ed196001600160a01b038b16016105c1575062040b2890506012610761565b6001600160a01b038a1673cbb7c0000ab88b473b1f5afd9ef808440eed33bf148061060857506001600160a01b038a16732260fac5e5542a773aa44fbcfedf7c193bc2c599145b1561061b5750626760d890506008610761565b6001600160a01b038a1673a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48148061066257506001600160a01b038a1673dac17f958d2ee523a2206206994597c13d831ec7145b156106735750606490506006610761565b739d39a5de30e57443bff2a8307a4256c8797a3496196001600160a01b038b16016106a45750606f90506012610761565b73a3931d71877c0e7a3148cb7eb4463524fec27fbc196001600160a01b038b16016106d55750607090506012610761565b6001600160a01b038a167340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f148061071c57506001600160a01b038a16734c9edd5852cd905f086c759e8383e09bff1e68b3145b1561072d5750606490506012610761565b60405162461bcd60e51b81526020600482015260096024820152681b9bdd0b599bdd5b9960ba1b60448201526064016103cf565b60008761076e5783610770565b845b9050891561079f57806107888b64e8d4a5100061250c565b6107929190612539565b96505050505050506107d9565b60646107ab828561250c565b6107b59190612539565b6107c083600a61263f565b6107cf8b64e8d4a5100061250c565b610788919061250c565b949350505050565b3373a45f7bd6a5ff45d31aace6bcd3d426d9328cea011480610816575033734f6f977acdd1177dcd81ab83074855ecb9c2d49e145b8061083457503073059a94a72451c0ae1cc1ce4bf0db52421bbe8210145b80610852575030739efde135ca4832abf0408c44c6f5f370eb0f35e8145b8061085b575060015b6108a05760405162461bcd60e51b81526020600482015260166024820152751b5cd9cb9cd95b99195c8b5b9bdd0b585b1b1bddd95960521b60448201526064016103cf565b604080516001808252818301909252600090826020808301908036833701905050905060008267ffffffffffffffff8111156108de576108de6123f2565b604051908082528060200260200182016040528015610907578160200160208202803683370190505b50905060008367ffffffffffffffff811115610925576109256123f2565b60405190808252806020026020018201604052801561095857816020015b60608152602001906001900390816109435790505b50905060008467ffffffffffffffff811115610976576109766123f2565b6040519080825280602002602001820160405280156109a957816020015b60608152602001906001900390816109945790505b509050732386dc45added673317ef068992f19421b481f4c846000815181106109d4576109d461264b565b60200260200101906001600160a01b031690816001600160a01b031681525050600083600081518110610a0957610a0961264b565b602002602001018181525050604051806060016040528060248152602001612a436024913982600081518110610a4157610a4161264b565b60209081029190910181019190915260408051600081529182018152610a8a917f000000000000000000000000038642fe64cdbd4de681dfa86652890bfc740b969181016126a7565b60405160208183030381529060405281600081518110610aac57610aac61264b565b6020908102919091010152604051636d4ab48d60e11b8152600090730204cd037b2ec03605cfdfe482d8e257c765fa1b9063da95691a90610af99088908890889088908e906004016127b1565b6020604051808303816000875af1158015610b18573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3c91906124dd565b905060308114610b855760405162461bcd60e51b815260206004820152601460248201527350524f504f53414c5f49535f4e4f545f53414d4560601b60448201526064016103cf565b50505050505050565b60405163735e935160e11b81526004810182905260009073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d9063e6bd26a290602401610336565b604080516001808252818301909252600091816020015b610c1b6040518060a0016040528060006001600160a01b03168152602001600081526020016000815260200160008152602001600081525090565b815260200190600190039081610be05790505090506040518060a00160405280734c9edd5852cd905f086c759e8383e09bff1e68b36001600160a01b031681526020016124548152602001600081526020016102ee81526020016109c481525081600081518110610c8e57610c8e61264b565b6020908102919091010152604051630feb5dff60e21b81527352aa899454998be5b000ad077a46bbe360f4e49790633fad77fc90610cd0908490600401612860565b600060405180830381600087803b158015610cea57600080fd5b505af1158015610cfe573d6000803e3d6000fd5b505050505073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d6001600160a01b0316637faa1d21610d30603d610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60006040518463ffffffff1660e01b8152600401610d65939291906128ca565b600060405180830381600087803b158015610d7f57600080fd5b505af1158015610d93573d6000803e3d6000fd5b50505050306001600160a01b03167391716c4eda1fb55e84bf8b4c7085f84285c190856001600160a01b0316638da5cb5b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610df3573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e1791906124b9565b6001600160a01b031603610408577391716c4eda1fb55e84bf8b4c7085f84285c190856378c7e138610e4960046102ff565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60006040518463ffffffff1660e01b8152600401610e7e939291906128ca565b600060405180830381600087803b158015610e9857600080fd5b505af1158015610eac573d6000803e3d6000fd5b50505050565b6000604051806101000160405280610eca60046102ff565b6001600160a01b031681527340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f602082015273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4860408201526001606082018190526080820152627270e060a0820152624c4b4060c0820152625b8d8060e0909101529050610f3e81611f63565b50565b6000604051806101000160405280610f5960056102ff565b6001600160a01b0316815273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee602082015273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486040820152600160608201819052608082015261c35060a08201819052619c4060c083015260e0909101529050610fc981611f63565b506000604051806101000160405280610fe260066102ff565b6001600160a01b03168152732260fac5e5542a773aa44fbcfedf7c193bc2c599602082015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6040820152600160608201819052608082015261c35060a08201819052619c4060c083015260e090910152905061105281611f63565b50600060405180610100016040528061106b60076102ff565b6001600160a01b0316815273cbb7c0000ab88b473b1f5afd9ef808440eed33bf602082015273eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6040820152600160608201819052608082015261c35060a08201819052619c4060c083015260e09091015290506110db81611f63565b5060006040518061010001604052806110f460086102ff565b6001600160a01b03168152734c9edd5852cd905f086c759e8383e09bff1e68b3602082015273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486040820152600160608201819052608082015261c35060a08201819052619c4060c083015260e0909101529050610f3e81611f63565b604080516001808252818301909252600091602080830190803683375050604080516001808252818301909252929350600092915060208083019080368337505060408051600180825281830190925292935060009291506020808301908036833701905050604051635fd6196560e01b815273e85eb0acb7281faf00810d167c7de14bb070b4806004820152909150736a29a46e21c730dca1d8b23d637c101cec605c5b90635fd6196590602401600060405180830381600087803b15801561122d57600080fd5b505af1158015611241573d6000803e3d6000fd5b5050604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce926004820152736a29a46e21c730dca1d8b23d637c101cec605c5b6024820152600092507340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f915063dd62ed3e90604401602060405180830381865afa1580156112c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112e991906124dd565b9050736a29a46e21c730dca1d8b23d637c101cec605c5b846000815181106113135761131361264b565b60200260200101906001600160a01b031690816001600160a01b0316815250507340d16fc0246ad3160ccc09b8d0d3a2cd28ae6c2f8360008151811061135b5761135b61264b565b6001600160a01b039092166020928302919091019091015261138781690f7556828d6c7ba000006128ee565b8260008151811061139a5761139a61264b565b6020908102919091010152506040516303e8046160e01b815273264786ef916af64a1db19f513f24a3681734ce92906303e80461906113e190869086908690600401612901565b600060405180830381600087803b1580156113fb57600080fd5b505af1158015610b85573d6000803e3d6000fd5b60408051600680825260e082019092526000916020820160c080368337505060408051600680825260e0820190925292935060009291506020820160c080368337505060408051600680825260e0820190925292935060009291506020820160c08036833701905050905060006114866015610b8e565b905060006114946016610b8e565b905060006114a2601d610b8e565b905060006114b0601e610b8e565b905083876000815181106114c6576114c661264b565b60200260200101906001600160a01b031690816001600160a01b031681525050732260fac5e5542a773aa44fbcfedf7c193bc2c5998660008151811061150e5761150e61264b565b60200260200101906001600160a01b031690816001600160a01b031681525050620249f0856000815181106115455761154561264b565b60200260200101818152505082876001815181106115655761156561264b565b60200260200101906001600160a01b031690816001600160a01b031681525050732260fac5e5542a773aa44fbcfedf7c193bc2c599866001815181106115ad576115ad61264b565b60200260200101906001600160a01b031690816001600160a01b031681525050620249f0856001815181106115e4576115e461264b565b6020908102919091010152604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce9260048201526001600160a01b038516602482015260009073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489063dd62ed3e90604401602060405180830381865afa158015611664573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061168891906124dd565b9050848860028151811061169e5761169e61264b565b60200260200101906001600160a01b031690816001600160a01b03168152505073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48876002815181106116e6576116e661264b565b6001600160a01b039092166020928302919091019091015261170d8164060db884006128ee565b866002815181106117205761172061264b565b602090810291909101015250604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce9260048201526001600160a01b038416602482015260009073dac17f958d2ee523a2206206994597c13d831ec79063dd62ed3e90604401602060405180830381865afa1580156117a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c591906124dd565b905083886003815181106117db576117db61264b565b60200260200101906001600160a01b031690816001600160a01b03168152505073dac17f958d2ee523a2206206994597c13d831ec7876003815181106118235761182361264b565b6001600160a01b039092166020928302919091019091015261184a8164060db884006128ee565b8660038151811061185d5761185d61264b565b602090810291909101015250604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce9260048201526001600160a01b038316602482015260009073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489063dd62ed3e90604401602060405180830381865afa1580156118de573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061190291906124dd565b905082886004815181106119185761191861264b565b60200260200101906001600160a01b031690816001600160a01b03168152505073a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48876004815181106119605761196061264b565b6001600160a01b03909216602092830291909101909101526119878164060db884006128ee565b8660048151811061199a5761199a61264b565b602090810291909101015250604051636eb1769f60e11b815273264786ef916af64a1db19f513f24a3681734ce9260048201526001600160a01b038216602482015260009073dac17f958d2ee523a2206206994597c13d831ec79063dd62ed3e90604401602060405180830381865afa158015611a1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a3f91906124dd565b90508188600581518110611a5557611a5561264b565b60200260200101906001600160a01b031690816001600160a01b03168152505073dac17f958d2ee523a2206206994597c13d831ec787600581518110611a9d57611a9d61264b565b6001600160a01b0390921660209283029190910190910152611ac48164060db884006128ee565b86600581518110611ad757611ad761264b565b6020908102919091010152506040516303e8046160e01b815273264786ef916af64a1db19f513f24a3681734ce92906303e8046190611b1e908a908a908a90600401612901565b600060405180830381600087803b158015611b3857600080fd5b505af1158015611b4c573d6000803e3d6000fd5b5050505050505050505050565b73324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d637faa1d21611b7d603e610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60016040518463ffffffff1660e01b8152600401611bb2939291906128ca565b600060405180830381600087803b158015611bcc57600080fd5b505af1158015611be0573d6000803e3d6000fd5b5050505073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d6001600160a01b0316637faa1d21611c11603f610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60016040518463ffffffff1660e01b8152600401611c46939291906128ca565b600060405180830381600087803b158015611c6057600080fd5b505af1158015611c74573d6000803e3d6000fd5b5050505073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d6001600160a01b0316637faa1d21611ca56040610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60016040518463ffffffff1660e01b8152600401611cda939291906128ca565b600060405180830381600087803b158015611cf457600080fd5b505af1158015611d08573d6000803e3d6000fd5b5050505073324c5dc1fc42c7a4d43d92df1eba58a54d13bf2d6001600160a01b0316637faa1d21611d396041610b8e565b734f6f977acdd1177dcd81ab83074855ecb9c2d49e60016040518463ffffffff1660e01b8152600401610e7e939291906128ca565b67ffffffffffffffff605b82901c811690609b83901c16811580611d90575080155b15611db357604051636a86ba8960e11b81526201117160048201526024016103cf565b61ffff8316603a84901c6401ffffffff16428181039160ea87901c617fff16911480611ddd575082155b80611de85750806001145b15611df557505050915091565b64496cebb80084840283020484019350617fff60db87901c16925082600103611e2057505050915091565b82600116600103611e755760019290921c91826c7e37be2022c0914b268000000081611e4e57611e4e612523565b049250612710601e87901c613fff166b033b2e3c9fd0803ce8000000850102049250611ea2565b60019290921c916305f5e100601e87901c613fff166127108501026b033b2e3c9fd0803ce8000000020492505b80600116600103611ed95760011c61271081016b033b2e3c9fd0803ce8000000820281611ed157611ed1612523565b049050611f0f565b60011c61271081016b033b2e3c9fd0803ce8000000820281611efd57611efd612523565b046b033b2e3c9fd0803ce80000000390505b760a70c3c40a64e6c51999090b65f67d92400000000000008382026127100261ffff881691900402601087901c613fff16612710030292506801b5a660ea44b8000085840283020485019450505050915091565b80606001511561200e5760006040518060a0016040528083600001516001600160a01b0316815260200183602001516001600160a01b031681526020016113888152602001610e1081526020018360a001518152509050611fc3816120cf565b6040805160a0808201835284516001600160a01b0390811683528584015116602083015261138892820192909252610e10606082015290830151608082015261200b816120cf565b50505b806080015115610f3e5760006040518060c0016040528083600001516001600160a01b0316815260200183602001516001600160a01b031681526020016113888152602001610e1081526020018360c0015181526020018360e0015181525090506120788161221d565b6040805160c0808201835284516001600160a01b0390811683528584015116602083015261138892820192909252610e10606082015290830151608082015260e083015160a08201526120ca8161221d565b505050565b604080516001808252818301909252600091816020015b6040805160c08101825260008082526020808301829052928201819052606082018190526080820181905260a082015282526000199092019101816120e65790505090506040518060c0016040528083600001516001600160a01b0316815260200183602001516001600160a01b03168152602001600160ff168152602001836040015181526020018360600151815260200161218f846020015160008660800151600161040a565b815250816000815181106121a5576121a561264b565b60209081029190910101526040516301635db560e11b81527352aa899454998be5b000ad077a46bbe360f4e497906302c6bb6a906121e7908490600401612944565b600060405180830381600087803b15801561220157600080fd5b505af1158015612215573d6000803e3d6000fd5b505050505050565b604080516001808252818301909252600091816020015b6122896040518060e0016040528060006001600160a01b0316815260200160006001600160a01b03168152602001600060ff168152602001600081526020016000815260200160008152602001600081525090565b8152602001906001900390816122345790505090506040518060e0016040528083600001516001600160a01b0316815260200183602001516001600160a01b03168152602001600160ff1681526020018360400151815260200183606001518152602001612303846020015160008660800151600061040a565b815260200161231e846020015160008660a00151600061040a565b815250816000815181106123345761233461264b565b602090810291909101015260405162dc47c360e11b81527352aa899454998be5b000ad077a46bbe360f4e497906301b88f86906121e79084906004016129be565b60006020828403121561238757600080fd5b5035919050565b6001600160a01b0381168114610f3e57600080fd5b600080600080608085870312156123b957600080fd5b84356123c48161238e565b93506020850135925060408501359150606085013580151581146123e757600080fd5b939692955090935050565b634e487b7160e01b600052604160045260246000fd5b60006020828403121561241a57600080fd5b813567ffffffffffffffff8082111561243257600080fd5b818401915084601f83011261244657600080fd5b813581811115612458576124586123f2565b604051601f8201601f19908116603f01168101908382118183101715612480576124806123f2565b8160405282815287602084870101111561249957600080fd5b826020860160208301376000928101602001929092525095945050505050565b6000602082840312156124cb57600080fd5b81516124d68161238e565b9392505050565b6000602082840312156124ef57600080fd5b5051919050565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610377576103776124f6565b634e487b7160e01b600052601260045260246000fd5b60008261255657634e487b7160e01b600052601260045260246000fd5b500490565b600181815b8085111561259657816000190482111561257c5761257c6124f6565b8085161561258957918102915b93841c9390800290612560565b509250929050565b6000826125ad57506001610377565b816125ba57506000610377565b81600181146125d057600281146125da576125f6565b6001915050610377565b60ff8411156125eb576125eb6124f6565b50506001821b610377565b5060208310610133831016604e8410600b8410161715612619575081810a610377565b612623838361255b565b8060001904821115612637576126376124f6565b029392505050565b60006124d6838361259e565b634e487b7160e01b600052603260045260246000fd5b6000815180845260005b818110156126875760208185018101518683018201520161266b565b506000602082860101526020601f19601f83011685010191505092915050565b60018060a01b038316815260606020820152600960608201526865786563757465282960b81b608082015260a0604082015260006107d960a0830184612661565b600081518084526020808501945080840160005b838110156127215781516001600160a01b0316875295820195908201906001016126fc565b509495945050505050565b600081518084526020808501945080840160005b8381101561272157815187529582019590820190600101612740565b600081518084526020808501808196508360051b8101915082860160005b858110156127a4578284038952612792848351612661565b9885019893509084019060010161277a565b5091979650505050505050565b60a0815260006127c460a08301886126e8565b6020838203818501526127d7828961272c565b915083820360408501528187518084528284019150828160051b850101838a0160005b8381101561282857601f19878403018552612816838351612661565b948601949250908501906001016127fa565b5050868103606088015261283c818a61275c565b94505050505082810360808401526128548185612661565b98975050505050505050565b602080825282518282018190526000919060409081850190868401855b828110156127a457815180516001600160a01b0316855286810151878601528581015186860152606080820151908601526080908101519085015260a0909301929085019060010161287d565b6001600160a01b039384168152919092166020820152901515604082015260600190565b80820180821115610377576103776124f6565b60608152600061291460608301866126e8565b828103602084015261292681866126e8565b9050828103604084015261293a818561272c565b9695505050505050565b602080825282518282018190526000919060409081850190868401855b828110156127a457815180516001600160a01b0390811686528782015116878601528581015160ff1686860152606080820151908601526080808201519086015260a0908101519085015260c09093019290850190600101612961565b602080825282518282018190526000919060409081850190868401855b828110156127a457815180516001600160a01b0390811686528782015116878601528581015160ff1686860152606080820151908601526080808201519086015260a0808201519086015260c0908101519085015260e090930192908501906001016129db56fe657865637574655061796c6f616428616464726573732c737472696e672c627974657329a2646970667358221220637aa95cde9bde1a825b9d67574be05d823775c37237b681b0ed26f2296289f664736f6c63430008150033
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
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.