Warning! Contract bytecode has been changed and doesn't match the verified one. Therefore, interaction with this smart contract may be risky.
This contract has been verified via Sourcify.
View contract in Sourcify repository
- Contract name:
- LendingLedger
- Optimization enabled
- true
- Compiler version
- v0.8.17+commit.8df45f5f
- Optimization runs
- 1000
- EVM Version
- london
- Verified at
- 2024-04-26T13:23:08.014959Z
src/LendingLedger.sol
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.16; import {VotingEscrow} from "./VotingEscrow.sol"; import {GaugeController} from "./GaugeController.sol"; import {LiquidityGauge} from "./LiquidityGauge.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; interface BaseV1Factory { function getPair( address, address, bool ) external view returns (address); function createPair( address, address, bool ) external returns (address); } contract LendingLedger { // Constants uint256 public constant BLOCK_EPOCH = 100_000; // 100000 blocks, roughly 1 week uint256 public averageBlockTime = 5700; // Average block time in milliseconds uint256 public referenceBlockNumber; uint256 public referenceBlockTime; // Used to convert block numbers to timestamps together with averageBlockTime address public constant WCANTO = 0x826551890Dc65655a0Aceca109aB11AbDbD7a07B; BaseV1Factory public constant baseV1Factory = BaseV1Factory(0xE387067f12561e579C5f7d4294f51867E0c1cFba); // State address public governance; GaugeController public gaugeController; mapping(address => bool) public lendingMarketWhitelist; /// @dev Info for each user. struct UserInfo { uint256 amount; // Amount of cNOTE that the user has provided. int256 rewardDebt; // Amount of CANTO entitled to the user. int256 secRewardDebt; // Amount of secondary rewards entitled to the user. } /// @dev Info of each lending market. struct MarketInfo { uint128 accCantoPerShare; uint128 secRewardsPerShare; uint64 lastRewardBlock; } mapping(address => mapping(address => UserInfo)) public userInfo; // Info of each user for the different lending markets mapping(address => MarketInfo) public marketInfo; // Info of each lending market mapping(uint256 => uint256) public cantoPerBlock; // CANTO per block for each epoch /// @dev Lending Market => Epoch => Balance mapping(address => uint256) public lendingMarketTotalBalance; // Total balance locked within the market mapping(address => address) public liquidityGauges; // Two way mapping for markets with liquidity gauge enabled modifier onlyGovernance() { require(msg.sender == governance); _; } constructor(address _gaugeController, address _governance) { gaugeController = GaugeController(_gaugeController); governance = _governance; referenceBlockNumber = block.number; referenceBlockTime = block.timestamp; } /// @notice Set governance address /// @param _governance New governance address function setGovernance(address _governance) external onlyGovernance { governance = _governance; } function update_market(address _market) public { require(lendingMarketWhitelist[_market], "Market not whitelisted"); MarketInfo storage market = marketInfo[_market]; if (block.number > market.lastRewardBlock) { uint256 marketSupply = lendingMarketTotalBalance[_market]; if (marketSupply > 0) { uint256 i = market.lastRewardBlock; while (i < block.number) { uint256 epoch = (i / BLOCK_EPOCH) * BLOCK_EPOCH; // Rewards and voting weights are aligned on a weekly basis uint256 nextEpoch = epoch + BLOCK_EPOCH; uint256 blockDelta = Math.min(nextEpoch, block.number) - i; // May not be the exact time, but will ensure that it is equal for all users and epochs. // If this ever drifts significantly, the average block time and / or reference block time & number can be updated. However, update_market needs to be called for all markets beforehand. uint256 epochTime = referenceBlockTime + ((block.number - referenceBlockNumber) * averageBlockTime) / 1000; market.accCantoPerShare += uint128( (blockDelta * cantoPerBlock[epoch] * gaugeController.gauge_relative_weight_write(_market, epochTime)) / marketSupply ); market.secRewardsPerShare += uint128((blockDelta * 1e36) / marketSupply); // Scale by 1e18, consumers need to divide by it i += blockDelta; } } market.lastRewardBlock = uint64(block.number); } } /// @notice Function that is called by the lending market on cNOTE deposits / withdrawals /// @param _lender The address of the lender /// @param _delta The amount of cNote deposited (positive) or withdrawn (negative) function sync_ledger(address _lender, int256 _delta) external { address lendingMarket = msg.sender; // check if liquidity gauge is being used for the market if (liquidityGauges[lendingMarket] != address(0)) lendingMarket = liquidityGauges[lendingMarket]; update_market(lendingMarket); // Checks if the market is whitelisted MarketInfo storage market = marketInfo[lendingMarket]; UserInfo storage user = userInfo[lendingMarket][_lender]; if (_delta >= 0) { user.amount += uint256(_delta); user.rewardDebt += int256((uint256(_delta) * market.accCantoPerShare) / 1e18); user.secRewardDebt += int256((uint256(_delta) * market.secRewardsPerShare) / 1e18); } else { user.amount -= uint256(-_delta); user.rewardDebt -= int256((uint256(-_delta) * market.accCantoPerShare) / 1e18); user.secRewardDebt -= int256((uint256(-_delta) * market.secRewardsPerShare) / 1e18); } int256 updatedMarketBalance = int256(lendingMarketTotalBalance[lendingMarket]) + _delta; require(updatedMarketBalance >= 0, "Market balance underflow"); // Sanity check performed here, but the market should ensure that this never happens lendingMarketTotalBalance[lendingMarket] = uint256(updatedMarketBalance); } /// @notice Claim the CANTO for a given market. Can only be performed for prior (i.e. finished) epochs, not the current one /// @param _market Address of the market function claim(address _market) external { update_market(_market); // Checks if the market is whitelisted MarketInfo storage market = marketInfo[_market]; UserInfo storage user = userInfo[_market][msg.sender]; int256 accumulatedCanto = int256((uint256(user.amount) * market.accCantoPerShare) / 1e18); int256 cantoToSend = accumulatedCanto - user.rewardDebt; user.rewardDebt = accumulatedCanto; if (cantoToSend > 0) { (bool success, ) = msg.sender.call{value: uint256(cantoToSend)}(""); require(success, "Failed to send CANTO"); } } /// @notice Used by governance to set the overall CANTO rewards per epoch /// @param _fromEpoch From which epoch (provided as block number) to set the rewards from /// @param _toEpoch Until which epoch (provided as block number) to set the rewards to /// @param _amountPerBlock The amount per block function setRewards( uint256 _fromEpoch, uint256 _toEpoch, uint256 _amountPerBlock ) external onlyGovernance { require(_fromEpoch % BLOCK_EPOCH == 0 && _toEpoch % BLOCK_EPOCH == 0, "Invalid block number"); for (uint256 i = _fromEpoch; i <= _toEpoch; i += BLOCK_EPOCH) { cantoPerBlock[i] = _amountPerBlock; } } /// @notice Used by governance to whitelist a lending market /// @param _market Address of the market to whitelist /// @param _isWhiteListed Whether the market is whitelisted or not function whiteListLendingMarket( address _market, bool _isWhiteListed, bool _isLP ) external onlyGovernance { require(lendingMarketWhitelist[_market] != _isWhiteListed, "No change"); if (_isLP && liquidityGauges[_market] == address(0)) { address pair = baseV1Factory.getPair(_market, WCANTO, false); if (pair == address(0)) { pair = baseV1Factory.createPair(_market, WCANTO, false); } LiquidityGauge liquidityGauge = new LiquidityGauge(pair, address(this)); liquidityGauges[_market] = address(liquidityGauge); // add reverse also for reference in sync_ledger liquidityGauges[address(liquidityGauge)] = _market; } lendingMarketWhitelist[_market] = _isWhiteListed; if (_isWhiteListed) { marketInfo[_market].lastRewardBlock = uint64(block.number); } } /// @notice Used by governance to set the block time parameters if the drift is too large /// @param _averageBlockTime The average block time in milliseconds /// @param _referenceBlockTime The reference block time /// @param _referenceBlockNumber The reference block number function setBlockTimeParameters( uint256 _averageBlockTime, uint256 _referenceBlockTime, uint256 _referenceBlockNumber ) external onlyGovernance { averageBlockTime = _averageBlockTime; referenceBlockTime = _referenceBlockTime; referenceBlockNumber = _referenceBlockNumber; } receive() external payable {} }
/src/VotingEscrow.sol
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.16; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; /// @title VotingEscrow /// @author Curve Finance (MIT) - original concept and implementation in Vyper /// (see https://github.com/curvefi/curve-dao-contracts/blob/master/contracts/VotingEscrow.vy) /// mStable (AGPL) - forking Curve's Vyper contract and porting to Solidity /// (see https://github.com/mstable/mStable-contracts/blob/master/contracts/governance/IncentivisedVotingLockup.sol) /// FIAT DAO (AGPL) - https://github.com/code-423n4/2022-08-fiatdao/blob/main/contracts/VotingEscrow.sol /// mkt.market (AGPL) - This version /// @notice Plain Curve VotingEscrow mechanics with following adjustments: /// 1) Reduced pointHistory array size and, as a result, lifetime of the contract /// 2) Removed public deposit_for and Aragon compatibility (no use case) /// 3) Use native token (CANTO) instead of an ERC20 token as the underlying asset /// 4) Lock time is fixed to 5 years, every action resets it contract VotingEscrow is ReentrancyGuard { // Shared Events event Deposit(address indexed provider, uint256 value, uint256 locktime, LockAction indexed action, uint256 ts); event Withdraw(address indexed provider, uint256 value, LockAction indexed action, uint256 ts); event Unlock(); // Voting token string public name; string public symbol; uint256 public decimals = 18; // Shared global state address public governance; uint256 public constant WEEK = 7 days; uint256 public constant LOCKTIME = 1825 days; uint256 public constant MULTIPLIER = 10**18; // Lock state uint256 public globalEpoch; Point[1000000000000000000] public pointHistory; // 1e9 * userPointHistory-length, so sufficient for 1e9 users mapping(address => Point[1000000000]) public userPointHistory; mapping(address => uint256) public userPointEpoch; mapping(uint256 => int128) public slopeChanges; mapping(address => LockedBalance) public locked; bool public unlockOverride; // Structs struct Point { int128 bias; int128 slope; uint256 ts; uint256 blk; } struct LockedBalance { int128 amount; uint256 end; int128 delegated; address delegatee; } // Miscellaneous enum LockAction { CREATE, INCREASE_AMOUNT, INCREASE_AMOUNT_AND_DELEGATION, INCREASE_TIME, WITHDRAW, QUIT, DELEGATE, UNDELEGATE } modifier onlyGovernance() { require(msg.sender == governance); _; } /// @notice Initializes state /// @param _name The name of the voting token /// @param _symbol The symbol of the voting token /// @param _governance The governance address constructor( string memory _name, string memory _symbol, address _governance ) { pointHistory[0] = Point({bias: int128(0), slope: int128(0), ts: block.timestamp, blk: block.number}); name = _name; symbol = _symbol; governance = _governance; } /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// GOVERNANCE /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// @notice Set governance address /// @param _governance New governance address function setGovernance(address _governance) external onlyGovernance { governance = _governance; } /// @notice Toggle unlock override function toggleUnlockOverride() external onlyGovernance { unlockOverride = !unlockOverride; } /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// LOCK MANAGEMENT /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// @notice Returns a user's lock expiration /// @param _addr The address of the user /// @return Expiration of the user's lock function lockEnd(address _addr) external view returns (uint256) { return locked[_addr].end; } /// @notice Returns the last available user point for a user /// @param _addr User address /// @return bias i.e. y /// @return slope i.e. linear gradient /// @return ts i.e. time point was logged function getLastUserPoint(address _addr) external view returns ( int128 bias, int128 slope, uint256 ts ) { uint256 uepoch = userPointEpoch[_addr]; if (uepoch == 0) { return (0, 0, 0); } Point memory point = userPointHistory[_addr][uepoch]; return (point.bias, point.slope, point.ts); } /// @notice Records a checkpoint of both individual and global slope /// @param _addr User address, or address(0) for only global /// @param _oldLocked Old amount that user had locked, or null for global /// @param _newLocked new amount that user has locked, or null for global function _checkpoint( address _addr, LockedBalance memory _oldLocked, LockedBalance memory _newLocked ) internal { Point memory userOldPoint; Point memory userNewPoint; int128 oldSlopeDelta = 0; int128 newSlopeDelta = 0; uint256 epoch = globalEpoch; if (_addr != address(0)) { // Calculate slopes and biases // Kept at zero when they have to if (_oldLocked.end > block.timestamp && _oldLocked.delegated > 0) { userOldPoint.slope = _oldLocked.delegated / int128(int256(LOCKTIME)); userOldPoint.bias = userOldPoint.slope * int128(int256(_oldLocked.end - block.timestamp)); } if (_newLocked.end > block.timestamp && _newLocked.delegated > 0) { userNewPoint.slope = _newLocked.delegated / int128(int256(LOCKTIME)); userNewPoint.bias = userNewPoint.slope * int128(int256(_newLocked.end - block.timestamp)); } // Moved from bottom final if statement to resolve stack too deep err // start { // Now handle user history uint256 uEpoch = userPointEpoch[_addr]; if (uEpoch == 0) { userPointHistory[_addr][uEpoch + 1] = userOldPoint; } userPointEpoch[_addr] = uEpoch + 1; userNewPoint.ts = block.timestamp; userNewPoint.blk = block.number; userPointHistory[_addr][uEpoch + 1] = userNewPoint; // } end // Read values of scheduled changes in the slope // oldLocked.end can be in the past and in the future // newLocked.end can ONLY by in the FUTURE unless everything expired: than zeros oldSlopeDelta = slopeChanges[_oldLocked.end]; if (_newLocked.end != 0) { if (_newLocked.end == _oldLocked.end) { newSlopeDelta = oldSlopeDelta; } else { newSlopeDelta = slopeChanges[_newLocked.end]; } } } Point memory lastPoint = Point({bias: 0, slope: 0, ts: block.timestamp, blk: block.number}); if (epoch > 0) { lastPoint = pointHistory[epoch]; } uint256 lastCheckpoint = lastPoint.ts; // initialLastPoint is used for extrapolation to calculate block number // (approximately, for *At methods) and save them // as we cannot figure that out exactly from inside the contract Point memory initialLastPoint = Point({bias: 0, slope: 0, ts: lastPoint.ts, blk: lastPoint.blk}); uint256 blockSlope = 0; // dblock/dt if (block.timestamp > lastPoint.ts) { blockSlope = (MULTIPLIER * (block.number - lastPoint.blk)) / (block.timestamp - lastPoint.ts); } // If last point is already recorded in this block, slope=0 // But that's ok b/c we know the block in such case // Go over weeks to fill history and calculate what the current point is uint256 iterativeTime = _floorToWeek(lastCheckpoint); for (uint256 i = 0; i < 255; i++) { // Hopefully it won't happen that this won't get used in 5 years! // If it does, users will be able to withdraw but vote weight will be broken iterativeTime = iterativeTime + WEEK; int128 dSlope = 0; if (iterativeTime > block.timestamp) { iterativeTime = block.timestamp; } else { dSlope = slopeChanges[iterativeTime]; } int128 biasDelta = lastPoint.slope * int128(int256((iterativeTime - lastCheckpoint))); lastPoint.bias = lastPoint.bias - biasDelta; lastPoint.slope = lastPoint.slope + dSlope; // This can happen if (lastPoint.bias < 0) { lastPoint.bias = 0; } // This cannot happen - just in case if (lastPoint.slope < 0) { lastPoint.slope = 0; } lastCheckpoint = iterativeTime; lastPoint.ts = iterativeTime; lastPoint.blk = initialLastPoint.blk + (blockSlope * (iterativeTime - initialLastPoint.ts)) / MULTIPLIER; // when epoch is incremented, we either push here or after slopes updated below epoch = epoch + 1; if (iterativeTime == block.timestamp) { lastPoint.blk = block.number; break; } else { pointHistory[epoch] = lastPoint; } } globalEpoch = epoch; // Now pointHistory is filled until t=now if (_addr != address(0)) { // If last point was in this block, the slope change has been applied already // But in such case we have 0 slope(s) lastPoint.slope = lastPoint.slope + userNewPoint.slope - userOldPoint.slope; lastPoint.bias = lastPoint.bias + userNewPoint.bias - userOldPoint.bias; if (lastPoint.slope < 0) { lastPoint.slope = 0; } if (lastPoint.bias < 0) { lastPoint.bias = 0; } } // Record the changed point into history pointHistory[epoch] = lastPoint; if (_addr != address(0)) { // Schedule the slope changes (slope is going down) // We subtract new_user_slope from [new_locked.end] // and add old_user_slope to [old_locked.end] if (_oldLocked.end > block.timestamp) { // oldSlopeDelta was <something> - userOldPoint.slope, so we cancel that oldSlopeDelta = oldSlopeDelta + userOldPoint.slope; if (_newLocked.end == _oldLocked.end) { oldSlopeDelta = oldSlopeDelta - userNewPoint.slope; // It was a new deposit, not extension } slopeChanges[_oldLocked.end] = oldSlopeDelta; } if (_newLocked.end > block.timestamp) { if (_newLocked.end > _oldLocked.end) { newSlopeDelta = newSlopeDelta - userNewPoint.slope; // old slope disappeared at this point slopeChanges[_newLocked.end] = newSlopeDelta; } // else: we recorded it already in oldSlopeDelta } } } /// @notice Public function to trigger global checkpoint function checkpoint() external { LockedBalance memory empty; _checkpoint(address(0), empty, empty); } // See IVotingEscrow for documentation function createLock(uint256 _value) external payable nonReentrant { require(!unlockOverride, "Unlock ovveride enabled"); uint256 unlock_time = _floorToWeek(block.timestamp + LOCKTIME); // Locktime is rounded down to weeks LockedBalance memory locked_ = locked[msg.sender]; // Validate inputs require(_value > 0, "Only non zero amount"); require(msg.value == _value, "Invalid value"); require(locked_.amount == 0, "Lock exists"); // Update lock and voting power (checkpoint) locked_.amount += int128(int256(_value)); locked_.end = unlock_time; locked_.delegated += int128(int256(_value)); locked_.delegatee = msg.sender; locked[msg.sender] = locked_; _checkpoint(msg.sender, LockedBalance(0, 0, 0, address(0)), locked_); emit Deposit(msg.sender, _value, unlock_time, LockAction.CREATE, block.timestamp); } // See IVotingEscrow for documentation // @dev A lock is active until both lock.amount==0 and lock.end<=block.timestamp function increaseAmount(uint256 _value) external payable nonReentrant { require(!unlockOverride, "Unlock ovveride enabled"); LockedBalance memory locked_ = locked[msg.sender]; // Validate inputs require(_value > 0, "Only non zero amount"); require(msg.value == _value, "Invalid value"); require(locked_.amount > 0, "No lock"); require(locked_.end > block.timestamp, "Lock expired"); // Update lock address delegatee = locked_.delegatee; uint256 unlockTime = locked_.end; LockAction action = LockAction.INCREASE_AMOUNT; LockedBalance memory newLocked = _copyLock(locked_); newLocked.amount += int128(int256(_value)); newLocked.end = _floorToWeek(block.timestamp + LOCKTIME); if (delegatee == msg.sender) { // Undelegated lock action = LockAction.INCREASE_AMOUNT_AND_DELEGATION; newLocked.delegated += int128(int256(_value)); locked[msg.sender] = newLocked; _checkpoint(msg.sender, locked_, newLocked); } else { // Delegated lock, update sender's lock first locked[msg.sender] = newLocked; _checkpoint(msg.sender, locked_, newLocked); // Then, update delegatee's lock and voting power (checkpoint) locked_ = locked[delegatee]; require(locked_.amount > 0, "Delegatee has no lock"); require(locked_.end > block.timestamp, "Delegatee lock expired"); newLocked = _copyLock(locked_); newLocked.delegated += int128(int256(_value)); locked[delegatee] = newLocked; _checkpoint(delegatee, locked_, newLocked); emit Deposit(delegatee, _value, newLocked.end, LockAction.DELEGATE, block.timestamp); } emit Deposit(msg.sender, _value, unlockTime, action, block.timestamp); } // See IVotingEscrow for documentation function withdraw() external nonReentrant { LockedBalance memory locked_ = locked[msg.sender]; // Validate inputs require(locked_.amount > 0, "No lock"); require(locked_.end <= block.timestamp || unlockOverride, "Lock not expired"); require(locked_.delegatee == msg.sender, "Lock delegated"); // Update lock uint256 amountToSend = uint256(uint128(locked_.amount)); LockedBalance memory newLocked = _copyLock(locked_); newLocked.amount = 0; newLocked.end = 0; newLocked.delegated -= int128(int256(amountToSend)); newLocked.delegatee = address(0); locked[msg.sender] = newLocked; newLocked.delegated = 0; // oldLocked can have either expired <= timestamp or zero end // currentLock has only 0 end // Both can have >= 0 amount _checkpoint(msg.sender, locked_, newLocked); // Send back deposited tokens (bool success, ) = msg.sender.call{value: amountToSend}(""); require(success, "Failed to send CANTO"); emit Withdraw(msg.sender, amountToSend, LockAction.WITHDRAW, block.timestamp); } // Creates a copy of a lock function _copyLock(LockedBalance memory _locked) internal pure returns (LockedBalance memory) { return LockedBalance({ amount: _locked.amount, end: _locked.end, delegatee: _locked.delegatee, delegated: _locked.delegated }); } // @dev Floors a timestamp to the nearest weekly increment // @param _t Timestamp to floor function _floorToWeek(uint256 _t) internal pure returns (uint256) { return (_t / WEEK) * WEEK; } // @dev Uses binarysearch to find the most recent point history preceeding block // @param _block Find the most recent point history before this block // @param _maxEpoch Do not search pointHistories past this index function _findBlockEpoch(uint256 _block, uint256 _maxEpoch) internal view returns (uint256) { // Binary search uint256 min = 0; uint256 max = _maxEpoch; // Will be always enough for 128-bit numbers for (uint256 i = 0; i < 128; i++) { if (min >= max) break; uint256 mid = (min + max + 1) / 2; if (pointHistory[mid].blk <= _block) { min = mid; } else { max = mid - 1; } } return min; } // @dev Uses binarysearch to find the most recent user point history preceeding block // @param _addr User for which to search // @param _block Find the most recent point history before this block function _findUserBlockEpoch(address _addr, uint256 _block) internal view returns (uint256) { uint256 min = 0; uint256 max = userPointEpoch[_addr]; for (uint256 i = 0; i < 128; i++) { if (min >= max) { break; } uint256 mid = (min + max + 1) / 2; if (userPointHistory[_addr][mid].blk <= _block) { min = mid; } else { max = mid - 1; } } return min; } /// ~~~~~~~~~~~~~~~~~~~~~~~~~~ /// /// GETTERS /// /// ~~~~~~~~~~~~~~~~~~~~~~~~~~ /// // See IVotingEscrow for documentation function balanceOf(address _owner) public view returns (uint256) { uint256 epoch = userPointEpoch[_owner]; if (epoch == 0) { return 0; } Point memory lastPoint = userPointHistory[_owner][epoch]; lastPoint.bias = lastPoint.bias - (lastPoint.slope * int128(int256(block.timestamp - lastPoint.ts))); if (lastPoint.bias < 0) { lastPoint.bias = 0; } return uint256(uint128(lastPoint.bias)); } // See IVotingEscrow for documentation function balanceOfAt(address _owner, uint256 _blockNumber) public view returns (uint256) { require(_blockNumber <= block.number, "Only past block number"); // Get most recent user Point to block uint256 userEpoch = _findUserBlockEpoch(_owner, _blockNumber); if (userEpoch == 0) { return 0; } Point memory upoint = userPointHistory[_owner][userEpoch]; // Get most recent global Point to block uint256 maxEpoch = globalEpoch; uint256 epoch = _findBlockEpoch(_blockNumber, maxEpoch); Point memory point0 = pointHistory[epoch]; // Calculate delta (block & time) between user Point and target block // Allowing us to calculate the average seconds per block between // the two points uint256 dBlock = 0; uint256 dTime = 0; if (epoch < maxEpoch) { Point memory point1 = pointHistory[epoch + 1]; dBlock = point1.blk - point0.blk; dTime = point1.ts - point0.ts; } else { dBlock = block.number - point0.blk; dTime = block.timestamp - point0.ts; } // (Deterministically) Estimate the time at which block _blockNumber was mined uint256 blockTime = point0.ts; if (dBlock != 0) { blockTime = blockTime + ((dTime * (_blockNumber - point0.blk)) / dBlock); } // Current Bias = most recent bias - (slope * time since update) upoint.bias = upoint.bias - (upoint.slope * int128(int256(blockTime - upoint.ts))); if (upoint.bias >= 0) { return uint256(uint128(upoint.bias)); } else { return 0; } } // See IVotingEscrow for documentation function totalSupply() public view returns (uint256) { uint256 epoch_ = globalEpoch; Point memory lastPoint = pointHistory[epoch_]; return _supplyAt(lastPoint, block.timestamp); } // See IVotingEscrow for documentation function totalSupplyAt(uint256 _blockNumber) public view returns (uint256) { require(_blockNumber <= block.number, "Only past block number"); uint256 epoch = globalEpoch; uint256 targetEpoch = _findBlockEpoch(_blockNumber, epoch); Point memory point = pointHistory[targetEpoch]; // If point.blk > _blockNumber that means we got the initial epoch & contract did not yet exist if (point.blk > _blockNumber) { return 0; } uint256 dTime = 0; if (targetEpoch < epoch) { Point memory pointNext = pointHistory[targetEpoch + 1]; if (point.blk != pointNext.blk) { dTime = ((_blockNumber - point.blk) * (pointNext.ts - point.ts)) / (pointNext.blk - point.blk); } } else if (point.blk != block.number) { dTime = ((_blockNumber - point.blk) * (block.timestamp - point.ts)) / (block.number - point.blk); } // Now dTime contains info on how far are we beyond point return _supplyAt(point, point.ts + dTime); } /// @notice Calculate total supply of voting power at a given time _t /// @param _point Most recent point before time _t /// @param _t Time at which to calculate supply /// @return totalSupply at given point in time function _supplyAt(Point memory _point, uint256 _t) internal view returns (uint256) { Point memory lastPoint = _point; // Floor the timestamp to weekly interval uint256 iterativeTime = _floorToWeek(lastPoint.ts); // Iterate through all weeks between _point & _t to account for slope changes for (uint256 i = 0; i < 255; i++) { iterativeTime = iterativeTime + WEEK; int128 dSlope = 0; // If week end is after timestamp, then truncate & leave dSlope to 0 if (iterativeTime > _t) { iterativeTime = _t; } // else get most recent slope change else { dSlope = slopeChanges[iterativeTime]; } lastPoint.bias = lastPoint.bias - (lastPoint.slope * int128(int256(iterativeTime - lastPoint.ts))); if (iterativeTime == _t) { break; } lastPoint.slope = lastPoint.slope + dSlope; lastPoint.ts = iterativeTime; } if (lastPoint.bias < 0) { lastPoint.bias = 0; } return uint256(uint128(lastPoint.bias)); } }
/src/LiquidityGauge.sol
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.16; import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import {ERC20Burnable} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; interface LendingLedger { function sync_ledger(address _lender, int256 _delta) external; } contract LiquidityGauge is ERC20, ERC20Burnable { using SafeERC20 for IERC20; address public lendingLedger; address public underlyingToken; constructor(address _underlyingToken, address _lendingLedger) ERC20( string.concat(ERC20(_underlyingToken).symbol(), " NeoFinance Gauge"), string.concat(ERC20(_underlyingToken).symbol(), "-gauge") ) { underlyingToken = _underlyingToken; lendingLedger = _lendingLedger; } /// @notice Function called by user to deposit underlying tokens /// @param _amount The amount of token to be deposited function depositUnderlying(uint256 _amount) external { address _user = msg.sender; IERC20(underlyingToken).safeTransferFrom(_user, address(this), _amount); _mint(_user, _amount); } /// @notice Function called by the user to withdraw underlying tokens /// @param _amount The amount of token to be withdrawn function withdrawUnderlying(uint256 _amount) external { address _user = msg.sender; _burn(_user, _amount); IERC20(underlyingToken).safeTransfer(_user, _amount); } function _afterTokenTransfer( address from, address to, uint256 amount ) internal virtual override { super._afterTokenTransfer(from, to, amount); if (from != address(0)) { LendingLedger(lendingLedger).sync_ledger(from, -int256(amount)); } if (to != address(0)) { LendingLedger(lendingLedger).sync_ledger(to, int256(amount)); } } }
/src/GaugeController.sol
// SPDX-License-Identifier: AGPL-3.0-or-later pragma solidity ^0.8.16; import {VotingEscrow} from "./VotingEscrow.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; /// @title GaugeController /// @author Curve Finance (MIT) - original concept and implementation in Vyper /// mkt.market - Porting to Solidity with some modifications (this version) /// @notice Allows users to vote on distribution of CANTO that the contract receives from governance. Modifications from Curve: /// - Different whitelisting of gauge addresses /// - Removal of gauges contract GaugeController { // Constants uint256 public constant WEEK = 7 days; uint256 public constant MULTIPLIER = 10**18; // Events event NewType(string mame, int128 type_id); event NewGauge(address indexed gauge_address, int128 gauge_type); event GaugeRemoved(address indexed gauge_address); // State VotingEscrow public votingEscrow; address public governance; int128 public n_gauge_types; mapping(int128 => string) public gauge_type_names; // we increment values by 1 prior to storing them here so we can rely on a value // of zero as meaning the gauge has not been set mapping(address => int128) public gauge_types_; mapping(address => bool) public is_removed; mapping(address => mapping(address => VotedSlope)) public vote_user_slopes; mapping(address => uint256) public vote_user_power; mapping(address => mapping(address => uint256)) public last_user_vote; mapping(address => mapping(uint256 => Point)) public points_weight; mapping(address => mapping(uint256 => uint256)) public changes_weight; mapping(address => uint256) time_weight; mapping(int128 => mapping(uint256 => Point)) points_sum; mapping(int128 => mapping(uint256 => uint256)) changes_sum; mapping(int128 => uint256) public time_sum; mapping(uint256 => uint256) points_total; uint256 time_total; mapping(int128 => mapping(uint256 => uint256)) points_type_weight; mapping(int128 => uint256) time_type_weight; struct Point { uint256 bias; uint256 slope; } struct VotedSlope { uint256 slope; uint256 power; uint256 end; } modifier onlyGovernance() { require(msg.sender == governance); _; } /// @notice Initializes state /// @param _votingEscrow The voting escrow address constructor(address _votingEscrow, address _governance) { votingEscrow = VotingEscrow(_votingEscrow); governance = _governance; uint256 last_epoch = (block.timestamp / WEEK) * WEEK; time_total = last_epoch; } /// @notice Set governance address /// @param _governance New governance address function setGovernance(address _governance) external onlyGovernance { governance = _governance; } // @notice Get gauge type for address // @param _addr Gauge address // @return Gauge type id function gauge_types(address _addr) external view returns (int128) { int128 gauge_type = gauge_types_[_addr]; require(gauge_type != 0, "Invalid gauge address"); return gauge_type - 1; } /// @notice Fill historic type weights week-over-week for missed checkins /// and return the type weight for the future week /// @param gauge_type Gauge type id /// @return Type weight function _get_type_weight(int128 gauge_type) internal returns (uint256) { uint256 t = time_type_weight[gauge_type]; if (t > 0) { uint256 w = points_type_weight[gauge_type][t]; for (uint256 i; i < 500; ++i) { if (t > block.timestamp) break; t += WEEK; points_type_weight[gauge_type][t] = w; if (t > block.timestamp) time_type_weight[gauge_type] = t; } return w; } else { return 0; } } /// @notice Fill historic gauge weights week-over-week for missed checkins and return the sum for the future week /// @return Sum of weights function _get_sum(int128 gauge_type) internal returns (uint256) { uint256 t = time_sum[gauge_type]; if (t > 0) { Point memory pt = points_sum[gauge_type][t]; for (uint256 i; i < 500; ++i) { if (t > block.timestamp) break; t += WEEK; uint256 d_bias = pt.slope * WEEK; if (pt.bias > d_bias) { pt.bias -= d_bias; uint256 d_slope = changes_sum[gauge_type][t]; pt.slope -= d_slope; } else { pt.bias = 0; pt.slope = 0; } points_sum[gauge_type][t] = pt; if (t > block.timestamp) time_sum[gauge_type] = t; } return pt.bias; } else { return 0; } } // @notice Fill historic total weights week-over-week for missed checkins // and return the total for the future week // @return Total weight function _get_total() internal returns (uint256) { uint256 t = time_total; int128 _n_gauge_types = n_gauge_types; if (t > block.timestamp) { // If we have already checkpointed - still need to change the value t -= WEEK; } uint256 pt = points_total[t]; for (int128 gauge_type; gauge_type < _n_gauge_types; ++gauge_type) { _get_sum(gauge_type); _get_type_weight(gauge_type); } for (uint256 i; i < 500; ++i) { if (t > block.timestamp) break; t += WEEK; pt = 0; for (int128 gauge_type; gauge_type < _n_gauge_types; ++gauge_type) { uint256 type_sum = points_sum[gauge_type][t].bias; uint256 type_weight = points_type_weight[gauge_type][t]; pt += type_sum * type_weight; } points_total[t] = pt; if (t > block.timestamp) { time_total = t; } } return pt; } /// @notice Fill historic gauge weights week-over-week for missed checkins /// and return the total for the future week /// @param _gauge_addr Address of the gauge /// @return Gauge weight function _get_weight(address _gauge_addr) private returns (uint256) { uint256 t = time_weight[_gauge_addr]; if (t > 0) { Point memory pt = points_weight[_gauge_addr][t]; for (uint256 i; i < 500; ++i) { if (t > block.timestamp) break; t += WEEK; uint256 d_bias = pt.slope * WEEK; if (pt.bias > d_bias) { pt.bias -= d_bias; uint256 d_slope = changes_weight[_gauge_addr][t]; pt.slope -= d_slope; } else { pt.bias = 0; pt.slope = 0; } points_weight[_gauge_addr][t] = pt; if (t > block.timestamp) time_weight[_gauge_addr] = t; } return pt.bias; } else { return 0; } } /// @notice Add a new gauge, only callable by governance /// @param addr The gauge address /// @param gauge_type The gauge type function add_gauge(address addr, int128 gauge_type) external onlyGovernance { require(gauge_type >= 0 && gauge_type < n_gauge_types, "Invalid gauge type"); require(gauge_types_[addr] == 0 || is_removed[addr], "Gauge already exists"); gauge_types_[addr] = gauge_type + 1; if (is_removed[addr]) is_removed[addr] = false; uint256 next_time = ((block.timestamp + WEEK) / WEEK) * WEEK; _change_gauge_weight(addr, 0); if (time_sum[gauge_type] == 0) time_sum[gauge_type] = next_time; time_weight[addr] = next_time; emit NewGauge(addr, gauge_type); } /// @notice Remove a gauge, only callable by governance /// @dev Sets the gauge weight to 0 /// @param _gauge The gauge address function remove_gauge(address _gauge) external onlyGovernance { require(gauge_types_[_gauge] != 0 && !is_removed[_gauge], "Invalid gauge address"); is_removed[_gauge] = true; _remove_gauge_weight(_gauge); emit GaugeRemoved(_gauge); } /// @notice Checkpoint to fill data common for all gauges function checkpoint() external { _get_total(); } /// @notice Checkpoint to fill data for both a specific gauge and common for all gauges /// @param _gauge The gauge address function checkpoint_gauge(address _gauge) external { _get_weight(_gauge); _get_total(); } /// @notice Get Gauge relative weight (not more than 1.0) normalized to 1e18 /// (e.g. 1.0 == 1e18). Inflation which will be received by it is /// inflation_rate * relative_weight / 1e18 /// @param _gauge Gauge address /// @param _time Relative weight at the specified timestamp in the past or present /// @return Value of relative weight normalized to 1e18 function _gauge_relative_weight(address _gauge, uint256 _time) private view returns (uint256) { uint256 t = (_time / WEEK) * WEEK; uint256 total_weight = points_total[t]; if (total_weight > 0) { int128 gauge_type = gauge_types_[_gauge] - 1; uint256 _type_weight = points_type_weight[gauge_type][t]; uint256 gauge_weight = points_weight[_gauge][t].bias; return (MULTIPLIER * _type_weight * gauge_weight) / total_weight; } else { return 0; } } /// @notice Get Gauge relative weight (not more than 1.0) normalized to 1e18 /// (e.g. 1.0 == 1e18). Inflation which will be received by it is /// inflation_rate * relative_weight / 1e18 /// @param _gauge Gauge address /// @param _time Relative weight at the specified timestamp in the past or present /// @return Value of relative weight normalized to 1e18 function gauge_relative_weight(address _gauge, uint256 _time) external view returns (uint256) { return _gauge_relative_weight(_gauge, _time); } /// @notice Get gauge weight normalized to 1e18 and also fill all the unfilled /// values for type and gauge records /// @dev Any address can call, however nothing is recorded if the values are filled already /// @param _gauge Gauge address /// @param _time Relative weight at the specified timestamp in the past or present /// @return Value of relative weight normalized to 1e18 function gauge_relative_weight_write(address _gauge, uint256 _time) external returns (uint256) { _get_weight(_gauge); _get_total(); return _gauge_relative_weight(_gauge, _time); } // @notice Change type weight // @param type_id Type id // @param weight New type weight function _change_type_weight(int128 type_id, uint256 weight) internal { uint256 old_weight = _get_type_weight(type_id); uint256 old_sum = _get_sum(type_id); uint256 _total_weight = _get_total(); uint256 next_time = ((block.timestamp + WEEK) / WEEK) * WEEK; _total_weight = _total_weight + old_sum * weight - old_sum * old_weight; points_total[next_time] = _total_weight; points_type_weight[type_id][next_time] = weight; time_total = next_time; time_type_weight[type_id] = next_time; } // @notice Add gauge type with name `_name` and weight `weight` // @param _name Name of gauge type // @param weight Weight of gauge type function add_type(string memory _name, uint256 _weight) external onlyGovernance { int128 type_id = n_gauge_types; gauge_type_names[type_id] = _name; n_gauge_types = type_id + 1; if (_weight != 0) { _change_type_weight(type_id, _weight); } emit NewType(_name, type_id); } // @notice Change gauge type `type_id` weight to `weight` // @param type_id Gauge type id // @param weight New Gauge weight function change_type_weight(int128 type_id, uint256 weight) external onlyGovernance { _change_type_weight(type_id, weight); } /// @notice Overwrite gauge weight /// @param addr Gauge address /// @param weight New weight function _change_gauge_weight(address addr, uint256 weight) internal { int128 gauge_type = gauge_types_[addr] - 1; uint256 old_gauge_weight = _get_weight(addr); uint256 type_weight = _get_type_weight(gauge_type); uint256 old_sum = _get_sum(gauge_type); uint256 _total_weight = _get_total(); uint256 next_time = ((block.timestamp + WEEK) / WEEK) * WEEK; points_weight[addr][next_time].bias = weight; time_weight[addr] = next_time; uint256 new_sum = old_sum + weight - old_gauge_weight; points_sum[gauge_type][next_time].bias = new_sum; time_sum[gauge_type] = next_time; _total_weight = _total_weight + new_sum * type_weight - old_sum * type_weight; points_total[next_time] = _total_weight; time_total = next_time; } function _remove_gauge_weight(address _gauge) internal { int128 gauge_type = gauge_types_[_gauge] - 1; uint256 next_time = ((block.timestamp + WEEK) / WEEK) * WEEK; uint256 old_weight_bias = _get_weight(_gauge); uint256 old_weight_slope = points_weight[_gauge][next_time].slope; uint256 old_sum_bias = _get_sum(gauge_type); points_weight[_gauge][next_time].bias = 0; points_weight[_gauge][next_time].slope = 0; uint256 new_sum = old_sum_bias - old_weight_bias; points_sum[gauge_type][next_time].bias = new_sum; points_sum[gauge_type][next_time].slope -= old_weight_slope; // We have to cancel all slope changes (gauge specific and global) that were caused by this gauge // This is not very efficient, but does the job for a governance function that is called very rarely for (uint256 i; i < 263; ++i) { uint256 time_to_check = next_time + i * WEEK; uint256 gauge_weight_change = changes_weight[_gauge][time_to_check]; if (gauge_weight_change > 0) { changes_weight[_gauge][time_to_check] = 0; changes_sum[gauge_type][time_to_check] -= gauge_weight_change; } } } /// @notice Allows governance to remove gauge weights /// @param _gauge Gauge address function remove_gauge_weight(address _gauge) public onlyGovernance { _remove_gauge_weight(_gauge); } /// @notice Allocate voting power for changing pool weights /// @param _gauge_addr Gauge which `msg.sender` votes for /// @param _user_weight Weight for a gauge in bps (units of 0.01%). Minimal is 0.01%. Ignored if 0 function vote_for_gauge_weights(address _gauge_addr, uint256 _user_weight) external { require(_user_weight >= 0 && _user_weight <= 10_000, "Invalid user weight"); require( _user_weight == 0 || (gauge_types_[_gauge_addr] != 0 && !is_removed[_gauge_addr]), "Can only vote 0 on non-gauges" ); // We allow withdrawing voting power from invalid (removed) gauges VotingEscrow ve = votingEscrow; ( , /*int128 bias*/ int128 slope_, /*uint256 ts*/ ) = ve.getLastUserPoint(msg.sender); require(slope_ >= 0, "Invalid slope"); uint256 slope = uint256(uint128(slope_)); uint256 lock_end = ve.lockEnd(msg.sender); uint256 next_time = ((block.timestamp + WEEK) / WEEK) * WEEK; require(lock_end > next_time, "Lock expires too soon"); int128 gauge_type = gauge_types_[_gauge_addr] - 1; require(gauge_type >= 0, "Gauge not added"); VotedSlope memory old_slope = vote_user_slopes[msg.sender][_gauge_addr]; uint256 old_dt = 0; if (old_slope.end > next_time) old_dt = old_slope.end - next_time; uint256 old_bias = old_slope.slope * old_dt; VotedSlope memory new_slope = VotedSlope({ slope: (slope * _user_weight) / 10_000, end: lock_end, power: _user_weight }); uint256 new_dt = lock_end - next_time; uint256 new_bias = new_slope.slope * new_dt; // Check and update powers (weights) used uint256 power_used = vote_user_power[msg.sender]; power_used = power_used + new_slope.power - old_slope.power; require(power_used >= 0 && power_used <= 10_000, "Used too much power"); vote_user_power[msg.sender] = power_used; // Remove old and schedule new slope changes // Remove slope changes for old slopes // Schedule recording of initial slope for next_time uint256 old_weight_bias = _get_weight(_gauge_addr); uint256 old_weight_slope = points_weight[_gauge_addr][next_time].slope; uint256 old_sum_bias = _get_sum(gauge_type); uint256 old_sum_slope = points_sum[gauge_type][next_time].slope; points_weight[_gauge_addr][next_time].bias = Math.max(old_weight_bias + new_bias, old_bias) - old_bias; points_sum[gauge_type][next_time].bias = Math.max(old_sum_bias + new_bias, old_bias) - old_bias; if (old_slope.end > next_time) { points_weight[_gauge_addr][next_time].slope = Math.max(old_weight_slope + new_slope.slope, old_slope.slope) - old_slope.slope; points_sum[gauge_type][next_time].slope = Math.max(old_sum_slope + new_slope.slope, old_slope.slope) - old_slope.slope; } else { points_weight[_gauge_addr][next_time].slope += new_slope.slope; points_sum[gauge_type][next_time].slope += new_slope.slope; } if (old_slope.end > block.timestamp) { // Cancel old slope changes if they still didn't happen // Because of manual slope changes when gauge is removed, an underflow is possible here and we have to check for that. if (changes_weight[_gauge_addr][old_slope.end] >= old_slope.slope) { changes_weight[_gauge_addr][old_slope.end] -= old_slope.slope; } else { changes_weight[_gauge_addr][old_slope.end] = 0; } if (changes_sum[gauge_type][old_slope.end] >= old_slope.slope) { changes_sum[gauge_type][old_slope.end] -= old_slope.slope; } else { changes_sum[gauge_type][old_slope.end] = 0; } } // Add slope changes for new slopes changes_weight[_gauge_addr][new_slope.end] += new_slope.slope; changes_sum[gauge_type][new_slope.end] += new_slope.slope; _get_total(); vote_user_slopes[msg.sender][_gauge_addr] = new_slope; // Record last action time last_user_vote[msg.sender][_gauge_addr] = block.timestamp; } /// @notice Get current gauge weight /// @param _gauge Gauge address /// @return Gauge weight function get_gauge_weight(address _gauge) external view returns (uint256) { return points_weight[_gauge][time_weight[_gauge]].bias; } // @notice Get current type weight // @param type_id Type id // @return Type weight function get_type_weight(int128 type_id) external view returns (uint256) { return points_type_weight[type_id][time_type_weight[type_id]]; } /// @notice Get total weight /// @return Total weight function get_total_weight() external view returns (uint256) { return points_total[time_total]; } // @notice Get sum of gauge weights per type // @param type_id Type id // @return Sum of gauge weights function get_weights_sum_per_type(int128 type_id) external view returns (uint256) { return points_sum[type_id][time_sum[type_id]].bias; } }
/lib/openzeppelin-contracts/contracts/utils/math/Math.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 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 256, 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 << 3) < value ? 1 : 0); } } }
/lib/openzeppelin-contracts/contracts/utils/Context.sol
// 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; } }
/lib/openzeppelin-contracts/contracts/utils/Address.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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 * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [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://consensys.net/diligence/blog/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.8.0/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); } } }
/lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/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; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ 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)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ 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"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Compatible with tokens that require the approval to be set to * 0 before setting it to a non-zero value. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ 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"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation 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). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // 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 cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/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); }
/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Metadata.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
/lib/openzeppelin-contracts/contracts/token/ERC20/extensions/ERC20Burnable.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol) pragma solidity ^0.8.0; import "../ERC20.sol"; import "../../../utils/Context.sol"; /** * @dev Extension of {ERC20} that allows token holders to destroy both their own * tokens and those that they have an allowance for, in a way that can be * recognized off-chain (via event analysis). */ abstract contract ERC20Burnable is Context, ERC20 { /** * @dev Destroys `amount` tokens from the caller. * * See {ERC20-_burn}. */ function burn(uint256 amount) public virtual { _burn(_msgSender(), amount); } /** * @dev Destroys `amount` tokens from `account`, deducting from the caller's * allowance. * * See {ERC20-_burn} and {ERC20-allowance}. * * Requirements: * * - the caller must have allowance for ``accounts``'s tokens of at least * `amount`. */ function burnFrom(address account, uint256 amount) public virtual { _spendAllowance(account, _msgSender(), amount); _burn(account, amount); } }
/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.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); }
/lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.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]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * 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 ERC20 is Context, IERC20, IERC20Metadata { 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}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _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 default value returned by this function, unless * it's 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 {} }
/lib/openzeppelin-contracts/contracts/security/ReentrancyGuard.sol
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } }
Compiler Settings
{"viaIR":true,"remappings":[":@openzeppelin/=lib/openzeppelin-contracts/",":ds-test/=lib/ds-test/src/",":erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",":forge-std/=lib/forge-std/src/",":openzeppelin-contracts/=lib/openzeppelin-contracts/",":openzeppelin/=lib/openzeppelin-contracts/contracts/"],"optimizer":{"runs":1000,"enabled":true},"metadata":{"bytecodeHash":"ipfs"},"libraries":{},"evmVersion":"london","compilationTarget":{"src/LendingLedger.sol":"LendingLedger"}}
Contract ABI
[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_gaugeController","internalType":"address"},{"type":"address","name":"_governance","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"BLOCK_EPOCH","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"WCANTO","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"averageBlockTime","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract BaseV1Factory"}],"name":"baseV1Factory","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"cantoPerBlock","inputs":[{"type":"uint256","name":"","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"claim","inputs":[{"type":"address","name":"_market","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract GaugeController"}],"name":"gaugeController","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"governance","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"lendingMarketTotalBalance","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"bool","name":"","internalType":"bool"}],"name":"lendingMarketWhitelist","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"address"}],"name":"liquidityGauges","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint128","name":"accCantoPerShare","internalType":"uint128"},{"type":"uint128","name":"secRewardsPerShare","internalType":"uint128"},{"type":"uint64","name":"lastRewardBlock","internalType":"uint64"}],"name":"marketInfo","inputs":[{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"referenceBlockNumber","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"","internalType":"uint256"}],"name":"referenceBlockTime","inputs":[]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setBlockTimeParameters","inputs":[{"type":"uint256","name":"_averageBlockTime","internalType":"uint256"},{"type":"uint256","name":"_referenceBlockTime","internalType":"uint256"},{"type":"uint256","name":"_referenceBlockNumber","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setGovernance","inputs":[{"type":"address","name":"_governance","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"setRewards","inputs":[{"type":"uint256","name":"_fromEpoch","internalType":"uint256"},{"type":"uint256","name":"_toEpoch","internalType":"uint256"},{"type":"uint256","name":"_amountPerBlock","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"sync_ledger","inputs":[{"type":"address","name":"_lender","internalType":"address"},{"type":"int256","name":"_delta","internalType":"int256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"update_market","inputs":[{"type":"address","name":"_market","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"amount","internalType":"uint256"},{"type":"int256","name":"rewardDebt","internalType":"int256"},{"type":"int256","name":"secRewardDebt","internalType":"int256"}],"name":"userInfo","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"whiteListLendingMarket","inputs":[{"type":"address","name":"_market","internalType":"address"},{"type":"bool","name":"_isWhiteListed","internalType":"bool"},{"type":"bool","name":"_isLP","internalType":"bool"}]},{"type":"receive","stateMutability":"payable"}]
Contract Creation Code
0x60803461009b57601f61284938819003918201601f19168301916001600160401b038311848410176100a057808492604094855283398101031261009b57610052602061004b836100b6565b92016100b6565b611644600055600480546001600160a01b039384166001600160a01b03199182161790915560038054929093169116179055436001554260025560405161277e90816100cb8239f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b038216820361009b5756fe608060409080825260048036101562000024575b50505036156200002257600080fd5b005b600091823560e01c9081630a14c7461462000bdd575080630f208beb1462000b71578063122d914014620009765780631e0f63db146200094b5780631e83409a146200080d578063226a850614620007dc578063233dedf114620007bd5780633ef7fd3d1462000782578063485fd835146200075157806351f4a33714620007265780635aa6e67514620006fc5780637c00da3014620003d757806399eecb3b14620003ad5780639fa2d0fd146200036f578063ab033ea9146200031a578063ba27807514620002e2578063c43513861462000276578063c974905a1462000255578063d3cb0e631462000235578063eec9e65114620001f55763f36d1e4e03620000135734620001f1576200013a3662000c17565b90946001600160a01b03600354163303620001ed57620186a0928381061580620001e2575b156200019f575b8681111562000173578580f35b80865260086020528282872055838101809111156200016657602486601187634e487b7160e01b835252fd5b606485602084519162461bcd60e51b8352820152601460248201527f496e76616c696420626c6f636b206e756d6265720000000000000000000000006044820152fd5b50838706156200015f565b8480fd5b5080fd5b828434620001f1576020366003190112620001f15760ff816020936001600160a01b036200022262000bfb565b1681526005855220541690519015158152f35b828434620001f15781600319360112620001f15760209051620186a08152f35b828434620001f15781600319360112620001f1576020906002549051908152f35b828434620001f1576020366003190112620001f157806060926001600160a01b03620002a162000bfb565b1681526007602052209067ffffffffffffffff6001835493015416908051926fffffffffffffffffffffffffffffffff8116845260801c6020840152820152f35b82346200031757620002f43662000c17565b916001600160a01b036003541633036200031357835560025560015580f35b8380fd5b80fd5b82346200031757602036600319011262000317576200033862000bfb565b600354906001600160a01b038083163303620003135773ffffffffffffffffffffffffffffffffffffffff19911691161760035580f35b828434620001f1576020366003190112620001f157602091816001600160a01b0391826200039c62000bfb565b168152600a85522054169051908152f35b509190346200031757806003193601126200031757506001600160a01b0360209254169051908152f35b508234620006f8576060366003190112620006f857620003f662000bfb565b916024359081151590818303620006f457604435948515158603620006f0576001600160a01b039081600354163303620006ec57811691828852602096600588528460ff888b205416151514620006aa578062000695575b6200049f575b50508552600584528285209060ff8019835416911617905562000475578280f35b60076001925282200167ffffffffffffffff431667ffffffffffffffff1982541617905581808280f35b8551907f6801cc3000000000000000000000000000000000000000000000000000000000825283818301528873826551890dc65655a0aceca109ab11abdbd7a07b80602485015281604485015273e387067f12561e579c5f7d4294f51867e0c1cfba908a85606481855afa9485156200068b57908b9291849662000667575b5086861615620005c9575b505050508651916116e08084019284841067ffffffffffffffff851117620005b65750918484928a94620010698539168152308a82015203019088f08015620005ac5716818752600a86528487209073ffffffffffffffffffffffffffffffffffffffff1991818382541617905587528185882091825416179055868062000454565b85513d89823e3d90fd5b8b6041602492634e487b7160e01b835252fd5b60649192939495508a5194859384927f82dfdce40000000000000000000000000000000000000000000000000000000084528a8985015260248401528160448401525af19081156200065d57899162000629575b509088888b8062000529565b6200064e9150883d8a1162000655575b62000645818362000c99565b81019062001047565b896200061d565b503d62000639565b87513d8b823e3d90fd5b62000683919650833d8511620006555762000645818362000c99565b948d6200051e565b8a513d85823e3d90fd5b50828852600a8752818689205416156200044e565b6064828989519162461bcd60e51b8352820152600960248201527f4e6f206368616e676500000000000000000000000000000000000000000000006044820152fd5b8780fd5b8680fd5b8580fd5b8280fd5b828434620001f15781600319360112620001f1576020906001600160a01b03600354169051908152f35b50829034620006f8576020366003190112620006f85760209282913581526008845220549051908152f35b828434620001f15781600319360112620001f1576020905173e387067f12561e579c5f7d4294f51867e0c1cfba8152f35b828434620001f1576020366003190112620001f157806020926001600160a01b03620007ad62000bfb565b1681526009845220549051908152f35b828434620001f15781600319360112620001f157602091549051908152f35b828434620001f15781600319360112620001f1576020905173826551890dc65655a0aceca109ab11abdbd7a07b8152f35b508234620006f8576020908160031936011262000313576001600160a01b036200083662000bfb565b620008418162000cf7565b16845260078252838181206006845282822033835284526001670de0b6b3a764000062000888858520936fffffffffffffffffffffffffffffffff85549154169062000c53565b049101906200089982548262001010565b9155818113620008a7575080f35b81808092335af13d1562000945573d67ffffffffffffffff81116200093257825190620008de601f8201601f191686018362000c99565b815285843d92013e5b15620008f1578380f35b60649350519162461bcd60e51b8352820152601460248201527f4661696c656420746f2073656e642043414e544f0000000000000000000000006044820152fd5b602486604187634e487b7160e01b835252fd5b620008e7565b8234620003175760203660031901126200031757620009736200096d62000bfb565b62000cf7565b80f35b508234620006f85780600319360112620006f8576200099462000bfb565b6024359062000a733392838752602093600a85526001600160a01b03809181888b20541662000b64575b620009c98162000cf7565b168089526007865286892060068752878a209590921689529385528588209088831262000acb57600262000a5f9162000a0485855462000c7d565b845562000a53670de0b6b3a7640000918262000a346fffffffffffffffffffffffffffffffff8354168962000c53565b0462000a46600188019182546200102a565b90555460801c8662000c53565b0492019182546200102a565b90555b82875260098452848720546200102a565b9385851262000a89575084526009905282205580f35b6064908385519162461bcd60e51b8352820152601860248201527f4d61726b65742062616c616e636520756e646572666c6f7700000000000000006044820152fd5b600262000b2562000b5062000b5c9362000af162000ae98862000fe2565b875462000c8b565b865562000afe8762000fe2565b670de0b6b3a76400009384916fffffffffffffffffffffffffffffffff8454169062000c53565b0462000b376001880191825462001010565b905562000b448762000fe2565b905460801c9062000c53565b04920191825462001010565b905562000a62565b5080878a205416620009be565b828434620001f15780600319360112620001f15762000b8f62000bfb565b602435906001600160a01b0390818316809303620001ed5791606094918493168252600660205282822090825260205220805491600260018301549201549181519384526020840152820152f35b839034620001f15781600319360112620001f1576020906001548152f35b600435906001600160a01b038216820362000c1257565b600080fd5b606090600319011262000c1257600435906024359060443590565b811562000c3d570490565b634e487b7160e01b600052601260045260246000fd5b8181029291811591840414171562000c6757565b634e487b7160e01b600052601160045260246000fd5b9190820180921162000c6757565b9190820391821162000c6757565b90601f8019910116810190811067ffffffffffffffff82111762000cbc57604052565b634e487b7160e01b600052604160045260246000fd5b9190916fffffffffffffffffffffffffffffffff8080941691160191821162000c6757565b6001600160a01b038091166000908082526020916005835260409160ff83832054161562000f9f57808252600784528282206001928382019667ffffffffffffffff968789541680431162000d53575b50505050505050505050565b6009825287842054908162000d8e575b505050505050505050431667ffffffffffffffff198254161790553880808080808080808062000d47565b4381101562000d6357620186a090818104918083029280840482149015171562000f8b57820180831162000f8b57814382101562000f7e5762000dd19162000c8b565b9162000dff8a6103e862000df762000dee60025493544362000c8b565b8b549062000c53565b049062000c7d565b9087526008855288878c8762000e19828420548862000c53565b9160446004958c875416925195869384927f6472eee1000000000000000000000000000000000000000000000000000000008452898401526024998a8401525af191821562000f74578a9262000f3b575b5062000e948762000e8e6fffffffffffffffffffffffffffffffff94859462000c53565b62000c32565b16908062000ea78c549382851662000cd2565b16936ec097ce7bc90715b34b9f10000000009384880294888604148815171562000f2a5750509062000f1862000f24969594939262000f08897fffffffffffffffffffffffffffffffff000000000000000000000000000000009562000c32565b1684846080941617831c62000cd2565b901b1617885562000c7d565b62000d8e565b60118c91634e487b7160e01b835252fd5b9091508781813d831162000f6c575b62000f56818362000c99565b8101031262000f685751903862000e6a565b8980fd5b503d62000f4a565b8e513d8c823e3d90fd5b62000dd191504362000c8b565b602487634e487b7160e01b81526011600452fd5b60648484519062461bcd60e51b82526004820152601660248201527f4d61726b6574206e6f742077686974656c6973746564000000000000000000006044820152fd5b7f8000000000000000000000000000000000000000000000000000000000000000811462000c675760000390565b8181039291600013801582851316918412161762000c6757565b9190916000838201938412911290801582169115161762000c6757565b9081602091031262000c1257516001600160a01b038116810362000c12579056fe60806040908082523462000444578181620016e0803803809162000024828562000449565b83398101031262000444576200003a8162000483565b62000049602080930162000483565b83516395d89b4160e01b8082526001600160a01b0393841694600492909190600090818186818b5afa9081156200041c57829162000426575b50620000cb60318a5183620000a182955180928a808601910162000498565b810170204e656f46696e616e636520476175676560781b8882015203601181018452018262000449565b8851928352818386818b5afa9283156200041c578293620003f3575b506200012560268a51856200010682975180928a808601910162000498565b8101652d676175676560d01b8882015203600681018652018462000449565b80516001600160401b0393848211620003e0576003938454926001948585811c95168015620003d5575b89861014620003c2578190601f958681116200036f575b5089908683116001146200030c57849262000300575b505060001982881b1c191690851b1785555b8151958611620002ed5787548481811c91168015620002e2575b88821014620002cf5783811162000287575b50869286116001146200021b5794955085929190836200020f575b50501b92600019911b1c19161790555b60018060a01b03199283600654161760065516906005541617600555516111ae9081620005328239f35b015193503880620001d5565b87815286812093969394938691601f198316915b898383106200026c575050501062000251575b50505050811b019055620001e5565b01519060f884600019921b161c191690553880808062000242565b8587015189559097019694850194889350908101906200022f565b8882528782208480890160051c8201928a8a10620002c5575b0160051c019085905b828110620002b9575050620001ba565b838155018590620002a9565b92508192620002a0565b634e487b7160e01b825260228952602482fd5b90607f1690620001a8565b634e487b7160e01b815260418852602490fd5b0151905038806200017c565b8885528a85208894509190601f198416865b8d8282106200035857505084116200033f575b505050811b0185556200018e565b0151600019838a1b60f8161c1916905538808062000331565b8385015186558b979095019493840193016200031e565b9091508784528984208680850160051c8201928c8610620003b8575b918991869594930160051c01915b828110620003a957505062000166565b86815585945089910162000399565b925081926200038b565b634e487b7160e01b835260228a52602483fd5b94607f16946200014f565b634e487b7160e01b845260418752602484fd5b620004149193503d8084833e6200040b818362000449565b810190620004bd565b9138620000e7565b89513d84823e3d90fd5b6200043d91503d8084833e6200040b818362000449565b3862000082565b600080fd5b601f909101601f19168101906001600160401b038211908210176200046d57604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036200044457565b60005b838110620004ac5750506000910152565b81810151838201526020016200049b565b602081830312620004445780516001600160401b03918282116200044457019082601f83011215620004445781519081116200046d57604051926200050d601f8301601f19166020018562000449565b8184526020828401011162000444576200052e916020808501910162000498565b9056fe608060408181526004918236101561001657600080fd5b600092833560e01c91826306fdde031461071057508163095ea7b3146106e65781631071a2901461064a57816318160ddd1461062b57816323b872dd146105ee5781632495a599146105c6578163254de2661461059e578163313ce56714610582578163395093511461053357816342966c681461051557816370a08231146104df57816379cc6790146104af57816395d89b41146103ac578163a457c2d7146102eb578163a9059cbb146102ba578163b9f5be411461012b575063dd62ed3e146100e057600080fd5b34610127578060031936011261012757806020926100fc61084e565b610104610869565b6001600160a01b0391821683526001865283832091168252845220549051908152f35b5080fd5b919050346102605760203660031901126102605781356001600160a01b0380600654168351907f23b872dd0000000000000000000000000000000000000000000000000000000060208301523360248301523060448301528360648301526064825260a0820182811067ffffffffffffffff8211176102a75785526101b09190610f80565b3315610264579084916101c5826002546108cb565b600255338352826020528383208281540190558351828152837fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203393a360055416803b156102605783516248b64560e61b8152339581019586526020860192909252909384919082908490829060400103925af1908115610257575061024b575080f35b6102549061087f565b80f35b513d84823e3d90fd5b8280fd5b606484602085519162461bcd60e51b8352820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b602488604189634e487b7160e01b835252fd5b5050346101275780600319360112610127576020906102e46102da61084e565b60243590336108ee565b5160018152f35b905082346103a957826003193601126103a95761030661084e565b91836024359233815260016020528181206001600160a01b0386168252602052205490828210610340576020856102e48585038733610b8d565b608490602086519162461bcd60e51b8352820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152fd5b80fd5b838334610127578160031936011261012757805191809380549160019083821c928285169485156104a5575b60209586861081146104925785895290811561046e5750600114610416575b6104128787610408828c03836108a9565b5191829182610805565b0390f35b81529295507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b82841061045b575050508261041294610408928201019486806103f7565b805486850188015292860192810161043d565b60ff19168887015250505050151560051b83010192506104088261041286806103f7565b602484602285634e487b7160e01b835252fd5b93607f16936103d8565b505034610127573660031901126103a9576102546104cb61084e565b602435906104da823383610cc1565b610d59565b50503461012757602036600319011261012757806020926001600160a01b0361050661084e565b16815280845220549051908152f35b83903461012757602036600319011261012757610254903533610d59565b5050346101275780600319360112610127576102e460209261057b61055661084e565b91338152600186528481206001600160a01b03841682528652846024359120546108cb565b9033610b8d565b5050346101275781600319360112610127576020905160128152f35b5050346101275781600319360112610127576020906001600160a01b03600554169051908152f35b5050346101275781600319360112610127576020906001600160a01b03600654169051908152f35b505034610127576060366003190112610127576020906102e461060f61084e565b610617610869565b60443591610626833383610cc1565b6108ee565b5050346101275781600319360112610127576020906002549051908152f35b8383346101275760203660031901126101275782359061066a8233610d59565b6001600160a01b0360065416908051927fa9059cbb000000000000000000000000000000000000000000000000000000006020850152336024850152604484015260448352608083019083821067ffffffffffffffff8311176106d35761025494955052610f80565b602485604188634e487b7160e01b835252fd5b5050346101275780600319360112610127576020906102e461070661084e565b6024359033610b8d565b92915034610801578360031936011261080157600354600181811c91869082811680156107f7575b60209586861082146107e457508488529081156107c25750600114610769575b6104128686610408828b03836108a9565b929550600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b8284106107af575050508261041294610408928201019438610758565b8054868501880152928601928101610792565b60ff191687860152505050151560051b83010192506104088261041238610758565b836022602492634e487b7160e01b835252fd5b93607f1693610738565b8380fd5b6020808252825181830181905290939260005b82811061083a57505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501610818565b600435906001600160a01b038216820361086457565b600080fd5b602435906001600160a01b038216820361086457565b67ffffffffffffffff811161089357604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761089357604052565b919082018092116108d857565b634e487b7160e01b600052601160045260246000fd5b916001600160a01b0392838116928315610b2357848116948515610ab9576000958587528660205260409586882054868110610a50578690828a528960205203878920558188528688208681540190557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851888152a3858160055416600160ff1b8614610a3c57803b156101275786516248b64560e61b8082526001600160a01b0396909616600482015286830360248201529082908290604490829084905af18015610a3257610a1e575b50506005541691823b15610a1a5784519081526001600160a01b03919091166004820152602481019290925290919083908390604490829084905af19081156102575750610a09575050565b610a13829161087f565b6103a95750565b8580fd5b610a2a9192975061087f565b9438806109bd565b87513d84823e3d90fd5b602482634e487b7160e01b81526011600452fd5b6084885162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152fd5b6001600160a01b03809116918215610c585716918215610bee5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b608460405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152fd5b906001600160a01b0380831660005260016020526040600020908216600052602052604060002054926000198403610cfa575b50505050565b808410610d1557610d0c930391610b8d565b38808080610cf4565b606460405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b6001600160a01b0391828216918215610f16576000938385528460205260409384862054848110610ead579084879282845283602052038683205584600254036002557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208751878152a3848160055416600160ff1b8514610a3c57803b156101275785516248b64560e61b8082526001600160a01b0395909516600482015285830360248201529082908290604490829084905af18015610ea357610e8f575b505084610e2a575b5050505050565b6005541690813b15610e8b5783519081526001600160a01b0385166004820152602481019290925290919083908390604490829084905af19081156102575750610e77575b808080610e23565b610e81829161087f565b6103a95780610e6f565b8480fd5b610e9b9192965061087f565b933880610e1b565b86513d84823e3d90fd5b6084865162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152fd5b6001600160a01b0316906040516040810167ffffffffffffffff9082811082821117610893576040526020938483527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564858401526000808587829751910182855af1903d156110d9573d9283116110c5579061101c9392916040519261100f88601f19601f84011601856108a9565b83523d868885013e6110e4565b8051918215918483156110a1575b5050509050156110375750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b919381809450010312610127578201519081151582036103a957508038808461102a565b602485634e487b7160e01b81526041600452fd5b9061101c9392506060915b9192901561114557508151156110f8575090565b3b156111015790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156111585750805190602001fd5b6111749060405191829162461bcd60e51b835260048301610805565b0390fdfea264697066735822122057064ce255ed206b51dbe1f12443ee5c91317ede7900d50f8f8b7e57cff3ace964736f6c63430008110033a2646970667358221220238b8b4957e6f4ed7c309ebfebfeddfb0ee83529a69312b7ce92f75921937e3764736f6c63430008110033000000000000000000000000cf91278964fd4ba0f51d50bae608793b21981a12000000000000000000000000e32882187e4d38aeabc2b443d05aeee3c5050b51
Deployed ByteCode
0x608060409080825260048036101562000024575b50505036156200002257600080fd5b005b600091823560e01c9081630a14c7461462000bdd575080630f208beb1462000b71578063122d914014620009765780631e0f63db146200094b5780631e83409a146200080d578063226a850614620007dc578063233dedf114620007bd5780633ef7fd3d1462000782578063485fd835146200075157806351f4a33714620007265780635aa6e67514620006fc5780637c00da3014620003d757806399eecb3b14620003ad5780639fa2d0fd146200036f578063ab033ea9146200031a578063ba27807514620002e2578063c43513861462000276578063c974905a1462000255578063d3cb0e631462000235578063eec9e65114620001f55763f36d1e4e03620000135734620001f1576200013a3662000c17565b90946001600160a01b03600354163303620001ed57620186a0928381061580620001e2575b156200019f575b8681111562000173578580f35b80865260086020528282872055838101809111156200016657602486601187634e487b7160e01b835252fd5b606485602084519162461bcd60e51b8352820152601460248201527f496e76616c696420626c6f636b206e756d6265720000000000000000000000006044820152fd5b50838706156200015f565b8480fd5b5080fd5b828434620001f1576020366003190112620001f15760ff816020936001600160a01b036200022262000bfb565b1681526005855220541690519015158152f35b828434620001f15781600319360112620001f15760209051620186a08152f35b828434620001f15781600319360112620001f1576020906002549051908152f35b828434620001f1576020366003190112620001f157806060926001600160a01b03620002a162000bfb565b1681526007602052209067ffffffffffffffff6001835493015416908051926fffffffffffffffffffffffffffffffff8116845260801c6020840152820152f35b82346200031757620002f43662000c17565b916001600160a01b036003541633036200031357835560025560015580f35b8380fd5b80fd5b82346200031757602036600319011262000317576200033862000bfb565b600354906001600160a01b038083163303620003135773ffffffffffffffffffffffffffffffffffffffff19911691161760035580f35b828434620001f1576020366003190112620001f157602091816001600160a01b0391826200039c62000bfb565b168152600a85522054169051908152f35b509190346200031757806003193601126200031757506001600160a01b0360209254169051908152f35b508234620006f8576060366003190112620006f857620003f662000bfb565b916024359081151590818303620006f457604435948515158603620006f0576001600160a01b039081600354163303620006ec57811691828852602096600588528460ff888b205416151514620006aa578062000695575b6200049f575b50508552600584528285209060ff8019835416911617905562000475578280f35b60076001925282200167ffffffffffffffff431667ffffffffffffffff1982541617905581808280f35b8551907f6801cc3000000000000000000000000000000000000000000000000000000000825283818301528873826551890dc65655a0aceca109ab11abdbd7a07b80602485015281604485015273e387067f12561e579c5f7d4294f51867e0c1cfba908a85606481855afa9485156200068b57908b9291849662000667575b5086861615620005c9575b505050508651916116e08084019284841067ffffffffffffffff851117620005b65750918484928a94620010698539168152308a82015203019088f08015620005ac5716818752600a86528487209073ffffffffffffffffffffffffffffffffffffffff1991818382541617905587528185882091825416179055868062000454565b85513d89823e3d90fd5b8b6041602492634e487b7160e01b835252fd5b60649192939495508a5194859384927f82dfdce40000000000000000000000000000000000000000000000000000000084528a8985015260248401528160448401525af19081156200065d57899162000629575b509088888b8062000529565b6200064e9150883d8a1162000655575b62000645818362000c99565b81019062001047565b896200061d565b503d62000639565b87513d8b823e3d90fd5b62000683919650833d8511620006555762000645818362000c99565b948d6200051e565b8a513d85823e3d90fd5b50828852600a8752818689205416156200044e565b6064828989519162461bcd60e51b8352820152600960248201527f4e6f206368616e676500000000000000000000000000000000000000000000006044820152fd5b8780fd5b8680fd5b8580fd5b8280fd5b828434620001f15781600319360112620001f1576020906001600160a01b03600354169051908152f35b50829034620006f8576020366003190112620006f85760209282913581526008845220549051908152f35b828434620001f15781600319360112620001f1576020905173e387067f12561e579c5f7d4294f51867e0c1cfba8152f35b828434620001f1576020366003190112620001f157806020926001600160a01b03620007ad62000bfb565b1681526009845220549051908152f35b828434620001f15781600319360112620001f157602091549051908152f35b828434620001f15781600319360112620001f1576020905173826551890dc65655a0aceca109ab11abdbd7a07b8152f35b508234620006f8576020908160031936011262000313576001600160a01b036200083662000bfb565b620008418162000cf7565b16845260078252838181206006845282822033835284526001670de0b6b3a764000062000888858520936fffffffffffffffffffffffffffffffff85549154169062000c53565b049101906200089982548262001010565b9155818113620008a7575080f35b81808092335af13d1562000945573d67ffffffffffffffff81116200093257825190620008de601f8201601f191686018362000c99565b815285843d92013e5b15620008f1578380f35b60649350519162461bcd60e51b8352820152601460248201527f4661696c656420746f2073656e642043414e544f0000000000000000000000006044820152fd5b602486604187634e487b7160e01b835252fd5b620008e7565b8234620003175760203660031901126200031757620009736200096d62000bfb565b62000cf7565b80f35b508234620006f85780600319360112620006f8576200099462000bfb565b6024359062000a733392838752602093600a85526001600160a01b03809181888b20541662000b64575b620009c98162000cf7565b168089526007865286892060068752878a209590921689529385528588209088831262000acb57600262000a5f9162000a0485855462000c7d565b845562000a53670de0b6b3a7640000918262000a346fffffffffffffffffffffffffffffffff8354168962000c53565b0462000a46600188019182546200102a565b90555460801c8662000c53565b0492019182546200102a565b90555b82875260098452848720546200102a565b9385851262000a89575084526009905282205580f35b6064908385519162461bcd60e51b8352820152601860248201527f4d61726b65742062616c616e636520756e646572666c6f7700000000000000006044820152fd5b600262000b2562000b5062000b5c9362000af162000ae98862000fe2565b875462000c8b565b865562000afe8762000fe2565b670de0b6b3a76400009384916fffffffffffffffffffffffffffffffff8454169062000c53565b0462000b376001880191825462001010565b905562000b448762000fe2565b905460801c9062000c53565b04920191825462001010565b905562000a62565b5080878a205416620009be565b828434620001f15780600319360112620001f15762000b8f62000bfb565b602435906001600160a01b0390818316809303620001ed5791606094918493168252600660205282822090825260205220805491600260018301549201549181519384526020840152820152f35b839034620001f15781600319360112620001f1576020906001548152f35b600435906001600160a01b038216820362000c1257565b600080fd5b606090600319011262000c1257600435906024359060443590565b811562000c3d570490565b634e487b7160e01b600052601260045260246000fd5b8181029291811591840414171562000c6757565b634e487b7160e01b600052601160045260246000fd5b9190820180921162000c6757565b9190820391821162000c6757565b90601f8019910116810190811067ffffffffffffffff82111762000cbc57604052565b634e487b7160e01b600052604160045260246000fd5b9190916fffffffffffffffffffffffffffffffff8080941691160191821162000c6757565b6001600160a01b038091166000908082526020916005835260409160ff83832054161562000f9f57808252600784528282206001928382019667ffffffffffffffff968789541680431162000d53575b50505050505050505050565b6009825287842054908162000d8e575b505050505050505050431667ffffffffffffffff198254161790553880808080808080808062000d47565b4381101562000d6357620186a090818104918083029280840482149015171562000f8b57820180831162000f8b57814382101562000f7e5762000dd19162000c8b565b9162000dff8a6103e862000df762000dee60025493544362000c8b565b8b549062000c53565b049062000c7d565b9087526008855288878c8762000e19828420548862000c53565b9160446004958c875416925195869384927f6472eee1000000000000000000000000000000000000000000000000000000008452898401526024998a8401525af191821562000f74578a9262000f3b575b5062000e948762000e8e6fffffffffffffffffffffffffffffffff94859462000c53565b62000c32565b16908062000ea78c549382851662000cd2565b16936ec097ce7bc90715b34b9f10000000009384880294888604148815171562000f2a5750509062000f1862000f24969594939262000f08897fffffffffffffffffffffffffffffffff000000000000000000000000000000009562000c32565b1684846080941617831c62000cd2565b901b1617885562000c7d565b62000d8e565b60118c91634e487b7160e01b835252fd5b9091508781813d831162000f6c575b62000f56818362000c99565b8101031262000f685751903862000e6a565b8980fd5b503d62000f4a565b8e513d8c823e3d90fd5b62000dd191504362000c8b565b602487634e487b7160e01b81526011600452fd5b60648484519062461bcd60e51b82526004820152601660248201527f4d61726b6574206e6f742077686974656c6973746564000000000000000000006044820152fd5b7f8000000000000000000000000000000000000000000000000000000000000000811462000c675760000390565b8181039291600013801582851316918412161762000c6757565b9190916000838201938412911290801582169115161762000c6757565b9081602091031262000c1257516001600160a01b038116810362000c12579056fe60806040908082523462000444578181620016e0803803809162000024828562000449565b83398101031262000444576200003a8162000483565b62000049602080930162000483565b83516395d89b4160e01b8082526001600160a01b0393841694600492909190600090818186818b5afa9081156200041c57829162000426575b50620000cb60318a5183620000a182955180928a808601910162000498565b810170204e656f46696e616e636520476175676560781b8882015203601181018452018262000449565b8851928352818386818b5afa9283156200041c578293620003f3575b506200012560268a51856200010682975180928a808601910162000498565b8101652d676175676560d01b8882015203600681018652018462000449565b80516001600160401b0393848211620003e0576003938454926001948585811c95168015620003d5575b89861014620003c2578190601f958681116200036f575b5089908683116001146200030c57849262000300575b505060001982881b1c191690851b1785555b8151958611620002ed5787548481811c91168015620002e2575b88821014620002cf5783811162000287575b50869286116001146200021b5794955085929190836200020f575b50501b92600019911b1c19161790555b60018060a01b03199283600654161760065516906005541617600555516111ae9081620005328239f35b015193503880620001d5565b87815286812093969394938691601f198316915b898383106200026c575050501062000251575b50505050811b019055620001e5565b01519060f884600019921b161c191690553880808062000242565b8587015189559097019694850194889350908101906200022f565b8882528782208480890160051c8201928a8a10620002c5575b0160051c019085905b828110620002b9575050620001ba565b838155018590620002a9565b92508192620002a0565b634e487b7160e01b825260228952602482fd5b90607f1690620001a8565b634e487b7160e01b815260418852602490fd5b0151905038806200017c565b8885528a85208894509190601f198416865b8d8282106200035857505084116200033f575b505050811b0185556200018e565b0151600019838a1b60f8161c1916905538808062000331565b8385015186558b979095019493840193016200031e565b9091508784528984208680850160051c8201928c8610620003b8575b918991869594930160051c01915b828110620003a957505062000166565b86815585945089910162000399565b925081926200038b565b634e487b7160e01b835260228a52602483fd5b94607f16946200014f565b634e487b7160e01b845260418752602484fd5b620004149193503d8084833e6200040b818362000449565b810190620004bd565b9138620000e7565b89513d84823e3d90fd5b6200043d91503d8084833e6200040b818362000449565b3862000082565b600080fd5b601f909101601f19168101906001600160401b038211908210176200046d57604052565b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b03821682036200044457565b60005b838110620004ac5750506000910152565b81810151838201526020016200049b565b602081830312620004445780516001600160401b03918282116200044457019082601f83011215620004445781519081116200046d57604051926200050d601f8301601f19166020018562000449565b8184526020828401011162000444576200052e916020808501910162000498565b9056fe608060408181526004918236101561001657600080fd5b600092833560e01c91826306fdde031461071057508163095ea7b3146106e65781631071a2901461064a57816318160ddd1461062b57816323b872dd146105ee5781632495a599146105c6578163254de2661461059e578163313ce56714610582578163395093511461053357816342966c681461051557816370a08231146104df57816379cc6790146104af57816395d89b41146103ac578163a457c2d7146102eb578163a9059cbb146102ba578163b9f5be411461012b575063dd62ed3e146100e057600080fd5b34610127578060031936011261012757806020926100fc61084e565b610104610869565b6001600160a01b0391821683526001865283832091168252845220549051908152f35b5080fd5b919050346102605760203660031901126102605781356001600160a01b0380600654168351907f23b872dd0000000000000000000000000000000000000000000000000000000060208301523360248301523060448301528360648301526064825260a0820182811067ffffffffffffffff8211176102a75785526101b09190610f80565b3315610264579084916101c5826002546108cb565b600255338352826020528383208281540190558351828152837fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60203393a360055416803b156102605783516248b64560e61b8152339581019586526020860192909252909384919082908490829060400103925af1908115610257575061024b575080f35b6102549061087f565b80f35b513d84823e3d90fd5b8280fd5b606484602085519162461bcd60e51b8352820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152fd5b602488604189634e487b7160e01b835252fd5b5050346101275780600319360112610127576020906102e46102da61084e565b60243590336108ee565b5160018152f35b905082346103a957826003193601126103a95761030661084e565b91836024359233815260016020528181206001600160a01b0386168252602052205490828210610340576020856102e48585038733610b8d565b608490602086519162461bcd60e51b8352820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152fd5b80fd5b838334610127578160031936011261012757805191809380549160019083821c928285169485156104a5575b60209586861081146104925785895290811561046e5750600114610416575b6104128787610408828c03836108a9565b5191829182610805565b0390f35b81529295507f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b5b82841061045b575050508261041294610408928201019486806103f7565b805486850188015292860192810161043d565b60ff19168887015250505050151560051b83010192506104088261041286806103f7565b602484602285634e487b7160e01b835252fd5b93607f16936103d8565b505034610127573660031901126103a9576102546104cb61084e565b602435906104da823383610cc1565b610d59565b50503461012757602036600319011261012757806020926001600160a01b0361050661084e565b16815280845220549051908152f35b83903461012757602036600319011261012757610254903533610d59565b5050346101275780600319360112610127576102e460209261057b61055661084e565b91338152600186528481206001600160a01b03841682528652846024359120546108cb565b9033610b8d565b5050346101275781600319360112610127576020905160128152f35b5050346101275781600319360112610127576020906001600160a01b03600554169051908152f35b5050346101275781600319360112610127576020906001600160a01b03600654169051908152f35b505034610127576060366003190112610127576020906102e461060f61084e565b610617610869565b60443591610626833383610cc1565b6108ee565b5050346101275781600319360112610127576020906002549051908152f35b8383346101275760203660031901126101275782359061066a8233610d59565b6001600160a01b0360065416908051927fa9059cbb000000000000000000000000000000000000000000000000000000006020850152336024850152604484015260448352608083019083821067ffffffffffffffff8311176106d35761025494955052610f80565b602485604188634e487b7160e01b835252fd5b5050346101275780600319360112610127576020906102e461070661084e565b6024359033610b8d565b92915034610801578360031936011261080157600354600181811c91869082811680156107f7575b60209586861082146107e457508488529081156107c25750600114610769575b6104128686610408828b03836108a9565b929550600383527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b5b8284106107af575050508261041294610408928201019438610758565b8054868501880152928601928101610792565b60ff191687860152505050151560051b83010192506104088261041238610758565b836022602492634e487b7160e01b835252fd5b93607f1693610738565b8380fd5b6020808252825181830181905290939260005b82811061083a57505060409293506000838284010152601f8019910116010190565b818101860151848201604001528501610818565b600435906001600160a01b038216820361086457565b600080fd5b602435906001600160a01b038216820361086457565b67ffffffffffffffff811161089357604052565b634e487b7160e01b600052604160045260246000fd5b90601f8019910116810190811067ffffffffffffffff82111761089357604052565b919082018092116108d857565b634e487b7160e01b600052601160045260246000fd5b916001600160a01b0392838116928315610b2357848116948515610ab9576000958587528660205260409586882054868110610a50578690828a528960205203878920558188528688208681540190557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851888152a3858160055416600160ff1b8614610a3c57803b156101275786516248b64560e61b8082526001600160a01b0396909616600482015286830360248201529082908290604490829084905af18015610a3257610a1e575b50506005541691823b15610a1a5784519081526001600160a01b03919091166004820152602481019290925290919083908390604490829084905af19081156102575750610a09575050565b610a13829161087f565b6103a95750565b8580fd5b610a2a9192975061087f565b9438806109bd565b87513d84823e3d90fd5b602482634e487b7160e01b81526011600452fd5b6084885162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152fd5b6001600160a01b03809116918215610c585716918215610bee5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925918360005260018252604060002085600052825280604060002055604051908152a3565b608460405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152fd5b906001600160a01b0380831660005260016020526040600020908216600052602052604060002054926000198403610cfa575b50505050565b808410610d1557610d0c930391610b8d565b38808080610cf4565b606460405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152fd5b6001600160a01b0391828216918215610f16576000938385528460205260409384862054848110610ead579084879282845283602052038683205584600254036002557fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208751878152a3848160055416600160ff1b8514610a3c57803b156101275785516248b64560e61b8082526001600160a01b0395909516600482015285830360248201529082908290604490829084905af18015610ea357610e8f575b505084610e2a575b5050505050565b6005541690813b15610e8b5783519081526001600160a01b0385166004820152602481019290925290919083908390604490829084905af19081156102575750610e77575b808080610e23565b610e81829161087f565b6103a95780610e6f565b8480fd5b610e9b9192965061087f565b933880610e1b565b86513d84823e3d90fd5b6084865162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152fd5b608460405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152fd5b6001600160a01b0316906040516040810167ffffffffffffffff9082811082821117610893576040526020938483527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564858401526000808587829751910182855af1903d156110d9573d9283116110c5579061101c9392916040519261100f88601f19601f84011601856108a9565b83523d868885013e6110e4565b8051918215918483156110a1575b5050509050156110375750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b919381809450010312610127578201519081151582036103a957508038808461102a565b602485634e487b7160e01b81526041600452fd5b9061101c9392506060915b9192901561114557508151156110f8575090565b3b156111015790565b606460405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8251909150156111585750805190602001fd5b6111749060405191829162461bcd60e51b835260048301610805565b0390fdfea264697066735822122057064ce255ed206b51dbe1f12443ee5c91317ede7900d50f8f8b7e57cff3ace964736f6c63430008110033a2646970667358221220238b8b4957e6f4ed7c309ebfebfeddfb0ee83529a69312b7ce92f75921937e3764736f6c63430008110033