More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 3,253 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Redeem | 22002558 | 2 hrs ago | IN | 0 ETH | 0.00011639 | ||||
Redeem Futures R... | 22002556 | 2 hrs ago | IN | 0 ETH | 0.00013364 | ||||
Redeem Futures R... | 22002441 | 2 hrs ago | IN | 0 ETH | 0.00018882 | ||||
Redeem Futures R... | 22002391 | 2 hrs ago | IN | 0 ETH | 0.00013004 | ||||
Redeem Futures R... | 22002390 | 2 hrs ago | IN | 0 ETH | 0.00014505 | ||||
Redeem Futures R... | 22002388 | 2 hrs ago | IN | 0 ETH | 0.00014821 | ||||
Redeem Futures R... | 22002387 | 2 hrs ago | IN | 0 ETH | 0.0001476 | ||||
Redeem Futures R... | 22002385 | 2 hrs ago | IN | 0 ETH | 0.00015681 | ||||
Redeem Futures R... | 22002383 | 2 hrs ago | IN | 0 ETH | 0.00017886 | ||||
Redeem | 22002372 | 2 hrs ago | IN | 0 ETH | 0.0000962 | ||||
Redeem Futures R... | 22002359 | 2 hrs ago | IN | 0 ETH | 0.00020451 | ||||
Redeem Futures R... | 21997172 | 20 hrs ago | IN | 0 ETH | 0.00018451 | ||||
Redeem | 21995383 | 26 hrs ago | IN | 0 ETH | 0.00032158 | ||||
Redeem Futures R... | 21995367 | 26 hrs ago | IN | 0 ETH | 0.00043065 | ||||
Redeem Futures R... | 21995364 | 26 hrs ago | IN | 0 ETH | 0.00050506 | ||||
Redeem Futures R... | 21987640 | 2 days ago | IN | 0 ETH | 0.00028571 | ||||
Redeem Futures R... | 21987637 | 2 days ago | IN | 0 ETH | 0.00031778 | ||||
Redeem Futures R... | 21987630 | 2 days ago | IN | 0 ETH | 0.00032546 | ||||
Redeem Futures R... | 21987627 | 2 days ago | IN | 0 ETH | 0.00040009 | ||||
Redeem | 21987604 | 2 days ago | IN | 0 ETH | 0.00021857 | ||||
Lock | 21984172 | 2 days ago | IN | 0 ETH | 0.00067792 | ||||
Lock | 21984083 | 2 days ago | IN | 0 ETH | 0.00054277 | ||||
Redeem Futures R... | 21975489 | 3 days ago | IN | 0 ETH | 0.00062039 | ||||
Redeem Snapshot ... | 21973640 | 4 days ago | IN | 0 ETH | 0.00049681 | ||||
Claim Votium Rew... | 21973490 | 4 days ago | IN | 0 ETH | 0.00197873 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
PirexCvx
Compiler Version
v0.8.12+commit.f00d7308
Optimization Enabled:
Yes with 179 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.12; import {ReentrancyGuard} from "@rari-capital/solmate/src/utils/ReentrancyGuard.sol"; import {ERC20} from "@rari-capital/solmate/src/tokens/ERC20.sol"; import {SafeTransferLib} from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; import {Bytes32AddressLib} from "@rari-capital/solmate/src/utils/Bytes32AddressLib.sol"; import {PirexCvxConvex} from "./PirexCvxConvex.sol"; import {PxCvx} from "./PxCvx.sol"; import {PirexFees} from "./PirexFees.sol"; import {UnionPirexVault} from "./vault/UnionPirexVault.sol"; import {ERC1155Solmate} from "./tokens/ERC1155Solmate.sol"; import {ERC1155PresetMinterSupply} from "./tokens/ERC1155PresetMinterSupply.sol"; import {IVotiumMultiMerkleStash} from "./interfaces/IVotiumMultiMerkleStash.sol"; import {ICvxLocker} from "./interfaces/ICvxLocker.sol"; /** For Jude 🐾 - kings never die. QmPXRvYyDSqiqk9Xj9zsoaLa3UMN2uL5A8J9CPQPHvPQ6i - kp Arise, you have nothing to lose but your barbed wire fences! - never Hakuna matata - greenbergz ZA WARUDO - seiji Why not both. The answer to all of life's questions. CVX locked or liquid? Crypto or childcare? - Percival Dreams are made of these... QmU6oGG8J1cKWKiuQU1y9YTiBF89LBUyojTjfvfzZ4GMif - funky 🫡 - Sami Put cereal in my coffee, INNOVATING ON CHAIN & OFF CHAIN - Marcel Development will continue until morale improves - Alunara Imagine using a lame ass quote instead of ascii art - Benny */ contract PirexCvx is ReentrancyGuard, PirexCvxConvex { using SafeTransferLib for ERC20; using Bytes32AddressLib for address; /** @notice Data pertaining to an emergency migration @param recipient address Recipient of the tokens (e.g. new PirexCvx contract) @param tokens address[] Token addresses */ struct EmergencyMigration { address recipient; address[] tokens; } // Users can choose between the two futures tokens when staking or initiating a redemption enum Futures { Vote, Reward } // Configurable contracts enum Contract { PxCvx, PirexFees, Votium, UpxCvx, SpxCvx, VpxCvx, RpxCvx, UnionPirexVault } // Configurable fees enum Fees { Reward, RedemptionMax, RedemptionMin, Developers } // Convex voting round duration (1,209,600 seconds) uint32 public constant EPOCH_DURATION = 2 weeks; // Fee denominator uint32 public constant FEE_DENOMINATOR = 1_000_000; // Fee maximum uint32 public constant FEE_MAX = 100_000; // Maximum wait time for a CVX redemption (10,281,600 seconds) uint32 public constant MAX_REDEMPTION_TIME = 17 weeks; // Unused ERC1155 `data` param value bytes private constant UNUSED_1155_DATA = ""; PxCvx public pxCvx; PirexFees public pirexFees; IVotiumMultiMerkleStash public votiumMultiMerkleStash; ERC1155Solmate public upxCvx; ERC1155Solmate public spxCvx; ERC1155PresetMinterSupply public vpxCvx; ERC1155PresetMinterSupply public rpxCvx; UnionPirexVault public unionPirex; // Fees (e.g. 5000 / 1000000 = 0.5%) mapping(Fees => uint32) public fees; // Convex unlock timestamps mapped to amount being redeemed mapping(uint256 => uint256) public redemptions; // Developers who are eligible for incentives as part of the new initiative // to enable builders to sustainably build apps for the Pirex ecosystem mapping(address => bool) public developers; // Emergency migration data EmergencyMigration public emergencyMigration; // Non-Pirex multisig which has authority to fulfill emergency procedures address public emergencyExecutor; // In the case of a mass unlock by Convex, the current upxCVX would be deprecated // and should allow holders to immediately redeem their CVX by burning upxCVX bool public upxCvxDeprecated; event SetContract(Contract indexed c, address contractAddress); event SetFee(Fees indexed f, uint32 fee); event AddDeveloper(address developer); event RemoveDeveloper(address developer); event MintFutures( uint256 rounds, Futures indexed f, uint256 assets, address indexed receiver ); event Deposit( uint256 assets, address indexed receiver, bool indexed shouldCompound, address indexed developer ); event InitiateRedemptions( uint256[] lockIndexes, Futures indexed f, uint256[] assets, address indexed receiver ); event Redeem( uint256[] unlockTimes, uint256[] assets, address indexed receiver, bool legacy ); event Stake( uint256 rounds, Futures indexed f, uint256 assets, address indexed receiver ); event Unstake(uint256 id, uint256 assets, address indexed receiver); event ClaimMiscRewards(uint256 timestamp, ConvexReward[] rewards); event ClaimVotiumReward( address indexed token, uint256 index, uint256 amount ); event RedeemSnapshotRewards( uint256 indexed epoch, uint256[] rewardIndexes, address indexed receiver, uint256 snapshotBalance, uint256 snapshotSupply ); event RedeemFuturesRewards( uint256 indexed epoch, address indexed receiver, bytes32[] rewards ); event ExchangeFutures( uint256 indexed epoch, uint256 amount, address indexed receiver, Futures f ); event InitializeEmergencyExecutor(address _emergencyExecutor); event SetEmergencyMigration(EmergencyMigration _emergencyMigration); event SetUpxCvxDeprecated(bool state); event ExecuteEmergencyMigration(address recipient, address[] tokens); error ZeroAmount(); error BeforeUnlock(); error InsufficientBalance(); error AlreadyRedeemed(); error InsufficientRedemptionAllowance(); error PastExchangePeriod(); error InvalidFee(); error BeforeEffectiveTimestamp(); error BeforeStakingExpiry(); error InvalidEpoch(); error EmptyArray(); error MismatchedArrayLengths(); error NoRewards(); error RedeemClosed(); error AlreadyInitialized(); error NoEmergencyExecutor(); error InvalidEmergencyMigration(); error NotAuthorized(); /** @param _CVX address CVX address @param _cvxLocker address CvxLocker address @param _cvxDelegateRegistry address CvxDelegateRegistry address @param _pxCvx address PxCvx address @param _upxCvx address UpxCvx address @param _spxCvx address SpxCvx address @param _vpxCvx address VpxCvx address @param _rpxCvx address RpxCvx address @param _pirexFees address PirexFees address @param _votiumMultiMerkleStash address VotiumMultiMerkleStash address */ constructor( address _CVX, address _cvxLocker, address _cvxDelegateRegistry, address _pxCvx, address _upxCvx, address _spxCvx, address _vpxCvx, address _rpxCvx, address _pirexFees, address _votiumMultiMerkleStash ) PirexCvxConvex(_CVX, _cvxLocker, _cvxDelegateRegistry) { // Init with paused state, should only unpause after fully perform the full setup _pause(); if (_pxCvx == address(0)) revert ZeroAddress(); if (_pirexFees == address(0)) revert ZeroAddress(); if (_upxCvx == address(0)) revert ZeroAddress(); if (_spxCvx == address(0)) revert ZeroAddress(); if (_vpxCvx == address(0)) revert ZeroAddress(); if (_rpxCvx == address(0)) revert ZeroAddress(); if (_votiumMultiMerkleStash == address(0)) revert ZeroAddress(); pxCvx = PxCvx(_pxCvx); pirexFees = PirexFees(_pirexFees); upxCvx = ERC1155Solmate(_upxCvx); spxCvx = ERC1155Solmate(_spxCvx); vpxCvx = ERC1155PresetMinterSupply(_vpxCvx); rpxCvx = ERC1155PresetMinterSupply(_rpxCvx); votiumMultiMerkleStash = IVotiumMultiMerkleStash( _votiumMultiMerkleStash ); } /** @notice Set a contract address @param c enum Contract @param contractAddress address Contract address */ function setContract(Contract c, address contractAddress) external onlyOwner { if (contractAddress == address(0)) revert ZeroAddress(); emit SetContract(c, contractAddress); if (c == Contract.PxCvx) { pxCvx = PxCvx(contractAddress); return; } if (c == Contract.PirexFees) { pirexFees = PirexFees(contractAddress); return; } if (c == Contract.Votium) { votiumMultiMerkleStash = IVotiumMultiMerkleStash(contractAddress); return; } if (c == Contract.UpxCvx) { upxCvx = ERC1155Solmate(contractAddress); return; } if (c == Contract.SpxCvx) { spxCvx = ERC1155Solmate(contractAddress); return; } if (c == Contract.VpxCvx) { vpxCvx = ERC1155PresetMinterSupply(contractAddress); return; } if (c == Contract.RpxCvx) { rpxCvx = ERC1155PresetMinterSupply(contractAddress); return; } ERC20 pxCvxERC20 = ERC20(address(pxCvx)); address oldUnionPirex = address(unionPirex); if (oldUnionPirex != address(0)) { pxCvxERC20.safeApprove(oldUnionPirex, 0); } unionPirex = UnionPirexVault(contractAddress); pxCvxERC20.safeApprove(address(unionPirex), type(uint256).max); } /** @notice Set fee @param f enum Fee @param fee uint32 Fee amount */ function setFee(Fees f, uint32 fee) external onlyOwner { if (fee > FEE_MAX) revert InvalidFee(); if (f == Fees.RedemptionMax && fee < fees[Fees.RedemptionMin]) revert InvalidFee(); if (f == Fees.RedemptionMin && fee > fees[Fees.RedemptionMax]) revert InvalidFee(); fees[f] = fee; emit SetFee(f, fee); } /** @notice Add developer to whitelist mapping @param developer address Developer */ function addDeveloper(address developer) external onlyOwner { if (developer == address(0)) revert ZeroAddress(); developers[developer] = true; emit AddDeveloper(developer); } /** @notice Remove developer from whitelist mapping @param developer address Developer */ function removeDeveloper(address developer) external onlyOwner { if (developer == address(0)) revert ZeroAddress(); developers[developer] = false; emit RemoveDeveloper(developer); } /** @notice Get current epoch @return uint256 Current epoch */ function getCurrentEpoch() public view returns (uint256) { return (block.timestamp / EPOCH_DURATION) * EPOCH_DURATION; } /** @notice Mint futures tokens @param rounds uint256 Rounds (i.e. Convex voting rounds) @param f enum Futures enum @param assets uint256 Futures amount @param receiver address Receives futures */ function _mintFutures( uint256 rounds, Futures f, uint256 assets, address receiver ) internal { emit MintFutures(rounds, f, assets, receiver); ERC1155PresetMinterSupply token = f == Futures.Vote ? vpxCvx : rpxCvx; uint256 startingEpoch = getCurrentEpoch() + EPOCH_DURATION; uint256[] memory tokenIds = new uint256[](rounds); uint256[] memory amounts = new uint256[](rounds); for (uint256 i; i < rounds; ++i) { tokenIds[i] = startingEpoch + i * EPOCH_DURATION; amounts[i] = assets; } token.mintBatch(receiver, tokenIds, amounts, UNUSED_1155_DATA); } /** @notice Redeem CVX for specified unlock times @param unlockTimes uint256[] vlCVX unlock timestamps @param assets uint256[] upxCVX amounts @param receiver address Receives CVX @param legacy bool Whether upxCVX has been deprecated */ function _redeem( uint256[] calldata unlockTimes, uint256[] calldata assets, address receiver, bool legacy ) internal { uint256 unlockLen = unlockTimes.length; if (unlockLen == 0) revert EmptyArray(); if (unlockLen != assets.length) revert MismatchedArrayLengths(); if (receiver == address(0)) revert ZeroAddress(); emit Redeem(unlockTimes, assets, receiver, legacy); uint256 totalAssets; for (uint256 i; i < unlockLen; ++i) { uint256 asset = assets[i]; if (!legacy && unlockTimes[i] > block.timestamp) revert BeforeUnlock(); if (asset == 0) revert ZeroAmount(); totalAssets += asset; } // Perform unlocking and locking procedure to ensure enough CVX is available if (!legacy) { _lock(); } // Subtract redemption amount from outstanding CVX amount outstandingRedemptions -= totalAssets; // Reverts if sender has an insufficient upxCVX balance for any `unlockTime` id upxCvx.burnBatch(msg.sender, unlockTimes, assets); // Validates `to` CVX.safeTransfer(receiver, totalAssets); } /** @notice Calculate rewards @param feePercent uint32 Reward fee percent @param snapshotSupply uint256 pxCVX supply for the current snapshot id @param rpxCvxSupply uint256 rpxCVX supply for the current epoch @param received uint256 Received amount @return rewardFee uint256 Fee for protocol @return snapshotRewards uint256 Rewards for pxCVX token holders @return futuresRewards uint256 Rewards for futures token holders */ function _calculateRewards( uint32 feePercent, uint256 snapshotSupply, uint256 rpxCvxSupply, uint256 received ) internal pure returns ( uint256 rewardFee, uint256 snapshotRewards, uint256 futuresRewards ) { // Rewards paid to the protocol rewardFee = (received * feePercent) / FEE_DENOMINATOR; // Rewards distributed amongst snapshot and futures tokenholders uint256 rewards = received - rewardFee; // Rewards distributed to snapshotted tokenholders snapshotRewards = (rewards * snapshotSupply) / (snapshotSupply + rpxCvxSupply); // Rewards distributed to rpxCVX token holders futuresRewards = rewards - snapshotRewards; } /** @notice Deposit CVX @param assets uint256 CVX amount @param receiver address Receives pxCVX @param shouldCompound bool Whether to auto-compound @param developer address Developer incentive receiver */ function deposit( uint256 assets, address receiver, bool shouldCompound, address developer ) external whenNotPaused nonReentrant { if (assets == 0) revert ZeroAmount(); if (receiver == address(0)) revert ZeroAddress(); emit Deposit(assets, receiver, shouldCompound, developer); // Track amount of CVX waiting to be locked before `assets` is modified pendingLocks += assets; // Calculate the dev incentive, which will come out of the minted pxCVX uint256 developerIncentive = developer != address(0) && developers[developer] ? (assets * fees[Fees.Developers]) / FEE_DENOMINATOR : 0; // Take snapshot if necessary pxCvx.takeEpochSnapshot(); // Mint pxCVX sans developer incentive - recipient depends on shouldCompound pxCvx.mint( shouldCompound ? address(this) : receiver, assets - developerIncentive ); // Transfer CVX to self in preparation for lock CVX.safeTransferFrom(msg.sender, address(this), assets); if (developerIncentive != 0) { // Mint pxCVX for the developer pxCvx.mint(developer, developerIncentive); } if (shouldCompound) { // Update assets to ensure only the appropriate amount is deposited in vault assets -= developerIncentive; // Deposit pxCVX into Union vault - user receives shares unionPirex.deposit(assets, receiver); } } /** @notice Initiate CVX redemption @param lockData ICvxLocker.LockedBalance Locked balance index @param f enum Futures enum @param assets uint256 pxCVX amount @param receiver address Receives upxCVX @param feeMin uint256 Initiate redemption fee min @param feeMax uint256 Initiate redemption fee max @return feeAmount uint256 Fee amount */ function _initiateRedemption( ICvxLocker.LockedBalance memory lockData, Futures f, uint256 assets, address receiver, uint256 feeMin, uint256 feeMax ) internal returns (uint256 feeAmount) { if (assets == 0) revert ZeroAmount(); if (receiver == address(0)) revert ZeroAddress(); uint256 unlockTime = lockData.unlockTime; // Used for calculating the fee and conditionally adding a round uint256 waitTime = unlockTime - block.timestamp; if (feeMax != 0) { uint256 feePercent = feeMax - (((feeMax - feeMin) * waitTime) / MAX_REDEMPTION_TIME); feeAmount = (assets * feePercent) / FEE_DENOMINATOR; } uint256 postFeeAmount = assets - feeAmount; // Increment redemptions for this unlockTime to prevent over-redeeming redemptions[unlockTime] += postFeeAmount; // Check if there is any sufficient allowance after factoring in redemptions by others if (redemptions[unlockTime] > lockData.amount) revert InsufficientRedemptionAllowance(); // Track assets that needs to remain unlocked for redemptions outstandingRedemptions += postFeeAmount; // Mint upxCVX with unlockTime as the id - validates `to` upxCvx.mint(receiver, unlockTime, postFeeAmount, UNUSED_1155_DATA); // Determine how many futures notes rounds to mint uint256 rounds = waitTime / EPOCH_DURATION; // Check if the lock was in the first week/half of an epoch // Handle case where remaining time is between 1 and 2 weeks if ( rounds == 0 && unlockTime % EPOCH_DURATION != 0 && waitTime > (EPOCH_DURATION / 2) ) { // Rounds is 0 if waitTime is between 1 and 2 weeks // Increment by 1 since user should receive 1 round of rewards unchecked { ++rounds; } } // Mint vpxCVX or rpxCVX (using assets as we do not take a fee from this) _mintFutures(rounds, f, assets, receiver); return feeAmount; } /** @notice Initiate CVX redemptions @param lockIndexes uint256[] Locked balance index @param f enum Futures enum @param assets uint256[] pxCVX amounts @param receiver address Receives upxCVX */ function initiateRedemptions( uint256[] calldata lockIndexes, Futures f, uint256[] calldata assets, address receiver ) external whenNotPaused nonReentrant { uint256 lockLen = lockIndexes.length; if (lockLen == 0) revert EmptyArray(); if (lockLen != assets.length) revert MismatchedArrayLengths(); emit InitiateRedemptions(lockIndexes, f, assets, receiver); (, , , ICvxLocker.LockedBalance[] memory lockData) = cvxLocker .lockedBalances(address(this)); uint256 totalAssets; uint256 feeAmount; uint256 feeMin = fees[Fees.RedemptionMin]; uint256 feeMax = fees[Fees.RedemptionMax]; for (uint256 i; i < lockLen; ++i) { totalAssets += assets[i]; feeAmount += _initiateRedemption( lockData[lockIndexes[i]], f, assets[i], receiver, feeMin, feeMax ); } // Burn pxCVX - reverts if sender balance is insufficient pxCvx.burn(msg.sender, totalAssets - feeAmount); if (feeAmount != 0) { // Allow PirexFees to distribute fees directly from sender pxCvx.operatorApprove(msg.sender, address(pirexFees), feeAmount); // Distribute fees pirexFees.distributeFees(msg.sender, address(pxCvx), feeAmount); } } /** @notice Redeem CVX for specified unlock times @param unlockTimes uint256[] CVX unlock timestamps @param assets uint256[] upxCVX amounts @param receiver address Receives CVX */ function redeem( uint256[] calldata unlockTimes, uint256[] calldata assets, address receiver ) external whenNotPaused nonReentrant { if (upxCvxDeprecated) revert RedeemClosed(); _redeem(unlockTimes, assets, receiver, false); } /** @notice Redeem CVX for deprecated upxCVX holders if enabled @param unlockTimes uint256[] CVX unlock timestamps @param assets uint256[] upxCVX amounts @param receiver address Receives CVX */ function redeemLegacy( uint256[] calldata unlockTimes, uint256[] calldata assets, address receiver ) external whenPaused nonReentrant { if (!upxCvxDeprecated) revert RedeemClosed(); _redeem(unlockTimes, assets, receiver, true); } /** @notice Stake pxCVX @param rounds uint256 Rounds (i.e. Convex voting rounds) @param f enum Futures enum @param assets uint256 pxCVX amount @param receiver address Receives spxCVX */ function stake( uint256 rounds, Futures f, uint256 assets, address receiver ) external whenNotPaused nonReentrant { if (rounds == 0) revert ZeroAmount(); if (assets == 0) revert ZeroAmount(); if (receiver == address(0)) revert ZeroAddress(); // Burn pxCVX pxCvx.burn(msg.sender, assets); emit Stake(rounds, f, assets, receiver); // Mint spxCVX with the stake expiry timestamp as the id spxCvx.mint( receiver, getCurrentEpoch() + EPOCH_DURATION * rounds, assets, UNUSED_1155_DATA ); _mintFutures(rounds, f, assets, receiver); } /** @notice Unstake pxCVX @param id uint256 spxCVX id (an epoch timestamp) @param assets uint256 spxCVX amount @param receiver address Receives pxCVX */ function unstake( uint256 id, uint256 assets, address receiver ) external whenNotPaused nonReentrant { if (id > block.timestamp) revert BeforeStakingExpiry(); if (assets == 0) revert ZeroAmount(); if (receiver == address(0)) revert ZeroAddress(); // Mint pxCVX for receiver pxCvx.mint(receiver, assets); emit Unstake(id, assets, receiver); // Burn spxCVX from sender spxCvx.burn(msg.sender, id, assets); } /** @notice Claim multiple Votium rewards @param votiumRewards VotiumRewards[] Votium rewards metadata */ function claimVotiumRewards( IVotiumMultiMerkleStash.claimParam[] calldata votiumRewards ) external whenNotPaused nonReentrant { uint256 tLen = votiumRewards.length; if (tLen == 0) revert EmptyArray(); // Take snapshot before claiming rewards, if necessary pxCvx.takeEpochSnapshot(); uint256 epoch = getCurrentEpoch(); (uint256 snapshotId, , , ) = pxCvx.getEpoch(epoch); uint256 rpxCvxSupply = rpxCvx.totalSupply(epoch); for (uint256 i; i < tLen; ++i) { address token = votiumRewards[i].token; uint256 index = votiumRewards[i].index; uint256 amount = votiumRewards[i].amount; bytes32[] memory merkleProof = votiumRewards[i].merkleProof; if (token == address(0)) revert ZeroAddress(); if (amount == 0) revert ZeroAmount(); emit ClaimVotiumReward(token, index, amount); ERC20 t = ERC20(token); // Used for calculating the actual token amount received uint256 prevBalance = t.balanceOf(address(this)); // Validates `token`, `index`, `amount`, and `merkleProof` votiumMultiMerkleStash.claim( token, index, address(this), amount, merkleProof ); ( uint256 rewardFee, uint256 snapshotRewards, uint256 futuresRewards ) = _calculateRewards( fees[Fees.Reward], pxCvx.totalSupplyAt(snapshotId), rpxCvxSupply, t.balanceOf(address(this)) - prevBalance ); // Add reward token address and snapshot/futuresRewards amounts (same index for all) pxCvx.addEpochRewardMetadata( epoch, token.fillLast12Bytes(), snapshotRewards, futuresRewards ); // Distribute fees t.safeApprove(address(pirexFees), rewardFee); pirexFees.distributeFees(address(this), token, rewardFee); } } /** @notice Claim misc. rewards (e.g. emissions) and distribute to stakeholders */ function claimMiscRewards() external nonReentrant { // Get claimable rewards and balances ConvexReward[] memory c = _claimableRewards(); emit ClaimMiscRewards(block.timestamp, c); // Claim rewards from Convex _getReward(); uint256 cLen = c.length; // Iterate over rewards and distribute to stakeholders (rlBTRFLY, Redacted, and Pirex) for (uint256 i; i < cLen; ++i) { if (c[i].amount == 0) continue; ERC20 t = ERC20(c[i].token); uint256 received = t.balanceOf(address(this)) - c[i].balance; // Distribute fees t.safeApprove(address(pirexFees), received); pirexFees.distributeFees(address(this), c[i].token, received); } } /** @notice Redeem multiple Snapshot rewards as a pxCVX holder @param epoch uint256 Epoch @param rewardIndexes uint256[] Reward token indexes @param receiver address Receives snapshot rewards */ function redeemSnapshotRewards( uint256 epoch, uint256[] calldata rewardIndexes, address receiver ) external whenNotPaused nonReentrant { if (epoch == 0) revert InvalidEpoch(); if (receiver == address(0)) revert ZeroAddress(); uint256 rewardLen = rewardIndexes.length; if (rewardLen == 0) revert EmptyArray(); ( uint256 snapshotId, bytes32[] memory rewards, uint256[] memory snapshotRewards, ) = pxCvx.getEpoch(epoch); // Used to update the redeemed flag locally before updating to the storage all at once for gas efficiency uint256 redeemed = pxCvx.getEpochRedeemedSnapshotRewards( msg.sender, epoch ); // Check whether msg.sender maintained a positive balance before the snapshot uint256 snapshotBalance = pxCvx.balanceOfAt(msg.sender, snapshotId); uint256 snapshotSupply = pxCvx.totalSupplyAt(snapshotId); if (snapshotBalance == 0) revert InsufficientBalance(); emit RedeemSnapshotRewards( epoch, rewardIndexes, receiver, snapshotBalance, snapshotSupply ); for (uint256 i; i < rewardLen; ++i) { uint256 index = rewardIndexes[i]; uint256 indexRedeemed = (1 << index); if ((redeemed & indexRedeemed) != 0) revert AlreadyRedeemed(); redeemed |= indexRedeemed; ERC20(address(uint160(bytes20(rewards[index])))).safeTransfer( receiver, (snapshotRewards[index] * snapshotBalance) / snapshotSupply ); } // Update the redeemed rewards flag in storage to prevent double claimings pxCvx.setEpochRedeemedSnapshotRewards(msg.sender, epoch, redeemed); } /** @notice Redeem futures rewards for rpxCVX holders for an epoch @param epoch uint256 Epoch (ERC1155 token id) @param receiver address Receives futures rewards */ function redeemFuturesRewards(uint256 epoch, address receiver) external whenNotPaused nonReentrant { if (epoch == 0) revert InvalidEpoch(); if (epoch > getCurrentEpoch()) revert InvalidEpoch(); if (receiver == address(0)) revert ZeroAddress(); // Prevent users from burning their futures notes before rewards are claimed (, bytes32[] memory rewards, , uint256[] memory futuresRewards) = pxCvx .getEpoch(epoch); if (rewards.length == 0) revert NoRewards(); emit RedeemFuturesRewards(epoch, receiver, rewards); // Check sender rpxCVX balance uint256 rpxCvxBalance = rpxCvx.balanceOf(msg.sender, epoch); if (rpxCvxBalance == 0) revert InsufficientBalance(); // Store rpxCVX total supply before burning uint256 rpxCvxTotalSupply = rpxCvx.totalSupply(epoch); // Burn rpxCVX tokens rpxCvx.burn(msg.sender, epoch, rpxCvxBalance); uint256 rLen = rewards.length; // Loop over rewards and transfer the amount entitled to the rpxCVX token holder for (uint256 i; i < rLen; ++i) { uint256 rewardAmount = (futuresRewards[i] * rpxCvxBalance) / rpxCvxTotalSupply; // Update reward amount by deducting the amount transferred to the receiver futuresRewards[i] -= rewardAmount; // Proportionate to the % of rpxCVX owned out of the rpxCVX total supply ERC20(address(uint160(bytes20(rewards[i])))).safeTransfer( receiver, rewardAmount ); } // Update future rewards to reflect the amounts remaining post-redemption pxCvx.updateEpochFuturesRewards(epoch, futuresRewards); } /** @notice Exchange one futures token for another @param epoch uint256 Epoch (ERC1155 token id) @param amount uint256 Exchange amount @param receiver address Receives futures token @param f enum Futures enum */ function exchangeFutures( uint256 epoch, uint256 amount, address receiver, Futures f ) external whenNotPaused { // Users can only exchange futures tokens for future epochs if (epoch <= getCurrentEpoch()) revert PastExchangePeriod(); if (amount == 0) revert ZeroAmount(); if (receiver == address(0)) revert ZeroAddress(); ERC1155PresetMinterSupply futuresIn = f == Futures.Vote ? vpxCvx : rpxCvx; ERC1155PresetMinterSupply futuresOut = f == Futures.Vote ? rpxCvx : vpxCvx; emit ExchangeFutures(epoch, amount, receiver, f); // Validates `amount` (balance) futuresIn.burn(msg.sender, epoch, amount); // Validates `to` futuresOut.mint(receiver, epoch, amount, UNUSED_1155_DATA); } /*////////////////////////////////////////////////////////////// EMERGENCY/MIGRATION LOGIC //////////////////////////////////////////////////////////////*/ /** @notice Initialize the emergency executor address @param _emergencyExecutor address Non-Pirex multisig */ function initializeEmergencyExecutor(address _emergencyExecutor) external onlyOwner whenPaused { if (_emergencyExecutor == address(0)) revert ZeroAddress(); if (emergencyExecutor != address(0)) revert AlreadyInitialized(); emergencyExecutor = _emergencyExecutor; emit InitializeEmergencyExecutor(_emergencyExecutor); } /** @notice Set the emergency migration data @param _emergencyMigration EmergencyMigration Emergency migration data */ function setEmergencyMigration( EmergencyMigration calldata _emergencyMigration ) external onlyOwner whenPaused { if (emergencyExecutor == address(0)) revert NoEmergencyExecutor(); if (_emergencyMigration.recipient == address(0)) revert InvalidEmergencyMigration(); if (_emergencyMigration.tokens.length == 0) revert InvalidEmergencyMigration(); emergencyMigration = _emergencyMigration; emit SetEmergencyMigration(_emergencyMigration); } /** @notice Execute the emergency migration */ function executeEmergencyMigration() external whenPaused { if (msg.sender != emergencyExecutor) revert NotAuthorized(); address migrationRecipient = emergencyMigration.recipient; if (migrationRecipient == address(0)) revert InvalidEmergencyMigration(); address[] memory migrationTokens = emergencyMigration.tokens; uint256 tLen = migrationTokens.length; if (tLen == 0) revert InvalidEmergencyMigration(); uint256 o = outstandingRedemptions; for (uint256 i; i < tLen; ++i) { ERC20 token = ERC20(migrationTokens[i]); uint256 balance = token.balanceOf(address(this)); if (token == CVX) { // Transfer the diff between CVX balance and outstandingRedemptions balance = balance > o ? balance - o : 0; } token.safeTransfer(migrationRecipient, balance); } emit ExecuteEmergencyMigration(migrationRecipient, migrationTokens); } /** @notice Set whether the currently set upxCvx is deprecated or not @param state bool Deprecation state */ function setUpxCvxDeprecated(bool state) external onlyOwner whenPaused { upxCvxDeprecated = state; emit SetUpxCvxDeprecated(state); } }
// 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/Rari-Capital/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() { require(locked == 1, "REENTRANCY"); locked = 2; _; locked = 1; } }
// 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/Rari-Capital/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: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; /// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SafeTransferLib.sol) /// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer. /// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller. library SafeTransferLib { event Debug(bool one, bool two, uint256 retsize); /*/////////////////////////////////////////////////////////////// ETH OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferETH(address to, uint256 amount) internal { bool success; assembly { // Transfer the ETH and store if it succeeded or not. success := call(gas(), to, amount, 0, 0, 0, 0) } require(success, "ETH_TRANSFER_FAILED"); } /*/////////////////////////////////////////////////////////////// ERC20 OPERATIONS //////////////////////////////////////////////////////////////*/ function safeTransferFrom( ERC20 token, address from, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument. mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. success := and( // 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(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 100 because the length of our calldata totals up like so: 4 + 32 * 3. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 100, 0, 32) ) } require(success, "TRANSFER_FROM_FAILED"); } function safeTransfer( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // 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(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "TRANSFER_FAILED"); } function safeApprove( ERC20 token, address to, uint256 amount ) internal { bool success; assembly { // Get a pointer to some free memory. let freeMemoryPointer := mload(0x40) // Write the abi-encoded calldata into memory, beginning with the function selector. mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000) mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument. mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. success := and( // 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(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())), // We use 68 because the length of our calldata totals up like so: 4 + 32 * 2. // We use 0 and 32 to copy up to 32 bytes of return data into the scratch space. // Counterintuitively, this call must be positioned second to the or() call in the // surrounding and() call or else returndatasize() will be zero during the computation. call(gas(), token, 0, freeMemoryPointer, 68, 0, 32) ) } require(success, "APPROVE_FAILED"); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Library for converting between addresses and bytes32 values. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/Bytes32AddressLib.sol) library Bytes32AddressLib { function fromLast20Bytes(bytes32 bytesValue) internal pure returns (address) { return address(uint160(uint256(bytesValue))); } function fillLast12Bytes(address addressValue) internal pure returns (bytes32) { return bytes32(bytes20(addressValue)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.12; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; import {ERC20} from "@rari-capital/solmate/src/tokens/ERC20.sol"; import {SafeTransferLib} from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; import {ICvxLocker} from "./interfaces/ICvxLocker.sol"; import {ICvxDelegateRegistry} from "./interfaces/ICvxDelegateRegistry.sol"; contract PirexCvxConvex is Ownable, Pausable { using SafeTransferLib for ERC20; /** @notice Convex reward details @param token address Token @param amount uint256 Amount @param balance uint256 Balance (used for calculating the actual received amount) */ struct ConvexReward { address token; uint256 amount; uint256 balance; } // Configurable contracts enum ConvexContract { CvxLocker, CvxDelegateRegistry } ERC20 public immutable CVX; ICvxLocker public cvxLocker; ICvxDelegateRegistry public cvxDelegateRegistry; // Convex Snapshot space bytes32 public delegationSpace = bytes32("cvx.eth"); // The amount of CVX that needs to remain unlocked for redemptions uint256 public outstandingRedemptions; // The amount of new CVX deposits that is awaiting lock uint256 public pendingLocks; event SetConvexContract(ConvexContract c, address contractAddress); event SetDelegationSpace(string _delegationSpace, bool shouldClear); event SetVoteDelegate(address voteDelegate); event ClearVoteDelegate(); error ZeroAddress(); error EmptyString(); /** @param _CVX address CVX address @param _cvxLocker address CvxLocker address @param _cvxDelegateRegistry address CvxDelegateRegistry address */ constructor( address _CVX, address _cvxLocker, address _cvxDelegateRegistry ) { if (_CVX == address(0)) revert ZeroAddress(); if (_cvxLocker == address(0)) revert ZeroAddress(); if (_cvxDelegateRegistry == address(0)) revert ZeroAddress(); CVX = ERC20(_CVX); cvxLocker = ICvxLocker(_cvxLocker); cvxDelegateRegistry = ICvxDelegateRegistry(_cvxDelegateRegistry); // Max allowance for cvxLocker CVX.safeApprove(address(cvxLocker), type(uint256).max); } /** @notice Set a contract address @param c enum ConvexContract enum @param contractAddress address Contract address */ function setConvexContract(ConvexContract c, address contractAddress) external onlyOwner { if (contractAddress == address(0)) revert ZeroAddress(); emit SetConvexContract(c, contractAddress); if (c == ConvexContract.CvxLocker) { // Revoke approval from the old locker and add allowances to the new locker CVX.safeApprove(address(cvxLocker), 0); cvxLocker = ICvxLocker(contractAddress); CVX.safeApprove(contractAddress, type(uint256).max); return; } cvxDelegateRegistry = ICvxDelegateRegistry(contractAddress); } /** @notice Unlock CVX */ function _unlock() internal { (, uint256 unlockable, , ) = cvxLocker.lockedBalances(address(this)); if (unlockable != 0) cvxLocker.processExpiredLocks(false); } /** @notice Unlock CVX and relock excess */ function _lock() internal { _unlock(); uint256 balance = CVX.balanceOf(address(this)); bool balanceGreaterThanRedemptions = balance > outstandingRedemptions; // Lock CVX if the balance is greater than outstanding redemptions or if there are pending locks if (balanceGreaterThanRedemptions || pendingLocks != 0) { uint256 balanceRedemptionsDifference = balanceGreaterThanRedemptions ? balance - outstandingRedemptions : 0; // Lock amount is the greater of the two: balanceRedemptionsDifference or pendingLocks // balanceRedemptionsDifference is greater if there is unlocked CVX that isn't reserved for redemptions + deposits // pendingLocks is greater if there are more new deposits than unlocked CVX that is reserved for redemptions cvxLocker.lock( address(this), balanceRedemptionsDifference > pendingLocks ? balanceRedemptionsDifference : pendingLocks, 0 ); pendingLocks = 0; } } /** @notice Non-permissioned relock method */ function lock() external whenNotPaused { _lock(); } /** @notice Get claimable rewards and balances @return rewards ConvexReward[] Claimable rewards and balances */ function _claimableRewards() internal view returns (ConvexReward[] memory rewards) { address addr = address(this); // Get claimable rewards ICvxLocker.EarnedData[] memory c = cvxLocker.claimableRewards(addr); uint256 cLen = c.length; rewards = new ConvexReward[](cLen); // Get the current balances for each token to calculate the amount received for (uint256 i; i < cLen; ++i) { rewards[i] = ConvexReward({ token: c[i].token, amount: c[i].amount, balance: ERC20(c[i].token).balanceOf(addr) }); } } /** @notice Claim Convex rewards */ function _getReward() internal { // Claim rewards from Convex cvxLocker.getReward(address(this), false); } /** @notice Set delegationSpace @param _delegationSpace string Convex Snapshot delegation space @param shouldClear bool Whether to clear the vote delegate for current delegation space */ function setDelegationSpace( string memory _delegationSpace, bool shouldClear ) external onlyOwner { if (shouldClear) { // Clear the delegation for the current delegation space clearVoteDelegate(); } bytes memory d = bytes(_delegationSpace); if (d.length == 0) revert EmptyString(); delegationSpace = bytes32(d); emit SetDelegationSpace(_delegationSpace, shouldClear); } /** @notice Set vote delegate @param voteDelegate address Account to delegate votes to */ function setVoteDelegate(address voteDelegate) external onlyOwner { if (voteDelegate == address(0)) revert ZeroAddress(); emit SetVoteDelegate(voteDelegate); cvxDelegateRegistry.setDelegate(delegationSpace, voteDelegate); } /** @notice Remove vote delegate */ function clearVoteDelegate() public onlyOwner { emit ClearVoteDelegate(); cvxDelegateRegistry.clearDelegate(delegationSpace); } /*////////////////////////////////////////////////////////////// EMERGENCY/MIGRATION LOGIC //////////////////////////////////////////////////////////////*/ /** @notice Set the contract's pause state @param state bool Pause state */ function setPauseState(bool state) external onlyOwner { if (state) { _pause(); } else { _unpause(); } } /** @notice Manually unlock CVX in the case of a mass unlock */ function unlock() external whenPaused onlyOwner { cvxLocker.processExpiredLocks(false); } /** @notice Manually relock CVX with a new CvxLocker contract */ function pausedRelock() external whenPaused onlyOwner { _lock(); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.12; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol"; import {ERC20SnapshotSolmate} from "./tokens/ERC20SnapshotSolmate.sol"; contract PxCvx is ERC20SnapshotSolmate("Pirex CVX", "pxCVX", 18), Ownable { /** @notice Epoch details Reward/snapshotRewards/futuresRewards indexes are associated with 1 reward @param snapshotId uint256 Snapshot id @param rewards bytes32[] Rewards @param snapshotRewards uint256[] Snapshot reward amounts @param futuresRewards uint256[] Futures reward amounts @param redeemedSnapshotRewards mapping Redeemed snapshot rewards */ struct Epoch { uint256 snapshotId; bytes32[] rewards; uint256[] snapshotRewards; uint256[] futuresRewards; mapping(address => uint256) redeemedSnapshotRewards; } // Address of currently assigned operator address public operator; // Epochs mapped to epoch details mapping(uint256 => Epoch) private epochs; event SetOperator(address operator); event UpdateEpochFuturesRewards( uint256 indexed epoch, uint256[] futuresRewards ); error NotAuthorized(); error NoOperator(); error Paused(); error ZeroAddress(); error ZeroAmount(); error InvalidEpoch(); error InvalidFuturesRewards(); error MismatchedFuturesRewards(); modifier onlyOperator() { if (msg.sender != operator) revert NotAuthorized(); _; } modifier onlyOperatorOrNotPaused() { address _operator = operator; // Ensure an operator is set if (_operator == address(0)) revert NoOperator(); // This contract shares the same pause state as the operator if (msg.sender != _operator && Pausable(_operator).paused()) revert Paused(); _; } /** @notice Set a new operator address @param _operator address New operator address */ function setOperator(address _operator) external onlyOwner { if (_operator == address(0)) revert ZeroAddress(); emit SetOperator(_operator); // If it's the first operator, also set up 1st epoch with snapshot id 1 // and prevent reward claims until subsequent epochs if (operator == address(0)) { uint256 currentEpoch = getCurrentEpoch(); epochs[currentEpoch].snapshotId = _snapshot(); } operator = _operator; } /** @notice Return the current snapshotId @return uint256 Current snapshot id */ function getCurrentSnapshotId() external view returns (uint256) { return _getCurrentSnapshotId(); } /** @notice Get current epoch @return uint256 Current epoch */ function getCurrentEpoch() public view returns (uint256) { return (block.timestamp / 1209600) * 1209600; } /** @notice Get epoch @param epoch uint256 Epoch @return snapshotId uint256 Snapshot id @return rewards address[] Reward tokens @return snapshotRewards uint256[] Snapshot reward amounts @return futuresRewards uint256[] Futures reward amounts */ function getEpoch(uint256 epoch) external view returns ( uint256 snapshotId, bytes32[] memory rewards, uint256[] memory snapshotRewards, uint256[] memory futuresRewards ) { Epoch storage e = epochs[epoch]; return (e.snapshotId, e.rewards, e.snapshotRewards, e.futuresRewards); } /** @notice Get redeemed snapshot rewards bitmap @param account address Account @param epoch uint256 Epoch @return uint256 Redeemed snapshot bitmap */ function getEpochRedeemedSnapshotRewards(address account, uint256 epoch) external view returns (uint256) { return epochs[epoch].redeemedSnapshotRewards[account]; } /** @notice Add new epoch reward metadata @param epoch uint256 Epoch @param token address Token address @param snapshotReward uint256 Snapshot reward amount @param futuresReward uint256 Futures reward amount */ function addEpochRewardMetadata( uint256 epoch, bytes32 token, uint256 snapshotReward, uint256 futuresReward ) external onlyOperator { Epoch storage e = epochs[epoch]; e.rewards.push(token); e.snapshotRewards.push(snapshotReward); e.futuresRewards.push(futuresReward); } /** @notice Set redeemed snapshot rewards bitmap @param account address Account @param epoch uint256 Epoch @param redeemed uint256 Redeemed bitmap */ function setEpochRedeemedSnapshotRewards( address account, uint256 epoch, uint256 redeemed ) external onlyOperator { epochs[epoch].redeemedSnapshotRewards[account] = redeemed; } /** @notice Update epoch futures rewards to reflect amounts remaining after redemptions @param epoch uint256 Epoch @param futuresRewards uint256[] Futures rewards */ function updateEpochFuturesRewards( uint256 epoch, uint256[] memory futuresRewards ) external onlyOperator { if (epoch == 0) revert InvalidEpoch(); uint256 fLen = epochs[epoch].futuresRewards.length; if (fLen == 0) revert InvalidEpoch(); if (futuresRewards.length == 0) revert InvalidFuturesRewards(); if (futuresRewards.length != fLen) revert MismatchedFuturesRewards(); epochs[epoch].futuresRewards = futuresRewards; emit UpdateEpochFuturesRewards(epoch, futuresRewards); } /** @notice Mint the specified amount of tokens to the specified account @param account address Receiver of the tokens @param amount uint256 Amount to be minted */ function mint(address account, uint256 amount) external onlyOperator { if (account == address(0)) revert ZeroAddress(); if (amount == 0) revert ZeroAmount(); _mint(account, amount); } /** @notice Burn the specified amount of tokens from the specified account @param account address Owner of the tokens @param amount uint256 Amount to be burned */ function burn(address account, uint256 amount) external onlyOperator { if (account == address(0)) revert ZeroAddress(); if (amount == 0) revert ZeroAmount(); _burn(account, amount); } /** @notice Approve allowances by operator with specified accounts and amount @param from address Owner of the tokens @param to address Account to be approved @param amount uint256 Amount to be approved */ function operatorApprove( address from, address to, uint256 amount ) external onlyOperator { if (from == address(0)) revert ZeroAddress(); if (to == address(0)) revert ZeroAddress(); if (amount == 0) revert ZeroAmount(); _approve(from, to, amount); } /** @notice Snapshot token balances for the current epoch */ function takeEpochSnapshot() external onlyOperatorOrNotPaused { uint256 currentEpoch = getCurrentEpoch(); // If snapshot has not been set for current epoch, take snapshot if (epochs[currentEpoch].snapshotId == 0) { epochs[currentEpoch].snapshotId = _snapshot(); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.12; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {ERC20} from "@rari-capital/solmate/src/tokens/ERC20.sol"; import {SafeTransferLib} from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; contract PirexFees is Ownable { using SafeTransferLib for ERC20; // Types of fee recipients enum FeeRecipient { Treasury, Contributors } uint8 public constant PERCENT_DENOMINATOR = 100; // Configurable fee recipient percent-share uint8 public treasuryPercent = 75; // Configurable fee recipient addresses address public treasury; address public contributors; event SetFeeRecipient(FeeRecipient f, address recipient); event SetTreasuryPercent(uint8 _treasuryPercent); event DistributeFees(address token, uint256 amount); error ZeroAddress(); error InvalidFeePercent(); /** @param _treasury address Redacted treasury @param _contributors address Pirex contributor multisig */ constructor(address _treasury, address _contributors) { if (_treasury == address(0)) revert ZeroAddress(); if (_contributors == address(0)) revert ZeroAddress(); treasury = _treasury; contributors = _contributors; } /** @notice Set a fee recipient address @param f enum FeeRecipient enum @param recipient address Fee recipient address */ function setFeeRecipient(FeeRecipient f, address recipient) external onlyOwner { if (recipient == address(0)) revert ZeroAddress(); emit SetFeeRecipient(f, recipient); if (f == FeeRecipient.Treasury) { treasury = recipient; return; } contributors = recipient; } /** @notice Set treasury fee percent @param _treasuryPercent uint8 Treasury fee percent */ function setTreasuryPercent(uint8 _treasuryPercent) external onlyOwner { // Treasury fee percent should never exceed 75 if (_treasuryPercent > 75) revert InvalidFeePercent(); treasuryPercent = _treasuryPercent; emit SetTreasuryPercent(_treasuryPercent); } /** @notice Distribute fees @param from address Fee source @param token address Fee token @param amount uint256 Fee token amount */ function distributeFees( address from, address token, uint256 amount ) external { emit DistributeFees(token, amount); ERC20 t = ERC20(token); uint256 treasuryDistribution = (amount * treasuryPercent) / PERCENT_DENOMINATOR; // Favoring push over pull to reduce accounting complexity for different tokens t.safeTransferFrom(from, treasury, treasuryDistribution); t.safeTransferFrom(from, contributors, amount - treasuryDistribution); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.12; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {ERC4626} from "@rari-capital/solmate/src/mixins/ERC4626.sol"; import {ERC20} from "@rari-capital/solmate/src/tokens/ERC20.sol"; import {FixedPointMathLib} from "@rari-capital/solmate/src/utils/FixedPointMathLib.sol"; import {SafeTransferLib} from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; import {UnionPirexStaking} from "./UnionPirexStaking.sol"; contract UnionPirexVault is Ownable, ERC4626 { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; UnionPirexStaking public strategy; uint256 public constant MAX_WITHDRAWAL_PENALTY = 500; uint256 public constant MAX_PLATFORM_FEE = 2000; uint256 public constant FEE_DENOMINATOR = 10000; uint256 public withdrawalPenalty = 300; uint256 public platformFee = 1000; address public platform; event Harvest(address indexed caller, uint256 value); event WithdrawalPenaltyUpdated(uint256 penalty); event PlatformFeeUpdated(uint256 fee); event PlatformUpdated(address indexed _platform); event StrategySet(address indexed _strategy); error ZeroAddress(); error ExceedsMax(); error AlreadySet(); constructor(address pxCvx) ERC4626(ERC20(pxCvx), "Union Pirex", "uCVX") {} /** @notice Set the withdrawal penalty @param penalty uint256 Withdrawal penalty */ function setWithdrawalPenalty(uint256 penalty) external onlyOwner { if (penalty > MAX_WITHDRAWAL_PENALTY) revert ExceedsMax(); withdrawalPenalty = penalty; emit WithdrawalPenaltyUpdated(penalty); } /** @notice Set the platform fee @param fee uint256 Platform fee */ function setPlatformFee(uint256 fee) external onlyOwner { if (fee > MAX_PLATFORM_FEE) revert ExceedsMax(); platformFee = fee; emit PlatformFeeUpdated(fee); } /** @notice Set the platform @param _platform address Platform */ function setPlatform(address _platform) external onlyOwner { if (_platform == address(0)) revert ZeroAddress(); platform = _platform; emit PlatformUpdated(_platform); } /** @notice Set the strategy @param _strategy address Strategy */ function setStrategy(address _strategy) external onlyOwner { if (_strategy == address(0)) revert ZeroAddress(); if (address(strategy) != address(0)) revert AlreadySet(); // Set new strategy contract and approve max allowance strategy = UnionPirexStaking(_strategy); asset.safeApprove(_strategy, type(uint256).max); emit StrategySet(_strategy); } /** @notice Get the pxCVX custodied by the UnionPirex contracts @return uint256 Assets */ function totalAssets() public view override returns (uint256) { // Vault assets + rewards should always be stored in strategy until withdrawal-time (uint256 _totalSupply, uint256 rewards) = strategy .totalSupplyWithRewards(); // Deduct the exact reward amount staked (after fees are deducted when calling `harvest`) return _totalSupply + ( rewards == 0 ? 0 : (rewards - ((rewards * platformFee) / FEE_DENOMINATOR)) ); } /** @notice Withdraw assets from the staking contract to prepare for transfer to user @param assets uint256 Assets */ function beforeWithdraw(uint256 assets, uint256) internal override { // Harvest rewards in the event where there is not enough staked assets to cover the withdrawal if (assets > strategy.totalSupply()) harvest(); strategy.withdraw(assets); } /** @notice Stake assets so that rewards can be properly distributed @param assets uint256 Assets */ function afterDeposit(uint256 assets, uint256) internal override { strategy.stake(assets); } /** @notice Preview the amount of assets a user would receive from redeeming shares @param shares uint256 Shares @return uint256 Assets */ function previewRedeem(uint256 shares) public view override returns (uint256) { // Calculate assets based on a user's % ownership of vault shares uint256 assets = convertToAssets(shares); uint256 _totalSupply = totalSupply; // Calculate a penalty - zero if user is the last to withdraw uint256 penalty = (_totalSupply == 0 || _totalSupply - shares == 0) ? 0 : assets.mulDivDown(withdrawalPenalty, FEE_DENOMINATOR); // Redeemable amount is the post-penalty amount return assets - penalty; } /** @notice Preview the amount of shares a user would need to redeem the specified asset amount @notice This modified version takes into consideration the withdrawal fee @param assets uint256 Assets @return uint256 Shares */ function previewWithdraw(uint256 assets) public view override returns (uint256) { // Calculate shares based on the specified assets' proportion of the pool uint256 shares = convertToShares(assets); // Save 1 SLOAD uint256 _totalSupply = totalSupply; // Factor in additional shares to fulfill withdrawal if user is not the last to withdraw return (_totalSupply == 0 || _totalSupply - shares == 0) ? shares : (shares * FEE_DENOMINATOR) / (FEE_DENOMINATOR - withdrawalPenalty); } /** @notice Harvest rewards */ function harvest() public { // Claim rewards strategy.getReward(); // Since we don't normally store pxCVX within the vault, a non-zero balance equals rewards uint256 rewards = asset.balanceOf(address(this)); emit Harvest(msg.sender, rewards); if (rewards != 0) { // Fee for platform uint256 feeAmount = (rewards * platformFee) / FEE_DENOMINATOR; // Deduct fee from reward balance rewards -= feeAmount; // Claimed rewards should be in pxCVX asset.safeTransfer(platform, feeAmount); // Stake rewards sans fee strategy.stake(rewards); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/presets/ERC1155PresetMinterPauser.sol) pragma solidity 0.8.12; import {ERC1155} from "@rari-capital/solmate/src/tokens/ERC1155.sol"; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; import {Context} from "@openzeppelin/contracts/utils/Context.sol"; /** * @dev {ERC1155} token, including: * * - ability to check the total supply for a token id * - ability for holders to burn (destroy) their tokens * - a minter role that allows for token minting (creation) * * This contract uses {AccessControl} to lock permissioned functions using the * different roles - head to its documentation for details. * * The account that deploys the contract will be granted the minter and pauser * roles, as well as the default admin role, which will let it grant both minter * and pauser roles to other accounts. * * _Deprecated in favor of https://wizard.openzeppelin.com/[Contracts Wizard]._ */ contract ERC1155Solmate is AccessControl, ERC1155 { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); event GrantMinterRole(address minter); event RevokeMinterRole(address minter); error ZeroAddress(); error NotMinter(); constructor() { _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); } /** @notice Grant the minter role to an address @param minter address Address to grant the minter role */ function grantMinterRole(address minter) external onlyRole(DEFAULT_ADMIN_ROLE) { if (minter == address(0)) revert ZeroAddress(); _grantRole(MINTER_ROLE, minter); emit GrantMinterRole(minter); } /** @notice Revoke the minter role from an address @param minter address Address to revoke the minter role */ function revokeMinterRole(address minter) external onlyRole(DEFAULT_ADMIN_ROLE) { if (hasRole(MINTER_ROLE, minter) == false) revert NotMinter(); _revokeRole(MINTER_ROLE, minter); emit RevokeMinterRole(minter); } /** * @dev Creates `amount` new tokens for `to`, of token type `id`. * * See {ERC1155-_mint}. * * Requirements: * * - the caller must have the `MINTER_ROLE`. */ function mint( address to, uint256 id, uint256 amount, bytes calldata data ) external onlyRole(MINTER_ROLE) { _mint(to, id, amount, data); } function mintBatch( address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external onlyRole(MINTER_ROLE) { _batchMint(to, ids, amounts, data); } function burnBatch( address from, uint256[] calldata ids, uint256[] calldata amounts ) external onlyRole(MINTER_ROLE) { _batchBurn(from, ids, amounts); } function burn( address from, uint256 id, uint256 amount ) external onlyRole(MINTER_ROLE) { _burn(from, id, amount); } function uri(uint256 id) public view override returns (string memory) {} // Necessary override due to AccessControl having the same method function supportsInterface(bytes4 interfaceId) public pure override(AccessControl, ERC1155) returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.12; import {ERC1155} from "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import {ERC1155Supply} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Supply.sol"; import {ERC1155Burnable} from "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol"; import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol"; import {Context} from "@openzeppelin/contracts/utils/Context.sol"; /** * @dev {ERC1155} token, including: * * - ability to check the total supply for a token id * - ability for holders to burn (destroy) their tokens * - a minter role that allows for token minting (creation) * * This contract uses {AccessControl} to lock permissioned functions using the * different roles - head to its documentation for details. * * The account that deploys the contract will be granted the minter role as well * as the default admin role, which will let it grant the minter role to other accounts. */ contract ERC1155PresetMinterSupply is Context, AccessControl, ERC1155Supply, ERC1155Burnable { bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE"); /** * @dev Grants `DEFAULT_ADMIN_ROLE` and `MINTER_ROLE` to the account that * deploys the contract. */ constructor(string memory uri) ERC1155(uri) { _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); _setupRole(MINTER_ROLE, _msgSender()); } /** * @dev Creates `amount` new tokens for `to`, of token type `id`. * * See {ERC1155-_mint}. * * Requirements: * * - the caller must have the `MINTER_ROLE`. */ function mint( address to, uint256 id, uint256 amount, bytes memory data ) public virtual { require( hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint" ); _mint(to, id, amount, data); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] variant of {mint}. */ function mintBatch( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual { require( hasRole(MINTER_ROLE, _msgSender()), "ERC1155PresetMinterPauser: must have minter role to mint" ); _mintBatch(to, ids, amounts, data); } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(AccessControl, ERC1155) returns (bool) { return super.supportsInterface(interfaceId); } function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual override(ERC1155, ERC1155Supply) { super._beforeTokenTransfer(operator, from, to, ids, amounts, data); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.12; interface IVotiumMultiMerkleStash { struct claimParam { address token; uint256 index; uint256 amount; bytes32[] merkleProof; } function claim( address token, uint256 index, address account, uint256 amount, bytes32[] calldata merkleProof ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.12; interface ICvxLocker { struct LockedBalance { uint112 amount; uint112 boosted; uint32 unlockTime; } struct EarnedData { address token; uint256 amount; } function lock( address _account, uint256 _amount, uint256 _spendRatio ) external; function lockedBalances(address _user) external view returns ( uint256 total, uint256 unlockable, uint256 locked, LockedBalance[] memory lockData ); function processExpiredLocks(bool _relock) external; function claimableRewards(address _account) external view returns (EarnedData[] memory userRewards); function getReward(address _account, bool _stake) external; function lockedBalanceOf(address _user) external view returns (uint256 amount); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { require(owner() == _msgSender(), "Ownable: caller is not the owner"); _; } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions anymore. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby removing any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (security/Pausable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which allows children to implement an emergency stop * mechanism that can be triggered by an authorized account. * * This module is used through inheritance. It will make available the * modifiers `whenNotPaused` and `whenPaused`, which can be applied to * the functions of your contract. Note that they will not be pausable by * simply including this module, only once the modifiers are put in place. */ abstract contract Pausable is Context { /** * @dev Emitted when the pause is triggered by `account`. */ event Paused(address account); /** * @dev Emitted when the pause is lifted by `account`. */ event Unpaused(address account); bool private _paused; /** * @dev Initializes the contract in unpaused state. */ constructor() { _paused = false; } /** * @dev Returns true if the contract is paused, and false otherwise. */ function paused() public view virtual returns (bool) { return _paused; } /** * @dev Modifier to make a function callable only when the contract is not paused. * * Requirements: * * - The contract must not be paused. */ modifier whenNotPaused() { require(!paused(), "Pausable: paused"); _; } /** * @dev Modifier to make a function callable only when the contract is paused. * * Requirements: * * - The contract must be paused. */ modifier whenPaused() { require(paused(), "Pausable: not paused"); _; } /** * @dev Triggers stopped state. * * Requirements: * * - The contract must not be paused. */ function _pause() internal virtual whenNotPaused { _paused = true; emit Paused(_msgSender()); } /** * @dev Returns to normal state. * * Requirements: * * - The contract must be paused. */ function _unpause() internal virtual whenPaused { _paused = false; emit Unpaused(_msgSender()); } }
// SPDX-License-Identifier: LGPL-3.0-only pragma solidity 0.8.12; interface ICvxDelegateRegistry { function setDelegate(bytes32 id, address delegate) external; function clearDelegate(bytes32 id) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/ERC20Snapshot.sol) pragma solidity ^0.8.0; import {Arrays} from "@openzeppelin/contracts/utils/Arrays.sol"; import {Counters} from "@openzeppelin/contracts/utils/Counters.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; /// @notice Modern and gas efficient ERC20 + EIP-2612 implementation. /// @author Solmate (https://github.com/Rari-Capital/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 _approve( address owner, address spender, uint256 amount ) internal { allowance[owner][spender] = amount; emit Approval(owner, spender, amount); } function transfer(address to, uint256 amount) public virtual returns (bool) { _beforeTokenTransfer(msg.sender, to, amount); 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) { _beforeTokenTransfer(from, to, amount); 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 = ECDSA.recover( 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 { _beforeTokenTransfer(address(0), to, amount); 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 { _beforeTokenTransfer(from, address(0), amount); 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); } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual {} } /** * @dev This contract extends an ERC20 token with a snapshot mechanism. When a snapshot is created, the balances and * total supply at the time are recorded for later access. * * This can be used to safely create mechanisms based on token balances such as trustless dividends or weighted voting. * In naive implementations it's possible to perform a "double spend" attack by reusing the same balance from different * accounts. By using snapshots to calculate dividends or voting power, those attacks no longer apply. It can also be * used to create an efficient ERC20 forking mechanism. * * Snapshots are created by the internal {_snapshot} function, which will emit the {Snapshot} event and return a * snapshot id. To get the total supply at the time of a snapshot, call the function {totalSupplyAt} with the snapshot * id. To get the balance of an account at the time of a snapshot, call the {balanceOfAt} function with the snapshot id * and the account address. * * NOTE: Snapshot policy can be customized by overriding the {_getCurrentSnapshotId} method. For example, having it * return `block.number` will trigger the creation of snapshot at the begining of each new block. When overridding this * function, be careful about the monotonicity of its result. Non-monotonic snapshot ids will break the contract. * * Implementing snapshots for every block using this method will incur significant gas costs. For a gas-efficient * alternative consider {ERC20Votes}. * * ==== Gas Costs * * Snapshots are efficient. Snapshot creation is _O(1)_. Retrieval of balances or total supply from a snapshot is _O(log * n)_ in the number of snapshots that have been created, although _n_ for a specific account will generally be much * smaller since identical balances in subsequent snapshots are stored as a single entry. * * There is a constant overhead for normal ERC20 transfers due to the additional snapshot bookkeeping. This overhead is * only significant for the first transfer that immediately follows a snapshot for a particular account. Subsequent * transfers will have normal cost until the next snapshot, and so on. */ contract ERC20SnapshotSolmate is ERC20 { // Inspired by Jordi Baylina's MiniMeToken to record historical balances: // https://github.com/Giveth/minimd/blob/ea04d950eea153a04c51fa510b068b9dded390cb/contracts/MiniMeToken.sol using Arrays for uint256[]; using Counters for Counters.Counter; // Snapshotted values have arrays of ids and the value corresponding to that id. These could be an array of a // Snapshot struct, but that would impede usage of functions that work on an array. struct Snapshots { uint256[] ids; uint256[] values; } mapping(address => Snapshots) private _accountBalanceSnapshots; Snapshots private _totalSupplySnapshots; // Snapshot ids increase monotonically, with the first value being 1. An id of 0 is invalid. Counters.Counter private _currentSnapshotId; /** * @dev Emitted by {_snapshot} when a snapshot identified by `id` is created. */ event Snapshot(uint256 id); constructor( string memory _name, string memory _symbol, uint8 _decimals ) ERC20(_name, _symbol, _decimals) {} /** * @dev Creates a new snapshot and returns its snapshot id. * * Emits a {Snapshot} event that contains the same id. * * {_snapshot} is `internal` and you have to decide how to expose it externally. Its usage may be restricted to a * set of accounts, for example using {AccessControl}, or it may be open to the public. * * [WARNING] * ==== * While an open way of calling {_snapshot} is required for certain trust minimization mechanisms such as forking, * you must consider that it can potentially be used by attackers in two ways. * * First, it can be used to increase the cost of retrieval of values from snapshots, although it will grow * logarithmically thus rendering this attack ineffective in the long term. Second, it can be used to target * specific accounts and increase the cost of ERC20 transfers for them, in the ways specified in the Gas Costs * section above. * * We haven't measured the actual numbers; if this is something you're interested in please reach out to us. * ==== */ function _snapshot() internal virtual returns (uint256) { _currentSnapshotId.increment(); uint256 currentId = _getCurrentSnapshotId(); emit Snapshot(currentId); return currentId; } /** * @dev Get the current snapshotId */ function _getCurrentSnapshotId() internal view virtual returns (uint256) { return _currentSnapshotId.current(); } /** * @dev Retrieves the balance of `account` at the time `snapshotId` was created. */ function balanceOfAt(address account, uint256 snapshotId) public view virtual returns (uint256) { (bool snapshotted, uint256 value) = _valueAt( snapshotId, _accountBalanceSnapshots[account] ); return snapshotted ? value : balanceOf[account]; } /** * @dev Retrieves the total supply at the time `snapshotId` was created. */ function totalSupplyAt(uint256 snapshotId) public view virtual returns (uint256) { (bool snapshotted, uint256 value) = _valueAt( snapshotId, _totalSupplySnapshots ); return snapshotted ? value : totalSupply; } // Update balance and/or total supply snapshots before the values are modified. This is implemented // in the _beforeTokenTransfer hook, which is executed for _mint, _burn, and _transfer operations. function _beforeTokenTransfer( address from, address to, uint256 amount ) internal virtual override { super._beforeTokenTransfer(from, to, amount); if (from == address(0)) { // mint _updateAccountSnapshot(to); _updateTotalSupplySnapshot(); } else if (to == address(0)) { // burn _updateAccountSnapshot(from); _updateTotalSupplySnapshot(); } else { // transfer _updateAccountSnapshot(from); _updateAccountSnapshot(to); } } function _valueAt(uint256 snapshotId, Snapshots storage snapshots) private view returns (bool, uint256) { require(snapshotId > 0, "ERC20Snapshot: id is 0"); require( snapshotId <= _getCurrentSnapshotId(), "ERC20Snapshot: nonexistent id" ); // When a valid snapshot is queried, there are three possibilities: // a) The queried value was not modified after the snapshot was taken. Therefore, a snapshot entry was never // created for this id, and all stored snapshot ids are smaller than the requested one. The value that corresponds // to this id is the current one. // b) The queried value was modified after the snapshot was taken. Therefore, there will be an entry with the // requested id, and its value is the one to return. // c) More snapshots were created after the requested one, and the queried value was later modified. There will be // no entry for the requested id: the value that corresponds to it is that of the smallest snapshot id that is // larger than the requested one. // // In summary, we need to find an element in an array, returning the index of the smallest value that is larger if // it is not found, unless said value doesn't exist (e.g. when all values are smaller). Arrays.findUpperBound does // exactly this. uint256 index = snapshots.ids.findUpperBound(snapshotId); if (index == snapshots.ids.length) { return (false, 0); } else { return (true, snapshots.values[index]); } } function _updateAccountSnapshot(address account) private { _updateSnapshot(_accountBalanceSnapshots[account], balanceOf[account]); } function _updateTotalSupplySnapshot() private { _updateSnapshot(_totalSupplySnapshots, totalSupply); } function _updateSnapshot(Snapshots storage snapshots, uint256 currentValue) private { uint256 currentId = _getCurrentSnapshotId(); if (_lastSnapshotId(snapshots.ids) < currentId) { snapshots.ids.push(currentId); snapshots.values.push(currentValue); } } function _lastSnapshotId(uint256[] storage ids) private view returns (uint256) { uint256 idsLen = ids.length; if (idsLen == 0) { return 0; } else { return ids[idsLen - 1]; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Arrays.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; /** * @dev Collection of functions related to array types. */ library Arrays { /** * @dev Searches a sorted `array` and returns the first index that contains * a value greater or equal to `element`. If no such index exists (i.e. all * values in the array are strictly less than `element`), the array length is * returned. Time complexity O(log n). * * `array` is expected to be sorted in ascending order, and to contain no * repeated elements. */ function findUpperBound(uint256[] storage array, uint256 element) internal view returns (uint256) { if (array.length == 0) { return 0; } uint256 low = 0; uint256 high = array.length; while (low < high) { uint256 mid = Math.average(low, high); // Note that mid will always be strictly less than high (i.e. it will be a valid array index) // because Math.average rounds down (it does integer division with truncation). if (array[mid] > element) { high = mid; } else { low = mid + 1; } } // At this point `low` is the exclusive upper bound. We will return the inclusive upper bound. if (low > 0 && array[low - 1] == element) { return low - 1; } else { return low; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) pragma solidity ^0.8.0; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` */ library Counters { struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { unchecked { counter._value += 1; } } function decrement(Counter storage counter) internal { uint256 value = counter._value; require(value > 0, "Counter: decrement overflow"); unchecked { counter._value = value - 1; } } function reset(Counter storage counter) internal { counter._value = 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../Strings.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } else if (error == RecoverError.InvalidSignatureV) { revert("ECDSA: invalid signature 'v' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { // Check the signature length // - case 65: r,s,v signature (standard) // - case 64: r,vs signature (cf https://eips.ethereum.org/EIPS/eip-2098) _Available since v4.1._ if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else if (signature.length == 64) { bytes32 r; bytes32 vs; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. assembly { r := mload(add(signature, 0x20)) vs := mload(add(signature, 0x40)) } return tryRecover(hash, r, vs); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover( bytes32 hash, bytes32 r, bytes32 vs ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } if (v != 27 && v != 28) { return (address(0), RecoverError.InvalidSignatureV); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) { // 32 is the length in bytes of hash, // enforced by the type signature above return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)); } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x01", domainSeparator, structHash)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a >= b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a / b + (a % b == 0 ? 0 : 1); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) pragma solidity ^0.8.0; /** * @dev String operations. */ library Strings { bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); while (value != 0) { digits -= 1; buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); value /= 10; } return string(buffer); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { if (value == 0) { return "0x00"; } uint256 temp = value; uint256 length = 0; while (temp != 0) { length++; temp >>= 8; } return toHexString(value, length); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _HEX_SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; import {ERC20} from "../tokens/ERC20.sol"; import {SafeTransferLib} from "../utils/SafeTransferLib.sol"; import {FixedPointMathLib} from "../utils/FixedPointMathLib.sol"; /// @notice Minimal ERC4626 tokenized Vault implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/mixins/ERC4626.sol) abstract contract ERC4626 is ERC20 { using SafeTransferLib for ERC20; using FixedPointMathLib for uint256; /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /*/////////////////////////////////////////////////////////////// IMMUTABLES //////////////////////////////////////////////////////////////*/ ERC20 public immutable asset; constructor( ERC20 _asset, string memory _name, string memory _symbol ) ERC20(_name, _symbol, _asset.decimals()) { asset = _asset; } /*/////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LOGIC //////////////////////////////////////////////////////////////*/ function deposit(uint256 assets, address receiver) public virtual returns (uint256 shares) { // Check for rounding error since we round down in previewDeposit. require((shares = previewDeposit(assets)) != 0, "ZERO_SHARES"); // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function mint(uint256 shares, address receiver) public virtual returns (uint256 assets) { assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up. // Need to transfer before minting or ERC777s could reenter. asset.safeTransferFrom(msg.sender, address(this), assets); _mint(receiver, shares); emit Deposit(msg.sender, receiver, assets, shares); afterDeposit(assets, shares); } function withdraw( uint256 assets, address receiver, address owner ) public virtual returns (uint256 shares) { shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up. if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } function redeem( uint256 shares, address receiver, address owner ) public virtual returns (uint256 assets) { if (msg.sender != owner) { uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals. if (allowed != type(uint256).max) allowance[owner][msg.sender] = allowed - shares; } // Check for rounding error since we round down in previewRedeem. require((assets = previewRedeem(shares)) != 0, "ZERO_ASSETS"); beforeWithdraw(assets, shares); _burn(owner, shares); emit Withdraw(msg.sender, receiver, owner, assets, shares); asset.safeTransfer(receiver, assets); } /*/////////////////////////////////////////////////////////////// ACCOUNTING LOGIC //////////////////////////////////////////////////////////////*/ function totalAssets() public view virtual returns (uint256); function convertToShares(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivDown(supply, totalAssets()); } function convertToAssets(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivDown(totalAssets(), supply); } function previewDeposit(uint256 assets) public view virtual returns (uint256) { return convertToShares(assets); } function previewMint(uint256 shares) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? shares : shares.mulDivUp(totalAssets(), supply); } function previewWithdraw(uint256 assets) public view virtual returns (uint256) { uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero. return supply == 0 ? assets : assets.mulDivUp(supply, totalAssets()); } function previewRedeem(uint256 shares) public view virtual returns (uint256) { return convertToAssets(shares); } /*/////////////////////////////////////////////////////////////// DEPOSIT/WITHDRAWAL LIMIT LOGIC //////////////////////////////////////////////////////////////*/ function maxDeposit(address) public view virtual returns (uint256) { return type(uint256).max; } function maxMint(address) public view virtual returns (uint256) { return type(uint256).max; } function maxWithdraw(address owner) public view virtual returns (uint256) { return convertToAssets(balanceOf[owner]); } function maxRedeem(address owner) public view virtual returns (uint256) { return balanceOf[owner]; } /*/////////////////////////////////////////////////////////////// INTERNAL HOOKS LOGIC //////////////////////////////////////////////////////////////*/ function beforeWithdraw(uint256 assets, uint256 shares) internal virtual {} function afterDeposit(uint256 assets, uint256 shares) internal virtual {} }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Arithmetic library with operations for fixed-point numbers. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol) /// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol) library FixedPointMathLib { /*/////////////////////////////////////////////////////////////// SIMPLIFIED FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s. function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down. } function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up. } function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down. } function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up. } /*/////////////////////////////////////////////////////////////// LOW LEVEL FIXED POINT OPERATIONS //////////////////////////////////////////////////////////////*/ function mulDivDown( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // Divide z by the denominator. z := div(z, denominator) } } function mulDivUp( uint256 x, uint256 y, uint256 denominator ) internal pure returns (uint256 z) { assembly { // Store x * y in z for now. z := mul(x, y) // Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y)) if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) { revert(0, 0) } // First, divide z - 1 by the denominator and add 1. // We allow z - 1 to underflow if z is 0, because we multiply the // end result by 0 if z is zero, ensuring we return 0 if z is zero. z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1)) } } function rpow( uint256 x, uint256 n, uint256 scalar ) internal pure returns (uint256 z) { assembly { switch x case 0 { switch n case 0 { // 0 ** 0 = 1 z := scalar } default { // 0 ** n = 0 z := 0 } } default { switch mod(n, 2) case 0 { // If n is even, store scalar in z for now. z := scalar } default { // If n is odd, store x in z for now. z := x } // Shifting right by 1 is like dividing by 2. let half := shr(1, scalar) for { // Shift n right by 1 before looping to halve it. n := shr(1, n) } n { // Shift n right by 1 each iteration to halve it. n := shr(1, n) } { // Revert immediately if x ** 2 would overflow. // Equivalent to iszero(eq(div(xx, x), x)) here. if shr(128, x) { revert(0, 0) } // Store x squared. let xx := mul(x, x) // Round to the nearest number. let xxRound := add(xx, half) // Revert if xx + half overflowed. if lt(xxRound, xx) { revert(0, 0) } // Set x to scaled xxRound. x := div(xxRound, scalar) // If n is even: if mod(n, 2) { // Compute z * x. let zx := mul(z, x) // If z * x overflowed: if iszero(eq(div(zx, x), z)) { // Revert if x is non-zero. if iszero(iszero(x)) { revert(0, 0) } } // Round to the nearest number. let zxRound := add(zx, half) // Revert if zx + half overflowed. if lt(zxRound, zx) { revert(0, 0) } // Return properly scaled zxRound. z := div(zxRound, scalar) } } } } } /*/////////////////////////////////////////////////////////////// GENERAL NUMBER UTILITIES //////////////////////////////////////////////////////////////*/ function sqrt(uint256 x) internal pure returns (uint256 z) { assembly { // Start off with z at 1. z := 1 // Used below to help find a nearby power of 2. let y := x // Find the lowest power of 2 that is at least sqrt(x). if iszero(lt(y, 0x100000000000000000000000000000000)) { y := shr(128, y) // Like dividing by 2 ** 128. z := shl(64, z) // Like multiplying by 2 ** 64. } if iszero(lt(y, 0x10000000000000000)) { y := shr(64, y) // Like dividing by 2 ** 64. z := shl(32, z) // Like multiplying by 2 ** 32. } if iszero(lt(y, 0x100000000)) { y := shr(32, y) // Like dividing by 2 ** 32. z := shl(16, z) // Like multiplying by 2 ** 16. } if iszero(lt(y, 0x10000)) { y := shr(16, y) // Like dividing by 2 ** 16. z := shl(8, z) // Like multiplying by 2 ** 8. } if iszero(lt(y, 0x100)) { y := shr(8, y) // Like dividing by 2 ** 8. z := shl(4, z) // Like multiplying by 2 ** 4. } if iszero(lt(y, 0x10)) { y := shr(4, y) // Like dividing by 2 ** 4. z := shl(2, z) // Like multiplying by 2 ** 2. } if iszero(lt(y, 0x8)) { // Equivalent to 2 ** z. z := shl(1, z) } // Shifting right by 1 is like dividing by 2. 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))) // Compute a rounded down version of z. let zRoundDown := div(x, z) // If zRoundDown is smaller, use it. if lt(zRoundDown, z) { z := zRoundDown } } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.12; import {ERC20} from "@rari-capital/solmate/src/tokens/ERC20.sol"; import {SafeTransferLib} from "@rari-capital/solmate/src/utils/SafeTransferLib.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; // https://docs.synthetix.io/contracts/source/contracts/StakingRewards/ // https://github.com/Synthetixio/synthetix/blob/v2.66.0/contracts/StakingRewards.sol /** Modifications - Pin pragma to 0.8.12 - Remove IStakingRewards, RewardsDistributionRecipient, and Pausable - Add and inherit from Ownable - Add `RewardsDistributionRecipient` logic to contract - Add `vault` state variable and `onlyVault` modifier - Add `onlyVault` modifier to `stake` method - Change `rewardsDuration` to 14 days - Update contract to support only the vault as a user - Remove SafeMath since pragma 0.8.0 has those checks built-in - Replace OpenZeppelin ERC20, ReentrancyGuard, and SafeERC20 with Solmate v6 (audited) - Consolidate `rewardsToken` and `stakingToken` since they're the same - Remove `onlyVault` modifier from getReward - Remove ReentrancyGuard as it is no longer needed - Add `totalSupplyWithRewards` method to save gas as _totalSupply + rewards are accessed by vault - Updated `notifyRewardsAmount` - Remove the method parameter and compute the reward amount inside the function - Remove the conditional logic since we will always distribute the rewards balance - Remove overflow check since the caller cannot pass in the reward amount */ contract UnionPirexStaking is Ownable { using SafeTransferLib for ERC20; /* ========== STATE VARIABLES ========== */ address public immutable vault; ERC20 public immutable token; uint256 public constant rewardsDuration = 14 days; address public distributor; uint256 public periodFinish; uint256 public rewardRate; uint256 public lastUpdateTime; uint256 public rewardPerTokenStored; uint256 public userRewardPerTokenPaid; uint256 public rewards; uint256 internal _totalSupply; /* ========== CONSTRUCTOR ========== */ constructor( address _token, address _distributor, address _vault ) { token = ERC20(_token); distributor = _distributor; vault = _vault; } /* ========== VIEWS ========== */ function totalSupply() external view returns (uint256) { return _totalSupply; } function totalSupplyWithRewards() external view returns (uint256, uint256) { uint256 t = _totalSupply; return ( t, ((t * (rewardPerToken() - userRewardPerTokenPaid)) / 1e18) + rewards ); } function lastTimeRewardApplicable() public view returns (uint256) { return block.timestamp < periodFinish ? block.timestamp : periodFinish; } function rewardPerToken() public view returns (uint256) { if (_totalSupply == 0) { return rewardPerTokenStored; } return rewardPerTokenStored + ((((lastTimeRewardApplicable() - lastUpdateTime) * rewardRate) * 1e18) / _totalSupply); } function earned() public view returns (uint256) { return ((_totalSupply * (rewardPerToken() - userRewardPerTokenPaid)) / 1e18) + rewards; } function getRewardForDuration() external view returns (uint256) { return rewardRate * rewardsDuration; } /* ========== MUTATIVE FUNCTIONS ========== */ function stake(uint256 amount) external onlyVault updateReward(vault) { require(amount > 0, "Cannot stake 0"); _totalSupply += amount; token.safeTransferFrom(vault, address(this), amount); emit Staked(amount); } function withdraw(uint256 amount) external onlyVault updateReward(vault) { require(amount > 0, "Cannot withdraw 0"); _totalSupply -= amount; token.safeTransfer(vault, amount); emit Withdrawn(amount); } function getReward() external updateReward(vault) { uint256 reward = rewards; if (reward > 0) { rewards = 0; token.safeTransfer(vault, reward); emit RewardPaid(reward); } } /* ========== RESTRICTED FUNCTIONS ========== */ function notifyRewardAmount() external onlyDistributor updateReward(address(0)) { // Rewards transferred directly to this contract are not added to _totalSupply // To get the rewards w/o relying on a potentially incorrect passed in arg, // we can use the difference between the token balance and _totalSupply. // Additionally, to avoid re-distributing rewards, deduct the output of `earned` uint256 rewardBalance = token.balanceOf(address(this)) - _totalSupply - earned(); rewardRate = rewardBalance / rewardsDuration; require(rewardRate != 0, "No rewards"); lastUpdateTime = block.timestamp; periodFinish = block.timestamp + rewardsDuration; emit RewardAdded(rewardBalance); } // Added to support recovering LP Rewards from other systems such as BAL to be distributed to holders function recoverERC20(address tokenAddress, uint256 tokenAmount) external onlyOwner { require( tokenAddress != address(token), "Cannot withdraw the staking token" ); ERC20(tokenAddress).safeTransfer(owner(), tokenAmount); emit Recovered(tokenAddress, tokenAmount); } function setDistributor(address _distributor) external onlyOwner { require(_distributor != address(0)); distributor = _distributor; } /* ========== MODIFIERS ========== */ modifier updateReward(address account) { rewardPerTokenStored = rewardPerToken(); lastUpdateTime = lastTimeRewardApplicable(); if (account != address(0)) { rewards = earned(); userRewardPerTokenPaid = rewardPerTokenStored; } _; } /* ========== EVENTS ========== */ event RewardAdded(uint256 reward); event Staked(uint256 amount); event Withdrawn(uint256 amount); event RewardPaid(uint256 reward); event Recovered(address token, uint256 amount); modifier onlyDistributor() { require((msg.sender == distributor), "Distributor only"); _; } modifier onlyVault() { require((msg.sender == vault), "Vault only"); _; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Minimalist and gas efficient standard ERC1155 implementation. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) abstract contract ERC1155 { /*/////////////////////////////////////////////////////////////// EVENTS //////////////////////////////////////////////////////////////*/ event TransferSingle( address indexed operator, address indexed from, address indexed to, uint256 id, uint256 amount ); event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] amounts ); event ApprovalForAll(address indexed owner, address indexed operator, bool approved); event URI(string value, uint256 indexed id); /*/////////////////////////////////////////////////////////////// ERC1155 STORAGE //////////////////////////////////////////////////////////////*/ mapping(address => mapping(uint256 => uint256)) public balanceOf; mapping(address => mapping(address => bool)) public isApprovedForAll; /*/////////////////////////////////////////////////////////////// METADATA LOGIC //////////////////////////////////////////////////////////////*/ function uri(uint256 id) public view virtual returns (string memory); /*/////////////////////////////////////////////////////////////// ERC1155 LOGIC //////////////////////////////////////////////////////////////*/ function setApprovalForAll(address operator, bool approved) public virtual { isApprovedForAll[msg.sender][operator] = approved; emit ApprovalForAll(msg.sender, operator, approved); } function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual { require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); balanceOf[from][id] -= amount; balanceOf[to][id] += amount; emit TransferSingle(msg.sender, from, to, id, amount); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) == ERC1155TokenReceiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); } function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual { uint256 idsLength = ids.length; // Saves MLOADs. require(idsLength == amounts.length, "LENGTH_MISMATCH"); require(msg.sender == from || isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED"); // Storing these outside the loop saves ~15 gas per iteration. uint256 id; uint256 amount; for (uint256 i = 0; i < idsLength; ) { id = ids[i]; amount = amounts[i]; balanceOf[from][id] -= amount; balanceOf[to][id] += amount; // An array can't have a total length // larger than the max uint256 value. unchecked { ++i; } } emit TransferBatch(msg.sender, from, to, ids, amounts); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) == ERC1155TokenReceiver.onERC1155BatchReceived.selector, "UNSAFE_RECIPIENT" ); } function balanceOfBatch(address[] memory owners, uint256[] memory ids) public view virtual returns (uint256[] memory balances) { uint256 ownersLength = owners.length; // Saves MLOADs. require(ownersLength == ids.length, "LENGTH_MISMATCH"); balances = new uint256[](ownersLength); // Unchecked because the only math done is incrementing // the array index counter which cannot possibly overflow. unchecked { for (uint256 i = 0; i < ownersLength; ++i) { balances[i] = balanceOf[owners[i]][ids[i]]; } } } /*/////////////////////////////////////////////////////////////// ERC165 LOGIC //////////////////////////////////////////////////////////////*/ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165 interfaceId == 0xd9b67a26 || // ERC165 Interface ID for ERC1155 interfaceId == 0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI } /*/////////////////////////////////////////////////////////////// INTERNAL MINT/BURN LOGIC //////////////////////////////////////////////////////////////*/ function _mint( address to, uint256 id, uint256 amount, bytes memory data ) internal { balanceOf[to][id] += amount; emit TransferSingle(msg.sender, address(0), to, id, amount); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) == ERC1155TokenReceiver.onERC1155Received.selector, "UNSAFE_RECIPIENT" ); } function _batchMint( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal { uint256 idsLength = ids.length; // Saves MLOADs. require(idsLength == amounts.length, "LENGTH_MISMATCH"); for (uint256 i = 0; i < idsLength; ) { balanceOf[to][ids[i]] += amounts[i]; // An array can't have a total length // larger than the max uint256 value. unchecked { ++i; } } emit TransferBatch(msg.sender, address(0), to, ids, amounts); require( to.code.length == 0 ? to != address(0) : ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) == ERC1155TokenReceiver.onERC1155BatchReceived.selector, "UNSAFE_RECIPIENT" ); } function _batchBurn( address from, uint256[] memory ids, uint256[] memory amounts ) internal { uint256 idsLength = ids.length; // Saves MLOADs. require(idsLength == amounts.length, "LENGTH_MISMATCH"); for (uint256 i = 0; i < idsLength; ) { balanceOf[from][ids[i]] -= amounts[i]; // An array can't have a total length // larger than the max uint256 value. unchecked { ++i; } } emit TransferBatch(msg.sender, from, address(0), ids, amounts); } function _burn( address from, uint256 id, uint256 amount ) internal { balanceOf[from][id] -= amount; emit TransferSingle(msg.sender, from, address(0), id, amount); } } /// @notice A generic interface for a contract which properly accepts ERC1155 tokens. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/tokens/ERC1155.sol) interface ERC1155TokenReceiver { function onERC1155Received( address operator, address from, uint256 id, uint256 amount, bytes calldata data ) external returns (bytes4); function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControl.sol"; import "../utils/Context.sol"; import "../utils/Strings.sol"; import "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ``` * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ``` * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role, _msgSender()); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", Strings.toHexString(uint160(account), 20), " is missing role ", Strings.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/ERC1155.sol) pragma solidity ^0.8.0; import "./IERC1155.sol"; import "./IERC1155Receiver.sol"; import "./extensions/IERC1155MetadataURI.sol"; import "../../utils/Address.sol"; import "../../utils/Context.sol"; import "../../utils/introspection/ERC165.sol"; /** * @dev Implementation of the basic standard multi-token. * See https://eips.ethereum.org/EIPS/eip-1155 * Originally based on code by Enjin: https://github.com/enjin/erc-1155 * * _Available since v3.1._ */ contract ERC1155 is Context, ERC165, IERC1155, IERC1155MetadataURI { using Address for address; // Mapping from token ID to account balances mapping(uint256 => mapping(address => uint256)) private _balances; // Mapping from account to operator approvals mapping(address => mapping(address => bool)) private _operatorApprovals; // Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json string private _uri; /** * @dev See {_setURI}. */ constructor(string memory uri_) { _setURI(uri_); } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { return interfaceId == type(IERC1155).interfaceId || interfaceId == type(IERC1155MetadataURI).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC1155MetadataURI-uri}. * * This implementation returns the same URI for *all* token types. It relies * on the token type ID substitution mechanism * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. * * Clients calling this function must replace the `\{id\}` substring with the * actual token type ID. */ function uri(uint256) public view virtual override returns (string memory) { return _uri; } /** * @dev See {IERC1155-balanceOf}. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) public view virtual override returns (uint256) { require(account != address(0), "ERC1155: balance query for the zero address"); return _balances[id][account]; } /** * @dev See {IERC1155-balanceOfBatch}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] memory accounts, uint256[] memory ids) public view virtual override returns (uint256[] memory) { require(accounts.length == ids.length, "ERC1155: accounts and ids length mismatch"); uint256[] memory batchBalances = new uint256[](accounts.length); for (uint256 i = 0; i < accounts.length; ++i) { batchBalances[i] = balanceOf(accounts[i], ids[i]); } return batchBalances; } /** * @dev See {IERC1155-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC1155-isApprovedForAll}. */ function isApprovedForAll(address account, address operator) public view virtual override returns (bool) { return _operatorApprovals[account][operator]; } /** * @dev See {IERC1155-safeTransferFrom}. */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), "ERC1155: caller is not owner nor approved" ); _safeTransferFrom(from, to, id, amount, data); } /** * @dev See {IERC1155-safeBatchTransferFrom}. */ function safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) public virtual override { require( from == _msgSender() || isApprovedForAll(from, _msgSender()), "ERC1155: transfer caller is not owner nor approved" ); _safeBatchTransferFrom(from, to, ids, amounts, data); } /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function _safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: transfer to the zero address"); address operator = _msgSender(); _beforeTokenTransfer(operator, from, to, _asSingletonArray(id), _asSingletonArray(amount), data); uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); unchecked { _balances[id][from] = fromBalance - amount; } _balances[id][to] += amount; emit TransferSingle(operator, from, to, id, amount); _doSafeTransferAcceptanceCheck(operator, from, to, id, amount, data); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function _safeBatchTransferFrom( address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); require(to != address(0), "ERC1155: transfer to the zero address"); address operator = _msgSender(); _beforeTokenTransfer(operator, from, to, ids, amounts, data); for (uint256 i = 0; i < ids.length; ++i) { uint256 id = ids[i]; uint256 amount = amounts[i]; uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: insufficient balance for transfer"); unchecked { _balances[id][from] = fromBalance - amount; } _balances[id][to] += amount; } emit TransferBatch(operator, from, to, ids, amounts); _doSafeBatchTransferAcceptanceCheck(operator, from, to, ids, amounts, data); } /** * @dev Sets a new URI for all token types, by relying on the token type ID * substitution mechanism * https://eips.ethereum.org/EIPS/eip-1155#metadata[defined in the EIP]. * * By this mechanism, any occurrence of the `\{id\}` substring in either the * URI or any of the amounts in the JSON file at said URI will be replaced by * clients with the token type ID. * * For example, the `https://token-cdn-domain/\{id\}.json` URI would be * interpreted by clients as * `https://token-cdn-domain/000000000000000000000000000000000000000000000000000000000004cce0.json` * for token type ID 0x4cce0. * * See {uri}. * * Because these URIs cannot be meaningfully represented by the {URI} event, * this function emits no events. */ function _setURI(string memory newuri) internal virtual { _uri = newuri; } /** * @dev Creates `amount` tokens of token type `id`, and assigns them to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function _mint( address to, uint256 id, uint256 amount, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: mint to the zero address"); address operator = _msgSender(); _beforeTokenTransfer(operator, address(0), to, _asSingletonArray(id), _asSingletonArray(amount), data); _balances[id][to] += amount; emit TransferSingle(operator, address(0), to, id, amount); _doSafeTransferAcceptanceCheck(operator, address(0), to, id, amount, data); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_mint}. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function _mintBatch( address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual { require(to != address(0), "ERC1155: mint to the zero address"); require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); address operator = _msgSender(); _beforeTokenTransfer(operator, address(0), to, ids, amounts, data); for (uint256 i = 0; i < ids.length; i++) { _balances[ids[i]][to] += amounts[i]; } emit TransferBatch(operator, address(0), to, ids, amounts); _doSafeBatchTransferAcceptanceCheck(operator, address(0), to, ids, amounts, data); } /** * @dev Destroys `amount` tokens of token type `id` from `from` * * Requirements: * * - `from` cannot be the zero address. * - `from` must have at least `amount` tokens of token type `id`. */ function _burn( address from, uint256 id, uint256 amount ) internal virtual { require(from != address(0), "ERC1155: burn from the zero address"); address operator = _msgSender(); _beforeTokenTransfer(operator, from, address(0), _asSingletonArray(id), _asSingletonArray(amount), ""); uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); unchecked { _balances[id][from] = fromBalance - amount; } emit TransferSingle(operator, from, address(0), id, amount); } /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {_burn}. * * Requirements: * * - `ids` and `amounts` must have the same length. */ function _burnBatch( address from, uint256[] memory ids, uint256[] memory amounts ) internal virtual { require(from != address(0), "ERC1155: burn from the zero address"); require(ids.length == amounts.length, "ERC1155: ids and amounts length mismatch"); address operator = _msgSender(); _beforeTokenTransfer(operator, from, address(0), ids, amounts, ""); for (uint256 i = 0; i < ids.length; i++) { uint256 id = ids[i]; uint256 amount = amounts[i]; uint256 fromBalance = _balances[id][from]; require(fromBalance >= amount, "ERC1155: burn amount exceeds balance"); unchecked { _balances[id][from] = fromBalance - amount; } } emit TransferBatch(operator, from, address(0), ids, amounts); } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Emits a {ApprovalForAll} event. */ function _setApprovalForAll( address owner, address operator, bool approved ) internal virtual { require(owner != operator, "ERC1155: setting approval status for self"); _operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Hook that is called before any token transfer. This includes minting * and burning, as well as batched variants. * * The same hook is called on both single and batched variants. For single * transfers, the length of the `id` and `amount` arrays will be 1. * * Calling conditions (for each `id` and `amount` pair): * * - When `from` and `to` are both non-zero, `amount` of ``from``'s tokens * of token type `id` will be transferred to `to`. * - When `from` is zero, `amount` tokens of token type `id` will be minted * for `to`. * - when `to` is zero, `amount` of ``from``'s tokens of token type `id` * will be burned. * - `from` and `to` are never both zero. * - `ids` and `amounts` have the same, non-zero length. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual {} function _doSafeTransferAcceptanceCheck( address operator, address from, address to, uint256 id, uint256 amount, bytes memory data ) private { if (to.isContract()) { try IERC1155Receiver(to).onERC1155Received(operator, from, id, amount, data) returns (bytes4 response) { if (response != IERC1155Receiver.onERC1155Received.selector) { revert("ERC1155: ERC1155Receiver rejected tokens"); } } catch Error(string memory reason) { revert(reason); } catch { revert("ERC1155: transfer to non ERC1155Receiver implementer"); } } } function _doSafeBatchTransferAcceptanceCheck( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) private { if (to.isContract()) { try IERC1155Receiver(to).onERC1155BatchReceived(operator, from, ids, amounts, data) returns ( bytes4 response ) { if (response != IERC1155Receiver.onERC1155BatchReceived.selector) { revert("ERC1155: ERC1155Receiver rejected tokens"); } } catch Error(string memory reason) { revert(reason); } catch { revert("ERC1155: transfer to non ERC1155Receiver implementer"); } } } function _asSingletonArray(uint256 element) private pure returns (uint256[] memory) { uint256[] memory array = new uint256[](1); array[0] = element; return array; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Supply.sol) pragma solidity ^0.8.0; import "../ERC1155.sol"; /** * @dev Extension of ERC1155 that adds tracking of total supply per id. * * Useful for scenarios where Fungible and Non-fungible tokens have to be * clearly identified. Note: While a totalSupply of 1 might mean the * corresponding is an NFT, there is no guarantees that no other token with the * same id are not going to be minted. */ abstract contract ERC1155Supply is ERC1155 { mapping(uint256 => uint256) private _totalSupply; /** * @dev Total amount of tokens in with a given id. */ function totalSupply(uint256 id) public view virtual returns (uint256) { return _totalSupply[id]; } /** * @dev Indicates whether any token exist with a given id, or not. */ function exists(uint256 id) public view virtual returns (bool) { return ERC1155Supply.totalSupply(id) > 0; } /** * @dev See {ERC1155-_beforeTokenTransfer}. */ function _beforeTokenTransfer( address operator, address from, address to, uint256[] memory ids, uint256[] memory amounts, bytes memory data ) internal virtual override { super._beforeTokenTransfer(operator, from, to, ids, amounts, data); if (from == address(0)) { for (uint256 i = 0; i < ids.length; ++i) { _totalSupply[ids[i]] += amounts[i]; } } if (to == address(0)) { for (uint256 i = 0; i < ids.length; ++i) { _totalSupply[ids[i]] -= amounts[i]; } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/ERC1155Burnable.sol) pragma solidity ^0.8.0; import "../ERC1155.sol"; /** * @dev Extension of {ERC1155} that allows token holders to destroy both their * own tokens and those that they have been approved to use. * * _Available since v3.1._ */ abstract contract ERC1155Burnable is ERC1155 { function burn( address account, uint256 id, uint256 value ) public virtual { require( account == _msgSender() || isApprovedForAll(account, _msgSender()), "ERC1155: caller is not owner nor approved" ); _burn(account, id, value); } function burnBatch( address account, uint256[] memory ids, uint256[] memory values ) public virtual { require( account == _msgSender() || isApprovedForAll(account, _msgSender()), "ERC1155: caller is not owner nor approved" ); _burnBatch(account, ids, values); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/IERC1155.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC1155 compliant contract, as defined in the * https://eips.ethereum.org/EIPS/eip-1155[EIP]. * * _Available since v3.1._ */ interface IERC1155 is IERC165 { /** * @dev Emitted when `value` tokens of token type `id` are transferred from `from` to `to` by `operator`. */ event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value); /** * @dev Equivalent to multiple {TransferSingle} events, where `operator`, `from` and `to` are the same for all * transfers. */ event TransferBatch( address indexed operator, address indexed from, address indexed to, uint256[] ids, uint256[] values ); /** * @dev Emitted when `account` grants or revokes permission to `operator` to transfer their tokens, according to * `approved`. */ event ApprovalForAll(address indexed account, address indexed operator, bool approved); /** * @dev Emitted when the URI for token type `id` changes to `value`, if it is a non-programmatic URI. * * If an {URI} event was emitted for `id`, the standard * https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[guarantees] that `value` will equal the value * returned by {IERC1155MetadataURI-uri}. */ event URI(string value, uint256 indexed id); /** * @dev Returns the amount of tokens of token type `id` owned by `account`. * * Requirements: * * - `account` cannot be the zero address. */ function balanceOf(address account, uint256 id) external view returns (uint256); /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {balanceOf}. * * Requirements: * * - `accounts` and `ids` must have the same length. */ function balanceOfBatch(address[] calldata accounts, uint256[] calldata ids) external view returns (uint256[] memory); /** * @dev Grants or revokes permission to `operator` to transfer the caller's tokens, according to `approved`, * * Emits an {ApprovalForAll} event. * * Requirements: * * - `operator` cannot be the caller. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns true if `operator` is approved to transfer ``account``'s tokens. * * See {setApprovalForAll}. */ function isApprovedForAll(address account, address operator) external view returns (bool); /** * @dev Transfers `amount` tokens of token type `id` from `from` to `to`. * * Emits a {TransferSingle} event. * * Requirements: * * - `to` cannot be the zero address. * - If the caller is not `from`, it must be have been approved to spend ``from``'s tokens via {setApprovalForAll}. * - `from` must have a balance of tokens of type `id` of at least `amount`. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155Received} and return the * acceptance magic value. */ function safeTransferFrom( address from, address to, uint256 id, uint256 amount, bytes calldata data ) external; /** * @dev xref:ROOT:erc1155.adoc#batch-operations[Batched] version of {safeTransferFrom}. * * Emits a {TransferBatch} event. * * Requirements: * * - `ids` and `amounts` must have the same length. * - If `to` refers to a smart contract, it must implement {IERC1155Receiver-onERC1155BatchReceived} and return the * acceptance magic value. */ function safeBatchTransferFrom( address from, address to, uint256[] calldata ids, uint256[] calldata amounts, bytes calldata data ) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC1155/IERC1155Receiver.sol) pragma solidity ^0.8.0; import "../../utils/introspection/IERC165.sol"; /** * @dev _Available since v3.1._ */ interface IERC1155Receiver is IERC165 { /** * @dev Handles the receipt of a single ERC1155 token type. This function is * called at the end of a `safeTransferFrom` after the balance has been updated. * * NOTE: To accept the transfer, this must return * `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` * (i.e. 0xf23a6e61, or its own function selector). * * @param operator The address which initiated the transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param id The ID of the token being transferred * @param value The amount of tokens being transferred * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed */ function onERC1155Received( address operator, address from, uint256 id, uint256 value, bytes calldata data ) external returns (bytes4); /** * @dev Handles the receipt of a multiple ERC1155 token types. This function * is called at the end of a `safeBatchTransferFrom` after the balances have * been updated. * * NOTE: To accept the transfer(s), this must return * `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` * (i.e. 0xbc197c81, or its own function selector). * * @param operator The address which initiated the batch transfer (i.e. msg.sender) * @param from The address which previously owned the token * @param ids An array containing ids of each token being transferred (order and length must match values array) * @param values An array containing amounts of each token being transferred (order and length must match ids array) * @param data Additional data with no specified format * @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed */ function onERC1155BatchReceived( address operator, address from, uint256[] calldata ids, uint256[] calldata values, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC1155/extensions/IERC1155MetadataURI.sol) pragma solidity ^0.8.0; import "../IERC1155.sol"; /** * @dev Interface of the optional ERC1155MetadataExtension interface, as defined * in the https://eips.ethereum.org/EIPS/eip-1155#metadata-extensions[EIP]. * * _Available since v3.1._ */ interface IERC1155MetadataURI is IERC1155 { /** * @dev Returns the URI for token type `id`. * * If the `\{id\}` substring is present in the URI, it must be replaced by * clients with the actual token type ID. */ function uri(uint256 id) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value ) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResult(success, returndata, errorMessage); } /** * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
{ "optimizer": { "enabled": true, "runs": 179 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_CVX","type":"address"},{"internalType":"address","name":"_cvxLocker","type":"address"},{"internalType":"address","name":"_cvxDelegateRegistry","type":"address"},{"internalType":"address","name":"_pxCvx","type":"address"},{"internalType":"address","name":"_upxCvx","type":"address"},{"internalType":"address","name":"_spxCvx","type":"address"},{"internalType":"address","name":"_vpxCvx","type":"address"},{"internalType":"address","name":"_rpxCvx","type":"address"},{"internalType":"address","name":"_pirexFees","type":"address"},{"internalType":"address","name":"_votiumMultiMerkleStash","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"AlreadyInitialized","type":"error"},{"inputs":[],"name":"AlreadyRedeemed","type":"error"},{"inputs":[],"name":"BeforeEffectiveTimestamp","type":"error"},{"inputs":[],"name":"BeforeStakingExpiry","type":"error"},{"inputs":[],"name":"BeforeUnlock","type":"error"},{"inputs":[],"name":"EmptyArray","type":"error"},{"inputs":[],"name":"EmptyString","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InsufficientRedemptionAllowance","type":"error"},{"inputs":[],"name":"InvalidEmergencyMigration","type":"error"},{"inputs":[],"name":"InvalidEpoch","type":"error"},{"inputs":[],"name":"InvalidFee","type":"error"},{"inputs":[],"name":"MismatchedArrayLengths","type":"error"},{"inputs":[],"name":"NoEmergencyExecutor","type":"error"},{"inputs":[],"name":"NoRewards","type":"error"},{"inputs":[],"name":"NotAuthorized","type":"error"},{"inputs":[],"name":"PastExchangePeriod","type":"error"},{"inputs":[],"name":"RedeemClosed","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"developer","type":"address"}],"name":"AddDeveloper","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"timestamp","type":"uint256"},{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"balance","type":"uint256"}],"indexed":false,"internalType":"struct PirexCvxConvex.ConvexReward[]","name":"rewards","type":"tuple[]"}],"name":"ClaimMiscRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"index","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimVotiumReward","type":"event"},{"anonymous":false,"inputs":[],"name":"ClearVoteDelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":true,"internalType":"bool","name":"shouldCompound","type":"bool"},{"indexed":true,"internalType":"address","name":"developer","type":"address"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"enum PirexCvx.Futures","name":"f","type":"uint8"}],"name":"ExchangeFutures","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"ExecuteEmergencyMigration","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"_emergencyExecutor","type":"address"}],"name":"InitializeEmergencyExecutor","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"lockIndexes","type":"uint256[]"},{"indexed":true,"internalType":"enum PirexCvx.Futures","name":"f","type":"uint8"},{"indexed":false,"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"}],"name":"InitiateRedemptions","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rounds","type":"uint256"},{"indexed":true,"internalType":"enum PirexCvx.Futures","name":"f","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"}],"name":"MintFutures","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256[]","name":"unlockTimes","type":"uint256[]"},{"indexed":false,"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"bool","name":"legacy","type":"bool"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"bytes32[]","name":"rewards","type":"bytes32[]"}],"name":"RedeemFuturesRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"epoch","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"rewardIndexes","type":"uint256[]"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"snapshotBalance","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"snapshotSupply","type":"uint256"}],"name":"RedeemSnapshotRewards","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"developer","type":"address"}],"name":"RemoveDeveloper","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum PirexCvx.Contract","name":"c","type":"uint8"},{"indexed":false,"internalType":"address","name":"contractAddress","type":"address"}],"name":"SetContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"enum PirexCvxConvex.ConvexContract","name":"c","type":"uint8"},{"indexed":false,"internalType":"address","name":"contractAddress","type":"address"}],"name":"SetConvexContract","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"_delegationSpace","type":"string"},{"indexed":false,"internalType":"bool","name":"shouldClear","type":"bool"}],"name":"SetDelegationSpace","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"indexed":false,"internalType":"struct PirexCvx.EmergencyMigration","name":"_emergencyMigration","type":"tuple"}],"name":"SetEmergencyMigration","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum PirexCvx.Fees","name":"f","type":"uint8"},{"indexed":false,"internalType":"uint32","name":"fee","type":"uint32"}],"name":"SetFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"state","type":"bool"}],"name":"SetUpxCvxDeprecated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"voteDelegate","type":"address"}],"name":"SetVoteDelegate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rounds","type":"uint256"},{"indexed":true,"internalType":"enum PirexCvx.Futures","name":"f","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"}],"name":"Stake","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"assets","type":"uint256"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"}],"name":"Unstake","type":"event"},{"inputs":[],"name":"CVX","outputs":[{"internalType":"contract ERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"EPOCH_DURATION","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_DENOMINATOR","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FEE_MAX","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_REDEMPTION_TIME","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"developer","type":"address"}],"name":"addDeveloper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimMiscRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes32[]","name":"merkleProof","type":"bytes32[]"}],"internalType":"struct IVotiumMultiMerkleStash.claimParam[]","name":"votiumRewards","type":"tuple[]"}],"name":"claimVotiumRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"clearVoteDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cvxDelegateRegistry","outputs":[{"internalType":"contract ICvxDelegateRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"cvxLocker","outputs":[{"internalType":"contract ICvxLocker","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegationSpace","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bool","name":"shouldCompound","type":"bool"},{"internalType":"address","name":"developer","type":"address"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"developers","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyExecutor","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyMigration","outputs":[{"internalType":"address","name":"recipient","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"enum PirexCvx.Futures","name":"f","type":"uint8"}],"name":"exchangeFutures","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"executeEmergencyMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum PirexCvx.Fees","name":"","type":"uint8"}],"name":"fees","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentEpoch","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_emergencyExecutor","type":"address"}],"name":"initializeEmergencyExecutor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"lockIndexes","type":"uint256[]"},{"internalType":"enum PirexCvx.Futures","name":"f","type":"uint8"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"initiateRedemptions","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"outstandingRedemptions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pausedRelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pendingLocks","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pirexFees","outputs":[{"internalType":"contract PirexFees","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pxCvx","outputs":[{"internalType":"contract PxCvx","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"unlockTimes","type":"uint256[]"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"redeemFuturesRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"unlockTimes","type":"uint256[]"},{"internalType":"uint256[]","name":"assets","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"redeemLegacy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"epoch","type":"uint256"},{"internalType":"uint256[]","name":"rewardIndexes","type":"uint256[]"},{"internalType":"address","name":"receiver","type":"address"}],"name":"redeemSnapshotRewards","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"redemptions","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"developer","type":"address"}],"name":"removeDeveloper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"rpxCvx","outputs":[{"internalType":"contract ERC1155PresetMinterSupply","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"enum PirexCvx.Contract","name":"c","type":"uint8"},{"internalType":"address","name":"contractAddress","type":"address"}],"name":"setContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum PirexCvxConvex.ConvexContract","name":"c","type":"uint8"},{"internalType":"address","name":"contractAddress","type":"address"}],"name":"setConvexContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_delegationSpace","type":"string"},{"internalType":"bool","name":"shouldClear","type":"bool"}],"name":"setDelegationSpace","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address[]","name":"tokens","type":"address[]"}],"internalType":"struct PirexCvx.EmergencyMigration","name":"_emergencyMigration","type":"tuple"}],"name":"setEmergencyMigration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum PirexCvx.Fees","name":"f","type":"uint8"},{"internalType":"uint32","name":"fee","type":"uint32"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"state","type":"bool"}],"name":"setPauseState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"state","type":"bool"}],"name":"setUpxCvxDeprecated","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"voteDelegate","type":"address"}],"name":"setVoteDelegate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"spxCvx","outputs":[{"internalType":"contract ERC1155Solmate","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"rounds","type":"uint256"},{"internalType":"enum PirexCvx.Futures","name":"f","type":"uint8"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"stake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unionPirex","outputs":[{"internalType":"contract UnionPirexVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unlock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"unstake","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"upxCvx","outputs":[{"internalType":"contract ERC1155Solmate","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"upxCvxDeprecated","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"votiumMultiMerkleStash","outputs":[{"internalType":"contract IVotiumMultiMerkleStash","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"vpxCvx","outputs":[{"internalType":"contract ERC1155PresetMinterSupply","name":"","type":"address"}],"stateMutability":"view","type":"function"}]
Contract Creation Code
60a06040526001600055660c6ecf05ccae8d60cb1b6004553480156200002457600080fd5b5060405162005cc438038062005cc483398101604081905262000047916200047b565b8989896200005533620002da565b6001805460ff60a01b191690556001600160a01b0383166200008a5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038216620000b25760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038116620000da5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038381166080819052600280546001600160a01b031990811686851690811790925560038054909116938516939093179092556200012e916000196200032c602090811b620037c217901c565b506200013c915050620003af565b6001600160a01b038716620001645760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0382166200018c5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038616620001b45760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038516620001dc5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038416620002045760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b0383166200022c5760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038116620002545760405163d92e233d60e01b815260040160405180910390fd5b600780546001600160a01b03199081166001600160a01b03998a161790915560088054821693891693909317909255600a8054831696881696909617909555600b8054821694871694909417909355600c8054841692861692909217909155600d805483169185169190911790556009805490911691909216179055506200054a915050565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d1160016000511416171691505080620003a95760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b60448201526064015b60405180910390fd5b50505050565b620003c3600154600160a01b900460ff1690565b15620004055760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401620003a0565b6001805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258620004413390565b6040516001600160a01b03909116815260200160405180910390a1565b80516001600160a01b03811681146200047657600080fd5b919050565b6000806000806000806000806000806101408b8d0312156200049c57600080fd5b620004a78b6200045e565b9950620004b760208c016200045e565b9850620004c760408c016200045e565b9750620004d760608c016200045e565b9650620004e760808c016200045e565b9550620004f760a08c016200045e565b94506200050760c08c016200045e565b93506200051760e08c016200045e565b9250620005286101008c016200045e565b9150620005396101208c016200045e565b90509295989b9194979a5092959850565b608051615734620005906000396000818161050801528181610ecc01528181610f1c015281816131b20152818161350701528181613a2c0152613f5801526157346000f3fe608060405234801561001057600080fd5b50600436106102f85760003560e01c80638ce5991f11610193578063bbea0974116100e4578063d73792a911610092578063d73792a9146106c4578063dc945327146106ce578063dfc1d332146106e1578063edf187f0146106f4578063f2fde38b146106fc578063f7a1f5ff1461070f578063f83d08ba1461071857600080fd5b8063bbea097414610645578063bc8a72b61461064e578063beb6589314610661578063c22f940614610681578063c2624e1e14610694578063cd82fc131461069e578063cdb88ad1146106b157600080fd5b8063a2787cbb11610141578063a2787cbb146105e6578063a69df4b5146105f9578063a70b9f0c14610601578063ae1d65151461060b578063b0dba76814610622578063b29b355e14610635578063b97dd9e21461063d57600080fd5b80638ce5991f146105635780638d68f9ff146105765780638da5cb5b1461058957806393d4f2c91461059a5780639a7fdb58146105ad5780639cbb4108146105c05780639e23c209146105d357600080fd5b80635ed810ac1161024d5780636ae778de116101fb5780636ae778de146104d4578063715018a6146104e857806374874323146104f0578063759cb53b14610503578063813e434e1461052a578063865e6fd31461053d5780638bea9d501461055057600080fd5b80635ed810ac14610455578063607de7981461045d57806361fd5c20146104705780636310fbdd1461049357806364a34beb146104a657806364c0bef1146104b957806369c408e1146104c157600080fd5b8063385d792a116102aa578063385d792a146103c95780633f12ff39146103dc578063407cd779146103ef57806349bfe411146104025780634b81c36d1461041557806351c2d64f1461041f5780635c975abb1461043257600080fd5b806305c9399c146102fd57806308c676e914610312578063189fa7a2146103255780631d3c2b2f1461035557806322fbf1e81461036857806323c133ef1461037b578063357c13541461038e575b600080fd5b61031061030b3660046145b1565b610720565b005b610310610320366004614648565b610906565b601454610338906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61031061036336600461476b565b6109a3565b610310610376366004614810565b610a47565b610310610389366004614810565b610af3565b6103b461039c366004614834565b600f6020526000908152604090205463ffffffff1681565b60405163ffffffff909116815260200161034c565b600b54610338906001600160a01b031681565b6103106103ea36600461485c565b610bea565b6103106103fd3660046148a6565b610e17565b6103106104103660046148d4565b610f68565b6103b4629ce28081565b600754610338906001600160a01b031681565b610445600154600160a01b900460ff1690565b604051901515815260200161034c565b6103106115fe565b61031061046b366004614915565b611824565b61044561047e366004614810565b60116020526000908152604090205460ff1681565b600a54610338906001600160a01b031681565b6103106104b4366004614930565b6118ca565b610310611c8c565b6103106104cf3660046149c6565b611cef565b60145461044590600160a01b900460ff1681565b61031061212a565b6103106104fe366004614810565b61215e565b6103387f000000000000000000000000000000000000000000000000000000000000000081565b6103106105383660046149eb565b612256565b61031061054b366004614a3e565b612680565b600954610338906001600160a01b031681565b600e54610338906001600160a01b031681565b600254610338906001600160a01b031681565b6001546001600160a01b0316610338565b6103106105a8366004614a60565b61291d565b6103106105bb366004614648565b612ae4565b6103106105ce366004614a99565b612b73565b6103106105e1366004614810565b612d91565b6103106105f4366004614ad8565b612e33565b610310612f57565b6103b46212750081565b61061460065481565b60405190815260200161034c565b600c54610338906001600160a01b031681565b61031061300b565b61061461326c565b61061460045481565b600854610338906001600160a01b031681565b61061461066f366004614b12565b60106020526000908152604090205481565b61031061068f366004614b2b565b61328b565b6103b4620186a081565b601254610338906001600160a01b031681565b6103106106bf366004614915565b613632565b6103b4620f424081565b600d54610338906001600160a01b031681565b600354610338906001600160a01b031681565b610310613675565b61031061070a366004614810565b6136fa565b61061460055481565b610310613792565b6001546001600160a01b031633146107535760405162461bcd60e51b815260040161074a90614b71565b60405180910390fd5b620186a063ffffffff8216111561077d576040516358d620b360e01b815260040160405180910390fd5b600182600381111561079157610791614ba6565b1480156107d257506002600052600f6020527fa74ba3945261e09fde15ba3db55005b205e61eeb4ad811ac0faa2b315bffeead5463ffffffff908116908216105b156107f0576040516358d620b360e01b815260040160405180910390fd5b600282600381111561080457610804614ba6565b14801561084557506001600052600f6020527f169f97de0d9a84d840042b17d3c6b9638b3d6fd9024c9eb0c7a306a17b49f88f5463ffffffff908116908216115b15610863576040516358d620b360e01b815260040160405180910390fd5b80600f600084600381111561087a5761087a614ba6565b600381111561088b5761088b614ba6565b815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff1602179055508160038111156108c9576108c9614ba6565b60405163ffffffff831681527f60cca38ba894e5ddc4db50e39d1e729d5ae76ddbd3874395ead72add5125bc0f9060200160405180910390a25050565b610919600154600160a01b900460ff1690565b156109365760405162461bcd60e51b815260040161074a90614bbc565b6000546001146109585760405162461bcd60e51b815260040161074a90614be6565b6002600055601454600160a01b900460ff161561098857604051635bbdd11b60e01b815260040160405180910390fd5b61099785858585856000613839565b50506001600055505050565b6001546001600160a01b031633146109cd5760405162461bcd60e51b815260040161074a90614b71565b80156109db576109db613675565b815182906109fc5760405163ecd7b0d160e01b815260040160405180910390fd5b610a0581614c0a565b6004556040517f4969e7d59c970ec13a80e7e632b460c9202863355217c5d7ee7d0b5d7782e7c790610a3a9085908590614c7e565b60405180910390a1505050565b6001546001600160a01b03163314610a715760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b038116610a985760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038116600081815260116020908152604091829020805460ff1916600117905590519182527fbec2cf13b5a3044b9654dd6cc38607f4f4c78bee63efc62ac573d7116f00727d91015b60405180910390a150565b6001546001600160a01b03163314610b1d5760405162461bcd60e51b815260040161074a90614b71565b610b30600154600160a01b900460ff1690565b610b4c5760405162461bcd60e51b815260040161074a90614ca2565b6001600160a01b038116610b735760405163d92e233d60e01b815260040160405180910390fd5b6014546001600160a01b031615610b9c5760405162dc149f60e41b815260040160405180910390fd5b601480546001600160a01b0319166001600160a01b0383169081179091556040519081527f17a1e16b1eb20d593558f091e129e05f9226d60dc8294b9a45c45d75def3818590602001610ae8565b610bfd600154600160a01b900460ff1690565b15610c1a5760405162461bcd60e51b815260040161074a90614bbc565b610c2261326c565b8411610c4157604051631fff902d60e11b815260040160405180910390fd5b82610c5f57604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b038216610c865760405163d92e233d60e01b815260040160405180910390fd5b600080826001811115610c9b57610c9b614ba6565b14610cb157600d546001600160a01b0316610cbe565b600c546001600160a01b03165b9050600080836001811115610cd557610cd5614ba6565b14610ceb57600c546001600160a01b0316610cf8565b600d546001600160a01b03165b9050836001600160a01b0316867f5f27e148f282010505552bbed99662d7f6c5e4b25bfda923b4f487091ff602808786604051610d36929190614cee565b60405180910390a3604051637a94c56560e11b81526001600160a01b0383169063f5298aca90610d6e9033908a908a90600401614d0b565b600060405180830381600087803b158015610d8857600080fd5b505af1158015610d9c573d6000803e3d6000fd5b50506040805160208101825260008152905163731133e960e01b81526001600160a01b038516935063731133e99250610ddd9188918b918b91600401614d2c565b600060405180830381600087803b158015610df757600080fd5b505af1158015610e0b573d6000803e3d6000fd5b50505050505050505050565b6001546001600160a01b03163314610e415760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b038116610e685760405163d92e233d60e01b815260040160405180910390fd5b7fc16ef8dc1c6d7fb9d24f8525ad674572540847348de1a67d0542b3c80defe4ad8282604051610e99929190614d63565b60405180910390a16000826001811115610eb557610eb5614ba6565b1415610f4957600254610ef6906001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116911660006137c2565b600280546001600160a01b0319166001600160a01b0383811691909117909155610f45907f000000000000000000000000000000000000000000000000000000000000000016826000196137c2565b5050565b600380546001600160a01b0319166001600160a01b0383161790555050565b610f7b600154600160a01b900460ff1690565b15610f985760405162461bcd60e51b815260040161074a90614bbc565b600054600114610fba5760405162461bcd60e51b815260040161074a90614be6565b60026000558080610fde5760405163521299a960e01b815260040160405180910390fd5b600760009054906101000a90046001600160a01b03166001600160a01b0316634108d57c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561102e57600080fd5b505af1158015611042573d6000803e3d6000fd5b50505050600061105061326c565b600754604051635e05e35d60e11b8152600481018390529192506000916001600160a01b039091169063bc0bc6ba90602401600060405180830381865afa15801561109f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526110c79190810190614e16565b5050600d5460405163bd85b03960e01b8152600481018690529293506000926001600160a01b03909116915063bd85b03990602401602060405180830381865afa158015611119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113d9190614eff565b905060005b848110156115f057600087878381811061115e5761115e614f18565b90506020028101906111709190614f2e565b61117e906020810190614810565b9050600088888481811061119457611194614f18565b90506020028101906111a69190614f2e565b60200135905060008989858181106111c0576111c0614f18565b90506020028101906111d29190614f2e565b60400135905060008a8a868181106111ec576111ec614f18565b90506020028101906111fe9190614f2e565b61120c906060810190614f4e565b808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509293505050506001600160a01b0384166112675760405163d92e233d60e01b815260040160405180910390fd5b8161128557604051631f2a200560e01b815260040160405180910390fd5b60408051848152602081018490526001600160a01b038616917fc8fb114192ae3823bff69d1b8922f93f9e88da0ae61a2145277ca71bd550773b910160405180910390a26040516370a0823160e01b815230600482015284906000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611312573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113369190614eff565b600954604051630968c76b60e11b81529192506001600160a01b0316906312d18ed69061136f908990899030908a908a90600401614fd2565b600060405180830381600087803b15801561138957600080fd5b505af115801561139d573d6000803e3d6000fd5b50506000808052600f6020527ff4803e074bd026baaf6ed2e288c9515f68c72fb7216eebdd7cae1718a53ec37554600754604051630981b24d60e41b8152600481018f905292945084935083926114c49263ffffffff16916001600160a01b03169063981b24d090602401602060405180830381865afa158015611425573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114499190614eff565b6040516370a0823160e01b81523060048201528e9088906001600160a01b038b16906370a0823190602401602060405180830381865afa158015611491573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114b59190614eff565b6114bf919061502d565b613a5f565b60075492955090935091506001600160a01b0316638dcc05838e60608c901b6bffffffffffffffffffffffff19166040516001600160e01b031960e085901b168152600481019290925260248201526044810185905260648101849052608401600060405180830381600087803b15801561153e57600080fd5b505af1158015611552573d6000803e3d6000fd5b505060085461157092506001600160a01b03888116925016856137c2565b600854604051631e75e2a960e11b81526001600160a01b0390911690633cebc552906115a49030908d908890600401615044565b600060405180830381600087803b1580156115be57600080fd5b505af11580156115d2573d6000803e3d6000fd5b50505050505050505050505050806115e990615068565b9050611142565b505060016000555050505050565b6000546001146116205760405162461bcd60e51b815260040161074a90614be6565b6002600090815561162f613ac8565b90507f3d1942b9a9626cb3a12d9bb6a45104e2d8a398cac0cb1782f22d5bdcf6e0f4824282604051611662929190615083565b60405180910390a1611672613ce4565b805160005b8181101561181a5782818151811061169157611691614f18565b602002602001015160200151600014156116aa5761180a565b60008382815181106116be576116be614f18565b602002602001015160000151905060008483815181106116e0576116e0614f18565b602090810291909101015160409081015190516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015611734573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117589190614eff565b611762919061502d565b60085490915061177f906001600160a01b038481169116836137c2565b60085485516001600160a01b0390911690633cebc5529030908890879081106117aa576117aa614f18565b602002602001015160000151846040518463ffffffff1660e01b81526004016117d593929190615044565b600060405180830381600087803b1580156117ef57600080fd5b505af1158015611803573d6000803e3d6000fd5b5050505050505b61181381615068565b9050611677565b5050600160005550565b6001546001600160a01b0316331461184e5760405162461bcd60e51b815260040161074a90614b71565b611861600154600160a01b900460ff1690565b61187d5760405162461bcd60e51b815260040161074a90614ca2565b60148054821515600160a01b0260ff60a01b199091161790556040517ffd8b4222303dc911d36e18aaf75a668ccdb8796c1a6150765cf912337fcb215490610ae890831515815260200190565b6118dd600154600160a01b900460ff1690565b156118fa5760405162461bcd60e51b815260040161074a90614bbc565b60005460011461191c5760405162461bcd60e51b815260040161074a90614be6565b600260005584806119405760405163521299a960e01b815260040160405180910390fd5b80831461196057604051632b477e7160e11b815260040160405180910390fd5b816001600160a01b031685600181111561197c5761197c614ba6565b7f41d92a50cdd5f1afd68a62623521edd0b02d64d636979e3dbb2ab0b33693e866898988886040516119b19493929190615124565b60405180910390a3600254604051630241d3fb60e11b81523060048201526000916001600160a01b031690630483a7f690602401600060405180830381865afa158015611a02573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a2a9190810190615162565b600f6020527fa74ba3945261e09fde15ba3db55005b205e61eeb4ad811ac0faa2b315bffeead54600160009081527f169f97de0d9a84d840042b17d3c6b9638b3d6fd9024c9eb0c7a306a17b49f88f54929650945084935063ffffffff908116925016825b86811015611b3057898982818110611aa957611aa9614f18565b9050602002013585611abb9190615251565b9450611b14868e8e84818110611ad357611ad3614f18565b9050602002013581518110611aea57611aea614f18565b60200260200101518c8c8c85818110611b0557611b05614f18565b905060200201358b8787613d1b565b611b1e9085615251565b9350611b2981615068565b9050611a8f565b506007546001600160a01b0316639dc29fac33611b4d868861502d565b6040518363ffffffff1660e01b8152600401611b6a929190615269565b600060405180830381600087803b158015611b8457600080fd5b505af1158015611b98573d6000803e3d6000fd5b5050505082600014611c7957600754600854604051630c29702960e01b81526001600160a01b0392831692630c29702992611bdd923392909116908890600401615044565b600060405180830381600087803b158015611bf757600080fd5b505af1158015611c0b573d6000803e3d6000fd5b5050600854600754604051631e75e2a960e11b81526001600160a01b039283169450633cebc5529350611c4692339216908890600401615044565b600060405180830381600087803b158015611c6057600080fd5b505af1158015611c74573d6000803e3d6000fd5b505050505b5050600160005550505050505050505050565b611c9f600154600160a01b900460ff1690565b611cbb5760405162461bcd60e51b815260040161074a90614ca2565b6001546001600160a01b03163314611ce55760405162461bcd60e51b815260040161074a90614b71565b611ced613f38565b565b611d02600154600160a01b900460ff1690565b15611d1f5760405162461bcd60e51b815260040161074a90614bbc565b600054600114611d415760405162461bcd60e51b815260040161074a90614be6565b600260005581611d645760405163d5b25b6360e01b815260040160405180910390fd5b611d6c61326c565b821115611d8c5760405163d5b25b6360e01b815260040160405180910390fd5b6001600160a01b038116611db35760405163d92e233d60e01b815260040160405180910390fd5b600754604051635e05e35d60e11b81526004810184905260009182916001600160a01b039091169063bc0bc6ba90602401600060405180830381865afa158015611e01573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e299190810190614e16565b935050925050815160001415611e5257604051630fec21fd60e21b815260040160405180910390fd5b826001600160a01b0316847ff175480c0dfa18114de22d2cf0a65233a94439be6c56f58c54f500d0408cd4f384604051611e8c9190615282565b60405180910390a3600d54604051627eeac760e11b81526000916001600160a01b03169062fdd58e90611ec59033908990600401615269565b602060405180830381865afa158015611ee2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f069190614eff565b905080611f2657604051631e9acf1760e31b815260040160405180910390fd5b600d5460405163bd85b03960e01b8152600481018790526000916001600160a01b03169063bd85b03990602401602060405180830381865afa158015611f70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f949190614eff565b600d54604051637a94c56560e11b81529192506001600160a01b03169063f5298aca90611fc99033908a908790600401614d0b565b600060405180830381600087803b158015611fe357600080fd5b505af1158015611ff7573d6000803e3d6000fd5b505085519150600090505b818110156120b7576000838587848151811061202057612020614f18565b60200260200101516120329190615295565b61203c91906152ca565b90508086838151811061205157612051614f18565b60200260200101818151612065919061502d565b915081815250506120a6888289858151811061208357612083614f18565b602002602001015160601c6001600160a01b03166140879092919063ffffffff16565b506120b081615068565b9050612002565b5060075460405163734b1d8760e01b81526001600160a01b039091169063734b1d87906120ea908a9088906004016152de565b600060405180830381600087803b15801561210457600080fd5b505af1158015612118573d6000803e3d6000fd5b50506001600055505050505050505050565b6001546001600160a01b031633146121545760405162461bcd60e51b815260040161074a90614b71565b611ced60006140ff565b6001546001600160a01b031633146121885760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b0381166121af5760405163d92e233d60e01b815260040160405180910390fd5b6040516001600160a01b03821681527f906903afca224114603eddea72fbada90dcce859ee67b20861e842b79fc82ac09060200160405180910390a1600354600480546040516317b0dca160e31b8152918201526001600160a01b0383811660248301529091169063bd86e508906044015b600060405180830381600087803b15801561223b57600080fd5b505af115801561224f573d6000803e3d6000fd5b5050505050565b612269600154600160a01b900460ff1690565b156122865760405162461bcd60e51b815260040161074a90614bbc565b6000546001146122a85760405162461bcd60e51b815260040161074a90614be6565b6002600055836122cb5760405163d5b25b6360e01b815260040160405180910390fd5b6001600160a01b0381166122f25760405163d92e233d60e01b815260040160405180910390fd5b81806123115760405163521299a960e01b815260040160405180910390fd5b600754604051635e05e35d60e11b815260048101879052600091829182916001600160a01b03169063bc0bc6ba90602401600060405180830381865afa15801561235f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526123879190810190614e16565b5060075460405163206e4f0760e01b815293965091945092506000916001600160a01b039091169063206e4f07906123c59033908d90600401615269565b602060405180830381865afa1580156123e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124069190614eff565b60075460405163277166bf60e11b81529192506000916001600160a01b0390911690634ee2cd7e9061243e9033908990600401615269565b602060405180830381865afa15801561245b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247f9190614eff565b600754604051630981b24d60e41b8152600481018890529192506000916001600160a01b039091169063981b24d090602401602060405180830381865afa1580156124ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f29190614eff565b90508161251257604051631e9acf1760e31b815260040160405180910390fd5b876001600160a01b03168b7fb4508e4d3d04fe3c1551b10be4b1e7bbcd4d90381d183619bcfc896ba3ea74658c8c868660405161255294939291906152ff565b60405180910390a360005b878110156126075760008b8b8381811061257957612579614f18565b60200291909101359150506001811b858116156125a9576040516306d3830f60e21b815260040160405180910390fd5b80861795506125f48b85878a86815181106125c6576125c6614f18565b60200260200101516125d89190615295565b6125e291906152ca565b8a858151811061208357612083614f18565b50508061260090615068565b905061255d565b50600754604051631e38bf3160e01b81526001600160a01b0390911690631e38bf319061263c9033908f908890600401614d0b565b600060405180830381600087803b15801561265657600080fd5b505af115801561266a573d6000803e3d6000fd5b5050600160005550505050505050505050505050565b6001546001600160a01b031633146126aa5760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b0381166126d15760405163d92e233d60e01b815260040160405180910390fd5b8160078111156126e3576126e3614ba6565b6040516001600160a01b03831681527e0961cd4320f350803f764de4992b70c8fc5c948b5881a151f883df0af333579060200160405180910390a2600082600781111561273257612732614ba6565b141561275957600780546001600160a01b0383166001600160a01b03199091161790555050565b600182600781111561276d5761276d614ba6565b141561279457600880546001600160a01b0383166001600160a01b03199091161790555050565b60028260078111156127a8576127a8614ba6565b14156127cf57600980546001600160a01b0383166001600160a01b03199091161790555050565b60038260078111156127e3576127e3614ba6565b141561280a57600a80546001600160a01b0383166001600160a01b03199091161790555050565b600482600781111561281e5761281e614ba6565b141561284557600b80546001600160a01b0383166001600160a01b03199091161790555050565b600582600781111561285957612859614ba6565b141561288057600c80546001600160a01b0383166001600160a01b03199091161790555050565b600682600781111561289457612894614ba6565b14156128bb57600d80546001600160a01b0383166001600160a01b03199091161790555050565b600754600e546001600160a01b03918216911680156128e9576128e96001600160a01b0383168260006137c2565b600e80546001600160a01b0319166001600160a01b03858116918217909255612917918416906000196137c2565b50505050565b612930600154600160a01b900460ff1690565b1561294d5760405162461bcd60e51b815260040161074a90614bbc565b60005460011461296f5760405162461bcd60e51b815260040161074a90614be6565b6002600055428311156129955760405163bc58aaa760e01b815260040160405180910390fd5b816129b357604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b0381166129da5760405163d92e233d60e01b815260040160405180910390fd5b6007546040516340c10f1960e01b81526001600160a01b03909116906340c10f1990612a0c9084908690600401615269565b600060405180830381600087803b158015612a2657600080fd5b505af1158015612a3a573d6000803e3d6000fd5b505060408051868152602081018690526001600160a01b03851693507f8c930e6d96bc30b53e6f52f5670ccab75b962922809178c0da8bf7f931d0059092500160405180910390a2600b54604051637a94c56560e11b81526001600160a01b039091169063f5298aca90612ab690339087908790600401614d0b565b600060405180830381600087803b158015612ad057600080fd5b505af11580156115f0573d6000803e3d6000fd5b612af7600154600160a01b900460ff1690565b612b135760405162461bcd60e51b815260040161074a90614ca2565b600054600114612b355760405162461bcd60e51b815260040161074a90614be6565b6002600055601454600160a01b900460ff16612b6457604051635bbdd11b60e01b815260040160405180910390fd5b61099785858585856001613839565b612b86600154600160a01b900460ff1690565b15612ba35760405162461bcd60e51b815260040161074a90614bbc565b600054600114612bc55760405162461bcd60e51b815260040161074a90614be6565b600260005583612be857604051631f2a200560e01b815260040160405180910390fd5b81612c0657604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b038116612c2d5760405163d92e233d60e01b815260040160405180910390fd5b600754604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac90612c5f9033908690600401615269565b600060405180830381600087803b158015612c7957600080fd5b505af1158015612c8d573d6000803e3d6000fd5b50505050806001600160a01b0316836001811115612cad57612cad614ba6565b60408051878152602081018690527f544190d889f77f8ba2f4a23c82e6cb9e2d8736398ebcad6d46fee54904664a91910160405180910390a3600b546001600160a01b031663731133e982612d058762127500615295565b612d0d61326c565b612d179190615251565b604080516020810182526000815290516001600160e01b031960e086901b168152612d489392918891600401614d2c565b600060405180830381600087803b158015612d6257600080fd5b505af1158015612d76573d6000803e3d6000fd5b50505050612d8684848484614151565b505060016000555050565b6001546001600160a01b03163314612dbb5760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b038116612de25760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038116600081815260116020908152604091829020805460ff1916905590519182527fac45f98530f79c12f05455e56ee49fe4dc4b6415e6f4f3b37c81bbaec27a67179101610ae8565b6001546001600160a01b03163314612e5d5760405162461bcd60e51b815260040161074a90614b71565b612e70600154600160a01b900460ff1690565b612e8c5760405162461bcd60e51b815260040161074a90614ca2565b6014546001600160a01b0316612eb5576040516398243aff60e01b815260040160405180910390fd5b6000612ec46020830183614810565b6001600160a01b03161415612eeb5760405162ff058560e61b815260040160405180910390fd5b612ef86020820182614f4e565b15159050612f185760405162ff058560e61b815260040160405180910390fd5b806012612f258282615339565b9050507ffdb0b10cafd6502602393136e48f4e31d44715ada8f26e5b312c87a523da25f181604051610ae89190615445565b612f6a600154600160a01b900460ff1690565b612f865760405162461bcd60e51b815260040161074a90614ca2565b6001546001600160a01b03163314612fb05760405162461bcd60e51b815260040161074a90614b71565b60025460405163312ff83960e01b8152600060048201526001600160a01b039091169063312ff839906024015b600060405180830381600087803b158015612ff757600080fd5b505af1158015612917573d6000803e3d6000fd5b61301e600154600160a01b900460ff1690565b61303a5760405162461bcd60e51b815260040161074a90614ca2565b6014546001600160a01b031633146130655760405163ea8e4eb560e01b815260040160405180910390fd5b6012546001600160a01b03168061308e5760405162ff058560e61b815260040160405180910390fd5b600060126001018054806020026020016040519081016040528092919081815260200182805480156130e957602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116130cb575b50508351939450505081151590506131135760405162ff058560e61b815260040160405180910390fd5b60055460005b8281101561322c57600084828151811061313557613135614f18565b60209081029190910101516040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561318a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ae9190614eff565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316826001600160a01b03161415613205578381116131f8576000613202565b613202848261502d565b90505b6132196001600160a01b0383168883614087565b50508061322590615068565b9050613119565b507f926c8ada80e43251424f6d741adc1c1c92ab1ddafbace323e78c2ac3c02e78ec848460405161325e9291906154fa565b60405180910390a150505050565b60006212750061327c81426152ca565b6132869190615295565b905090565b61329e600154600160a01b900460ff1690565b156132bb5760405162461bcd60e51b815260040161074a90614bbc565b6000546001146132dd5760405162461bcd60e51b815260040161074a90614be6565b60026000558361330057604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b0383166133275760405163d92e233d60e01b815260040160405180910390fd5b806001600160a01b0316821515846001600160a01b03167f31b839c908d80493b4ad00f5243f7bff78cb31b2f654ac5789067ef156a3d32c8760405161336f91815260200190565b60405180910390a483600660008282546133899190615251565b90915550600090506001600160a01b038216158015906133c157506001600160a01b03821660009081526011602052604090205460ff165b6133cc576000613417565b6003600052600f6020527f45f76dafbbad695564362934e24d72eedc57f9fc1a65f39bca62176cc829682854620f42409061340d9063ffffffff1687615295565b61341791906152ca565b9050600760009054906101000a90046001600160a01b03166001600160a01b0316634108d57c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561346957600080fd5b505af115801561347d573d6000803e3d6000fd5b50506007546001600160a01b031691506340c10f1990508461349f57856134a1565b305b6134ab848961502d565b6040518363ffffffff1660e01b81526004016134c8929190615269565b600060405180830381600087803b1580156134e257600080fd5b505af11580156134f6573d6000803e3d6000fd5b506135319250506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016905033308861436e565b801561359c576007546040516340c10f1960e01b81526001600160a01b03909116906340c10f19906135699085908590600401615269565b600060405180830381600087803b15801561358357600080fd5b505af1158015613597573d6000803e3d6000fd5b505050505b8215610997576135ac818661502d565b600e54604051636e553f6560e01b8152600481018390526001600160a01b038781166024830152929750911690636e553f65906044016020604051808303816000875af1158015613601573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136259190614eff565b5050506001600055505050565b6001546001600160a01b0316331461365c5760405162461bcd60e51b815260040161074a90614b71565b801561366d5761366a6143f1565b50565b61366a614479565b6001546001600160a01b0316331461369f5760405162461bcd60e51b815260040161074a90614b71565b6040517ffc6c5ac637f59dfbd7866ad96839e5f10712c8ee8f74ac0a28b2126ba34ffa2b90600090a16003546004805460405163785f6df160e11b8152918201526001600160a01b039091169063f0bedbe290602401612fdd565b6001546001600160a01b031633146137245760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b0381166137895760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161074a565b61366a816140ff565b6137a5600154600160a01b900460ff1690565b15611ce55760405162461bcd60e51b815260040161074a90614bbc565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806129175760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b604482015260640161074a565b84806138585760405163521299a960e01b815260040160405180910390fd5b80841461387857604051632b477e7160e11b815260040160405180910390fd5b6001600160a01b03831661389f5760405163d92e233d60e01b815260040160405180910390fd5b826001600160a01b03167f2caa05a02204e9c2b1678ebb749612708eb807abf2dc2f1cafb6e65484ebdc9e88888888876040516138e0959493929190615556565b60405180910390a26000805b8281101561399057600087878381811061390857613908614f18565b905060200201359050841580156139365750428a8a8481811061392d5761392d614f18565b90506020020135115b15613954576040516317defc1b60e01b815260040160405180910390fd5b8061397257604051631f2a200560e01b815260040160405180910390fd5b61397c8184615251565b9250508061398990615068565b90506138ec565b508261399e5761399e613f38565b80600560008282546139b0919061502d565b9091555050600a54604051631ac8311560e21b81526001600160a01b0390911690636b20c454906139ed9033908c908c908c908c90600401615592565b600060405180830381600087803b158015613a0757600080fd5b505af1158015613a1b573d6000803e3d6000fd5b50613a559250506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690508583614087565b5050505050505050565b60008080620f4240613a7763ffffffff891686615295565b613a8191906152ca565b92506000613a8f848661502d565b9050613a9b8688615251565b613aa58883615295565b613aaf91906152ca565b9250613abb838261502d565b9150509450945094915050565b60025460405163dc01f60d60e01b8152306004820181905260609290916000916001600160a01b03169063dc01f60d90602401600060405180830381865afa158015613b18573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613b4091908101906155ca565b8051909150806001600160401b03811115613b5d57613b5d6146cb565b604051908082528060200260200182016040528015613bbb57816020015b613ba8604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b815260200190600190039081613b7b5790505b50935060005b81811015613cdd576040518060600160405280848381518110613be657613be6614f18565b6020026020010151600001516001600160a01b03168152602001848381518110613c1257613c12614f18565b6020026020010151602001518152602001848381518110613c3557613c35614f18565b6020908102919091010151516040516370a0823160e01b81526001600160a01b038881166004830152909116906370a0823190602401602060405180830381865afa158015613c88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cac9190614eff565b815250858281518110613cc157613cc1614f18565b602002602001018190525080613cd690615068565b9050613bc1565b5050505090565b600254604051637050ccd960e01b8152306004820152600060248201526001600160a01b0390911690637050ccd990604401612fdd565b600084613d3b57604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b038416613d625760405163d92e233d60e01b815260040160405180910390fd5b604087015163ffffffff166000613d79428361502d565b90508315613dce576000629ce28082613d92888861502d565b613d9c9190615295565b613da691906152ca565b613db0908661502d565b9050620f4240613dc0828a615295565b613dca91906152ca565b9350505b6000613dda848961502d565b905080601060008581526020019081526020016000206000828254613dff9190615251565b909155505089516000848152601060205260409020546001600160701b039091161015613e3f57604051632f15da2d60e11b815260040160405180910390fd5b8060056000828254613e519190615251565b9091555050600a546040805160208101825260008152905163731133e960e01b81526001600160a01b039092169163731133e991613e98918b918891879190600401614d2c565b600060405180830381600087803b158015613eb257600080fd5b505af1158015613ec6573d6000803e3d6000fd5b5060009250613edc9150621275009050846152ca565b905080158015613ef75750613ef4621275008561567d565b15155b8015613f155750613f0c600262127500615691565b63ffffffff1683115b15613f1e576001015b613f2a818b8b8b614151565b505050509695505050505050565b613f406144dc565b6040516370a0823160e01b81523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613fa7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fcb9190614eff565b60055490915081118080613fe0575060065415155b15610f4557600081613ff3576000614000565b600554614000908461502d565b6002546006549192506001600160a01b03169063e2ab691d90309084116140295760065461402b565b835b60006040518463ffffffff1660e01b815260040161404b93929190614d0b565b600060405180830381600087803b15801561406557600080fd5b505af1158015614079573d6000803e3d6000fd5b505060006006555050505050565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806129175760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015260640161074a565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b806001600160a01b031683600181111561416d5761416d614ba6565b60408051878152602081018690527f1f6f8046c07a1cc7c25f2e21621c740a089a57c3f5aa1ee85534b651716fd90c910160405180910390a36000808460018111156141bb576141bb614ba6565b146141d157600d546001600160a01b03166141de565b600c546001600160a01b03165b90506000621275006141ee61326c565b6141f89190615251565b90506000866001600160401b03811115614214576142146146cb565b60405190808252806020026020018201604052801561423d578160200160208202803683370190505b5090506000876001600160401b0381111561425a5761425a6146cb565b604051908082528060200260200182016040528015614283578160200160208202803683370190505b50905060005b888110156142f45761429e6212750082615295565b6142a89085615251565b8382815181106142ba576142ba614f18565b602002602001018181525050868282815181106142d9576142d9614f18565b60209081029190910101526142ed81615068565b9050614289565b5060408051602081018252600081529051630fbfeffd60e11b81526001600160a01b03861691631f7fdffa91614332918991879187916004016156b4565b600060405180830381600087803b15801561434c57600080fd5b505af1158015614360573d6000803e3d6000fd5b505050505050505050505050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061224f5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b604482015260640161074a565b614404600154600160a01b900460ff1690565b156144215760405162461bcd60e51b815260040161074a90614bbc565b6001805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861445c3390565b6040516001600160a01b03909116815260200160405180910390a1565b61448c600154600160a01b900460ff1690565b6144a85760405162461bcd60e51b815260040161074a90614ca2565b6001805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa3361445c565b600254604051630241d3fb60e11b81523060048201526000916001600160a01b031690630483a7f690602401600060405180830381865afa158015614525573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261454d9190810190615162565b50509150508060001461366a5760025460405163312ff83960e01b8152600060048201526001600160a01b039091169063312ff83990602401612221565b80356004811061459a57600080fd5b919050565b63ffffffff8116811461366a57600080fd5b600080604083850312156145c457600080fd5b6145cd8361458b565b915060208301356145dd8161459f565b809150509250929050565b60008083601f8401126145fa57600080fd5b5081356001600160401b0381111561461157600080fd5b6020830191508360208260051b850101111561462c57600080fd5b9250929050565b6001600160a01b038116811461366a57600080fd5b60008060008060006060868803121561466057600080fd5b85356001600160401b038082111561467757600080fd5b61468389838a016145e8565b9097509550602088013591508082111561469c57600080fd5b506146a9888289016145e8565b90945092505060408601356146bd81614633565b809150509295509295909350565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715614703576147036146cb565b60405290565b604080519081016001600160401b0381118282101715614703576147036146cb565b604051601f8201601f191681016001600160401b0381118282101715614753576147536146cb565b604052919050565b8035801515811461459a57600080fd5b6000806040838503121561477e57600080fd5b82356001600160401b038082111561479557600080fd5b818501915085601f8301126147a957600080fd5b81356020828211156147bd576147bd6146cb565b6147cf601f8301601f1916820161472b565b925081835287818386010111156147e557600080fd5b8181850182850137600081838501015282955061480381880161475b565b9450505050509250929050565b60006020828403121561482257600080fd5b813561482d81614633565b9392505050565b60006020828403121561484657600080fd5b61482d8261458b565b6002811061366a57600080fd5b6000806000806080858703121561487257600080fd5b8435935060208501359250604085013561488b81614633565b9150606085013561489b8161484f565b939692955090935050565b600080604083850312156148b957600080fd5b82356148c48161484f565b915060208301356145dd81614633565b600080602083850312156148e757600080fd5b82356001600160401b038111156148fd57600080fd5b614909858286016145e8565b90969095509350505050565b60006020828403121561492757600080fd5b61482d8261475b565b6000806000806000806080878903121561494957600080fd5b86356001600160401b038082111561496057600080fd5b61496c8a838b016145e8565b9098509650602089013591506149818261484f565b9094506040880135908082111561499757600080fd5b506149a489828a016145e8565b90945092505060608701356149b881614633565b809150509295509295509295565b600080604083850312156149d957600080fd5b8235915060208301356145dd81614633565b60008060008060608587031215614a0157600080fd5b8435935060208501356001600160401b03811115614a1e57600080fd5b614a2a878288016145e8565b909450925050604085013561489b81614633565b60008060408385031215614a5157600080fd5b8235600881106148c457600080fd5b600080600060608486031215614a7557600080fd5b83359250602084013591506040840135614a8e81614633565b809150509250925092565b60008060008060808587031215614aaf57600080fd5b843593506020850135614ac18161484f565b925060408501359150606085013561489b81614633565b600060208284031215614aea57600080fd5b81356001600160401b03811115614b0057600080fd5b82016040818503121561482d57600080fd5b600060208284031215614b2457600080fd5b5035919050565b60008060008060808587031215614b4157600080fd5b843593506020850135614b5381614633565b9250614b616040860161475b565b9150606085013561489b81614633565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052602160045260246000fd5b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b80516020808301519190811015614c2b576000198160200360031b1b821691505b50919050565b6000815180845260005b81811015614c5757602081850181015186830182015201614c3b565b81811115614c69576000602083870101525b50601f01601f19169290920160200192915050565b604081526000614c916040830185614c31565b905082151560208301529392505050565b60208082526014908201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604082015260600190565b6002811061366a57634e487b7160e01b600052602160045260246000fd5b82815260408101614cfe83614cd0565b8260208301529392505050565b6001600160a01b039390931683526020830191909152604082015260600190565b60018060a01b0385168152836020820152826040820152608060608201526000614d596080830184614c31565b9695505050505050565b60408101614d7084614cd0565b9281526001600160a01b039190911660209091015290565b60006001600160401b03821115614da157614da16146cb565b5060051b60200190565b600082601f830112614dbc57600080fd5b81516020614dd1614dcc83614d88565b61472b565b82815260059290921b84018101918181019086841115614df057600080fd5b8286015b84811015614e0b5780518352918301918301614df4565b509695505050505050565b60008060008060808587031215614e2c57600080fd5b845193506020808601516001600160401b0380821115614e4b57600080fd5b818801915088601f830112614e5f57600080fd5b8151614e6d614dcc82614d88565b81815260059190911b8301840190848101908b831115614e8c57600080fd5b938501935b82851015614eaa57845182529385019390850190614e91565b60408b01519098509450505080831115614ec357600080fd5b614ecf89848a01614dab565b94506060880151925080831115614ee557600080fd5b5050614ef387828801614dab565b91505092959194509250565b600060208284031215614f1157600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b60008235607e19833603018112614f4457600080fd5b9190910192915050565b6000808335601e19843603018112614f6557600080fd5b8301803591506001600160401b03821115614f7f57600080fd5b6020019150600581901b360382131561462c57600080fd5b600081518084526020808501945080840160005b83811015614fc757815187529582019590820190600101614fab565b509495945050505050565b6001600160a01b03868116825260208201869052841660408201526060810183905260a06080820181905260009061500c90830184614f97565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b60008282101561503f5761503f615017565b500390565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600060001982141561507c5761507c615017565b5060010190565b600060408083018584526020828186015281865180845260609350838701915082880160005b828110156150df57815180516001600160a01b0316855285810151868601528701518785015292850192908401906001016150a9565b50919998505050505050505050565b81835260006001600160fb1b0383111561510757600080fd5b8260051b8083602087013760009401602001938452509192915050565b6040815260006151386040830186886150ee565b828103602084015261500c8185876150ee565b80516001600160701b038116811461459a57600080fd5b6000806000806080858703121561517857600080fd5b84519350602080860151935060408087015193506060808801516001600160401b038111156151a657600080fd5b8801601f81018a136151b757600080fd5b80516151c5614dcc82614d88565b8181529083028201850190858101908c8311156151e157600080fd5b928601925b828410156152405784848e0312156151fe5760008081fd5b6152066146e1565b61520f8561514b565b815261521c88860161514b565b888201528685015161522d8161459f565b81880152825292840192908601906151e6565b999c989b5096995050505050505050565b6000821982111561526457615264615017565b500190565b6001600160a01b03929092168252602082015260400190565b60208152600061482d6020830184614f97565b60008160001904831182151516156152af576152af615017565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826152d9576152d96152b4565b500490565b8281526040602082015260006152f76040830184614f97565b949350505050565b6060815260006153136060830186886150ee565b6020830194909452506040015292915050565b6000813561533381614633565b92915050565b813561534481614633565b81546001600160a01b0319166001600160a01b0391909116178155600181810160208481013536869003601e1901811261537d57600080fd5b850180356001600160401b0381111561539557600080fd5b82820191508060051b36038213156153ac57600080fd5b680100000000000000008111156153c5576153c56146cb565b8354818555808210156153f95760008581528481208381019083015b808210156153f557828255908801906153e1565b5050505b50600093845260208420935b81811015613a555761543661541984615326565b86546001600160a01b0319166001600160a01b0391909116178655565b93850193918301918501615405565b6000602080835260608301843561545b81614633565b6001600160a01b0390811685840152858301359036879003601e1901821261548257600080fd5b9086019081356001600160401b0381111561549c57600080fd5b8060051b36038813156154ae57600080fd5b604087810152928390529083019160009190608087015b818410156154ee5784356154d881614633565b83168152938501936001939093019285016154c5565b98975050505050505050565b6001600160a01b038381168252604060208084018290528451918401829052600092858201929091906060860190855b8181101561554857855185168352948301949183019160010161552a565b509098975050505050505050565b60608152600061556a6060830187896150ee565b828103602084015261557d8186886150ee565b91505082151560408301529695505050505050565b6001600160a01b03861681526060602082018190526000906155b790830186886150ee565b82810360408401526154ee8185876150ee565b600060208083850312156155dd57600080fd5b82516001600160401b038111156155f357600080fd5b8301601f8101851361560457600080fd5b8051615612614dcc82614d88565b81815260069190911b8201830190838101908783111561563157600080fd5b928401925b8284101561500c576040848903121561564f5760008081fd5b615657614709565b845161566281614633565b81528486015186820152825260409093019290840190615636565b60008261568c5761568c6152b4565b500690565b600063ffffffff808416806156a8576156a86152b4565b92169190910492915050565b6001600160a01b03851681526080602082018190526000906156d890830186614f97565b82810360408401526156ea8186614f97565b9050828103606084015261500c8185614c3156fea26469706673582212206c22135479bef5f310a4c289cae12da4468e6fa5ebed483ebcc6d8edc83be73c64736f6c634300080c00330000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b00000000000000000000000072a19342e8f1838460ebfccef09f6585e32db86e000000000000000000000000469788fe6e9e9681c6ebf3bf78e7fd26fc015446000000000000000000000000bce0cf87f513102f22232436cca2ca49e815c3ac0000000000000000000000007a3d81cfc5a942abe9ec656eff818f7dab4e0fe1000000000000000000000000e47e8b5b4ee05b7663b15087381bd47bcbd20fc3000000000000000000000000bae47cd0e2f0f687f4ef78f972af13f6cf8f2b76000000000000000000000000c044613b702ed11567a38108703ac5478a3f7db80000000000000000000000009b0dff589fc0acac23581ecaff5688434db0720f000000000000000000000000378ba9b73309be80bf4c2c027aad799766a7ed5a
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106102f85760003560e01c80638ce5991f11610193578063bbea0974116100e4578063d73792a911610092578063d73792a9146106c4578063dc945327146106ce578063dfc1d332146106e1578063edf187f0146106f4578063f2fde38b146106fc578063f7a1f5ff1461070f578063f83d08ba1461071857600080fd5b8063bbea097414610645578063bc8a72b61461064e578063beb6589314610661578063c22f940614610681578063c2624e1e14610694578063cd82fc131461069e578063cdb88ad1146106b157600080fd5b8063a2787cbb11610141578063a2787cbb146105e6578063a69df4b5146105f9578063a70b9f0c14610601578063ae1d65151461060b578063b0dba76814610622578063b29b355e14610635578063b97dd9e21461063d57600080fd5b80638ce5991f146105635780638d68f9ff146105765780638da5cb5b1461058957806393d4f2c91461059a5780639a7fdb58146105ad5780639cbb4108146105c05780639e23c209146105d357600080fd5b80635ed810ac1161024d5780636ae778de116101fb5780636ae778de146104d4578063715018a6146104e857806374874323146104f0578063759cb53b14610503578063813e434e1461052a578063865e6fd31461053d5780638bea9d501461055057600080fd5b80635ed810ac14610455578063607de7981461045d57806361fd5c20146104705780636310fbdd1461049357806364a34beb146104a657806364c0bef1146104b957806369c408e1146104c157600080fd5b8063385d792a116102aa578063385d792a146103c95780633f12ff39146103dc578063407cd779146103ef57806349bfe411146104025780634b81c36d1461041557806351c2d64f1461041f5780635c975abb1461043257600080fd5b806305c9399c146102fd57806308c676e914610312578063189fa7a2146103255780631d3c2b2f1461035557806322fbf1e81461036857806323c133ef1461037b578063357c13541461038e575b600080fd5b61031061030b3660046145b1565b610720565b005b610310610320366004614648565b610906565b601454610338906001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61031061036336600461476b565b6109a3565b610310610376366004614810565b610a47565b610310610389366004614810565b610af3565b6103b461039c366004614834565b600f6020526000908152604090205463ffffffff1681565b60405163ffffffff909116815260200161034c565b600b54610338906001600160a01b031681565b6103106103ea36600461485c565b610bea565b6103106103fd3660046148a6565b610e17565b6103106104103660046148d4565b610f68565b6103b4629ce28081565b600754610338906001600160a01b031681565b610445600154600160a01b900460ff1690565b604051901515815260200161034c565b6103106115fe565b61031061046b366004614915565b611824565b61044561047e366004614810565b60116020526000908152604090205460ff1681565b600a54610338906001600160a01b031681565b6103106104b4366004614930565b6118ca565b610310611c8c565b6103106104cf3660046149c6565b611cef565b60145461044590600160a01b900460ff1681565b61031061212a565b6103106104fe366004614810565b61215e565b6103387f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b81565b6103106105383660046149eb565b612256565b61031061054b366004614a3e565b612680565b600954610338906001600160a01b031681565b600e54610338906001600160a01b031681565b600254610338906001600160a01b031681565b6001546001600160a01b0316610338565b6103106105a8366004614a60565b61291d565b6103106105bb366004614648565b612ae4565b6103106105ce366004614a99565b612b73565b6103106105e1366004614810565b612d91565b6103106105f4366004614ad8565b612e33565b610310612f57565b6103b46212750081565b61061460065481565b60405190815260200161034c565b600c54610338906001600160a01b031681565b61031061300b565b61061461326c565b61061460045481565b600854610338906001600160a01b031681565b61061461066f366004614b12565b60106020526000908152604090205481565b61031061068f366004614b2b565b61328b565b6103b4620186a081565b601254610338906001600160a01b031681565b6103106106bf366004614915565b613632565b6103b4620f424081565b600d54610338906001600160a01b031681565b600354610338906001600160a01b031681565b610310613675565b61031061070a366004614810565b6136fa565b61061460055481565b610310613792565b6001546001600160a01b031633146107535760405162461bcd60e51b815260040161074a90614b71565b60405180910390fd5b620186a063ffffffff8216111561077d576040516358d620b360e01b815260040160405180910390fd5b600182600381111561079157610791614ba6565b1480156107d257506002600052600f6020527fa74ba3945261e09fde15ba3db55005b205e61eeb4ad811ac0faa2b315bffeead5463ffffffff908116908216105b156107f0576040516358d620b360e01b815260040160405180910390fd5b600282600381111561080457610804614ba6565b14801561084557506001600052600f6020527f169f97de0d9a84d840042b17d3c6b9638b3d6fd9024c9eb0c7a306a17b49f88f5463ffffffff908116908216115b15610863576040516358d620b360e01b815260040160405180910390fd5b80600f600084600381111561087a5761087a614ba6565b600381111561088b5761088b614ba6565b815260200190815260200160002060006101000a81548163ffffffff021916908363ffffffff1602179055508160038111156108c9576108c9614ba6565b60405163ffffffff831681527f60cca38ba894e5ddc4db50e39d1e729d5ae76ddbd3874395ead72add5125bc0f9060200160405180910390a25050565b610919600154600160a01b900460ff1690565b156109365760405162461bcd60e51b815260040161074a90614bbc565b6000546001146109585760405162461bcd60e51b815260040161074a90614be6565b6002600055601454600160a01b900460ff161561098857604051635bbdd11b60e01b815260040160405180910390fd5b61099785858585856000613839565b50506001600055505050565b6001546001600160a01b031633146109cd5760405162461bcd60e51b815260040161074a90614b71565b80156109db576109db613675565b815182906109fc5760405163ecd7b0d160e01b815260040160405180910390fd5b610a0581614c0a565b6004556040517f4969e7d59c970ec13a80e7e632b460c9202863355217c5d7ee7d0b5d7782e7c790610a3a9085908590614c7e565b60405180910390a1505050565b6001546001600160a01b03163314610a715760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b038116610a985760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038116600081815260116020908152604091829020805460ff1916600117905590519182527fbec2cf13b5a3044b9654dd6cc38607f4f4c78bee63efc62ac573d7116f00727d91015b60405180910390a150565b6001546001600160a01b03163314610b1d5760405162461bcd60e51b815260040161074a90614b71565b610b30600154600160a01b900460ff1690565b610b4c5760405162461bcd60e51b815260040161074a90614ca2565b6001600160a01b038116610b735760405163d92e233d60e01b815260040160405180910390fd5b6014546001600160a01b031615610b9c5760405162dc149f60e41b815260040160405180910390fd5b601480546001600160a01b0319166001600160a01b0383169081179091556040519081527f17a1e16b1eb20d593558f091e129e05f9226d60dc8294b9a45c45d75def3818590602001610ae8565b610bfd600154600160a01b900460ff1690565b15610c1a5760405162461bcd60e51b815260040161074a90614bbc565b610c2261326c565b8411610c4157604051631fff902d60e11b815260040160405180910390fd5b82610c5f57604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b038216610c865760405163d92e233d60e01b815260040160405180910390fd5b600080826001811115610c9b57610c9b614ba6565b14610cb157600d546001600160a01b0316610cbe565b600c546001600160a01b03165b9050600080836001811115610cd557610cd5614ba6565b14610ceb57600c546001600160a01b0316610cf8565b600d546001600160a01b03165b9050836001600160a01b0316867f5f27e148f282010505552bbed99662d7f6c5e4b25bfda923b4f487091ff602808786604051610d36929190614cee565b60405180910390a3604051637a94c56560e11b81526001600160a01b0383169063f5298aca90610d6e9033908a908a90600401614d0b565b600060405180830381600087803b158015610d8857600080fd5b505af1158015610d9c573d6000803e3d6000fd5b50506040805160208101825260008152905163731133e960e01b81526001600160a01b038516935063731133e99250610ddd9188918b918b91600401614d2c565b600060405180830381600087803b158015610df757600080fd5b505af1158015610e0b573d6000803e3d6000fd5b50505050505050505050565b6001546001600160a01b03163314610e415760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b038116610e685760405163d92e233d60e01b815260040160405180910390fd5b7fc16ef8dc1c6d7fb9d24f8525ad674572540847348de1a67d0542b3c80defe4ad8282604051610e99929190614d63565b60405180910390a16000826001811115610eb557610eb5614ba6565b1415610f4957600254610ef6906001600160a01b037f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b8116911660006137c2565b600280546001600160a01b0319166001600160a01b0383811691909117909155610f45907f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b16826000196137c2565b5050565b600380546001600160a01b0319166001600160a01b0383161790555050565b610f7b600154600160a01b900460ff1690565b15610f985760405162461bcd60e51b815260040161074a90614bbc565b600054600114610fba5760405162461bcd60e51b815260040161074a90614be6565b60026000558080610fde5760405163521299a960e01b815260040160405180910390fd5b600760009054906101000a90046001600160a01b03166001600160a01b0316634108d57c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561102e57600080fd5b505af1158015611042573d6000803e3d6000fd5b50505050600061105061326c565b600754604051635e05e35d60e11b8152600481018390529192506000916001600160a01b039091169063bc0bc6ba90602401600060405180830381865afa15801561109f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526110c79190810190614e16565b5050600d5460405163bd85b03960e01b8152600481018690529293506000926001600160a01b03909116915063bd85b03990602401602060405180830381865afa158015611119573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061113d9190614eff565b905060005b848110156115f057600087878381811061115e5761115e614f18565b90506020028101906111709190614f2e565b61117e906020810190614810565b9050600088888481811061119457611194614f18565b90506020028101906111a69190614f2e565b60200135905060008989858181106111c0576111c0614f18565b90506020028101906111d29190614f2e565b60400135905060008a8a868181106111ec576111ec614f18565b90506020028101906111fe9190614f2e565b61120c906060810190614f4e565b808060200260200160405190810160405280939291908181526020018383602002808284376000920191909152509293505050506001600160a01b0384166112675760405163d92e233d60e01b815260040160405180910390fd5b8161128557604051631f2a200560e01b815260040160405180910390fd5b60408051848152602081018490526001600160a01b038616917fc8fb114192ae3823bff69d1b8922f93f9e88da0ae61a2145277ca71bd550773b910160405180910390a26040516370a0823160e01b815230600482015284906000906001600160a01b038316906370a0823190602401602060405180830381865afa158015611312573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113369190614eff565b600954604051630968c76b60e11b81529192506001600160a01b0316906312d18ed69061136f908990899030908a908a90600401614fd2565b600060405180830381600087803b15801561138957600080fd5b505af115801561139d573d6000803e3d6000fd5b50506000808052600f6020527ff4803e074bd026baaf6ed2e288c9515f68c72fb7216eebdd7cae1718a53ec37554600754604051630981b24d60e41b8152600481018f905292945084935083926114c49263ffffffff16916001600160a01b03169063981b24d090602401602060405180830381865afa158015611425573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114499190614eff565b6040516370a0823160e01b81523060048201528e9088906001600160a01b038b16906370a0823190602401602060405180830381865afa158015611491573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114b59190614eff565b6114bf919061502d565b613a5f565b60075492955090935091506001600160a01b0316638dcc05838e60608c901b6bffffffffffffffffffffffff19166040516001600160e01b031960e085901b168152600481019290925260248201526044810185905260648101849052608401600060405180830381600087803b15801561153e57600080fd5b505af1158015611552573d6000803e3d6000fd5b505060085461157092506001600160a01b03888116925016856137c2565b600854604051631e75e2a960e11b81526001600160a01b0390911690633cebc552906115a49030908d908890600401615044565b600060405180830381600087803b1580156115be57600080fd5b505af11580156115d2573d6000803e3d6000fd5b50505050505050505050505050806115e990615068565b9050611142565b505060016000555050505050565b6000546001146116205760405162461bcd60e51b815260040161074a90614be6565b6002600090815561162f613ac8565b90507f3d1942b9a9626cb3a12d9bb6a45104e2d8a398cac0cb1782f22d5bdcf6e0f4824282604051611662929190615083565b60405180910390a1611672613ce4565b805160005b8181101561181a5782818151811061169157611691614f18565b602002602001015160200151600014156116aa5761180a565b60008382815181106116be576116be614f18565b602002602001015160000151905060008483815181106116e0576116e0614f18565b602090810291909101015160409081015190516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa158015611734573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117589190614eff565b611762919061502d565b60085490915061177f906001600160a01b038481169116836137c2565b60085485516001600160a01b0390911690633cebc5529030908890879081106117aa576117aa614f18565b602002602001015160000151846040518463ffffffff1660e01b81526004016117d593929190615044565b600060405180830381600087803b1580156117ef57600080fd5b505af1158015611803573d6000803e3d6000fd5b5050505050505b61181381615068565b9050611677565b5050600160005550565b6001546001600160a01b0316331461184e5760405162461bcd60e51b815260040161074a90614b71565b611861600154600160a01b900460ff1690565b61187d5760405162461bcd60e51b815260040161074a90614ca2565b60148054821515600160a01b0260ff60a01b199091161790556040517ffd8b4222303dc911d36e18aaf75a668ccdb8796c1a6150765cf912337fcb215490610ae890831515815260200190565b6118dd600154600160a01b900460ff1690565b156118fa5760405162461bcd60e51b815260040161074a90614bbc565b60005460011461191c5760405162461bcd60e51b815260040161074a90614be6565b600260005584806119405760405163521299a960e01b815260040160405180910390fd5b80831461196057604051632b477e7160e11b815260040160405180910390fd5b816001600160a01b031685600181111561197c5761197c614ba6565b7f41d92a50cdd5f1afd68a62623521edd0b02d64d636979e3dbb2ab0b33693e866898988886040516119b19493929190615124565b60405180910390a3600254604051630241d3fb60e11b81523060048201526000916001600160a01b031690630483a7f690602401600060405180830381865afa158015611a02573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611a2a9190810190615162565b600f6020527fa74ba3945261e09fde15ba3db55005b205e61eeb4ad811ac0faa2b315bffeead54600160009081527f169f97de0d9a84d840042b17d3c6b9638b3d6fd9024c9eb0c7a306a17b49f88f54929650945084935063ffffffff908116925016825b86811015611b3057898982818110611aa957611aa9614f18565b9050602002013585611abb9190615251565b9450611b14868e8e84818110611ad357611ad3614f18565b9050602002013581518110611aea57611aea614f18565b60200260200101518c8c8c85818110611b0557611b05614f18565b905060200201358b8787613d1b565b611b1e9085615251565b9350611b2981615068565b9050611a8f565b506007546001600160a01b0316639dc29fac33611b4d868861502d565b6040518363ffffffff1660e01b8152600401611b6a929190615269565b600060405180830381600087803b158015611b8457600080fd5b505af1158015611b98573d6000803e3d6000fd5b5050505082600014611c7957600754600854604051630c29702960e01b81526001600160a01b0392831692630c29702992611bdd923392909116908890600401615044565b600060405180830381600087803b158015611bf757600080fd5b505af1158015611c0b573d6000803e3d6000fd5b5050600854600754604051631e75e2a960e11b81526001600160a01b039283169450633cebc5529350611c4692339216908890600401615044565b600060405180830381600087803b158015611c6057600080fd5b505af1158015611c74573d6000803e3d6000fd5b505050505b5050600160005550505050505050505050565b611c9f600154600160a01b900460ff1690565b611cbb5760405162461bcd60e51b815260040161074a90614ca2565b6001546001600160a01b03163314611ce55760405162461bcd60e51b815260040161074a90614b71565b611ced613f38565b565b611d02600154600160a01b900460ff1690565b15611d1f5760405162461bcd60e51b815260040161074a90614bbc565b600054600114611d415760405162461bcd60e51b815260040161074a90614be6565b600260005581611d645760405163d5b25b6360e01b815260040160405180910390fd5b611d6c61326c565b821115611d8c5760405163d5b25b6360e01b815260040160405180910390fd5b6001600160a01b038116611db35760405163d92e233d60e01b815260040160405180910390fd5b600754604051635e05e35d60e11b81526004810184905260009182916001600160a01b039091169063bc0bc6ba90602401600060405180830381865afa158015611e01573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052611e299190810190614e16565b935050925050815160001415611e5257604051630fec21fd60e21b815260040160405180910390fd5b826001600160a01b0316847ff175480c0dfa18114de22d2cf0a65233a94439be6c56f58c54f500d0408cd4f384604051611e8c9190615282565b60405180910390a3600d54604051627eeac760e11b81526000916001600160a01b03169062fdd58e90611ec59033908990600401615269565b602060405180830381865afa158015611ee2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f069190614eff565b905080611f2657604051631e9acf1760e31b815260040160405180910390fd5b600d5460405163bd85b03960e01b8152600481018790526000916001600160a01b03169063bd85b03990602401602060405180830381865afa158015611f70573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611f949190614eff565b600d54604051637a94c56560e11b81529192506001600160a01b03169063f5298aca90611fc99033908a908790600401614d0b565b600060405180830381600087803b158015611fe357600080fd5b505af1158015611ff7573d6000803e3d6000fd5b505085519150600090505b818110156120b7576000838587848151811061202057612020614f18565b60200260200101516120329190615295565b61203c91906152ca565b90508086838151811061205157612051614f18565b60200260200101818151612065919061502d565b915081815250506120a6888289858151811061208357612083614f18565b602002602001015160601c6001600160a01b03166140879092919063ffffffff16565b506120b081615068565b9050612002565b5060075460405163734b1d8760e01b81526001600160a01b039091169063734b1d87906120ea908a9088906004016152de565b600060405180830381600087803b15801561210457600080fd5b505af1158015612118573d6000803e3d6000fd5b50506001600055505050505050505050565b6001546001600160a01b031633146121545760405162461bcd60e51b815260040161074a90614b71565b611ced60006140ff565b6001546001600160a01b031633146121885760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b0381166121af5760405163d92e233d60e01b815260040160405180910390fd5b6040516001600160a01b03821681527f906903afca224114603eddea72fbada90dcce859ee67b20861e842b79fc82ac09060200160405180910390a1600354600480546040516317b0dca160e31b8152918201526001600160a01b0383811660248301529091169063bd86e508906044015b600060405180830381600087803b15801561223b57600080fd5b505af115801561224f573d6000803e3d6000fd5b5050505050565b612269600154600160a01b900460ff1690565b156122865760405162461bcd60e51b815260040161074a90614bbc565b6000546001146122a85760405162461bcd60e51b815260040161074a90614be6565b6002600055836122cb5760405163d5b25b6360e01b815260040160405180910390fd5b6001600160a01b0381166122f25760405163d92e233d60e01b815260040160405180910390fd5b81806123115760405163521299a960e01b815260040160405180910390fd5b600754604051635e05e35d60e11b815260048101879052600091829182916001600160a01b03169063bc0bc6ba90602401600060405180830381865afa15801561235f573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526123879190810190614e16565b5060075460405163206e4f0760e01b815293965091945092506000916001600160a01b039091169063206e4f07906123c59033908d90600401615269565b602060405180830381865afa1580156123e2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124069190614eff565b60075460405163277166bf60e11b81529192506000916001600160a01b0390911690634ee2cd7e9061243e9033908990600401615269565b602060405180830381865afa15801561245b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061247f9190614eff565b600754604051630981b24d60e41b8152600481018890529192506000916001600160a01b039091169063981b24d090602401602060405180830381865afa1580156124ce573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124f29190614eff565b90508161251257604051631e9acf1760e31b815260040160405180910390fd5b876001600160a01b03168b7fb4508e4d3d04fe3c1551b10be4b1e7bbcd4d90381d183619bcfc896ba3ea74658c8c868660405161255294939291906152ff565b60405180910390a360005b878110156126075760008b8b8381811061257957612579614f18565b60200291909101359150506001811b858116156125a9576040516306d3830f60e21b815260040160405180910390fd5b80861795506125f48b85878a86815181106125c6576125c6614f18565b60200260200101516125d89190615295565b6125e291906152ca565b8a858151811061208357612083614f18565b50508061260090615068565b905061255d565b50600754604051631e38bf3160e01b81526001600160a01b0390911690631e38bf319061263c9033908f908890600401614d0b565b600060405180830381600087803b15801561265657600080fd5b505af115801561266a573d6000803e3d6000fd5b5050600160005550505050505050505050505050565b6001546001600160a01b031633146126aa5760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b0381166126d15760405163d92e233d60e01b815260040160405180910390fd5b8160078111156126e3576126e3614ba6565b6040516001600160a01b03831681527e0961cd4320f350803f764de4992b70c8fc5c948b5881a151f883df0af333579060200160405180910390a2600082600781111561273257612732614ba6565b141561275957600780546001600160a01b0383166001600160a01b03199091161790555050565b600182600781111561276d5761276d614ba6565b141561279457600880546001600160a01b0383166001600160a01b03199091161790555050565b60028260078111156127a8576127a8614ba6565b14156127cf57600980546001600160a01b0383166001600160a01b03199091161790555050565b60038260078111156127e3576127e3614ba6565b141561280a57600a80546001600160a01b0383166001600160a01b03199091161790555050565b600482600781111561281e5761281e614ba6565b141561284557600b80546001600160a01b0383166001600160a01b03199091161790555050565b600582600781111561285957612859614ba6565b141561288057600c80546001600160a01b0383166001600160a01b03199091161790555050565b600682600781111561289457612894614ba6565b14156128bb57600d80546001600160a01b0383166001600160a01b03199091161790555050565b600754600e546001600160a01b03918216911680156128e9576128e96001600160a01b0383168260006137c2565b600e80546001600160a01b0319166001600160a01b03858116918217909255612917918416906000196137c2565b50505050565b612930600154600160a01b900460ff1690565b1561294d5760405162461bcd60e51b815260040161074a90614bbc565b60005460011461296f5760405162461bcd60e51b815260040161074a90614be6565b6002600055428311156129955760405163bc58aaa760e01b815260040160405180910390fd5b816129b357604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b0381166129da5760405163d92e233d60e01b815260040160405180910390fd5b6007546040516340c10f1960e01b81526001600160a01b03909116906340c10f1990612a0c9084908690600401615269565b600060405180830381600087803b158015612a2657600080fd5b505af1158015612a3a573d6000803e3d6000fd5b505060408051868152602081018690526001600160a01b03851693507f8c930e6d96bc30b53e6f52f5670ccab75b962922809178c0da8bf7f931d0059092500160405180910390a2600b54604051637a94c56560e11b81526001600160a01b039091169063f5298aca90612ab690339087908790600401614d0b565b600060405180830381600087803b158015612ad057600080fd5b505af11580156115f0573d6000803e3d6000fd5b612af7600154600160a01b900460ff1690565b612b135760405162461bcd60e51b815260040161074a90614ca2565b600054600114612b355760405162461bcd60e51b815260040161074a90614be6565b6002600055601454600160a01b900460ff16612b6457604051635bbdd11b60e01b815260040160405180910390fd5b61099785858585856001613839565b612b86600154600160a01b900460ff1690565b15612ba35760405162461bcd60e51b815260040161074a90614bbc565b600054600114612bc55760405162461bcd60e51b815260040161074a90614be6565b600260005583612be857604051631f2a200560e01b815260040160405180910390fd5b81612c0657604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b038116612c2d5760405163d92e233d60e01b815260040160405180910390fd5b600754604051632770a7eb60e21b81526001600160a01b0390911690639dc29fac90612c5f9033908690600401615269565b600060405180830381600087803b158015612c7957600080fd5b505af1158015612c8d573d6000803e3d6000fd5b50505050806001600160a01b0316836001811115612cad57612cad614ba6565b60408051878152602081018690527f544190d889f77f8ba2f4a23c82e6cb9e2d8736398ebcad6d46fee54904664a91910160405180910390a3600b546001600160a01b031663731133e982612d058762127500615295565b612d0d61326c565b612d179190615251565b604080516020810182526000815290516001600160e01b031960e086901b168152612d489392918891600401614d2c565b600060405180830381600087803b158015612d6257600080fd5b505af1158015612d76573d6000803e3d6000fd5b50505050612d8684848484614151565b505060016000555050565b6001546001600160a01b03163314612dbb5760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b038116612de25760405163d92e233d60e01b815260040160405180910390fd5b6001600160a01b038116600081815260116020908152604091829020805460ff1916905590519182527fac45f98530f79c12f05455e56ee49fe4dc4b6415e6f4f3b37c81bbaec27a67179101610ae8565b6001546001600160a01b03163314612e5d5760405162461bcd60e51b815260040161074a90614b71565b612e70600154600160a01b900460ff1690565b612e8c5760405162461bcd60e51b815260040161074a90614ca2565b6014546001600160a01b0316612eb5576040516398243aff60e01b815260040160405180910390fd5b6000612ec46020830183614810565b6001600160a01b03161415612eeb5760405162ff058560e61b815260040160405180910390fd5b612ef86020820182614f4e565b15159050612f185760405162ff058560e61b815260040160405180910390fd5b806012612f258282615339565b9050507ffdb0b10cafd6502602393136e48f4e31d44715ada8f26e5b312c87a523da25f181604051610ae89190615445565b612f6a600154600160a01b900460ff1690565b612f865760405162461bcd60e51b815260040161074a90614ca2565b6001546001600160a01b03163314612fb05760405162461bcd60e51b815260040161074a90614b71565b60025460405163312ff83960e01b8152600060048201526001600160a01b039091169063312ff839906024015b600060405180830381600087803b158015612ff757600080fd5b505af1158015612917573d6000803e3d6000fd5b61301e600154600160a01b900460ff1690565b61303a5760405162461bcd60e51b815260040161074a90614ca2565b6014546001600160a01b031633146130655760405163ea8e4eb560e01b815260040160405180910390fd5b6012546001600160a01b03168061308e5760405162ff058560e61b815260040160405180910390fd5b600060126001018054806020026020016040519081016040528092919081815260200182805480156130e957602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116130cb575b50508351939450505081151590506131135760405162ff058560e61b815260040160405180910390fd5b60055460005b8281101561322c57600084828151811061313557613135614f18565b60209081029190910101516040516370a0823160e01b81523060048201529091506000906001600160a01b038316906370a0823190602401602060405180830381865afa15801561318a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906131ae9190614eff565b90507f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b6001600160a01b0316826001600160a01b03161415613205578381116131f8576000613202565b613202848261502d565b90505b6132196001600160a01b0383168883614087565b50508061322590615068565b9050613119565b507f926c8ada80e43251424f6d741adc1c1c92ab1ddafbace323e78c2ac3c02e78ec848460405161325e9291906154fa565b60405180910390a150505050565b60006212750061327c81426152ca565b6132869190615295565b905090565b61329e600154600160a01b900460ff1690565b156132bb5760405162461bcd60e51b815260040161074a90614bbc565b6000546001146132dd5760405162461bcd60e51b815260040161074a90614be6565b60026000558361330057604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b0383166133275760405163d92e233d60e01b815260040160405180910390fd5b806001600160a01b0316821515846001600160a01b03167f31b839c908d80493b4ad00f5243f7bff78cb31b2f654ac5789067ef156a3d32c8760405161336f91815260200190565b60405180910390a483600660008282546133899190615251565b90915550600090506001600160a01b038216158015906133c157506001600160a01b03821660009081526011602052604090205460ff165b6133cc576000613417565b6003600052600f6020527f45f76dafbbad695564362934e24d72eedc57f9fc1a65f39bca62176cc829682854620f42409061340d9063ffffffff1687615295565b61341791906152ca565b9050600760009054906101000a90046001600160a01b03166001600160a01b0316634108d57c6040518163ffffffff1660e01b8152600401600060405180830381600087803b15801561346957600080fd5b505af115801561347d573d6000803e3d6000fd5b50506007546001600160a01b031691506340c10f1990508461349f57856134a1565b305b6134ab848961502d565b6040518363ffffffff1660e01b81526004016134c8929190615269565b600060405180830381600087803b1580156134e257600080fd5b505af11580156134f6573d6000803e3d6000fd5b506135319250506001600160a01b037f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b16905033308861436e565b801561359c576007546040516340c10f1960e01b81526001600160a01b03909116906340c10f19906135699085908590600401615269565b600060405180830381600087803b15801561358357600080fd5b505af1158015613597573d6000803e3d6000fd5b505050505b8215610997576135ac818661502d565b600e54604051636e553f6560e01b8152600481018390526001600160a01b038781166024830152929750911690636e553f65906044016020604051808303816000875af1158015613601573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136259190614eff565b5050506001600055505050565b6001546001600160a01b0316331461365c5760405162461bcd60e51b815260040161074a90614b71565b801561366d5761366a6143f1565b50565b61366a614479565b6001546001600160a01b0316331461369f5760405162461bcd60e51b815260040161074a90614b71565b6040517ffc6c5ac637f59dfbd7866ad96839e5f10712c8ee8f74ac0a28b2126ba34ffa2b90600090a16003546004805460405163785f6df160e11b8152918201526001600160a01b039091169063f0bedbe290602401612fdd565b6001546001600160a01b031633146137245760405162461bcd60e51b815260040161074a90614b71565b6001600160a01b0381166137895760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840161074a565b61366a816140ff565b6137a5600154600160a01b900460ff1690565b15611ce55760405162461bcd60e51b815260040161074a90614bbc565b600060405163095ea7b360e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806129175760405162461bcd60e51b815260206004820152600e60248201526d1054141493d59157d1905253115160921b604482015260640161074a565b84806138585760405163521299a960e01b815260040160405180910390fd5b80841461387857604051632b477e7160e11b815260040160405180910390fd5b6001600160a01b03831661389f5760405163d92e233d60e01b815260040160405180910390fd5b826001600160a01b03167f2caa05a02204e9c2b1678ebb749612708eb807abf2dc2f1cafb6e65484ebdc9e88888888876040516138e0959493929190615556565b60405180910390a26000805b8281101561399057600087878381811061390857613908614f18565b905060200201359050841580156139365750428a8a8481811061392d5761392d614f18565b90506020020135115b15613954576040516317defc1b60e01b815260040160405180910390fd5b8061397257604051631f2a200560e01b815260040160405180910390fd5b61397c8184615251565b9250508061398990615068565b90506138ec565b508261399e5761399e613f38565b80600560008282546139b0919061502d565b9091555050600a54604051631ac8311560e21b81526001600160a01b0390911690636b20c454906139ed9033908c908c908c908c90600401615592565b600060405180830381600087803b158015613a0757600080fd5b505af1158015613a1b573d6000803e3d6000fd5b50613a559250506001600160a01b037f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b1690508583614087565b5050505050505050565b60008080620f4240613a7763ffffffff891686615295565b613a8191906152ca565b92506000613a8f848661502d565b9050613a9b8688615251565b613aa58883615295565b613aaf91906152ca565b9250613abb838261502d565b9150509450945094915050565b60025460405163dc01f60d60e01b8152306004820181905260609290916000916001600160a01b03169063dc01f60d90602401600060405180830381865afa158015613b18573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f19168201604052613b4091908101906155ca565b8051909150806001600160401b03811115613b5d57613b5d6146cb565b604051908082528060200260200182016040528015613bbb57816020015b613ba8604051806060016040528060006001600160a01b0316815260200160008152602001600081525090565b815260200190600190039081613b7b5790505b50935060005b81811015613cdd576040518060600160405280848381518110613be657613be6614f18565b6020026020010151600001516001600160a01b03168152602001848381518110613c1257613c12614f18565b6020026020010151602001518152602001848381518110613c3557613c35614f18565b6020908102919091010151516040516370a0823160e01b81526001600160a01b038881166004830152909116906370a0823190602401602060405180830381865afa158015613c88573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613cac9190614eff565b815250858281518110613cc157613cc1614f18565b602002602001018190525080613cd690615068565b9050613bc1565b5050505090565b600254604051637050ccd960e01b8152306004820152600060248201526001600160a01b0390911690637050ccd990604401612fdd565b600084613d3b57604051631f2a200560e01b815260040160405180910390fd5b6001600160a01b038416613d625760405163d92e233d60e01b815260040160405180910390fd5b604087015163ffffffff166000613d79428361502d565b90508315613dce576000629ce28082613d92888861502d565b613d9c9190615295565b613da691906152ca565b613db0908661502d565b9050620f4240613dc0828a615295565b613dca91906152ca565b9350505b6000613dda848961502d565b905080601060008581526020019081526020016000206000828254613dff9190615251565b909155505089516000848152601060205260409020546001600160701b039091161015613e3f57604051632f15da2d60e11b815260040160405180910390fd5b8060056000828254613e519190615251565b9091555050600a546040805160208101825260008152905163731133e960e01b81526001600160a01b039092169163731133e991613e98918b918891879190600401614d2c565b600060405180830381600087803b158015613eb257600080fd5b505af1158015613ec6573d6000803e3d6000fd5b5060009250613edc9150621275009050846152ca565b905080158015613ef75750613ef4621275008561567d565b15155b8015613f155750613f0c600262127500615691565b63ffffffff1683115b15613f1e576001015b613f2a818b8b8b614151565b505050509695505050505050565b613f406144dc565b6040516370a0823160e01b81523060048201526000907f0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b6001600160a01b0316906370a0823190602401602060405180830381865afa158015613fa7573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613fcb9190614eff565b60055490915081118080613fe0575060065415155b15610f4557600081613ff3576000614000565b600554614000908461502d565b6002546006549192506001600160a01b03169063e2ab691d90309084116140295760065461402b565b835b60006040518463ffffffff1660e01b815260040161404b93929190614d0b565b600060405180830381600087803b15801561406557600080fd5b505af1158015614079573d6000803e3d6000fd5b505060006006555050505050565b600060405163a9059cbb60e01b8152836004820152826024820152602060006044836000895af13d15601f3d11600160005114161716915050806129175760405162461bcd60e51b815260206004820152600f60248201526e1514905394d1915497d19052531151608a1b604482015260640161074a565b600180546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b806001600160a01b031683600181111561416d5761416d614ba6565b60408051878152602081018690527f1f6f8046c07a1cc7c25f2e21621c740a089a57c3f5aa1ee85534b651716fd90c910160405180910390a36000808460018111156141bb576141bb614ba6565b146141d157600d546001600160a01b03166141de565b600c546001600160a01b03165b90506000621275006141ee61326c565b6141f89190615251565b90506000866001600160401b03811115614214576142146146cb565b60405190808252806020026020018201604052801561423d578160200160208202803683370190505b5090506000876001600160401b0381111561425a5761425a6146cb565b604051908082528060200260200182016040528015614283578160200160208202803683370190505b50905060005b888110156142f45761429e6212750082615295565b6142a89085615251565b8382815181106142ba576142ba614f18565b602002602001018181525050868282815181106142d9576142d9614f18565b60209081029190910101526142ed81615068565b9050614289565b5060408051602081018252600081529051630fbfeffd60e11b81526001600160a01b03861691631f7fdffa91614332918991879187916004016156b4565b600060405180830381600087803b15801561434c57600080fd5b505af1158015614360573d6000803e3d6000fd5b505050505050505050505050565b60006040516323b872dd60e01b81528460048201528360248201528260448201526020600060648360008a5af13d15601f3d116001600051141617169150508061224f5760405162461bcd60e51b81526020600482015260146024820152731514905394d1915497d19493d357d1905253115160621b604482015260640161074a565b614404600154600160a01b900460ff1690565b156144215760405162461bcd60e51b815260040161074a90614bbc565b6001805460ff60a01b1916600160a01b1790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a25861445c3390565b6040516001600160a01b03909116815260200160405180910390a1565b61448c600154600160a01b900460ff1690565b6144a85760405162461bcd60e51b815260040161074a90614ca2565b6001805460ff60a01b191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa3361445c565b600254604051630241d3fb60e11b81523060048201526000916001600160a01b031690630483a7f690602401600060405180830381865afa158015614525573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f1916820160405261454d9190810190615162565b50509150508060001461366a5760025460405163312ff83960e01b8152600060048201526001600160a01b039091169063312ff83990602401612221565b80356004811061459a57600080fd5b919050565b63ffffffff8116811461366a57600080fd5b600080604083850312156145c457600080fd5b6145cd8361458b565b915060208301356145dd8161459f565b809150509250929050565b60008083601f8401126145fa57600080fd5b5081356001600160401b0381111561461157600080fd5b6020830191508360208260051b850101111561462c57600080fd5b9250929050565b6001600160a01b038116811461366a57600080fd5b60008060008060006060868803121561466057600080fd5b85356001600160401b038082111561467757600080fd5b61468389838a016145e8565b9097509550602088013591508082111561469c57600080fd5b506146a9888289016145e8565b90945092505060408601356146bd81614633565b809150509295509295909350565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715614703576147036146cb565b60405290565b604080519081016001600160401b0381118282101715614703576147036146cb565b604051601f8201601f191681016001600160401b0381118282101715614753576147536146cb565b604052919050565b8035801515811461459a57600080fd5b6000806040838503121561477e57600080fd5b82356001600160401b038082111561479557600080fd5b818501915085601f8301126147a957600080fd5b81356020828211156147bd576147bd6146cb565b6147cf601f8301601f1916820161472b565b925081835287818386010111156147e557600080fd5b8181850182850137600081838501015282955061480381880161475b565b9450505050509250929050565b60006020828403121561482257600080fd5b813561482d81614633565b9392505050565b60006020828403121561484657600080fd5b61482d8261458b565b6002811061366a57600080fd5b6000806000806080858703121561487257600080fd5b8435935060208501359250604085013561488b81614633565b9150606085013561489b8161484f565b939692955090935050565b600080604083850312156148b957600080fd5b82356148c48161484f565b915060208301356145dd81614633565b600080602083850312156148e757600080fd5b82356001600160401b038111156148fd57600080fd5b614909858286016145e8565b90969095509350505050565b60006020828403121561492757600080fd5b61482d8261475b565b6000806000806000806080878903121561494957600080fd5b86356001600160401b038082111561496057600080fd5b61496c8a838b016145e8565b9098509650602089013591506149818261484f565b9094506040880135908082111561499757600080fd5b506149a489828a016145e8565b90945092505060608701356149b881614633565b809150509295509295509295565b600080604083850312156149d957600080fd5b8235915060208301356145dd81614633565b60008060008060608587031215614a0157600080fd5b8435935060208501356001600160401b03811115614a1e57600080fd5b614a2a878288016145e8565b909450925050604085013561489b81614633565b60008060408385031215614a5157600080fd5b8235600881106148c457600080fd5b600080600060608486031215614a7557600080fd5b83359250602084013591506040840135614a8e81614633565b809150509250925092565b60008060008060808587031215614aaf57600080fd5b843593506020850135614ac18161484f565b925060408501359150606085013561489b81614633565b600060208284031215614aea57600080fd5b81356001600160401b03811115614b0057600080fd5b82016040818503121561482d57600080fd5b600060208284031215614b2457600080fd5b5035919050565b60008060008060808587031215614b4157600080fd5b843593506020850135614b5381614633565b9250614b616040860161475b565b9150606085013561489b81614633565b6020808252818101527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604082015260600190565b634e487b7160e01b600052602160045260246000fd5b60208082526010908201526f14185d5cd8589b194e881c185d5cd95960821b604082015260600190565b6020808252600a90820152695245454e5452414e435960b01b604082015260600190565b80516020808301519190811015614c2b576000198160200360031b1b821691505b50919050565b6000815180845260005b81811015614c5757602081850181015186830182015201614c3b565b81811115614c69576000602083870101525b50601f01601f19169290920160200192915050565b604081526000614c916040830185614c31565b905082151560208301529392505050565b60208082526014908201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b604082015260600190565b6002811061366a57634e487b7160e01b600052602160045260246000fd5b82815260408101614cfe83614cd0565b8260208301529392505050565b6001600160a01b039390931683526020830191909152604082015260600190565b60018060a01b0385168152836020820152826040820152608060608201526000614d596080830184614c31565b9695505050505050565b60408101614d7084614cd0565b9281526001600160a01b039190911660209091015290565b60006001600160401b03821115614da157614da16146cb565b5060051b60200190565b600082601f830112614dbc57600080fd5b81516020614dd1614dcc83614d88565b61472b565b82815260059290921b84018101918181019086841115614df057600080fd5b8286015b84811015614e0b5780518352918301918301614df4565b509695505050505050565b60008060008060808587031215614e2c57600080fd5b845193506020808601516001600160401b0380821115614e4b57600080fd5b818801915088601f830112614e5f57600080fd5b8151614e6d614dcc82614d88565b81815260059190911b8301840190848101908b831115614e8c57600080fd5b938501935b82851015614eaa57845182529385019390850190614e91565b60408b01519098509450505080831115614ec357600080fd5b614ecf89848a01614dab565b94506060880151925080831115614ee557600080fd5b5050614ef387828801614dab565b91505092959194509250565b600060208284031215614f1157600080fd5b5051919050565b634e487b7160e01b600052603260045260246000fd5b60008235607e19833603018112614f4457600080fd5b9190910192915050565b6000808335601e19843603018112614f6557600080fd5b8301803591506001600160401b03821115614f7f57600080fd5b6020019150600581901b360382131561462c57600080fd5b600081518084526020808501945080840160005b83811015614fc757815187529582019590820190600101614fab565b509495945050505050565b6001600160a01b03868116825260208201869052841660408201526060810183905260a06080820181905260009061500c90830184614f97565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b60008282101561503f5761503f615017565b500390565b6001600160a01b039384168152919092166020820152604081019190915260600190565b600060001982141561507c5761507c615017565b5060010190565b600060408083018584526020828186015281865180845260609350838701915082880160005b828110156150df57815180516001600160a01b0316855285810151868601528701518785015292850192908401906001016150a9565b50919998505050505050505050565b81835260006001600160fb1b0383111561510757600080fd5b8260051b8083602087013760009401602001938452509192915050565b6040815260006151386040830186886150ee565b828103602084015261500c8185876150ee565b80516001600160701b038116811461459a57600080fd5b6000806000806080858703121561517857600080fd5b84519350602080860151935060408087015193506060808801516001600160401b038111156151a657600080fd5b8801601f81018a136151b757600080fd5b80516151c5614dcc82614d88565b8181529083028201850190858101908c8311156151e157600080fd5b928601925b828410156152405784848e0312156151fe5760008081fd5b6152066146e1565b61520f8561514b565b815261521c88860161514b565b888201528685015161522d8161459f565b81880152825292840192908601906151e6565b999c989b5096995050505050505050565b6000821982111561526457615264615017565b500190565b6001600160a01b03929092168252602082015260400190565b60208152600061482d6020830184614f97565b60008160001904831182151516156152af576152af615017565b500290565b634e487b7160e01b600052601260045260246000fd5b6000826152d9576152d96152b4565b500490565b8281526040602082015260006152f76040830184614f97565b949350505050565b6060815260006153136060830186886150ee565b6020830194909452506040015292915050565b6000813561533381614633565b92915050565b813561534481614633565b81546001600160a01b0319166001600160a01b0391909116178155600181810160208481013536869003601e1901811261537d57600080fd5b850180356001600160401b0381111561539557600080fd5b82820191508060051b36038213156153ac57600080fd5b680100000000000000008111156153c5576153c56146cb565b8354818555808210156153f95760008581528481208381019083015b808210156153f557828255908801906153e1565b5050505b50600093845260208420935b81811015613a555761543661541984615326565b86546001600160a01b0319166001600160a01b0391909116178655565b93850193918301918501615405565b6000602080835260608301843561545b81614633565b6001600160a01b0390811685840152858301359036879003601e1901821261548257600080fd5b9086019081356001600160401b0381111561549c57600080fd5b8060051b36038813156154ae57600080fd5b604087810152928390529083019160009190608087015b818410156154ee5784356154d881614633565b83168152938501936001939093019285016154c5565b98975050505050505050565b6001600160a01b038381168252604060208084018290528451918401829052600092858201929091906060860190855b8181101561554857855185168352948301949183019160010161552a565b509098975050505050505050565b60608152600061556a6060830187896150ee565b828103602084015261557d8186886150ee565b91505082151560408301529695505050505050565b6001600160a01b03861681526060602082018190526000906155b790830186886150ee565b82810360408401526154ee8185876150ee565b600060208083850312156155dd57600080fd5b82516001600160401b038111156155f357600080fd5b8301601f8101851361560457600080fd5b8051615612614dcc82614d88565b81815260069190911b8201830190838101908783111561563157600080fd5b928401925b8284101561500c576040848903121561564f5760008081fd5b615657614709565b845161566281614633565b81528486015186820152825260409093019290840190615636565b60008261568c5761568c6152b4565b500690565b600063ffffffff808416806156a8576156a86152b4565b92169190910492915050565b6001600160a01b03851681526080602082018190526000906156d890830186614f97565b82810360408401526156ea8186614f97565b9050828103606084015261500c8185614c3156fea26469706673582212206c22135479bef5f310a4c289cae12da4468e6fa5ebed483ebcc6d8edc83be73c64736f6c634300080c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b00000000000000000000000072a19342e8f1838460ebfccef09f6585e32db86e000000000000000000000000469788fe6e9e9681c6ebf3bf78e7fd26fc015446000000000000000000000000bce0cf87f513102f22232436cca2ca49e815c3ac0000000000000000000000007a3d81cfc5a942abe9ec656eff818f7dab4e0fe1000000000000000000000000e47e8b5b4ee05b7663b15087381bd47bcbd20fc3000000000000000000000000bae47cd0e2f0f687f4ef78f972af13f6cf8f2b76000000000000000000000000c044613b702ed11567a38108703ac5478a3f7db80000000000000000000000009b0dff589fc0acac23581ecaff5688434db0720f000000000000000000000000378ba9b73309be80bf4c2c027aad799766a7ed5a
-----Decoded View---------------
Arg [0] : _CVX (address): 0x4e3FBD56CD56c3e72c1403e103b45Db9da5B9D2B
Arg [1] : _cvxLocker (address): 0x72a19342e8F1838460eBFCCEf09F6585e32db86E
Arg [2] : _cvxDelegateRegistry (address): 0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446
Arg [3] : _pxCvx (address): 0xBCe0Cf87F513102F22232436CCa2ca49e815C3aC
Arg [4] : _upxCvx (address): 0x7A3D81CFC5A942aBE9ec656EFF818f7daB4E0Fe1
Arg [5] : _spxCvx (address): 0xe47E8B5b4eE05B7663B15087381bD47bcbD20fc3
Arg [6] : _vpxCvx (address): 0xBae47cd0E2f0F687f4EF78F972af13F6Cf8f2b76
Arg [7] : _rpxCvx (address): 0xC044613B702Ed11567A38108703Ac5478a3F7DB8
Arg [8] : _pirexFees (address): 0x9b0DfF589FC0AcaC23581ECaFf5688434DB0720F
Arg [9] : _votiumMultiMerkleStash (address): 0x378Ba9B73309bE80BF4C2c027aAD799766a7ED5A
-----Encoded View---------------
10 Constructor Arguments found :
Arg [0] : 0000000000000000000000004e3fbd56cd56c3e72c1403e103b45db9da5b9d2b
Arg [1] : 00000000000000000000000072a19342e8f1838460ebfccef09f6585e32db86e
Arg [2] : 000000000000000000000000469788fe6e9e9681c6ebf3bf78e7fd26fc015446
Arg [3] : 000000000000000000000000bce0cf87f513102f22232436cca2ca49e815c3ac
Arg [4] : 0000000000000000000000007a3d81cfc5a942abe9ec656eff818f7dab4e0fe1
Arg [5] : 000000000000000000000000e47e8b5b4ee05b7663b15087381bd47bcbd20fc3
Arg [6] : 000000000000000000000000bae47cd0e2f0f687f4ef78f972af13f6cf8f2b76
Arg [7] : 000000000000000000000000c044613b702ed11567a38108703ac5478a3f7db8
Arg [8] : 0000000000000000000000009b0dff589fc0acac23581ecaff5688434db0720f
Arg [9] : 000000000000000000000000378ba9b73309be80bf4c2c027aad799766a7ed5a
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 31 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
ETH | 94.58% | $2.33 | 513,320.9658 | $1,194,733.68 | |
ETH | 2.68% | $1.53 | 22,167.4068 | $33,916.13 | |
ETH | 1.73% | $10.99 | 1,985.4797 | $21,820.42 | |
ETH | 0.51% | $0.308121 | 20,873.7546 | $6,431.64 | |
ETH | 0.14% | $2,177.9 | 0.8308 | $1,809.29 | |
ETH | 0.12% | $0.998038 | 1,475.0089 | $1,472.11 | |
ETH | 0.08% | $0.416443 | 2,507.0653 | $1,044.05 | |
ETH | 0.06% | $0.304769 | 2,546.8668 | $776.21 | |
ETH | 0.03% | $33.5 | 12.2615 | $410.76 | |
ETH | 0.02% | $0.999952 | 289.5317 | $289.52 | |
ETH | 0.02% | $0.000643 | 335,994.3363 | $216.07 | |
ETH | 0.01% | $0.214063 | 814.1715 | $174.28 | |
ETH | <0.01% | $0.029008 | 1,793 | $52.01 | |
ETH | <0.01% | $0.037687 | 567.5297 | $21.39 | |
ETH | <0.01% | $0.999651 | 18.3644 | $18.36 | |
ETH | <0.01% | $0.999902 | 2.8315 | $2.83 |
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.