Feature Tip: Add private address tag to any address under My Name Tag !
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
|
|||||
---|---|---|---|---|---|---|---|---|---|
0x60806040 | 18572311 | 356 days ago | IN | 0 ETH | 0.19866536 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
LendingPool
Compiler Version
v0.8.18+commit.87f61d96
Optimization Enabled:
Yes with 10 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "./PoolCalculations.sol"; import "./PoolTransfers.sol"; import "./ILendingPool.sol"; import "../fee_sharing/IFeeSharing.sol"; import "../authority/AuthorityAware.sol"; import "../vaults/TrancheVault.sol"; contract LendingPool is ILendingPool, AuthorityAware, PausableUpgradeable { using EnumerableSet for EnumerableSet.AddressSet; using Math for uint; /*/////////////////////////////////// CONSTANTS ///////////////////////////////////*/ string public constant VERSION = "2023-06-12"; uint internal constant WAD = 10 ** 18; uint internal constant DAY = 24 * 60 * 60; uint internal constant YEAR = 365 * DAY; // DO NOT TOUCH WITHOUT LIBRARY CONSIDERATIONS struct Rewardable { uint stakedAssets; uint lockedPlatformTokens; uint redeemedRewards; uint64 start; } struct RollOverSetting { bool enabled; bool principal; bool rewards; bool platformTokens; } enum Stages { // WARNING, DO NOT REORDER ENUM!!! INITIAL, // 0 OPEN, // 1 FUNDED, // 2 FUNDING_FAILED, // 3 FLC_DEPOSITED, // 4 BORROWED, // 5 BORROWER_INTEREST_REPAID, // 6 DELINQUENT, // 7 REPAID, // 8 DEFAULTED, // 9 FLC_WITHDRAWN // 10 } struct LendingPoolParams { string name; string token; address stableCoinContractAddress; address platformTokenContractAddress; uint minFundingCapacity; uint maxFundingCapacity; uint64 fundingPeriodSeconds; uint64 lendingTermSeconds; address borrowerAddress; uint firstLossAssets; uint borrowerTotalInterestRateWad; uint repaymentRecurrenceDays; uint gracePeriodDays; uint protocolFeeWad; uint defaultPenalty; uint penaltyRateWad; uint8 tranchesCount; uint[] trancheAPRsWads; uint[] trancheBoostedAPRsWads; uint[] trancheBoostRatios; uint[] trancheCoveragesWads; } /*/////////////////////////////////// CONTRACT VARIABLES ///////////////////////////////////*/ /*Initializer parameters*/ string public name; string public token; address public stableCoinContractAddress; address public platformTokenContractAddress; uint public minFundingCapacity; uint public maxFundingCapacity; uint64 public fundingPeriodSeconds; uint64 public lendingTermSeconds; address public borrowerAddress; uint public firstLossAssets; uint public repaymentRecurrenceDays; uint public gracePeriodDays; uint public borrowerTotalInterestRateWad; uint public protocolFeeWad; uint public defaultPenalty; uint public penaltyRateWad; uint8 public tranchesCount; uint[] public trancheAPRsWads; uint[] public trancheBoostedAPRsWads; uint[] public trancheBoostRatios; uint[] public trancheCoveragesWads; /* Other contract addresses */ address public poolFactoryAddress; address public feeSharingContractAddress; address[] public trancheVaultAddresses; /* Some Timestamps */ uint64 public openedAt; uint64 public fundedAt; uint64 public fundingFailedAt; uint64 public flcDepositedAt; uint64 public borrowedAt; uint64 public repaidAt; uint64 public flcWithdrawntAt; uint64 public defaultedAt; /* Interests & Yields */ uint public collectedAssets; uint public borrowedAssets; uint public borrowerInterestRepaid; EnumerableSet.AddressSet internal s_lenders; /// @dev trancheId => (lenderAddress => RewardableRecord) mapping(uint8 => mapping(address => Rewardable)) public s_trancheRewardables; /// @dev trancheId => stakedassets mapping(uint8 => uint256) public s_totalStakedAssetsByTranche; /// @dev trancheId => lockedTokens mapping(uint8 => uint256) public s_totalLockedPlatformTokensByTranche; /// @dev lenderAddress => RollOverSetting mapping(address => RollOverSetting) private s_rollOverSettings; Stages public currentStage; /*/////////////////////////////////// MODIFIERS ///////////////////////////////////*/ modifier authTrancheVault(uint8 id) { _authTrancheVault(id); _; } function _authTrancheVault(uint8 id) internal view { require(id < trancheVaultAddresses.length, "LP001"); // "LendingPool: invalid trancheVault id" require(trancheVaultAddresses[id] == _msgSender(), "LP002"); // "LendingPool: trancheVault auth" } modifier onlyPoolBorrower() { _onlyPoolBorrower(); _; } function _onlyPoolBorrower() internal view { require(_msgSender() == borrowerAddress, "LP003"); // "LendingPool: not a borrower" } modifier atStage(Stages _stage) { _atStage(_stage); _; } function _atStage(Stages _stage) internal view { require(currentStage == _stage, "LP004"); // "LendingPool: not at correct stage" } modifier atStages2(Stages _stage1, Stages _stage2) { _atStages2(_stage1, _stage2); _; } function _atStages2(Stages _stage1, Stages _stage2) internal view { require(currentStage == _stage1 || currentStage == _stage2, "LP004"); // "LendingPool: not at correct stage" } modifier atStages3( Stages _stage1, Stages _stage2, Stages _stage3 ) { _atStages3(_stage1, _stage2, _stage3); _; } function _atStages3(Stages _stage1, Stages _stage2, Stages _stage3) internal view { require( currentStage == _stage1 || currentStage == _stage2 || currentStage == _stage3, "LP004" // "LendingPool: not at correct stage" ); } /*/////////////////////////////////// EVENTS ///////////////////////////////////*/ // State Changes // event PoolInitialized( LendingPoolParams params, address[] _trancheVaultAddresses, address _feeSharingContractAddress, address _authorityAddress ); event PoolOpen(uint64 openedAt); event PoolFunded(uint64 fundedAt, uint collectedAssets); event PoolFundingFailed(uint64 fundingFailedAt); event PoolRepaid(uint64 repaidAt); event PoolDefaulted(uint64 defaultedAt); event PoolFirstLossCapitalWithdrawn(uint64 flcWithdrawntAt); // Lender // event LenderDeposit(address indexed lender, uint8 indexed trancheId, uint256 amount); event LenderWithdraw(address indexed lender, uint8 indexed trancheId, uint256 amount); event LenderWithdrawInterest(address indexed lender, uint8 indexed trancheId, uint256 amount); event LenderTrancheRewardsChange( address indexed lender, uint8 indexed trancheId, uint lenderEffectiveAprWad, uint totalExpectedRewards, uint redeemedRewards ); event LenderLockPlatformTokens(address indexed lender, uint8 indexed trancheId, uint256 amount); event LenderUnlockPlatformTokens(address indexed lender, uint8 indexed trancheId, uint256 amount); // Borrower // event BorrowerDepositFirstLossCapital(address indexed borrower, uint amount); event BorrowerBorrow(address indexed borrower, uint amount); event BorrowerPayInterest( address indexed borrower, uint amount, uint lendersDistributedAmount, uint feeSharingContractAmount ); event BorrowerPayPenalty(address indexed borrower, uint amount); event BorrowerRepayPrincipal(address indexed borrower, uint amount); event BorrowerWithdrawFirstLossCapital(address indexed borrower, uint amount); /*/////////////////////////////////// INITIALIZATION ///////////////////////////////////*/ function initialize( LendingPoolParams calldata params, address[] calldata _trancheVaultAddresses, address _feeSharingContractAddress, address _authorityAddress, address _poolFactoryAddress ) external initializer { PoolCalculations.validateInitParams( params, _trancheVaultAddresses, _feeSharingContractAddress, _authorityAddress ); PoolCalculations.validateWad(params.trancheCoveragesWads); name = params.name; token = params.token; stableCoinContractAddress = params.stableCoinContractAddress; platformTokenContractAddress = params.platformTokenContractAddress; minFundingCapacity = params.minFundingCapacity; maxFundingCapacity = params.maxFundingCapacity; fundingPeriodSeconds = params.fundingPeriodSeconds; lendingTermSeconds = params.lendingTermSeconds; borrowerAddress = params.borrowerAddress; firstLossAssets = params.firstLossAssets; borrowerTotalInterestRateWad = params.borrowerTotalInterestRateWad; repaymentRecurrenceDays = params.repaymentRecurrenceDays; gracePeriodDays = params.gracePeriodDays; protocolFeeWad = params.protocolFeeWad; defaultPenalty = params.defaultPenalty; penaltyRateWad = params.penaltyRateWad; tranchesCount = params.tranchesCount; trancheAPRsWads = params.trancheAPRsWads; trancheBoostedAPRsWads = params.trancheBoostedAPRsWads; trancheBoostRatios = params.trancheBoostRatios; trancheCoveragesWads = params.trancheCoveragesWads; trancheVaultAddresses = _trancheVaultAddresses; feeSharingContractAddress = _feeSharingContractAddress; poolFactoryAddress = _poolFactoryAddress; __Ownable_init(); __Pausable_init(); __AuthorityAware__init(_authorityAddress); emit PoolInitialized(params, _trancheVaultAddresses, _feeSharingContractAddress, _authorityAddress); } /*/////////////////////////////////// ADMIN FUNCTIONS ///////////////////////////////////*/ /** @dev Pauses the pool */ function pause() external onlyOwnerOrAdmin { _pause(); } /** @dev Unpauses the pool */ function unpause() external onlyOwnerOrAdmin { _unpause(); } /** @notice Marks the pool as opened. This function has to be called by *owner* when * - sets openedAt to current block timestamp * - enables deposits and withdrawals to tranche vaults */ function adminOpenPool() external onlyOwnerOrAdmin atStage(Stages.FLC_DEPOSITED) whenNotPaused { openedAt = uint64(block.timestamp); currentStage = Stages.OPEN; TrancheVault[] memory vaults = trancheVaultContracts(); for (uint i; i < trancheVaultAddresses.length; i++) { vaults[i].enableDeposits(); vaults[i].enableWithdrawals(); } emit PoolOpen(openedAt); } /** @notice Checks whether the pool was funded successfully or not. * this function is expected to be called by *owner* once the funding period ends */ function adminTransitionToFundedState() external onlyOwnerOrAdmin atStage(Stages.OPEN) { require(block.timestamp >= openedAt + fundingPeriodSeconds, "Cannot accrue interest or declare failure before start time"); if (collectedAssets >= minFundingCapacity) { _transitionToFundedStage(); } else { _transitionToFundingFailedStage(); } } function adminTransitionToDefaultedState() external onlyOwnerOrAdmin atStage(Stages.BORROWED) { require(block.timestamp >= fundedAt + lendingTermSeconds, "LP023"); // "LendingPool: maturityDate not reached" _transitionToDefaultedStage(); } function _transitionToFundedStage() internal whenNotPaused { fundedAt = uint64(block.timestamp); currentStage = Stages.FUNDED; TrancheVault[] memory vaults = trancheVaultContracts(); for (uint i; i < vaults.length; i++) { TrancheVault tv = vaults[i]; tv.disableDeposits(); tv.disableWithdrawals(); tv.sendAssetsToPool(tv.totalAssets()); } emit PoolFunded(fundedAt, collectedAssets); } function _transitionToFundingFailedStage() internal whenNotPaused { fundingFailedAt = uint64(block.timestamp); currentStage = Stages.FUNDING_FAILED; TrancheVault[] memory vaults = trancheVaultContracts(); for (uint i; i < trancheVaultAddresses.length; i++) { vaults[i].disableDeposits(); vaults[i].enableWithdrawals(); } emit PoolFundingFailed(fundingFailedAt); } function _transitionToFlcDepositedStage(uint flcAssets) internal whenNotPaused { flcDepositedAt = uint64(block.timestamp); currentStage = Stages.FLC_DEPOSITED; emit BorrowerDepositFirstLossCapital(borrowerAddress, flcAssets); } function _transitionToBorrowedStage(uint amountToBorrow) internal whenNotPaused { borrowedAt = uint64(block.timestamp); borrowedAssets = amountToBorrow; currentStage = Stages.BORROWED; emit BorrowerBorrow(borrowerAddress, amountToBorrow); } function _transitionToPrincipalRepaidStage(uint repaidPrincipal) internal whenNotPaused { repaidAt = uint64(block.timestamp); currentStage = Stages.REPAID; emit BorrowerRepayPrincipal(borrowerAddress, repaidPrincipal); emit PoolRepaid(repaidAt); } function _transitionToFlcWithdrawnStage(uint flcAssets) internal whenNotPaused { flcWithdrawntAt = uint64(block.timestamp); currentStage = Stages.FLC_WITHDRAWN; emit BorrowerWithdrawFirstLossCapital(borrowerAddress, flcAssets); } function _claimTrancheInterestForLender(address lender, uint8 trancheId) internal { uint rewards = lenderRewardsByTrancheRedeemable(lender, trancheId); if (rewards > 0) { s_trancheRewardables[trancheId][lender].redeemedRewards += rewards; SafeERC20.safeTransfer(_stableCoinContract(), lender, rewards); emit LenderWithdrawInterest(lender, trancheId, rewards); } } function _claimInterestForAllLenders() internal { TrancheVault[] memory vaults = trancheVaultContracts(); for (uint8 i; i < tranchesCount; i++) { for (uint j; j < lenderCount(); j++) { _claimTrancheInterestForLender(lendersAt(j), vaults[i].id()); } } } /** * @notice Transitions the pool to the defaulted state and pays out remaining assets to the tranche vaults * @dev This function is expected to be called by *owner* after the maturity date has passed and principal has not been repaid */ function _transitionToDefaultedStage() internal whenNotPaused { defaultedAt = uint64(block.timestamp); currentStage = Stages.DEFAULTED; _claimInterestForAllLenders(); // TODO: update repaid interest to be the total interest paid to lenders // TODO: should the protocol fees be paid in event of default uint availableAssets = _stableCoinContract().balanceOf(address(this)); TrancheVault[] memory vaults = trancheVaultContracts(); for (uint i; i < trancheVaultAddresses.length; i++) { TrancheVault tv = vaults[i]; uint assetsToSend = (trancheCoveragesWads[i] * availableAssets) / WAD; uint trancheDefaultRatioWad = (assetsToSend * WAD) / tv.totalAssets(); if (assetsToSend > 0) { SafeERC20.safeTransfer(_stableCoinContract(), address(tv), assetsToSend); } availableAssets -= assetsToSend; tv.setDefaultRatioWad(trancheDefaultRatioWad); tv.enableWithdrawals(); } emit PoolDefaulted(defaultedAt); } /*/////////////////////////////////// Lender (please also see onTrancheDeposit() and onTrancheWithdraw()) Error group: 1 ///////////////////////////////////*/ /** @notice Lock platform tokens in order to get APR boost * @param trancheId tranche id * @param platformTokens amount of PLATFORM tokens to lock */ function lenderLockPlatformTokensByTranche( uint8 trancheId, uint platformTokens ) external onlyLender atStage(Stages.OPEN) whenNotPaused { require( platformTokens <= lenderPlatformTokensByTrancheLockable(_msgSender(), trancheId), "LP101" //"LendingPool: lock will lead to overboost" ); require(IERC20(platformTokenContractAddress).totalSupply() > 0, "Lock: Token Locking Disabled"); Rewardable storage r = s_trancheRewardables[trancheId][_msgSender()]; r.lockedPlatformTokens += platformTokens; s_totalLockedPlatformTokensByTranche[trancheId] += platformTokens; SafeERC20.safeTransferFrom(IERC20(platformTokenContractAddress), _msgSender(), address(this), platformTokens); emit LenderLockPlatformTokens(_msgSender(), trancheId, platformTokens); _emitLenderTrancheRewardsChange(_msgSender(), trancheId); } /** @notice Unlock platform tokens after the pool is repaid AND rewards are redeemed * @param trancheId tranche id * @param platformTokens amount of PLATFORM tokens to unlock */ function lenderUnlockPlatformTokensByTranche( uint8 trancheId, uint platformTokens ) external onlyLender atStages2(Stages.REPAID, Stages.FLC_WITHDRAWN) whenNotPaused { require(!s_rollOverSettings[msg.sender].platformTokens, "LP102"); // "LendingPool: tokens are locked for rollover" require(lenderRewardsByTrancheRedeemable(_msgSender(), trancheId) == 0, "LP103"); // "LendingPool: rewards not redeemed" require(IERC20(platformTokenContractAddress).totalSupply() > 0, "Unlock: Token Locking Disabled"); Rewardable storage r = s_trancheRewardables[trancheId][_msgSender()]; require(r.lockedPlatformTokens >= platformTokens, "LP104"); // LendingPool: not enough locked tokens" r.lockedPlatformTokens -= platformTokens; SafeERC20.safeTransfer(IERC20(platformTokenContractAddress), _msgSender(), platformTokens); emit LenderUnlockPlatformTokens(_msgSender(), trancheId, platformTokens); } /** @notice Redeem currently available rewards for a tranche * @param trancheId tranche id * @param toWithdraw amount of rewards to withdraw */ function lenderRedeemRewardsByTranche( uint8 trancheId, uint toWithdraw ) public onlyLender atStages3(Stages.BORROWED, Stages.REPAID, Stages.FLC_WITHDRAWN) whenNotPaused { require(!s_rollOverSettings[msg.sender].rewards, "LP105"); // "LendingPool: rewards are locked for rollover" if (toWithdraw == 0) { return; } uint maxWithdraw = lenderRewardsByTrancheRedeemable(_msgSender(), trancheId); require(toWithdraw <= maxWithdraw, "LP106"); // "LendingPool: amount to withdraw is too big" s_trancheRewardables[trancheId][_msgSender()].redeemedRewards += toWithdraw; SafeERC20.safeTransfer(_stableCoinContract(), _msgSender(), toWithdraw); // if (IERC20(stableCoinContractAddress()).balanceOf(address(this)) < poolBalanceThreshold()) { // _transitionToDelinquentStage(); // } emit LenderWithdrawInterest(_msgSender(), trancheId, toWithdraw); _emitLenderTrancheRewardsChange(_msgSender(), trancheId); } /** @notice Redeem currently available rewards for two tranches * @param toWithdraws amount of rewards to withdraw accross all tranches */ function lenderRedeemRewards( uint[] calldata toWithdraws ) external onlyLender atStages3(Stages.BORROWED, Stages.REPAID, Stages.FLC_WITHDRAWN) whenNotPaused { require(!s_rollOverSettings[msg.sender].rewards, "LP105"); //"LendingPool: rewards are locked for rollover" require(toWithdraws.length == tranchesCount, "LP107"); //"LendingPool: wrong amount of tranches" for (uint8 i; i < toWithdraws.length; i++) { lenderRedeemRewardsByTranche(i, toWithdraws[i]); } } /* VIEWS */ /// @notice average APR of all lenders across all tranches, boosted or not function allLendersInterest() public view returns (uint) { return (((allLendersEffectiveAprWad() * collectedAssets) / WAD) * lendingTermSeconds) / YEAR; } function allLendersInterestByDate() public view returns (uint) { return PoolCalculations.allLendersInterestByDate(this); } /// @notice average APR of all lenders across all tranches, boosted or not function allLendersEffectiveAprWad() public view returns (uint) { return PoolCalculations.allLendersEffectiveAprWad(this, tranchesCount); } /// @notice weighted APR accross all the lenders function lenderTotalAprWad(address lenderAddress) public view returns (uint) { return PoolCalculations.lenderTotalAprWad(this, lenderAddress); } /// @notice Returns amount of stablecoins deposited across all the pool tranches by a lender function lenderAllDepositedAssets(address lenderAddress) public view returns (uint totalAssets) { totalAssets = 0; for (uint8 i; i < tranchesCount; ++i) { totalAssets += s_trancheRewardables[i][lenderAddress].stakedAssets; } } /* VIEWS BY TRANCHE*/ /** @notice Returns amount of stablecoins deposited to a pool tranche by a lender * @param lenderAddress lender address * @param trancheId tranche id */ function lenderDepositedAssetsByTranche(address lenderAddress, uint8 trancheId) public view returns (uint) { return s_trancheRewardables[trancheId][lenderAddress].stakedAssets; } /** @notice Returns amount of stablecoins to be paid for the lender by the end of the pool term. * `lenderAPR * lenderDepositedAssets * lendingTermSeconds / YEAR` * @param lenderAddress lender address * @param trancheId tranche id */ function lenderTotalExpectedRewardsByTranche(address lenderAddress, uint8 trancheId) public view returns (uint) { return PoolCalculations.lenderTotalExpectedRewardsByTranche( lenderDepositedAssetsByTranche(lenderAddress, trancheId), lenderEffectiveAprByTrancheWad(lenderAddress, trancheId), lendingTermSeconds ); } /** @notice Returns amount of stablecoin rewards generated for the lenders by current second. * `lenderTotalExpectedRewardsByTranche * (secondsElapsed / lendingTermSeconds)` * @param lenderAddress lender address * @param trancheId tranche id */ function lenderRewardsByTrancheGeneratedByDate(address lenderAddress, uint8 trancheId) public view returns (uint) { return PoolCalculations.lenderRewardsByTrancheGeneratedByDate(this, lenderAddress, trancheId); } /** @notice Returns amount of stablecoin rewards that has been withdrawn by the lender. * @param lenderAddress lender address * @param trancheId tranche id */ function lenderRewardsByTrancheRedeemed(address lenderAddress, uint8 trancheId) public view returns (uint) { return s_trancheRewardables[trancheId][lenderAddress].redeemedRewards; } /** @notice Returns amount of stablecoin rewards that can be withdrawn by the lender. (generated - redeemed). Special means this one is distinguished from the FE version and is only used within the SCs * @param lenderAddress lender address * @param trancheId tranche id */ function lenderRewardsByTrancheRedeemable(address lenderAddress, uint8 trancheId) public view returns (uint) { uint256 willReward = lenderRewardsByTrancheGeneratedByDate(lenderAddress, trancheId); uint256 hasRewarded = lenderRewardsByTrancheRedeemed(lenderAddress, trancheId); return willReward - hasRewarded; } /** @notice Returns amount of stablecoin rewards that can be withdrawn by the lender. (generated - redeemed) only use in FE * @param lenderAddress lender address * @param trancheId tranche id */ function lenderRewardsByTrancheRedeemableSpecial(address lenderAddress, uint8 trancheId) public view returns (uint) { uint256 willReward = lenderRewardsByTrancheGeneratedByDate(lenderAddress, trancheId); uint256 hasRewarded = lenderRewardsByTrancheRedeemed(lenderAddress, trancheId); if(hasRewarded > willReward) { return 0; } return willReward - hasRewarded; } /** @notice Returns APR for the lender taking into account all the deposited USDC + platform tokens * @param lenderAddress lender address * @param trancheId tranche id */ function lenderEffectiveAprByTrancheWad(address lenderAddress, uint8 trancheId) public view returns (uint) { return PoolCalculations.lenderEffectiveAprByTrancheWad(this, lenderAddress, trancheId); } /** @notice Returns amount of platform tokens locked by the lender * @param lenderAddress lender address * @param trancheId tranche id */ function lenderPlatformTokensByTrancheLocked(address lenderAddress, uint8 trancheId) public view returns (uint) { return s_trancheRewardables[trancheId][lenderAddress].lockedPlatformTokens; } /** @notice Returns amount of staked tokens committed by the lender * @param lenderAddress lender address * @param trancheId tranche id */ function lenderStakedTokensByTranche(address lenderAddress, uint8 trancheId) public view returns (uint) { return s_trancheRewardables[trancheId][lenderAddress].stakedAssets; } /** @notice Returns amount of platform tokens that lender can lock in order to boost their APR * @param lenderAddress lender address * @param trancheId tranche id */ function lenderPlatformTokensByTrancheLockable(address lenderAddress, uint8 trancheId) public view returns (uint) { Rewardable storage r = s_trancheRewardables[trancheId][lenderAddress]; uint maxLockablePlatformTokens = r.stakedAssets * trancheBoostRatios[trancheId]; return maxLockablePlatformTokens - r.lockedPlatformTokens; } /*/////////////////////////////////// Rollover settings ///////////////////////////////////*/ /** @notice marks the intent of the lender to roll over their capital to the upcoming pool (called by older pool) * if you opt to roll over you will not be able to withdraw stablecoins / platform tokens from the pool * @param principal whether the principal should be rolled over * @param rewards whether the rewards should be rolled over * @param platformTokens whether the platform tokens should be rolled over */ function lenderEnableRollOver(bool principal, bool rewards, bool platformTokens) external onlyLender { address lender = _msgSender(); s_rollOverSettings[lender] = RollOverSetting(true, principal, rewards, platformTokens); PoolTransfers.lenderEnableRollOver(this, principal, rewards, platformTokens, lender); } /** * @dev This function rolls funds from prior deployments into currently active deployments * @param deadLendingPoolAddr The address of the lender whose funds are transfering over to the new lender * @param deadTrancheAddrs The address of the tranches whose funds are mapping 1:1 with the next traches * @param lenderStartIndex The first lender to start migrating over * @param lenderEndIndex The last lender to migrate */ function executeRollover( address deadLendingPoolAddr, address[] memory deadTrancheAddrs, uint256 lenderStartIndex, uint256 lenderEndIndex ) external onlyOwnerOrAdmin atStage(Stages.OPEN) whenNotPaused { PoolTransfers.executeRollover(this, deadLendingPoolAddr, deadTrancheAddrs, lenderStartIndex, lenderEndIndex); } /** @notice cancels lenders intent to roll over the funds to the next pool. */ function lenderDisableRollOver() external onlyLender { s_rollOverSettings[_msgSender()] = RollOverSetting(false, false, false, false); } /** @notice returns lender's roll over settings * @param lender lender address */ function lenderRollOverSettings(address lender) external view returns (RollOverSetting memory) { return s_rollOverSettings[lender]; } /*/////////////////////////////////// Borrower functions Error group: 2 ///////////////////////////////////*/ /** @notice Deposits first loss capital into the pool * should be called by the borrower before the pool can start */ function borrowerDepositFirstLossCapital() external onlyPoolBorrower atStage(Stages.INITIAL) whenNotPaused { _transitionToFlcDepositedStage(firstLossAssets); SafeERC20.safeTransferFrom(_stableCoinContract(), msg.sender, address(this), firstLossAssets); } /** @notice Borrows collected funds from the pool */ function borrow() external onlyPoolBorrower atStage(Stages.FUNDED) whenNotPaused { _transitionToBorrowedStage(collectedAssets); SafeERC20.safeTransfer(_stableCoinContract(), borrowerAddress, collectedAssets); } /** @notice Lets the borrower withdraw first loss deposit in the event of funding failed */ function borrowerRecoverFirstLossCapital() external atStage(Stages.FUNDING_FAILED) { uint256 copyFirstLossAssets = firstLossAssets; firstLossAssets = 0; SafeERC20.safeTransfer(_stableCoinContract(), borrowerAddress, copyFirstLossAssets); } /** @notice Make an interest payment. * If the pool is delinquent, the minimum payment is penalty + whatever interest that needs to be paid to bring the pool back to healthy state */ function borrowerPayInterest(uint assets) external onlyPoolBorrower whenNotPaused { uint penalty = borrowerPenaltyAmount(); require(penalty < assets, "LP201"); // "LendingPool: penalty cannot be more than assets" if (penalty > 0) { uint balanceDifference = poolBalanceThreshold() - poolBalance(); require(assets >= penalty + balanceDifference, "LP202"); // "LendingPool: penalty+interest will not bring pool to healthy state" } uint feeableInterestAmount = assets - penalty; if (feeableInterestAmount > borrowerOutstandingInterest()) { feeableInterestAmount = borrowerOutstandingInterest(); } uint assetsToSendToFeeSharing = (feeableInterestAmount * protocolFeeWad) / WAD + penalty; uint assetsForLenders = assets - assetsToSendToFeeSharing; borrowerInterestRepaid = borrowerInterestRepaid + assets - penalty; if (assetsToSendToFeeSharing > 0) { SafeERC20.safeTransfer(_stableCoinContract(), feeSharingContractAddress, assetsToSendToFeeSharing); } SafeERC20.safeTransferFrom(_stableCoinContract(), _msgSender(), address(this), assets); if (penalty > 0) { emit BorrowerPayPenalty(_msgSender(), penalty); } emit BorrowerPayInterest(borrowerAddress, assets, assetsForLenders, assetsToSendToFeeSharing); } /** @notice Repay principal * can be called only after all interest is paid * can be called only after all penalties are paid */ function borrowerRepayPrincipal() external onlyPoolBorrower atStage(Stages.BORROWED) whenNotPaused { require(borrowerOutstandingInterest() == 0, "LP203"); // "LendingPool: interest must be paid before repaying principal" require(borrowerPenaltyAmount() == 0, "LP204"); // "LendingPool: penalty must be paid before repaying principal" _transitionToPrincipalRepaidStage(borrowedAssets); TrancheVault[] memory vaults = trancheVaultContracts(); SafeERC20.safeTransferFrom(_stableCoinContract(), _msgSender(), address(this), borrowedAssets); for (uint i; i < tranchesCount; ++i) { TrancheVault tv = vaults[i]; SafeERC20.safeTransfer(_stableCoinContract(), address(tv), tv.totalAssets()); tv.enableWithdrawals(); } } /** @notice Withdraw first loss capital and excess spread * can be called only after principal is repaid */ function borrowerWithdrawFirstLossCapitalAndExcessSpread() external onlyPoolBorrower atStage(Stages.REPAID) whenNotPaused { uint assetsToSend = firstLossAssets + borrowerExcessSpread(); _transitionToFlcWithdrawnStage(assetsToSend); SafeERC20.safeTransfer(_stableCoinContract(), borrowerAddress, assetsToSend); } /* VIEWS */ /** @notice Pool balance threshold. * if pool balance fallse below this threshold, the pool is considered delinquent and the borrower starts to face penalties. */ function poolBalanceThreshold() public view returns (uint) { return PoolCalculations.poolBalanceThreshold(this); } /** @notice Pool balance * First loss capital minus whatever rewards are generated for the lenders by date. */ function poolBalance() public view returns (uint) { return PoolCalculations.poolBalance(this); } function lendersAt(uint i) public view returns (address) { return s_lenders.at(i); } function lenderCount() public view returns (uint256) { return s_lenders.length(); } /** @notice how much penalty the borrower owes because of the delinquency fact */ function borrowerPenaltyAmount() public view returns (uint) { if(currentStage > Stages.FLC_DEPOSITED) { return PoolCalculations.borrowerPenaltyAmount(this); } } /** @dev total interest to be paid by borrower = adjustedBorrowerAPR * collectedAssets * @return interest amount of assets to be repaid */ function borrowerExpectedInterest() public view returns (uint) { return PoolCalculations.borrowerExpectedInterest(collectedAssets, borrowerAdjustedInterestRateWad()); } /** @dev outstanding borrower interest = expectedBorrowerInterest - borrowerInterestAlreadyPaid * @return interest amount of outstanding assets to be repaid */ function borrowerOutstandingInterest() public view returns (uint) { return PoolCalculations.borrowerOutstandingInterest(borrowerInterestRepaid, borrowerExpectedInterest()); } /** @notice excess spread = interest paid by borrower - interest paid to lenders - fees * Once the pool ends, can be withdrawn by the borrower alongside the first loss capital */ function borrowerExcessSpread() public view returns (uint) { return PoolCalculations.borrowerExcessSpread(this); } /** @dev adjusted borrower interest rate = APR * duration / 365 days * @return adj borrower interest rate adjusted by duration of the loan */ function borrowerAdjustedInterestRateWad() public view returns (uint adj) { return PoolCalculations.borrowerAdjustedInterestRateWad(borrowerTotalInterestRateWad, lendingTermSeconds); } /*/////////////////////////////////// COMMUNICATION WITH VAULTS Error group: 3 ///////////////////////////////////*/ /// @dev TrancheVault will call that callback function when a lender deposits assets function onTrancheDeposit( uint8 trancheId, address depositorAddress, uint amount ) external authTrancheVault(trancheId) { // 1. find / create the rewardable Rewardable storage rewardable = s_trancheRewardables[trancheId][depositorAddress]; // 2. add lender to the lenders set s_lenders.add(depositorAddress); // 3. add to the staked assets rewardable.stakedAssets += amount; collectedAssets += amount; s_totalStakedAssetsByTranche[trancheId] += amount; // 4. set the start of the rewardable rewardable.start = uint64(block.timestamp); emit LenderDeposit(depositorAddress, trancheId, amount); _emitLenderTrancheRewardsChange(depositorAddress, trancheId); } /// @dev TrancheVault will call that callback function when a lender withdraws assets function onTrancheWithdraw( uint8 trancheId, address depositorAddress, uint amount ) external authTrancheVault(trancheId) whenNotPaused { require(!s_rollOverSettings[depositorAddress].principal, "LP301"); // "LendingPool: principal locked for rollover" if (currentStage == Stages.REPAID || currentStage == Stages.FLC_WITHDRAWN) { emit LenderWithdraw(depositorAddress, trancheId, amount); } else { Rewardable storage rewardable = s_trancheRewardables[trancheId][depositorAddress]; assert(rewardable.stakedAssets >= amount); rewardable.stakedAssets -= amount; collectedAssets -= amount; s_totalStakedAssetsByTranche[trancheId] -= amount; if (rewardable.stakedAssets == 0) { s_lenders.remove(depositorAddress); } emit LenderWithdraw(depositorAddress, trancheId, amount); _emitLenderTrancheRewardsChange(depositorAddress, trancheId); } } /*/////////////////////////////////// HELPERS ///////////////////////////////////*/ function trancheVaultContracts() internal view returns (TrancheVault[] memory) { return PoolCalculations.trancheVaultContracts(this); } function _emitLenderTrancheRewardsChange(address lenderAddress, uint8 trancheId) internal { emit LenderTrancheRewardsChange( lenderAddress, trancheId, lenderEffectiveAprByTrancheWad(lenderAddress, trancheId), lenderTotalExpectedRewardsByTranche(lenderAddress, trancheId), lenderRewardsByTrancheRedeemed(lenderAddress, trancheId) ); } function _stableCoinContract() internal view returns (IERC20) { return IERC20(stableCoinContractAddress); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ function __Ownable_init() internal onlyInitializing { __Ownable_init_unchained(); } function __Ownable_init_unchained() internal onlyInitializing { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol) pragma solidity ^0.8.0; import "../token/ERC20/IERC20Upgradeable.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (interfaces/IERC4626.sol) pragma solidity ^0.8.0; import "../token/ERC20/IERC20Upgradeable.sol"; import "../token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; /** * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626]. * * _Available since v4.7._ */ interface IERC4626Upgradeable is IERC20Upgradeable, IERC20MetadataUpgradeable { event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /** * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing. * * - MUST be an ERC-20 token contract. * - MUST NOT revert. */ function asset() external view returns (address assetTokenAddress); /** * @dev Returns the total amount of the underlying asset that is “managed” by Vault. * * - SHOULD include any compounding that occurs from yield. * - MUST be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT revert. */ function totalAssets() external view returns (uint256 totalManagedAssets); /** * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToShares(uint256 assets) external view returns (uint256 shares); /** * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal * scenario where all the conditions are met. * * - MUST NOT be inclusive of any fees that are charged against assets in the Vault. * - MUST NOT show any variations depending on the caller. * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange. * - MUST NOT revert. * * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and * from. */ function convertToAssets(uint256 shares) external view returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver, * through a deposit call. * * - MUST return a limited value if receiver is subject to some deposit limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited. * - MUST NOT revert. */ function maxDeposit(address receiver) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given * current on-chain conditions. * * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit * call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called * in the same transaction. * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the * deposit would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewDeposit(uint256 assets) external view returns (uint256 shares); /** * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * deposit execution, and are accounted for during deposit. * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function deposit(uint256 assets, address receiver) external returns (uint256 shares); /** * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call. * - MUST return a limited value if receiver is subject to some mint limit. * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted. * - MUST NOT revert. */ function maxMint(address receiver) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given * current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call * in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the * same transaction. * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint * would be accepted, regardless if the user has enough tokens approved, etc. * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by minting. */ function previewMint(uint256 shares) external view returns (uint256 assets); /** * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens. * * - MUST emit the Deposit event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint * execution, and are accounted for during mint. * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not * approving enough underlying tokens to the Vault contract, etc). * * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token. */ function mint(uint256 shares, address receiver) external returns (uint256 assets); /** * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the * Vault, through a withdraw call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST NOT revert. */ function maxWithdraw(address owner) external view returns (uint256 maxAssets); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block, * given current on-chain conditions. * * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw * call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if * called * in the same transaction. * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though * the withdrawal would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by depositing. */ function previewWithdraw(uint256 assets) external view returns (uint256 shares); /** * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * withdraw execution, and are accounted for during withdraw. * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function withdraw( uint256 assets, address receiver, address owner ) external returns (uint256 shares); /** * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault, * through a redeem call. * * - MUST return a limited value if owner is subject to some withdrawal limit or timelock. * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock. * - MUST NOT revert. */ function maxRedeem(address owner) external view returns (uint256 maxShares); /** * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block, * given current on-chain conditions. * * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call * in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the * same transaction. * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the * redemption would be accepted, regardless if the user has enough shares, etc. * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees. * - MUST NOT revert. * * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in * share price or some other type of condition, meaning the depositor will lose assets by redeeming. */ function previewRedeem(uint256 shares) external view returns (uint256 assets); /** * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver. * * - MUST emit the Withdraw event. * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the * redeem execution, and are accounted for during redeem. * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner * not having enough shares, etc). * * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed. * Those methods should be performed separately. */ function redeem( uint256 shares, address receiver, address owner ) external returns (uint256 assets); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ``` * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized < type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/ContextUpgradeable.sol"; import "../proxy/utils/Initializable.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract PausableUpgradeable is Initializable, ContextUpgradeable { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ function __Pausable_init() internal onlyInitializing { __Pausable_init_unchained(); } function __Pausable_init_unchained() internal onlyInitializing { _paused = false; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { _requireNotPaused(); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { _requirePaused(); _; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Throws if the contract is paused. */ function _requireNotPaused() internal view virtual { require(!paused(), "Pausable: paused"); } /** * @dev Throws if the contract is not paused. */ function _requirePaused() internal view virtual { require(paused(), "Pausable: not paused"); } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20Upgradeable.sol"; import "./extensions/IERC20MetadataUpgradeable.sol"; import "../../utils/ContextUpgradeable.sol"; import "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * The default value of {decimals} is 18. To select a different value for * {decimals} you should overload it. * * All two of these values are immutable: they can only be set once during * construction. */ function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing { __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the value {ERC20} uses, unless this function is * overridden; * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom( address from, address to, uint256 amount ) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer( address from, address to, uint256 amount ) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve( address owner, address spender, uint256 amount ) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance( address owner, address spender, uint256 amount ) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual {} /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[45] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20PermitUpgradeable { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.1) (token/ERC20/extensions/ERC4626.sol) pragma solidity ^0.8.0; import "../ERC20Upgradeable.sol"; import "../utils/SafeERC20Upgradeable.sol"; import "../../../interfaces/IERC4626Upgradeable.sol"; import "../../../utils/math/MathUpgradeable.sol"; import "../../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the ERC4626 "Tokenized Vault Standard" as defined in * https://eips.ethereum.org/EIPS/eip-4626[EIP-4626]. * * This extension allows the minting and burning of "shares" (represented using the ERC20 inheritance) in exchange for * underlying "assets" through standardized {deposit}, {mint}, {redeem} and {burn} workflows. This contract extends * the ERC20 standard. Any additional extensions included along it would affect the "shares" token represented by this * contract and not the "assets" token which is an independent contract. * * CAUTION: When the vault is empty or nearly empty, deposits are at high risk of being stolen through frontrunning with * a "donation" to the vault that inflates the price of a share. This is variously known as a donation or inflation * attack and is essentially a problem of slippage. Vault deployers can protect against this attack by making an initial * deposit of a non-trivial amount of the asset, such that price manipulation becomes infeasible. Withdrawals may * similarly be affected by slippage. Users can protect against this attack as well unexpected slippage in general by * verifying the amount received is as expected, using a wrapper that performs these checks such as * https://github.com/fei-protocol/ERC4626#erc4626router-and-base[ERC4626Router]. * * _Available since v4.7._ */ abstract contract ERC4626Upgradeable is Initializable, ERC20Upgradeable, IERC4626Upgradeable { using MathUpgradeable for uint256; IERC20Upgradeable private _asset; uint8 private _decimals; /** * @dev Set the underlying asset contract. This must be an ERC20-compatible contract (ERC20 or ERC777). */ function __ERC4626_init(IERC20Upgradeable asset_) internal onlyInitializing { __ERC4626_init_unchained(asset_); } function __ERC4626_init_unchained(IERC20Upgradeable asset_) internal onlyInitializing { (bool success, uint8 assetDecimals) = _tryGetAssetDecimals(asset_); _decimals = success ? assetDecimals : super.decimals(); _asset = asset_; } /** * @dev Attempts to fetch the asset decimals. A return value of false indicates that the attempt failed in some way. */ function _tryGetAssetDecimals(IERC20Upgradeable asset_) private view returns (bool, uint8) { (bool success, bytes memory encodedDecimals) = address(asset_).staticcall( abi.encodeWithSelector(IERC20MetadataUpgradeable.decimals.selector) ); if (success && encodedDecimals.length >= 32) { uint256 returnedDecimals = abi.decode(encodedDecimals, (uint256)); if (returnedDecimals <= type(uint8).max) { return (true, uint8(returnedDecimals)); } } return (false, 0); } /** * @dev Decimals are read from the underlying asset in the constructor and cached. If this fails (e.g., the asset * has not been created yet), the cached value is set to a default obtained by `super.decimals()` (which depends on * inheritance but is most likely 18). Override this function in order to set a guaranteed hardcoded value. * See {IERC20Metadata-decimals}. */ function decimals() public view virtual override(IERC20MetadataUpgradeable, ERC20Upgradeable) returns (uint8) { return _decimals; } /** @dev See {IERC4626-asset}. */ function asset() public view virtual override returns (address) { return address(_asset); } /** @dev See {IERC4626-totalAssets}. */ function totalAssets() public view virtual override returns (uint256) { return _asset.balanceOf(address(this)); } /** @dev See {IERC4626-convertToShares}. */ function convertToShares(uint256 assets) public view virtual override returns (uint256 shares) { return _convertToShares(assets, MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-convertToAssets}. */ function convertToAssets(uint256 shares) public view virtual override returns (uint256 assets) { return _convertToAssets(shares, MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-maxDeposit}. */ function maxDeposit(address) public view virtual override returns (uint256) { return _isVaultCollateralized() ? type(uint256).max : 0; } /** @dev See {IERC4626-maxMint}. */ function maxMint(address) public view virtual override returns (uint256) { return type(uint256).max; } /** @dev See {IERC4626-maxWithdraw}. */ function maxWithdraw(address owner) public view virtual override returns (uint256) { return _convertToAssets(balanceOf(owner), MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-maxRedeem}. */ function maxRedeem(address owner) public view virtual override returns (uint256) { return balanceOf(owner); } /** @dev See {IERC4626-previewDeposit}. */ function previewDeposit(uint256 assets) public view virtual override returns (uint256) { return _convertToShares(assets, MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-previewMint}. */ function previewMint(uint256 shares) public view virtual override returns (uint256) { return _convertToAssets(shares, MathUpgradeable.Rounding.Up); } /** @dev See {IERC4626-previewWithdraw}. */ function previewWithdraw(uint256 assets) public view virtual override returns (uint256) { return _convertToShares(assets, MathUpgradeable.Rounding.Up); } /** @dev See {IERC4626-previewRedeem}. */ function previewRedeem(uint256 shares) public view virtual override returns (uint256) { return _convertToAssets(shares, MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-deposit}. */ function deposit(uint256 assets, address receiver) public virtual override returns (uint256) { require(assets <= maxDeposit(receiver), "ERC4626: deposit more than max"); uint256 shares = previewDeposit(assets); _deposit(_msgSender(), receiver, assets, shares); return shares; } /** @dev See {IERC4626-mint}. * * As opposed to {deposit}, minting is allowed even if the vault is in a state where the price of a share is zero. * In this case, the shares will be minted without requiring any assets to be deposited. */ function mint(uint256 shares, address receiver) public virtual override returns (uint256) { require(shares <= maxMint(receiver), "ERC4626: mint more than max"); uint256 assets = previewMint(shares); _deposit(_msgSender(), receiver, assets, shares); return assets; } /** @dev See {IERC4626-withdraw}. */ function withdraw( uint256 assets, address receiver, address owner ) public virtual override returns (uint256) { require(assets <= maxWithdraw(owner), "ERC4626: withdraw more than max"); uint256 shares = previewWithdraw(assets); _withdraw(_msgSender(), receiver, owner, assets, shares); return shares; } /** @dev See {IERC4626-redeem}. */ function redeem( uint256 shares, address receiver, address owner ) public virtual override returns (uint256) { require(shares <= maxRedeem(owner), "ERC4626: redeem more than max"); uint256 assets = previewRedeem(shares); _withdraw(_msgSender(), receiver, owner, assets, shares); return assets; } /** * @dev Internal conversion function (from assets to shares) with support for rounding direction. * * Will revert if assets > 0, totalSupply > 0 and totalAssets = 0. That corresponds to a case where any asset * would represent an infinite amount of shares. */ function _convertToShares(uint256 assets, MathUpgradeable.Rounding rounding) internal view virtual returns (uint256 shares) { uint256 supply = totalSupply(); return (assets == 0 || supply == 0) ? _initialConvertToShares(assets, rounding) : assets.mulDiv(supply, totalAssets(), rounding); } /** * @dev Internal conversion function (from assets to shares) to apply when the vault is empty. * * NOTE: Make sure to keep this function consistent with {_initialConvertToAssets} when overriding it. */ function _initialConvertToShares( uint256 assets, MathUpgradeable.Rounding /*rounding*/ ) internal view virtual returns (uint256 shares) { return assets; } /** * @dev Internal conversion function (from shares to assets) with support for rounding direction. */ function _convertToAssets(uint256 shares, MathUpgradeable.Rounding rounding) internal view virtual returns (uint256 assets) { uint256 supply = totalSupply(); return (supply == 0) ? _initialConvertToAssets(shares, rounding) : shares.mulDiv(totalAssets(), supply, rounding); } /** * @dev Internal conversion function (from shares to assets) to apply when the vault is empty. * * NOTE: Make sure to keep this function consistent with {_initialConvertToShares} when overriding it. */ function _initialConvertToAssets( uint256 shares, MathUpgradeable.Rounding /*rounding*/ ) internal view virtual returns (uint256 assets) { return shares; } /** * @dev Deposit/mint common workflow. */ function _deposit( address caller, address receiver, uint256 assets, uint256 shares ) internal virtual { // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the // assets are transferred and before the shares are minted, which is a valid state. // slither-disable-next-line reentrancy-no-eth SafeERC20Upgradeable.safeTransferFrom(_asset, caller, address(this), assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } /** * @dev Withdraw/redeem common workflow. */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal virtual { if (caller != owner) { _spendAllowance(owner, caller, shares); } // If _asset is ERC777, `transfer` can trigger a reentrancy AFTER the transfer happens through the // `tokensReceived` hook. On the other hand, the `tokensToSend` hook, that is triggered before the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer after the burn so that any reentrancy would happen after the // shares are burned and after the assets are transferred, which is a valid state. _burn(owner, shares); SafeERC20Upgradeable.safeTransfer(_asset, receiver, assets); emit Withdraw(caller, receiver, owner, assets, shares); } /** * @dev Checks if vault is "healthy" in the sense of having assets backing the circulating shares. */ function _isVaultCollateralized() private view returns (bool) { return totalAssets() > 0 || totalSupply() == 0; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20MetadataUpgradeable is IERC20Upgradeable { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; import "../extensions/draft-IERC20PermitUpgradeable.sol"; import "../../../utils/AddressUpgradeable.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20Upgradeable { using AddressUpgradeable for address; function safeTransfer( IERC20Upgradeable token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20Upgradeable token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20Upgradeable token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20Upgradeable token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20PermitUpgradeable token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20Upgradeable token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; import "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library MathUpgradeable { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSetUpgradeable { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (proxy/Clones.sol) pragma solidity ^0.8.0; /** * @dev https://eips.ethereum.org/EIPS/eip-1167[EIP 1167] is a standard for * deploying minimal proxy contracts, also known as "clones". * * > To simply and cheaply clone contract functionality in an immutable way, this standard specifies * > a minimal bytecode implementation that delegates all calls to a known, fixed address. * * The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2` * (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the * deterministic method. * * _Available since v3.4._ */ library Clones { /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create opcode, which should never revert. */ function clone(address implementation) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create(0, 0x09, 0x37) } require(instance != address(0), "ERC1167: create failed"); } /** * @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`. * * This function uses the create2 opcode and a `salt` to deterministically deploy * the clone. Using the same `implementation` and `salt` multiple time will revert, since * the clones cannot be deployed twice at the same address. */ function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) { /// @solidity memory-safe-assembly assembly { // Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes // of the `implementation` address with the bytecode before the address. mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000)) // Packs the remaining 17 bytes of `implementation` with the bytecode after the address. mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3)) instance := create2(0, 0x09, 0x37, salt) } require(instance != address(0), "ERC1167: create2 failed"); } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress( address implementation, bytes32 salt, address deployer ) internal pure returns (address predicted) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(add(ptr, 0x38), deployer) mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff) mstore(add(ptr, 0x14), implementation) mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73) mstore(add(ptr, 0x58), salt) mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37)) predicted := keccak256(add(ptr, 0x43), 0x55) } } /** * @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}. */ function predictDeterministicAddress(address implementation, bytes32 salt) internal view returns (address predicted) { return predictDeterministicAddress(implementation, salt, address(this)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom( address from, address to, uint256 amount ) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/draft-IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; function safeTransfer( IERC20 token, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } function safeTransferFrom( IERC20 token, address from, address to, uint256 value ) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove( IERC20 token, address spender, uint256 value ) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } function safeIncreaseAllowance( IERC20 token, address spender, uint256 value ) internal { uint256 newAllowance = token.allowance(address(this), spender) + value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } function safeDecreaseAllowance( IERC20 token, address spender, uint256 value ) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); uint256 newAllowance = oldAllowance - value; _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance)); } } function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); if (returndata.length > 0) { // Return data is optional require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv( uint256 x, uint256 y, uint256 denominator, Rounding rounding ) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10**64) { value /= 10**64; result += 64; } if (value >= 10**32) { value /= 10**32; result += 32; } if (value >= 10**16) { value /= 10**16; result += 16; } if (value >= 10**8) { value /= 10**8; result += 8; } if (value >= 10**4) { value /= 10**4; result += 4; } if (value >= 10**2) { value /= 10**2; result += 2; } if (value >= 10**1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import "./IAuthority.sol"; /** * @title Authority Whitelist smart contract * @notice this contract manages a whitelists for all the admins, borrowers and lenders */ contract Authority is OwnableUpgradeable, IAuthority { using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; event BorrowerAdded(address indexed actor, address indexed borrower); event BorrowerRemoved(address indexed actor, address indexed borrower); event LenderAdded(address indexed actor, address indexed lender); event LenderRemoved(address indexed actor, address indexed lender); event AdminAdded(address indexed actor, address indexed admin); event AdminRemoved(address indexed actor, address indexed admin); EnumerableSetUpgradeable.AddressSet whitelistedBorrowers; EnumerableSetUpgradeable.AddressSet whitelistedLenders; EnumerableSetUpgradeable.AddressSet admins; /** * @notice Restricts function execution to the contract owner or admins * @dev Throws an error if the caller is not the owner or admin */ modifier onlyOwnerOrAdmin() { require(owner() == msg.sender || admins.contains(msg.sender), "Authority: caller is not the owner or admin"); _; } /// @dev initializer function initialize() external initializer { __Ownable_init(); } constructor() { _disableInitializers(); } /** * @notice adds borrower address to the whitelist. * @param a address to add to the whitelist * @dev can only be called by the contract owner or admins */ function addBorrower(address a) external onlyOwnerOrAdmin { if (whitelistedBorrowers.add(a)) { emit BorrowerAdded(msg.sender, a); } } /** * @notice removes borrower address from the whitelist. * @param a address to remove from the whitelist * @dev can only be called by the contract owner or admins */ function removeBorrower(address a) external onlyOwnerOrAdmin { if (whitelistedBorrowers.remove(a)) { emit BorrowerRemoved(msg.sender, a); } } /** * @notice checks if the borrower address is in the whitelist. * @param a address to check * @return true if the address is in the whitelist */ function isWhitelistedBorrower(address a) external view returns (bool) { return whitelistedBorrowers.contains(a); } /** * @notice returns array of all whitelisted borrower addresses * */ function allBorrowers() external view returns (address[] memory) { return whitelistedBorrowers.values(); } /** * @notice adds lenders address to the whitelist. * @param lender address to add to the whitelist */ function addLender(address lender) external onlyOwnerOrAdmin { if (whitelistedLenders.add(lender)) { emit LenderAdded(msg.sender, lender); } } /** * @notice removes lenders address from the whitelist. * @param lender address to remove from the whitelist */ function removeLender(address lender) external onlyOwnerOrAdmin { if (whitelistedLenders.remove(lender)) { emit LenderRemoved(msg.sender, lender); } } /** * @notice checks if the lender address is in the whitelist. * @param lender address to check * @return true if the address is in the whitelist */ function isWhitelistedLender(address lender) external view returns (bool) { return whitelistedLenders.contains(lender); } /// @notice returns array of all whitelisted lender addresses function allLenders() external view returns (address[] memory) { return whitelistedLenders.values(); } /** * @notice adds admin address to the list. * @param newAdmin address to add to the list */ function addAdmin(address newAdmin) external onlyOwnerOrAdmin { if (admins.add(newAdmin)) { emit AdminAdded(msg.sender, newAdmin); } } /** * @notice removes admin address from the list. * @param admin address to remove from the list */ function removeAdmin(address admin) external onlyOwnerOrAdmin { if (admins.remove(admin)) { emit AdminRemoved(msg.sender, admin); } } /** * @notice checks if the admin in the list. * @param a address to check * @return true if the address is in the list */ function isAdmin(address a) external view returns (bool) { return admins.contains(a); } /** * @notice returns array of all admin addresses */ function allAdmins() external view returns (address[] memory) { return admins.values(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "./IAuthority.sol"; /** * @title Authority Whitelist smart contract * @notice this contract manages a whitelists for all the admins, borrowers and lenders */ abstract contract AuthorityAware is OwnableUpgradeable { using EnumerableSet for EnumerableSet.AddressSet; IAuthority public authority; bytes32[50] private __gaps; modifier onlyOwnerOrAdmin() { _onlyOwnerOrAdmin(); _; } function _onlyOwnerOrAdmin() internal view { require( owner() == msg.sender || authority.isAdmin(msg.sender), "AA:OA" // "AuthorityAware: caller is not the owner or admin" ); } modifier onlyLender() { // only whitelisted lender require( authority.isWhitelistedLender(msg.sender), "AA:L" // "AuthorityAware: caller is not a whitelisted lender" ); _; } modifier onlyWhitelisted() { _onlyWhitelisted(); _; } function _onlyWhitelisted() internal view { require( owner() == msg.sender || authority.isWhitelistedBorrower(msg.sender) || authority.isWhitelistedLender(msg.sender) || authority.isAdmin(msg.sender), "AA:W" // "AuthorityAware: caller is not a whitelisted borrower or lender" ); } function __AuthorityAware__init(address _authority) internal { authority = IAuthority(_authority); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; /** * @title Authority Whitelist smart contract interface * @notice this contract manages a whitelists for all the admins, borrowers and lenders */ interface IAuthority { function isWhitelistedBorrower(address a) external view returns (bool); function isWhitelistedLender(address a) external view returns (bool); function isAdmin(address a) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import "@openzeppelin/contracts/proxy/Clones.sol"; import "@openzeppelin/contracts/utils/Strings.sol"; import "@openzeppelin/contracts/utils/math/Math.sol"; import "../authority/AuthorityAware.sol"; import "../pool/LendingPool.sol"; contract PoolFactory is AuthorityAware { using Math for uint; struct PoolRecord { string name; string tokenName; address poolAddress; address firstTrancheVaultAddress; address secondTrancheVaultAddress; address poolImplementationAddress; address trancheVaultImplementationAddress; } uint private constant WAD = 10 ** 18; event PoolCloned(address indexed addr, address implementationAddress); event TrancheVaultCloned(address indexed addr, address implementationAddress); event PoolDeployed(address indexed deployer, PoolRecord record); address public poolImplementationAddress; address public trancheVaultImplementationAddress; PoolRecord[] public poolRegistry; address public feeSharingContractAddress; /// @dev we need to track a nonce as salt for each implementation mapping(address => uint256) public nonces; mapping(address => bool) public prevDeployedTranche; function initialize(address _authority) public initializer { __Ownable_init(); __AuthorityAware__init(_authority); } constructor() { _disableInitializers(); } /// @notice it should be expressed that updating implemetation will make nonces at prior implementation stale /// @dev sets implementation for future pool deployments function setPoolImplementation(address implementation) external onlyOwnerOrAdmin { poolImplementationAddress = implementation; } /// @dev sets implementation for future tranche vault deployments function setTrancheVaultImplementation(address implementation) external onlyOwnerOrAdmin { trancheVaultImplementationAddress = implementation; } function setFeeSharingContractAddress(address implementation) external onlyOwnerOrAdmin { feeSharingContractAddress = implementation; } /// @dev returns last deployed pool record function lastDeployedPoolRecord() external view returns (PoolRecord memory p) { p = poolRegistry[poolRegistry.length - 1]; } /// @dev removes all the pool records from storage function clearPoolRecords() external onlyOwnerOrAdmin { delete poolRegistry; } /// @dev gets the length of the pool of records function poolRecordsLength() external view returns (uint) { return poolRegistry.length; } /** @dev Deploys a clone of implementation as a new pool. * . See {LendingPool-initialize} */ function deployPool( LendingPool.LendingPoolParams calldata params, uint[][] calldata fundingSplitWads ) external onlyOwner returns (address) { // validate wad uint256 wadMax; uint256 wadMin; for(uint256 i = 0; i < fundingSplitWads.length; i++) { require(fundingSplitWads[i].length == 2, "LP026 - bad fundingSplitWads"); wadMax += fundingSplitWads[i][0]; wadMin += fundingSplitWads[i][1]; } require(wadMax == 1e18, "LP024 - bad max wad"); require(wadMin == 1e18, "LP027 - bad min wad"); address poolAddress = _clonePool(); address[] memory trancheVaultAddresses = _deployTrancheVaults( params, fundingSplitWads, poolAddress, _msgSender() ); initializePoolAndCreatePoolRecord(poolAddress, params, trancheVaultAddresses, feeSharingContractAddress); return poolAddress; } function _clonePool() internal onlyOwner returns (address poolAddress) { address impl = poolImplementationAddress; poolAddress = Clones.cloneDeterministic(impl, bytes32(nonces[impl]++)); emit PoolCloned(poolAddress, poolImplementationAddress); } function nextLender() public view returns(address) { return nextAddress(poolImplementationAddress); } function nextLenders() public view returns(address[4] memory lenders) { address impl = poolImplementationAddress; for(uint256 i = 0; i < lenders.length; i++) { lenders[i] = Clones.predictDeterministicAddress(impl, bytes32(nonces[impl] + i)); } } function nextTranches() public view returns(address[8] memory lenders) { address impl = trancheVaultImplementationAddress; for(uint256 i = 0; i < lenders.length; i++) { lenders[i] = Clones.predictDeterministicAddress(impl, bytes32(nonces[impl] + i)); } } function nextAddress(address impl) public view returns(address) { return Clones.predictDeterministicAddress(impl, bytes32(nonces[impl] + 1)); } function _deployTrancheVaults( LendingPool.LendingPoolParams calldata params, uint[][] calldata fundingSplitWads, address poolAddress, address ownerAddress ) internal onlyOwner returns (address[] memory trancheVaultAddresses) { require(params.tranchesCount > 0, "Error TrancheCount must be gt 0"); trancheVaultAddresses = new address[](params.tranchesCount); for (uint8 i; i < params.tranchesCount; ++i) { address impl = trancheVaultImplementationAddress; trancheVaultAddresses[i] = Clones.cloneDeterministic(impl, bytes32(nonces[impl]++)); emit TrancheVaultCloned(trancheVaultAddresses[i], impl); prevDeployedTranche[trancheVaultAddresses[i]] = true; TrancheVault(trancheVaultAddresses[i]).initialize( poolAddress, i, params.minFundingCapacity.mulDiv(fundingSplitWads[i][1], WAD), params.maxFundingCapacity.mulDiv(fundingSplitWads[i][0], WAD), string(abi.encodePacked(params.name, " Tranche ", Strings.toString(uint(i)), " Token")), string(abi.encodePacked("tv", Strings.toString(uint(i)), params.token)), params.stableCoinContractAddress, address(authority) ); TrancheVault(trancheVaultAddresses[i]).transferOwnership(ownerAddress); } } function initializePoolAndCreatePoolRecord( address poolAddress, LendingPool.LendingPoolParams calldata params, address[] memory trancheVaultAddresses, address _feeSharingContractAddress ) public onlyOwner { LendingPool(poolAddress).initialize( params, trancheVaultAddresses, _feeSharingContractAddress, address(authority), address(this) ); Ownable(poolAddress).transferOwnership(_msgSender()); PoolRecord memory record = PoolRecord( params.name, params.token, poolAddress, trancheVaultAddresses[0], trancheVaultAddresses.length > 1 ? trancheVaultAddresses[1] : address(0), poolImplementationAddress, trancheVaultImplementationAddress ); poolRegistry.push(record); emit PoolDeployed(_msgSender(), record); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IFeeSharing { function distributeFees() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; /// @notice interface for lending pool /// This is not the full interface, rather a bare mininum to connect other contracts to the pool interface ILendingPool { function onTrancheDeposit(uint8 trancheId, address depositorAddress, uint amount) external; function onTrancheWithdraw(uint8 trancheId, address depositorAddress, uint amount) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import "./LendingPool.sol"; import "../vaults/TrancheVault.sol"; import "../authority/Authority.sol"; library PoolCalculations { uint constant WAD = 10 ** 18; uint constant YEAR = 365 days; function _wadPow(uint _xWad, uint _n) internal pure returns (uint) { uint xWad = _xWad; uint n = _n; uint result = n % 2 != 0 ? xWad : WAD; for (n /= 2; n != 0; n /= 2) { xWad = (xWad * xWad) / WAD; if (n % 2 != 0) { result = (result * xWad) / WAD; } } return result; } function poolBalanceThreshold(LendingPool lendingPool) public view returns (uint) { uint borrowedAssets = lendingPool.borrowedAssets(); uint borrowerTotalInterestRateWad = lendingPool.borrowerTotalInterestRateWad(); uint repaymentRecurrenceDays = lendingPool.repaymentRecurrenceDays(); uint gracePeriodDays = lendingPool.gracePeriodDays(); uint firstLossAssets = lendingPool.firstLossAssets(); uint dailyBorrowerInterestAmount = (borrowedAssets * borrowerTotalInterestRateWad) / WAD / 365; uint interestGoDownAmount = (repaymentRecurrenceDays + gracePeriodDays) * dailyBorrowerInterestAmount; if (interestGoDownAmount > firstLossAssets) { return 0; } uint threshold = firstLossAssets - interestGoDownAmount; return threshold; } function poolBalance(LendingPool lendingPool) public view returns (uint) { uint firstLossAssets = lendingPool.firstLossAssets(); uint borrowerInterestRepaid = lendingPool.borrowerInterestRepaid(); uint allLendersInterestByDate = lendingPool.allLendersInterestByDate(); uint positiveBalance = firstLossAssets + borrowerInterestRepaid; if (allLendersInterestByDate > positiveBalance) { return 0; } return positiveBalance - allLendersInterestByDate; } function borrowerPenaltyAmount(LendingPool lendingPool) public view returns (uint) { uint poolBalance = lendingPool.poolBalance(); uint poolBalanceThreshold = lendingPool.poolBalanceThreshold(); uint collectedAssets = lendingPool.collectedAssets(); uint allLendersEffectiveAprWad = lendingPool.allLendersEffectiveAprWad(); uint penaltyRateWad = lendingPool.penaltyRateWad(); if (poolBalance >= poolBalanceThreshold) { return 0; } uint dailyLendersInterestAmount = (collectedAssets * allLendersEffectiveAprWad) / WAD / 365; uint balanceDifference = poolBalanceThreshold - poolBalance; uint daysDelinquent = balanceDifference / dailyLendersInterestAmount; if (daysDelinquent == 0) { return 0; } uint penaltyCoefficientWad = _wadPow(WAD + penaltyRateWad, daysDelinquent); uint penalty = (balanceDifference * penaltyCoefficientWad) / WAD - balanceDifference; return penalty; } function borrowerExpectedInterest( uint collectedAssets, uint borrowerAdjustedInterestRateWad ) public pure returns (uint) { return (collectedAssets * borrowerAdjustedInterestRateWad) / WAD; } function borrowerOutstandingInterest( uint borrowerInterestRepaid, uint borrowerExpectedInterest ) public pure returns (uint) { if (borrowerInterestRepaid > borrowerExpectedInterest) { return 0; } return borrowerExpectedInterest - borrowerInterestRepaid; } function borrowerExcessSpread(LendingPool lendingPool) public view returns (uint) { uint borrowerInterestRepaid = lendingPool.borrowerInterestRepaid(); uint allLendersInterest = lendingPool.allLendersInterest(); uint borrowerExpectedInterest = lendingPool.borrowerExpectedInterest(); uint protocolFeeWad = lendingPool.protocolFeeWad(); if (borrowerOutstandingInterest(borrowerInterestRepaid, borrowerExpectedInterest) > 0) { return 0; } uint fees = (borrowerExpectedInterest * protocolFeeWad) / WAD; return borrowerInterestRepaid - allLendersInterest - fees; } function borrowerAdjustedInterestRateWad( uint borrowerTotalInterestRateWad, uint lendingTermSeconds ) public pure returns (uint adj) { return (borrowerTotalInterestRateWad * lendingTermSeconds) / YEAR; } function lenderEffectiveAprByTrancheWad( LendingPool lendingPool, address lenderAddress, uint8 trancheId ) public view returns (uint) { uint stakedAssets = lendingPool.lenderStakedTokensByTranche(lenderAddress, trancheId); uint lockedPlatformTokens = lendingPool.lenderPlatformTokensByTrancheLocked(lenderAddress, trancheId); uint trancheBoostRatio = lendingPool.trancheBoostRatios(trancheId); uint trancheAPRWad = lendingPool.trancheAPRsWads(trancheId); uint trancheBoostedAPRWad = lendingPool.trancheBoostedAPRsWads(trancheId); if (stakedAssets == 0) { return 0; } uint boostedAssets = lockedPlatformTokens / trancheBoostRatio; if (boostedAssets > stakedAssets) { boostedAssets = stakedAssets; } uint unBoostedAssets = stakedAssets - boostedAssets; uint weightedAverage = (unBoostedAssets * trancheAPRWad + boostedAssets * trancheBoostedAPRWad) / stakedAssets; return weightedAverage; } function lenderRewardsByTrancheGeneratedByDate( LendingPool lendingPool, address lenderAddress, uint8 trancheId ) public view returns (uint) { uint fundedAt = lendingPool.fundedAt(); if (fundedAt == 0) { return 0; } uint lenderDepositedAssets = lendingPool.lenderDepositedAssetsByTranche(lenderAddress, trancheId); uint lenderEffectiveApr = lendingPool.lenderEffectiveAprByTrancheWad(lenderAddress, trancheId); uint lendingTermSeconds = lendingPool.lendingTermSeconds(); uint secondsElapsed = block.timestamp - fundedAt; if (secondsElapsed > lendingTermSeconds) { secondsElapsed = lendingTermSeconds; } return (lenderDepositedAssets * lenderEffectiveApr * secondsElapsed) / (YEAR * WAD); } function lenderTotalExpectedRewardsByTranche( uint lenderDepositedAssets, uint lenderEffectiveApr, uint lendingTermSeconds ) public pure returns (uint) { return (lenderDepositedAssets * lenderEffectiveApr * lendingTermSeconds) / (YEAR * WAD); } function lenderTotalAprWad(LendingPool lendingPool, address lenderAddress) public view returns (uint) { uint256 tranchesCount = lendingPool.tranchesCount(); uint weightedApysWad = 0; uint totalAssets = 0; for (uint8 i; i < tranchesCount; i++) { uint staked = lendingPool.lenderStakedTokensByTranche(lenderAddress, i); totalAssets += staked; weightedApysWad += (lendingPool.lenderEffectiveAprByTrancheWad(lenderAddress, i) * staked); } if (totalAssets == 0) { return 0; } return weightedApysWad / totalAssets; } function allLendersEffectiveAprWad(LendingPool lendingPool, uint256 tranchesCount) public view returns (uint) { uint weightedSum = 0; uint totalStakedAssets = 0; for (uint8 trancheId; trancheId < tranchesCount; trancheId++) { uint stakedAssets = lendingPool.s_totalStakedAssetsByTranche(trancheId); totalStakedAssets += stakedAssets; uint boostedAssets = lendingPool.s_totalLockedPlatformTokensByTranche(trancheId) / lendingPool.trancheBoostRatios(trancheId); if (boostedAssets > stakedAssets) { boostedAssets = stakedAssets; } uint unBoostedAssets = stakedAssets - boostedAssets; weightedSum += unBoostedAssets * lendingPool.trancheAPRsWads(trancheId); weightedSum += boostedAssets * lendingPool.trancheBoostedAPRsWads(trancheId); } return weightedSum / totalStakedAssets; } function allLendersInterestByDate(LendingPool lendingPool) public view returns (uint) { uint256 fundedAt = lendingPool.fundedAt(); uint256 lendingTermSeconds = lendingPool.lendingTermSeconds(); if (fundedAt == 0 || block.timestamp <= fundedAt) { return 0; } uint time = block.timestamp < fundedAt + lendingTermSeconds ? block.timestamp : fundedAt + lendingTermSeconds; uint elapsedTime = time - fundedAt; return (lendingPool.allLendersInterest() * elapsedTime) / lendingTermSeconds; } function trancheVaultContracts(LendingPool lendingPool) public view returns (TrancheVault[] memory contracts) { uint256 trancheCount = lendingPool.tranchesCount(); contracts = new TrancheVault[](trancheCount); for (uint i; i < contracts.length; ++i) { contracts[i] = TrancheVault(lendingPool.trancheVaultAddresses(i)); } } function validateInitParams( LendingPool.LendingPoolParams calldata params, address[] calldata _trancheVaultAddresses, address _feeSharingContractAddress, address _authorityAddress ) public view { require(params.stableCoinContractAddress != address(0), "LP005"); // "LendingPool: stableCoinContractAddress empty" require(params.minFundingCapacity > 0, "LP006"); // "LendingPool: minFundingCapacity == 0" require(params.maxFundingCapacity > 0, "LP007"); // "LendingPool: maxFundingCapacity == 0" require( params.maxFundingCapacity >= params.minFundingCapacity, "LP008" // "LendingPool: maxFundingCapacity < minFundingCapacity" ); require(params.fundingPeriodSeconds > 0, "LP009"); // "LendingPool: fundingPeriodSeconds == 0" require(params.lendingTermSeconds > 0, "LP010"); // "LendingPool: lendingTermSeconds == 0" require(params.borrowerAddress != address(0), "LP011"); // "LendingPool: borrowerAddress empty" require(Authority(_authorityAddress).isWhitelistedBorrower(params.borrowerAddress), "LP023"); require(params.borrowerTotalInterestRateWad > 0, "LP012"); // "LendingPool: borrower interest rate = 0%" require(params.protocolFeeWad > 0, "LP013"); // "LendingPool: protocolFee == 0%" require(params.penaltyRateWad > 0, "LP014"); // "LendingPool: penaltyRate == 0" require(params.tranchesCount > 0, "LP015"); // "LendingPool: tranchesCount == 0" require(_trancheVaultAddresses.length == params.tranchesCount, "LP016"); // "LendingPool: trancheAddresses length" require(params.trancheAPRsWads.length == params.tranchesCount, "LP017"); // "LP001");// "LendingPool: tranche APRs length" require( params.trancheBoostedAPRsWads.length == params.tranchesCount, "LP018" // "LendingPool: tranche Boosted APRs length" ); require( params.trancheBoostedAPRsWads.length == params.tranchesCount, "LP019" // "LendingPool: tranche Coverage APRs length" ); for (uint i; i < params.tranchesCount; ++i) { require(params.trancheAPRsWads[i] > 0, "tranche APRs == 0"); require( params.trancheBoostedAPRsWads[i] >= params.trancheAPRsWads[i], "LP020" // "LendingPool: tranche boosted APRs < tranche APRs" ); } require(_feeSharingContractAddress != address(0), "LP021"); // "LendingPool: feeSharingAddress empty" require(_authorityAddress != address(0), "LP022"); // "LendingPool: authorityAddress empty" } function validateWad(uint256[] memory ints) external pure { for (uint256 i = 0; i < ints.length; i++) { require(ints[i] <= 1e18, "LP024 - bad wad"); } } function setInitializer( LendingPool.LendingPoolParams calldata params, string storage name, string storage token, uint[] storage trancheAPRsWads, uint[] storage trancheBoostedAPRsWads, uint[] storage trancheBoostRatios, uint[] storage trancheCoveragesWads ) public { bytes memory nameBytes = bytes(params.name); bytes memory tokenBytes = bytes(params.token); for (uint i = 0; i < nameBytes.length; i++) { bytes(name)[i] = nameBytes[i]; } for (uint i = 0; i < tokenBytes.length; i++) { bytes(token)[i] = tokenBytes[i]; } for (uint i = 0; i < params.trancheAPRsWads.length; i++) { trancheAPRsWads[i] = params.trancheAPRsWads[i]; } for (uint i = 0; i < params.trancheBoostedAPRsWads.length; i++) { trancheBoostedAPRsWads[i] = params.trancheBoostedAPRsWads[i]; } for (uint i = 0; i < params.trancheBoostRatios.length; i++) { trancheBoostRatios[i] = params.trancheBoostRatios[i]; } for (uint i = 0; i < params.trancheCoveragesWads.length; i++) { trancheCoveragesWads[i] = params.trancheCoveragesWads[i]; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "./LendingPool.sol"; import "../factory/PoolFactory.sol"; import "../vaults/TrancheVault.sol"; library PoolTransfers { function lenderEnableRollOver( LendingPool lendingPool, bool principal, bool rewards, bool platformTokens, address lender ) external { PoolFactory poolFactory = PoolFactory(lendingPool.poolFactoryAddress()); uint256 lockedPlatformTokens; uint256 trancheCount = lendingPool.tranchesCount(); for (uint8 trancheId; trancheId < trancheCount; trancheId++) { (uint256 staked, , , ) = lendingPool.s_trancheRewardables(trancheId, lender); TrancheVault vault = TrancheVault(lendingPool.trancheVaultAddresses(trancheId)); (, uint256 locked, , ) = lendingPool.s_trancheRewardables(trancheId, lender); lockedPlatformTokens += locked; vault.approveRollover(lender, staked); } address[4] memory futureLenders = poolFactory.nextLenders(); for (uint256 i = 0; i < futureLenders.length; i++) { SafeERC20.safeApprove( IERC20(lendingPool.platformTokenContractAddress()), futureLenders[i], 0 ); // approve transfer of platform tokens SafeERC20.safeApprove( IERC20(lendingPool.platformTokenContractAddress()), futureLenders[i], lockedPlatformTokens ); SafeERC20.safeApprove( IERC20(lendingPool.stableCoinContractAddress()), futureLenders[i], 0 ); // approve transfer of the stablecoin contract SafeERC20.safeApprove( IERC20(lendingPool.stableCoinContractAddress()), // asume tranches.asset() == stablecoin address futureLenders[i], 2 ** 256 - 1 // infinity approve because we don't know how much interest will need to be accounted for ); } } function executeRollover( LendingPool lendingPool, address deadLendingPoolAddr, address[] memory deadTrancheAddrs, uint256 lenderStartIndex, uint256 lenderEndIndex ) external { uint256 tranchesCount = lendingPool.tranchesCount(); require(tranchesCount == deadTrancheAddrs.length, "tranche array mismatch"); require( keccak256(deadLendingPoolAddr.code) == keccak256(address(this).code), "rollover incampatible due to version mismatch" ); // upgrades to the next contract need to be set before users are allowed to rollover in the current contract // should do a check to ensure there aren't more than n protocols running in parallel, if this is true, the protocol will revert for reasons unknown to future devs LendingPool deadpool = LendingPool(deadLendingPoolAddr); for (uint256 i = lenderStartIndex; i <= lenderEndIndex; i++) { address lender = deadpool.lendersAt(i); LendingPool.RollOverSetting memory settings = LendingPool(deadLendingPoolAddr).lenderRollOverSettings(lender); if (!settings.enabled) { continue; } for (uint8 trancheId; trancheId < tranchesCount; trancheId++) { TrancheVault vault = TrancheVault(lendingPool.trancheVaultAddresses(trancheId)); uint256 rewards = settings.rewards ? deadpool.lenderRewardsByTrancheRedeemable(lender, trancheId) : 0; // lenderRewardsByTrancheRedeemable will revert if the lender has previously withdrawn // transfer rewards from dead lender to dead tranche SafeERC20.safeTransferFrom( IERC20(lendingPool.stableCoinContractAddress()), deadLendingPoolAddr, deadTrancheAddrs[trancheId], rewards ); vault.rollover(lender, deadTrancheAddrs[trancheId], rewards); } // ask deadpool to move platform token into this new contract IERC20 platoken = IERC20(lendingPool.platformTokenContractAddress()); uint256 platokens = platoken.allowance(deadLendingPoolAddr, address(this)); SafeERC20.safeTransferFrom(platoken, deadLendingPoolAddr, address(this), platokens); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.18; import "@openzeppelin/contracts-upgradeable/interfaces/IERC20Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC4626Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; import "../authority/AuthorityAware.sol"; import "../pool/LendingPool.sol"; import "../factory/PoolFactory.sol"; contract TrancheVault is Initializable, ERC4626Upgradeable, PausableUpgradeable, AuthorityAware { using MathUpgradeable for uint256; /*//////////////////////////////////////////////// State ////////////////////////////////////////////////*/ mapping(address => mapping(address => uint256)) approvedRollovers; /* id */ uint8 private s_id; event ChangeId(uint8 oldValue, uint8 newValue); function id() public view returns (uint8) { return s_id; } function _setId(uint8 newValue) internal { uint8 oldValue = s_id; s_id = newValue; emit ChangeId(oldValue, newValue); } /* poolAddress */ address private s_poolAddress; event ChangePoolAddress(address oldValue, address newValue); function poolAddress() public view returns (address) { return s_poolAddress; } function _setPoolAddress(address newValue) internal { address oldValue = s_poolAddress; s_poolAddress = newValue; emit ChangePoolAddress(oldValue, newValue); } /* minFundingCapacity */ uint256 private s_minFundingCapacity; event ChangeMinFundingCapacity(uint256 oldValue, uint256 newValue); function minFundingCapacity() public view returns (uint256) { return s_minFundingCapacity; } function _setMinFundingCapacity(uint256 newValue) internal { uint256 oldValue = s_minFundingCapacity; s_minFundingCapacity = newValue; emit ChangeMinFundingCapacity(oldValue, newValue); } /* maxFundingCapacity */ uint256 private s_maxFundingCapacity; event ChangeMaxFundingCapacity(uint256 oldValue, uint256 newValue); function maxFundingCapacity() public view returns (uint256) { return s_maxFundingCapacity; } function _setMaxFundingCapacity(uint256 newValue) internal { uint256 oldValue = s_maxFundingCapacity; s_maxFundingCapacity = newValue; emit ChangeMaxFundingCapacity(oldValue, newValue); } /* withdrawEnabled */ bool private s_withdrawEnabled; event ChangeWithdrawEnabled(address indexed actor, bool oldValue, bool newValue); function withdrawEnabled() public view returns (bool) { return s_withdrawEnabled; } function _setWithdrawEnabled(bool newValue) internal { bool oldValue = s_withdrawEnabled; s_withdrawEnabled = newValue; emit ChangeWithdrawEnabled(msg.sender, oldValue, newValue); } /* depositEnabled */ bool private s_depositEnabled; event ChangeDepositEnabled(address indexed actor, bool oldValue, bool newValue); function depositEnabled() public view returns (bool) { return s_depositEnabled; } function _setDepositEnabled(bool newValue) internal { bool oldValue = s_depositEnabled; s_depositEnabled = newValue; emit ChangeDepositEnabled(msg.sender, oldValue, newValue); } /* transferEnabled */ bool private s_transferEnabled; event ChangeTransferEnabled(address indexed actor, bool oldValue, bool newValue); function transferEnabled() public view returns (bool) { return s_transferEnabled; } function _setTransferEnabled(bool newValue) internal { bool oldValue = s_transferEnabled; s_transferEnabled = newValue; emit ChangeTransferEnabled(msg.sender, oldValue, newValue); } /* defaultRatio */ uint private s_defaultRatioWad; event ChangeDefaultRatio(address indexed actor, uint oldValue, uint newValue); function defaultRatioWad() public view returns (uint) { return s_defaultRatioWad; } function isDefaulted() public view returns (bool) { return s_defaultRatioWad != 0; } function _setDefaultRatioWad(uint newValue) internal { uint oldValue = s_defaultRatioWad; s_defaultRatioWad = newValue; emit ChangeDefaultRatio(msg.sender, oldValue, newValue); } function setDefaultRatioWad(uint newValue) external onlyPool { _setDefaultRatioWad(newValue); } /*//////////////////////////////////////////////// Modifiers ////////////////////////////////////////////////*/ modifier onlyPool() { require(_msgSender() == poolAddress(), "Vault: onlyPool"); _; } modifier onlyDeadTranche() { LendingPool pool = LendingPool(s_poolAddress); PoolFactory factory = PoolFactory(pool.poolFactoryAddress()); require(factory.prevDeployedTranche(msg.sender), "Vault: onlyDeadTranche"); _; } modifier onlyOwnerOrPool() { require(_msgSender() == poolAddress() || _msgSender() == owner(), "Vault: onlyOwnerOrPool"); _; } modifier whenWithdrawEnabled() { require(withdrawEnabled(), "Vault: withdraw disabled"); _; } modifier whenDepositEnabled() { require(depositEnabled(), "Vault: deposit disabled"); _; } modifier whenTransferEnabled() { require(transferEnabled(), "Vault: transfer disabled"); _; } function _isWhitelisted(address) internal virtual returns (bool) { return true; } /*//////////////////////////////////////////////// CONSTRUCTOR ////////////////////////////////////////////////*/ function initialize( address _poolAddress, uint8 _trancheId, uint _minCapacity, uint _maxCapacity, string memory _tokenName, string memory _symbol, address _underlying, address _authority ) external initializer { if (_minCapacity > _maxCapacity) { uint256 tmpMin = _minCapacity; _minCapacity = _maxCapacity; _maxCapacity = tmpMin; } require(_minCapacity <= _maxCapacity, "Vault: min > max"); _setPoolAddress(_poolAddress); _setId(_trancheId); _setMinFundingCapacity(_minCapacity); _setMaxFundingCapacity(_maxCapacity); __ERC20_init(_tokenName, _symbol); __Pausable_init(); __Ownable_init(); __ERC4626_init(IERC20Upgradeable(_underlying)); __AuthorityAware__init(_authority); } /*//////////////////////////////////////////////// ADMIN METHODS ////////////////////////////////////////////////*/ /** @dev enables deposits to the vault */ function enableDeposits() external onlyOwnerOrPool { _setDepositEnabled(true); } /** @dev disables deposits to the vault */ function disableDeposits() external onlyOwnerOrPool { _setDepositEnabled(false); } /** @dev enables withdrawals from the vault*/ function enableWithdrawals() external onlyOwnerOrPool { _setWithdrawEnabled(true); } /** @dev disables withdrawals from the vault*/ function disableWithdrawals() external onlyOwnerOrPool { _setWithdrawEnabled(false); } /** @dev enables vault token transfers */ function enableTransfers() external onlyOwnerOrPool { _setTransferEnabled(true); } /** @dev disables vault token transfers */ function disableTransfers() external onlyOwnerOrPool { _setTransferEnabled(false); } /** @dev Pauses the pool */ function pause() external onlyOwnerOrAdmin { _pause(); } /** @dev Unpauses the pool */ function unpause() external onlyOwnerOrAdmin { _unpause(); } /** @dev called by the pool in order to send assets*/ function sendAssetsToPool(uint assets) external onlyPool whenNotPaused { SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(asset()), poolAddress(), assets); } /**@dev used to approve the process of the rollover to deployments that do not yet exist (executed with older tranche before creation of next tranche) */ function approveRollover(address lender, uint256 assets) external onlyOwnerOrPool { LendingPool pool = LendingPool(poolAddress()); PoolFactory factory = PoolFactory(pool.poolFactoryAddress()); address[8] memory futureTranches = factory.nextTranches(); for (uint256 i = 0; i < futureTranches.length; i++) { //super.approve(futureTranches[i], convertToShares(amount)); approvedRollovers[lender][futureTranches[i]] = assets; } } function executeRolloverAndBurn(address lender, uint256 rewards) external onlyDeadTranche whenNotPaused returns (uint256) { TrancheVault newTranche = TrancheVault(_msgSender()); uint256 assets = approvedRollovers[lender][address(newTranche)] + rewards; SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(asset()), address(newTranche), assets); uint256 shares = convertToAssets(assets - rewards); _burn(lender, shares); return assets; } /**@dev used to process the rollover (executed with newer tranche on deploy) */ function rollover(address lender, address deadTrancheAddr, uint256 rewards) external onlyPool { TrancheVault deadTranche = TrancheVault(deadTrancheAddr); require(deadTranche.asset() == asset(), "Incompatible asset types"); // transfer in capital from prev tranche uint256 assetsRolled = deadTranche.executeRolloverAndBurn(lender, rewards); IERC20Upgradeable(asset()).approve(address(this), assetsRolled); uint256 shares = previewDeposit(assetsRolled); _deposit(address(this), lender, assetsRolled, shares); } /*//////////////////////////////////////////////// ERC-4626 Overrides ////////////////////////////////////////////////*/ /** @dev Deposit asset to the pool * See {IERC4626-deposit}. * @param assets amount of underlying asset to deposit * @param receiver receiver address (just set it to msg sender) * @return amount of pool tokens minted for the deposit */ function deposit( uint256 assets, address receiver ) public virtual override whenNotPaused onlyLender whenDepositEnabled returns (uint256) { return super.deposit(assets, receiver); } /** @dev See {IERC4626-mint} */ function mint( uint256 shares, address receiver ) public virtual override whenNotPaused onlyLender whenDepositEnabled returns (uint256) { return super.mint(shares, receiver); } /** @dev Withdraw principal from the pool * See {IERC4626-withdraw}. * @param assets amount of underlying asset to withdraw * @param receiver address to which the underlying assets should be sent * @param owner owner of the principal (just use msg sender) * @return amount of pool tokens burned after withdrawal */ function withdraw( uint256 assets, address receiver, address owner ) public override onlyLender whenNotPaused whenWithdrawEnabled returns (uint256) { return super.withdraw(assets, receiver, owner); } /** @dev See {IERC4626-redeem}. */ function redeem( uint256 shares, address receiver, address owner ) public override onlyLender whenNotPaused whenWithdrawEnabled returns (uint256) { return super.redeem(shares, receiver, owner); } /** @dev Maximum amount of assets that the vault will accept * See {IERC4626-maxDeposit}. * @param . lender address (just set it to msg sender) * @return maximum amount of assets that can be deposited to the pool */ function maxDeposit(address) public view override returns (uint256) { if (paused() || !depositEnabled()) { return 0; } if (totalAssets() >= maxFundingCapacity()) { return 0; } return maxFundingCapacity() - totalAssets(); } /** @dev See {IERC4626-totalAssets}. */ function totalAssets() public view override returns (uint256) { return convertToAssets(totalSupply()); } /** @dev See {IERC4626-maxMint}. */ function maxMint(address) public view override returns (uint256) { return convertToShares(maxDeposit(msg.sender)); } /** @dev See {IERC4626-maxWithdraw}. */ function maxWithdraw(address owner) public view override returns (uint256) { if (paused() || !withdrawEnabled()) { return 0; } return _convertToAssets(balanceOf(owner), MathUpgradeable.Rounding.Down); } /** @dev See {IERC4626-maxRedeem}. */ function maxRedeem(address owner) public view override returns (uint256) { if (paused() || !withdrawEnabled()) { return 0; } return balanceOf(owner); } /** @dev will return 1:1 */ function _convertToShares( uint256 assets, MathUpgradeable.Rounding rounding ) internal view override returns (uint256 shares) { if (isDefaulted()) { return assets.mulDiv(10 ** 18, s_defaultRatioWad, rounding); } return _initialConvertToShares(assets, rounding); } /** @dev will return 1:1 */ function _convertToAssets( uint256 shares, MathUpgradeable.Rounding rounding ) internal view override returns (uint256 assets) { if (isDefaulted()) { return shares.mulDiv(s_defaultRatioWad, 10 ** 18, rounding); } return _initialConvertToAssets(shares, rounding); // 1:1 } function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal whenNotPaused override { // If _asset is ERC777, `transferFrom` can trigger a reenterancy BEFORE the transfer happens through the // `tokensToSend` hook. On the other hand, the `tokenReceived` hook, that is triggered after the transfer, // calls the vault, which is assumed not malicious. // // Conclusion: we need to do the transfer before we mint so that any reentrancy would happen before the // assets are transferred and before the shares are minted, which is a valid state. // slither-disable-next-line reentrancy-no-eth SafeERC20Upgradeable.safeTransferFrom(IERC20Upgradeable(asset()), caller, address(this), assets); _mint(receiver, shares); LendingPool(poolAddress()).onTrancheDeposit(id(), receiver, assets); emit Deposit(caller, receiver, assets, shares); } /** * @dev Withdraw/redeem common workflow. */ function _withdraw( address caller, address receiver, address owner, uint256 assets, uint256 shares ) internal override whenNotPaused { if (caller != owner) { _spendAllowance(owner, caller, shares); } _burn(owner, shares); SafeERC20Upgradeable.safeTransfer(IERC20Upgradeable(asset()), receiver, assets); LendingPool(poolAddress()).onTrancheWithdraw(id(), owner, assets); emit Withdraw(caller, receiver, owner, assets, shares); } /*//////////////////////////////////////////////// ERC20Upgradeable overrides ////////////////////////////////////////////////*/ function _transfer(address, address, uint256) internal override whenNotPaused whenTransferEnabled { revert("Transfers are not implemented"); } }
{ "optimizer": { "enabled": true, "runs": 10 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": { "contracts/pool/PoolCalculations.sol": { "PoolCalculations": "0xb7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a" }, "contracts/pool/PoolTransfers.sol": { "PoolTransfers": "0xe77fb404549dcfb65d82812e1dfb7b931bddd533" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BorrowerBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BorrowerDepositFirstLossCapital","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"lendersDistributedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeSharingContractAmount","type":"uint256"}],"name":"BorrowerPayInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BorrowerPayPenalty","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BorrowerRepayPrincipal","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"BorrowerWithdrawFirstLossCapital","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender","type":"address"},{"indexed":true,"internalType":"uint8","name":"trancheId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LenderDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender","type":"address"},{"indexed":true,"internalType":"uint8","name":"trancheId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LenderLockPlatformTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender","type":"address"},{"indexed":true,"internalType":"uint8","name":"trancheId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"lenderEffectiveAprWad","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalExpectedRewards","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"redeemedRewards","type":"uint256"}],"name":"LenderTrancheRewardsChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender","type":"address"},{"indexed":true,"internalType":"uint8","name":"trancheId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LenderUnlockPlatformTokens","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender","type":"address"},{"indexed":true,"internalType":"uint8","name":"trancheId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LenderWithdraw","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"lender","type":"address"},{"indexed":true,"internalType":"uint8","name":"trancheId","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LenderWithdrawInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"defaultedAt","type":"uint64"}],"name":"PoolDefaulted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"flcWithdrawntAt","type":"uint64"}],"name":"PoolFirstLossCapitalWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"fundedAt","type":"uint64"},{"indexed":false,"internalType":"uint256","name":"collectedAssets","type":"uint256"}],"name":"PoolFunded","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"fundingFailedAt","type":"uint64"}],"name":"PoolFundingFailed","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"token","type":"string"},{"internalType":"address","name":"stableCoinContractAddress","type":"address"},{"internalType":"address","name":"platformTokenContractAddress","type":"address"},{"internalType":"uint256","name":"minFundingCapacity","type":"uint256"},{"internalType":"uint256","name":"maxFundingCapacity","type":"uint256"},{"internalType":"uint64","name":"fundingPeriodSeconds","type":"uint64"},{"internalType":"uint64","name":"lendingTermSeconds","type":"uint64"},{"internalType":"address","name":"borrowerAddress","type":"address"},{"internalType":"uint256","name":"firstLossAssets","type":"uint256"},{"internalType":"uint256","name":"borrowerTotalInterestRateWad","type":"uint256"},{"internalType":"uint256","name":"repaymentRecurrenceDays","type":"uint256"},{"internalType":"uint256","name":"gracePeriodDays","type":"uint256"},{"internalType":"uint256","name":"protocolFeeWad","type":"uint256"},{"internalType":"uint256","name":"defaultPenalty","type":"uint256"},{"internalType":"uint256","name":"penaltyRateWad","type":"uint256"},{"internalType":"uint8","name":"tranchesCount","type":"uint8"},{"internalType":"uint256[]","name":"trancheAPRsWads","type":"uint256[]"},{"internalType":"uint256[]","name":"trancheBoostedAPRsWads","type":"uint256[]"},{"internalType":"uint256[]","name":"trancheBoostRatios","type":"uint256[]"},{"internalType":"uint256[]","name":"trancheCoveragesWads","type":"uint256[]"}],"indexed":false,"internalType":"struct LendingPool.LendingPoolParams","name":"params","type":"tuple"},{"indexed":false,"internalType":"address[]","name":"_trancheVaultAddresses","type":"address[]"},{"indexed":false,"internalType":"address","name":"_feeSharingContractAddress","type":"address"},{"indexed":false,"internalType":"address","name":"_authorityAddress","type":"address"}],"name":"PoolInitialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"openedAt","type":"uint64"}],"name":"PoolOpen","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"repaidAt","type":"uint64"}],"name":"PoolRepaid","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"adminOpenPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adminTransitionToDefaultedState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"adminTransitionToFundedState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"allLendersEffectiveAprWad","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allLendersInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"allLendersInterestByDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract IAuthority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrow","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowedAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowedAt","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerAdjustedInterestRateWad","outputs":[{"internalType":"uint256","name":"adj","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerDepositFirstLossCapital","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowerExcessSpread","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerExpectedInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerInterestRepaid","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerOutstandingInterest","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"name":"borrowerPayInterest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowerPenaltyAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerRecoverFirstLossCapital","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowerRepayPrincipal","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"borrowerTotalInterestRateWad","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrowerWithdrawFirstLossCapitalAndExcessSpread","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collectedAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentStage","outputs":[{"internalType":"enum LendingPool.Stages","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultPenalty","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"defaultedAt","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"deadLendingPoolAddr","type":"address"},{"internalType":"address[]","name":"deadTrancheAddrs","type":"address[]"},{"internalType":"uint256","name":"lenderStartIndex","type":"uint256"},{"internalType":"uint256","name":"lenderEndIndex","type":"uint256"}],"name":"executeRollover","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeSharingContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"firstLossAssets","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flcDepositedAt","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"flcWithdrawntAt","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundedAt","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundingFailedAt","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"fundingPeriodSeconds","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gracePeriodDays","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"token","type":"string"},{"internalType":"address","name":"stableCoinContractAddress","type":"address"},{"internalType":"address","name":"platformTokenContractAddress","type":"address"},{"internalType":"uint256","name":"minFundingCapacity","type":"uint256"},{"internalType":"uint256","name":"maxFundingCapacity","type":"uint256"},{"internalType":"uint64","name":"fundingPeriodSeconds","type":"uint64"},{"internalType":"uint64","name":"lendingTermSeconds","type":"uint64"},{"internalType":"address","name":"borrowerAddress","type":"address"},{"internalType":"uint256","name":"firstLossAssets","type":"uint256"},{"internalType":"uint256","name":"borrowerTotalInterestRateWad","type":"uint256"},{"internalType":"uint256","name":"repaymentRecurrenceDays","type":"uint256"},{"internalType":"uint256","name":"gracePeriodDays","type":"uint256"},{"internalType":"uint256","name":"protocolFeeWad","type":"uint256"},{"internalType":"uint256","name":"defaultPenalty","type":"uint256"},{"internalType":"uint256","name":"penaltyRateWad","type":"uint256"},{"internalType":"uint8","name":"tranchesCount","type":"uint8"},{"internalType":"uint256[]","name":"trancheAPRsWads","type":"uint256[]"},{"internalType":"uint256[]","name":"trancheBoostedAPRsWads","type":"uint256[]"},{"internalType":"uint256[]","name":"trancheBoostRatios","type":"uint256[]"},{"internalType":"uint256[]","name":"trancheCoveragesWads","type":"uint256[]"}],"internalType":"struct LendingPool.LendingPoolParams","name":"params","type":"tuple"},{"internalType":"address[]","name":"_trancheVaultAddresses","type":"address[]"},{"internalType":"address","name":"_feeSharingContractAddress","type":"address"},{"internalType":"address","name":"_authorityAddress","type":"address"},{"internalType":"address","name":"_poolFactoryAddress","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"}],"name":"lenderAllDepositedAssets","outputs":[{"internalType":"uint256","name":"totalAssets","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lenderCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderDepositedAssetsByTranche","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lenderDisableRollOver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderEffectiveAprByTrancheWad","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"principal","type":"bool"},{"internalType":"bool","name":"rewards","type":"bool"},{"internalType":"bool","name":"platformTokens","type":"bool"}],"name":"lenderEnableRollOver","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"trancheId","type":"uint8"},{"internalType":"uint256","name":"platformTokens","type":"uint256"}],"name":"lenderLockPlatformTokensByTranche","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderPlatformTokensByTrancheLockable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderPlatformTokensByTrancheLocked","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"toWithdraws","type":"uint256[]"}],"name":"lenderRedeemRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"trancheId","type":"uint8"},{"internalType":"uint256","name":"toWithdraw","type":"uint256"}],"name":"lenderRedeemRewardsByTranche","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderRewardsByTrancheGeneratedByDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderRewardsByTrancheRedeemable","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderRewardsByTrancheRedeemableSpecial","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderRewardsByTrancheRedeemed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lender","type":"address"}],"name":"lenderRollOverSettings","outputs":[{"components":[{"internalType":"bool","name":"enabled","type":"bool"},{"internalType":"bool","name":"principal","type":"bool"},{"internalType":"bool","name":"rewards","type":"bool"},{"internalType":"bool","name":"platformTokens","type":"bool"}],"internalType":"struct LendingPool.RollOverSetting","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderStakedTokensByTranche","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"}],"name":"lenderTotalAprWad","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"lenderAddress","type":"address"},{"internalType":"uint8","name":"trancheId","type":"uint8"}],"name":"lenderTotalExpectedRewardsByTranche","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"trancheId","type":"uint8"},{"internalType":"uint256","name":"platformTokens","type":"uint256"}],"name":"lenderUnlockPlatformTokensByTranche","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"i","type":"uint256"}],"name":"lendersAt","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lendingTermSeconds","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxFundingCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minFundingCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"trancheId","type":"uint8"},{"internalType":"address","name":"depositorAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"onTrancheDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint8","name":"trancheId","type":"uint8"},{"internalType":"address","name":"depositorAddress","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"onTrancheWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"openedAt","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"penaltyRateWad","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"platformTokenContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolBalanceThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolFactoryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFeeWad","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"repaidAt","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"repaymentRecurrenceDays","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"s_totalLockedPlatformTokensByTranche","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"}],"name":"s_totalStakedAssetsByTranche","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8","name":"","type":"uint8"},{"internalType":"address","name":"","type":"address"}],"name":"s_trancheRewardables","outputs":[{"internalType":"uint256","name":"stakedAssets","type":"uint256"},{"internalType":"uint256","name":"lockedPlatformTokens","type":"uint256"},{"internalType":"uint256","name":"redeemedRewards","type":"uint256"},{"internalType":"uint64","name":"start","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stableCoinContractAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"trancheAPRsWads","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"trancheBoostRatios","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"trancheBoostedAPRsWads","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"trancheCoveragesWads","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"trancheVaultAddresses","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tranchesCount","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
608060405234801561001057600080fd5b5061529a806100206000396000f3fe608060405234801561001057600080fd5b50600436106104195760003560e01c8063a32cf53611610229578063a32cf5361461077b578063a6e308501461078e578063a86b446b146107a1578063a8d6b6b7146107a9578063a9a748ff14610879578063aa024a2314610893578063ac5032a1146108a6578063aed8354f146108b9578063b3e76772146108c1578063b50409c9146108c9578063b5bf30e4146108d2578063b5cae532146108e5578063bf7e214f146108ff578063c1120b2c14610912578063c1c081b914610925578063c2f41fdb1461092d578063c62acbc314610940578063c772c6571461095a578063c80863d11461096d578063c88ca17414610975578063cba900b71461097e578063d1914dbe1461099e578063d2227acf146109a6578063d28fa81a146109ae578063d4a41906146109c1578063d5335429146109d4578063d783df67146109dd578063d78db1a8146109e5578063d81d6484146109f8578063e011184a14610a0b578063e08cbe5a14610a2b578063e68d356914610a3e578063e6d5f2e614610a46578063eb4db09814610a60578063ee73cee714610a68578063effd4b9214610a7b578063f0a2fc6b14610a83578063f2f1438914610a96578063f2fde38b14610a9e578063f4ce550a14610ab1578063f4ff294214610acb578063f9beb7c714610ade578063fc0c546a14610ae6578063fd1163ab14610aee578063fe70e0f7146109f8578063ffa1ad7414610b0857600080fd5b8063030f5b461461041e57806306fdde03146104335780630d2f1f581461045157806312236e05146104645780631348ed5e1461047b5780631d151526146104845780632be5ec691461048c57806333c701ab146104b357806333ce843f146104bb57806338930203146104ce5780633f27cfb7146104e15780633f4ba83a146104f4578063408710c5146104fc578063486e04741461050f5780634be86c3a146105175780634f9d96321461051f57806353f0366c1461053f578063579832c7146105485780635bf5d54c146105505780635c975abb1461056a578063660bae35146105805780636af15e28146105935780636ca1b5c8146105a65780636cae70d6146105b95780636d05da7e146105cc578063715018a6146105eb57806378793074146105f35780637ba61ea5146105fc5780637d5848e91461060f5780637ea8a7801461062257806381f64186146106355780638456cb59146106a75780638d51a1c1146106af5780638da5cb5b146106b8578063902667c4146106c05780639147e73c146106c857806393132c9f146106db57806396365d44146106e4578063981b9705146106ec5780639a4f6323146106f55780639cbc6b87146106fe5780639d6a1c9f14610711578063a1375b8714610724578063a16c460e1461072d578063a18f672314610768575b600080fd5b61043161042c366004614473565b610b31565b005b61043b610ce5565b60405161044891906144c3565b60405180910390f35b61043161045f36600461457f565b610d73565b61046d60d85481565b604051908152602001610448565b61046d60d45481565b61046d610e04565b60e1546104a690600160c01b90046001600160401b031681565b6040516104489190614640565b610431610e84565b61046d6104c9366004614654565b610f0a565b60e1546104a6906001600160401b031681565b61046d6104ef36600461466d565b610f2b565b610431610fb6565b61043161050a366004614698565b610fc8565b610431611169565b6104316111c7565b60cc54610532906001600160a01b031681565b60405161044891906146f0565b61046d60d75481565b61046d61128d565b60ec5461055d9060ff1681565b604051610448919061471a565b60985460ff166040519015158152602001610448565b61046d61058e36600461466d565b6112c7565b61046d6105a1366004614742565b611325565b6104316105b436600461477b565b611352565b6104316105c7366004614473565b61151f565b60d9546105d99060ff1681565b60405160ff9091168152602001610448565b610431611774565b61046d60cf5481565b60d154610532906001600160a01b031681565b61046d61061d366004614742565b611786565b61043161063036600461477b565b6117fa565b61067e6106433660046147bc565b60e86020908152600092835260408084209091529082529020805460018201546002830154600390930154919290916001600160401b031684565b604080519485526020850193909352918301526001600160401b03166060820152608001610448565b6104316118ec565b61046d60e35481565b6105326118fc565b61043161190b565b60e2546104a6906001600160401b031681565b61046d60d65481565b61046d611ad7565b61046d60d25481565b61046d60ce5481565b61046d61070c366004614742565b611b11565b61043161071f366004614835565b611bc9565b61046d60d55481565b61046d61073b366004614742565b60ff16600090815260e8602090815260408083206001600160a01b03949094168352929052206001015490565b61046d610776366004614654565b612084565b60de54610532906001600160a01b031681565b60d0546104a6906001600160401b031681565b61046d612094565b61083e6107b736600461466d565b604080516080810182526000808252602082018190529181018290526060810191909152506001600160a01b0316600090815260eb60209081526040918290208251608081018452905460ff808216151583526101008204811615159383019390935262010000810483161515938201939093526301000000909204161515606082015290565b604051610448919081511515815260208083015115159082015260408083015115159082015260609182015115159181019190915260800190565b60e1546104a690600160401b90046001600160401b031681565b6104316108a13660046148df565b6120e4565b6105326108b4366004614654565b612241565b61046d61224e565b61046d6122a9565b61046d60e55481565b60cd54610532906001600160a01b031681565b60e2546104a690600160c01b90046001600160401b031681565b606554610532906001600160a01b031681565b60df54610532906001600160a01b031681565b610431612315565b61043161093b366004614473565b612350565b60d0546104a690600160401b90046001600160401b031681565b61046d610968366004614654565b612606565b61046d612616565b61046d60d35481565b61046d61098c366004614920565b60ea6020526000908152604090205481565b61046d612650565b61046d612697565b61046d6109bc366004614654565b6126a3565b6105326109cf366004614654565b6126b3565b61046d60e45481565b61046d6126dd565b61046d6109f3366004614742565b612704565b61046d610a06366004614742565b61272b565b61046d610a19366004614920565b60e96020526000908152604090205481565b61046d610a39366004614742565b612755565b61043161278f565b60e2546104a690600160801b90046001600160401b031681565b61046d6127d7565b610431610a76366004614654565b61282e565b610431612a33565b61046d610a91366004614742565b612bc4565b610431612c02565b610431610aac36600461466d565b612c21565b60e1546104a690600160801b90046001600160401b031681565b61046d610ad9366004614742565b612c97565b610431612cd5565b61043b612de7565b60e2546104a690600160401b90046001600160401b031681565b61043b6040518060400160405280600a8152602001691918191996981b16989960b11b81525081565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f1790610b619033906004016146f0565b602060405180830381865afa158015610b7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba2919061493d565b610bc75760405162461bcd60e51b8152600401610bbe9061495a565b60405180910390fd5b60056008600a610bd8838383612df4565b610be0612e9c565b33600090815260eb602052604090205462010000900460ff1615610c165760405162461bcd60e51b8152600401610bbe90614978565b8315610cde576000610c283387612704565b905080851115610c625760405162461bcd60e51b8152602060048201526005602482015264262818981b60d91b6044820152606401610bbe565b60ff8616600090815260e86020908152604080832033845290915281206002018054879290610c929084906149ad565b90915550610caa9050610ca3612ee2565b3387612ef1565b60405185815260ff87169033906000805160206152258339815191529060200160405180910390a3610cdc3387612f54565b505b5050505050565b60ca8054610cf2906149c0565b80601f0160208091040260200160405190810160405280929190818152602001828054610d1e906149c0565b8015610d6b5780601f10610d4057610100808354040283529160200191610d6b565b820191906000526020600020905b815481529060010190602001808311610d4e57829003601f168201915b505050505081565b610d7b612fc3565b6001610d8681613081565b610d8e612e9c565b604051636895215160e01b815273e77fb404549dcfb65d82812e1dfb7b931bddd53390636895215190610dcd90309089908990899089906004016149f4565b60006040518083038186803b158015610de557600080fd5b505af4158015610df9573d6000803e3d6000fd5b505050505050505050565b60405163498d011960e11b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063931a023290610e3e9030906004016146f0565b602060405180830381865af4158015610e5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7f9190614a61565b905090565b610e8c612fc3565b6005610e9781613081565b60d05460e154610ebf916001600160401b03600160401b918290048116929190910416614a7a565b6001600160401b0316421015610eff5760405162461bcd60e51b81526020600482015260056024820152644c5030323360d81b6044820152606401610bbe565b610f076130c7565b50565b60da8181548110610f1a57600080fd5b600091825260209091200154905081565b604051630dfe47d560e31b81523060048201526001600160a01b038216602482015260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a90636ff23ea890604401602060405180830381865af4158015610f8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb09190614a61565b92915050565b610fbe612fc3565b610fc66133a4565b565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f1790610ff89033906004016146f0565b602060405180830381865afa158015611015573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611039919061493d565b6110555760405162461bcd60e51b8152600401610bbe9061495a565b604080516080810182526001815284151560208083018281528615158486018181528715156060870181815233600081815260eb9097528987209851895496519451925161ffff1990971690151561ff00191617610100941515949094029390931763ffff00001916620100009115159190910263ff0000001916176301000000941515949094029390931790955585516376dfb46760e01b8152306004820152602481019490945260448401526064830152608482018390529251919273e77fb404549dcfb65d82812e1dfb7b931bddd533926376dfb4679260a4808201939291829003018186803b15801561114b57600080fd5b505af415801561115f573d6000803e3d6000fd5b5050505050505050565b6111716133f0565b600861117c81613081565b611184612e9c565b600061118e610e04565b60d25461119b91906149ad565b90506111a68161343b565b6111c36111b1612ee2565b60d1546001600160a01b031683612ef1565b5050565b6111cf612fc3565b60016111da81613081565b60d05460e1546111f6916001600160401b039081169116614a7a565b6001600160401b03164210156112725760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742061636372756520696e746572657374206f72206465636c617260448201527a65206661696c757265206265666f72652073746172742074696d6560281b6064820152608401610bbe565b60ce5460e3541061128557610f076134bc565b610f076136fd565b60405163cc8c64b360e01b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063cc8c64b390610e3e9030906004016146f0565b6000805b60d95460ff908116908216101561131f5760ff8116600090815260e8602090815260408083206001600160a01b038716845290915290205461130d90836149ad565b915061131881614a9a565b90506112cb565b50919050565b60ff16600090815260e8602090815260408083206001600160a01b03949094168352929052206002015490565b8261135c81613881565b611364612e9c565b6001600160a01b038316600090815260eb6020526040902054610100900460ff16156113ba5760405162461bcd60e51b81526020600482015260056024820152644c5033303160d81b6044820152606401610bbe565b600860ec5460ff16600a8111156113d3576113d3614704565b14806113f55750600a60ec5460ff16600a8111156113f3576113f3614704565b145b15611434578360ff16836001600160a01b03166000805160206152458339815191528460405161142791815260200190565b60405180910390a3611519565b60ff8416600090815260e8602090815260408083206001600160a01b03871684529091529020805483111561146b5761146b614ab9565b8281600001600082825461147f9190614acf565b925050819055508260e360008282546114989190614acf565b909155505060ff8516600090815260e96020526040812080548592906114bf908490614acf565b909155505080546000036114da576114d860e685613927565b505b8460ff16846001600160a01b03166000805160206152458339815191528560405161150791815260200190565b60405180910390a3610cde8486612f54565b50505050565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f179061154f9033906004016146f0565b602060405180830381865afa15801561156c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611590919061493d565b6115ac5760405162461bcd60e51b8152600401610bbe9061495a565b60016115b781613081565b6115bf612e9c565b6115c93384611786565b8211156116005760405162461bcd60e51b81526020600482015260056024820152644c5031303160d81b6044820152606401610bbe565b60cd54604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd9160048083019260209291908290030181865afa15801561164a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061166e9190614a61565b116116ba5760405162461bcd60e51b815260206004820152601c60248201527b131bd8dace88151bdad95b88131bd8dada5b99c8111a5cd8589b195960221b6044820152606401610bbe565b60ff8316600090815260e8602090815260408083203384529091528120600181018054919285926116ec9084906149ad565b909155505060ff8416600090815260ea6020526040812080548592906117139084906149ad565b909155505060cd54611730906001600160a01b031633308661393c565b60405183815260ff85169033907f687dbd8fb5b0193ed88cef1a599255ed2987fb0ca34adb416494afbd0fc7ec599060200160405180910390a36115193385612f54565b61177c613974565b610fc660006139d3565b60ff8116600081815260e8602090815260408083206001600160a01b0387168452909152812060dc80549293919284929081106117c5576117c5614ae2565b906000526020600020015482600001546117df9190614af8565b90508160010154816117f19190614acf565b95945050505050565b8261180481613881565b60ff8416600090815260e8602090815260408083206001600160a01b0387168452909152902061183560e685613a25565b508281600001600082825461184a91906149ad565b925050819055508260e3600082825461186391906149ad565b909155505060ff8516600090815260e960205260408120805485929061188a9084906149ad565b90915550506003810180546001600160401b031916426001600160401b031617905560405183815260ff8616906001600160a01b038616907fd879168324d037274653fc4d6f6baf56a95cae66607399eb0fc799787820882c90602001611507565b6118f4612fc3565b610fc6613a3a565b6033546001600160a01b031690565b6119136133f0565b600561191e81613081565b611926612e9c565b61192e612094565b156119635760405162461bcd60e51b81526020600482015260056024820152644c5032303360d81b6044820152606401610bbe565b61196b61224e565b156119a05760405162461bcd60e51b815260206004820152600560248201526413140c8c0d60da1b6044820152606401610bbe565b6119ab60e454613a77565b60006119b5613b38565b90506119cc6119c2612ee2565b333060e45461393c565b60005b60d95460ff16811015611ad25760008282815181106119f0576119f0614ae2565b60200260200101519050611a6d611a05612ee2565b82836001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a689190614a61565b612ef1565b806001600160a01b031663e8bac93b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611aa857600080fd5b505af1158015611abc573d6000803e3d6000fd5b505050505080611acb90614b0f565b90506119cf565b505050565b6040516351ce1fe360e01b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a906351ce1fe390610e3e9030906004016146f0565b600073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a635a2dc73d611b37858561272b565b611b418686612bc4565b60d05460405160e085901b6001600160e01b031916815260048101939093526024830191909152600160401b90046001600160401b031660448201526064015b602060405180830381865af4158015611b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc29190614a61565b9392505050565b600054610100900460ff1615808015611be95750600054600160ff909116105b80611c0a5750611bf830613bb7565b158015611c0a575060005460ff166001145b611c6d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bbe565b6000805460ff191660011790558015611c90576000805461ff0019166101001790555b60405163378da0eb60e01b815273b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063378da0eb90611ccf908a908a908a908a908a90600401614c70565b60006040518083038186803b158015611ce757600080fd5b505af4158015611cfb573d6000803e3d6000fd5b5050505073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a6366db307c88806102800190611d2a9190614ed2565b6040518363ffffffff1660e01b8152600401611d47929190614f1b565b60006040518083038186803b158015611d5f57600080fd5b505af4158015611d73573d6000803e3d6000fd5b50611d849250899150819050614f2f565b60ca91611d92919083614fbb565b50611da06020880188614f2f565b60cb91611dae919083614fbb565b50611dbf606088016040890161466d565b60cc80546001600160a01b0319166001600160a01b0392909216919091179055611def608088016060890161466d565b60cd80546001600160a01b0319166001600160a01b0392909216919091179055608087013560ce5560a087013560cf55611e2f60e0880160c0890161507a565b60d080546001600160401b0319166001600160401b0392909216919091179055611e60610100880160e0890161507a565b60d080546001600160401b0392909216600160401b02600160401b600160801b0319909216919091179055611e9d6101208801610100890161466d565b60d180546001600160a01b0319166001600160a01b039290921691909117905561012087013560d25561014087013560d55561016087013560d35561018087013560d4556101a087013560d6556101c087013560d7556101e087013560d855611f0e61022088016102008901614920565b60d9805460ff191660ff92909216919091179055611f30610220880188614ed2565b611f3c9160da916143a1565b50611f4b610240880188614ed2565b611f579160db916143a1565b50611f66610260880188614ed2565b611f729160dc916143a1565b50611f81610280880188614ed2565b611f8d9160dd916143a1565b50611f9a60e087876143ec565b5060df80546001600160a01b038087166001600160a01b03199283161790925560de805492851692909116919091179055611fd3613bc6565b611fdb613bf5565b606580546001600160a01b0319166001600160a01b0385161790557f6750b3feabf3a89cdbe58c1a46e1483a74cd1acad94c5186534ac83715a836bb878787878760405161202d959493929190614c70565b60405180910390a1801561207b576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b60dc8181548110610f1a57600080fd5b600073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a63b7e8332660e5546120bb6126dd565b6040516001600160e01b031960e085901b16815260048101929092526024820152604401610e3e565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f17906121149033906004016146f0565b602060405180830381865afa158015612131573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612155919061493d565b6121715760405162461bcd60e51b8152600401610bbe9061495a565b60056008600a612182838383612df4565b61218a612e9c565b33600090815260eb602052604090205462010000900460ff16156121c05760405162461bcd60e51b8152600401610bbe90614978565b60d95460ff1684146121fc5760405162461bcd60e51b81526020600482015260056024820152644c5031303760d81b6044820152606401610bbe565b60005b60ff8116851115610cdc5761222f8187878460ff1681811061222357612223614ae2565b90506020020135610b31565b8061223981614a9a565b9150506121ff565b6000610fb060e683613c24565b6000600460ec5460ff16600a81111561226957612269614704565b11156122a657604051631a00010d60e01b815273b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a90631a00010d90610e3e9030906004016146f0565b90565b60006122ba6201518061016d614af8565b60d060089054906101000a90046001600160401b03166001600160401b0316670de0b6b3a764000060e3546122ed612650565b6122f79190614af8565b6123019190615095565b61230b9190614af8565b610e7f9190615095565b61231d6133f0565b600061232881613081565b612330612e9c565b61233b60d254613c30565b610f07612346612ee2565b333060d25461393c565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f17906123809033906004016146f0565b602060405180830381865afa15801561239d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123c1919061493d565b6123dd5760405162461bcd60e51b8152600401610bbe9061495a565b6008600a6123eb8282613ca6565b6123f3612e9c565b33600090815260eb60205260409020546301000000900460ff16156124425760405162461bcd60e51b8152602060048201526005602482015264262818981960d91b6044820152606401610bbe565b61244c3385612704565b156124815760405162461bcd60e51b81526020600482015260056024820152644c5031303360d81b6044820152606401610bbe565b60cd54604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd9160048083019260209291908290030181865afa1580156124cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ef9190614a61565b1161253c5760405162461bcd60e51b815260206004820152601e60248201527f556e6c6f636b3a20546f6b656e204c6f636b696e672044697361626c656400006044820152606401610bbe565b60ff8416600090815260e860209081526040808320338452909152902060018101548411156125955760405162461bcd60e51b815260206004820152600560248201526413140c4c0d60da1b6044820152606401610bbe565b838160010160008282546125a99190614acf565b909155505060cd546125c5906001600160a01b03163386612ef1565b60405184815260ff86169033907f66cf26ac7213d700d4f403bef72fa02e2deb59197318103e3eabe90e48d70f239060200160405180910390a35050505050565b60db8181548110610f1a57600080fd5b60405163dbb5ffc960e01b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063dbb5ffc990610e3e9030906004016146f0565b60d95460405163b8b3b07560e01b815230600482015260ff909116602482015260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063b8b3b07590604401610e3e565b6000610e7f60e6613d1d565b60dd8181548110610f1a57600080fd5b60e081815481106126c357600080fd5b6000918252602090912001546001600160a01b0316905081565b600073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a6333f74e4260e3546120bb6127d7565b6000806127118484612c97565b9050600061271f8585611325565b90506117f18183614acf565b60ff16600090815260e8602090815260408083206001600160a01b03949094168352929052205490565b6000806127628484612c97565b905060006127708585611325565b90508181111561278557600092505050610fb0565b6117f18183614acf565b6127976133f0565b60026127a281613081565b6127aa612e9c565b6127b560e354613d27565b610f076127c0612ee2565b60d15460e3546001600160a01b0390911690612ef1565b60d55460d054604051633099159f60e01b81526004810192909252600160401b90046001600160401b0316602482015260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a90633099159f90604401610e3e565b6128366133f0565b61283e612e9c565b600061284861224e565b90508181106128815760405162461bcd60e51b81526020600482015260056024820152644c5032303160d81b6044820152606401610bbe565b80156128e8576000612891611ad7565b612899612616565b6128a39190614acf565b90506128af81836149ad565b8310156128e65760405162461bcd60e51b8152602060048201526005602482015264262819181960d91b6044820152606401610bbe565b505b60006128f48284614acf565b90506128fe612094565b8111156129105761290d612094565b90505b600082670de0b6b3a764000060d6548461292a9190614af8565b6129349190615095565b61293e91906149ad565b9050600061294c8286614acf565b9050838560e55461295d91906149ad565b6129679190614acf565b60e555811561298d5761298d61297b612ee2565b60df546001600160a01b031684612ef1565b6129a0612998612ee2565b33308861393c565b83156129dc5760405184815233907fe7963b683dbff9c1510ab87dece1fffff5d084905fe94cdd43f551e9b32a46739060200160405180910390a25b60d15460408051878152602081018490529081018490526001600160a01b03909116907f8c1b2960f1620d7bf5f7a6c04f6ed9673508112f95e73af8681433cca020d7929060600160405180910390a25050505050565b612a3b612fc3565b6004612a4681613081565b612a4e612e9c565b60e180546001600160401b031916426001600160401b031617905560ec805460ff191660011790556000612a80613b38565b905060005b60e054811015612b7a57818181518110612aa157612aa1614ae2565b60200260200101516001600160a01b0316635e4c57a46040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612ae357600080fd5b505af1158015612af7573d6000803e3d6000fd5b50505050818181518110612b0d57612b0d614ae2565b60200260200101516001600160a01b031663e8bac93b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612b4f57600080fd5b505af1158015612b63573d6000803e3d6000fd5b505050508080612b7290614b0f565b915050612a85565b5060e1546040517f558d1359f6a6235015698503e6fbee2e3ba2c372f84350d2f82609577383203891612bb8916001600160401b0390911690614640565b60405180910390a15050565b60405163075adf8960e41b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a906375adf89090611b81903090879087906004016150b7565b6003612c0d81613081565b60d2805460009091556111c36111b1612ee2565b612c29613974565b6001600160a01b038116612c8e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610bbe565b610f07816139d3565b6040516338133fbb60e21b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063e04cfeec90611b81903090879087906004016150b7565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f1790612d059033906004016146f0565b602060405180830381865afa158015612d22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d46919061493d565b612d625760405162461bcd60e51b8152600401610bbe9061495a565b60408051608081018252600080825260208083018281528385018381526060850184815233855260eb9093529490922092518354925194519151151563010000000263ff0000001992151562010000029290921663ffff0000199515156101000261ff00199215159290921661ffff1990941693909317179390931617919091179055565b60cb8054610cf2906149c0565b82600a811115612e0657612e06614704565b60ec5460ff16600a811115612e1d57612e1d614704565b1480612e4f575081600a811115612e3657612e36614704565b60ec5460ff16600a811115612e4d57612e4d614704565b145b80612e80575080600a811115612e6757612e67614704565b60ec5460ff16600a811115612e7e57612e7e614704565b145b611ad25760405162461bcd60e51b8152600401610bbe906150dd565b60985460ff1615610fc65760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610bbe565b60cc546001600160a01b031690565b6040516001600160a01b038316602482015260448101829052611ad290849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613d9d565b8060ff16826001600160a01b03167fee5f2ff093bda9a8bbe9bb17806ee2dd3d60f26843793755cffd62d63d6b1130612f8d8585612bc4565b612f978686611b11565b612fa18787611325565b6040805193845260208401929092529082015260600160405180910390a35050565b33612fcc6118fc565b6001600160a01b0316148061304d5750606554604051630935e01b60e21b81526001600160a01b03909116906324d7806c9061300c9033906004016146f0565b602060405180830381865afa158015613029573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304d919061493d565b610fc65760405162461bcd60e51b815260206004820152600560248201526441413a4f4160d81b6044820152606401610bbe565b80600a81111561309357613093614704565b60ec5460ff16600a8111156130aa576130aa614704565b14610f075760405162461bcd60e51b8152600401610bbe906150dd565b6130cf612e9c565b60e280546001600160401b034216600160c01b026001600160c01b0390911617905560ec805460ff19166009179055613106613e6f565b6000613110612ee2565b6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161313b91906146f0565b602060405180830381865afa158015613158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061317c9190614a61565b90506000613188613b38565b905060005b60e05481101561335f5760008282815181106131ab576131ab614ae2565b602002602001015190506000670de0b6b3a76400008560dd85815481106131d4576131d4614ae2565b90600052602060002001546131e99190614af8565b6131f39190615095565b90506000826001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015613235573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132599190614a61565b61326b670de0b6b3a764000084614af8565b6132759190615095565b9050811561328f5761328f613288612ee2565b8484612ef1565b6132998287614acf565b6040516351baff6160e11b8152600481018390529096506001600160a01b0384169063a375fec290602401600060405180830381600087803b1580156132de57600080fd5b505af11580156132f2573d6000803e3d6000fd5b50505050826001600160a01b031663e8bac93b6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561333157600080fd5b505af1158015613345573d6000803e3d6000fd5b50505050505050808061335790614b0f565b91505061318d565b507fca020e3ba10c59076a4153bc43dfb5b5edc2fd376cfe19f53ccad5a2686dab4060e260189054906101000a90046001600160401b0316604051612bb89190614640565b6133ac613f55565b6098805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516133e691906146f0565b60405180910390a1565b60d1546001600160a01b0316336001600160a01b031614610fc65760405162461bcd60e51b81526020600482015260056024820152644c5030303360d81b6044820152606401610bbe565b613443612e9c565b60e280546001600160401b034216600160801b02600160801b600160c01b031990911617905560ec805460ff1916600a17905560d1546040518281526001600160a01b03909116907fa2d53e3de69d40a03f150e903c3da14325fbdf2cfdfb9cdc4cfed25bf8cb4bdb906020015b60405180910390a250565b6134c4612e9c565b60e180546001600160401b034216600160401b02600160401b600160801b031990911617905560ec805460ff191660021790556000613501613b38565b905060005b81518110156136a857600082828151811061352357613523614ae2565b60200260200101519050806001600160a01b031663ac67e1af6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561356857600080fd5b505af115801561357c573d6000803e3d6000fd5b50505050806001600160a01b031663ad960ce16040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156135bb57600080fd5b505af11580156135cf573d6000803e3d6000fd5b50505050806001600160a01b031663baa83cdd826001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015613620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136449190614a61565b6040518263ffffffff1660e01b815260040161366291815260200190565b600060405180830381600087803b15801561367c57600080fd5b505af1158015613690573d6000803e3d6000fd5b505050505080806136a090614b0f565b915050613506565b5060e15460e35460408051600160401b9093046001600160401b0316835260208301919091527f30dd79baab3a98aaf5598d29e79e0bccc05be70214863bd4b1dcfa8f2ad6fca791015b60405180910390a150565b613705612e9c565b60e180546001600160401b034216600160801b02600160801b600160c01b031990911617905560ec805460ff191660031790556000613742613b38565b905060005b60e05481101561383c5781818151811061376357613763614ae2565b60200260200101516001600160a01b031663ac67e1af6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156137a557600080fd5b505af11580156137b9573d6000803e3d6000fd5b505050508181815181106137cf576137cf614ae2565b60200260200101516001600160a01b031663e8bac93b6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561381157600080fd5b505af1158015613825573d6000803e3d6000fd5b50505050808061383490614b0f565b915050613747565b507f8c96276c843b319de12382c36146032b02dfe79d5150583d30bce0979b5b48f360e160109054906101000a90046001600160401b03166040516136f29190614640565b60e05460ff8216106138bd5760405162461bcd60e51b81526020600482015260056024820152644c5030303160d81b6044820152606401610bbe565b336001600160a01b031660e08260ff16815481106138dd576138dd614ae2565b6000918252602090912001546001600160a01b031614610f075760405162461bcd60e51b8152602060048201526005602482015264262818181960d91b6044820152606401610bbe565b6000611bc2836001600160a01b038416613f9e565b6040516001600160a01b03808516602483015283166044820152606481018290526115199085906323b872dd60e01b90608401612f1d565b3361397d6118fc565b6001600160a01b031614610fc65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610bbe565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000611bc2836001600160a01b038416614098565b613a42612e9c565b6098805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586133d93390565b613a7f612e9c565b60e280546001600160401b034216600160401b02600160401b600160801b031990911617905560ec805460ff1916600817905560d1546040518281526001600160a01b03909116907fd42b39211abc7e36cc2cba4151ce436c7a5140bba8e34d0df17f7380842dd79e9060200160405180910390a27f087734f394f09d0a1ca486f05319fcc6041f1b2f852eabe4c62f261112aa5aca60e260089054906101000a90046001600160401b03166040516136f29190614640565b60405163186a3e8760e31b815260609073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063c351f43890613b729030906004016146f0565b600060405180830381865af4158015613b8f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e7f91908101906150fc565b6001600160a01b03163b151590565b600054610100900460ff16613bed5760405162461bcd60e51b8152600401610bbe9061518a565b610fc66140e7565b600054610100900460ff16613c1c5760405162461bcd60e51b8152600401610bbe9061518a565b610fc6614117565b6000611bc2838361414a565b613c38612e9c565b60e180546001600160401b034216600160c01b026001600160c01b0390911617905560ec805460ff1916600417905560d1546040518281526001600160a01b03909116907f177047f9a933a61817e2caeb4dd0612b2f2c53341207667b849404a48d73d3c0906020016134b1565b81600a811115613cb857613cb8614704565b60ec5460ff16600a811115613ccf57613ccf614704565b1480613d01575080600a811115613ce857613ce8614704565b60ec5460ff16600a811115613cff57613cff614704565b145b6111c35760405162461bcd60e51b8152600401610bbe906150dd565b6000610fb0825490565b613d2f612e9c565b60e280546001600160401b0342166001600160401b031990911617905560e481905560ec805460ff1916600517905560d1546040518281526001600160a01b03909116907f30344454671aa5c91747fc90b3c935dbd2b9fe1112579bcad62044307af98bcf906020016134b1565b6000613df2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166141749092919063ffffffff16565b805190915015611ad25780806020019051810190613e10919061493d565b611ad25760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bbe565b6000613e79613b38565b905060005b60d95460ff90811690821610156111c35760005b613e9a612697565b811015613f4257613f30613ead82612241565b848460ff1681518110613ec257613ec2614ae2565b60200260200101516001600160a01b031663af640d0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613f07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f2b91906151d5565b61418b565b80613f3a81614b0f565b915050613e92565b5080613f4d81614a9a565b915050613e7e565b60985460ff16610fc65760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610bbe565b60008181526001830160205260408120548015614087576000613fc2600183614acf565b8554909150600090613fd690600190614acf565b905081811461403b576000866000018281548110613ff657613ff6614ae2565b906000526020600020015490508087600001848154811061401957614019614ae2565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061404c5761404c6151f2565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610fb0565b6000915050610fb0565b5092915050565b60008181526001830160205260408120546140df57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610fb0565b506000610fb0565b600054610100900460ff1661410e5760405162461bcd60e51b8152600401610bbe9061518a565b610fc6336139d3565b600054610100900460ff1661413e5760405162461bcd60e51b8152600401610bbe9061518a565b6098805460ff19169055565b600082600001828154811061416157614161614ae2565b9060005260206000200154905092915050565b6060614183848460008561422a565b949350505050565b60006141978383612704565b90508015611ad25760ff8216600090815260e8602090815260408083206001600160a01b0387168452909152812060020180548392906141d89084906149ad565b909155506141f090506141e9612ee2565b8483612ef1565b8160ff16836001600160a01b03166000805160206152258339815191528360405161421d91815260200190565b60405180910390a3505050565b60608247101561428b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610bbe565b600080866001600160a01b031685876040516142a79190615208565b60006040518083038185875af1925050503d80600081146142e4576040519150601f19603f3d011682016040523d82523d6000602084013e6142e9565b606091505b50915091506142fa87838387614305565b979650505050505050565b6060831561437257825160000361436b5761431f85613bb7565b61436b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bbe565b5081614183565b61418383838151156143875781518083602001fd5b8060405162461bcd60e51b8152600401610bbe91906144c3565b8280548282559060005260206000209081019282156143dc579160200282015b828111156143dc5782358255916020019190600101906143c1565b506143e892915061443f565b5090565b8280548282559060005260206000209081019282156143dc579160200282015b828111156143dc5781546001600160a01b0319166001600160a01b0384351617825560209092019160019091019061440c565b5b808211156143e85760008155600101614440565b60ff81168114610f0757600080fd5b803561446e81614454565b919050565b6000806040838503121561448657600080fd5b823561449181614454565b946020939093013593505050565b60005b838110156144ba5781810151838201526020016144a2565b50506000910152565b60208152600082518060208401526144e281604085016020870161449f565b601f01601f19169190910160400192915050565b6001600160a01b0381168114610f0757600080fd5b803561446e816144f6565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561455457614554614516565b604052919050565b60006001600160401b0382111561457557614575614516565b5060051b60200190565b6000806000806080858703121561459557600080fd5b84356145a0816144f6565b93506020858101356001600160401b038111156145bc57600080fd5b8601601f810188136145cd57600080fd5b80356145e06145db8261455c565b61452c565b81815260059190911b8201830190838101908a8311156145ff57600080fd5b928401925b82841015614626578335614617816144f6565b82529284019290840190614604565b979a97995050505060408601359560600135949350505050565b6001600160401b0391909116815260200190565b60006020828403121561466657600080fd5b5035919050565b60006020828403121561467f57600080fd5b8135611bc2816144f6565b8015158114610f0757600080fd5b6000806000606084860312156146ad57600080fd5b83356146b88161468a565b925060208401356146c88161468a565b915060408401356146d88161468a565b809150509250925092565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b634e487b7160e01b600052602160045260246000fd5b60208101600b831061473c57634e487b7160e01b600052602160045260246000fd5b91905290565b6000806040838503121561475557600080fd5b8235614760816144f6565b9150602083013561477081614454565b809150509250929050565b60008060006060848603121561479057600080fd5b833561479b81614454565b925060208401356147ab816144f6565b929592945050506040919091013590565b600080604083850312156147cf57600080fd5b82356147da81614454565b91506020830135614770816144f6565b60008083601f8401126147fc57600080fd5b5081356001600160401b0381111561481357600080fd5b6020830191508360208260051b850101111561482e57600080fd5b9250929050565b60008060008060008060a0878903121561484e57600080fd5b86356001600160401b038082111561486557600080fd5b908801906102a0828b03121561487a57600080fd5b9096506020880135908082111561489057600080fd5b5061489d89828a016147ea565b90965094505060408701356148b1816144f6565b925060608701356148c1816144f6565b915060808701356148d1816144f6565b809150509295509295509295565b600080602083850312156148f257600080fd5b82356001600160401b0381111561490857600080fd5b614914858286016147ea565b90969095509350505050565b60006020828403121561493257600080fd5b8135611bc281614454565b60006020828403121561494f57600080fd5b8151611bc28161468a565b60208082526004908201526310504e9360e21b604082015260600190565b6020808252600590820152644c5031303560d81b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b80820180821115610fb057610fb0614997565b600181811c908216806149d457607f821691505b60208210810361131f57634e487b7160e01b600052602260045260246000fd5b600060a0820160018060a01b03808916845260208189168186015260a0604086015282885180855260c087019150828a01945060005b81811015614a48578551851683529483019491830191600101614a2a565b5050606086019790975250505050608001529392505050565b600060208284031215614a7357600080fd5b5051919050565b6001600160401b0381811683821601908082111561409157614091614997565b600060ff821660ff8103614ab057614ab0614997565b60010192915050565b634e487b7160e01b600052600160045260246000fd5b81810381811115610fb057610fb0614997565b634e487b7160e01b600052603260045260246000fd5b8082028115828204841417610fb057610fb0614997565b600060018201614b2157614b21614997565b5060010190565b6000808335601e19843603018112614b3f57600080fd5b83016020810192503590506001600160401b03811115614b5e57600080fd5b80360382131561482e57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b80356001600160401b038116811461446e57600080fd5b6000808335601e19843603018112614bc457600080fd5b83016020810192503590506001600160401b03811115614be357600080fd5b8060051b360382131561482e57600080fd5b81835260006001600160fb1b03831115614c0e57600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b85811015614c65578135614c4a816144f6565b6001600160a01b031687529582019590820190600101614c37565b509495945050505050565b608081526000614c808788614b28565b6102a06080850152614c9761032085018284614b6d565b915050614ca76020890189614b28565b607f19808685030160a0870152614cbf848385614b6d565b9350614ccd60408c0161450b565b9250614cdc60c08701846146e3565b614ce860608c0161450b565b9250614cf760e08701846146e3565b610100925060808b013583870152610120915060a08b013582870152614d1f60c08c01614b96565b610140614d36818901836001600160401b03169052565b614d4260e08e01614b96565b9150610160614d5b818a01846001600160401b03169052565b614d66868f0161450b565b95506101809250614d79838a01876146e3565b6101a09550848e0135868a01526101c09450818e0135858a01526101e09150808e0135828a015250610200828e0135818a01526102209250858e0135838a01526102409550848e0135868a01526102609450818e0135858a0152614dde818f01614463565b915050610280614df2818a018360ff169052565b614dfe838f018f614bad565b9350915083898803016102a08a0152614e18878484614bf5565b9650614e26868f018f614bad565b9650925083898803016102c08a0152614e40878785614bf5565b9650614e4e858f018f614bad565b9650945083898803016102e08a0152614e68878787614bf5565b9650614e76818f018f614bad565b96509450505050808685030161030087015250614e94838383614bf5565b925050508281036020840152614eab818789614c27565b915050614ebb60408301856146e3565b614ec860608301846146e3565b9695505050505050565b6000808335601e19843603018112614ee957600080fd5b8301803591506001600160401b03821115614f0357600080fd5b6020019150600581901b360382131561482e57600080fd5b602081526000614183602083018486614bf5565b6000808335601e19843603018112614f4657600080fd5b8301803591506001600160401b03821115614f6057600080fd5b60200191503681900382131561482e57600080fd5b601f821115611ad257600081815260208120601f850160051c81016020861015614f9c5750805b601f850160051c820191505b81811015610cdc57828155600101614fa8565b6001600160401b03831115614fd257614fd2614516565b614fe683614fe083546149c0565b83614f75565b6000601f84116001811461501a57600085156150025750838201355b600019600387901b1c1916600186901b178355610cde565b600083815260209020601f19861690835b8281101561504b578685013582556020948501946001909201910161502b565b50868210156150685760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b60006020828403121561508c57600080fd5b611bc282614b96565b6000826150b257634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b03938416815291909216602082015260ff909116604082015260600190565b60208082526005908201526413140c0c0d60da1b604082015260600190565b6000602080838503121561510f57600080fd5b82516001600160401b0381111561512557600080fd5b8301601f8101851361513657600080fd5b80516151446145db8261455c565b81815260059190911b8201830190838101908783111561516357600080fd5b928401925b828410156142fa57835161517b816144f6565b82529284019290840190615168565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6000602082840312156151e757600080fd5b8151611bc281614454565b634e487b7160e01b600052603160045260246000fd5b6000825161521a81846020870161449f565b919091019291505056fe8804794d500e9b037c6ed2b6b3b001fb1d50923e083430e8084f35899bc17f91efb7b3ef9e1a73a4afe1f53d0a172ac1c2225eb0ecc20d9db73eea4a716e8ce6a2646970667358221220a5bfe74baed8036e4a4b5038af678d073c2565efa25203b4226f1de43e0d638764736f6c63430008120033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106104195760003560e01c8063a32cf53611610229578063a32cf5361461077b578063a6e308501461078e578063a86b446b146107a1578063a8d6b6b7146107a9578063a9a748ff14610879578063aa024a2314610893578063ac5032a1146108a6578063aed8354f146108b9578063b3e76772146108c1578063b50409c9146108c9578063b5bf30e4146108d2578063b5cae532146108e5578063bf7e214f146108ff578063c1120b2c14610912578063c1c081b914610925578063c2f41fdb1461092d578063c62acbc314610940578063c772c6571461095a578063c80863d11461096d578063c88ca17414610975578063cba900b71461097e578063d1914dbe1461099e578063d2227acf146109a6578063d28fa81a146109ae578063d4a41906146109c1578063d5335429146109d4578063d783df67146109dd578063d78db1a8146109e5578063d81d6484146109f8578063e011184a14610a0b578063e08cbe5a14610a2b578063e68d356914610a3e578063e6d5f2e614610a46578063eb4db09814610a60578063ee73cee714610a68578063effd4b9214610a7b578063f0a2fc6b14610a83578063f2f1438914610a96578063f2fde38b14610a9e578063f4ce550a14610ab1578063f4ff294214610acb578063f9beb7c714610ade578063fc0c546a14610ae6578063fd1163ab14610aee578063fe70e0f7146109f8578063ffa1ad7414610b0857600080fd5b8063030f5b461461041e57806306fdde03146104335780630d2f1f581461045157806312236e05146104645780631348ed5e1461047b5780631d151526146104845780632be5ec691461048c57806333c701ab146104b357806333ce843f146104bb57806338930203146104ce5780633f27cfb7146104e15780633f4ba83a146104f4578063408710c5146104fc578063486e04741461050f5780634be86c3a146105175780634f9d96321461051f57806353f0366c1461053f578063579832c7146105485780635bf5d54c146105505780635c975abb1461056a578063660bae35146105805780636af15e28146105935780636ca1b5c8146105a65780636cae70d6146105b95780636d05da7e146105cc578063715018a6146105eb57806378793074146105f35780637ba61ea5146105fc5780637d5848e91461060f5780637ea8a7801461062257806381f64186146106355780638456cb59146106a75780638d51a1c1146106af5780638da5cb5b146106b8578063902667c4146106c05780639147e73c146106c857806393132c9f146106db57806396365d44146106e4578063981b9705146106ec5780639a4f6323146106f55780639cbc6b87146106fe5780639d6a1c9f14610711578063a1375b8714610724578063a16c460e1461072d578063a18f672314610768575b600080fd5b61043161042c366004614473565b610b31565b005b61043b610ce5565b60405161044891906144c3565b60405180910390f35b61043161045f36600461457f565b610d73565b61046d60d85481565b604051908152602001610448565b61046d60d45481565b61046d610e04565b60e1546104a690600160c01b90046001600160401b031681565b6040516104489190614640565b610431610e84565b61046d6104c9366004614654565b610f0a565b60e1546104a6906001600160401b031681565b61046d6104ef36600461466d565b610f2b565b610431610fb6565b61043161050a366004614698565b610fc8565b610431611169565b6104316111c7565b60cc54610532906001600160a01b031681565b60405161044891906146f0565b61046d60d75481565b61046d61128d565b60ec5461055d9060ff1681565b604051610448919061471a565b60985460ff166040519015158152602001610448565b61046d61058e36600461466d565b6112c7565b61046d6105a1366004614742565b611325565b6104316105b436600461477b565b611352565b6104316105c7366004614473565b61151f565b60d9546105d99060ff1681565b60405160ff9091168152602001610448565b610431611774565b61046d60cf5481565b60d154610532906001600160a01b031681565b61046d61061d366004614742565b611786565b61043161063036600461477b565b6117fa565b61067e6106433660046147bc565b60e86020908152600092835260408084209091529082529020805460018201546002830154600390930154919290916001600160401b031684565b604080519485526020850193909352918301526001600160401b03166060820152608001610448565b6104316118ec565b61046d60e35481565b6105326118fc565b61043161190b565b60e2546104a6906001600160401b031681565b61046d60d65481565b61046d611ad7565b61046d60d25481565b61046d60ce5481565b61046d61070c366004614742565b611b11565b61043161071f366004614835565b611bc9565b61046d60d55481565b61046d61073b366004614742565b60ff16600090815260e8602090815260408083206001600160a01b03949094168352929052206001015490565b61046d610776366004614654565b612084565b60de54610532906001600160a01b031681565b60d0546104a6906001600160401b031681565b61046d612094565b61083e6107b736600461466d565b604080516080810182526000808252602082018190529181018290526060810191909152506001600160a01b0316600090815260eb60209081526040918290208251608081018452905460ff808216151583526101008204811615159383019390935262010000810483161515938201939093526301000000909204161515606082015290565b604051610448919081511515815260208083015115159082015260408083015115159082015260609182015115159181019190915260800190565b60e1546104a690600160401b90046001600160401b031681565b6104316108a13660046148df565b6120e4565b6105326108b4366004614654565b612241565b61046d61224e565b61046d6122a9565b61046d60e55481565b60cd54610532906001600160a01b031681565b60e2546104a690600160c01b90046001600160401b031681565b606554610532906001600160a01b031681565b60df54610532906001600160a01b031681565b610431612315565b61043161093b366004614473565b612350565b60d0546104a690600160401b90046001600160401b031681565b61046d610968366004614654565b612606565b61046d612616565b61046d60d35481565b61046d61098c366004614920565b60ea6020526000908152604090205481565b61046d612650565b61046d612697565b61046d6109bc366004614654565b6126a3565b6105326109cf366004614654565b6126b3565b61046d60e45481565b61046d6126dd565b61046d6109f3366004614742565b612704565b61046d610a06366004614742565b61272b565b61046d610a19366004614920565b60e96020526000908152604090205481565b61046d610a39366004614742565b612755565b61043161278f565b60e2546104a690600160801b90046001600160401b031681565b61046d6127d7565b610431610a76366004614654565b61282e565b610431612a33565b61046d610a91366004614742565b612bc4565b610431612c02565b610431610aac36600461466d565b612c21565b60e1546104a690600160801b90046001600160401b031681565b61046d610ad9366004614742565b612c97565b610431612cd5565b61043b612de7565b60e2546104a690600160401b90046001600160401b031681565b61043b6040518060400160405280600a8152602001691918191996981b16989960b11b81525081565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f1790610b619033906004016146f0565b602060405180830381865afa158015610b7e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ba2919061493d565b610bc75760405162461bcd60e51b8152600401610bbe9061495a565b60405180910390fd5b60056008600a610bd8838383612df4565b610be0612e9c565b33600090815260eb602052604090205462010000900460ff1615610c165760405162461bcd60e51b8152600401610bbe90614978565b8315610cde576000610c283387612704565b905080851115610c625760405162461bcd60e51b8152602060048201526005602482015264262818981b60d91b6044820152606401610bbe565b60ff8616600090815260e86020908152604080832033845290915281206002018054879290610c929084906149ad565b90915550610caa9050610ca3612ee2565b3387612ef1565b60405185815260ff87169033906000805160206152258339815191529060200160405180910390a3610cdc3387612f54565b505b5050505050565b60ca8054610cf2906149c0565b80601f0160208091040260200160405190810160405280929190818152602001828054610d1e906149c0565b8015610d6b5780601f10610d4057610100808354040283529160200191610d6b565b820191906000526020600020905b815481529060010190602001808311610d4e57829003601f168201915b505050505081565b610d7b612fc3565b6001610d8681613081565b610d8e612e9c565b604051636895215160e01b815273e77fb404549dcfb65d82812e1dfb7b931bddd53390636895215190610dcd90309089908990899089906004016149f4565b60006040518083038186803b158015610de557600080fd5b505af4158015610df9573d6000803e3d6000fd5b505050505050505050565b60405163498d011960e11b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063931a023290610e3e9030906004016146f0565b602060405180830381865af4158015610e5b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e7f9190614a61565b905090565b610e8c612fc3565b6005610e9781613081565b60d05460e154610ebf916001600160401b03600160401b918290048116929190910416614a7a565b6001600160401b0316421015610eff5760405162461bcd60e51b81526020600482015260056024820152644c5030323360d81b6044820152606401610bbe565b610f076130c7565b50565b60da8181548110610f1a57600080fd5b600091825260209091200154905081565b604051630dfe47d560e31b81523060048201526001600160a01b038216602482015260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a90636ff23ea890604401602060405180830381865af4158015610f8c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fb09190614a61565b92915050565b610fbe612fc3565b610fc66133a4565b565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f1790610ff89033906004016146f0565b602060405180830381865afa158015611015573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611039919061493d565b6110555760405162461bcd60e51b8152600401610bbe9061495a565b604080516080810182526001815284151560208083018281528615158486018181528715156060870181815233600081815260eb9097528987209851895496519451925161ffff1990971690151561ff00191617610100941515949094029390931763ffff00001916620100009115159190910263ff0000001916176301000000941515949094029390931790955585516376dfb46760e01b8152306004820152602481019490945260448401526064830152608482018390529251919273e77fb404549dcfb65d82812e1dfb7b931bddd533926376dfb4679260a4808201939291829003018186803b15801561114b57600080fd5b505af415801561115f573d6000803e3d6000fd5b5050505050505050565b6111716133f0565b600861117c81613081565b611184612e9c565b600061118e610e04565b60d25461119b91906149ad565b90506111a68161343b565b6111c36111b1612ee2565b60d1546001600160a01b031683612ef1565b5050565b6111cf612fc3565b60016111da81613081565b60d05460e1546111f6916001600160401b039081169116614a7a565b6001600160401b03164210156112725760405162461bcd60e51b815260206004820152603b60248201527f43616e6e6f742061636372756520696e746572657374206f72206465636c617260448201527a65206661696c757265206265666f72652073746172742074696d6560281b6064820152608401610bbe565b60ce5460e3541061128557610f076134bc565b610f076136fd565b60405163cc8c64b360e01b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063cc8c64b390610e3e9030906004016146f0565b6000805b60d95460ff908116908216101561131f5760ff8116600090815260e8602090815260408083206001600160a01b038716845290915290205461130d90836149ad565b915061131881614a9a565b90506112cb565b50919050565b60ff16600090815260e8602090815260408083206001600160a01b03949094168352929052206002015490565b8261135c81613881565b611364612e9c565b6001600160a01b038316600090815260eb6020526040902054610100900460ff16156113ba5760405162461bcd60e51b81526020600482015260056024820152644c5033303160d81b6044820152606401610bbe565b600860ec5460ff16600a8111156113d3576113d3614704565b14806113f55750600a60ec5460ff16600a8111156113f3576113f3614704565b145b15611434578360ff16836001600160a01b03166000805160206152458339815191528460405161142791815260200190565b60405180910390a3611519565b60ff8416600090815260e8602090815260408083206001600160a01b03871684529091529020805483111561146b5761146b614ab9565b8281600001600082825461147f9190614acf565b925050819055508260e360008282546114989190614acf565b909155505060ff8516600090815260e96020526040812080548592906114bf908490614acf565b909155505080546000036114da576114d860e685613927565b505b8460ff16846001600160a01b03166000805160206152458339815191528560405161150791815260200190565b60405180910390a3610cde8486612f54565b50505050565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f179061154f9033906004016146f0565b602060405180830381865afa15801561156c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611590919061493d565b6115ac5760405162461bcd60e51b8152600401610bbe9061495a565b60016115b781613081565b6115bf612e9c565b6115c93384611786565b8211156116005760405162461bcd60e51b81526020600482015260056024820152644c5031303160d81b6044820152606401610bbe565b60cd54604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd9160048083019260209291908290030181865afa15801561164a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061166e9190614a61565b116116ba5760405162461bcd60e51b815260206004820152601c60248201527b131bd8dace88151bdad95b88131bd8dada5b99c8111a5cd8589b195960221b6044820152606401610bbe565b60ff8316600090815260e8602090815260408083203384529091528120600181018054919285926116ec9084906149ad565b909155505060ff8416600090815260ea6020526040812080548592906117139084906149ad565b909155505060cd54611730906001600160a01b031633308661393c565b60405183815260ff85169033907f687dbd8fb5b0193ed88cef1a599255ed2987fb0ca34adb416494afbd0fc7ec599060200160405180910390a36115193385612f54565b61177c613974565b610fc660006139d3565b60ff8116600081815260e8602090815260408083206001600160a01b0387168452909152812060dc80549293919284929081106117c5576117c5614ae2565b906000526020600020015482600001546117df9190614af8565b90508160010154816117f19190614acf565b95945050505050565b8261180481613881565b60ff8416600090815260e8602090815260408083206001600160a01b0387168452909152902061183560e685613a25565b508281600001600082825461184a91906149ad565b925050819055508260e3600082825461186391906149ad565b909155505060ff8516600090815260e960205260408120805485929061188a9084906149ad565b90915550506003810180546001600160401b031916426001600160401b031617905560405183815260ff8616906001600160a01b038616907fd879168324d037274653fc4d6f6baf56a95cae66607399eb0fc799787820882c90602001611507565b6118f4612fc3565b610fc6613a3a565b6033546001600160a01b031690565b6119136133f0565b600561191e81613081565b611926612e9c565b61192e612094565b156119635760405162461bcd60e51b81526020600482015260056024820152644c5032303360d81b6044820152606401610bbe565b61196b61224e565b156119a05760405162461bcd60e51b815260206004820152600560248201526413140c8c0d60da1b6044820152606401610bbe565b6119ab60e454613a77565b60006119b5613b38565b90506119cc6119c2612ee2565b333060e45461393c565b60005b60d95460ff16811015611ad25760008282815181106119f0576119f0614ae2565b60200260200101519050611a6d611a05612ee2565b82836001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015611a44573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611a689190614a61565b612ef1565b806001600160a01b031663e8bac93b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015611aa857600080fd5b505af1158015611abc573d6000803e3d6000fd5b505050505080611acb90614b0f565b90506119cf565b505050565b6040516351ce1fe360e01b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a906351ce1fe390610e3e9030906004016146f0565b600073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a635a2dc73d611b37858561272b565b611b418686612bc4565b60d05460405160e085901b6001600160e01b031916815260048101939093526024830191909152600160401b90046001600160401b031660448201526064015b602060405180830381865af4158015611b9e573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc29190614a61565b9392505050565b600054610100900460ff1615808015611be95750600054600160ff909116105b80611c0a5750611bf830613bb7565b158015611c0a575060005460ff166001145b611c6d5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610bbe565b6000805460ff191660011790558015611c90576000805461ff0019166101001790555b60405163378da0eb60e01b815273b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063378da0eb90611ccf908a908a908a908a908a90600401614c70565b60006040518083038186803b158015611ce757600080fd5b505af4158015611cfb573d6000803e3d6000fd5b5050505073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a6366db307c88806102800190611d2a9190614ed2565b6040518363ffffffff1660e01b8152600401611d47929190614f1b565b60006040518083038186803b158015611d5f57600080fd5b505af4158015611d73573d6000803e3d6000fd5b50611d849250899150819050614f2f565b60ca91611d92919083614fbb565b50611da06020880188614f2f565b60cb91611dae919083614fbb565b50611dbf606088016040890161466d565b60cc80546001600160a01b0319166001600160a01b0392909216919091179055611def608088016060890161466d565b60cd80546001600160a01b0319166001600160a01b0392909216919091179055608087013560ce5560a087013560cf55611e2f60e0880160c0890161507a565b60d080546001600160401b0319166001600160401b0392909216919091179055611e60610100880160e0890161507a565b60d080546001600160401b0392909216600160401b02600160401b600160801b0319909216919091179055611e9d6101208801610100890161466d565b60d180546001600160a01b0319166001600160a01b039290921691909117905561012087013560d25561014087013560d55561016087013560d35561018087013560d4556101a087013560d6556101c087013560d7556101e087013560d855611f0e61022088016102008901614920565b60d9805460ff191660ff92909216919091179055611f30610220880188614ed2565b611f3c9160da916143a1565b50611f4b610240880188614ed2565b611f579160db916143a1565b50611f66610260880188614ed2565b611f729160dc916143a1565b50611f81610280880188614ed2565b611f8d9160dd916143a1565b50611f9a60e087876143ec565b5060df80546001600160a01b038087166001600160a01b03199283161790925560de805492851692909116919091179055611fd3613bc6565b611fdb613bf5565b606580546001600160a01b0319166001600160a01b0385161790557f6750b3feabf3a89cdbe58c1a46e1483a74cd1acad94c5186534ac83715a836bb878787878760405161202d959493929190614c70565b60405180910390a1801561207b576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b60dc8181548110610f1a57600080fd5b600073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a63b7e8332660e5546120bb6126dd565b6040516001600160e01b031960e085901b16815260048101929092526024820152604401610e3e565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f17906121149033906004016146f0565b602060405180830381865afa158015612131573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612155919061493d565b6121715760405162461bcd60e51b8152600401610bbe9061495a565b60056008600a612182838383612df4565b61218a612e9c565b33600090815260eb602052604090205462010000900460ff16156121c05760405162461bcd60e51b8152600401610bbe90614978565b60d95460ff1684146121fc5760405162461bcd60e51b81526020600482015260056024820152644c5031303760d81b6044820152606401610bbe565b60005b60ff8116851115610cdc5761222f8187878460ff1681811061222357612223614ae2565b90506020020135610b31565b8061223981614a9a565b9150506121ff565b6000610fb060e683613c24565b6000600460ec5460ff16600a81111561226957612269614704565b11156122a657604051631a00010d60e01b815273b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a90631a00010d90610e3e9030906004016146f0565b90565b60006122ba6201518061016d614af8565b60d060089054906101000a90046001600160401b03166001600160401b0316670de0b6b3a764000060e3546122ed612650565b6122f79190614af8565b6123019190615095565b61230b9190614af8565b610e7f9190615095565b61231d6133f0565b600061232881613081565b612330612e9c565b61233b60d254613c30565b610f07612346612ee2565b333060d25461393c565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f17906123809033906004016146f0565b602060405180830381865afa15801561239d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123c1919061493d565b6123dd5760405162461bcd60e51b8152600401610bbe9061495a565b6008600a6123eb8282613ca6565b6123f3612e9c565b33600090815260eb60205260409020546301000000900460ff16156124425760405162461bcd60e51b8152602060048201526005602482015264262818981960d91b6044820152606401610bbe565b61244c3385612704565b156124815760405162461bcd60e51b81526020600482015260056024820152644c5031303360d81b6044820152606401610bbe565b60cd54604080516318160ddd60e01b815290516000926001600160a01b0316916318160ddd9160048083019260209291908290030181865afa1580156124cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ef9190614a61565b1161253c5760405162461bcd60e51b815260206004820152601e60248201527f556e6c6f636b3a20546f6b656e204c6f636b696e672044697361626c656400006044820152606401610bbe565b60ff8416600090815260e860209081526040808320338452909152902060018101548411156125955760405162461bcd60e51b815260206004820152600560248201526413140c4c0d60da1b6044820152606401610bbe565b838160010160008282546125a99190614acf565b909155505060cd546125c5906001600160a01b03163386612ef1565b60405184815260ff86169033907f66cf26ac7213d700d4f403bef72fa02e2deb59197318103e3eabe90e48d70f239060200160405180910390a35050505050565b60db8181548110610f1a57600080fd5b60405163dbb5ffc960e01b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063dbb5ffc990610e3e9030906004016146f0565b60d95460405163b8b3b07560e01b815230600482015260ff909116602482015260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063b8b3b07590604401610e3e565b6000610e7f60e6613d1d565b60dd8181548110610f1a57600080fd5b60e081815481106126c357600080fd5b6000918252602090912001546001600160a01b0316905081565b600073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a6333f74e4260e3546120bb6127d7565b6000806127118484612c97565b9050600061271f8585611325565b90506117f18183614acf565b60ff16600090815260e8602090815260408083206001600160a01b03949094168352929052205490565b6000806127628484612c97565b905060006127708585611325565b90508181111561278557600092505050610fb0565b6117f18183614acf565b6127976133f0565b60026127a281613081565b6127aa612e9c565b6127b560e354613d27565b610f076127c0612ee2565b60d15460e3546001600160a01b0390911690612ef1565b60d55460d054604051633099159f60e01b81526004810192909252600160401b90046001600160401b0316602482015260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a90633099159f90604401610e3e565b6128366133f0565b61283e612e9c565b600061284861224e565b90508181106128815760405162461bcd60e51b81526020600482015260056024820152644c5032303160d81b6044820152606401610bbe565b80156128e8576000612891611ad7565b612899612616565b6128a39190614acf565b90506128af81836149ad565b8310156128e65760405162461bcd60e51b8152602060048201526005602482015264262819181960d91b6044820152606401610bbe565b505b60006128f48284614acf565b90506128fe612094565b8111156129105761290d612094565b90505b600082670de0b6b3a764000060d6548461292a9190614af8565b6129349190615095565b61293e91906149ad565b9050600061294c8286614acf565b9050838560e55461295d91906149ad565b6129679190614acf565b60e555811561298d5761298d61297b612ee2565b60df546001600160a01b031684612ef1565b6129a0612998612ee2565b33308861393c565b83156129dc5760405184815233907fe7963b683dbff9c1510ab87dece1fffff5d084905fe94cdd43f551e9b32a46739060200160405180910390a25b60d15460408051878152602081018490529081018490526001600160a01b03909116907f8c1b2960f1620d7bf5f7a6c04f6ed9673508112f95e73af8681433cca020d7929060600160405180910390a25050505050565b612a3b612fc3565b6004612a4681613081565b612a4e612e9c565b60e180546001600160401b031916426001600160401b031617905560ec805460ff191660011790556000612a80613b38565b905060005b60e054811015612b7a57818181518110612aa157612aa1614ae2565b60200260200101516001600160a01b0316635e4c57a46040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612ae357600080fd5b505af1158015612af7573d6000803e3d6000fd5b50505050818181518110612b0d57612b0d614ae2565b60200260200101516001600160a01b031663e8bac93b6040518163ffffffff1660e01b8152600401600060405180830381600087803b158015612b4f57600080fd5b505af1158015612b63573d6000803e3d6000fd5b505050508080612b7290614b0f565b915050612a85565b5060e1546040517f558d1359f6a6235015698503e6fbee2e3ba2c372f84350d2f82609577383203891612bb8916001600160401b0390911690614640565b60405180910390a15050565b60405163075adf8960e41b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a906375adf89090611b81903090879087906004016150b7565b6003612c0d81613081565b60d2805460009091556111c36111b1612ee2565b612c29613974565b6001600160a01b038116612c8e5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610bbe565b610f07816139d3565b6040516338133fbb60e21b815260009073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063e04cfeec90611b81903090879087906004016150b7565b606554604051637c2f7f1760e01b81526001600160a01b0390911690637c2f7f1790612d059033906004016146f0565b602060405180830381865afa158015612d22573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612d46919061493d565b612d625760405162461bcd60e51b8152600401610bbe9061495a565b60408051608081018252600080825260208083018281528385018381526060850184815233855260eb9093529490922092518354925194519151151563010000000263ff0000001992151562010000029290921663ffff0000199515156101000261ff00199215159290921661ffff1990941693909317179390931617919091179055565b60cb8054610cf2906149c0565b82600a811115612e0657612e06614704565b60ec5460ff16600a811115612e1d57612e1d614704565b1480612e4f575081600a811115612e3657612e36614704565b60ec5460ff16600a811115612e4d57612e4d614704565b145b80612e80575080600a811115612e6757612e67614704565b60ec5460ff16600a811115612e7e57612e7e614704565b145b611ad25760405162461bcd60e51b8152600401610bbe906150dd565b60985460ff1615610fc65760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610bbe565b60cc546001600160a01b031690565b6040516001600160a01b038316602482015260448101829052611ad290849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152613d9d565b8060ff16826001600160a01b03167fee5f2ff093bda9a8bbe9bb17806ee2dd3d60f26843793755cffd62d63d6b1130612f8d8585612bc4565b612f978686611b11565b612fa18787611325565b6040805193845260208401929092529082015260600160405180910390a35050565b33612fcc6118fc565b6001600160a01b0316148061304d5750606554604051630935e01b60e21b81526001600160a01b03909116906324d7806c9061300c9033906004016146f0565b602060405180830381865afa158015613029573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061304d919061493d565b610fc65760405162461bcd60e51b815260206004820152600560248201526441413a4f4160d81b6044820152606401610bbe565b80600a81111561309357613093614704565b60ec5460ff16600a8111156130aa576130aa614704565b14610f075760405162461bcd60e51b8152600401610bbe906150dd565b6130cf612e9c565b60e280546001600160401b034216600160c01b026001600160c01b0390911617905560ec805460ff19166009179055613106613e6f565b6000613110612ee2565b6001600160a01b03166370a08231306040518263ffffffff1660e01b815260040161313b91906146f0565b602060405180830381865afa158015613158573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061317c9190614a61565b90506000613188613b38565b905060005b60e05481101561335f5760008282815181106131ab576131ab614ae2565b602002602001015190506000670de0b6b3a76400008560dd85815481106131d4576131d4614ae2565b90600052602060002001546131e99190614af8565b6131f39190615095565b90506000826001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015613235573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906132599190614a61565b61326b670de0b6b3a764000084614af8565b6132759190615095565b9050811561328f5761328f613288612ee2565b8484612ef1565b6132998287614acf565b6040516351baff6160e11b8152600481018390529096506001600160a01b0384169063a375fec290602401600060405180830381600087803b1580156132de57600080fd5b505af11580156132f2573d6000803e3d6000fd5b50505050826001600160a01b031663e8bac93b6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561333157600080fd5b505af1158015613345573d6000803e3d6000fd5b50505050505050808061335790614b0f565b91505061318d565b507fca020e3ba10c59076a4153bc43dfb5b5edc2fd376cfe19f53ccad5a2686dab4060e260189054906101000a90046001600160401b0316604051612bb89190614640565b6133ac613f55565b6098805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516133e691906146f0565b60405180910390a1565b60d1546001600160a01b0316336001600160a01b031614610fc65760405162461bcd60e51b81526020600482015260056024820152644c5030303360d81b6044820152606401610bbe565b613443612e9c565b60e280546001600160401b034216600160801b02600160801b600160c01b031990911617905560ec805460ff1916600a17905560d1546040518281526001600160a01b03909116907fa2d53e3de69d40a03f150e903c3da14325fbdf2cfdfb9cdc4cfed25bf8cb4bdb906020015b60405180910390a250565b6134c4612e9c565b60e180546001600160401b034216600160401b02600160401b600160801b031990911617905560ec805460ff191660021790556000613501613b38565b905060005b81518110156136a857600082828151811061352357613523614ae2565b60200260200101519050806001600160a01b031663ac67e1af6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561356857600080fd5b505af115801561357c573d6000803e3d6000fd5b50505050806001600160a01b031663ad960ce16040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156135bb57600080fd5b505af11580156135cf573d6000803e3d6000fd5b50505050806001600160a01b031663baa83cdd826001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015613620573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136449190614a61565b6040518263ffffffff1660e01b815260040161366291815260200190565b600060405180830381600087803b15801561367c57600080fd5b505af1158015613690573d6000803e3d6000fd5b505050505080806136a090614b0f565b915050613506565b5060e15460e35460408051600160401b9093046001600160401b0316835260208301919091527f30dd79baab3a98aaf5598d29e79e0bccc05be70214863bd4b1dcfa8f2ad6fca791015b60405180910390a150565b613705612e9c565b60e180546001600160401b034216600160801b02600160801b600160c01b031990911617905560ec805460ff191660031790556000613742613b38565b905060005b60e05481101561383c5781818151811061376357613763614ae2565b60200260200101516001600160a01b031663ac67e1af6040518163ffffffff1660e01b8152600401600060405180830381600087803b1580156137a557600080fd5b505af11580156137b9573d6000803e3d6000fd5b505050508181815181106137cf576137cf614ae2565b60200260200101516001600160a01b031663e8bac93b6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561381157600080fd5b505af1158015613825573d6000803e3d6000fd5b50505050808061383490614b0f565b915050613747565b507f8c96276c843b319de12382c36146032b02dfe79d5150583d30bce0979b5b48f360e160109054906101000a90046001600160401b03166040516136f29190614640565b60e05460ff8216106138bd5760405162461bcd60e51b81526020600482015260056024820152644c5030303160d81b6044820152606401610bbe565b336001600160a01b031660e08260ff16815481106138dd576138dd614ae2565b6000918252602090912001546001600160a01b031614610f075760405162461bcd60e51b8152602060048201526005602482015264262818181960d91b6044820152606401610bbe565b6000611bc2836001600160a01b038416613f9e565b6040516001600160a01b03808516602483015283166044820152606481018290526115199085906323b872dd60e01b90608401612f1d565b3361397d6118fc565b6001600160a01b031614610fc65760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610bbe565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000611bc2836001600160a01b038416614098565b613a42612e9c565b6098805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586133d93390565b613a7f612e9c565b60e280546001600160401b034216600160401b02600160401b600160801b031990911617905560ec805460ff1916600817905560d1546040518281526001600160a01b03909116907fd42b39211abc7e36cc2cba4151ce436c7a5140bba8e34d0df17f7380842dd79e9060200160405180910390a27f087734f394f09d0a1ca486f05319fcc6041f1b2f852eabe4c62f261112aa5aca60e260089054906101000a90046001600160401b03166040516136f29190614640565b60405163186a3e8760e31b815260609073b7e9ab34ab60ef686ba9de08d1b7ab85ed88c02a9063c351f43890613b729030906004016146f0565b600060405180830381865af4158015613b8f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052610e7f91908101906150fc565b6001600160a01b03163b151590565b600054610100900460ff16613bed5760405162461bcd60e51b8152600401610bbe9061518a565b610fc66140e7565b600054610100900460ff16613c1c5760405162461bcd60e51b8152600401610bbe9061518a565b610fc6614117565b6000611bc2838361414a565b613c38612e9c565b60e180546001600160401b034216600160c01b026001600160c01b0390911617905560ec805460ff1916600417905560d1546040518281526001600160a01b03909116907f177047f9a933a61817e2caeb4dd0612b2f2c53341207667b849404a48d73d3c0906020016134b1565b81600a811115613cb857613cb8614704565b60ec5460ff16600a811115613ccf57613ccf614704565b1480613d01575080600a811115613ce857613ce8614704565b60ec5460ff16600a811115613cff57613cff614704565b145b6111c35760405162461bcd60e51b8152600401610bbe906150dd565b6000610fb0825490565b613d2f612e9c565b60e280546001600160401b0342166001600160401b031990911617905560e481905560ec805460ff1916600517905560d1546040518281526001600160a01b03909116907f30344454671aa5c91747fc90b3c935dbd2b9fe1112579bcad62044307af98bcf906020016134b1565b6000613df2826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166141749092919063ffffffff16565b805190915015611ad25780806020019051810190613e10919061493d565b611ad25760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610bbe565b6000613e79613b38565b905060005b60d95460ff90811690821610156111c35760005b613e9a612697565b811015613f4257613f30613ead82612241565b848460ff1681518110613ec257613ec2614ae2565b60200260200101516001600160a01b031663af640d0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613f07573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613f2b91906151d5565b61418b565b80613f3a81614b0f565b915050613e92565b5080613f4d81614a9a565b915050613e7e565b60985460ff16610fc65760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610bbe565b60008181526001830160205260408120548015614087576000613fc2600183614acf565b8554909150600090613fd690600190614acf565b905081811461403b576000866000018281548110613ff657613ff6614ae2565b906000526020600020015490508087600001848154811061401957614019614ae2565b6000918252602080832090910192909255918252600188019052604090208390555b855486908061404c5761404c6151f2565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610fb0565b6000915050610fb0565b5092915050565b60008181526001830160205260408120546140df57508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610fb0565b506000610fb0565b600054610100900460ff1661410e5760405162461bcd60e51b8152600401610bbe9061518a565b610fc6336139d3565b600054610100900460ff1661413e5760405162461bcd60e51b8152600401610bbe9061518a565b6098805460ff19169055565b600082600001828154811061416157614161614ae2565b9060005260206000200154905092915050565b6060614183848460008561422a565b949350505050565b60006141978383612704565b90508015611ad25760ff8216600090815260e8602090815260408083206001600160a01b0387168452909152812060020180548392906141d89084906149ad565b909155506141f090506141e9612ee2565b8483612ef1565b8160ff16836001600160a01b03166000805160206152258339815191528360405161421d91815260200190565b60405180910390a3505050565b60608247101561428b5760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610bbe565b600080866001600160a01b031685876040516142a79190615208565b60006040518083038185875af1925050503d80600081146142e4576040519150601f19603f3d011682016040523d82523d6000602084013e6142e9565b606091505b50915091506142fa87838387614305565b979650505050505050565b6060831561437257825160000361436b5761431f85613bb7565b61436b5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610bbe565b5081614183565b61418383838151156143875781518083602001fd5b8060405162461bcd60e51b8152600401610bbe91906144c3565b8280548282559060005260206000209081019282156143dc579160200282015b828111156143dc5782358255916020019190600101906143c1565b506143e892915061443f565b5090565b8280548282559060005260206000209081019282156143dc579160200282015b828111156143dc5781546001600160a01b0319166001600160a01b0384351617825560209092019160019091019061440c565b5b808211156143e85760008155600101614440565b60ff81168114610f0757600080fd5b803561446e81614454565b919050565b6000806040838503121561448657600080fd5b823561449181614454565b946020939093013593505050565b60005b838110156144ba5781810151838201526020016144a2565b50506000910152565b60208152600082518060208401526144e281604085016020870161449f565b601f01601f19169190910160400192915050565b6001600160a01b0381168114610f0757600080fd5b803561446e816144f6565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171561455457614554614516565b604052919050565b60006001600160401b0382111561457557614575614516565b5060051b60200190565b6000806000806080858703121561459557600080fd5b84356145a0816144f6565b93506020858101356001600160401b038111156145bc57600080fd5b8601601f810188136145cd57600080fd5b80356145e06145db8261455c565b61452c565b81815260059190911b8201830190838101908a8311156145ff57600080fd5b928401925b82841015614626578335614617816144f6565b82529284019290840190614604565b979a97995050505060408601359560600135949350505050565b6001600160401b0391909116815260200190565b60006020828403121561466657600080fd5b5035919050565b60006020828403121561467f57600080fd5b8135611bc2816144f6565b8015158114610f0757600080fd5b6000806000606084860312156146ad57600080fd5b83356146b88161468a565b925060208401356146c88161468a565b915060408401356146d88161468a565b809150509250925092565b6001600160a01b03169052565b6001600160a01b0391909116815260200190565b634e487b7160e01b600052602160045260246000fd5b60208101600b831061473c57634e487b7160e01b600052602160045260246000fd5b91905290565b6000806040838503121561475557600080fd5b8235614760816144f6565b9150602083013561477081614454565b809150509250929050565b60008060006060848603121561479057600080fd5b833561479b81614454565b925060208401356147ab816144f6565b929592945050506040919091013590565b600080604083850312156147cf57600080fd5b82356147da81614454565b91506020830135614770816144f6565b60008083601f8401126147fc57600080fd5b5081356001600160401b0381111561481357600080fd5b6020830191508360208260051b850101111561482e57600080fd5b9250929050565b60008060008060008060a0878903121561484e57600080fd5b86356001600160401b038082111561486557600080fd5b908801906102a0828b03121561487a57600080fd5b9096506020880135908082111561489057600080fd5b5061489d89828a016147ea565b90965094505060408701356148b1816144f6565b925060608701356148c1816144f6565b915060808701356148d1816144f6565b809150509295509295509295565b600080602083850312156148f257600080fd5b82356001600160401b0381111561490857600080fd5b614914858286016147ea565b90969095509350505050565b60006020828403121561493257600080fd5b8135611bc281614454565b60006020828403121561494f57600080fd5b8151611bc28161468a565b60208082526004908201526310504e9360e21b604082015260600190565b6020808252600590820152644c5031303560d81b604082015260600190565b634e487b7160e01b600052601160045260246000fd5b80820180821115610fb057610fb0614997565b600181811c908216806149d457607f821691505b60208210810361131f57634e487b7160e01b600052602260045260246000fd5b600060a0820160018060a01b03808916845260208189168186015260a0604086015282885180855260c087019150828a01945060005b81811015614a48578551851683529483019491830191600101614a2a565b5050606086019790975250505050608001529392505050565b600060208284031215614a7357600080fd5b5051919050565b6001600160401b0381811683821601908082111561409157614091614997565b600060ff821660ff8103614ab057614ab0614997565b60010192915050565b634e487b7160e01b600052600160045260246000fd5b81810381811115610fb057610fb0614997565b634e487b7160e01b600052603260045260246000fd5b8082028115828204841417610fb057610fb0614997565b600060018201614b2157614b21614997565b5060010190565b6000808335601e19843603018112614b3f57600080fd5b83016020810192503590506001600160401b03811115614b5e57600080fd5b80360382131561482e57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b80356001600160401b038116811461446e57600080fd5b6000808335601e19843603018112614bc457600080fd5b83016020810192503590506001600160401b03811115614be357600080fd5b8060051b360382131561482e57600080fd5b81835260006001600160fb1b03831115614c0e57600080fd5b8260051b80836020870137939093016020019392505050565b8183526000602080850194508260005b85811015614c65578135614c4a816144f6565b6001600160a01b031687529582019590820190600101614c37565b509495945050505050565b608081526000614c808788614b28565b6102a06080850152614c9761032085018284614b6d565b915050614ca76020890189614b28565b607f19808685030160a0870152614cbf848385614b6d565b9350614ccd60408c0161450b565b9250614cdc60c08701846146e3565b614ce860608c0161450b565b9250614cf760e08701846146e3565b610100925060808b013583870152610120915060a08b013582870152614d1f60c08c01614b96565b610140614d36818901836001600160401b03169052565b614d4260e08e01614b96565b9150610160614d5b818a01846001600160401b03169052565b614d66868f0161450b565b95506101809250614d79838a01876146e3565b6101a09550848e0135868a01526101c09450818e0135858a01526101e09150808e0135828a015250610200828e0135818a01526102209250858e0135838a01526102409550848e0135868a01526102609450818e0135858a0152614dde818f01614463565b915050610280614df2818a018360ff169052565b614dfe838f018f614bad565b9350915083898803016102a08a0152614e18878484614bf5565b9650614e26868f018f614bad565b9650925083898803016102c08a0152614e40878785614bf5565b9650614e4e858f018f614bad565b9650945083898803016102e08a0152614e68878787614bf5565b9650614e76818f018f614bad565b96509450505050808685030161030087015250614e94838383614bf5565b925050508281036020840152614eab818789614c27565b915050614ebb60408301856146e3565b614ec860608301846146e3565b9695505050505050565b6000808335601e19843603018112614ee957600080fd5b8301803591506001600160401b03821115614f0357600080fd5b6020019150600581901b360382131561482e57600080fd5b602081526000614183602083018486614bf5565b6000808335601e19843603018112614f4657600080fd5b8301803591506001600160401b03821115614f6057600080fd5b60200191503681900382131561482e57600080fd5b601f821115611ad257600081815260208120601f850160051c81016020861015614f9c5750805b601f850160051c820191505b81811015610cdc57828155600101614fa8565b6001600160401b03831115614fd257614fd2614516565b614fe683614fe083546149c0565b83614f75565b6000601f84116001811461501a57600085156150025750838201355b600019600387901b1c1916600186901b178355610cde565b600083815260209020601f19861690835b8281101561504b578685013582556020948501946001909201910161502b565b50868210156150685760001960f88860031b161c19848701351681555b505060018560011b0183555050505050565b60006020828403121561508c57600080fd5b611bc282614b96565b6000826150b257634e487b7160e01b600052601260045260246000fd5b500490565b6001600160a01b03938416815291909216602082015260ff909116604082015260600190565b60208082526005908201526413140c0c0d60da1b604082015260600190565b6000602080838503121561510f57600080fd5b82516001600160401b0381111561512557600080fd5b8301601f8101851361513657600080fd5b80516151446145db8261455c565b81815260059190911b8201830190838101908783111561516357600080fd5b928401925b828410156142fa57835161517b816144f6565b82529284019290840190615168565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6000602082840312156151e757600080fd5b8151611bc281614454565b634e487b7160e01b600052603160045260246000fd5b6000825161521a81846020870161449f565b919091019291505056fe8804794d500e9b037c6ed2b6b3b001fb1d50923e083430e8084f35899bc17f91efb7b3ef9e1a73a4afe1f53d0a172ac1c2225eb0ecc20d9db73eea4a716e8ce6a2646970667358221220a5bfe74baed8036e4a4b5038af678d073c2565efa25203b4226f1de43e0d638764736f6c63430008120033
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.