Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 48,444 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Purchase Token W... | 19900366 | 219 days ago | IN | 0.005 ETH | 0.00007161 | ||||
Purchase Token W... | 19784239 | 235 days ago | IN | 0.02 ETH | 0.00213897 | ||||
Purchase Token W... | 19451703 | 282 days ago | IN | 0.0193 ETH | 0.0006119 | ||||
Purchase Token W... | 19393434 | 290 days ago | IN | 0.13 ETH | 0.00281762 | ||||
Purchase Token W... | 19386252 | 291 days ago | IN | 0.26 ETH | 0.00185048 | ||||
Purchase Token W... | 19386211 | 291 days ago | IN | 0.088 ETH | 0.00162066 | ||||
Purchase Token W... | 19384303 | 291 days ago | IN | 0.3 ETH | 0.00321912 | ||||
Purchase Token W... | 19384053 | 291 days ago | IN | 0.11 ETH | 0.00371048 | ||||
Purchase Token W... | 19384050 | 291 days ago | IN | 0 ETH | 0.01009364 | ||||
Purchase Token W... | 19384050 | 291 days ago | IN | 0.31 ETH | 0.00915563 | ||||
Purchase Token W... | 19384050 | 291 days ago | IN | 0.03 ETH | 0.01119721 | ||||
Purchase Token W... | 19384049 | 291 days ago | IN | 0.4 ETH | 0.00870281 | ||||
Purchase Token W... | 19384049 | 291 days ago | IN | 0.001 ETH | 0.01042944 | ||||
Purchase Token W... | 19384049 | 291 days ago | IN | 0.12 ETH | 0.01043164 | ||||
Purchase Token W... | 19384049 | 291 days ago | IN | 0.9 ETH | 0.00885912 | ||||
Purchase Token W... | 19384049 | 291 days ago | IN | 0.37 ETH | 0.01043164 | ||||
Purchase Token W... | 19384047 | 291 days ago | IN | 0.01 ETH | 0.00837535 | ||||
Purchase Token W... | 19384047 | 291 days ago | IN | 0.05 ETH | 0.01057907 | ||||
Purchase Token W... | 19384047 | 291 days ago | IN | 0.21 ETH | 0.01058131 | ||||
Purchase Token W... | 19384046 | 291 days ago | IN | 0.075 ETH | 0.00986791 | ||||
Purchase Token W... | 19384046 | 291 days ago | IN | 0.259 ETH | 0.00989688 | ||||
Purchase Token W... | 19384046 | 291 days ago | IN | 0.4 ETH | 0.01079652 | ||||
Purchase Token W... | 19384045 | 291 days ago | IN | 0.032 ETH | 0.00850323 | ||||
Purchase Token W... | 19384045 | 291 days ago | IN | 6 ETH | 0.00920971 | ||||
Purchase Token W... | 19384044 | 291 days ago | IN | 0.01 ETH | 0.0091309 |
Latest 25 internal transactions (View All)
Advanced mode:
Parent Transaction Hash | Block |
From
|
To
|
|||
---|---|---|---|---|---|---|
19523735 | 271 days ago | 0.59376 ETH | ||||
19523735 | 271 days ago | 0.59376 ETH | ||||
19507240 | 274 days ago | 0.015 ETH | ||||
19507240 | 274 days ago | 0.015 ETH | ||||
19481639 | 277 days ago | 0.01 ETH | ||||
19481639 | 277 days ago | 0.01 ETH | ||||
19481635 | 277 days ago | 0.06154 ETH | ||||
19481635 | 277 days ago | 0.06154 ETH | ||||
19475392 | 278 days ago | 0.174 ETH | ||||
19475392 | 278 days ago | 0.174 ETH | ||||
19466795 | 279 days ago | 0.40768 ETH | ||||
19466795 | 279 days ago | 0.40768 ETH | ||||
19465380 | 280 days ago | 0.01745 ETH | ||||
19465380 | 280 days ago | 0.01745 ETH | ||||
19465377 | 280 days ago | 0.04127 ETH | ||||
19465377 | 280 days ago | 0.04127 ETH | ||||
19457039 | 281 days ago | 0.21105 ETH | ||||
19457039 | 281 days ago | 0.21105 ETH | ||||
19452718 | 281 days ago | 0.03633 ETH | ||||
19452718 | 281 days ago | 0.03633 ETH | ||||
19448963 | 282 days ago | 0.08265 ETH | ||||
19448963 | 282 days ago | 0.08265 ETH | ||||
19447991 | 282 days ago | 0.0865 ETH | ||||
19447991 | 282 days ago | 0.0865 ETH | ||||
19447859 | 282 days ago | 0.30588 ETH |
Loading...
Loading
Contract Name:
PreSaleDop
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 1000000 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {Address} from "@openzeppelin/contracts/utils/Address.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import {Rounds, Ownable} from "./Rounds.sol"; import {IPreSaleDop} from "./IPreSaleDop.sol"; import "./Common.sol"; /// @title PreSaleDop contract /// @notice Implements the preSale of Dop Token /// @dev The presale contract allows you to purchase dop token with allowed tokens, /// and there will be certain rounds. /// @dev The recorded DOP tokens and NFT claims will be distributed later using another distributor contract. contract PreSaleDop is IPreSaleDop, Rounds, ReentrancyGuard { using SafeERC20 for IERC20; using Address for address payable; /// @notice Thrown when address is blacklisted error Blacklisted(); /// @notice Thrown when buy is disabled error BuyNotEnable(); /// @notice Thrown when sign deadline is expired error DeadlineExpired(); /// @notice Thrown when Sign is invalid error InvalidSignature(); /// @notice Thrown when Eth price suddenly drops while purchasing with ETH error UnexpectedPriceDifference(); /// @notice Thrown when value to transfer is zero error ZeroValue(); /// @notice Thrown when price from pricefeed is zero error PriceNotFound(); /// @notice Thrown when caller is not claimsContract error OnlyClaims(); /// @notice Thrown when investment is less than nft prices combined error InvalidInvestment(); /// @notice Thrown when both pricefeed and reference price are non zero error CodeSyncIssue(); /// @notice That buyEnable or not bool public buyEnable = true; /// @notice The address of signerWallet address public signerWallet; /// @notice The address of claimsContract address public claimsContract; /// @notice The address of fundsWallet address public fundsWallet; /// @notice The array of prices of each nft uint256[] public nftPricing; /// @notice Gives claim info of user in every round mapping(address => mapping(uint32 => uint256)) public claims; /// @notice Gives info about address's permission mapping(address => bool) public blacklistAddress; /// @notice Gives claim info of user nft in every round mapping(address => mapping(uint32 => ClaimNFT[])) public claimNFT; /// @member nftAmounts The nft amounts /// @member roundPrice The round number struct ClaimNFT { uint256[] nftAmounts; uint256 roundPrice; } /// @member price The price of token from priceFeed /// @member normalizationFactorForToken The normalization factor to achieve return value of 18 decimals ,while calculating dop token purchases and always with different token decimals /// @member normalizationFactorForNFT The normalization factor is the value which helps us to convert decimals of USDT to investment token decimals and always with different token decimals struct TokenInfo { uint256 latestPrice; uint8 normalizationFactorForToken; uint8 normalizationFactorForNFT; } /// @dev Emitted when dop is purchased with ETH event InvestedWithETH( address indexed by, string code, uint256 amountInvestedEth, uint32 indexed round, uint256 indexed roundPrice, uint256 dopPurchased ); /// @dev Emitted when dop is purchased with Token event InvestedWithToken( IERC20 indexed token, uint256 tokenPrice, address indexed by, string code, uint256 amountInvested, uint256 dopPurchased, uint32 indexed round ); /// @dev Emitted when dop NFT is purchased with ETH event InvestedWithETHForNFT( address indexed by, string code, uint256 amountInEth, uint256 ethPrice, uint32 indexed round, uint256 roundPrice, uint256[] nftAmounts ); /// @dev Emitted when dop NFT is purchased with token event InvestedWithTokenForNFT( IERC20 indexed token, uint256 tokenPrice, address indexed by, string code, uint256 amountInvested, uint32 indexed round, uint256 roundPrice, uint256[] nftAmounts ); /// @dev Emitted when dop is purchased claim amount event InvestedWithClaimAmount( address indexed by, uint256 amount, IERC20 token, uint32 indexed round, uint256 indexed tokenPrice, uint256 dopPurchased ); /// @dev Emitted when address of signer is updated event SignerUpdated(address oldSigner, address newSigner); /// @dev Emitted when address of funds wallet is updated event FundsWalletUpdated(address oldAddress, address newAddress); /// @dev Emitted when blacklist access of address is updated event BlacklistUpdated(address which, bool accessNow); /// @dev Emitted when buying access changes event BuyEnableUpdated(bool oldAccess, bool newAccess); /// @dev Emitted when dop NFT prices are updated event PricingUpdated(uint256 oldPrice, uint256 newPrice); /// @notice Restricts when updating wallet/contract address to zero address modifier checkAddressZero(address which) { if (which == address(0)) { revert ZeroAddress(); } _; } /// @notice Ensures that buy is enabled when buying modifier canBuy() { if (!buyEnable) { revert BuyNotEnable(); } _; } /// @dev Constructor. /// @param fundsWalletAddress The address of funds wallet /// @param signerAddress The address of signer wallet /// @param claimsContractAddress The address of claim contract /// @param lastRound The last round created /// @param nftPrices The prices of the dop NFTs constructor( address fundsWalletAddress, address signerAddress, address claimsContractAddress, address owner, uint32 lastRound, uint256[] memory nftPrices ) Rounds(lastRound) Ownable(owner) { if ( fundsWalletAddress == address(0) || signerAddress == address(0) || claimsContractAddress == address(0) || owner == address(0) ) { revert ZeroAddress(); } fundsWallet = fundsWalletAddress; signerWallet = signerAddress; claimsContract = claimsContractAddress; if (nftPrices.length == 0) { revert ZeroLengthArray(); } for (uint256 i = 0; i < nftPrices.length; ++i) { _checkValue(nftPrices[i]); } nftPricing = nftPrices; } /// @notice Changes access of buying /// @param enabled The decision about buying function enableBuy(bool enabled) external onlyOwner { if (buyEnable == enabled) { revert IdenticalValue(); } emit BuyEnableUpdated({oldAccess: buyEnable, newAccess: enabled}); buyEnable = enabled; } /// @notice Changes signer wallet address /// @param newSigner The address of the new signer wallet function changeSigner( address newSigner ) external checkAddressZero(newSigner) onlyOwner { address oldSigner = signerWallet; if (oldSigner == newSigner) { revert IdenticalValue(); } emit SignerUpdated({oldSigner: oldSigner, newSigner: newSigner}); signerWallet = newSigner; } /// @notice Changes funds wallet to a new address /// @param newFundsWallet The address of the new funds wallet function changeFundsWallet( address newFundsWallet ) external checkAddressZero(newFundsWallet) onlyOwner { address oldWallet = fundsWallet; if (oldWallet == newFundsWallet) { revert IdenticalValue(); } emit FundsWalletUpdated({ oldAddress: oldWallet, newAddress: newFundsWallet }); fundsWallet = newFundsWallet; } /// @notice Changes the access of any address in contract interaction /// @param which The address for which access is updated /// @param access The access decision of `which` address function updateBlackListedUser( address which, bool access ) external checkAddressZero(which) onlyOwner { bool oldAccess = blacklistAddress[which]; if (oldAccess == access) { revert IdenticalValue(); } emit BlacklistUpdated({which: which, accessNow: access}); blacklistAddress[which] = access; } /// @notice Purchases dopToken with Eth /// @param code The code is used to verify signature of the user /// @param round The round in which user wants to purchase /// @param deadline The deadline is validity of the signature /// @param minAmountDop The minAmountDop user agrees to purchase /// @param v The `v` signature parameter /// @param r The `r` signature parameter /// @param s The `s` signature parameter function purchaseTokenWithEth( string memory code, uint32 round, uint256 deadline, uint256 minAmountDop, uint8 v, bytes32 r, bytes32 s ) external payable canBuy { // The input must have been signed by the presale signer _validatePurchaseWithEth(msg.value, round, deadline, code, v, r, s); uint256 roundPrice = _getRoundPriceForToken(round, ETH); TokenInfo memory tokenInfo = getLatestPrice(ETH); if (tokenInfo.latestPrice == 0) { revert PriceNotFound(); } uint256 toReturn = _calculateDop( msg.value, tokenInfo.latestPrice, tokenInfo.normalizationFactorForToken, roundPrice ); if (toReturn < minAmountDop) { revert UnexpectedPriceDifference(); } claims[msg.sender][round] += toReturn; payable(fundsWallet).sendValue(msg.value); emit InvestedWithETH({ by: msg.sender, code: code, amountInvestedEth: msg.value, round: round, roundPrice: roundPrice, dopPurchased: toReturn }); } /// @notice Purchases dopToken with any token /// @param token The address of investment token /// @param referenceNormalizationFactor The normalization factor /// @param referenceTokenPrice The current price of token in 10 decimals /// @param investment The Investment amount /// @param minAmountDop The minAmountDop user agrees to purchase /// @param code The code is used to verify signature of the user /// @param round The round in which user wants to purchase /// @param deadline The deadline is validity of the signature /// @param v The `v` signature parameter /// @param r The `r` signature parameter /// @param s The `s` signature parameter function purchaseTokenWithToken( IERC20 token, uint8 referenceNormalizationFactor, uint256 referenceTokenPrice, uint256 investment, uint256 minAmountDop, string memory code, uint32 round, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external canBuy nonReentrant { // The input must have been signed by the presale signer _validatePurchaseWithToken( token, round, deadline, code, referenceTokenPrice, referenceNormalizationFactor, v, r, s ); _checkValue(investment); uint256 roundPrice = _getRoundPriceForToken(round, token); (uint256 latestPrice, uint256 normalizationFactor) = _validatePrice( token, referenceTokenPrice, referenceNormalizationFactor ); uint256 toReturn = _calculateDop( investment, latestPrice, normalizationFactor, roundPrice ); if (toReturn < minAmountDop) { revert UnexpectedPriceDifference(); } claims[msg.sender][round] += toReturn; token.safeTransferFrom(msg.sender, fundsWallet, investment); emit InvestedWithToken({ token: token, tokenPrice: latestPrice, by: msg.sender, code: code, amountInvested: investment, dopPurchased: toReturn, round: round }); } /// @notice Purchases NFT with Eth /// @param code The code is used to verify signature of the user /// @param round The round in which user wants to purchase /// @param nftAmounts The nftAmounts is array of nfts selected /// @param deadline The deadline is validity of the signature /// @param v The `v` signature parameter /// @param r The `r` signature parameter /// @param s The `s` signature parameter function purchaseNFTWithEth( string memory code, uint32 round, uint256[] calldata nftAmounts, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external payable canBuy nonReentrant { uint256[] memory nftPrices = nftPricing; _validateArrays(nftAmounts.length, nftPrices.length); // The input must have been signed by the presale signer _validatePurchaseWithEth(msg.value, round, deadline, code, v, r, s); TokenInfo memory tokenInfo = getLatestPrice(ETH); if (tokenInfo.latestPrice == 0) { revert PriceNotFound(); } (uint256 value, uint256 roundPrice) = _processPurchaseNFT( ETH, tokenInfo.latestPrice, tokenInfo.normalizationFactorForNFT, round, nftAmounts, nftPrices ); if (msg.value < value) { revert InvalidInvestment(); } _checkValue(value); uint256 amountUnused = msg.value - value; if (amountUnused > 0) { payable(msg.sender).sendValue(amountUnused); } payable(fundsWallet).sendValue(value); emit InvestedWithETHForNFT({ by: msg.sender, code: code, amountInEth: value, ethPrice: tokenInfo.latestPrice, round: round, roundPrice: roundPrice, nftAmounts: nftAmounts }); } /// @notice Purchases NFT with token /// @param token The address of investment token /// @param referenceTokenPrice The current price of token in 10 decimals /// @param referenceNormalizationFactor The normalization factor /// @param code The code is used to verify signature of the user /// @param round The round in which user wants to purchase /// @param nftAmounts The nftAmounts is array of nfts selected /// @param deadline The deadline is validity of the signature /// @param v The `v` signature parameter /// @param r The `r` signature parameter /// @param s The `s` signature parameter function purchaseNFTWithToken( IERC20 token, uint256 referenceTokenPrice, uint8 referenceNormalizationFactor, string memory code, uint32 round, uint256[] calldata nftAmounts, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external canBuy nonReentrant { uint256[] memory nftPrices = nftPricing; _validateArrays(nftAmounts.length, nftPrices.length); // The input must have been signed by the presale signer _validatePurchaseWithToken( token, round, deadline, code, referenceTokenPrice, referenceNormalizationFactor, v, r, s ); TokenInfo memory tokenInfo = getLatestPrice(token); if (tokenInfo.latestPrice != 0) { if (referenceTokenPrice != 0 || referenceNormalizationFactor != 0) { revert CodeSyncIssue(); } } // If price feed isn't available,we fallback to the reference price if (tokenInfo.latestPrice == 0) { if (referenceTokenPrice == 0 || referenceNormalizationFactor == 0) { revert ZeroValue(); } tokenInfo.latestPrice = referenceTokenPrice; tokenInfo.normalizationFactorForNFT = referenceNormalizationFactor; } (uint256 value, uint256 roundPrice) = _processPurchaseNFT( token, tokenInfo.latestPrice, tokenInfo.normalizationFactorForNFT, round, nftAmounts, nftPrices ); _checkValue(value); token.safeTransferFrom(msg.sender, fundsWallet, value); emit InvestedWithTokenForNFT({ token: token, tokenPrice: tokenInfo.latestPrice, by: msg.sender, code: code, amountInvested: value, round: round, roundPrice: roundPrice, nftAmounts: nftAmounts }); } /// @inheritdoc IPreSaleDop function purchaseWithClaim( IERC20 token, uint256 referenceTokenPrice, uint8 referenceNormalizationFactor, uint256 amount, uint256 minAmountDop, address recipient, uint32 round ) external payable canBuy nonReentrant { if (msg.sender != claimsContract) { revert OnlyClaims(); } _checkBlacklist(recipient); if (!allowedTokens[round][token].access) { revert TokenDisallowed(); } uint256 roundPrice = _getRoundPriceForToken(round, token); (uint256 latestPrice, uint256 normalizationFactor) = _validatePrice( token, referenceTokenPrice, referenceNormalizationFactor ); uint256 toReturn = _calculateDop( amount, latestPrice, normalizationFactor, roundPrice ); if (toReturn < minAmountDop) { revert UnexpectedPriceDifference(); } claims[recipient][round] += toReturn; if (token == ETH) { payable(fundsWallet).sendValue(msg.value); } else { token.safeTransferFrom(claimsContract, fundsWallet, amount); } emit InvestedWithClaimAmount({ by: recipient, amount: amount, token: token, round: round, tokenPrice: latestPrice, dopPurchased: toReturn }); } /// @notice Changes the access of any address in contract interaction /// @param newPrices The new prices of NFTs function updatePricing(uint256[] memory newPrices) external onlyOwner { uint256[] memory oldPrices = nftPricing; if (newPrices.length != oldPrices.length) { revert ArrayLengthMismatch(); } for (uint256 i = 0; i < newPrices.length; ++i) { uint256 newPrice = newPrices[i]; _checkValue(newPrice); emit PricingUpdated({oldPrice: oldPrices[i], newPrice: newPrice}); } nftPricing = newPrices; } /// @inheritdoc IPreSaleDop function verifyPurchaseWithClaim( address recipient, uint32 round, uint256 deadline, uint256[] calldata tokenPrices, uint8[] calldata normalizationFactors, IERC20[] calldata tokens, uint256[] calldata amounts, uint8 v, bytes32 r, bytes32 s ) external view { if (msg.sender != claimsContract) { revert OnlyClaims(); } bytes32 encodedMessageHash = keccak256( abi.encodePacked( recipient, round, tokenPrices, normalizationFactors, deadline, tokens, amounts ) ); _verifyMessage(encodedMessageHash, v, r, s); } /// @notice The Chainlink inherited function, give us tokens live price function getLatestPrice( IERC20 token ) public view returns (TokenInfo memory) { PriceFeedData memory data = tokenData[token]; TokenInfo memory tokenInfo; if (address(data.priceFeed) == address(0)) { return tokenInfo; } ( , /*uint80 roundID*/ int price /*uint256 startedAt*/ /*uint80 answeredInRound*/, , , ) = /*uint256 timeStamp*/ data.priceFeed.latestRoundData(); tokenInfo = TokenInfo({ latestPrice: uint256(price), normalizationFactorForToken: data.normalizationFactorForToken, normalizationFactorForNFT: data.normalizationFactorForNFT }); return tokenInfo; } /// @notice Checks value, if zero then reverts function _checkValue(uint256 value) private pure { if (value == 0) { revert ZeroValue(); } } /// @notice Validates blacklist address, round and deadline function _validatePurchase( uint32 round, uint256 deadline, IERC20 token ) private view { if (block.timestamp > deadline) { revert DeadlineExpired(); } _checkBlacklist(msg.sender); if (!allowedTokens[round][token].access) { revert TokenDisallowed(); } _verifyInRound(round); } /// @notice The helper function which verifies signature, signed by signerWallet, reverts if Invalid function _verifyCode( string memory code, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) private view { bytes32 encodedMessageHash = keccak256( abi.encodePacked(msg.sender, code, deadline) ); _verifyMessage(encodedMessageHash, v, r, s); } /// @notice The helper function which verifies signature, signed by signerWallet, reverts if Invalid function _verifyCodeWithPrice( string memory code, uint256 deadline, uint256 referenceTokenPrice, IERC20 token, uint256 normalizationFactor, uint8 v, bytes32 r, bytes32 s ) private view { bytes32 encodedMessageHash = keccak256( abi.encodePacked( msg.sender, code, referenceTokenPrice, deadline, token, normalizationFactor ) ); _verifyMessage(encodedMessageHash, v, r, s); } /// @notice Verifies the address that signed a hashed message (`hash`) with /// `signature` function _verifyMessage( bytes32 encodedMessageHash, uint8 v, bytes32 r, bytes32 s ) private view { if ( signerWallet != ECDSA.recover( MessageHashUtils.toEthSignedMessageHash(encodedMessageHash), v, r, s ) ) { revert InvalidSignature(); } } /// @notice Process nft purchase by calculating nft prices and investment amount function _processPurchaseNFT( IERC20 token, uint256 price, uint256 normalizationFactor, uint32 round, uint256[] calldata nftAmounts, uint256[] memory nftPrices ) private returns (uint256, uint256) { uint256 value = 0; for (uint256 i = 0; i < nftPrices.length; ++i) { // (10**0 * 10**6 +10**10) -10**10 = 6 decimals value += (nftAmounts[i] * nftPrices[i] * (10 ** (normalizationFactor))) / price; } uint256 roundPrice = _getRoundPriceForToken(round, token); ClaimNFT memory amounts = ClaimNFT({ nftAmounts: nftAmounts, roundPrice: roundPrice }); claimNFT[msg.sender][round].push(amounts); return (value, roundPrice); } /// @notice Checks that address is blacklisted or not function _checkBlacklist(address which) private view { if (blacklistAddress[which]) { revert Blacklisted(); } } /// @notice Validates round, deadline and signature function _validatePurchaseWithEth( uint256 amount, uint32 round, uint256 deadline, string memory code, uint8 v, bytes32 r, bytes32 s ) private view { _checkValue(amount); _validatePurchase(round, deadline, ETH); _verifyCode(code, deadline, v, r, s); } /// @notice Validates round, deadline and signature function _validatePurchaseWithToken( IERC20 token, uint32 round, uint256 deadline, string memory code, uint256 referenceTokenPrice, uint256 normalizationFactor, uint8 v, bytes32 r, bytes32 s ) private view { _validatePurchase(round, deadline, token); _verifyCodeWithPrice( code, deadline, referenceTokenPrice, token, normalizationFactor, v, r, s ); } /// @notice Validates round, deadline and signature function _getRoundPriceForToken( uint32 round, IERC20 token ) private view returns (uint256) { uint256 customPrice = allowedTokens[round][token].customPrice; uint256 roundPrice = customPrice > 0 ? customPrice : rounds[round].price; return roundPrice; } /// @notice Calculates the dop amount function _calculateDop( uint256 investment, uint256 referenceTokenPrice, uint256 normalizationFactor, uint256 roundPrice ) private pure returns (uint256) { // toReturn= (10**11 * 10**10 +10**15) -10**18 = 18 decimals uint256 toReturn = (investment * referenceTokenPrice * (10 ** normalizationFactor)) / roundPrice; return toReturn; } function _validatePrice( IERC20 token, uint256 referenceTokenPrice, uint8 referenceNormalizationFactor ) private view returns (uint256, uint256) { TokenInfo memory tokenInfo = getLatestPrice(token); if (tokenInfo.latestPrice != 0) { if (referenceTokenPrice != 0 || referenceNormalizationFactor != 0) { revert CodeSyncIssue(); } } // If price feed isn't available,we fallback to the reference price if (tokenInfo.latestPrice == 0) { if (referenceTokenPrice == 0 || referenceNormalizationFactor == 0) { revert ZeroValue(); } tokenInfo.latestPrice = referenceTokenPrice; tokenInfo .normalizationFactorForToken = referenceNormalizationFactor; } return (tokenInfo.latestPrice, tokenInfo.normalizationFactorForToken); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); function getRoundData(uint80 _roundId) external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); function latestRoundData() external view returns ( uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../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. * * The initial owner is set to the address provided by the deployer. 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; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling 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 { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @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 or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * 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. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @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`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.20; /** * @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 } /** * @dev The signature derives the `address(0)`. */ error ECDSAInvalidSignature(); /** * @dev The signature has an invalid length. */ error ECDSAInvalidSignatureLength(uint256 length); /** * @dev The signature has an S value that is in the upper half order. */ error ECDSAInvalidSignatureS(bytes32 s); /** * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not * return address(0) without also returning an error description. Errors are documented using an enum (error type) * and a bytes32 providing additional information about the error. * * If no error is returned, then the address can be used for verification purposes. * * The `ecrecover` EVM precompile 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 {MessageHashUtils-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] */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { 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. /// @solidity memory-safe-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 { return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM precompile 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 {MessageHashUtils-toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); _throwError(error, errorArg); 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] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { unchecked { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); // We do not check for an overflow here since the shift operation results in 0 or 1. 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. */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError, bytes32) { // 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, s); } // 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, bytes32(0)); } return (signer, RecoverError.NoError, bytes32(0)); } /** * @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, bytes32 errorArg) = tryRecover(hash, v, r, s); _throwError(error, errorArg); return recovered; } /** * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. */ function _throwError(RecoverError error, bytes32 errorArg) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert ECDSAInvalidSignature(); } else if (error == RecoverError.InvalidSignatureLength) { revert ECDSAInvalidSignatureLength(uint256(errorArg)); } else if (error == RecoverError.InvalidSignatureS) { revert ECDSAInvalidSignatureS(errorArg); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol) pragma solidity ^0.8.20; import {Strings} from "../Strings.sol"; /** * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. * * The library provides methods for generating a hash of a message that conforms to the * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] * specifications. */ library MessageHashUtils { /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing a bytes32 `messageHash` with * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with * keccak256, although any bytes32 value can be safely used because the final digest will * be re-hashed. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20) } } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing an arbitrary `message` with * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) { return keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message)); } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x00` (data with intended validator). * * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended * `validator` address. Then hashing the result. * * See {ECDSA-recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked(hex"19_00", validator, data)); } /** * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). * * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with * `\x19\x01` and hashing the result. It corresponds to the hash signed by the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. * * See {ECDSA-recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, hex"19_01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) digest := keccak256(ptr, 0x42) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @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 towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.20; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant NOT_ENTERED = 1; uint256 private constant ENTERED = 2; uint256 private _status; /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); constructor() { _status = NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be NOT_ENTERED if (_status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail _status = ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol) pragma solidity ^0.8.20; import {Math} from "./math/Math.sol"; import {SignedMath} from "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; /** * @dev The `value` string doesn't fit in the specified `length`. */ error StringsInsufficientHexLength(uint256 value, uint256 length); /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toStringSigned(int256 value) internal pure returns (string memory) { return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { uint256 localValue = value; 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_DIGITS[localValue & 0xf]; localValue >>= 4; } if (localValue != 0) { revert StringsInsufficientHexLength(value, length); } return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal * representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @notice Thrown when updating an address with zero address error ZeroAddress(); /// @notice Thrown when updating with an array of no values error ZeroLengthArray(); /// @notice Thrown when updating with the same value as previously stored error IdenticalValue(); /// @notice Thrown when two array lengths does not match error ArrayLengthMismatch(); /// @dev The address of the Ethereum IERC20 constant ETH = IERC20(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IRounds} from "./IRounds.sol"; interface IPreSaleDop is IRounds { /// @notice Purchases Dop token with claim amount /// @param token The address of investment token /// @param tokenPrice The current price of token in 10 decimals /// @param referenceNormalizationFactor The value to handle decimals /// @param amount The investment amount /// @param minAmountDop The minimum amount of dop recipient will get /// @param recipient The address of the recipient /// @param round The round in which user will purchase function purchaseWithClaim( IERC20 token, uint256 tokenPrice, uint8 referenceNormalizationFactor, uint256 amount, uint256 minAmountDop, address recipient, uint32 round ) external payable; /// @notice The helper function which verifies signature, signed by signerWallet, reverts if invalidSignature function verifyPurchaseWithClaim( address recipient, uint32 round, uint256 deadline, uint256[] calldata tokenPrices, uint8[] calldata normalizationFactors, IERC20[] calldata tokens, uint256[] calldata amounts, uint8 v, bytes32 r, bytes32 s ) external; }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; interface IRounds { /// @notice Returns the round details of the round numberz function rounds( uint32 round ) external view returns (uint256 startTime, uint256 endTime, uint256 price); }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {TokensRegistry} from "./TokensRegistry.sol"; import {IRounds} from "./IRounds.sol"; import {ZeroAddress, ArrayLengthMismatch, ZeroLengthArray} from "./Common.sol"; /// @title Rounds contract /// @notice Implements the Round creation and updating of presale /// @dev The Rounds contract allows you to create a round, update a round abstract contract Rounds is IRounds, Ownable, TokensRegistry { /// @notice Thrown when round time is not started error RoundNotStarted(); /// @notice Thrown when round time is ended error RoundEnded(); /// @notice Thrown when Round is not created error IncorrectRound(); /// @notice Thrown when new round price is less than previous round price error PriceLessThanOldRound(); /// @notice Thrown when round start time is invalid error InvalidStartTime(); /// @notice Thrown when round end time is invalid error InvalidEndTime(); /// @notice Thrown when new price is invalid error PriceInvalid(); /// @notice Thrown when startTime is incorrect when updating round error IncorrectStartTime(); /// @notice Thrown when endTime is incorrect when updating round error IncorrectEndTime(); /// @notice Thrown when round price is greater than next round while updating error PriceGreaterThanNextRound(); /// @notice Thrown when Token is restricted in given round error TokenDisallowed(); /// @notice The round index of last round created uint32 internal immutable _startRound; /// @notice The count of rounds created uint32 internal _roundIndex; /// @notice mapping gives us access info of the token in a given round mapping(uint32 => mapping(IERC20 => AllowedToken)) public allowedTokens; /// @notice mapping gives Round Data of each round mapping(uint32 => RoundData) public rounds; /// @member access The access of the token /// @member customPrice The customPrice price in the round for the token struct AllowedToken { bool access; uint256 customPrice; } /// @member startTime The start time of round /// @member endTime The end time of round /// @member price The price in usd per DOP struct RoundData { uint256 startTime; uint256 endTime; uint256 price; } /// @dev Emitted when creating a new round event RoundCreated(uint32 indexed newRound, RoundData roundData); /// @dev Emitted when round is updated event RoundUpdated(uint32 indexed round, RoundData roundData); /// @dev Emitted when token access is updated event TokensAccessUpdated( uint32 indexed round, IERC20 indexed token, bool indexed access, uint256 customPrice ); /// @dev Constructor. /// @param lastRound The last round created constructor(uint32 lastRound) { _startRound = lastRound; _roundIndex = lastRound; } /// @notice Creates a new Round /// @param startTime The startTime of the round /// @param endTime The endTime of the round /// @param price The dopToken price in 18 decimals, because our calculations returns a value in 36 decimals and toget returning value in 18 decimals we divide by round price function createNewRound( uint256 startTime, uint256 endTime, uint256 price ) external onlyOwner { RoundData memory prevRoundData = rounds[_roundIndex]; uint32 newRound = ++_roundIndex; if (price < prevRoundData.price) { revert PriceLessThanOldRound(); } if (startTime < prevRoundData.endTime) { revert InvalidStartTime(); } _verifyRound(startTime, endTime, price); prevRoundData = RoundData({ startTime: startTime, endTime: endTime, price: price }); rounds[newRound] = prevRoundData; emit RoundCreated({newRound: newRound, roundData: prevRoundData}); } /// @notice Updates the access of tokens in a given round /// @param round The round in which you want to update /// @param tokens addresses of the tokens /// @param accesses The access for the tokens /// @param customPrices The customPrice prices if any for the tokens function updateAllowedTokens( uint32 round, IERC20[] calldata tokens, bool[] memory accesses, uint256[] memory customPrices ) external onlyOwner { if (tokens.length == 0) { revert ZeroLengthArray(); } if ( tokens.length != accesses.length || accesses.length != customPrices.length ) { revert ArrayLengthMismatch(); } mapping(IERC20 => AllowedToken) storage selectedRound = allowedTokens[ round ]; for (uint256 i = 0; i < tokens.length; ++i) { IERC20 token = tokens[i]; if (address(token) == address(0)) { revert ZeroAddress(); } AllowedToken memory allowedToken = AllowedToken({ access: accesses[i], customPrice: customPrices[i] }); selectedRound[token] = allowedToken; emit TokensAccessUpdated({ round: round, token: token, access: allowedToken.access, customPrice: allowedToken.customPrice }); } } /// @notice Updates round data /// @param round The Round that will be updated /// @param startTime The StartTime of the round /// @param endTime The EndTime of the round /// @param price The price of the round in 18 decimals function updateRound( uint32 round, uint256 startTime, uint256 endTime, uint256 price ) external onlyOwner { if (round <= _startRound || round > _roundIndex) { revert IncorrectRound(); } RoundData memory previousRound = rounds[round - 1]; RoundData memory nextRound = rounds[round + 1]; if (startTime < previousRound.endTime) { revert IncorrectStartTime(); } if (round != _roundIndex && endTime > nextRound.startTime) { revert IncorrectEndTime(); } if (price < previousRound.price) { revert PriceLessThanOldRound(); } if (round != _roundIndex && price > nextRound.price) { revert PriceGreaterThanNextRound(); } _verifyRound(startTime, endTime, price); rounds[round] = RoundData({ startTime: startTime, endTime: endTime, price: price }); emit RoundUpdated({round: round, roundData: rounds[round]}); } /// @notice Returns total rounds created /// @return The Round count function getRoundCount() external view returns (uint32) { return _roundIndex; } /// @notice Validates array length and values function _validateArrays( uint256 firstLength, uint256 secondLength ) internal pure { if (firstLength == 0) { revert ZeroLengthArray(); } if (firstLength != secondLength) { revert ArrayLengthMismatch(); } } /// @notice Checks round start and end time, reverts if Invalid function _verifyInRound(uint32 round) internal view { RoundData memory dataRound = rounds[round]; if (block.timestamp < dataRound.startTime) { revert RoundNotStarted(); } if (block.timestamp >= dataRound.endTime) { revert RoundEnded(); } } /// @notice Checks the validity of startTime, endTime and price function _verifyRound( uint256 startTime, uint256 endTime, uint256 price ) internal view { if (startTime < block.timestamp) { revert InvalidStartTime(); } if (endTime <= startTime) { revert InvalidEndTime(); } if (price == 0) { revert PriceInvalid(); } } }
// SPDX-License-Identifier: MIT pragma solidity 0.8.22; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; import {AggregatorV3Interface} from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {ZeroAddress, ArrayLengthMismatch, ZeroLengthArray, IdenticalValue} from "./Common.sol"; /// @title TokensRegistry contract /// @notice Implements the pricefeed of the tokens abstract contract TokensRegistry is Ownable { /// @notice The USDT normalization factor between DOP and USDT uint256 internal constant NORMALIZATION_FACTOR_DOP_USDT = 1e30; /// @notice Gives us onchain price oracle address of the token mapping(IERC20 => PriceFeedData) public tokenData; /// @dev Emitted when address of Chainlink priceFeed contract is added for the token event TokenDataAdded(IERC20 token, AggregatorV3Interface priceFeed); /// @member priceFeed The Chainlink priceFeed address /// @member normalizationFactorForToken The normalization factor to achieve return value of 18 decimals ,while calculating dop token purchases and always with different token decimals /// @member normalizationFactorForNFT The normalization factor is the value which helps us to convert decimals of USDT to investment token decimals and always with different token decimals struct PriceFeedData { AggregatorV3Interface priceFeed; uint8 normalizationFactorForToken; uint8 normalizationFactorForNFT; } /// @notice Of Chainlink price feed contracts /// @param tokens The addresses of the tokens /// @param priceFeedData Contains the priceFeed of the tokens and the normalization factor function setTokenPriceFeed( IERC20[] calldata tokens, PriceFeedData[] calldata priceFeedData ) external onlyOwner { if (tokens.length == 0) { revert ZeroLengthArray(); } if (tokens.length != priceFeedData.length) { revert ArrayLengthMismatch(); } for (uint256 i = 0; i < tokens.length; ++i) { PriceFeedData memory data = priceFeedData[i]; IERC20 token = tokens[i]; PriceFeedData memory currentPriceFeedData = tokenData[token]; if ( address(token) == address(0) || address(data.priceFeed) == address(0) ) { revert ZeroAddress(); } if ( currentPriceFeedData.priceFeed == data.priceFeed && currentPriceFeedData.normalizationFactorForToken == data.normalizationFactorForToken && currentPriceFeedData.normalizationFactorForNFT == data.normalizationFactorForNFT ) { revert IdenticalValue(); } emit TokenDataAdded({token: token, priceFeed: data.priceFeed}); tokenData[token] = data; } } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 1000000 }, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "libraries": {} }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"fundsWalletAddress","type":"address"},{"internalType":"address","name":"signerAddress","type":"address"},{"internalType":"address","name":"claimsContractAddress","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint32","name":"lastRound","type":"uint32"},{"internalType":"uint256[]","name":"nftPrices","type":"uint256[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ArrayLengthMismatch","type":"error"},{"inputs":[],"name":"Blacklisted","type":"error"},{"inputs":[],"name":"BuyNotEnable","type":"error"},{"inputs":[],"name":"CodeSyncIssue","type":"error"},{"inputs":[],"name":"DeadlineExpired","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[],"name":"IdenticalValue","type":"error"},{"inputs":[],"name":"IncorrectEndTime","type":"error"},{"inputs":[],"name":"IncorrectRound","type":"error"},{"inputs":[],"name":"IncorrectStartTime","type":"error"},{"inputs":[],"name":"InvalidEndTime","type":"error"},{"inputs":[],"name":"InvalidInvestment","type":"error"},{"inputs":[],"name":"InvalidSignature","type":"error"},{"inputs":[],"name":"InvalidStartTime","type":"error"},{"inputs":[],"name":"OnlyClaims","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"PriceGreaterThanNextRound","type":"error"},{"inputs":[],"name":"PriceInvalid","type":"error"},{"inputs":[],"name":"PriceLessThanOldRound","type":"error"},{"inputs":[],"name":"PriceNotFound","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[],"name":"RoundEnded","type":"error"},{"inputs":[],"name":"RoundNotStarted","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TokenDisallowed","type":"error"},{"inputs":[],"name":"UnexpectedPriceDifference","type":"error"},{"inputs":[],"name":"ZeroAddress","type":"error"},{"inputs":[],"name":"ZeroLengthArray","type":"error"},{"inputs":[],"name":"ZeroValue","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"which","type":"address"},{"indexed":false,"internalType":"bool","name":"accessNow","type":"bool"}],"name":"BlacklistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"oldAccess","type":"bool"},{"indexed":false,"internalType":"bool","name":"newAccess","type":"bool"}],"name":"BuyEnableUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"FundsWalletUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":true,"internalType":"uint256","name":"tokenPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dopPurchased","type":"uint256"}],"name":"InvestedWithClaimAmount","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"amountInvestedEth","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":true,"internalType":"uint256","name":"roundPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dopPurchased","type":"uint256"}],"name":"InvestedWithETH","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"amountInEth","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ethPrice","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"roundPrice","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"}],"name":"InvestedWithETHForNFT","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenPrice","type":"uint256"},{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"amountInvested","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"dopPurchased","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"}],"name":"InvestedWithToken","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenPrice","type":"uint256"},{"indexed":true,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"string","name":"code","type":"string"},{"indexed":false,"internalType":"uint256","name":"amountInvested","type":"uint256"},{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":false,"internalType":"uint256","name":"roundPrice","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"}],"name":"InvestedWithTokenForNFT","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":"uint256","name":"oldPrice","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newPrice","type":"uint256"}],"name":"PricingUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"newRound","type":"uint32"},{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"indexed":false,"internalType":"struct Rounds.RoundData","name":"roundData","type":"tuple"}],"name":"RoundCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"components":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"indexed":false,"internalType":"struct Rounds.RoundData","name":"roundData","type":"tuple"}],"name":"RoundUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldSigner","type":"address"},{"indexed":false,"internalType":"address","name":"newSigner","type":"address"}],"name":"SignerUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"}],"name":"TokenDataAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint32","name":"round","type":"uint32"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":true,"internalType":"bool","name":"access","type":"bool"},{"indexed":false,"internalType":"uint256","name":"customPrice","type":"uint256"}],"name":"TokensAccessUpdated","type":"event"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"contract IERC20","name":"","type":"address"}],"name":"allowedTokens","outputs":[{"internalType":"bool","name":"access","type":"bool"},{"internalType":"uint256","name":"customPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"blacklistAddress","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"buyEnable","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newFundsWallet","type":"address"}],"name":"changeFundsWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newSigner","type":"address"}],"name":"changeSigner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"claimNFT","outputs":[{"internalType":"uint256","name":"roundPrice","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint32","name":"","type":"uint32"}],"name":"claims","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"claimsContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"createNewRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"enabled","type":"bool"}],"name":"enableBuy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"fundsWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"}],"name":"getLatestPrice","outputs":[{"components":[{"internalType":"uint256","name":"latestPrice","type":"uint256"},{"internalType":"uint8","name":"normalizationFactorForToken","type":"uint8"},{"internalType":"uint8","name":"normalizationFactorForNFT","type":"uint8"}],"internalType":"struct PreSaleDop.TokenInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRoundCount","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"nftPricing","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"code","type":"string"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"purchaseNFTWithEth","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"referenceTokenPrice","type":"uint256"},{"internalType":"uint8","name":"referenceNormalizationFactor","type":"uint8"},{"internalType":"string","name":"code","type":"string"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256[]","name":"nftAmounts","type":"uint256[]"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"purchaseNFTWithToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"code","type":"string"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256","name":"minAmountDop","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"purchaseTokenWithEth","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint8","name":"referenceNormalizationFactor","type":"uint8"},{"internalType":"uint256","name":"referenceTokenPrice","type":"uint256"},{"internalType":"uint256","name":"investment","type":"uint256"},{"internalType":"uint256","name":"minAmountDop","type":"uint256"},{"internalType":"string","name":"code","type":"string"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"purchaseTokenWithToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"referenceTokenPrice","type":"uint256"},{"internalType":"uint8","name":"referenceNormalizationFactor","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minAmountDop","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"round","type":"uint32"}],"name":"purchaseWithClaim","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"rounds","outputs":[{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"components":[{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"normalizationFactorForToken","type":"uint8"},{"internalType":"uint8","name":"normalizationFactorForNFT","type":"uint8"}],"internalType":"struct TokensRegistry.PriceFeedData[]","name":"priceFeedData","type":"tuple[]"}],"name":"setTokenPriceFeed","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"signerWallet","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"name":"tokenData","outputs":[{"internalType":"contract AggregatorV3Interface","name":"priceFeed","type":"address"},{"internalType":"uint8","name":"normalizationFactorForToken","type":"uint8"},{"internalType":"uint8","name":"normalizationFactorForNFT","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"bool[]","name":"accesses","type":"bool[]"},{"internalType":"uint256[]","name":"customPrices","type":"uint256[]"}],"name":"updateAllowedTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"which","type":"address"},{"internalType":"bool","name":"access","type":"bool"}],"name":"updateBlackListedUser","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"newPrices","type":"uint256[]"}],"name":"updatePricing","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"price","type":"uint256"}],"name":"updateRound","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint32","name":"round","type":"uint32"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint256[]","name":"tokenPrices","type":"uint256[]"},{"internalType":"uint8[]","name":"normalizationFactors","type":"uint8[]"},{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"verifyPurchaseWithClaim","outputs":[],"stateMutability":"view","type":"function"}]
Contract Creation Code
604060a08152346200030c5762003e80803803806200001e8162000310565b92833981019060c0818303126200030c576200003a8162000336565b6020906200004a82840162000336565b906200005886850162000336565b94620000676060860162000336565b9560808601519263ffffffff8416948585036200030c5760a08801516001600160401b03988982116200030c57019584601f880112156200030c578651958987116200025a576005958760051b988a620000c3818c0162000310565b809a815201908b829b8201019283116200030c578b809101915b838310620002fb57506001600160a01b039d8e1692505081159050620002e457918c9593918c95935f549d8e988260018060a01b0319809b16175f55519e167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a360805263ffffffff19600254161760025560019a8b6005558460065493169081158015620002d9575b8015620002ce575b8015620002c6575b620002b7575060088054871690911781556001600160a81b0319909216911b610100600160a81b0316178917600655600780549093169116179055815115620002a6575f865b6200026e575b5050519283116200025a576801000000000000000083116200025a57600954836009558084106200022f575b509060095f52805f20905f5b8481106200021d578651613b3490816200034c823960805181611e070152f35b835183820155928101928501620001fd565b60095f528484845f2092830192015b8281106200024e575050620001f1565b5f81550185906200023e565b634e487b7160e01b5f52604160045260245ffd5b8251811015620002a0578481831b84010151156200028f57860186620001bf565b8751637c946ed760e01b8152600490fd5b620001c5565b8651630f59b9ff60e01b8152600490fd5b63d92e233d60e01b8152600490fd5b505f62000179565b508585161562000171565b508583161562000169565b8c51631e4fbdf760e01b81525f6004820152602490fd5b82518152918101918c9101620000dd565b5f80fd5b6040519190601f01601f191682016001600160401b038111838210176200025a57604052565b51906001600160a01b03821682036200030c5756fe604060808152600480361015610013575f80fd5b5f3560e01c80630a7e0c561461275e578063161c9d01146125b757806316345f18146125545780632194f3a21461250257806327824e53146122c257806327e45c2c146122465780632a253026146120b45780632d55e7df14611dae5780633d389faf14611d6d57806340c8e8921461194157806341e7e3411461188a57806364f0d35e146118355780636de7da78146117f3578063715018a61461175957806379a6d51f146116f25780637bc2d710146115065780637d6f0d5f146113ff5780637fcb0972146113ad5780638da5cb5b1461135c57806392f58881146112d257806395f002401461123c578063a77188f314611006578063aad2b72314610eb6578063aae0977114610cae578063cc73a94d14610952578063e09590d114610813578063e9f8958f1461079b578063f2fde38b146106ba578063f3290d7514610653578063f4648ae2146101e05763fc7a822d14610170575f80fd5b346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5735906009548210156101dc5760209160095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af01549051908152f35b5f80fd5b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5767ffffffffffffffff9080358281116101dc5761022b9036908301612c0a565b91610234612a01565b926044358281116101dc5761024c9036908501612a14565b90610255612bb2565b9260ff600654161561062b576102696130ea565b610293610274612cdb565b94610280865186613503565b60c4359060a43590846064358c346137b3565b61029b612dbe565b908151156106035781519660ff8a84015116965f9a5f9b5b88518d10156103135761030b6001916103058f6103008f918f908f8f908f836102e66102fa956102f4956102ed94612c96565b3592612cc7565b5190612d49565b9161326d565b90612d49565b61327b565b90612f87565b9c019b6102b3565b9063ffffffff8c98949596981697885f5260209560038752825f2073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5f5287526001835f2001548015155f146105ef57945b83519061036582612a45565b61037036898d612b03565b825288820190878252335f52600c8a52855f208d5f528a52855f2080546801000000000000000091828210156105c357906103b091600182018155612c50565b949094610598575190815192831161056c578211610540578a908454838655808410610518575b5001835f528a5f205f5b8381106105065750505050600190519101558334106104df57610403846130b9565b833403903482116104b35750927ff491b2ce492077dad65774a3004bf521f1a24c4355392c2f32161db477b2c306979592610499959261047e98958334036104a3575b506104698373ffffffffffffffffffffffffffffffffffffffff600854166133e1565b5191815198899860a08a5260a08a0190612fb5565b95880152860152606085015283820360808501523396612ff8565b0390a36001600555005b6104ad90336133e1565b8c610446565b6011907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b82517f26d748d6000000000000000000000000000000000000000000000000000000008152fd5b825182820155918c01916001016103e1565b855f5283835f2091820191015b81811061053257506103d7565b5f81558d9350600101610525565b6041857f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6041867f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b5f867f4e487b710000000000000000000000000000000000000000000000000000000082525260245ffd5b6041877f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b50895f528087526002835f20015494610359565b8689517f358e2ce7000000000000000000000000000000000000000000000000000000008152fd5b8588517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b82346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff6106a3612b6f565b165f52600b825260ff815f20541690519015158152f35b50346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576106f2612b6f565b906106fb613069565b73ffffffffffffffffffffffffffffffffffffffff80921692831561076c5750505f54827fffffffffffffffffffffffff00000000000000000000000000000000000000008216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b905f60249251917f1e4fbdf7000000000000000000000000000000000000000000000000000000008352820152fd5b82346101dc57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576020906107d5612b6f565b73ffffffffffffffffffffffffffffffffffffffff6107f2612a01565b91165f52600a835263ffffffff825f2091165f528252805f20549051908152f35b50346101dc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5761084a612b6f565b9160243591821515938484036101dc5773ffffffffffffffffffffffffffffffffffffffff1690811561092b5761087f613069565b815f52600b6020528460ff845f20541615151461090457507f6a12b3df6cba4203bd7fd06b816789f87de8c594299aed5717ae070fac781bac82610902958151908482526020820152a15f52600b6020525f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b005b82517f2620eb3a000000000000000000000000000000000000000000000000000000008152fd5b82517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b50346101dc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5767ffffffffffffffff9080358281116101dc576109a19036908301612a14565b602492602435958587116101dc57366023880112156101dc57868201359586116101dc5760609636602460608902830101116101dc576109df613069565b8315610c8757868403610c60575f5b8481106109f757005b87811015610c35578881028201897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82360301126101dc57835190610a3b82612a8e565b8881013573ffffffffffffffffffffffffffffffffffffffff80821682036101dc5786818592610a9f610a9a898f978f908852610a8e6064610a7f60448d01612bc2565b9b6020809b019c8d5201612bc2565b98878c01998a52612c96565b612ca6565b1692835f52600190818152835f2094845195610aba87612a8e565b5495848716815260ff60a0978483019282828b1c1684528260a8998201928a1c16825284158015610c2a575b610c015787905116878d5116149283610bf0575b5082610bdf575b5050610bb7578375ff000000000000000000000000000000000000000000938a999897969374ff0000000000000000000000000000000000000000938f7f05a55041f547bc02746ecf7b080f4a090ea42e2a9e0b0c0b151f939cd74d81829060019f9e997fffffffffffffffffffff000000000000000000000000000000000000000000009a511681519084825285820152a15f52528c5f209851169488549751901b169451901b1693161717179055016109ee565b8c8c517f2620eb3a000000000000000000000000000000000000000000000000000000008152fd5b819250511690885116145f80610b01565b518b5183169083161492505f610afa565b508f8f517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b50878d511615610ae6565b866032857f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b50517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b50517f0f59b9ff000000000000000000000000000000000000000000000000000000008152fd5b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57803567ffffffffffffffff81116101dc57610cf79036908301612c0a565b90610d00612a01565b610d08612bb2565b60ff6006541615610e8e5790610d3063ffffffff9260c4359060a435908760443586346137b3565b1691825f5260209160038352845f2073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5f5283526001855f2001548015155f14610e7a57945b610d72612dbe565b805115610e535786610300826102fa6102f460ff8a610d979751940151169234612d49565b916064358310610e2c5750907faa0444f72596a225170908d940d0111cb9af5ba71d3c4be91b6812e7dc5f88d69291335f52600a8552805f20865f528552805f20610de3838254612f87565b9055610e073473ffffffffffffffffffffffffffffffffffffffff600854166133e1565b610e1b815193606085526060850190612fb5565b9434908401528201528033930390a4005b90517fbde82093000000000000000000000000000000000000000000000000000000008152fd5b50517f358e2ce7000000000000000000000000000000000000000000000000000000008152fd5b50835f528083526002855f20015494610d6a565b8285517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b50346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57610eee612b6f565b9073ffffffffffffffffffffffffffffffffffffffff92838316918215610fdf57610f17613069565b600654948560081c16928314610fb857505173ffffffffffffffffffffffffffffffffffffffff918216815290821660208201527fffffffffffffffffffffff0000000000000000000000000000000000000000ff9174ffffffffffffffffffffffffffffffffffffffff00917f2d025324f0a785e8c12d0a0d91a9caa49df4ef20ff87e0df7213a1d4f3157beb90604090a160081b169116176006555f80f35b90517f2620eb3a000000000000000000000000000000000000000000000000000000008152fd5b90517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b50346101dc576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5761103f612b6f565b90611048612a01565b67ffffffffffffffff93906064358581116101dc5761106a9036908501612a14565b936084358781116101dc576110829036908301612a14565b93909160a4358981116101dc5761109c9036908301612a14565b93909960c4359081116101dc576110b69036908401612a14565b9690956110c1612ba2565b9873ffffffffffffffffffffffffffffffffffffffff94856007541633036112155750959493955199602094858c019c60601b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000168d5260e01b7fffffffff000000000000000000000000000000000000000000000000000000001660348c015260388b019061115092613035565b91905f5b8181106111ec575050506044358152810198925f905b8382106111c6575050505050611188610902966111b4938693613035565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283612aaa565b61012435926101043592519020613720565b90919293998a35908282168092036101dc5790815283019983019392916001019061116a565b909192848060019260ff611203889b999a9b612bc2565b16815296989796019401929101611154565b90517f049809b1000000000000000000000000000000000000000000000000000000008152fd5b82346101dc5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57611274612b6f565b9061127d612a01565b73ffffffffffffffffffffffffffffffffffffffff60443593165f52600c60205263ffffffff825f2091165f52602052805f209182548110156101dc576112c8600191602094612c50565b5001549051908152f35b82346101dc57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576113096129db565b6024359073ffffffffffffffffffffffffffffffffffffffff82168092036101dc5763ffffffff165f526003602052815f20905f52602052805f20600160ff825416910154825191151582526020820152f35b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff5f54169051908152f35b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff600754169051908152f35b50346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57611437612b6f565b73ffffffffffffffffffffffffffffffffffffffff918282169384156114e05761145f613069565b60085493841691858314610fb857505173ffffffffffffffffffffffffffffffffffffffff9182168152911660208201527fffffffffffffffffffffffff000000000000000000000000000000000000000091907fe22b566ac7db56412e2e041c88a7fd3151151ad6c6647e954f9bdc054bcb780e90604090a11617600855005b517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b5090346101dc576101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57611540612b6f565b6024359160ff8316928381036101dc5760643560443560a43567ffffffffffffffff81116101dc576115759036908901612c0a565b9261157e6129ee565b96610104359060ff821682036101dc5760ff60065416156116ca576115f3926115c9898b6103009897956115e3956115b46130ea565b878c6101443595610124359560e43591613564565b6115d2856130b9565b6115dc898b61317a565b92896131d3565b6102fa6102f48387989498612d49565b60843581106116a257907fc550025edff4b03b80afe475299833a5a79a49cf86b746a3e525030355b9a622939291335f52600a60205263ffffffff865f20981697885f52602052855f20611648828254612f87565b905573ffffffffffffffffffffffffffffffffffffffff91611670818460085416338b6132b2565b61168b87519586958652608060208701526080860190612fb5565b968401526060830152339516930390a46001600555005b8785517fbde82093000000000000000000000000000000000000000000000000000000008152fd5b8987517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b5090346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760609163ffffffff6117336129db565b165f52602052805f20805491600260018301549201549181519384526020840152820152f35b346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5761178f613069565b5f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209063ffffffff600254169051908152f35b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff60065460081c169051908152f35b5090346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc578135908115158092036101dc576118d0613069565b6006549260ff84161515908382146109045750917fe557486689c0bf71dde8cb27e7e87ed23badcf92ea724f4a0368676720d416f6827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009460ff9451908152836020820152a1169116176006555f80f35b5090346101dc576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5761197b612b6f565b9060243590611988612b92565b67ffffffffffffffff906064358281116101dc576119a99036908801612c0a565b956084359563ffffffff8716908188036101dc5760a4358581116101dc576119d49036908501612a14565b9390996119df612ba2565b60ff6006541615611d45576119f26130ea565b611a258a8c60ff611a01612cdb565b9a611a0d8c518c613503565b169384610124359387610104359460c435908c613564565b611a2e84612f23565b998a51611d05575b8a5115611cbb575b505088519760ff818b015116975f9a5f9b5b89518d1015611a8a57611a828f916103058f8f908f8f8f6102fa916102ed856102e6610300976102f49560019e612c96565b9c019b611a50565b86979892918f978f90611a9c9161317a565b92855190611aa982612a45565b611ab436878c612b03565b825260209889830191868352335f52600c8b528d895f20905f528b52885f2091825490680100000000000000009182811015611c6457611afc90600195600182018155612c50565b969096611c905751918251938411611c64578311611c3857508b908554838755808410611c0e575b50908e989796959493929101845f528c5f205f5b838110611be7575050505050611bdd9694927f95ad5726302123e85b4b1858ca3676b75b71bb88ddab4c7b181b3787122133889a99989694926001611bc39351910155611b84836130b9565b611bab8373ffffffffffffffffffffffffffffffffffffffff9687600854169033906132b2565b519760a087519a8b9a8b528a015260a0890190612fb5565b948701526060860152848303608086015233981696612ff8565b0390a46001600555005b8495969798999a508e8394959293519401938184015501908f999897969594939291611b38565b865f528484845f2092830192015b828110611c2a575050611b24565b5f81558f9450869101611c1c565b6041907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6041827f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b5f827f4e487b710000000000000000000000000000000000000000000000000000000082525260245ffd5b80158015611cfd575b611cd5578a52888a01525f80611a3e565b838a517f7c946ed7000000000000000000000000000000000000000000000000000000008152fd5b508115611cc4565b8015801590611d3c575b15611a3657838a517ff443cb16000000000000000000000000000000000000000000000000000000008152fd5b50811515611d0f565b8289517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209060ff6006541690519015158152f35b5090346101dc5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57611de76129db565b906024356044359260643591611dfb613069565b63ffffffff80921695827f00000000000000000000000000000000000000000000000000000000000000001687118015906120a7575b612080577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87018381116120545783165f52602092818452855f208651611e7781612a8e565b815481528760026001840154938884019485520154910190815260018a018381116120285783165f52838652875f20926002895194611eb586612a8e565b8054865260018101548987015201549289850193845251861061200057600254168a141580938194611ff5575b50611fcd57518610611fa55781611f9a575b50611f735792600292828795611f2f847fcf17268674ed4ffa34a117862c7b380287afb4c202e842a5f391b92219c0e02a9a60609a97613470565b845191611f3b83612a8e565b8252828201968752848201938452895f528252835f2090519586825551928360018301555194859101558251948552840152820152a2005b84517fecdcda19000000000000000000000000000000000000000000000000000000008152fd5b90505184115f611ef4565b8287517f434c92c9000000000000000000000000000000000000000000000000000000008152fd5b8388517f649bb635000000000000000000000000000000000000000000000000000000008152fd5b90505189115f611ee2565b8489517f442db2a5000000000000000000000000000000000000000000000000000000008152fd5b6011857f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6011827f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b84517f82a99657000000000000000000000000000000000000000000000000000000008152fd5b5082600254168711611e31565b5090346101dc5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc578135906044356024356120f6613069565b6002549463ffffffff9586811696875f5282602052855f209186519161211b83612a8e565b835483528760026001860154956020860196875201549301928352808a146120285760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009a0116988991161760025551841061221e575185106121f757918185936121ac83606097957f6877931d901fa420de6b1b5c5b565c4422ab189963a871c828e7b992220e95e399613470565b8351946121b886612a8e565b855260208501918252838501928352875f52602052825f20935193848155815160018201556002835191015582519384525160208401525190820152a2005b83517fb290253c000000000000000000000000000000000000000000000000000000008152fd5b5083517f434c92c9000000000000000000000000000000000000000000000000000000008152fd5b82346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760609073ffffffffffffffffffffffffffffffffffffffff9081612298612b6f565b165f52600160205260ff815f205482519381168452818160a01c16602085015260a81c1690820152f35b509060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576122f6612b6f565b906122ff612b92565b926064359060a4359273ffffffffffffffffffffffffffffffffffffffff93848116948582036101dc576123316129ee565b9160ff60065416156124d9576123456130ea565b816007541633036124b05761235990613125565b63ffffffff821696875f526003602052835f209282821693845f5260205260ff855f20541615612488576102fa996103006102f46123a761239d866123b19661317a565b93602435876131d3565b91819e918c612d49565b9460843586106124615750918593917fc79c5d6f2d27aee6474296eafbd08c97d8991c729c7b986e7bd076931c92fcb89660609694895f52600a602052845f208b5f52602052845f20612405878254612f87565b905573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8403612448575050612433906008541634906133e1565b81519384526020840152820152a46001600555005b8261245c93600754169060085416916132b2565b612433565b84517fbde82093000000000000000000000000000000000000000000000000000000008152fd5b8585517f49a8defd000000000000000000000000000000000000000000000000000000008152fd5b505050517f049809b1000000000000000000000000000000000000000000000000000000008152fd5b505050517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff600854169051908152f35b82346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57606090612597612592612b6f565b612f23565b9060ff818051938051855282602082015116602086015201511690820152f35b50346101dc5760209160207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5767ffffffffffffffff82358181116101dc576126099036908501612b51565b612611613069565b612619612cdb565b918151835103612735575f5b825181101561268357807f335f5afc83fe8c5a011a96dc39bcce9fb9d46fb5986502f7040e76e28b0361238661265d60019487612cc7565b51612667816130b9565b6126718489612cc7565b519082519182528b820152a101612625565b508591858151928311611c3857680100000000000000008311611c385750600954826009558083106126f5575b5060200160095f525f5b8281106126c357005b81517f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af820155908301906001016126ba565b827f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af91820191015b81811061272a57506126b0565b5f815560010161271d565b505050517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b5090346101dc5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576127976129db565b67ffffffffffffffff926024358481116101dc576127b89036908301612a14565b929093604435918683116101dc57366023840112156101dc5782840135966127df88612aeb565b936127ec84519586612aaa565b88855260209860248a87019160051b830101913683116101dc57602401905b8282106129c3575050506064359081116101dc5761282c9036908601612b51565b90612835613069565b851561299b5783518087149081159161298f575b506129675763ffffffff1692835f5260038852825f20955f5b81811061286b57005b73ffffffffffffffffffffffffffffffffffffffff61288e610a9a83858d612c96565b16801561293f579088600192887fc1a8d3b5c8695d3120c5ea6e6afca4fd990747cccf9c4513cbc02baf1c32e40d8e866128c8878b612cc7565b5115156128d5888d612cc7565b51968d51916128e383612a45565b8252838201978852865f5283528c5f20905115159661292d88839060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b5191829101558b51908152a401612862565b8786517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b8483517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b9050825114155f612849565b8483517f0f59b9ff000000000000000000000000000000000000000000000000000000008152fd5b813580151581036101dc578152908a01908a0161280b565b6004359063ffffffff821682036101dc57565b60c4359063ffffffff821682036101dc57565b6024359063ffffffff821682036101dc57565b9181601f840112156101dc5782359167ffffffffffffffff83116101dc576020808501948460051b0101116101dc57565b6040810190811067ffffffffffffffff821117612a6157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6060810190811067ffffffffffffffff821117612a6157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612a6157604052565b67ffffffffffffffff8111612a615760051b60200190565b9291612b0e82612aeb565b91612b1c6040519384612aaa565b829481845260208094019160051b81019283116101dc57905b828210612b425750505050565b81358152908301908301612b35565b9080601f830112156101dc57816020612b6c93359101612b03565b90565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101dc57565b6044359060ff821682036101dc57565b60e4359060ff821682036101dc57565b6084359060ff821682036101dc57565b359060ff821682036101dc57565b67ffffffffffffffff8111612a6157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b81601f820112156101dc57803590612c2182612bd0565b92612c2f6040519485612aaa565b828452602083830101116101dc57815f926020809301838601378301015290565b8054821015612c69575f5260205f209060011b01905f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190811015612c695760051b0190565b3573ffffffffffffffffffffffffffffffffffffffff811681036101dc5790565b8051821015612c695760209160051b010190565b6040519060095480835282602091602082019060095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af935f905b828210612d2f57505050612d2d92500383612aaa565b565b855484526001958601958895509381019390910190612d17565b81810292918115918404141715612d5c57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b60405190612d9682612a8e565b5f6040838281528260208201520152565b519069ffffffffffffffffffff821682036101dc57565b612dc6612d89565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5f52600160205260408051907fde3e59ea0eeb6a65f16dd826b7bdfb53723c09ad80c81d458bbf2e138d918523612e1283612a8e565b549073ffffffffffffffffffffffffffffffffffffffff8083168452602084019060ff8460a01c16825260ff8386019460a81c168452612e50612d89565b945116938415612f1b575060a06004948351958680927ffeaf968c0000000000000000000000000000000000000000000000000000000082525afa938415612f11575f94612ebd575b5060ff8091511692511691815193612eb085612a8e565b8452602084015282015290565b90935060a0813d60a011612f09575b81612ed960a09383612aaa565b810103126101dc5760ff81612eee8293612da7565b50612f00608060208301519201612da7565b50949150612e99565b3d9150612ecc565b82513d5f823e3d90fd5b935050505090565b612f2b612d89565b5073ffffffffffffffffffffffffffffffffffffffff8091165f526001602052604090815f2091805192612f5e84612a8e565b54918083168452602084019060ff8460a01c16825260ff8386019460a81c168452612e50612d89565b91908201809211612d5c57565b5f5b838110612fa55750505f910152565b8181015183820152602001612f96565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612ff181518092818752878088019101612f94565b0116010190565b90918281527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116101dc5760209260051b809284830137010190565b91907f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101dc5760051b809282370190565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361308957565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b156130c057565b60046040517f7c946ed7000000000000000000000000000000000000000000000000000000008152fd5b6002600554146130fb576002600555565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff165f52600b60205260ff60405f20541661315057565b60046040517f09550c77000000000000000000000000000000000000000000000000000000008152fd5b63ffffffff1690815f52600360205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052600160405f200154908115155f146131bf575090565b90505f526004602052600260405f20015490565b6131df90939293612f23565b928351613228575b8351156131fe575b505060ff602083519301511690565b8115801561321d575b6130c05760ff9184521660208301525f806131ef565b5060ff811615613207565b8115801590613261575b156131e75760046040517ff443cb16000000000000000000000000000000000000000000000000000000008152fd5b5060ff81161515613232565b604d8111612d5c57600a0a90565b8115613285570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b906040519360208501917f23b872dd00000000000000000000000000000000000000000000000000000000835273ffffffffffffffffffffffffffffffffffffffff9485809216602488015216604486015260648501526064845260a084019084821067ffffffffffffffff831117612a6157613347935f9384936040521694519082865af16133406133b2565b9083613a88565b805190811515918261338e575b505061335d5750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b81925090602091810103126101dc57602001518015908115036101dc575f80613354565b3d156133dc573d906133c382612bd0565b916133d16040519384612aaa565b82523d5f602084013e565b606090565b814710613440575f80809373ffffffffffffffffffffffffffffffffffffffff8294165af161340e6133b2565b501561341657565b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b60246040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152fd5b904282106134d95711156134af571561348557565b60046040517f2013535a000000000000000000000000000000000000000000000000000000008152fd5b60046040517f38af65f7000000000000000000000000000000000000000000000000000000008152fd5b60046040517fb290253c000000000000000000000000000000000000000000000000000000008152fd5b801561353a570361351057565b60046040517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b60046040517f0f59b9ff000000000000000000000000000000000000000000000000000000008152fd5b93909897969594989291928342116136f65763ffffffff9061358533613125565b1698895f526020946003865260409a8b5f2073ffffffffffffffffffffffffffffffffffffffff83165f52875260ff8c5f205416156136cd575f52600486528a5f208b516135d281612a8e565b8154908181528d60026001850154948b8401958652015491015242106136a4575142101561367b579161367393917fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060a894612d2d9c9d5197889561364a8b88019b3360601b8d52825192839160348b019101612f94565b8601946034860152605485015260601b1660748301526088820152036088810184520182612aaa565b519020613720565b60048b517f6b5fd31b000000000000000000000000000000000000000000000000000000008152fd5b60048c517f8e9c6e1c000000000000000000000000000000000000000000000000000000008152fd5b60048c517f49a8defd000000000000000000000000000000000000000000000000000000008152fd5b60046040517f1ab7da6b000000000000000000000000000000000000000000000000000000008152fd5b9261377890613781929373ffffffffffffffffffffffffffffffffffffffff948560065460081c16967f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f52601c52603c5f20613909565b909291926139a3565b160361378957565b60046040517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b6137c390979695949392976130b9565b8642116136f65763ffffffff906137d933613125565b1695865f5260209160038352604097885f2073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5f52845260ff895f205416156138e0575f5260048352875f20885161382481612a8e565b8154908181528a6002600185015494888401958652015491015242106138b7575142101561388e57605461367391612d2d989951938491613878878401973360601b89528251928391603487019101612f94565b8201906034820152036034810184520182612aaa565b600488517f6b5fd31b000000000000000000000000000000000000000000000000000000008152fd5b600489517f8e9c6e1c000000000000000000000000000000000000000000000000000000008152fd5b600489517f49a8defd000000000000000000000000000000000000000000000000000000008152fd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411613998579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa1561398d575f5173ffffffffffffffffffffffffffffffffffffffff81161561398357905f905f90565b505f906001905f90565b6040513d5f823e3d90fd5b5050505f9160039190565b6004811015613a5b57806139b5575050565b600181036139e75760046040517ff645eedf000000000000000000000000000000000000000000000000000000008152fd5b60028103613a2057602482604051907ffce698f70000000000000000000000000000000000000000000000000000000082526004820152fd5b600314613a2a5750565b602490604051907fd78bce0c0000000000000000000000000000000000000000000000000000000082526004820152fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b90613a9d575080511561341657805190602001fd5b81511580613af5575b613aae575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15613aa656fea2646970667358221220e6b863aa97cda840206a914f8d8d9c3d9aaa9e024a29d341af10f5a24b42cc5464736f6c63430008160033000000000000000000000000a22bac86e8f721875c6be26c1fc0b021442f7279000000000000000000000000ec38df358d2cb3557cb6cb9ebdb43a35891f48260000000000000000000000002cb197409ae65b344a611e2ab99a0e864ef28d4c0000000000000000000000009d0cf00f85015567084e79a76a65d6f5e912b8d2000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000001dcd6500000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000012a05f20000000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000006fc23ac000000000000000000000000000000000000000000000000000000001176592e00
Deployed Bytecode
0x604060808152600480361015610013575f80fd5b5f3560e01c80630a7e0c561461275e578063161c9d01146125b757806316345f18146125545780632194f3a21461250257806327824e53146122c257806327e45c2c146122465780632a253026146120b45780632d55e7df14611dae5780633d389faf14611d6d57806340c8e8921461194157806341e7e3411461188a57806364f0d35e146118355780636de7da78146117f3578063715018a61461175957806379a6d51f146116f25780637bc2d710146115065780637d6f0d5f146113ff5780637fcb0972146113ad5780638da5cb5b1461135c57806392f58881146112d257806395f002401461123c578063a77188f314611006578063aad2b72314610eb6578063aae0977114610cae578063cc73a94d14610952578063e09590d114610813578063e9f8958f1461079b578063f2fde38b146106ba578063f3290d7514610653578063f4648ae2146101e05763fc7a822d14610170575f80fd5b346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5735906009548210156101dc5760209160095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af01549051908152f35b5f80fd5b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5767ffffffffffffffff9080358281116101dc5761022b9036908301612c0a565b91610234612a01565b926044358281116101dc5761024c9036908501612a14565b90610255612bb2565b9260ff600654161561062b576102696130ea565b610293610274612cdb565b94610280865186613503565b60c4359060a43590846064358c346137b3565b61029b612dbe565b908151156106035781519660ff8a84015116965f9a5f9b5b88518d10156103135761030b6001916103058f6103008f918f908f8f908f836102e66102fa956102f4956102ed94612c96565b3592612cc7565b5190612d49565b9161326d565b90612d49565b61327b565b90612f87565b9c019b6102b3565b9063ffffffff8c98949596981697885f5260209560038752825f2073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5f5287526001835f2001548015155f146105ef57945b83519061036582612a45565b61037036898d612b03565b825288820190878252335f52600c8a52855f208d5f528a52855f2080546801000000000000000091828210156105c357906103b091600182018155612c50565b949094610598575190815192831161056c578211610540578a908454838655808410610518575b5001835f528a5f205f5b8381106105065750505050600190519101558334106104df57610403846130b9565b833403903482116104b35750927ff491b2ce492077dad65774a3004bf521f1a24c4355392c2f32161db477b2c306979592610499959261047e98958334036104a3575b506104698373ffffffffffffffffffffffffffffffffffffffff600854166133e1565b5191815198899860a08a5260a08a0190612fb5565b95880152860152606085015283820360808501523396612ff8565b0390a36001600555005b6104ad90336133e1565b8c610446565b6011907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b82517f26d748d6000000000000000000000000000000000000000000000000000000008152fd5b825182820155918c01916001016103e1565b855f5283835f2091820191015b81811061053257506103d7565b5f81558d9350600101610525565b6041857f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6041867f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b5f867f4e487b710000000000000000000000000000000000000000000000000000000082525260245ffd5b6041877f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b50895f528087526002835f20015494610359565b8689517f358e2ce7000000000000000000000000000000000000000000000000000000008152fd5b8588517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b82346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff6106a3612b6f565b165f52600b825260ff815f20541690519015158152f35b50346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576106f2612b6f565b906106fb613069565b73ffffffffffffffffffffffffffffffffffffffff80921692831561076c5750505f54827fffffffffffffffffffffffff00000000000000000000000000000000000000008216175f55167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b905f60249251917f1e4fbdf7000000000000000000000000000000000000000000000000000000008352820152fd5b82346101dc57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576020906107d5612b6f565b73ffffffffffffffffffffffffffffffffffffffff6107f2612a01565b91165f52600a835263ffffffff825f2091165f528252805f20549051908152f35b50346101dc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5761084a612b6f565b9160243591821515938484036101dc5773ffffffffffffffffffffffffffffffffffffffff1690811561092b5761087f613069565b815f52600b6020528460ff845f20541615151461090457507f6a12b3df6cba4203bd7fd06b816789f87de8c594299aed5717ae070fac781bac82610902958151908482526020820152a15f52600b6020525f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b005b82517f2620eb3a000000000000000000000000000000000000000000000000000000008152fd5b82517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b50346101dc57817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5767ffffffffffffffff9080358281116101dc576109a19036908301612a14565b602492602435958587116101dc57366023880112156101dc57868201359586116101dc5760609636602460608902830101116101dc576109df613069565b8315610c8757868403610c60575f5b8481106109f757005b87811015610c35578881028201897fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffdc82360301126101dc57835190610a3b82612a8e565b8881013573ffffffffffffffffffffffffffffffffffffffff80821682036101dc5786818592610a9f610a9a898f978f908852610a8e6064610a7f60448d01612bc2565b9b6020809b019c8d5201612bc2565b98878c01998a52612c96565b612ca6565b1692835f52600190818152835f2094845195610aba87612a8e565b5495848716815260ff60a0978483019282828b1c1684528260a8998201928a1c16825284158015610c2a575b610c015787905116878d5116149283610bf0575b5082610bdf575b5050610bb7578375ff000000000000000000000000000000000000000000938a999897969374ff0000000000000000000000000000000000000000938f7f05a55041f547bc02746ecf7b080f4a090ea42e2a9e0b0c0b151f939cd74d81829060019f9e997fffffffffffffffffffff000000000000000000000000000000000000000000009a511681519084825285820152a15f52528c5f209851169488549751901b169451901b1693161717179055016109ee565b8c8c517f2620eb3a000000000000000000000000000000000000000000000000000000008152fd5b819250511690885116145f80610b01565b518b5183169083161492505f610afa565b508f8f517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b50878d511615610ae6565b866032857f4e487b71000000000000000000000000000000000000000000000000000000005f52525ffd5b50517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b50517f0f59b9ff000000000000000000000000000000000000000000000000000000008152fd5b5060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57803567ffffffffffffffff81116101dc57610cf79036908301612c0a565b90610d00612a01565b610d08612bb2565b60ff6006541615610e8e5790610d3063ffffffff9260c4359060a435908760443586346137b3565b1691825f5260209160038352845f2073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5f5283526001855f2001548015155f14610e7a57945b610d72612dbe565b805115610e535786610300826102fa6102f460ff8a610d979751940151169234612d49565b916064358310610e2c5750907faa0444f72596a225170908d940d0111cb9af5ba71d3c4be91b6812e7dc5f88d69291335f52600a8552805f20865f528552805f20610de3838254612f87565b9055610e073473ffffffffffffffffffffffffffffffffffffffff600854166133e1565b610e1b815193606085526060850190612fb5565b9434908401528201528033930390a4005b90517fbde82093000000000000000000000000000000000000000000000000000000008152fd5b50517f358e2ce7000000000000000000000000000000000000000000000000000000008152fd5b50835f528083526002855f20015494610d6a565b8285517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b50346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57610eee612b6f565b9073ffffffffffffffffffffffffffffffffffffffff92838316918215610fdf57610f17613069565b600654948560081c16928314610fb857505173ffffffffffffffffffffffffffffffffffffffff918216815290821660208201527fffffffffffffffffffffff0000000000000000000000000000000000000000ff9174ffffffffffffffffffffffffffffffffffffffff00917f2d025324f0a785e8c12d0a0d91a9caa49df4ef20ff87e0df7213a1d4f3157beb90604090a160081b169116176006555f80f35b90517f2620eb3a000000000000000000000000000000000000000000000000000000008152fd5b90517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b50346101dc576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5761103f612b6f565b90611048612a01565b67ffffffffffffffff93906064358581116101dc5761106a9036908501612a14565b936084358781116101dc576110829036908301612a14565b93909160a4358981116101dc5761109c9036908301612a14565b93909960c4359081116101dc576110b69036908401612a14565b9690956110c1612ba2565b9873ffffffffffffffffffffffffffffffffffffffff94856007541633036112155750959493955199602094858c019c60601b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000168d5260e01b7fffffffff000000000000000000000000000000000000000000000000000000001660348c015260388b019061115092613035565b91905f5b8181106111ec575050506044358152810198925f905b8382106111c6575050505050611188610902966111b4938693613035565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101845283612aaa565b61012435926101043592519020613720565b90919293998a35908282168092036101dc5790815283019983019392916001019061116a565b909192848060019260ff611203889b999a9b612bc2565b16815296989796019401929101611154565b90517f049809b1000000000000000000000000000000000000000000000000000000008152fd5b82346101dc5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57611274612b6f565b9061127d612a01565b73ffffffffffffffffffffffffffffffffffffffff60443593165f52600c60205263ffffffff825f2091165f52602052805f209182548110156101dc576112c8600191602094612c50565b5001549051908152f35b82346101dc57807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576113096129db565b6024359073ffffffffffffffffffffffffffffffffffffffff82168092036101dc5763ffffffff165f526003602052815f20905f52602052805f20600160ff825416910154825191151582526020820152f35b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff5f54169051908152f35b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff600754169051908152f35b50346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57611437612b6f565b73ffffffffffffffffffffffffffffffffffffffff918282169384156114e05761145f613069565b60085493841691858314610fb857505173ffffffffffffffffffffffffffffffffffffffff9182168152911660208201527fffffffffffffffffffffffff000000000000000000000000000000000000000091907fe22b566ac7db56412e2e041c88a7fd3151151ad6c6647e954f9bdc054bcb780e90604090a11617600855005b517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b5090346101dc576101607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57611540612b6f565b6024359160ff8316928381036101dc5760643560443560a43567ffffffffffffffff81116101dc576115759036908901612c0a565b9261157e6129ee565b96610104359060ff821682036101dc5760ff60065416156116ca576115f3926115c9898b6103009897956115e3956115b46130ea565b878c6101443595610124359560e43591613564565b6115d2856130b9565b6115dc898b61317a565b92896131d3565b6102fa6102f48387989498612d49565b60843581106116a257907fc550025edff4b03b80afe475299833a5a79a49cf86b746a3e525030355b9a622939291335f52600a60205263ffffffff865f20981697885f52602052855f20611648828254612f87565b905573ffffffffffffffffffffffffffffffffffffffff91611670818460085416338b6132b2565b61168b87519586958652608060208701526080860190612fb5565b968401526060830152339516930390a46001600555005b8785517fbde82093000000000000000000000000000000000000000000000000000000008152fd5b8987517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b5090346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760609163ffffffff6117336129db565b165f52602052805f20805491600260018301549201549181519384526020840152820152f35b346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5761178f613069565b5f73ffffffffffffffffffffffffffffffffffffffff81547fffffffffffffffffffffffff000000000000000000000000000000000000000081168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a3005b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209063ffffffff600254169051908152f35b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff60065460081c169051908152f35b5090346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc578135908115158092036101dc576118d0613069565b6006549260ff84161515908382146109045750917fe557486689c0bf71dde8cb27e7e87ed23badcf92ea724f4a0368676720d416f6827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff009460ff9451908152836020820152a1169116176006555f80f35b5090346101dc576101407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5761197b612b6f565b9060243590611988612b92565b67ffffffffffffffff906064358281116101dc576119a99036908801612c0a565b956084359563ffffffff8716908188036101dc5760a4358581116101dc576119d49036908501612a14565b9390996119df612ba2565b60ff6006541615611d45576119f26130ea565b611a258a8c60ff611a01612cdb565b9a611a0d8c518c613503565b169384610124359387610104359460c435908c613564565b611a2e84612f23565b998a51611d05575b8a5115611cbb575b505088519760ff818b015116975f9a5f9b5b89518d1015611a8a57611a828f916103058f8f908f8f8f6102fa916102ed856102e6610300976102f49560019e612c96565b9c019b611a50565b86979892918f978f90611a9c9161317a565b92855190611aa982612a45565b611ab436878c612b03565b825260209889830191868352335f52600c8b528d895f20905f528b52885f2091825490680100000000000000009182811015611c6457611afc90600195600182018155612c50565b969096611c905751918251938411611c64578311611c3857508b908554838755808410611c0e575b50908e989796959493929101845f528c5f205f5b838110611be7575050505050611bdd9694927f95ad5726302123e85b4b1858ca3676b75b71bb88ddab4c7b181b3787122133889a99989694926001611bc39351910155611b84836130b9565b611bab8373ffffffffffffffffffffffffffffffffffffffff9687600854169033906132b2565b519760a087519a8b9a8b528a015260a0890190612fb5565b948701526060860152848303608086015233981696612ff8565b0390a46001600555005b8495969798999a508e8394959293519401938184015501908f999897969594939291611b38565b865f528484845f2092830192015b828110611c2a575050611b24565b5f81558f9450869101611c1c565b6041907f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6041827f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b5f827f4e487b710000000000000000000000000000000000000000000000000000000082525260245ffd5b80158015611cfd575b611cd5578a52888a01525f80611a3e565b838a517f7c946ed7000000000000000000000000000000000000000000000000000000008152fd5b508115611cc4565b8015801590611d3c575b15611a3657838a517ff443cb16000000000000000000000000000000000000000000000000000000008152fd5b50811515611d0f565b8289517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209060ff6006541690519015158152f35b5090346101dc5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57611de76129db565b906024356044359260643591611dfb613069565b63ffffffff80921695827f00000000000000000000000000000000000000000000000000000000000000061687118015906120a7575b612080577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff87018381116120545783165f52602092818452855f208651611e7781612a8e565b815481528760026001840154938884019485520154910190815260018a018381116120285783165f52838652875f20926002895194611eb586612a8e565b8054865260018101548987015201549289850193845251861061200057600254168a141580938194611ff5575b50611fcd57518610611fa55781611f9a575b50611f735792600292828795611f2f847fcf17268674ed4ffa34a117862c7b380287afb4c202e842a5f391b92219c0e02a9a60609a97613470565b845191611f3b83612a8e565b8252828201968752848201938452895f528252835f2090519586825551928360018301555194859101558251948552840152820152a2005b84517fecdcda19000000000000000000000000000000000000000000000000000000008152fd5b90505184115f611ef4565b8287517f434c92c9000000000000000000000000000000000000000000000000000000008152fd5b8388517f649bb635000000000000000000000000000000000000000000000000000000008152fd5b90505189115f611ee2565b8489517f442db2a5000000000000000000000000000000000000000000000000000000008152fd5b6011857f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b6011827f4e487b71000000000000000000000000000000000000000000000000000000005f525260245ffd5b84517f82a99657000000000000000000000000000000000000000000000000000000008152fd5b5082600254168711611e31565b5090346101dc5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc578135906044356024356120f6613069565b6002549463ffffffff9586811696875f5282602052855f209186519161211b83612a8e565b835483528760026001860154956020860196875201549301928352808a146120285760017fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000009a0116988991161760025551841061221e575185106121f757918185936121ac83606097957f6877931d901fa420de6b1b5c5b565c4422ab189963a871c828e7b992220e95e399613470565b8351946121b886612a8e565b855260208501918252838501928352875f52602052825f20935193848155815160018201556002835191015582519384525160208401525190820152a2005b83517fb290253c000000000000000000000000000000000000000000000000000000008152fd5b5083517f434c92c9000000000000000000000000000000000000000000000000000000008152fd5b82346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760609073ffffffffffffffffffffffffffffffffffffffff9081612298612b6f565b165f52600160205260ff815f205482519381168452818160a01c16602085015260a81c1690820152f35b509060e07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576122f6612b6f565b906122ff612b92565b926064359060a4359273ffffffffffffffffffffffffffffffffffffffff93848116948582036101dc576123316129ee565b9160ff60065416156124d9576123456130ea565b816007541633036124b05761235990613125565b63ffffffff821696875f526003602052835f209282821693845f5260205260ff855f20541615612488576102fa996103006102f46123a761239d866123b19661317a565b93602435876131d3565b91819e918c612d49565b9460843586106124615750918593917fc79c5d6f2d27aee6474296eafbd08c97d8991c729c7b986e7bd076931c92fcb89660609694895f52600a602052845f208b5f52602052845f20612405878254612f87565b905573eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee8403612448575050612433906008541634906133e1565b81519384526020840152820152a46001600555005b8261245c93600754169060085416916132b2565b612433565b84517fbde82093000000000000000000000000000000000000000000000000000000008152fd5b8585517f49a8defd000000000000000000000000000000000000000000000000000000008152fd5b505050517f049809b1000000000000000000000000000000000000000000000000000000008152fd5b505050517f46b57c6f000000000000000000000000000000000000000000000000000000008152fd5b82346101dc575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5760209073ffffffffffffffffffffffffffffffffffffffff600854169051908152f35b82346101dc5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc57606090612597612592612b6f565b612f23565b9060ff818051938051855282602082015116602086015201511690820152f35b50346101dc5760209160207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc5767ffffffffffffffff82358181116101dc576126099036908501612b51565b612611613069565b612619612cdb565b918151835103612735575f5b825181101561268357807f335f5afc83fe8c5a011a96dc39bcce9fb9d46fb5986502f7040e76e28b0361238661265d60019487612cc7565b51612667816130b9565b6126718489612cc7565b519082519182528b820152a101612625565b508591858151928311611c3857680100000000000000008311611c385750600954826009558083106126f5575b5060200160095f525f5b8281106126c357005b81517f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af820155908301906001016126ba565b827f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af91820191015b81811061272a57506126b0565b5f815560010161271d565b505050517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b5090346101dc5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126101dc576127976129db565b67ffffffffffffffff926024358481116101dc576127b89036908301612a14565b929093604435918683116101dc57366023840112156101dc5782840135966127df88612aeb565b936127ec84519586612aaa565b88855260209860248a87019160051b830101913683116101dc57602401905b8282106129c3575050506064359081116101dc5761282c9036908601612b51565b90612835613069565b851561299b5783518087149081159161298f575b506129675763ffffffff1692835f5260038852825f20955f5b81811061286b57005b73ffffffffffffffffffffffffffffffffffffffff61288e610a9a83858d612c96565b16801561293f579088600192887fc1a8d3b5c8695d3120c5ea6e6afca4fd990747cccf9c4513cbc02baf1c32e40d8e866128c8878b612cc7565b5115156128d5888d612cc7565b51968d51916128e383612a45565b8252838201978852865f5283528c5f20905115159661292d88839060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b5191829101558b51908152a401612862565b8786517fd92e233d000000000000000000000000000000000000000000000000000000008152fd5b8483517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b9050825114155f612849565b8483517f0f59b9ff000000000000000000000000000000000000000000000000000000008152fd5b813580151581036101dc578152908a01908a0161280b565b6004359063ffffffff821682036101dc57565b60c4359063ffffffff821682036101dc57565b6024359063ffffffff821682036101dc57565b9181601f840112156101dc5782359167ffffffffffffffff83116101dc576020808501948460051b0101116101dc57565b6040810190811067ffffffffffffffff821117612a6157604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b6060810190811067ffffffffffffffff821117612a6157604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff821117612a6157604052565b67ffffffffffffffff8111612a615760051b60200190565b9291612b0e82612aeb565b91612b1c6040519384612aaa565b829481845260208094019160051b81019283116101dc57905b828210612b425750505050565b81358152908301908301612b35565b9080601f830112156101dc57816020612b6c93359101612b03565b90565b6004359073ffffffffffffffffffffffffffffffffffffffff821682036101dc57565b6044359060ff821682036101dc57565b60e4359060ff821682036101dc57565b6084359060ff821682036101dc57565b359060ff821682036101dc57565b67ffffffffffffffff8111612a6157601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b81601f820112156101dc57803590612c2182612bd0565b92612c2f6040519485612aaa565b828452602083830101116101dc57815f926020809301838601378301015290565b8054821015612c69575f5260205f209060011b01905f90565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b9190811015612c695760051b0190565b3573ffffffffffffffffffffffffffffffffffffffff811681036101dc5790565b8051821015612c695760209160051b010190565b6040519060095480835282602091602082019060095f527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af935f905b828210612d2f57505050612d2d92500383612aaa565b565b855484526001958601958895509381019390910190612d17565b81810292918115918404141715612d5c57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b60405190612d9682612a8e565b5f6040838281528260208201520152565b519069ffffffffffffffffffff821682036101dc57565b612dc6612d89565b5073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5f52600160205260408051907fde3e59ea0eeb6a65f16dd826b7bdfb53723c09ad80c81d458bbf2e138d918523612e1283612a8e565b549073ffffffffffffffffffffffffffffffffffffffff8083168452602084019060ff8460a01c16825260ff8386019460a81c168452612e50612d89565b945116938415612f1b575060a06004948351958680927ffeaf968c0000000000000000000000000000000000000000000000000000000082525afa938415612f11575f94612ebd575b5060ff8091511692511691815193612eb085612a8e565b8452602084015282015290565b90935060a0813d60a011612f09575b81612ed960a09383612aaa565b810103126101dc5760ff81612eee8293612da7565b50612f00608060208301519201612da7565b50949150612e99565b3d9150612ecc565b82513d5f823e3d90fd5b935050505090565b612f2b612d89565b5073ffffffffffffffffffffffffffffffffffffffff8091165f526001602052604090815f2091805192612f5e84612a8e565b54918083168452602084019060ff8460a01c16825260ff8386019460a81c168452612e50612d89565b91908201809211612d5c57565b5f5b838110612fa55750505f910152565b8181015183820152602001612f96565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602093612ff181518092818752878088019101612f94565b0116010190565b90918281527f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff83116101dc5760209260051b809284830137010190565b91907f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81116101dc5760051b809282370190565b73ffffffffffffffffffffffffffffffffffffffff5f5416330361308957565b60246040517f118cdaa7000000000000000000000000000000000000000000000000000000008152336004820152fd5b156130c057565b60046040517f7c946ed7000000000000000000000000000000000000000000000000000000008152fd5b6002600554146130fb576002600555565b60046040517f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b73ffffffffffffffffffffffffffffffffffffffff165f52600b60205260ff60405f20541661315057565b60046040517f09550c77000000000000000000000000000000000000000000000000000000008152fd5b63ffffffff1690815f52600360205273ffffffffffffffffffffffffffffffffffffffff60405f2091165f52602052600160405f200154908115155f146131bf575090565b90505f526004602052600260405f20015490565b6131df90939293612f23565b928351613228575b8351156131fe575b505060ff602083519301511690565b8115801561321d575b6130c05760ff9184521660208301525f806131ef565b5060ff811615613207565b8115801590613261575b156131e75760046040517ff443cb16000000000000000000000000000000000000000000000000000000008152fd5b5060ff81161515613232565b604d8111612d5c57600a0a90565b8115613285570490565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b906040519360208501917f23b872dd00000000000000000000000000000000000000000000000000000000835273ffffffffffffffffffffffffffffffffffffffff9485809216602488015216604486015260648501526064845260a084019084821067ffffffffffffffff831117612a6157613347935f9384936040521694519082865af16133406133b2565b9083613a88565b805190811515918261338e575b505061335d5750565b602490604051907f5274afe70000000000000000000000000000000000000000000000000000000082526004820152fd5b81925090602091810103126101dc57602001518015908115036101dc575f80613354565b3d156133dc573d906133c382612bd0565b916133d16040519384612aaa565b82523d5f602084013e565b606090565b814710613440575f80809373ffffffffffffffffffffffffffffffffffffffff8294165af161340e6133b2565b501561341657565b60046040517f1425ea42000000000000000000000000000000000000000000000000000000008152fd5b60246040517fcd786059000000000000000000000000000000000000000000000000000000008152306004820152fd5b904282106134d95711156134af571561348557565b60046040517f2013535a000000000000000000000000000000000000000000000000000000008152fd5b60046040517f38af65f7000000000000000000000000000000000000000000000000000000008152fd5b60046040517fb290253c000000000000000000000000000000000000000000000000000000008152fd5b801561353a570361351057565b60046040517fa24a13a6000000000000000000000000000000000000000000000000000000008152fd5b60046040517f0f59b9ff000000000000000000000000000000000000000000000000000000008152fd5b93909897969594989291928342116136f65763ffffffff9061358533613125565b1698895f526020946003865260409a8b5f2073ffffffffffffffffffffffffffffffffffffffff83165f52875260ff8c5f205416156136cd575f52600486528a5f208b516135d281612a8e565b8154908181528d60026001850154948b8401958652015491015242106136a4575142101561367b579161367393917fffffffffffffffffffffffffffffffffffffffff00000000000000000000000060a894612d2d9c9d5197889561364a8b88019b3360601b8d52825192839160348b019101612f94565b8601946034860152605485015260601b1660748301526088820152036088810184520182612aaa565b519020613720565b60048b517f6b5fd31b000000000000000000000000000000000000000000000000000000008152fd5b60048c517f8e9c6e1c000000000000000000000000000000000000000000000000000000008152fd5b60048c517f49a8defd000000000000000000000000000000000000000000000000000000008152fd5b60046040517f1ab7da6b000000000000000000000000000000000000000000000000000000008152fd5b9261377890613781929373ffffffffffffffffffffffffffffffffffffffff948560065460081c16967f19457468657265756d205369676e6564204d6573736167653a0a3332000000005f52601c52603c5f20613909565b909291926139a3565b160361378957565b60046040517f8baa579f000000000000000000000000000000000000000000000000000000008152fd5b6137c390979695949392976130b9565b8642116136f65763ffffffff906137d933613125565b1695865f5260209160038352604097885f2073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee5f52845260ff895f205416156138e0575f5260048352875f20885161382481612a8e565b8154908181528a6002600185015494888401958652015491015242106138b7575142101561388e57605461367391612d2d989951938491613878878401973360601b89528251928391603487019101612f94565b8201906034820152036034810184520182612aaa565b600488517f6b5fd31b000000000000000000000000000000000000000000000000000000008152fd5b600489517f8e9c6e1c000000000000000000000000000000000000000000000000000000008152fd5b600489517f49a8defd000000000000000000000000000000000000000000000000000000008152fd5b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a08411613998579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa1561398d575f5173ffffffffffffffffffffffffffffffffffffffff81161561398357905f905f90565b505f906001905f90565b6040513d5f823e3d90fd5b5050505f9160039190565b6004811015613a5b57806139b5575050565b600181036139e75760046040517ff645eedf000000000000000000000000000000000000000000000000000000008152fd5b60028103613a2057602482604051907ffce698f70000000000000000000000000000000000000000000000000000000082526004820152fd5b600314613a2a5750565b602490604051907fd78bce0c0000000000000000000000000000000000000000000000000000000082526004820152fd5b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b90613a9d575080511561341657805190602001fd5b81511580613af5575b613aae575090565b60249073ffffffffffffffffffffffffffffffffffffffff604051917f9996b315000000000000000000000000000000000000000000000000000000008352166004820152fd5b50803b15613aa656fea2646970667358221220e6b863aa97cda840206a914f8d8d9c3d9aaa9e024a29d341af10f5a24b42cc5464736f6c63430008160033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000a22bac86e8f721875c6be26c1fc0b021442f7279000000000000000000000000ec38df358d2cb3557cb6cb9ebdb43a35891f48260000000000000000000000002cb197409ae65b344a611e2ab99a0e864ef28d4c0000000000000000000000009d0cf00f85015567084e79a76a65d6f5e912b8d2000000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000001dcd6500000000000000000000000000000000000000000000000000000000003b9aca00000000000000000000000000000000000000000000000000000000012a05f20000000000000000000000000000000000000000000000000000000002540be40000000000000000000000000000000000000000000000000000000006fc23ac000000000000000000000000000000000000000000000000000000001176592e00
-----Decoded View---------------
Arg [0] : fundsWalletAddress (address): 0xA22baC86E8F721875C6BE26C1Fc0B021442f7279
Arg [1] : signerAddress (address): 0xeC38Df358d2CB3557Cb6Cb9ebdB43a35891F4826
Arg [2] : claimsContractAddress (address): 0x2cb197409ae65b344a611E2ab99A0E864EF28d4c
Arg [3] : owner (address): 0x9D0CF00f85015567084E79A76a65D6F5e912b8d2
Arg [4] : lastRound (uint32): 6
Arg [5] : nftPrices (uint256[]): 500000000,1000000000,5000000000,10000000000,30000000000,75000000000
-----Encoded View---------------
13 Constructor Arguments found :
Arg [0] : 000000000000000000000000a22bac86e8f721875c6be26c1fc0b021442f7279
Arg [1] : 000000000000000000000000ec38df358d2cb3557cb6cb9ebdb43a35891f4826
Arg [2] : 0000000000000000000000002cb197409ae65b344a611e2ab99a0e864ef28d4c
Arg [3] : 0000000000000000000000009d0cf00f85015567084e79a76a65d6f5e912b8d2
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [5] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [7] : 000000000000000000000000000000000000000000000000000000001dcd6500
Arg [8] : 000000000000000000000000000000000000000000000000000000003b9aca00
Arg [9] : 000000000000000000000000000000000000000000000000000000012a05f200
Arg [10] : 00000000000000000000000000000000000000000000000000000002540be400
Arg [11] : 00000000000000000000000000000000000000000000000000000006fc23ac00
Arg [12] : 0000000000000000000000000000000000000000000000000000001176592e00
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|---|---|---|---|---|
BLAST | 42.23% | $3,387.08 | 1.5194 | $5,146.44 | |
ZKSYNC | 17.53% | $3,385.6 | 0.631 | $2,136.31 | |
ARB | 14.21% | $3,389.12 | 0.511 | $1,731.84 | |
LINEA | 8.33% | $3,385.6 | 0.3 | $1,015.68 | |
BSC | 6.80% | $693.98 | 1.1938 | $828.44 | |
POL | 5.49% | $0.49772 | 1,344.7904 | $669.33 | |
OP | 4.44% | $3,389.57 | 0.1595 | $540.47 | |
BASE | 0.92% | $3,389.52 | 0.0331 | $112.19 | |
OPBNB | 0.03% | $694.02 | 0.0044 | $3.05 | |
AVAX | 0.02% | $38.43 | 0.0494 | $1.9 | |
CELO | <0.01% | $0.683437 | 0.501 | $0.342402 | |
FTM | <0.01% | $1.02 | 0.0467 | $0.047795 |
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.