Spend less on fees, more on crypto. Buy crypto easily with MoonPay Balance. 20M+ users trust MoonPay worldwide.
Ready to onboard to Ethereum? With MetaMask Portfolio, you're in control.
Don’t invest unless you’re prepared to lose all the money you invest.
Ready to simplify your web3 experience? Try the all-in-one web3 app trusted by millions worldwide.
Available on 9 networks: Ethereum mainnet, Linea, Polygon, Optimism, BNB Chain, zkSync Era, Base, Avalanche.
Everyday giveaways up to 100 ETH, Lucky Spins. Deposit BONUS 300% and Cashbacks!
5000+ Slots & Live Casino Games, 50+cryptos. Register with Etherscan and get 760% deposit bonus. Win Big$, withdraw it fast.
Slots, Roulette, Poker & more - Proud sponsors of UFC, Everton & StakeF1 team!
5000+ Slots & Live Casino Games, 50+cryptos. Register with Etherscan and get 760% deposit bonus. Win Big$, withdraw it fast.
Anonymous play on awesome games - sign up now for 25 free jackpot spins - worth $100s!
100s of games, generous bonuses, 20+ years of trusted gaming. Join CryptoWins & start winning today!
Overview
ETH Balance
Eth Value
$0.00Token Holdings
Could not find any matches!
- ERC-20 Tokens (37)9,039.00700144 VECVector (VEC)$11,750.71@1.3047.43258478 ALCXAlchemix (ALCX)$934.42@19.70373.020163 AXLAxelar (AXL)$280.84@0.7529572.43385769 cvxPrismaConvex Prism... (cvxPri...)$31.02@0.0542724.68103422 CVXConvex Token (CVX)$3,011.23@4.155315.08512452 ETHFIether.fi gov... (ETHFI)$29.72@1.970.2 FRAXFrax (FRAX)$0.20@0.99511,497.31744046 FXSFrax Share (FXS)$5,045.96@3.37101.9436493 METMetronome2 (MET)$46.07@0.45196,855.00830449 OGNOriginToken (OGN)$723.43@0.10550.00249113 pxETHPirex Ether (pxETH)$8.30@3,331.9968.06389609 SDStader (SD)$64.36@0.945615,041.03286279 SDTStake DAO To... (SDT)$8,389.38@0.55780.00191252 swETHswETH (swETH)$6.88@3,595.86790 weETHWrapped eETH (weETH)$0.00@3,516.6745,646.64011476 ZUNZunami Token (ZUN)$1,496.99@0.03282,207.77596336 CRVCurve DAO To... (CRV)$1,879.44@0.85138.32060772 GNOGnosis (GNO)$2,170.35@260.841,257.026467 USDTTether USD (USDT)$1,254.92@0.998312,303.731695 USDCUSDC (USDC)$12,300.35@0.99970.06844226 WETHWrapped Ethe... (WETH)$228.26@3,335.1304724.78402408 BGBGBigMouthFrog740,208.57129632 BSNERC-20: Bloc... (BSN)119,717.33338679 CVGConvergence$188.45@0.00160.55234925 BobrCRVERC-20: CURV... (BobrCR...)209.69060165 eCFXethereum Conflux0.1 LFTERC-20: Lend... (LFT)21,444.74250307 OGVOrigin Dollar Governance$276.59@0.012962.69063088 QIQiDao$1.39@0.02222.98599379 SPRSpiral0.001 HQGERC-20: 环球股 (HQG)324.921669 ZUSDZ.com USD$325.05@1.00042,382.23 TokenERC-20 TOKEN*[Suspicious]1.7 TokenERC-20 TOKEN*[Suspicious]100,000 TokenERC-20 TOKEN*[Suspicious]587 TokenERC-20 TOKEN*[Suspicious]1.4 TokenERC-20 TOKEN*[Spam]NFT Tokens (19)claim rewards on apyusd.netapyusd.netERC-1155Official Authorize credentialFRAX: Airdrop NFT VoucherERC-1155claim rewards on fraxprotocol.comfraxprotocol.comERC-1155gnosis.giftGnosis Mysterybox NFTERC-1155nft-lido.comLDO Mysterybox NFTERC-1155puffeth.compuffeth.comERC-1155Withdrawal NFT puffereth.comWithdrawal NFT puffereth.comERC-1155ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]ERC-1155 TOKEN*[Suspicious]
More Info
Private Name Tags
ContractCreator
- Transactions
- Internal Transactions
- Token Transfers (ERC-20)
- NFT Transfers
- Contract
- Events
- Analytics
- Multichain Portfolio
- Cards New
Advanced Filter- Filter by Tx Type:
- Tx
- Internal Tx
- ERC-20
- NFTs
Latest 25 from a total of 22,196 transactions
Transaction Hash MethodBlockFromToClaim All 21458202 2024-12-22 13:01:47 22 hrs ago 1734872507 IN 0 ETH$0.00 0.00098003 7.33257835 Claim 21456991 2024-12-22 8:57:47 26 hrs ago 1734857867 IN 0 ETH$0.00 0.00102182 6.14124252 Claim 21447112 2024-12-20 23:47:23 2 days ago 1734738443 IN 0 ETH$0.00 0.00130426 7.838703 Claim All 21435845 2024-12-19 10:03:11 4 days ago 1734602591 IN 0 ETH$0.00 0.00469912 13.92197936 Claim 21434105 2024-12-19 4:12:59 4 days ago 1734581579 IN 0 ETH$0.00 0.00393106 9 Claim 21433099 2024-12-19 0:49:35 4 days ago 1734569375 IN 0 ETH$0.00 0.00448496 12.66700415 Claim All For 21432928 2024-12-19 0:15:11 4 days ago 1734567311 IN 0 ETH$0.00 0.00654625 17.63020005 Claim All 21432070 2024-12-18 21:22:47 4 days ago 1734556967 IN 0 ETH$0.00 0.00333516 33.5343815 Claim 21429871 2024-12-18 13:59:59 4 days ago 1734530399 IN 0 ETH$0.00 0.00497724 17.31649167 Claim 21402620 2024-12-14 18:44:11 8 days ago 1734201851 IN 0 ETH$0.00 0.00084493 8.54425145 Claim All 21400849 2024-12-14 12:47:11 8 days ago 1734180431 IN 0 ETH$0.00 0.00151355 8.22344261 Claim 21397371 2024-12-14 1:08:35 9 days ago 1734138515 IN 0 ETH$0.00 0.00228235 12.43869322 Claim 21396976 2024-12-13 23:49:11 9 days ago 1734133751 IN 0 ETH$0.00 0.0044085 10.52735933 Claim All 21392494 2024-12-13 8:47:59 10 days ago 1734079679 IN 0 ETH$0.00 0.004204 11.30921099 Claim 21391806 2024-12-13 6:29:47 10 days ago 1734071387 IN 0 ETH$0.00 0.00319978 9.89588087 Claim 21384733 2024-12-12 6:48:23 11 days ago 1733986103 IN 0 ETH$0.00 0.00275284 13.72387652 Claim 21383596 2024-12-12 2:59:59 11 days ago 1733972399 IN 0 ETH$0.00 0.0019923 17.1767059 Claim All For 21382871 2024-12-12 0:33:59 11 days ago 1733963639 IN 0 ETH$0.00 0.01034016 26.62184736 Claim 21380598 2024-12-11 16:56:59 11 days ago 1733936219 IN 0 ETH$0.00 0.00814643 24.175759 Claim 21380496 2024-12-11 16:36:11 11 days ago 1733934971 IN 0 ETH$0.00 0.00554467 28.06692035 Claim 21380482 2024-12-11 16:33:23 11 days ago 1733934803 IN 0 ETH$0.00 0.0008182 27.27362559 Claim All 21378469 2024-12-11 9:47:59 12 days ago 1733910479 IN 0 ETH$0.00 0.00144742 14.54346234 Claim All 21378143 2024-12-11 8:41:23 12 days ago 1733906483 IN 0 ETH$0.00 0.00157104 13.47103426 Claim 21376343 2024-12-11 2:39:47 12 days ago 1733884787 IN 0 ETH$0.00 0.00137054 13.84981303 Claim 21364314 2024-12-09 10:21:23 14 days ago 1733739683 IN 0 ETH$0.00 0.00123989 12.52948649 Latest 1 internal transaction
Advanced mode:Parent Transaction Hash Block FromTo17123807 2023-04-25 14:16:11 607 days ago 1682432171 Contract Creation 0 ETH$0.00 Loading...LoadingContract Name:Platform
Compiler Versionv0.8.17+commit.8df45f5f
Optimization Enabled:Yes with 99999999 runs
Other Settings:default evmVersionContract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: Unlicense pragma solidity 0.8.17; /* ▄▄▄█████▓ ██░ ██ ▓█████ ██░ ██ ▓█████ ██▀███ ▓█████▄ ▓ ██▒ ▓▒▓██░ ██▒▓█ ▀ ▓██░ ██▒▓█ ▀ ▓██ ▒ ██▒▒██▀ ██▌ ▒ ▓██░ ▒░▒██▀▀██░▒███ ▒██▀▀██░▒███ ▓██ ░▄█ ▒░██ █▌ ░ ▓██▓ ░ ░▓█ ░██ ▒▓█ ▄ ░▓█ ░██ ▒▓█ ▄ ▒██▀▀█▄ ░▓█▄ ▌ ▒██▒ ░ ░▓█▒░██▓░▒████▒ ░▓█▒░██▓░▒████▒░██▓ ▒██▒░▒████▓ ▒ ░░ ▒ ░░▒░▒░░ ▒░ ░ ▒ ░░▒░▒░░ ▒░ ░░ ▒▓ ░▒▓░ ▒▒▓ ▒ ░ ▒ ░▒░ ░ ░ ░ ░ ▒ ░▒░ ░ ░ ░ ░ ░▒ ░ ▒░ ░ ▒ ▒ ░ ░ ░░ ░ ░ ░ ░░ ░ ░ ░░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ .,;>>%%%%%>>;,. .>%%%%%%%%%%%%%%%%%%%%>,. .>%%%%%%%%%%%%%%%%%%>>,%%%%%%;,. .>>>>%%%%%%%%%%%%%>>,%%%%%%%%%%%%,>>%%,. .>>%>>>>%%%%%%%%%>>,%%%%%%%%%%%%%%%%%,>>%%%%%,. .>>%%%%%>>%%%%>>,%%>>%%%%%%%%%%%%%%%%%%%%,>>%%%%%%%, .>>%%%%%%%%%%>>,%%%%%%>>%%%%%%%%%%%%%%%%%%,>>%%%%%%%%%%. .>>%%%%%%%%%%>>,>>>>%%%%%%%%%%'..`%%%%%%%%,;>>%%%%%%%%%>%%. .>>%%%>>>%%%%%>,%%%%%%%%%%%%%%.%%%,`%%%%%%,;>>%%%%%%%%>>>%%%%. >>%%>%>>>%>%%%>,%%%%%>>%%%%%%%%%%%%%`%%%%%%,>%%%%%%%>>>>%%%%%%%. >>%>>>%%>>>%%%%>,%>>>%%%%%%%%%%%%%%%%`%%%%%%%%%%%%%%%%%%%%%%%%%%. >>%%%%%%%%%%%%%%,>%%%%%%%%%%%%%%%%%%%'%%%,>>%%%%%%%%%%%%%%%%%%%%%. >>%%%%%%%%%%%%%%%,>%%%>>>%%%%%%%%%%%%%%%,>>%%%%%%%%>>>>%%%%%%%%%%%. >>%%%%%%%%;%;%;%%;,%>>>>%%%%%%%%%%%%%%%,>>>%%%%%%>>;";>>%%%%%%%%%%%%. `>%%%%%%%%%;%;;;%;%,>%%%%%%%%%>>%%%%%%%%,>>>%%%%%%%%%%%%%%%%%%%%%%%%%%. >>%%%%%%%%%,;;;;;%%>,%%%%%%%%>>>>%%%%%%%%,>>%%%%%%%%%%%%%%%%%%%%%%%%%%%. `>>%%%%%%%%%,%;;;;%%%>,%%%%%%%%>>>>%%%%%%%%,>%%%%%%'%%%%%%%%%%%%%%%%%%%>>. `>>%%%%%%%%%%>,;;%%%%%>>,%%%%%%%%>>%%%%%%';;;>%%%%%,`%%%%%%%%%%%%%%%>>%%>. >>>%%%%%%%%%%>> %%%%%%%%>>,%%%%>>>%%%%%';;;;;;>>,%%%,`% `;>%%%%%%>>%% `>>%%%%%%%%%%>> %%%%%%%%%>>>>>>>>;;;;'.;;;;;>>%%' `%%' ;>%%%%%> >>%%%%%%%%%>>; %%%%%%%%>>;;;;;;'' ;;;;;>>%%% ;>%%%% `>>%%%%%%%>>>, %%%%%%%%%>>;;' ;;;;>>%%%' ;>%%% >>%%%%%%>>>':.%%%%%%%%%%>>; .;;;>>%%%% ;>%%%' `>>%%%%%>>> ::`%%%%%%%%%%>>;. ;;;>>%%%%' ;>%%%' `>>%%%%>>> `:::`%%%%%%%%%%>;. ;;>>%%%%% ;>%%' `>>%%%%>>, `::::`%%%%%%%%%%>, .;>>%%%%%' ;>%' `>>%%%%>>, `:::::`%%%%%%%%%>>. ;;>%%%%%% ;>%, `>>%%%%>>, :::::::`>>>%%%%>>> ;;>%%%%%' ;>%, `>>%%%%>>,::::::,>>>>>>>>>>' ;;>%%%%% ;%%, >>%%%%>>,:::,%%>>>>>>>>' ;>%%%%%. ;%% >>%%%%>>``%%%%%>>>>>' `>%%%%%%. >>%%%%>> `@@a%%%%%%' .%%%%%%%%%. `a@@a%@' `%a@@' `a@@a%a@@a */ import {Owned} from "solmate/auth/Owned.sol"; import {ERC20} from "solmate/tokens/ERC20.sol"; import {SafeTransferLib} from "solady/utils/SafeTransferLib.sol"; import {ReentrancyGuard} from "solmate/utils/ReentrancyGuard.sol"; import {GaugeController} from "src/interfaces/GaugeController.sol"; import {FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol"; /// @title Platform /// @author Stake DAO contract Platform is Owned, ReentrancyGuard { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; //////////////////////////////////////////////////////////////// /// --- EMERGENCY SHUTDOWN /////////////////////////////////////////////////////////////// /// @notice Emergency shutdown flag bool public isKilled; //////////////////////////////////////////////////////////////// /// --- STRUCTS /////////////////////////////////////////////////////////////// /// @notice Bounty struct requirements. struct Bounty { // Address of the target gauge. address gauge; // Manager. address manager; // Address of the ERC20 used for rewards. address rewardToken; // Number of periods. uint8 numberOfPeriods; // Timestamp where the bounty become unclaimable. uint256 endTimestamp; // Max Price per vote. uint256 maxRewardPerVote; // Total Reward Added. uint256 totalRewardAmount; // Blacklisted addresses. address[] blacklist; } struct Upgrade { // Number of periods after increase. uint8 numberOfPeriods; // Total reward amount after increase. uint256 totalRewardAmount; // New max reward per vote after increase. uint256 maxRewardPerVote; // New end timestamp after increase. uint256 endTimestamp; } /// @notice Period struct. struct Period { // Period id. // Eg: 0 is the first period, 1 is the second period, etc. uint8 id; // Timestamp of the period start. uint256 timestamp; // Reward amount distributed during the period. uint256 rewardPerPeriod; } //////////////////////////////////////////////////////////////// /// --- CONSTANTS & IMMUTABLES /////////////////////////////////////////////////////////////// /// @notice Minimum duration a Bounty. uint8 public constant MINIMUM_PERIOD = 2; /// @notice Week in seconds. uint256 private constant _WEEK = 1 weeks; /// @notice Base unit for fixed point compute. uint256 private constant _BASE_UNIT = 1e18; /// @notice Default fee. uint256 internal constant _DEFAULT_FEE = 2e16; // 2% /// @notice Gauge Controller. GaugeController public immutable gaugeController; //////////////////////////////////////////////////////////////// /// --- STORAGE VARS /////////////////////////////////////////////////////////////// /// @notice Fee. uint256 public fee; /// @notice Bounty ID Counter. uint256 public nextID; /// @notice Fee collector. address public feeCollector; /// @notice ID => Bounty. mapping(uint256 => Bounty) public bounties; /// @notice Recipient per address. mapping(address => address) public recipient; /// @notice Fee accrued per rewardToken. mapping(address => uint256) public feeAccrued; /// @notice BountyId => isUpgradeable. If true, the bounty can be upgraded. mapping(uint256 => bool) public isUpgradeable; /// @notice ID => Period running. mapping(uint256 => Period) public activePeriod; /// @notice ID => Amount Claimed per Bounty. mapping(uint256 => uint256) public amountClaimed; /// @notice ID => Amount of reward per vote distributed. mapping(uint256 => uint256) public rewardPerVote; /// @notice ID => Bounty In Queue to be upgraded. mapping(uint256 => Upgrade) public upgradeBountyQueue; /// @notice Blacklisted addresses per bounty that aren't counted for rewards arithmetics. mapping(uint256 => mapping(address => bool)) public isBlacklisted; /// @notice Last time a user claimed mapping(address => mapping(uint256 => uint256)) public lastUserClaim; //////////////////////////////////////////////////////////////// /// --- MODIFIERS /////////////////////////////////////////////////////////////// modifier notKilled() { if (isKilled) revert KILLED(); _; } modifier onlyManager(uint256 _id) { if (msg.sender != bounties[_id].manager) revert AUTH_MANAGER_ONLY(); _; } //////////////////////////////////////////////////////////////// /// --- EVENTS /////////////////////////////////////////////////////////////// /// @notice Emitted when a new bounty is created. /// @param id Bounty ID. /// @param gauge Gauge address. /// @param manager Manager address. /// @param rewardToken Reward token address. /// @param numberOfPeriods Number of periods. /// @param maxRewardPerVote Max reward per vote. /// @param rewardPerPeriod Reward per period. /// @param totalRewardAmount Total reward amount. /// @param isUpgradeable If true, the bounty can be upgraded. event BountyCreated( uint256 indexed id, address indexed gauge, address manager, address rewardToken, uint8 numberOfPeriods, uint256 maxRewardPerVote, uint256 rewardPerPeriod, uint256 totalRewardAmount, bool isUpgradeable ); /// @notice Emitted when a bounty is closed. /// @param id Bounty ID. /// @param remainingReward Remaining reward. event BountyClosed(uint256 id, uint256 remainingReward); /// @notice Emitted when a bounty period is rolled over. /// @param id Bounty ID. /// @param periodId Period ID. /// @param timestamp Period timestamp. /// @param rewardPerPeriod Reward per period. event PeriodRolledOver(uint256 id, uint256 periodId, uint256 timestamp, uint256 rewardPerPeriod); /// @notice Emitted on claim. /// @param user User address. /// @param rewardToken Reward token address. /// @param bountyId Bounty ID. /// @param amount Amount claimed. /// @param protocolFees Protocol fees. /// @param period Period timestamp. event Claimed( address indexed user, address rewardToken, uint256 indexed bountyId, uint256 amount, uint256 protocolFees, uint256 period ); /// @notice Emitted when a bounty is queued to upgrade. /// @param id Bounty ID. /// @param numberOfPeriods Number of periods. /// @param totalRewardAmount Total reward amount. /// @param maxRewardPerVote Max reward per vote. event BountyDurationIncreaseQueued( uint256 id, uint8 numberOfPeriods, uint256 totalRewardAmount, uint256 maxRewardPerVote ); /// @notice Emitted when a bounty is upgraded. /// @param id Bounty ID. /// @param numberOfPeriods Number of periods. /// @param totalRewardAmount Total reward amount. /// @param maxRewardPerVote Max reward per vote. event BountyDurationIncrease( uint256 id, uint8 numberOfPeriods, uint256 totalRewardAmount, uint256 maxRewardPerVote ); /// @notice Emitted when a bounty manager is updated. /// @param id Bounty ID. /// @param manager Manager address. event ManagerUpdated(uint256 id, address indexed manager); /// @notice Emitted when a recipient is set for an address. /// @param sender Sender address. /// @param recipient Recipient address. event RecipientSet(address indexed sender, address indexed recipient); /// @notice Emitted when fee is updated. /// @param fee Fee. event FeeUpdated(uint256 fee); /// @notice Emitted when fee collector is updated. /// @param feeCollector Fee collector. event FeeCollectorUpdated(address feeCollector); /// @notice Emitted when fees are collected. /// @param rewardToken Reward token address. /// @param amount Amount collected. event FeesCollected(address indexed rewardToken, uint256 amount); //////////////////////////////////////////////////////////////// /// --- ERRORS /////////////////////////////////////////////////////////////// error KILLED(); error WRONG_INPUT(); error ZERO_ADDRESS(); error ALREADY_CLOSED(); error NO_PERIODS_LEFT(); error NOT_UPGRADEABLE(); error AUTH_MANAGER_ONLY(); error INVALID_NUMBER_OF_PERIODS(); //////////////////////////////////////////////////////////////// /// --- CONSTRUCTOR /////////////////////////////////////////////////////////////// /// @notice Create Bounty platform. /// @param _gaugeController Address of the gauge controller. constructor(address _gaugeController, address _feeCollector, address _owner) Owned(_owner) { fee = _DEFAULT_FEE; feeCollector = _feeCollector; gaugeController = GaugeController(_gaugeController); } //////////////////////////////////////////////////////////////// /// --- BOUNTY CREATION LOGIC /////////////////////////////////////////////////////////////// /// @notice Create a new bounty. /// @param gauge Address of the target gauge. /// @param rewardToken Address of the ERC20 used or rewards. /// @param numberOfPeriods Number of periods. /// @param maxRewardPerVote Target Bias for the Gauge. /// @param totalRewardAmount Total Reward Added. /// @param blacklist Array of addresses to blacklist. /// @return newBountyId of the bounty created. function createBounty( address gauge, address manager, address rewardToken, uint8 numberOfPeriods, uint256 maxRewardPerVote, uint256 totalRewardAmount, address[] calldata blacklist, bool upgradeable ) external nonReentrant notKilled returns (uint256 newBountyId) { if (gaugeController.gauge_types(gauge) < 0) return newBountyId; if (numberOfPeriods < MINIMUM_PERIOD) revert INVALID_NUMBER_OF_PERIODS(); if (totalRewardAmount == 0 || maxRewardPerVote == 0) revert WRONG_INPUT(); if (rewardToken == address(0) || manager == address(0)) revert ZERO_ADDRESS(); // Transfer the rewards to the contracts. SafeTransferLib.safeTransferFrom(rewardToken, msg.sender, address(this), totalRewardAmount); unchecked { // Get the ID for that new Bounty and increment the nextID counter. newBountyId = nextID; ++nextID; } uint256 rewardPerPeriod = totalRewardAmount.mulDiv(1, numberOfPeriods); uint256 currentPeriod = getCurrentPeriod(); bounties[newBountyId] = Bounty({ gauge: gauge, manager: manager, rewardToken: rewardToken, numberOfPeriods: numberOfPeriods, endTimestamp: currentPeriod + ((numberOfPeriods + 1) * _WEEK), maxRewardPerVote: maxRewardPerVote, totalRewardAmount: totalRewardAmount, blacklist: blacklist }); emit BountyCreated( newBountyId, gauge, manager, rewardToken, numberOfPeriods, maxRewardPerVote, rewardPerPeriod, totalRewardAmount, upgradeable ); // Set Upgradeable status. isUpgradeable[newBountyId] = upgradeable; // Starting from next period. activePeriod[newBountyId] = Period(0, currentPeriod + _WEEK, rewardPerPeriod); // Add the addresses to the blacklist. uint256 length = blacklist.length; for (uint256 i = 0; i < length;) { isBlacklisted[newBountyId][blacklist[i]] = true; unchecked { ++i; } } } /// @notice Claim rewards for a given bounty. /// @param bountyId ID of the bounty. /// @return Amount of rewards claimed. function claim(uint256 bountyId) external returns (uint256) { return _claim(msg.sender, msg.sender, bountyId); } /// @notice Claim rewards for a given bounty. /// @param bountyId ID of the bounty. /// @return Amount of rewards claimed. function claim(uint256 bountyId, address _recipient) external returns (uint256) { return _claim(msg.sender, _recipient, bountyId); } /// @notice Claim rewards for a given bounty. /// @param bountyId ID of the bounty. /// @return Amount of rewards claimed. function claimFor(address user, uint256 bountyId) external returns (uint256) { address _recipient = recipient[user]; return _claim(user, _recipient != address(0) ? _recipient : user, bountyId); } /// @notice Claim all rewards for multiple bounties. /// @param ids Array of bounty IDs to claim. function claimAll(uint256[] calldata ids) external { uint256 length = ids.length; for (uint256 i = 0; i < length;) { uint256 id = ids[i]; _claim(msg.sender, msg.sender, id); unchecked { ++i; } } } /// @notice Claim all rewards for multiple bounties to a given recipient. /// @param ids Array of bounty IDs to claim. /// @param _recipient Address to send the rewards to. function claimAll(uint256[] calldata ids, address _recipient) external { uint256 length = ids.length; for (uint256 i = 0; i < length;) { uint256 id = ids[i]; _claim(msg.sender, _recipient, id); unchecked { ++i; } } } /// @notice Claim all rewards for multiple bounties on behalf of a user. /// @param ids Array of bounty IDs to claim. /// @param _user Address to claim the rewards for. function claimAllFor(address _user, uint256[] calldata ids) external { address _recipient = recipient[_user]; uint256 length = ids.length; for (uint256 i = 0; i < length;) { uint256 id = ids[i]; _claim(_user, _recipient != address(0) ? _recipient : _user, id); unchecked { ++i; } } } /// @notice Update Bounty for a given id. /// @param bountyId ID of the bounty. function updateBountyPeriod(uint256 bountyId) external { _updateBountyPeriod(bountyId); } /// @notice Update multiple bounties for given ids. /// @param ids Array of Bounty IDs. function updateBountyPeriods(uint256[] calldata ids) external { uint256 length = ids.length; for (uint256 i = 0; i < length;) { _updateBountyPeriod(ids[i]); unchecked { ++i; } } } /// @notice Set a recipient address for calling user. /// @param _recipient Address of the recipient. /// @dev Recipient are used when calling claimFor functions. Regular claim functions will use msg.sender as recipient, /// or recipient parameter provided if called by msg.sender. function setRecipient(address _recipient) external { recipient[msg.sender] = _recipient; emit RecipientSet(msg.sender, _recipient); } //////////////////////////////////////////////////////////////// /// --- INTERNAL LOGIC /////////////////////////////////////////////////////////////// /// @notice Claim rewards for a given bounty. /// @param _user Address of the user. /// @param _recipient Address of the recipient. /// @param _bountyId ID of the bounty. /// @return amount of rewards claimed. function _claim(address _user, address _recipient, uint256 _bountyId) internal nonReentrant notKilled returns (uint256 amount) { if (isBlacklisted[_bountyId][_user]) return 0; // Update if needed the current period. uint256 currentPeriod = _updateBountyPeriod(_bountyId); Bounty storage bounty = bounties[_bountyId]; // Get the last_vote timestamp. uint256 lastVote = gaugeController.last_user_vote(_user, bounty.gauge); GaugeController.VotedSlope memory userSlope = gaugeController.vote_user_slopes(_user, bounty.gauge); if ( userSlope.slope == 0 || lastUserClaim[_user][_bountyId] >= currentPeriod || currentPeriod >= userSlope.end || currentPeriod <= lastVote || currentPeriod >= bounty.endTimestamp || currentPeriod != getCurrentPeriod() || amountClaimed[_bountyId] == bounty.totalRewardAmount ) return 0; // Update User last claim period. lastUserClaim[_user][_bountyId] = currentPeriod; // Voting Power = userSlope * dt // with dt = lock_end - period. uint256 _bias = _getAddrBias(userSlope.slope, userSlope.end, currentPeriod); // Compute the reward amount based on // Reward / Total Votes. amount = _bias.mulWad(rewardPerVote[_bountyId]); // Compute the reward amount based on // the max price to pay. uint256 _amountWithMaxPrice = _bias.mulWad(bounty.maxRewardPerVote); // Distribute the _min between the amount based on votes, and price. amount = FixedPointMathLib.min(amount, _amountWithMaxPrice); // Update the amount claimed. uint256 _amountClaimed = amountClaimed[_bountyId]; if (amount + _amountClaimed > bounty.totalRewardAmount) { amount = bounty.totalRewardAmount - _amountClaimed; } amountClaimed[_bountyId] += amount; uint256 feeAmount; if (fee != 0) { feeAmount = amount.mulWad(fee); amount -= feeAmount; feeAccrued[bounty.rewardToken] += feeAmount; } // Transfer to user. SafeTransferLib.safeTransfer(bounty.rewardToken, _recipient, amount); emit Claimed(_user, bounty.rewardToken, _bountyId, amount, feeAmount, currentPeriod); } /// @notice Update the current period for a given bounty. /// @param bountyId Bounty ID. /// @return current/updated period. function _updateBountyPeriod(uint256 bountyId) internal returns (uint256) { Period storage _activePeriod = activePeriod[bountyId]; uint256 currentPeriod = getCurrentPeriod(); if (_activePeriod.id == 0 && currentPeriod == _activePeriod.timestamp) { // Check if there is an upgrade in queue and update the bounty. _checkForUpgrade(bountyId); // Initialize reward per token. // Only for the first period, and if not already initialized. _updateRewardPerToken(bountyId, currentPeriod); } // Increase Period if (block.timestamp >= _activePeriod.timestamp + _WEEK) { // Checkpoint gauge to have up to date gauge weight. gaugeController.checkpoint_gauge(bounties[bountyId].gauge); // Check if there is an upgrade in queue and update the bounty. _checkForUpgrade(bountyId); // Roll to next period. _rollOverToNextPeriod(bountyId, currentPeriod); return currentPeriod; } return _activePeriod.timestamp; } /// @notice Checks for an upgrade and update the bounty. function _checkForUpgrade(uint256 bountyId) internal { Upgrade storage upgradedBounty = upgradeBountyQueue[bountyId]; // Check if there is an upgrade in queue. if (upgradedBounty.totalRewardAmount != 0) { // Save new values. bounties[bountyId].endTimestamp = upgradedBounty.endTimestamp; bounties[bountyId].numberOfPeriods = upgradedBounty.numberOfPeriods; bounties[bountyId].maxRewardPerVote = upgradedBounty.maxRewardPerVote; bounties[bountyId].totalRewardAmount = upgradedBounty.totalRewardAmount; if (activePeriod[bountyId].id == 0) { activePeriod[bountyId].rewardPerPeriod = upgradedBounty.totalRewardAmount.mulDiv(1, upgradedBounty.numberOfPeriods); } emit BountyDurationIncrease( bountyId, upgradedBounty.numberOfPeriods, upgradedBounty.totalRewardAmount, upgradedBounty.maxRewardPerVote ); // Reset the next values. delete upgradeBountyQueue[bountyId]; } } /// @notice Roll over to next period. /// @param bountyId Bounty ID. /// @param currentPeriod Next period timestamp. function _rollOverToNextPeriod(uint256 bountyId, uint256 currentPeriod) internal { uint8 index = getActivePeriodPerBounty(bountyId); Bounty storage bounty = bounties[bountyId]; uint256 periodsLeft = getPeriodsLeft(bountyId); uint256 rewardPerPeriod; rewardPerPeriod = bounty.totalRewardAmount - amountClaimed[bountyId]; if (bounty.endTimestamp > currentPeriod + _WEEK && periodsLeft > 1) { rewardPerPeriod = rewardPerPeriod.mulDiv(1, periodsLeft); } // Get adjusted slope without blacklisted addresses. uint256 gaugeBias = _getAdjustedBias(bounty.gauge, bounty.blacklist, currentPeriod); rewardPerVote[bountyId] = rewardPerPeriod.mulDiv(_BASE_UNIT, gaugeBias); activePeriod[bountyId] = Period(index, currentPeriod, rewardPerPeriod); emit PeriodRolledOver(bountyId, index, currentPeriod, rewardPerPeriod); } /// @notice Update the amount of reward per token for a given bounty. /// @dev This function is only called once per Bounty. function _updateRewardPerToken(uint256 bountyId, uint256 currentPeriod) internal { if (rewardPerVote[bountyId] == 0) { Bounty storage bounty = bounties[bountyId]; // Checkpoint gauge to have up to date gauge weight. gaugeController.checkpoint_gauge(bounty.gauge); uint256 gaugeBias = _getAdjustedBias(bounty.gauge, bounty.blacklist, currentPeriod); if (gaugeBias != 0) { rewardPerVote[bountyId] = activePeriod[bountyId].rewardPerPeriod.mulDiv(_BASE_UNIT, gaugeBias); } } } //////////////////////////////////////////////////////////////// /// --- VIEWS /////////////////////////////////////////////////////////////// /// @notice Get an estimate of the reward amount for a given user. /// @param user Address of the user. /// @param bountyId ID of the bounty. /// @return amount of rewards. /// Mainly used for UI. function claimable(address user, uint256 bountyId) external view returns (uint256 amount) { if (isBlacklisted[bountyId][user]) return 0; Bounty memory bounty = bounties[bountyId]; // If there is an upgrade in progress but period hasn't been rolled over yet. Upgrade storage upgradedBounty = upgradeBountyQueue[bountyId]; // Update if needed the current period. uint256 currentPeriod = getCurrentPeriod(); // End timestamp of the bounty. uint256 endTimestamp = FixedPointMathLib.max(bounty.endTimestamp, upgradedBounty.endTimestamp); // Get the last_vote timestamp. uint256 lastVote = gaugeController.last_user_vote(user, bounty.gauge); GaugeController.VotedSlope memory userSlope = gaugeController.vote_user_slopes(user, bounty.gauge); if ( userSlope.slope == 0 || lastUserClaim[user][bountyId] >= currentPeriod || currentPeriod >= userSlope.end || currentPeriod <= lastVote || currentPeriod >= endTimestamp || currentPeriod < getActivePeriod(bountyId).timestamp || amountClaimed[bountyId] >= bounty.totalRewardAmount ) return 0; uint256 _rewardPerVote = rewardPerVote[bountyId]; // If period updated. if (_rewardPerVote == 0 || (_rewardPerVote > 0 && getActivePeriod(bountyId).timestamp != currentPeriod)) { uint256 _rewardPerPeriod; if (upgradedBounty.numberOfPeriods != 0) { // Update max reward per vote. bounty.maxRewardPerVote = upgradedBounty.maxRewardPerVote; bounty.totalRewardAmount = upgradedBounty.totalRewardAmount; } uint256 periodsLeft = endTimestamp > currentPeriod ? (endTimestamp - currentPeriod) / _WEEK : 0; _rewardPerPeriod = bounty.totalRewardAmount - amountClaimed[bountyId]; if (endTimestamp > currentPeriod + _WEEK && periodsLeft > 1) { _rewardPerPeriod = _rewardPerPeriod.mulDiv(1, periodsLeft); } // Get Adjusted Slope without blacklisted addresses weight. uint256 gaugeBias = _getAdjustedBias(bounty.gauge, bounty.blacklist, currentPeriod); _rewardPerVote = _rewardPerPeriod.mulDiv(_BASE_UNIT, gaugeBias); } // Get user voting power. uint256 _bias = _getAddrBias(userSlope.slope, userSlope.end, currentPeriod); // Estimation of the amount of rewards. amount = _bias.mulWad(_rewardPerVote); // Compute the reward amount based on // the max price to pay. uint256 _amountWithMaxPrice = _bias.mulWad(bounty.maxRewardPerVote); // Distribute the _min between the amount based on votes, and price. amount = FixedPointMathLib.min(amount, _amountWithMaxPrice); uint256 _amountClaimed = amountClaimed[bountyId]; // Update the amount claimed. if (amount + _amountClaimed > bounty.totalRewardAmount) { amount = bounty.totalRewardAmount - _amountClaimed; } // Substract fees. if (fee != 0) { amount = amount.mulWad(_BASE_UNIT - fee); } } //////////////////////////////////////////////////////////////// /// --- INTERNAL VIEWS /////////////////////////////////////////////////////////////// /// @notice Get adjusted slope from Gauge Controller for a given gauge address. /// Remove the weight of blacklisted addresses. /// @param gauge Address of the gauge. /// @param _addressesBlacklisted Array of blacklisted addresses. /// @param period Timestamp to check vote weight. function _getAdjustedBias(address gauge, address[] memory _addressesBlacklisted, uint256 period) internal view returns (uint256 gaugeBias) { // Cache the user slope. GaugeController.VotedSlope memory userSlope; // Bias uint256 _bias; // Last Vote uint256 _lastVote; // Cache the length of the array. uint256 length = _addressesBlacklisted.length; // Cache blacklist. // Get the gauge slope. gaugeBias = gaugeController.points_weight(gauge, period).bias; for (uint256 i = 0; i < length;) { // Get the user slope. userSlope = gaugeController.vote_user_slopes(_addressesBlacklisted[i], gauge); _lastVote = gaugeController.last_user_vote(_addressesBlacklisted[i], gauge); if (period > _lastVote) { _bias = _getAddrBias(userSlope.slope, userSlope.end, period); gaugeBias -= _bias; } // Increment i. unchecked { ++i; } } } //////////////////////////////////////////////////////////////// /// --- MANAGEMENT LOGIC /////////////////////////////////////////////////////////////// /// @notice Increase Bounty duration. /// @param _bountyId ID of the bounty. /// @param _additionnalPeriods Number of periods to add. /// @param _increasedAmount Total reward amount to add. /// @param _newMaxPricePerVote Total reward amount to add. function increaseBountyDuration( uint256 _bountyId, uint8 _additionnalPeriods, uint256 _increasedAmount, uint256 _newMaxPricePerVote ) external nonReentrant notKilled onlyManager(_bountyId) { if (!isUpgradeable[_bountyId]) revert NOT_UPGRADEABLE(); if (getPeriodsLeft(_bountyId) < 1) revert NO_PERIODS_LEFT(); if (_increasedAmount == 0 || _newMaxPricePerVote == 0) { revert WRONG_INPUT(); } Bounty storage bounty = bounties[_bountyId]; Upgrade memory upgradedBounty = upgradeBountyQueue[_bountyId]; SafeTransferLib.safeTransferFrom(bounty.rewardToken, msg.sender, address(this), _increasedAmount); if (upgradedBounty.totalRewardAmount != 0) { upgradedBounty = Upgrade({ numberOfPeriods: upgradedBounty.numberOfPeriods + _additionnalPeriods, totalRewardAmount: upgradedBounty.totalRewardAmount + _increasedAmount, maxRewardPerVote: _newMaxPricePerVote, endTimestamp: upgradedBounty.endTimestamp + (_additionnalPeriods * _WEEK) }); } else { upgradedBounty = Upgrade({ numberOfPeriods: bounty.numberOfPeriods + _additionnalPeriods, totalRewardAmount: bounty.totalRewardAmount + _increasedAmount, maxRewardPerVote: _newMaxPricePerVote, endTimestamp: bounty.endTimestamp + (_additionnalPeriods * _WEEK) }); } upgradeBountyQueue[_bountyId] = upgradedBounty; emit BountyDurationIncreaseQueued( _bountyId, upgradedBounty.numberOfPeriods, upgradedBounty.totalRewardAmount, _newMaxPricePerVote ); } /// @notice Close Bounty if there is remaining. /// @param bountyId ID of the bounty to close. function closeBounty(uint256 bountyId) external nonReentrant { // Check if the currentPeriod is the last one. // If not, we can increase the duration. Bounty storage bounty = bounties[bountyId]; if (bounty.manager == address(0)) revert ALREADY_CLOSED(); if (getCurrentPeriod() >= bounty.endTimestamp || isKilled) { uint256 leftOver; Upgrade memory upgradedBounty = upgradeBountyQueue[bountyId]; if (upgradedBounty.totalRewardAmount != 0) { leftOver = upgradedBounty.totalRewardAmount - amountClaimed[bountyId]; delete upgradeBountyQueue[bountyId]; } else { leftOver = bounties[bountyId].totalRewardAmount - amountClaimed[bountyId]; } // Transfer the left over to the owner. SafeTransferLib.safeTransfer(bounty.rewardToken, bounty.manager, leftOver); delete bounties[bountyId].manager; emit BountyClosed(bountyId, leftOver); } } /// @notice Update Bounty Manager. /// @param bountyId ID of the bounty. /// @param newManager Address of the new manager. function updateManager(uint256 bountyId, address newManager) external onlyManager(bountyId) { emit ManagerUpdated(bountyId, bounties[bountyId].manager = newManager); } //////////////////////////////////////////////////////////////// /// --- ONLY OWNER FUNCTIONS /////////////////////////////////////////////////////////////// /// @notice Claim fees. /// @param rewardTokens Array of reward tokens. function claimFees(address[] calldata rewardTokens) external nonReentrant { uint256 _feeAccrued; uint256 length = rewardTokens.length; for (uint256 i = 0; i < length;) { address rewardToken = rewardTokens[i]; _feeAccrued = feeAccrued[rewardToken]; delete feeAccrued[rewardToken]; emit FeesCollected(rewardToken, _feeAccrued); SafeTransferLib.safeTransfer(rewardToken, feeCollector, _feeAccrued); unchecked { i++; } } } /// @notice Set the platform fee. /// @param _platformFee Platform fee. function setPlatformFee(uint256 _platformFee) external onlyOwner { fee = _platformFee; emit FeeUpdated(_platformFee); } /// @notice Set the fee collector. /// @param _feeCollector Address of the fee collector. function setFeeCollector(address _feeCollector) external onlyOwner { feeCollector = _feeCollector; emit FeeCollectorUpdated(_feeCollector); } /// @notice Set the recipient for a given address. /// @param _for Address to set the recipient for. /// @param _recipient Address of the recipient. function setRecipientFor(address _for, address _recipient) external onlyOwner { recipient[_for] = _recipient; emit RecipientSet(_for, _recipient); } function kill() external onlyOwner { isKilled = true; } //////////////////////////////////////////////////////////////// /// --- UTILS FUNCTIONS /////////////////////////////////////////////////////////////// /// @notice Returns the number of periods left for a given bounty. /// @param bountyId ID of the bounty. function getPeriodsLeft(uint256 bountyId) public view returns (uint256 periodsLeft) { Bounty storage bounty = bounties[bountyId]; uint256 currentPeriod = getCurrentPeriod(); periodsLeft = bounty.endTimestamp > currentPeriod ? (bounty.endTimestamp - currentPeriod) / _WEEK : 0; } /// @notice Return the bounty object for a given ID. /// @param bountyId ID of the bounty. function getBounty(uint256 bountyId) external view returns (Bounty memory) { return bounties[bountyId]; } /// @notice Return the bounty in queue for a given ID. /// @dev Can return an empty bounty if there is no upgrade. /// @param bountyId ID of the bounty. function getUpgradedBountyQueued(uint256 bountyId) external view returns (Upgrade memory) { return upgradeBountyQueue[bountyId]; } /// @notice Return the blacklisted addresses of a bounty for a given ID. /// @param bountyId ID of the bounty. function getBlacklistedAddressesPerBounty(uint256 bountyId) external view returns (address[] memory) { return bounties[bountyId].blacklist; } /// @notice Return the active period running of bounty given an ID. /// @param bountyId ID of the bounty. function getActivePeriod(uint256 bountyId) public view returns (Period memory) { return activePeriod[bountyId]; } /// @notice Return the expected current period id. /// @param bountyId ID of the bounty. function getActivePeriodPerBounty(uint256 bountyId) public view returns (uint8) { Bounty storage bounty = bounties[bountyId]; uint256 currentPeriod = getCurrentPeriod(); uint256 periodsLeft = bounty.endTimestamp > currentPeriod ? (bounty.endTimestamp - currentPeriod) / _WEEK : 0; // If periodsLeft is superior, then the bounty didn't start yet. return uint8(periodsLeft > bounty.numberOfPeriods ? 0 : bounty.numberOfPeriods - periodsLeft); } /// @notice Return the current period based on Gauge Controller rounding. function getCurrentPeriod() public view returns (uint256) { return (block.timestamp / _WEEK) * _WEEK; } /// @notice Return the bias of a given address based on its lock end date and the current period. /// @param userSlope User slope. /// @param endLockTime Lock end date of the address. /// @param currentPeriod Current period. function _getAddrBias(uint256 userSlope, uint256 endLockTime, uint256 currentPeriod) internal pure returns (uint256) { if (currentPeriod + _WEEK >= endLockTime) return 0; return userSlope * (endLockTime - currentPeriod); } function getVersion() external pure returns (string memory) { return "2.1.0"; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Simple single owner authorization mixin. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Owned.sol) abstract contract Owned { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event OwnershipTransferred(address indexed user, address indexed newOwner); /*////////////////////////////////////////////////////////////// OWNERSHIP STORAGE //////////////////////////////////////////////////////////////*/ address public owner; modifier onlyOwner() virtual { require(msg.sender == owner, "UNAUTHORIZED"); _; } /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor(address _owner) { owner = _owner; emit OwnershipTransferred(address(0), _owner); } /*////////////////////////////////////////////////////////////// OWNERSHIP LOGIC //////////////////////////////////////////////////////////////*/ function transferOwnership(address newOwner) public virtual onlyOwner { owner = newOwner; emit OwnershipTransferred(msg.sender, newOwner); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol) /// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol) /// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it. abstract contract ERC20 { /*////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Transfer(address indexed from, address indexed to, uint256 amount); event Approval(address indexed owner, address indexed spender, uint256 amount); /*////////////////////////////////////////////////////////////// METADATA STORAGE //////////////////////////////////////////////////////////////*/ string public name; string public symbol; uint8 public immutable decimals; /*////////////////////////////////////////////////////////////// ERC20 STORAGE //////////////////////////////////////////////////////////////*/ uint256 public totalSupply; mapping(address => uint256) public balanceOf; mapping(address => mapping(address => uint256)) public allowance; /*////////////////////////////////////////////////////////////// EIP-2612 STORAGE //////////////////////////////////////////////////////////////*/ uint256 internal immutable INITIAL_CHAIN_ID; bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR; mapping(address => uint256) public nonces; /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ constructor( string memory _name, string memory _symbol, uint8 _decimals ) { name = _name; symbol = _symbol; decimals = _decimals; INITIAL_CHAIN_ID = block.chainid; INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator(); } /*////////////////////////////////////////////////////////////// ERC20 LOGIC //////////////////////////////////////////////////////////////*/ function approve(address spender, uint256 amount) public virtual returns (bool) { allowance[msg.sender][spender] = amount; emit Approval(msg.sender, spender, amount); return true; } function transfer(address to, uint256 amount) public virtual returns (bool) { balanceOf[msg.sender] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(msg.sender, to, amount); return true; } function transferFrom( address from, address to, uint256 amount ) public virtual returns (bool) { uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount; balanceOf[from] -= amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(from, to, amount); return true; } /*////////////////////////////////////////////////////////////// EIP-2612 LOGIC //////////////////////////////////////////////////////////////*/ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED"); // Unchecked because the only math done is incrementing // the owner's nonce which cannot realistically overflow. unchecked { address recoveredAddress = ecrecover( keccak256( abi.encodePacked( "\x19\x01", DOMAIN_SEPARATOR(), keccak256( abi.encode( keccak256( "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)" ), owner, spender, value, nonces[owner]++, deadline ) ) ) ), v, r, s ); require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER"); allowance[recoveredAddress][spender] = value; } emit Approval(owner, spender, value); } function DOMAIN_SEPARATOR() public view virtual returns (bytes32) { return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator(); } function computeDomainSeparator() internal view virtual returns (bytes32) { return keccak256( abi.encode( keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"), keccak256(bytes(name)), keccak256("1"), block.chainid, address(this) ) ); } /*////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint(address to, uint256 amount) internal virtual { totalSupply += amount; // Cannot overflow because the sum of all user // balances can't exceed the max uint256 value. unchecked { balanceOf[to] += amount; } emit Transfer(address(0), to, amount); } function _burn(address from, uint256 amount) internal virtual { balanceOf[from] -= amount; // Cannot underflow because a user's balance // will never be larger than the total supply. unchecked { totalSupply -= amount; } emit Transfer(from, address(0), amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Caution! This library won't check that a token has code, responsibility is delegated to the caller. library SafeTransferLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The ETH transfer has failed. error ETHTransferFailed(); /// @dev The ERC20 `transferFrom` has failed. error TransferFromFailed(); /// @dev The ERC20 `transfer` has failed. error TransferFailed(); /// @dev The ERC20 `approve` has failed. error ApproveFailed(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Suggested gas stipend for contract receiving ETH /// that disallows any storage writes. uint256 internal constant _GAS_STIPEND_NO_STORAGE_WRITES = 2300; /// @dev Suggested gas stipend for contract receiving ETH to perform a few /// storage reads and writes, but low enough to prevent griefing. /// Multiply by a small constant (e.g. 2), if needed. uint256 internal constant _GAS_STIPEND_NO_GRIEF = 100000; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ETH OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` (in wei) ETH to `to`. /// Reverts upon failure. function safeTransferETH(address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { // Transfer the ETH and check if it succeeded or not. if iszero(call(gas(), to, amount, 0, 0, 0, 0)) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`. /// The `gasStipend` can be set to a low enough value to prevent /// storage writes or gas griefing. /// /// If sending via the normal procedure fails, force sends the ETH by /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. /// /// Reverts if the current contract has insufficient balance. function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal { /// @solidity memory-safe-assembly assembly { // If insufficient balance, revert. if lt(selfbalance(), amount) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } // Transfer the ETH and check if it succeeded or not. if iszero(call(gasStipend, to, amount, 0, 0, 0, 0)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. // We can directly use `SELFDESTRUCT` in the contract creation. // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 pop(create(amount, 0x0b, 0x16)) } } } /// @dev Force sends `amount` (in wei) ETH to `to`, with a gas stipend /// equal to `_GAS_STIPEND_NO_GRIEF`. This gas stipend is a reasonable default /// for 99% of cases and can be overriden with the three-argument version of this /// function if necessary. /// /// If sending via the normal procedure fails, force sends the ETH by /// creating a temporary contract which uses `SELFDESTRUCT` to force send the ETH. /// /// Reverts if the current contract has insufficient balance. function forceSafeTransferETH(address to, uint256 amount) internal { // Manually inlined because the compiler doesn't inline functions with branches. /// @solidity memory-safe-assembly assembly { // If insufficient balance, revert. if lt(selfbalance(), amount) { // Store the function selector of `ETHTransferFailed()`. mstore(0x00, 0xb12d13eb) // Revert with (offset, size). revert(0x1c, 0x04) } // Transfer the ETH and check if it succeeded or not. if iszero(call(_GAS_STIPEND_NO_GRIEF, to, amount, 0, 0, 0, 0)) { mstore(0x00, to) // Store the address in scratch space. mstore8(0x0b, 0x73) // Opcode `PUSH20`. mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`. // We can directly use `SELFDESTRUCT` in the contract creation. // Compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758 pop(create(amount, 0x0b, 0x16)) } } } /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`. /// The `gasStipend` can be set to a low enough value to prevent /// storage writes or gas griefing. /// /// Simply use `gasleft()` for `gasStipend` if you don't need a gas stipend. /// /// Note: Does NOT revert upon failure. /// Returns whether the transfer of ETH is successful instead. function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal returns (bool success) { /// @solidity memory-safe-assembly assembly { // Transfer the ETH and check if it succeeded or not. success := call(gasStipend, to, amount, 0, 0, 0, 0) } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* ERC20 OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Sends `amount` of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferFrom(address token, address from, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x00, 0x23b872dd) mstore(0x20, from) // Store the `from` argument. mstore(0x40, to) // Store the `to` argument. mstore(0x60, amount) // Store the `amount` argument. if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends all of ERC20 `token` from `from` to `to`. /// Reverts upon failure. /// /// The `from` account must have at least `amount` approved for /// the current contract to manage. function safeTransferAllFrom(address token, address from, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { let m := mload(0x40) // Cache the free memory pointer. mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, from) // Store the `from` argument. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } // Store the function selector of `transferFrom(address,address,uint256)`. mstore(0x00, 0x23b872dd) mstore(0x40, to) // Store the `to` argument. // The `amount` argument is already written to the memory word at 0x6a. amount := mload(0x60) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20) ) ) { // Store the function selector of `TransferFromFailed()`. mstore(0x00, 0x7939f424) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x60, 0) // Restore the zero slot to zero. mstore(0x40, m) // Restore the free memory pointer. } } /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransfer(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x1a, to) // Store the `to` argument. mstore(0x3a, amount) // Store the `amount` argument. // Store the function selector of `transfer(address,uint256)`, // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer). // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL). mstore(0x00, 0xa9059cbb000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten, // which is guaranteed to be zero, if less than 8tb of memory is used. mstore(0x3a, 0) } } /// @dev Sends all of ERC20 `token` from the current contract to `to`. /// Reverts upon failure. function safeTransferAll(address token, address to) internal returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, address()) // Store the address of the current contract. if iszero( and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x3a, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } mstore(0x1a, to) // Store the `to` argument. // The `amount` argument is already written to the memory word at 0x3a. amount := mload(0x3a) // Store the function selector of `transfer(address,uint256)`, // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer). // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL). mstore(0x00, 0xa9059cbb000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `TransferFailed()`. mstore(0x00, 0x90b8ec18) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten, // which is guaranteed to be zero, if less than 8tb of memory is used. mstore(0x3a, 0) } } /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract. /// Reverts upon failure. function safeApprove(address token, address to, uint256 amount) internal { /// @solidity memory-safe-assembly assembly { mstore(0x1a, to) // Store the `to` argument. mstore(0x3a, amount) // Store the `amount` argument. // Store the function selector of `approve(address,uint256)`, // left by 6 bytes (enough for 8tb of memory represented by the free memory pointer). // We waste 6-3 = 3 bytes to save on 6 runtime gas (PUSH1 0x224 SHL). mstore(0x00, 0x095ea7b3000000000000) if iszero( and( // The arguments of `and` are evaluated from right to left. // Set success to whether the call reverted, if not we check it either // returned exactly 1 (can't just be non-zero data), or had no return data. or(eq(mload(0x00), 1), iszero(returndatasize())), call(gas(), token, 0, 0x16, 0x44, 0x00, 0x20) ) ) { // Store the function selector of `ApproveFailed()`. mstore(0x00, 0x3e3f8f73) // Revert with (offset, size). revert(0x1c, 0x04) } // Restore the part of the free memory pointer that was overwritten, // which is guaranteed to be zero, if less than 8tb of memory is used. mstore(0x3a, 0) } } /// @dev Returns the amount of ERC20 `token` owned by `account`. /// Returns zero if the `token` does not exist. function balanceOf(address token, address account) internal view returns (uint256 amount) { /// @solidity memory-safe-assembly assembly { mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`. mstore(0x20, account) // Store the `account` argument. amount := mul( mload(0x20), and( // The arguments of `and` are evaluated from right to left. gt(returndatasize(), 0x1f), // At least 32 bytes returned. staticcall(gas(), token, 0x1c, 0x24, 0x20, 0x20) ) ) } } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Gas optimized reentrancy protection for smart contracts. /// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/ReentrancyGuard.sol) /// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/security/ReentrancyGuard.sol) abstract contract ReentrancyGuard { uint256 private locked = 1; modifier nonReentrant() virtual { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.17; interface GaugeController { struct VotedSlope { uint256 slope; uint256 power; uint256 end; } struct Point { uint256 bias; uint256 slope; } function vote_user_slopes(address, address) external view returns (VotedSlope memory); function add_gauge(address, int128) external; function WEIGHT_VOTE_DELAY() external view returns (uint256); function last_user_vote(address, address) external view returns (uint256); function points_weight(address, uint256) external view returns (Point memory); function checkpoint_gauge(address) external; //solhint-disable-next-line function gauge_types(address addr) external view returns (int128); //solhint-disable-next-line function gauge_relative_weight_write(address addr, uint256 timestamp) external returns (uint256); //solhint-disable-next-line function gauge_relative_weight(address addr) external view returns (uint256); //solhint-disable-next-line function gauge_relative_weight(address addr, uint256 timestamp) external view returns (uint256); //solhint-disable-next-line function get_total_weight() external view returns (uint256); //solhint-disable-next-line function get_gauge_weight(address addr) external view returns (uint256); function vote_for_gauge_weights(address, uint256) external; function add_type(string memory, uint256) external; function admin() external view returns (address); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol) /// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol) library FixedPointMathLib { /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CUSTOM ERRORS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The operation failed, as the output exceeds the maximum value of uint256. error ExpOverflow(); /// @dev The operation failed, as the output exceeds the maximum value of uint256. error FactorialOverflow(); /// @dev The operation failed, due to an multiplication overflow. error MulWadFailed(); /// @dev The operation failed, either due to a /// multiplication overflow, or a division by a zero. error DivWadFailed(); /// @dev The multiply-divide operation failed, either due to a /// multiplication overflow, or a division by a zero. error MulDivFailed(); /// @dev The division failed, as the denominator is zero. error DivFailed(); /// @dev The full precision multiply-divide operation failed, either due /// to the result being larger than 256 bits, or a division by a zero. error FullMulDivFailed(); /// @dev The output is undefined, as the input is less-than-or-equal to zero. error LnWadUndefined(); /// @dev The output is undefined, as the input is zero. error Log2Undefined(); /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* CONSTANTS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev The scalar of ETH and most ERC20s. uint256 internal constant WAD = 1e18; /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* SIMPLIFIED FIXED POINT OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Equivalent to `(x * y) / WAD` rounded down. function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if mul(y, gt(x, div(not(0), y))) { // Store the function selector of `MulWadFailed()`. mstore(0x00, 0xbac65e5b) // Revert with (offset, size). revert(0x1c, 0x04) } z := div(mul(x, y), WAD) } } /// @dev Equivalent to `(x * y) / WAD` rounded up. function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`. if mul(y, gt(x, div(not(0), y))) { // Store the function selector of `MulWadFailed()`. mstore(0x00, 0xbac65e5b) // Revert with (offset, size). revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD)) } } /// @dev Equivalent to `(x * WAD) / y` rounded down. function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { // Store the function selector of `DivWadFailed()`. mstore(0x00, 0x7c5f487d) // Revert with (offset, size). revert(0x1c, 0x04) } z := div(mul(x, WAD), y) } } /// @dev Equivalent to `(x * WAD) / y` rounded up. function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`. if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) { // Store the function selector of `DivWadFailed()`. mstore(0x00, 0x7c5f487d) // Revert with (offset, size). revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y)) } } /// @dev Equivalent to `x` to the power of `y`. /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`. function powWad(int256 x, int256 y) internal pure returns (int256) { // Using `ln(x)` means `x` must be greater than 0. return expWad((lnWad(x) * y) / int256(WAD)); } /// @dev Returns `exp(x)`, denominated in `WAD`. function expWad(int256 x) internal pure returns (int256 r) { unchecked { // When the result is < 0.5 we return zero. This happens when // x <= floor(log(0.5e18) * 1e18) ~ -42e18 if (x <= -42139678854452767551) return r; /// @solidity memory-safe-assembly assembly { // When the result is > (2**255 - 1) / 1e18 we can not represent it as an // int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135. if iszero(slt(x, 135305999368893231589)) { // Store the function selector of `ExpOverflow()`. mstore(0x00, 0xa37bfec9) // Revert with (offset, size). revert(0x1c, 0x04) } } // x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96 // for more intermediate precision and a binary basis. This base conversion // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78. x = (x << 78) / 5 ** 18; // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers // of two such that exp(x) = exp(x') * 2**k, where k is an integer. // Solving this gives k = round(x / log(2)) and x' = x - k * log(2). int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96; x = x - k * 54916777467707473351141471128; // k is in the range [-61, 195]. // Evaluate using a (6, 7)-term rational approximation. // p is made monic, we'll multiply by a scale factor later. int256 y = x + 1346386616545796478920950773328; y = ((y * x) >> 96) + 57155421227552351082224309758442; int256 p = y + x - 94201549194550492254356042504812; p = ((p * y) >> 96) + 28719021644029726153956944680412240; p = p * x + (4385272521454847904659076985693276 << 96); // We leave p in 2**192 basis so we don't need to scale it back up for the division. int256 q = x - 2855989394907223263936484059900; q = ((q * x) >> 96) + 50020603652535783019961831881945; q = ((q * x) >> 96) - 533845033583426703283633433725380; q = ((q * x) >> 96) + 3604857256930695427073651918091429; q = ((q * x) >> 96) - 14423608567350463180887372962807573; q = ((q * x) >> 96) + 26449188498355588339934803723976023; /// @solidity memory-safe-assembly assembly { // Div in assembly because solidity adds a zero check despite the unchecked. // The q polynomial won't have zeros in the domain as all its roots are complex. // No scaling is necessary because p is already 2**96 too large. r := sdiv(p, q) } // r should be in the range (0.09, 0.25) * 2**96. // We now need to multiply r by: // * the scale factor s = ~6.031367120. // * the 2**k factor from the range reduction. // * the 1e18 / 2**96 factor for base conversion. // We do this all at once, with an intermediate result in 2**213 // basis, so the final right shift is always by a positive amount. r = int256( (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k) ); } } /// @dev Returns `ln(x)`, denominated in `WAD`. function lnWad(int256 x) internal pure returns (int256 r) { unchecked { /// @solidity memory-safe-assembly assembly { if iszero(sgt(x, 0)) { // Store the function selector of `LnWadUndefined()`. mstore(0x00, 0x1615e638) // Revert with (offset, size). revert(0x1c, 0x04) } } // We want to convert x from 10**18 fixed point to 2**96 fixed point. // We do this by multiplying by 2**96 / 10**18. But since // ln(x * C) = ln(x) + ln(C), we can simply do nothing here // and add ln(2**96 / 10**18) at the end. // Compute k = log2(x) - 96. int256 k; /// @solidity memory-safe-assembly assembly { let v := x k := shl(7, lt(0xffffffffffffffffffffffffffffffff, v)) k := or(k, shl(6, lt(0xffffffffffffffff, shr(k, v)))) k := or(k, shl(5, lt(0xffffffff, shr(k, v)))) // For the remaining 32 bits, use a De Bruijn lookup. // See: https://graphics.stanford.edu/~seander/bithacks.html v := shr(k, v) v := or(v, shr(1, v)) v := or(v, shr(2, v)) v := or(v, shr(4, v)) v := or(v, shr(8, v)) v := or(v, shr(16, v)) // forgefmt: disable-next-item k := sub(or(k, byte(shr(251, mul(v, shl(224, 0x07c4acdd))), 0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f)), 96) } // Reduce range of x to (1, 2) * 2**96 // ln(2^k * x) = k * ln(2) + ln(x) x <<= uint256(159 - k); x = int256(uint256(x) >> 159); // Evaluate using a (8, 8)-term rational approximation. // p is made monic, we will multiply by a scale factor later. int256 p = x + 3273285459638523848632254066296; p = ((p * x) >> 96) + 24828157081833163892658089445524; p = ((p * x) >> 96) + 43456485725739037958740375743393; p = ((p * x) >> 96) - 11111509109440967052023855526967; p = ((p * x) >> 96) - 45023709667254063763336534515857; p = ((p * x) >> 96) - 14706773417378608786704636184526; p = p * x - (795164235651350426258249787498 << 96); // We leave p in 2**192 basis so we don't need to scale it back up for the division. // q is monic by convention. int256 q = x + 5573035233440673466300451813936; q = ((q * x) >> 96) + 71694874799317883764090561454958; q = ((q * x) >> 96) + 283447036172924575727196451306956; q = ((q * x) >> 96) + 401686690394027663651624208769553; q = ((q * x) >> 96) + 204048457590392012362485061816622; q = ((q * x) >> 96) + 31853899698501571402653359427138; q = ((q * x) >> 96) + 909429971244387300277376558375; /// @solidity memory-safe-assembly assembly { // Div in assembly because solidity adds a zero check despite the unchecked. // The q polynomial is known not to have zeros in the domain. // No scaling required because p is already 2**96 too large. r := sdiv(p, q) } // r is in the range (0, 0.125) * 2**96 // Finalization, we need to: // * multiply by the scale factor s = 5.549… // * add ln(2**96 / 10**18) // * add k * ln(2) // * multiply by 10**18 / 2**96 = 5**18 >> 78 // mul s * 5e18 * 2**96, base is now 5**18 * 2**192 r *= 1677202110996718588342820967067443963516166; // add ln(2) * k * 5e18 * 2**192 r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k; // add ln(2**96 / 10**18) * 5e18 * 2**192 r += 600920179829731861736702779321621459595472258049074101567377883020018308; // base conversion: mul 2**18 / 2**192 r >>= 174; } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* GENERAL NUMBER UTILITIES */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Calculates `floor(a * b / d)` with full precision. /// Throws if result overflows a uint256 or when `d` is zero. /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { // forgefmt: disable-next-item for {} 1 {} { // 512-bit multiply `[prod1 prod0] = x * y`. // Compute the product mod `2**256` and mod `2**256 - 1` // then use the Chinese Remainder Theorem to reconstruct // the 512 bit result. The result is stored in two 256 // variables such that `product = prod1 * 2**256 + prod0`. // Least significant 256 bits of the product. let prod0 := mul(x, y) let mm := mulmod(x, y, not(0)) // Most significant 256 bits of the product. let prod1 := sub(mm, add(prod0, lt(mm, prod0))) // Handle non-overflow cases, 256 by 256 division. if iszero(prod1) { if iszero(d) { // Store the function selector of `FullMulDivFailed()`. mstore(0x00, 0xae47f702) // Revert with (offset, size). revert(0x1c, 0x04) } result := div(prod0, d) break } // Make sure the result is less than `2**256`. // Also prevents `d == 0`. if iszero(gt(d, prod1)) { // Store the function selector of `FullMulDivFailed()`. mstore(0x00, 0xae47f702) // Revert with (offset, size). revert(0x1c, 0x04) } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from `[prod1 prod0]`. // Compute remainder using mulmod. let remainder := mulmod(x, y, d) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) // Factor powers of two out of `d`. // Compute largest power of two divisor of `d`. // Always greater or equal to 1. let twos := and(d, sub(0, d)) // Divide d by power of two. d := div(d, twos) // Divide [prod1 prod0] by the factors of two. prod0 := div(prod0, twos) // Shift in bits from `prod1` into `prod0`. For this we need // to flip `twos` such that it is `2**256 / twos`. // If `twos` is zero, then it becomes one. prod0 := or(prod0, mul(prod1, add(div(sub(0, twos), twos), 1))) // Invert `d mod 2**256` // Now that `d` is an odd number, it has an inverse // modulo `2**256` such that `d * inv = 1 mod 2**256`. // Compute the inverse by starting with a seed that is correct // correct for four bits. That is, `d * inv = 1 mod 2**4`. let inv := xor(mul(3, d), 2) // Now use Newton-Raphson iteration to improve the precision. // Thanks to Hensel's lifting lemma, this also works in modular // arithmetic, doubling the correct bits in each step. inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64 inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128 result := mul(prod0, mul(inv, sub(2, mul(d, inv)))) // inverse mod 2**256 break } } } /// @dev Calculates `floor(x * y / d)` with full precision, rounded up. /// Throws if result overflows a uint256 or when `d` is zero. /// Credit to Uniswap-v3-core under MIT license: /// https://github.com/Uniswap/v3-core/blob/contracts/libraries/FullMath.sol function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) { result = fullMulDiv(x, y, d); /// @solidity memory-safe-assembly assembly { if mulmod(x, y, d) { if iszero(add(result, 1)) { // Store the function selector of `FullMulDivFailed()`. mstore(0x00, 0xae47f702) // Revert with (offset, size). revert(0x1c, 0x04) } result := add(result, 1) } } } /// @dev Returns `floor(x * y / d)`. /// Reverts if `x * y` overflows, or `d` is zero. function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { // Store the function selector of `MulDivFailed()`. mstore(0x00, 0xad251c27) // Revert with (offset, size). revert(0x1c, 0x04) } z := div(mul(x, y), d) } } /// @dev Returns `ceil(x * y / d)`. /// Reverts if `x * y` overflows, or `d` is zero. function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y)) if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) { // Store the function selector of `MulDivFailed()`. mstore(0x00, 0xad251c27) // Revert with (offset, size). revert(0x1c, 0x04) } z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d)) } } /// @dev Returns `ceil(x / d)`. /// Reverts if `d` is zero. function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { if iszero(d) { // Store the function selector of `DivFailed()`. mstore(0x00, 0x65244e4e) // Revert with (offset, size). revert(0x1c, 0x04) } z := add(iszero(iszero(mod(x, d))), div(x, d)) } } /// @dev Returns `max(0, x - y)`. function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mul(gt(x, y), sub(x, y)) } } /// @dev Returns the square root of `x`. function sqrt(uint256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`. z := 181 // The "correct" value is 1, but this saves a multiplication later. // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically. // Let `y = x / 2**r`. // We check `y >= 2**(k + 8)` but shift right by `k` bits // each branch to ensure that if `x >= 256`, then `y >= 256`. let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffffff, shr(r, x)))) r := or(r, shl(4, lt(0xffffff, shr(r, x)))) z := shl(shr(1, r), z) // Goal was to get `z*z*y` within a small factor of `x`. More iterations could // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`. // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small. // That's not possible if `x < 256` but we can just verify those cases exhaustively. // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`. // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`. // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps. // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)` // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`, // with largest error when `s = 1` and when `s = 256` or `1/256`. // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`. // Then we can estimate `sqrt(y)` using // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`. // There is no overflow risk here since `y < 2**136` after the first branch above. z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181. // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough. z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) z := shr(1, add(z, div(x, z))) // If `x+1` is a perfect square, the Babylonian method cycles between // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor. // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division // Since the ceil is rare, we save gas on the assignment and repeat division in the rare case. // If you don't care whether the floor or ceil square root is returned, you can remove this statement. z := sub(z, lt(div(x, z), z)) } } /// @dev Returns the factorial of `x`. function factorial(uint256 x) internal pure returns (uint256 result) { /// @solidity memory-safe-assembly assembly { for {} 1 {} { if iszero(lt(10, x)) { // forgefmt: disable-next-item result := and( shr(mul(22, x), 0x375f0016260009d80004ec0002d00001e0000180000180000200000400001), 0x3fffff ) break } if iszero(lt(57, x)) { let end := 31 result := 8222838654177922817725562880000000 if iszero(lt(end, x)) { end := 10 result := 3628800 } for { let w := not(0) } 1 {} { result := mul(result, x) x := add(x, w) if eq(x, end) { break } } break } // Store the function selector of `FactorialOverflow()`. mstore(0x00, 0xaba0f2a2) // Revert with (offset, size). revert(0x1c, 0x04) } } } /// @dev Returns the log2 of `x`. /// Equivalent to computing the index of the most significant bit (MSB) of `x`. function log2(uint256 x) internal pure returns (uint256 r) { /// @solidity memory-safe-assembly assembly { if iszero(x) { // Store the function selector of `Log2Undefined()`. mstore(0x00, 0x5be3aa5c) // Revert with (offset, size). revert(0x1c, 0x04) } r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x)) r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x)))) r := or(r, shl(5, lt(0xffffffff, shr(r, x)))) // For the remaining 32 bits, use a De Bruijn lookup. // See: https://graphics.stanford.edu/~seander/bithacks.html x := shr(r, x) x := or(x, shr(1, x)) x := or(x, shr(2, x)) x := or(x, shr(4, x)) x := or(x, shr(8, x)) x := or(x, shr(16, x)) // forgefmt: disable-next-item r := or(r, byte(shr(251, mul(x, shl(224, 0x07c4acdd))), 0x0009010a0d15021d0b0e10121619031e080c141c0f111807131b17061a05041f)) } } /// @dev Returns the log2 of `x`, rounded up. function log2Up(uint256 x) internal pure returns (uint256 r) { unchecked { uint256 isNotPo2; assembly { isNotPo2 := iszero(iszero(and(x, sub(x, 1)))) } return log2(x) + isNotPo2; } } /// @dev Returns the average of `x` and `y`. function avg(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = (x & y) + ((x ^ y) >> 1); } } /// @dev Returns the average of `x` and `y`. function avg(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = (x >> 1) + (y >> 1) + (((x & 1) + (y & 1)) >> 1); } } /// @dev Returns the absolute value of `x`. function abs(int256 x) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let mask := sub(0, shr(255, x)) z := xor(mask, add(mask, x)) } } /// @dev Returns the absolute distance between `x` and `y`. function dist(int256 x, int256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { let a := sub(y, x) z := xor(a, mul(xor(a, sub(x, y)), sgt(x, y))) } } /// @dev Returns the minimum of `x` and `y`. function min(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), lt(y, x))) } } /// @dev Returns the minimum of `x` and `y`. function min(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), slt(y, x))) } } /// @dev Returns the maximum of `x` and `y`. function max(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), gt(y, x))) } } /// @dev Returns the maximum of `x` and `y`. function max(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := xor(x, mul(xor(x, y), sgt(y, x))) } } /// @dev Returns `x`, bounded to `minValue` and `maxValue`. function clamp(uint256 x, uint256 minValue, uint256 maxValue) internal pure returns (uint256 z) { z = min(max(x, minValue), maxValue); } /// @dev Returns `x`, bounded to `minValue` and `maxValue`. function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) { z = min(max(x, minValue), maxValue); } /// @dev Returns greatest common divisor of `x` and `y`. function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { // forgefmt: disable-next-item for { z := x } y {} { let t := y y := mod(z, y) z := t } } } /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ /* RAW NUMBER OPERATIONS */ /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ /// @dev Returns `x + y`, without checking for overflow. function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x + y; } } /// @dev Returns `x + y`, without checking for overflow. function rawAdd(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x + y; } } /// @dev Returns `x - y`, without checking for underflow. function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x - y; } } /// @dev Returns `x - y`, without checking for underflow. function rawSub(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x - y; } } /// @dev Returns `x * y`, without checking for overflow. function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) { unchecked { z = x * y; } } /// @dev Returns `x * y`, without checking for overflow. function rawMul(int256 x, int256 y) internal pure returns (int256 z) { unchecked { z = x * y; } } /// @dev Returns `x / y`, returning 0 if `y` is zero. function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := div(x, y) } } /// @dev Returns `x / y`, returning 0 if `y` is zero. function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := sdiv(x, y) } } /// @dev Returns `x % y`, returning 0 if `y` is zero. function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mod(x, y) } } /// @dev Returns `x % y`, returning 0 if `y` is zero. function rawSMod(int256 x, int256 y) internal pure returns (int256 z) { /// @solidity memory-safe-assembly assembly { z := smod(x, y) } } /// @dev Returns `(x + y) % d`, return 0 if `d` if zero. function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := addmod(x, y, d) } } /// @dev Returns `(x * y) % d`, return 0 if `d` if zero. function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) { /// @solidity memory-safe-assembly assembly { z := mulmod(x, y, d) } } }
{ "remappings": [ "ds-test/=lib/forge-std/lib/ds-test/src/", "forge-std/=lib/forge-std/src/", "solady/=lib/solady/src/", "solmate/=lib/solmate/src/" ], "optimizer": { "enabled": true, "runs": 99999999 }, "metadata": { "bytecodeHash": "ipfs" }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "evmVersion": "london", "viaIR": true, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
[{"inputs":[{"internalType":"address","name":"_gaugeController","type":"address"},{"internalType":"address","name":"_feeCollector","type":"address"},{"internalType":"address","name":"_owner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ALREADY_CLOSED","type":"error"},{"inputs":[],"name":"AUTH_MANAGER_ONLY","type":"error"},{"inputs":[],"name":"INVALID_NUMBER_OF_PERIODS","type":"error"},{"inputs":[],"name":"KILLED","type":"error"},{"inputs":[],"name":"NOT_UPGRADEABLE","type":"error"},{"inputs":[],"name":"NO_PERIODS_LEFT","type":"error"},{"inputs":[],"name":"WRONG_INPUT","type":"error"},{"inputs":[],"name":"ZERO_ADDRESS","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingReward","type":"uint256"}],"name":"BountyClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"gauge","type":"address"},{"indexed":false,"internalType":"address","name":"manager","type":"address"},{"indexed":false,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint8","name":"numberOfPeriods","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"maxRewardPerVote","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardPerPeriod","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalRewardAmount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isUpgradeable","type":"bool"}],"name":"BountyCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"numberOfPeriods","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"totalRewardAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxRewardPerVote","type":"uint256"}],"name":"BountyDurationIncrease","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"numberOfPeriods","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"totalRewardAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxRewardPerVote","type":"uint256"}],"name":"BountyDurationIncreaseQueued","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":true,"internalType":"uint256","name":"bountyId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"protocolFees","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"period","type":"uint256"}],"name":"Claimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"feeCollector","type":"address"}],"name":"FeeCollectorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"}],"name":"FeeUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"rewardToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FeesCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"manager","type":"address"}],"name":"ManagerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"periodId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"rewardPerPeriod","type":"uint256"}],"name":"PeriodRolledOver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"}],"name":"RecipientSet","type":"event"},{"inputs":[],"name":"MINIMUM_PERIOD","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"activePeriod","outputs":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"rewardPerPeriod","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"amountClaimed","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"bounties","outputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint8","name":"numberOfPeriods","type":"uint8"},{"internalType":"uint256","name":"endTimestamp","type":"uint256"},{"internalType":"uint256","name":"maxRewardPerVote","type":"uint256"},{"internalType":"uint256","name":"totalRewardAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"claim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"claimAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"claimAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"claimAllFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"rewardTokens","type":"address[]"}],"name":"claimFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"claimFor","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"claimable","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"closeBounty","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint8","name":"numberOfPeriods","type":"uint8"},{"internalType":"uint256","name":"maxRewardPerVote","type":"uint256"},{"internalType":"uint256","name":"totalRewardAmount","type":"uint256"},{"internalType":"address[]","name":"blacklist","type":"address[]"},{"internalType":"bool","name":"upgradeable","type":"bool"}],"name":"createBounty","outputs":[{"internalType":"uint256","name":"newBountyId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fee","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"feeAccrued","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeCollector","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"gaugeController","outputs":[{"internalType":"contract GaugeController","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"getActivePeriod","outputs":[{"components":[{"internalType":"uint8","name":"id","type":"uint8"},{"internalType":"uint256","name":"timestamp","type":"uint256"},{"internalType":"uint256","name":"rewardPerPeriod","type":"uint256"}],"internalType":"struct Platform.Period","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"getActivePeriodPerBounty","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"getBlacklistedAddressesPerBounty","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"getBounty","outputs":[{"components":[{"internalType":"address","name":"gauge","type":"address"},{"internalType":"address","name":"manager","type":"address"},{"internalType":"address","name":"rewardToken","type":"address"},{"internalType":"uint8","name":"numberOfPeriods","type":"uint8"},{"internalType":"uint256","name":"endTimestamp","type":"uint256"},{"internalType":"uint256","name":"maxRewardPerVote","type":"uint256"},{"internalType":"uint256","name":"totalRewardAmount","type":"uint256"},{"internalType":"address[]","name":"blacklist","type":"address[]"}],"internalType":"struct Platform.Bounty","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"getPeriodsLeft","outputs":[{"internalType":"uint256","name":"periodsLeft","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"getUpgradedBountyQueued","outputs":[{"components":[{"internalType":"uint8","name":"numberOfPeriods","type":"uint8"},{"internalType":"uint256","name":"totalRewardAmount","type":"uint256"},{"internalType":"uint256","name":"maxRewardPerVote","type":"uint256"},{"internalType":"uint256","name":"endTimestamp","type":"uint256"}],"internalType":"struct Platform.Upgrade","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersion","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"_bountyId","type":"uint256"},{"internalType":"uint8","name":"_additionnalPeriods","type":"uint8"},{"internalType":"uint256","name":"_increasedAmount","type":"uint256"},{"internalType":"uint256","name":"_newMaxPricePerVote","type":"uint256"}],"name":"increaseBountyDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"isBlacklisted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isKilled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"isUpgradeable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"kill","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"lastUserClaim","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextID","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"recipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"rewardPerVote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_feeCollector","type":"address"}],"name":"setFeeCollector","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_platformFee","type":"uint256"}],"name":"setPlatformFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_recipient","type":"address"}],"name":"setRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_for","type":"address"},{"internalType":"address","name":"_recipient","type":"address"}],"name":"setRecipientFor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"}],"name":"updateBountyPeriod","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"ids","type":"uint256[]"}],"name":"updateBountyPeriods","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"bountyId","type":"uint256"},{"internalType":"address","name":"newManager","type":"address"}],"name":"updateManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"upgradeBountyQueue","outputs":[{"internalType":"uint8","name":"numberOfPeriods","type":"uint8"},{"internalType":"uint256","name":"totalRewardAmount","type":"uint256"},{"internalType":"uint256","name":"maxRewardPerVote","type":"uint256"},{"internalType":"uint256","name":"endTimestamp","type":"uint256"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a0346200011957601f62003c5538819003918201601f19168301916001600160401b038311848410176200011e57808492606094604052833981010312620001195760406200004f8262000134565b6200005d6020840162000134565b926001600160a01b0392839162000075910162000134565b16928260018060a01b031991858360005416176000556040519560007f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08180a36001805566470de4df8200006003551690600554161760055516608052613b0b90816200014a8239608051818181610daa015281816121bf015281816125df0152818161286801528181612ca001528181612d41015281816130db01526134f10152f35b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001195756fe604060a081526004908136101561001557600080fd5b600091823560e01c918263086146d214611c7b5782630888dc9014611b6d578263091cc55814611b025782630d8e6e2c14611a375782630de05659146119bc57826312e8e2c3146119365782631e96917d146118fa57826321bf936a1461189b578263228c076c1461184057826328c77820146117c4578263379607f5146117855782633ad86d721461173f5782633bbed4a01461169357826341c0e1b51461161457826343cd235a1461159a5782634ae001d8146115295782634fcf04f9146114be57826351cd41e8146114845782635749a07a146114455782635d34625d146113e857826360efe334146113a15782636b5646aa14611364578263725acc6b146112715782638c8f623b14610ea85782638da5cb5b14610e575782638fac393714610e105782638fe8a10114610dce57826399eecb3b14610d5f578263a42dce8014610caa578263aca47b7d14610c5f578263b3651eea14610bfa578263b6b6e3c814610b35578263b988997f14610931578263c415b95c146108de578263c7da33281461087c578263d1d1bb4f14610836578263d3c16e3014610788578263dc2f8744146106e4578263dd47a77f14610660578263ddca3f431461061f578263ddd5e1b2146105d3578263de4aaaf414610511578263ee8c4bbf146103c0578263ef2c4082146102c457505063f2fde38b1461021357600080fd5b346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1577fffffffffffffffffffffffff000000000000000000000000000000000000000061026b611cb6565b82549073ffffffffffffffffffffffffffffffffffffffff906102918284163314611d7c565b1691829116178255337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b80fd5b909150346103bc57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc57813590610300611cde565b92828552600660205273ffffffffffffffffffffffffffffffffffffffff9081600184882001541633036103955750916020917f5baaf19ee8739d1720c1401554ff4abe5682505ec43d3f2eb61b9dfc0abd97459382875260068452600182882001951694857fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905551908152a280f35b82517ffabbbc67000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b9150346103bc57602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c157908261050d92606060e0835161040881611ead565b8381528389820152838582015283838201528360808201528360a08201528360c0820152015282358152600686522090835191829161044683611ead565b60ff73ffffffffffffffffffffffffffffffffffffffff9687845416855287806001860154168a8701908152600286015492828189019281861684528660608b019660a01c1686526104bd600660038b01549960808d019a8b528b01549a60a08d019b8c5260c060058201549d019c8d5201612ab4565b9a60e081019b8c5283519e8f9e8f9282845251169101525116908b015251166060890152511660808701525160a08601525160c08501525160e08401525161010080840152610120830190611d32565b0390f35b9150346103bc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc57610549611cb6565b906024359067ffffffffffffffff82116105cf5761056991369101611d01565b909273ffffffffffffffffffffffffffffffffffffffff908184168652600760205285205416908490821515915b8181106105a2578680f35b806105c26105b3600193858a611f8b565b3585156105c957865b88612080565b5001610597565b876105bc565b8480fd5b83346102c157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1575061061860209235610612611cde565b33612080565b9051908152f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020906003549051908152f35b5080fd5b909150346103bc577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5780359067ffffffffffffffff82116103bc576106ae91369101611d01565b6106b6611cde565b90835b8181106106c4578480f35b806106dd6106d56001938588611f8b565b358533612080565b50016106b9565b909150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc578060e0938335815260066020522073ffffffffffffffffffffffffffffffffffffffff9260ff848354169480600185015416946002850154916005600387015495870154960154968251988952602089015282169087015260a01c166060850152608084015260a083015260c0820152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc57828291608094606084516107cc81611e91565b82815282602082015282868201520152358152600d60205220908051906107f282611e91565b60ff83541692838352600181015460208401908152606060036002840154938587019485520154940193845282519485525160208501525190830152516060820152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc576020928291358152600b845220549051908152f35b83903461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c578060209273ffffffffffffffffffffffffffffffffffffffff6108ce611cb6565b1681526008845220549051908152f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5760209073ffffffffffffffffffffffffffffffffffffffff600554169051908152f35b909150346103bc57602090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610b3157823560019361097685805414611de1565b6002855581865260068452828620938585019473ffffffffffffffffffffffffffffffffffffffff928387541615610b0a5750908792916109b5613437565b600382015411801590610afe575b6109cf575b8388805580f35b7fe44903a04328507d74e4709cbebc6c819f9f1cd0b7cb171ba22814fcc18ecf389686938692838752600d85528a8688208751610a0b81611e91565b60ff82541681528282015490606060038a83019484865260028101548d85015201549101521515600014610acb57918860036002938a9b610a5e8a8c9d600b610a849e8c9b519386525283205490612109565b9c8d9a8252600d8d52812091818355820155828582015501555b01541691541690612528565b8287526006815285848820017fffffffffffffffffffffffff000000000000000000000000000000000000000081541690558351928352820152a1388080848180806109c8565b505092610a84949550600296526006855280610af68c8a6005818320015491600b8a52205490612109565b968794610a78565b5060ff600254166109c3565b85517f29606baf000000000000000000000000000000000000000000000000000000008152fd5b8380fd5b83903461065c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c57610b6d611cb6565b90610b76611cde565b9173ffffffffffffffffffffffffffffffffffffffff8091610b9c828754163314611d7c565b169182855260076020528420921691827fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790557fc1416b5cdab50a9fbc872236e1aa54566c6deb40024e63a4b1737ecacf09d6f98380a380f35b83903461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020918173ffffffffffffffffffffffffffffffffffffffff9182610c4e611cb6565b168152600785522054169051908152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc578160209360ff923581526009855220541690519015158152f35b83903461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5760207fe5693914d19c789bdee50a362998c0bc8d035a835f9871da5d51152f0582c34f91610d07611cb6565b73ffffffffffffffffffffffffffffffffffffffff90610d2b828754163314611d7c565b1690817fffffffffffffffffffffffff0000000000000000000000000000000000000000600554161760055551908152a180f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5760209060ff6002541690519015158152f35b83346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1575060ff610e4f602093356133cf565b915191168152f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5773ffffffffffffffffffffffffffffffffffffffff60209254169051908152f35b83903461065c5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5782356024359060ff928383169384840361126d576044359160643592600196610f0488805414611de1565b600288558360025416611245578589526020966006885273ffffffffffffffffffffffffffffffffffffffff808a868d20015416330361121d57878b526009895285858c205416156111f55789610f5a89613352565b106111cd57831580156111c5575b61119d57908a868a94938a8352600686528c60038980862095600d8a52208a5192610f9284611e91565b85825416845281015498830198895260028101548b8401520154606082015295610fc788600286019384541630903390612038565b51156111065750610fec9150606092610fe291865116611f3b565b948a850151611f7e565b92015162093a80918281029281840414901517156110da577fbc253f6a158b7f3743255b0764f4408f10f66d26d277929a6bbe1bdf4fd76638999a9b506110d296959493929161103b91611f7e565b908484519361104985611e91565b1683528883015284838301526060820152915b858a52600d875260036060838c209285511698897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0085541617845585015194858c850155848101516002850155015191015551948594859094939260ff6060936080840197845216602083015260408201520152565b0390a1805580f35b60248b60118e7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b6111299295509261111e916003945460a01c16611f3b565b946005850154611f7e565b92015462093a80918281029281840414901517156110da577fbc253f6a158b7f3743255b0764f4408f10f66d26d277929a6bbe1bdf4fd76638999a9b506110d296959493929161117891611f7e565b908484519361118685611e91565b16835288830152848383015260608201529161105c565b8b85517f56e43e7f000000000000000000000000000000000000000000000000000000008152fd5b508615610f68565b8b85517f3bad1a57000000000000000000000000000000000000000000000000000000008152fd5b8b85517fdc370e01000000000000000000000000000000000000000000000000000000008152fd5b8b85517ffabbbc67000000000000000000000000000000000000000000000000000000008152fd5b8983517f63d81431000000000000000000000000000000000000000000000000000000008152fd5b8580fd5b9150346103bc57602090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610b315780359067ffffffffffffffff82116105cf576112c291369101611d01565b9290916001936112d485805414611de1565b6002855584865b8281106112e9578782805580f35b61135c6112ff6112fa83868a611f8b565b611fca565b73ffffffffffffffffffffffffffffffffffffffff9081811691828c5260088852888c2054928c8a8120557f9dc46f23cfb5ddcad0ae7ea2be38d47fec07bb9382ec7e564efc69e036dd66ce898b51868152a26005541690612528565b0185906112db565b83346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1575061061860209235613352565b83903461065c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020906106186113df611cb6565b60243590612b1b565b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc5760068261050d9461143293358152826020522001612ab4565b9051918291602083526020830190611d32565b83823461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c57611480903561256b565b5080f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020905160028152f35b83903461065c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c578060209273ffffffffffffffffffffffffffffffffffffffff61150f611cb6565b168152600f84528181206024358252845220549051908152f35b9150346103bc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc578160209360ff92611568611cde565b90358252600e865273ffffffffffffffffffffffffffffffffffffffff83832091168252855220541690519015158152f35b83823461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5780359067ffffffffffffffff82116103bc576115e991369101611d01565b825b8181106115f6578380f35b8061160d6116076001938587611f8b565b3561256b565b50016115eb565b83346102c157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c15761166573ffffffffffffffffffffffffffffffffffffffff8254163314611d7c565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00600254161760025580f35b83903461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5773ffffffffffffffffffffffffffffffffffffffff6116e1611cb6565b9133845260076020528320911690817fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055337fc1416b5cdab50a9fbc872236e1aa54566c6deb40024e63a4b1737ecacf09d6f98380a380f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc576020928291358152600c845220549051908152f35b83346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c15750610618602092353333612080565b83823461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5780359067ffffffffffffffff82116103bc5761181391369101611d01565b825b818110611820578380f35b806118396118316001938587611f8b565b353333612080565b5001611815565b83346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1575061187d6060923561338f565b908080519260ff815116845260208101516020850152015190820152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc576060928291358152600a6020522060ff81541691600260018301549201549181519384526020840152820152f35b9150346103bc57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc5760209250549051908152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc577f8c4d35e54a3f2ef1134138fd8ea3daee6a3c89e10d2665996babdf70261e2c769160209135906119b073ffffffffffffffffffffffffffffffffffffffff8654163314611d7c565b8160035551908152a180f35b83903461065c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576106186020926119fa611cb6565b73ffffffffffffffffffffffffffffffffffffffff80821683526007865291849020549091168015611a30575b60243591612080565b5080611a27565b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c578051611a7281611e46565b600581526020907f322e312e300000000000000000000000000000000000000000000000000000008282015282519382859384528251928382860152825b848110611aec57505050828201840152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168101030190f35b8181018301518882018801528795508201611ab0565b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc576080928291358152600d602052209060ff825416916001810154916003600283015492015492815194855260208501528301526060820152f35b83346102c1576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c157611ba6611cb6565b90611baf611cde565b6044359273ffffffffffffffffffffffffffffffffffffffff841684036103bc5760643560ff81168103610b315760c43567ffffffffffffffff81116105cf57611bfc9036908901611d01565b95909460e435988915158a0361065c57611c196001805414611de1565b600260015560ff60025416611c54575091602098959391611c4697959360805260a435936084359361348b565b608051906001805551908152f35b88517f63d81431000000000000000000000000000000000000000000000000000000008152fd5b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c57602090610618613437565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203611cd957565b600080fd5b6024359073ffffffffffffffffffffffffffffffffffffffff82168203611cd957565b9181601f84011215611cd95782359167ffffffffffffffff8311611cd9576020808501948460051b010111611cd957565b90815180825260208080930193019160005b828110611d52575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611d44565b15611d8357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b15611de857565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152fd5b6040810190811067ffffffffffffffff821117611e6257604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff821117611e6257604052565b610100810190811067ffffffffffffffff821117611e6257604052565b6060810190811067ffffffffffffffff821117611e6257604052565b67ffffffffffffffff8111611e6257604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611e6257604052565b9060ff8091169116019060ff8211611f4f57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b91908201809211611f4f57565b9190811015611f9b5760051b0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b3573ffffffffffffffffffffffffffffffffffffffff81168103611cd95790565b8115611ff5570490565b63ad251c276000526004601cfd5b670de0b6b3a7640000907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111820215830215611ff557020490565b92916064601c60209360405196600096879586936323b872dd855288526040526060525af13d15600183511417161561207357606052604052565b637939f42490526004601cfd5b919061208f6001805414611de1565b600260015560ff600254166120ae576120a792612116565b9060018055565b60046040517f63d81431000000000000000000000000000000000000000000000000000000008152fd5b90816060910312611cd95760408051916120f183611eca565b80518352602081015160208401520151604082015290565b91908203918211611f4f57565b929190600090828252602090600e82526040928381209373ffffffffffffffffffffffffffffffffffffffff80891695868452855260ff82842054166124d45761215f8761256b565b87845260068652828420805484517f7e418fa000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d811660048301529185169182166024820152919b9296927f00000000000000000000000000000000000000000000000000000000000000008516908984604481855afa9384156124ca578894612496575b5086517f0f467f9800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015292166024830152909190606090839060449082905afa91821561248c57869261245c575b5081511590811561243e575b8115612430575b8115612425575b508015612417575b8015612406575b80156123ef575b6123e257856122b791898752600f89528587208b88528952818688205585815191015190613451565b888552600c87526122da6122ce85872054836124df565b9160048d0154906124df565b818110908218021899888552600b8752838520546122f8818d611f7e565b60058301548091116123cf575b5050888552600b875283852061231c8c8254611f7e565b9055846003548061237c575b50507f6f9c9826be5976f3f82a3490c52a83328ce2ec7be9e62dcb39c26da5148d7c7696949260809694926123668d60028195019284845416612528565b54169482519586528501528301526060820152a3565b85989650612391908d9592939896949d6124df565b9b8c94859d61239f91612109565b9c8d998a948460028c015416815260088a5220908154906123bf91611f7e565b9055929496918194969850612328565b6123da929c50612109565b993880612305565b5092985050505050505050565b50888552600b87528385205460058c01541461228e565b5061240f613437565b861415612287565b5060038b0154861015612280565b905086111538612278565b828601518810159150612271565b9050888652600f88528486208a87528852868587205410159061226a565b61247e91925060603d8111612485575b6124768183611efa565b8101906120d8565b903861225e565b503d61246c565b85513d88823e3d90fd5b9093508981813d83116124c3575b6124ae8183611efa565b810103126124bf57519260606121f7565b8780fd5b503d6124a4565b87513d8a823e3d90fd5b509096505050505050565b90807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211810261251a57670de0b6b3a764000091020490565b63bac65e5b6000526004601cfd5b601692602092601a52603a52604460009384809369a9059cbb00000000000082525af13d15600183511417161561255e57603a52565b6390b8ec1890526004601cfd5b600090808252602090600a825260409182842092612587613437565b9360ff8154161580612957575b612831575b6001019485549262093a8093848101809111612804574210156125c0575050505050505490565b9091929394955073ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000000000000000000000000000000000000000000001686845260068352818585205416813b156105cf57849160248392885194859384927f615e523700000000000000000000000000000000000000000000000000000000845260048401525af180156127fa579084916127e6575b505061266386612964565b61266c866133cf565b90868452600683528484209061268188613352565b61269a60058401548a8852600b87528888205490612109565b976003840154908b0190818c116127b957927fb77c22cd311809931524bcc8d4a33a61a392e2304c8a7d476b64018e11ed6cb19a98959260809a9895928d989511806127af575b61278b575b5086826126ff60066127059461270b9654169201612ab4565b906130b3565b87612003565b878452600c835284842055600260ff85519261272684611eca565b169384835283830187815260ff87808601938b85528c8152600a8852209451167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008554161784555160018401555191015582519586528501528301526060820152a190565b61270b926126ff60066127a38b94612705969d611feb565b9b9450505092506126e6565b50600181116126e1565b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6127ef90611ee6565b6103bc578238612658565b85513d86823e3d90fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b61283a84612964565b838652600c835281862054612599576006835281862073ffffffffffffffffffffffffffffffffffffffff807f00000000000000000000000000000000000000000000000000000000000000001681835416813b1561295357899160248392885194859384927f615e523700000000000000000000000000000000000000000000000000000000845260048401525af1801561294957612924575b5086826126ff60066128ee946001979654169201612ab4565b806128fc575b509050612599565b61291390868952600a86526002858a200154612003565b858852600c855283882055386128f4565b600193926126ff60066128ee949b61293c8c95611ee6565b9b945050509293506128d5565b85513d8b823e3d90fd5b8980fd5b5060018101548514612594565b600090808252600d602052604080832091600183018054612987575b5050505050565b837f2e363750a1295e75baeb02bb4277be5bbd051627ff6fa2edae8a99c91ddf5ef1916003809601546006602052868689200155612a0f60ff8354166002878a2001907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000083549260a01b169116179055565b612a6a60ff600284019384546004898c20015583548060058a8d200155600a60205282898c20541615612a91575b50541691549254865193849387859094939260ff6060936080840197845216602083015260408201520152565b0390a18352600d602052822082815582600182015582600282015501553880808080612980565b612a9f908383541690611feb565b878b52600a6020526002898c20015538612a3d565b9060405191828154918282526020928383019160005283600020936000905b828210612aeb57505050612ae992500383611efa565b565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958895509381019390910190612ad3565b81600052600e602052604060002073ffffffffffffffffffffffffffffffffffffffff821660005260205260ff604060002054166130795781600052600660205260406000209060405191612b6f83611ead565b73ffffffffffffffffffffffffffffffffffffffff815416835273ffffffffffffffffffffffffffffffffffffffff600182015416602084015260ff600282015473ffffffffffffffffffffffffffffffffffffffff8116604086015260a01c166060840152612c01600660038301549260808601938452600481015460a0870152600581015460c087015201612ab4565b60e084015283600052600d602052612c87604060002092612c20613437565b9251600385015486516040517f7e418fa000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116600483015290911660248201529391929091602090859081906044820190565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa801561303957600090613045575b87516040517f0f467f9800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301529091166024820152945060608580604481015b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa94851561303957600095613019575b50845115918215612fdc575b508115612fcd575b8115612fc2575b508015612fb0575b8015612f9b575b8015612f7f575b612f735786600052600c602052604060002054948515801580612f4f575b50612e62575b505050612de2612df592826040612de9945191015190613451565b92836124df565b9160a0840151906124df565b818110908218021891600052600b6020526040600020549060c0612e198385611f7e565b910151809111612e51575b505060035480612e32575090565b670de0b6b3a7640000908103908111611f4f57612e4e916124df565b90565b612e5b9250612109565b3880612e24565b909192945060ff815416612f36575b508181119082180218818111600014612f2e5762093a80612e928383612109565b04905b612eb260c086015187600052600b60205260406000205490612109565b9262093a80810191828211611f4f57612df594612de994612de294612f0b931180612f24575b612f15575b50612f058373ffffffffffffffffffffffffffffffffffffffff8a511660e08b0151906130b3565b90612003565b9492819450612dc7565b612f1e91611feb565b38612edd565b5060018111612ed8565b600090612e95565b600281015460a08701526001015460c086015238612e71565b905080612f5d575b38612dc1565b50846020612f6a8a61338f565b01511415612f57565b50505050505050600090565b5086600052600b60205260406000205460c08701511115612da3565b506020612fa78861338f565b01518410612d9c565b50818111818318028218841015612d95565b905084111538612d8d565b60408501518610159150612d86565b73ffffffffffffffffffffffffffffffffffffffff91925016600052600f6020526040600020886000526020528460406000205410159038612d7e565b61303291955060603d8111612485576124768183611efa565b9338612d72565b6040513d6000823e3d90fd5b506020843d602011613071575b8161305f60209383611efa565b81010312611cd957612d289351612cd0565b3d9150613052565b5050600090565b6040519061308d82611eca565b60006040838281528260208201520152565b8051821015611f9b5760209160051b010190565b929190926130bf613080565b5083519073ffffffffffffffffffffffffffffffffffffffff807f000000000000000000000000000000000000000000000000000000000000000016936040918251907fedba52730000000000000000000000000000000000000000000000000000000082526004918186168382015283602482015284816044818b5afa90811561334757600091613302575b50519860005b87811061316457505050505050505050565b8683613170838561309f565b51168751907f0f467f98000000000000000000000000000000000000000000000000000000008252818c81806131d360609788968d840190602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b03915afa9182156132f7576000926132da575b5050613258846131f6848661309f565b5116868a8d8b5180809681947f7e418fa00000000000000000000000000000000000000000000000000000000083526020978897840190602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b03915afa9081156132cf576000916132a3575b509050861161327e575b50600101613152565b6001919b61329687838a61329c955191015190613451565b90612109565b9a90613275565b82813d83116132c8575b6132b78183611efa565b810103126102c1575051803861326b565b503d6132ad565b89513d6000823e3d90fd5b6132f09250803d10612485576124768183611efa565b38806131e6565b88513d6000823e3d90fd5b908582813d8311613340575b6133188183611efa565b810103126102c15750602085519161332f83611e46565b80518352015160208201523861314c565b503d61330e565b85513d6000823e3d90fd5b600052600660205260406000206003613369613437565b910154600082821115613389575062093a809161338591612109565b0490565b91505090565b613397613080565b50600052600a60205260406000206002604051916133b483611eca565b60ff8154168352600181015460208401520154604082015290565b60ff9060009081526006602052604081206133e8613437565b60038201548181111561342b5762093a806134068693600293612109565b04925b015460a01c168082111561341d5750501690565b6134279250612109565b1690565b50508260028392613409565b62093a80804204818102918183041490151715611f4f5790565b9162093a808101808211611f4f578211156134835761346f91612109565b90818102918183041490151715611f4f5790565b505050600090565b969791949092936040517f3f9095b700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8916600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa90811561303957600091613a98575b50600090600f0b12613a8857600260ff861610613a5e5781158015613a56575b613a2c5773ffffffffffffffffffffffffffffffffffffffff8616158015613a0e575b6139e45761357582303389612038565b6001600454806080520160045561358f60ff861683611feb565b94613598613437565b9660ff60018184160111611f4f5760ff600181841601168062093a8081020462093a801481151715611f4f5762093a806135d3910289611f7e565b604051906135e082611ead565b73ffffffffffffffffffffffffffffffffffffffff8c16825273ffffffffffffffffffffffffffffffffffffffff8816602083015273ffffffffffffffffffffffffffffffffffffffff8316604083015260ff8416606083015260808201528360a08201528460c082015267ffffffffffffffff8c11611e62578b60051b6040519061366f6020820183611efa565b8d82526020820190368d820111611cd9578c9181835b0183106139b55750505060e08201908152608051600052600660205260c060406000209261378773ffffffffffffffffffffffffffffffffffffffff8251167fffffffffffffffffffffffff000000000000000000000000000000000000000090818754161786556001860173ffffffffffffffffffffffffffffffffffffffff60208501511682825416179055600286019073ffffffffffffffffffffffffffffffffffffffff6040850151169082541617815560ff6060840151167fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000083549260a01b169116179055565b6080810151600385015560a0810151600485015501516005830155519081519167ffffffffffffffff8311611e6257680100000000000000008311611e62576006820154836006840155808410613988575b506020600691019101600052602060002060005b83811061395e575050505073ffffffffffffffffffffffffffffffffffffffff95949392918660ff928160405198168852166020870152166040850152606084015284608084015260a08301521515958660c08301527fbd09d6ba321593e1822a3fd27c75fcfaafc65d9ef961da9d03cf1bc0a6e9249560e06080519485931693a3600052600960205260406000207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009460ff86835416911617905562093a808201809211611f4f57600290604051926138c684611eca565b600084526020840190815260408401918252608051600052600a60205260ff6040600020945116868554161784555160018401555191015560005b83811061390e5750505050565b600190608051600052600e602052604060002073ffffffffffffffffffffffffffffffffffffffff6139446112fa848988611f8b565b166000526020526040600020828582541617905501613901565b600190602073ffffffffffffffffffffffffffffffffffffffff85511694019381840155016137ed565b600683016000526020600020908482015b81830181106139a95750506137d9565b60008155600101613999565b823573ffffffffffffffffffffffffffffffffffffffff81168103611cd957815260209283019201818e613685565b60046040517f538ba4f9000000000000000000000000000000000000000000000000000000008152fd5b5073ffffffffffffffffffffffffffffffffffffffff841615613565565b60046040517f56e43e7f000000000000000000000000000000000000000000000000000000008152fd5b508015613542565b60046040517f9f342962000000000000000000000000000000000000000000000000000000008152fd5b5050505050505050506000608052565b6020813d602011613acd575b81613ab160209383611efa565b8101031261065c57519081600f0b82036102c157506000613522565b3d9150613aa456fea264697066735822122022f0999d434fdebe11faf7ee79df4c349266a2243cffb3a2dbc2ba6f92c5935a64736f6c634300081100330000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb000000000000000000000000f930ebbd05ef8b25b1797b9b2109ddc9b0d4306300000000000000000000000090569d8a1cf801709577b24da526118f0c83fc75Deployed Bytecode
0x604060a081526004908136101561001557600080fd5b600091823560e01c918263086146d214611c7b5782630888dc9014611b6d578263091cc55814611b025782630d8e6e2c14611a375782630de05659146119bc57826312e8e2c3146119365782631e96917d146118fa57826321bf936a1461189b578263228c076c1461184057826328c77820146117c4578263379607f5146117855782633ad86d721461173f5782633bbed4a01461169357826341c0e1b51461161457826343cd235a1461159a5782634ae001d8146115295782634fcf04f9146114be57826351cd41e8146114845782635749a07a146114455782635d34625d146113e857826360efe334146113a15782636b5646aa14611364578263725acc6b146112715782638c8f623b14610ea85782638da5cb5b14610e575782638fac393714610e105782638fe8a10114610dce57826399eecb3b14610d5f578263a42dce8014610caa578263aca47b7d14610c5f578263b3651eea14610bfa578263b6b6e3c814610b35578263b988997f14610931578263c415b95c146108de578263c7da33281461087c578263d1d1bb4f14610836578263d3c16e3014610788578263dc2f8744146106e4578263dd47a77f14610660578263ddca3f431461061f578263ddd5e1b2146105d3578263de4aaaf414610511578263ee8c4bbf146103c0578263ef2c4082146102c457505063f2fde38b1461021357600080fd5b346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1577fffffffffffffffffffffffff000000000000000000000000000000000000000061026b611cb6565b82549073ffffffffffffffffffffffffffffffffffffffff906102918284163314611d7c565b1691829116178255337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b80fd5b909150346103bc57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc57813590610300611cde565b92828552600660205273ffffffffffffffffffffffffffffffffffffffff9081600184882001541633036103955750916020917f5baaf19ee8739d1720c1401554ff4abe5682505ec43d3f2eb61b9dfc0abd97459382875260068452600182882001951694857fffffffffffffffffffffffff000000000000000000000000000000000000000082541617905551908152a280f35b82517ffabbbc67000000000000000000000000000000000000000000000000000000008152fd5b8280fd5b9150346103bc57602092837ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c157908261050d92606060e0835161040881611ead565b8381528389820152838582015283838201528360808201528360a08201528360c0820152015282358152600686522090835191829161044683611ead565b60ff73ffffffffffffffffffffffffffffffffffffffff9687845416855287806001860154168a8701908152600286015492828189019281861684528660608b019660a01c1686526104bd600660038b01549960808d019a8b528b01549a60a08d019b8c5260c060058201549d019c8d5201612ab4565b9a60e081019b8c5283519e8f9e8f9282845251169101525116908b015251166060890152511660808701525160a08601525160c08501525160e08401525161010080840152610120830190611d32565b0390f35b9150346103bc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc57610549611cb6565b906024359067ffffffffffffffff82116105cf5761056991369101611d01565b909273ffffffffffffffffffffffffffffffffffffffff908184168652600760205285205416908490821515915b8181106105a2578680f35b806105c26105b3600193858a611f8b565b3585156105c957865b88612080565b5001610597565b876105bc565b8480fd5b83346102c157817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1575061061860209235610612611cde565b33612080565b9051908152f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020906003549051908152f35b5080fd5b909150346103bc577ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5780359067ffffffffffffffff82116103bc576106ae91369101611d01565b6106b6611cde565b90835b8181106106c4578480f35b806106dd6106d56001938588611f8b565b358533612080565b50016106b9565b909150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc578060e0938335815260066020522073ffffffffffffffffffffffffffffffffffffffff9260ff848354169480600185015416946002850154916005600387015495870154960154968251988952602089015282169087015260a01c166060850152608084015260a083015260c0820152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc57828291608094606084516107cc81611e91565b82815282602082015282868201520152358152600d60205220908051906107f282611e91565b60ff83541692838352600181015460208401908152606060036002840154938587019485520154940193845282519485525160208501525190830152516060820152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc576020928291358152600b845220549051908152f35b83903461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c578060209273ffffffffffffffffffffffffffffffffffffffff6108ce611cb6565b1681526008845220549051908152f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5760209073ffffffffffffffffffffffffffffffffffffffff600554169051908152f35b909150346103bc57602090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610b3157823560019361097685805414611de1565b6002855581865260068452828620938585019473ffffffffffffffffffffffffffffffffffffffff928387541615610b0a5750908792916109b5613437565b600382015411801590610afe575b6109cf575b8388805580f35b7fe44903a04328507d74e4709cbebc6c819f9f1cd0b7cb171ba22814fcc18ecf389686938692838752600d85528a8688208751610a0b81611e91565b60ff82541681528282015490606060038a83019484865260028101548d85015201549101521515600014610acb57918860036002938a9b610a5e8a8c9d600b610a849e8c9b519386525283205490612109565b9c8d9a8252600d8d52812091818355820155828582015501555b01541691541690612528565b8287526006815285848820017fffffffffffffffffffffffff000000000000000000000000000000000000000081541690558351928352820152a1388080848180806109c8565b505092610a84949550600296526006855280610af68c8a6005818320015491600b8a52205490612109565b968794610a78565b5060ff600254166109c3565b85517f29606baf000000000000000000000000000000000000000000000000000000008152fd5b8380fd5b83903461065c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c57610b6d611cb6565b90610b76611cde565b9173ffffffffffffffffffffffffffffffffffffffff8091610b9c828754163314611d7c565b169182855260076020528420921691827fffffffffffffffffffffffff00000000000000000000000000000000000000008254161790557fc1416b5cdab50a9fbc872236e1aa54566c6deb40024e63a4b1737ecacf09d6f98380a380f35b83903461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020918173ffffffffffffffffffffffffffffffffffffffff9182610c4e611cb6565b168152600785522054169051908152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc578160209360ff923581526009855220541690519015158152f35b83903461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5760207fe5693914d19c789bdee50a362998c0bc8d035a835f9871da5d51152f0582c34f91610d07611cb6565b73ffffffffffffffffffffffffffffffffffffffff90610d2b828754163314611d7c565b1690817fffffffffffffffffffffffff0000000000000000000000000000000000000000600554161760055551908152a180f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020905173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb168152f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5760209060ff6002541690519015158152f35b83346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1575060ff610e4f602093356133cf565b915191168152f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5773ffffffffffffffffffffffffffffffffffffffff60209254169051908152f35b83903461065c5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5782356024359060ff928383169384840361126d576044359160643592600196610f0488805414611de1565b600288558360025416611245578589526020966006885273ffffffffffffffffffffffffffffffffffffffff808a868d20015416330361121d57878b526009895285858c205416156111f55789610f5a89613352565b106111cd57831580156111c5575b61119d57908a868a94938a8352600686528c60038980862095600d8a52208a5192610f9284611e91565b85825416845281015498830198895260028101548b8401520154606082015295610fc788600286019384541630903390612038565b51156111065750610fec9150606092610fe291865116611f3b565b948a850151611f7e565b92015162093a80918281029281840414901517156110da577fbc253f6a158b7f3743255b0764f4408f10f66d26d277929a6bbe1bdf4fd76638999a9b506110d296959493929161103b91611f7e565b908484519361104985611e91565b1683528883015284838301526060820152915b858a52600d875260036060838c209285511698897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0085541617845585015194858c850155848101516002850155015191015551948594859094939260ff6060936080840197845216602083015260408201520152565b0390a1805580f35b60248b60118e7f4e487b7100000000000000000000000000000000000000000000000000000000835252fd5b6111299295509261111e916003945460a01c16611f3b565b946005850154611f7e565b92015462093a80918281029281840414901517156110da577fbc253f6a158b7f3743255b0764f4408f10f66d26d277929a6bbe1bdf4fd76638999a9b506110d296959493929161117891611f7e565b908484519361118685611e91565b16835288830152848383015260608201529161105c565b8b85517f56e43e7f000000000000000000000000000000000000000000000000000000008152fd5b508615610f68565b8b85517f3bad1a57000000000000000000000000000000000000000000000000000000008152fd5b8b85517fdc370e01000000000000000000000000000000000000000000000000000000008152fd5b8b85517ffabbbc67000000000000000000000000000000000000000000000000000000008152fd5b8983517f63d81431000000000000000000000000000000000000000000000000000000008152fd5b8580fd5b9150346103bc57602090817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610b315780359067ffffffffffffffff82116105cf576112c291369101611d01565b9290916001936112d485805414611de1565b6002855584865b8281106112e9578782805580f35b61135c6112ff6112fa83868a611f8b565b611fca565b73ffffffffffffffffffffffffffffffffffffffff9081811691828c5260088852888c2054928c8a8120557f9dc46f23cfb5ddcad0ae7ea2be38d47fec07bb9382ec7e564efc69e036dd66ce898b51868152a26005541690612528565b0185906112db565b83346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1575061061860209235613352565b83903461065c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020906106186113df611cb6565b60243590612b1b565b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc5760068261050d9461143293358152826020522001612ab4565b9051918291602083526020830190611d32565b83823461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c57611480903561256b565b5080f35b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576020905160028152f35b83903461065c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c578060209273ffffffffffffffffffffffffffffffffffffffff61150f611cb6565b168152600f84528181206024358252845220549051908152f35b9150346103bc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc578160209360ff92611568611cde565b90358252600e865273ffffffffffffffffffffffffffffffffffffffff83832091168252855220541690519015158152f35b83823461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5780359067ffffffffffffffff82116103bc576115e991369101611d01565b825b8181106115f6578380f35b8061160d6116076001938587611f8b565b3561256b565b50016115eb565b83346102c157807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c15761166573ffffffffffffffffffffffffffffffffffffffff8254163314611d7c565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00600254161760025580f35b83903461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5773ffffffffffffffffffffffffffffffffffffffff6116e1611cb6565b9133845260076020528320911690817fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055337fc1416b5cdab50a9fbc872236e1aa54566c6deb40024e63a4b1737ecacf09d6f98380a380f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc576020928291358152600c845220549051908152f35b83346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c15750610618602092353333612080565b83823461065c5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c5780359067ffffffffffffffff82116103bc5761181391369101611d01565b825b818110611820578380f35b806118396118316001938587611f8b565b353333612080565b5001611815565b83346102c15760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c1575061187d6060923561338f565b908080519260ff815116845260208101516020850152015190820152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc576060928291358152600a6020522060ff81541691600260018301549201549181519384526020840152820152f35b9150346103bc57827ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc5760209250549051908152f35b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc577f8c4d35e54a3f2ef1134138fd8ea3daee6a3c89e10d2665996babdf70261e2c769160209135906119b073ffffffffffffffffffffffffffffffffffffffff8654163314611d7c565b8160035551908152a180f35b83903461065c57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c576106186020926119fa611cb6565b73ffffffffffffffffffffffffffffffffffffffff80821683526007865291849020549091168015611a30575b60243591612080565b5080611a27565b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c578051611a7281611e46565b600581526020907f322e312e300000000000000000000000000000000000000000000000000000008282015282519382859384528251928382860152825b848110611aec57505050828201840152601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168101030190f35b8181018301518882018801528795508201611ab0565b9150346103bc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126103bc576080928291358152600d602052209060ff825416916001810154916003600283015492015492815194855260208501528301526060820152f35b83346102c1576101007ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102c157611ba6611cb6565b90611baf611cde565b6044359273ffffffffffffffffffffffffffffffffffffffff841684036103bc5760643560ff81168103610b315760c43567ffffffffffffffff81116105cf57611bfc9036908901611d01565b95909460e435988915158a0361065c57611c196001805414611de1565b600260015560ff60025416611c54575091602098959391611c4697959360805260a435936084359361348b565b608051906001805551908152f35b88517f63d81431000000000000000000000000000000000000000000000000000000008152fd5b83903461065c57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261065c57602090610618613437565b6004359073ffffffffffffffffffffffffffffffffffffffff82168203611cd957565b600080fd5b6024359073ffffffffffffffffffffffffffffffffffffffff82168203611cd957565b9181601f84011215611cd95782359167ffffffffffffffff8311611cd9576020808501948460051b010111611cd957565b90815180825260208080930193019160005b828110611d52575050505090565b835173ffffffffffffffffffffffffffffffffffffffff1685529381019392810192600101611d44565b15611d8357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b15611de857565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600a60248201527f5245454e5452414e4359000000000000000000000000000000000000000000006044820152fd5b6040810190811067ffffffffffffffff821117611e6257604052565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6080810190811067ffffffffffffffff821117611e6257604052565b610100810190811067ffffffffffffffff821117611e6257604052565b6060810190811067ffffffffffffffff821117611e6257604052565b67ffffffffffffffff8111611e6257604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117611e6257604052565b9060ff8091169116019060ff8211611f4f57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b91908201809211611f4f57565b9190811015611f9b5760051b0190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b3573ffffffffffffffffffffffffffffffffffffffff81168103611cd95790565b8115611ff5570490565b63ad251c276000526004601cfd5b670de0b6b3a7640000907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111820215830215611ff557020490565b92916064601c60209360405196600096879586936323b872dd855288526040526060525af13d15600183511417161561207357606052604052565b637939f42490526004601cfd5b919061208f6001805414611de1565b600260015560ff600254166120ae576120a792612116565b9060018055565b60046040517f63d81431000000000000000000000000000000000000000000000000000000008152fd5b90816060910312611cd95760408051916120f183611eca565b80518352602081015160208401520151604082015290565b91908203918211611f4f57565b929190600090828252602090600e82526040928381209373ffffffffffffffffffffffffffffffffffffffff80891695868452855260ff82842054166124d45761215f8761256b565b87845260068652828420805484517f7e418fa000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8d811660048301529185169182166024820152919b9296927f0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb8516908984604481855afa9384156124ca578894612496575b5086517f0f467f9800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff918216600482015292166024830152909190606090839060449082905afa91821561248c57869261245c575b5081511590811561243e575b8115612430575b8115612425575b508015612417575b8015612406575b80156123ef575b6123e257856122b791898752600f89528587208b88528952818688205585815191015190613451565b888552600c87526122da6122ce85872054836124df565b9160048d0154906124df565b818110908218021899888552600b8752838520546122f8818d611f7e565b60058301548091116123cf575b5050888552600b875283852061231c8c8254611f7e565b9055846003548061237c575b50507f6f9c9826be5976f3f82a3490c52a83328ce2ec7be9e62dcb39c26da5148d7c7696949260809694926123668d60028195019284845416612528565b54169482519586528501528301526060820152a3565b85989650612391908d9592939896949d6124df565b9b8c94859d61239f91612109565b9c8d998a948460028c015416815260088a5220908154906123bf91611f7e565b9055929496918194969850612328565b6123da929c50612109565b993880612305565b5092985050505050505050565b50888552600b87528385205460058c01541461228e565b5061240f613437565b861415612287565b5060038b0154861015612280565b905086111538612278565b828601518810159150612271565b9050888652600f88528486208a87528852868587205410159061226a565b61247e91925060603d8111612485575b6124768183611efa565b8101906120d8565b903861225e565b503d61246c565b85513d88823e3d90fd5b9093508981813d83116124c3575b6124ae8183611efa565b810103126124bf57519260606121f7565b8780fd5b503d6124a4565b87513d8a823e3d90fd5b509096505050505050565b90807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048211810261251a57670de0b6b3a764000091020490565b63bac65e5b6000526004601cfd5b601692602092601a52603a52604460009384809369a9059cbb00000000000082525af13d15600183511417161561255e57603a52565b6390b8ec1890526004601cfd5b600090808252602090600a825260409182842092612587613437565b9360ff8154161580612957575b612831575b6001019485549262093a8093848101809111612804574210156125c0575050505050505490565b9091929394955073ffffffffffffffffffffffffffffffffffffffff807f0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb1686845260068352818585205416813b156105cf57849160248392885194859384927f615e523700000000000000000000000000000000000000000000000000000000845260048401525af180156127fa579084916127e6575b505061266386612964565b61266c866133cf565b90868452600683528484209061268188613352565b61269a60058401548a8852600b87528888205490612109565b976003840154908b0190818c116127b957927fb77c22cd311809931524bcc8d4a33a61a392e2304c8a7d476b64018e11ed6cb19a98959260809a9895928d989511806127af575b61278b575b5086826126ff60066127059461270b9654169201612ab4565b906130b3565b87612003565b878452600c835284842055600260ff85519261272684611eca565b169384835283830187815260ff87808601938b85528c8152600a8852209451167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008554161784555160018401555191015582519586528501528301526060820152a190565b61270b926126ff60066127a38b94612705969d611feb565b9b9450505092506126e6565b50600181116126e1565b6024887f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6127ef90611ee6565b6103bc578238612658565b85513d86823e3d90fd5b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b61283a84612964565b838652600c835281862054612599576006835281862073ffffffffffffffffffffffffffffffffffffffff807f0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb1681835416813b1561295357899160248392885194859384927f615e523700000000000000000000000000000000000000000000000000000000845260048401525af1801561294957612924575b5086826126ff60066128ee946001979654169201612ab4565b806128fc575b509050612599565b61291390868952600a86526002858a200154612003565b858852600c855283882055386128f4565b600193926126ff60066128ee949b61293c8c95611ee6565b9b945050509293506128d5565b85513d8b823e3d90fd5b8980fd5b5060018101548514612594565b600090808252600d602052604080832091600183018054612987575b5050505050565b837f2e363750a1295e75baeb02bb4277be5bbd051627ff6fa2edae8a99c91ddf5ef1916003809601546006602052868689200155612a0f60ff8354166002878a2001907fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000083549260a01b169116179055565b612a6a60ff600284019384546004898c20015583548060058a8d200155600a60205282898c20541615612a91575b50541691549254865193849387859094939260ff6060936080840197845216602083015260408201520152565b0390a18352600d602052822082815582600182015582600282015501553880808080612980565b612a9f908383541690611feb565b878b52600a6020526002898c20015538612a3d565b9060405191828154918282526020928383019160005283600020936000905b828210612aeb57505050612ae992500383611efa565b565b855473ffffffffffffffffffffffffffffffffffffffff1684526001958601958895509381019390910190612ad3565b81600052600e602052604060002073ffffffffffffffffffffffffffffffffffffffff821660005260205260ff604060002054166130795781600052600660205260406000209060405191612b6f83611ead565b73ffffffffffffffffffffffffffffffffffffffff815416835273ffffffffffffffffffffffffffffffffffffffff600182015416602084015260ff600282015473ffffffffffffffffffffffffffffffffffffffff8116604086015260a01c166060840152612c01600660038301549260808601938452600481015460a0870152600581015460c087015201612ab4565b60e084015283600052600d602052612c87604060002092612c20613437565b9251600385015486516040517f7e418fa000000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff858116600483015290911660248201529391929091602090859081906044820190565b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb165afa801561303957600090613045575b87516040517f0f467f9800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff84811660048301529091166024820152945060608580604481015b038173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb165afa94851561303957600095613019575b50845115918215612fdc575b508115612fcd575b8115612fc2575b508015612fb0575b8015612f9b575b8015612f7f575b612f735786600052600c602052604060002054948515801580612f4f575b50612e62575b505050612de2612df592826040612de9945191015190613451565b92836124df565b9160a0840151906124df565b818110908218021891600052600b6020526040600020549060c0612e198385611f7e565b910151809111612e51575b505060035480612e32575090565b670de0b6b3a7640000908103908111611f4f57612e4e916124df565b90565b612e5b9250612109565b3880612e24565b909192945060ff815416612f36575b508181119082180218818111600014612f2e5762093a80612e928383612109565b04905b612eb260c086015187600052600b60205260406000205490612109565b9262093a80810191828211611f4f57612df594612de994612de294612f0b931180612f24575b612f15575b50612f058373ffffffffffffffffffffffffffffffffffffffff8a511660e08b0151906130b3565b90612003565b9492819450612dc7565b612f1e91611feb565b38612edd565b5060018111612ed8565b600090612e95565b600281015460a08701526001015460c086015238612e71565b905080612f5d575b38612dc1565b50846020612f6a8a61338f565b01511415612f57565b50505050505050600090565b5086600052600b60205260406000205460c08701511115612da3565b506020612fa78861338f565b01518410612d9c565b50818111818318028218841015612d95565b905084111538612d8d565b60408501518610159150612d86565b73ffffffffffffffffffffffffffffffffffffffff91925016600052600f6020526040600020886000526020528460406000205410159038612d7e565b61303291955060603d8111612485576124768183611efa565b9338612d72565b6040513d6000823e3d90fd5b506020843d602011613071575b8161305f60209383611efa565b81010312611cd957612d289351612cd0565b3d9150613052565b5050600090565b6040519061308d82611eca565b60006040838281528260208201520152565b8051821015611f9b5760209160051b010190565b929190926130bf613080565b5083519073ffffffffffffffffffffffffffffffffffffffff807f0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb16936040918251907fedba52730000000000000000000000000000000000000000000000000000000082526004918186168382015283602482015284816044818b5afa90811561334757600091613302575b50519860005b87811061316457505050505050505050565b8683613170838561309f565b51168751907f0f467f98000000000000000000000000000000000000000000000000000000008252818c81806131d360609788968d840190602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b03915afa9182156132f7576000926132da575b5050613258846131f6848661309f565b5116868a8d8b5180809681947f7e418fa00000000000000000000000000000000000000000000000000000000083526020978897840190602090939293604083019473ffffffffffffffffffffffffffffffffffffffff809216845216910152565b03915afa9081156132cf576000916132a3575b509050861161327e575b50600101613152565b6001919b61329687838a61329c955191015190613451565b90612109565b9a90613275565b82813d83116132c8575b6132b78183611efa565b810103126102c1575051803861326b565b503d6132ad565b89513d6000823e3d90fd5b6132f09250803d10612485576124768183611efa565b38806131e6565b88513d6000823e3d90fd5b908582813d8311613340575b6133188183611efa565b810103126102c15750602085519161332f83611e46565b80518352015160208201523861314c565b503d61330e565b85513d6000823e3d90fd5b600052600660205260406000206003613369613437565b910154600082821115613389575062093a809161338591612109565b0490565b91505090565b613397613080565b50600052600a60205260406000206002604051916133b483611eca565b60ff8154168352600181015460208401520154604082015290565b60ff9060009081526006602052604081206133e8613437565b60038201548181111561342b5762093a806134068693600293612109565b04925b015460a01c168082111561341d5750501690565b6134279250612109565b1690565b50508260028392613409565b62093a80804204818102918183041490151715611f4f5790565b9162093a808101808211611f4f578211156134835761346f91612109565b90818102918183041490151715611f4f5790565b505050600090565b969791949092936040517f3f9095b700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8916600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb165afa90811561303957600091613a98575b50600090600f0b12613a8857600260ff861610613a5e5781158015613a56575b613a2c5773ffffffffffffffffffffffffffffffffffffffff8616158015613a0e575b6139e45761357582303389612038565b6001600454806080520160045561358f60ff861683611feb565b94613598613437565b9660ff60018184160111611f4f5760ff600181841601168062093a8081020462093a801481151715611f4f5762093a806135d3910289611f7e565b604051906135e082611ead565b73ffffffffffffffffffffffffffffffffffffffff8c16825273ffffffffffffffffffffffffffffffffffffffff8816602083015273ffffffffffffffffffffffffffffffffffffffff8316604083015260ff8416606083015260808201528360a08201528460c082015267ffffffffffffffff8c11611e62578b60051b6040519061366f6020820183611efa565b8d82526020820190368d820111611cd9578c9181835b0183106139b55750505060e08201908152608051600052600660205260c060406000209261378773ffffffffffffffffffffffffffffffffffffffff8251167fffffffffffffffffffffffff000000000000000000000000000000000000000090818754161786556001860173ffffffffffffffffffffffffffffffffffffffff60208501511682825416179055600286019073ffffffffffffffffffffffffffffffffffffffff6040850151169082541617815560ff6060840151167fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff000000000000000000000000000000000000000083549260a01b169116179055565b6080810151600385015560a0810151600485015501516005830155519081519167ffffffffffffffff8311611e6257680100000000000000008311611e62576006820154836006840155808410613988575b506020600691019101600052602060002060005b83811061395e575050505073ffffffffffffffffffffffffffffffffffffffff95949392918660ff928160405198168852166020870152166040850152606084015284608084015260a08301521515958660c08301527fbd09d6ba321593e1822a3fd27c75fcfaafc65d9ef961da9d03cf1bc0a6e9249560e06080519485931693a3600052600960205260406000207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009460ff86835416911617905562093a808201809211611f4f57600290604051926138c684611eca565b600084526020840190815260408401918252608051600052600a60205260ff6040600020945116868554161784555160018401555191015560005b83811061390e5750505050565b600190608051600052600e602052604060002073ffffffffffffffffffffffffffffffffffffffff6139446112fa848988611f8b565b166000526020526040600020828582541617905501613901565b600190602073ffffffffffffffffffffffffffffffffffffffff85511694019381840155016137ed565b600683016000526020600020908482015b81830181106139a95750506137d9565b60008155600101613999565b823573ffffffffffffffffffffffffffffffffffffffff81168103611cd957815260209283019201818e613685565b60046040517f538ba4f9000000000000000000000000000000000000000000000000000000008152fd5b5073ffffffffffffffffffffffffffffffffffffffff841615613565565b60046040517f56e43e7f000000000000000000000000000000000000000000000000000000008152fd5b508015613542565b60046040517f9f342962000000000000000000000000000000000000000000000000000000008152fd5b5050505050505050506000608052565b6020813d602011613acd575b81613ab160209383611efa565b8101031261065c57519081600f0b82036102c157506000613522565b3d9150613aa456fea264697066735822122022f0999d434fdebe11faf7ee79df4c349266a2243cffb3a2dbc2ba6f92c5935a64736f6c63430008110033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb000000000000000000000000f930ebbd05ef8b25b1797b9b2109ddc9b0d4306300000000000000000000000090569d8a1cf801709577b24da526118f0c83fc75
-----Decoded View---------------
Arg [0] : _gaugeController (address): 0x2F50D538606Fa9EDD2B11E2446BEb18C9D5846bB
Arg [1] : _feeCollector (address): 0xF930EBBd05eF8b25B1797b9b2109DDC9B0d43063
Arg [2] : _owner (address): 0x90569D8A1cF801709577B24dA526118f0C83Fc75
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 0000000000000000000000002f50d538606fa9edd2b11e2446beb18c9d5846bb
Arg [1] : 000000000000000000000000f930ebbd05ef8b25b1797b9b2109ddc9b0d43063
Arg [2] : 00000000000000000000000090569d8a1cf801709577b24da526118f0c83fc75Loading...LoadingLoading...Loading
Loading...Loading
Loading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingLoading...LoadingMultichain Portfolio | 30 Chains
Chain Token Portfolio % Price Amount Value ETH 24.38% $0.999725 12,303.7317 $12,300.35 ETH 23.29% $1.3 9,039.007 $11,750.71 ETH 16.63% $0.557766 15,041.0329 $8,389.38 ETH 10.00% $3.37 1,497.3174 $5,045.96 ETH 5.97% $4.16 724.681 $3,011.23 ETH 4.30% $260.84 8.3206 $2,170.35 ETH 3.73% $0.85128 2,207.776 $1,879.44 ETH 2.97% $0.032795 45,646.6401 $1,496.99 ETH 2.49% $0.998323 1,257.0265 $1,254.92 ETH 1.85% $19.7 47.4326 $934.42 ETH 1.43% $0.105533 6,855.0083 $723.43 ETH 0.64% $1 324.9217 $325.05 ETH 0.56% $0.752885 373.0202 $280.84 ETH 0.55% $0.012898 21,444.7425 $276.59 ETH 0.45% $3,335.13 0.0684 $228.26 ETH 0.37% $0.001574 119,717.3334 $188.45 ETH 0.13% $0.94564 68.0639 $64.36 ETH 0.09% $0.451945 101.9436 $46.07 ETH 0.06% $0.054191 572.4339 $31.02 ETH 0.06% $1.97 15.0851 $29.72 ETH 0.02% $3,331.99 0.00249113 $8.3 ETH 0.01% $3,595.87 0.00191252 $6.88 ETH <0.01% $0.022201 62.6906 $1.39 ETH <0.01% $0.995142 0.2 $0.199 BASE <0.01% $0.030655 10 $0.3065 Loading...Loading[ Download: CSV Export ][ Download: CSV Export ]A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.
Address QR Code
My Address - Private Name Tag or Note
My Name Tag:
Private Name Tags (up to 35 characters) can be used for easy identification of addressesPrivate Note:
A private note (up to 500 characters) can be attached to this address.
Please DO NOT store any passwords or private keys here.Compiler specific version warnings:
The compiled contract might be susceptible to VerbatimInvalidDeduplication (low-severity), FullInlinerNonExpressionSplitArgumentEvaluationOrder (low-severity), MissingSideEffectsOnSelectorAccess (low-severity) Solidity Compiler Bugs.
Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.Connect a Wallet
Connecting wallet for read function is optional, useful if you want to call certain functions or simply use your wallet's node.SignIn
Address Cards
To use this feature, please login to your Etherscan account and return to this page.Before You Copy
Transaction Private Note
This website uses cookies to improve your experience. By continuing to use this website, you agree to its Terms and Privacy Policy.