More Info
Private Name Tags
ContractCreator
Latest 25 from a total of 272 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
---|---|---|---|---|---|---|---|---|---|
Update Root | 21644365 | 44 hrs ago | IN | 0 ETH | 0.00021273 | ||||
Update Root | 21644363 | 44 hrs ago | IN | 0 ETH | 0.00021313 | ||||
Update Root | 21636734 | 2 days ago | IN | 0 ETH | 0.00016113 | ||||
Update Root | 21633476 | 3 days ago | IN | 0 ETH | 0.00016465 | ||||
Update Root | 21631722 | 3 days ago | IN | 0 ETH | 0.00045103 | ||||
Update Root | 21624849 | 4 days ago | IN | 0 ETH | 0.000209 | ||||
Update Root | 21624138 | 4 days ago | IN | 0 ETH | 0.00040207 | ||||
Cancel Challenge | 21591751 | 9 days ago | IN | 0 ETH | 0.00028441 | ||||
Resolve Group Ch... | 21591230 | 9 days ago | IN | 0 ETH | 0.00069006 | ||||
Update Root | 21588614 | 9 days ago | IN | 0 ETH | 0.00035459 | ||||
Join Challenge | 21587850 | 9 days ago | IN | 0.00329498 ETH | 0.00343807 | ||||
Create Challenge | 21587784 | 9 days ago | IN | 0.0033 ETH | 0.0038463 | ||||
Join Challenge | 21587712 | 9 days ago | IN | 0.0032 ETH | 0.00260227 | ||||
Create Challenge | 21587698 | 9 days ago | IN | 0.0032 ETH | 0.00383721 | ||||
Update Root | 21587522 | 9 days ago | IN | 0 ETH | 0.00042783 | ||||
Update Root | 21587514 | 9 days ago | IN | 0 ETH | 0.00044014 | ||||
Update Root | 21587376 | 9 days ago | IN | 0 ETH | 0.00044929 | ||||
Update Root | 21587372 | 9 days ago | IN | 0 ETH | 0.0004112 | ||||
Update Root | 21587250 | 9 days ago | IN | 0 ETH | 0.00019415 | ||||
Update Root | 21587245 | 9 days ago | IN | 0 ETH | 0.00019982 | ||||
Update Root | 21586452 | 9 days ago | IN | 0 ETH | 0.00013182 | ||||
Update Root | 21586441 | 9 days ago | IN | 0 ETH | 0.00014145 | ||||
Update Root | 21580205 | 10 days ago | IN | 0 ETH | 0.00025694 | ||||
Update Root | 21573332 | 11 days ago | IN | 0 ETH | 0.00025873 | ||||
Withdraw | 21573308 | 11 days ago | IN | 0 ETH | 0.00062179 |
Latest 8 internal transactions
Advanced mode:
Loading...
Loading
Contract Name:
P2PSports
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol"; import "@openzeppelin/contracts/access/Ownable2Step.sol"; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import "@solarity/solidity-lib/libs/arrays/SetHelper.sol"; import "@solarity/solidity-lib/libs/arrays/ArrayHelper.sol"; import "@solarity/solidity-lib/libs/utils/TypeCaster.sol"; import "@solarity/solidity-lib/libs/decimals/DecimalsConverter.sol"; import "./interfaces/IP2PSports.sol"; import "@solarity/solidity-lib/utils/Globals.sol"; /// @title P2PSports: A Peer-to-Peer Sports Betting Smart Contract /** @notice This contract allows users to create and join sports betting challenges, bet on outcomes, * and withdraw winnings in a decentralized manner. It supports betting with STMX token and other ERC20 tokens, along with ETH * and uses Chainlink for price feeds to calculate admin shares. * @dev The contract uses OpenZeppelin's Ownable and ReentrancyGuard for access control and reentrancy protection, * and utilizes libraries from solidity-lib for array and decimal manipulations. * * ERROR CODES: In order to reduce the size of the Smart Contract, we have defined the short codes instead of the complete * error messages in the revert/require statements. The messages corresponding to the error codes can be seen in the following document. * https://duelnow.notion.site/Smart-Contract-Error-Codes-ca7427520ce04ca293d3e21fb1e21583 */ contract P2PSports is IP2PSports, Ownable2Step, ReentrancyGuard { using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.AddressSet; using EnumerableSet for EnumerableSet.UintSet; using SetHelper for EnumerableSet.AddressSet; using DecimalsConverter for *; using ArrayHelper for uint256[]; using TypeCaster for *; /// @notice merkle tree root node bytes32 private merkleRoot; /// @notice Backend server address to resolve, cancel challenges or some additional control. address public backend; /// @notice Token address for the STMX token, used as one of the betting currencies // Configuration / Validation parameters for different betting logics uint256 public maxChallengersEachSide; uint256 public maxChallengersForPickem; uint256 public minUSDBetAmount; bool public applyMembershipValues; /// @notice ChallengeId of the last challenge created uint256 public latestChallengeId; /// @notice Flag to allow or restrict creations or joining challenges bool public bettingAllowed; uint256 public constant maxAdminShareInUsd = 1000 * 10 ** 8; uint256 public constant maxAdminShareSTMX = 100000 * 10 ** 18; uint256 public constant maxForMinUSDBetAmount = 100 * 10 ** 8; uint256 public constant maxChallengesToResolve = 10; uint256 public constant maxWinnersGroupChallenge = 10; uint256 public constant awaitingTimeForPublicCancel = 172800; //48 hours uint256 public constant defaultOracleDecimals = 8; uint256 public constant maxAdminShareThresholds = 20; uint256 public constant priceFeedErrorMargin = 5; // Internal storage for tokens, price feeds, challenges, and bets EnumerableSet.AddressSet internal _allTokens; EnumerableSet.AddressSet internal _oraclessTokens; EnumerableSet.AddressSet internal _allowedTokens; mapping(address => AggregatorV3Interface) internal _priceFeeds; mapping(uint256 => Challenge) internal _challenges; mapping(address => mapping(uint256 => UserBet)) internal _userChallengeBets; mapping(address => mapping(address => uint256)) internal _withdrawables; mapping(address => AdminShareRule) internal _adminShareRules; mapping(address => uint256) internal _oraclessTokensMinBetAmount; /// @dev Ensures the function is called only by the backend address modifier onlyBackend() { _onlyBackend(); _; } /// @dev Ensures the function is called only by the backend address or owner address modifier onlyBackendOrOwner() { _onlyBackendOrOwner(); _; } /// @notice Initializes the contract with provided addresses and tokens /** @dev Sets initial configuration for the contract and allows specified tokens. * @param backend_ Address of the backend server for challenge resolution and control */ constructor(address backend_) { require(backend_ != address(0), "1"); // Contract setup and initial token allowance backend = backend_; maxChallengersEachSide = 50; maxChallengersForPickem = 50; bettingAllowed = true; minUSDBetAmount = 10 * 10 ** 8; } receive() external payable {} /// @notice Creates a new challenge for betting /** @dev Emits a `ChallengeCreated` event and calls `joinChallenge` for the challenge creator. * @param token Address of the token used for betting (zero address for native currency) * @param amountFromWallet Amount to be bet from the creator's wallet * @param amountFromWithdrawables Amount to be bet from the creator's withdrawable balance * @param decision The side of the bet the creator is taking * @param challengeType The type of challenge (Individual or Group) * @param startTime Start time of the challenge * @param endTime End time of the challenge * @param membershipLevel user membership level * @param feePercentage percentage amount reduced from admin share * @param referrer referrer address * @param referralCommision referral will get the comission from admin share * @param proof leaf nood proof */ function createChallenge( address token, uint256 amountFromWallet, uint256 amountFromWithdrawables, uint8 decision, ChallengeType challengeType, uint256 startTime, uint256 endTime, uint8 membershipLevel, uint256 feePercentage, address referrer, uint256 referralCommision, bytes32[] memory proof ) external payable { uint256 challengeId = ++latestChallengeId; require(startTime <= block.timestamp && endTime > block.timestamp, "2"); require(_allowedTokens.contains(token), "3"); Challenge storage _challenge = _challenges[challengeId]; _challenge.token = token; _challenge.status = ChallengeStatus.Betting; _challenge.challengeType = challengeType; _challenge.startTime = startTime; _challenge.endTime = endTime; emit ChallengeCreated( challengeId, token, msg.sender, amountFromWallet + amountFromWithdrawables ); joinChallenge( challengeId, amountFromWallet, amountFromWithdrawables, decision, membershipLevel, feePercentage, referrer, referralCommision, proof ); } /// @notice Allows users to join an existing challenge with their bet /** @dev Emits a `ChallengeJoined` event if the join is successful. * @param challengeId ID of the challenge to join * @param amountFromWallet Amount to be bet from the user's wallet * @param amountFromWithdrawables Amount to be bet from the user's withdrawable balance * @param decision The side of the bet the user is taking * @param membershipLevel user membership level * @param feePercentage percentage amount reduced from admin share * @param referrer referrer address * @param referralCommision referral will get the comission from admin share * @param proof leaf nood proof */ function joinChallenge( uint256 challengeId, uint256 amountFromWallet, uint256 amountFromWithdrawables, uint8 decision, uint8 membershipLevel, uint256 feePercentage, address referrer, uint256 referralCommision, bytes32[] memory proof ) public payable { Challenge memory challengeDetails = _challenges[challengeId]; _assertChallengeExistence(challengeId); if (challengeDetails.challengeType == ChallengeType.Group) { require(decision == 1, "4"); } else { require(decision == 1 || decision == 2, "5"); } require(_userChallengeBets[msg.sender][challengeId].decision == 0, "6"); _joinChallenge( challengeId, amountFromWallet, amountFromWithdrawables, decision, membershipLevel, feePercentage, referrer, referralCommision, proof ); } /// @notice Allows users to increase the bet amount /** @dev Emits a `BetAmountIncreased` event if the join is successful. * @param challengeId ID of the challenge for which user wants to increase the bet amount * @param amountFromWallet Amount to be bet from the user's wallet * @param amountFromWithdrawables Amount to be bet from the user's withdrawable balance * @param membershipLevel user membership level * @param feePercentage percentage amount reduced from admin share * @param referrer referrer address * @param referralCommision referral will get the comission from admin share * @param proof leaf nood proof */ function increaseBetAmount( uint256 challengeId, uint256 amountFromWallet, uint256 amountFromWithdrawables, uint8 membershipLevel, uint256 feePercentage, address referrer, uint256 referralCommision, bytes32[] memory proof ) public payable { _assertChallengeExistence(challengeId); UserBet storage betDetails = _userChallengeBets[msg.sender][challengeId]; require(betDetails.decision != 0, "7"); Challenge storage challengeDetails = _challenges[challengeId]; ( uint256 amount, uint256 adminShare, uint256 referralCommisionAmount ) = _calculateChallengeAmounts( challengeDetails.token, amountFromWallet, amountFromWithdrawables, challengeId, membershipLevel, feePercentage, referrer, referralCommision, proof, false ); betDetails.amount += amount; betDetails.adminShare += adminShare; betDetails.referralCommision += referralCommisionAmount; if (betDetails.decision == 1) { challengeDetails.amountFor += amount; } else { challengeDetails.amountAgainst += amount; } emit BetAmountIncreased( challengeId, betDetails.amount, amount, msg.sender, challengeDetails.token ); } /// @notice Checks if a challenge with the given ID exists /** @dev A challenge is considered to exist if its ID is greater than 0 and less than or equal to the latest challenge ID. * @param challengeId The ID of the challenge to check. * @return bool Returns true if the challenge exists, false otherwise. */ function challengeExists(uint256 challengeId) public view returns (bool) { return challengeId > 0 && challengeId <= latestChallengeId; } /// @notice Owner can update the root node of merkle /** @dev This function will allow the owner to update the root node of merkle tree * @param _root root node of merkle tree */ function updateRoot(bytes32 _root) public onlyBackend { merkleRoot = _root; emit MerkleRootUpdated(_root, msg.sender); } /// @notice Withdraws available tokens for the sender /** @dev This function allows users to withdraw their available tokens from the contract. It uses the * nonReentrant modifier from OpenZeppelin to prevent reentrancy attacks. A `UserWithdrawn` event is * emitted upon a successful withdrawal. * @param token The address of the token to be withdrawn. Use the zero address for the native currency. * * Requirements: * - The sender must have a positive withdrawable balance for the specified token. * Emits a {UserWithdrawn} event indicating the token, amount, and the user who performed the withdrawal. */ function withdraw(address token) external nonReentrant { uint256 amount = _withdrawables[msg.sender][token]; require(amount > 0, "8"); delete _withdrawables[msg.sender][token]; _withdraw(token, msg.sender, amount); emit UserWithdrawn(token, amount, msg.sender); } /// @notice Resolves multiple challenges with their final outcomes /** @dev This function is called by the backend to resolve challenges that have reached their end time * and are in the awaiting status. It updates the status of each challenge based on its final outcome. * Only challenges of type `Individual` can be resolved using this function. A `ChallengeResolved` event is * emitted for each challenge that is resolved. This function uses the `onlyBackend` modifier to ensure * that only authorized backend addresses can call it, and `nonReentrant` to prevent reentrancy attacks. * @param challengeIds Array of IDs of the challenges to be resolved. * @param finalOutcomes Array of final outcomes for each challenge, where outcomes are defined as follows: * - 1: Side A wins, * - 2: Side B wins, * - 3: Draw. * * Requirements: * - The lengths of `challengeIds` and `finalOutcomes` must be the same and not exceed `maxChallengesToResolve`. * - Each challenge must exist, be in the `Awaiting` status, and be of type `Individual`. * - Each `finalOutcome` must be within the range [1,3]. */ function resolveChallenge( uint256[] memory challengeIds, uint8[] memory finalOutcomes ) external onlyBackend nonReentrant { uint256 challengeIdsLength = challengeIds.length; require( challengeIdsLength <= maxChallengesToResolve && challengeIdsLength == finalOutcomes.length, "9" ); for (uint256 i = 0; i < challengeIdsLength; ++i) { uint256 challengeId = challengeIds[i]; Challenge storage challengeDetails = _challenges[challengeId]; require(challengeDetails.challengeType == ChallengeType.Individual, "10"); uint8 finalOutcome = finalOutcomes[i]; require(finalOutcome > 0 && finalOutcome < 4, "11"); _assertChallengeExistence(challengeId); _assertResolveableStatus(challengeId); challengeDetails.status = ChallengeStatus(finalOutcome + 4); emit ChallengeResolved(challengeId, finalOutcome); if (finalOutcome == 3) { _cancelBets(challengeId, 0); } else { _calculateChallenge(challengeId, finalOutcome); } } } /// @notice Cancels a user's participation in a challenge /** @dev This function allows the backend to cancel a user's participation in a challenge, refunding their bet. * It can only be called by the backend and is protected against reentrancy attacks. The function checks if the * challenge exists and ensures that the challenge is either in the `Awaiting` or `Betting` status, implying that * it has not been resolved yet. Additionally, it verifies that the user has indeed placed a bet on the challenge. * After these checks, it calls an internal function `_cancelParticipation` to handle the logic for cancelling the * user's participation and processing the refund. * @param user The address of the user whose participation is to be cancelled. * @param challengeId The ID of the challenge from which the user's participation is to be cancelled. * * Requirements: * - The challenge must exist and be in a state where participation can be cancelled (`Awaiting` or `Betting`). * - The user must have participated in the challenge. * Uses the `onlyBackend` modifier to ensure only the backend can invoke this function, and `nonReentrant` for security. */ function cancelParticipation( address user, uint256 challengeId, uint8 cancelType ) external onlyBackend nonReentrant { _assertChallengeExistence(challengeId); _assertCancelableStatus(challengeId); require(_userChallengeBets[user][challengeId].decision != 0, "12"); _cancelParticipation(user, challengeId, cancelType); } /// @notice Resolves a group challenge by determining winners and distributing profits /** @dev This function is used for resolving group challenges specifically, where multiple participants can win. * It can only be executed by the backend and is protected against reentrancy. The function ensures that the * challenge exists, is currently awaiting resolution, and is of the `Group` challenge type. It then validates * that the lengths of the winners and profits arrays match and do not exceed the maximum number of winners allowed. * Each winner's address must have participated in the challenge, and winners must be unique. The total of the profits * percentages must equal 100. Once validated, the challenge status is updated, and profits are calculated and * distributed to the winners based on the provided profits percentages. * @param challengeId The ID of the group challenge to resolve. * @param winners An array of addresses of the winners of the challenge. * @param profits An array of profit percentages corresponding to each winner, summing to 100. * * Requirements: * - The challenge must exist, be in the `Awaiting` status, and be of the `Group` type. * - The `winners` and `profits` arrays must have the same length and comply with the maximum winners limit. * - The sum of the `profits` percentages must equal 100. * Emits a {ChallengeResolved} event with the challenge ID and a hardcoded outcome of `5`, indicating group resolution. */ function resolveGroupChallenge( uint256 challengeId, address[] calldata winners, uint256[] calldata profits ) external onlyBackend nonReentrant { _assertChallengeExistence(challengeId); _assertResolveableStatus(challengeId); Challenge storage challengeDetails = _challenges[challengeId]; require(challengeDetails.challengeType == ChallengeType.Group, "13"); uint256 winnersLength = winners.length; require( winnersLength == profits.length && winnersLength <= maxWinnersGroupChallenge, "14" ); uint256 totalProfit = 0; for (uint256 i = 0; i < winnersLength; ++i) { totalProfit += profits[i]; if (i > 0) { require(winners[i] > winners[i - 1], "16"); } require(_userChallengeBets[winners[i]][challengeId].decision != 0, "15"); } require(totalProfit == (100 * DECIMAL), "17"); challengeDetails.status = ChallengeStatus.ResolvedFor; emit ChallengeResolved(challengeId, 5); _calculateGroupChallenge(challengeId, winners, profits); } /// @notice Cancels a challenge and refunds all participants /** @dev This function allows the backend to cancel a challenge if it's either awaiting resolution or still open for betting. * It ensures that the challenge exists and is in a cancelable state (either `Awaiting` or `Betting`). Upon cancellation, * the challenge's status is updated to `Canceled`, and all bets placed on the challenge are refunded to the participants. * This function is protected by the `onlyBackend` modifier to restrict access to the backend address, and `nonReentrant` * to prevent reentrancy attacks. * @param challengeId The ID of the challenge to be cancelled. * @param cancelType 0-Return bet amount without admin shares 1-Return bet amount with admin shares. * * Requirements: * - The challenge must exist and be in a state that allows cancellation (`Awaiting` or `Betting`). * Emits a {ChallengeCanceled} event upon successful cancellation, indicating which challenge was cancelled. */ function cancelChallenge( uint256 challengeId, uint8 cancelType ) external onlyBackendOrOwner nonReentrant { _assertChallengeExistence(challengeId); _assertCancelableStatus(challengeId); Challenge storage challengeDetails = _challenges[challengeId]; if (msg.sender == owner()) { require( (challengeDetails.endTime + awaitingTimeForPublicCancel) < block.timestamp, "18" ); } challengeDetails.status = ChallengeStatus.Canceled; emit ChallengeCanceled(challengeId); _cancelBets(challengeId, cancelType); } /// @notice Toggles the ability for users to place bets on challenges /** @dev This function allows the contract owner to enable or disable betting across the platform. * It's a straightforward toggle that sets the `bettingAllowed` state variable based on the input. * Access to this function is restricted to the contract owner through the `onlyOwner` modifier from * OpenZeppelin's Ownable contract, ensuring that only the owner can change the betting policy. * @param value_ A boolean indicating whether betting should be allowed (`true`) or not (`false`). */ function allowBetting(bool value_) external onlyOwner { bettingAllowed = value_; emit BettingAllowed(value_, msg.sender); } /// @notice Toggles the ability for membership discount and referral comisions /** @dev This function will allow the owner to toggle the apply membership values * @param value_ true to apply membership values and false for disable membership values */ function updateApplyMembershipValues(bool value_) external onlyOwner { applyMembershipValues = value_; emit MembershipApplied(value_, msg.sender); } /// @notice Updates the minimum USD betting amount. /// @dev Can only be called by the contract owner. /// @param value_ The new minimum betting amount in USD. function changeMinUSDBettingAmount(uint256 value_) external onlyOwner { require(value_ >= minUSDBetAmount && value_ <= maxForMinUSDBetAmount, "28"); minUSDBetAmount = value_; emit MinUSDBettingAmountUpdated(value_, msg.sender); } /// @notice Updates the address of the backend responsible for challenge resolutions and administrative actions /** @dev This function allows the contract owner to change the backend address to a new one. * Ensures the new backend address is not the zero address to prevent rendering the contract unusable. * The function is protected by the `onlyOwner` modifier, ensuring that only the contract owner has the authority * to update the backend address. This is crucial for maintaining the integrity and security of the contract's * administrative functions. * @param backend_ The new address to be set as the backend. It must be a non-zero address. * * Requirements: * - The new backend address cannot be the zero address, ensuring that the function call has meaningful intent. */ function changeBackend(address backend_) external onlyOwner { require(backend_ != address(0), "1"); backend = backend_; emit BackendChanged(backend_, msg.sender); } /// @notice Allows a batch of tokens to be used for betting, with optional price feeds for valuation /** @dev This function permits the contract owner to add tokens to the list of those allowed for betting. * It also associates Chainlink price feeds with tokens, enabling the conversion of bets to a common value basis for calculations. * Tokens without a specified price feed (address(0)) are considered to have fixed or known values and are added to a separate list. * The function ensures that each token in the input array has a corresponding price feed address (which can be the zero address). * The `onlyOwner` modifier restricts this function's execution to the contract's owner, safeguarding against unauthorized token addition. * @param tokens An array of token addresses to be allowed for betting. * @param priceFeeds An array of Chainlink price feed addresses corresponding to the tokens. Use address(0) for tokens without a need for price feeds. * @param minBetAmounts An array of amount corresponding to every token being allowed, the value for oracless tokens will be considers only in this method. * Requirements: * - The lengths of the `tokens` and `priceFeeds` arrays must match to ensure each token has a corresponding price feed address. */ function allowTokens( address[] memory tokens, address[] memory priceFeeds, uint256[] memory minBetAmounts ) public onlyOwner { uint256 tokensLength = tokens.length; uint256 priceFeedLength = priceFeeds.length; uint256 minBetAmountsLength = minBetAmounts.length; require(tokensLength == priceFeedLength && tokensLength == minBetAmountsLength, "9"); for (uint256 i = 0; i < tokensLength; ++i) { require(!_allowedTokens.contains(tokens[i]), "46"); if (priceFeeds[i] == address(0)) { require(minBetAmounts[i] > 0, "20"); _oraclessTokensMinBetAmount[tokens[i]] = minBetAmounts[i]; _oraclessTokens.add(tokens[i]); } else { isValidPriceFeed(priceFeeds[i], priceFeedErrorMargin); _priceFeeds[tokens[i]] = AggregatorV3Interface(priceFeeds[i]); } } _allowedTokens.add(tokens); _allTokens.add(tokens); emit TokenAllowed(tokens, priceFeeds, minBetAmounts, msg.sender); } /// @notice Removes a batch of tokens from being allowed for betting and deletes associated price feeds /** @dev This function enables the contract owner to restrict certain tokens from being used in betting activities. * It involves removing tokens from the list of allowed tokens, potentially removing them from the list of tokens * without a Chainlink price feed (oracless tokens), and deleting their associated price feeds if any were set. * This is a crucial administrative function for managing the tokens that can be used on the platform, allowing * for adjustments based on compliance, liquidity, or other operational considerations. * Execution is restricted to the contract's owner through the `onlyOwner` modifier, ensuring that token restrictions * can only be imposed by authorized parties. * @param tokens An array of token addresses that are to be restricted from use in betting. */ function restrictTokens(address[] memory tokens) external onlyOwner { uint256 tokensLength = tokens.length; for (uint256 i = 0; i < tokensLength; ++i) { require(_allowedTokens.contains(tokens[i]), "47"); delete _priceFeeds[tokens[i]]; delete _oraclessTokensMinBetAmount[tokens[i]]; } _allowedTokens.remove(tokens); _oraclessTokens.remove(tokens); emit TokenRestricted(tokens, msg.sender); } /// @notice Sets the rules for administrative shares on betting winnings based on thresholds /** @dev Allows the contract owner to define how administrative shares (a portion of betting winnings) are calculated. * This can be configured differently for the STMX token versus other tokens, as indicated by the `isSTMX` flag. * Each entry in the `thresholds` and `sharesInUSD` arrays defines a tier: if the winnings fall into a certain threshold, * the corresponding percentage is applied as the administrative share. The function enforces ascending order for thresholds * and ensures that the share in USD do not exceed a maximum limit. This setup allows for flexible configuration * of administrative fees based on the amount won. * Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only they can set these rules. * @param thresholds An array of threshold values, each representing the lower bound of a winnings bracket. * @param sharesInUSD An array of sharesInUSD corresponding to each threshold, defining the admin share for that bracket. * @param token Token address. * @param isSTMX A boolean flag indicating whether these rules apply to the STMX token (`true`) or other tokens (`false`). * * Requirements: * - The `thresholds` and `sharesInUSD` arrays must be of equal length and not empty, ensuring each threshold has a corresponding percentage. * - Thresholds must be in ascending order, and all sharesInUSD must not exceed the predefined maximum admin share percentage. */ function setAdminShareRules( uint256[] memory thresholds, uint256[] memory sharesInUSD, address token, bool isSTMX ) external onlyOwner { require(_allTokens.contains(token), "48"); uint256 thresholdsLength = thresholds.length; uint256 sharesInUSDLength = sharesInUSD.length; require( thresholdsLength > 0 && thresholdsLength == sharesInUSDLength && thresholdsLength <= maxAdminShareThresholds, "9" ); uint256 maxAdminShare = maxAdminShareInUsd; for (uint256 i = 0; i < thresholdsLength - 1; ++i) { require(thresholds[i] <= thresholds[i + 1], "21"); if (isSTMX) { maxAdminShare = maxAdminShareSTMX; } require(sharesInUSD[i] <= maxAdminShare, "22"); } if (isSTMX) { maxAdminShare = maxAdminShareSTMX; } require(sharesInUSD[sharesInUSDLength - 1] <= maxAdminShare, "22"); _adminShareRules[token] = AdminShareRule({ sharesInUSD: sharesInUSD, thresholds: thresholds, isSTMX: isSTMX }); emit AdminShareRulesUpdated(_adminShareRules[token], msg.sender); } /// @notice Update the maximum challenger limits /** * Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only they can set these rules. * @param _maxChallengersEachSide maximun limit of challengers can join in each side. * @param _maxChallengersForPickem maximun limit of challengers can join for pickem. */ function updateMaxChallengers( uint256 _maxChallengersEachSide, uint256 _maxChallengersForPickem ) external onlyOwner { require( _maxChallengersForPickem > 0 && _maxChallengersForPickem <= 50 && _maxChallengersEachSide > 0 && _maxChallengersEachSide <= 50, "23" ); maxChallengersForPickem = _maxChallengersForPickem; maxChallengersEachSide = _maxChallengersEachSide; emit MaxChallengersUpdated(_maxChallengersEachSide, _maxChallengersForPickem, msg.sender); } /// @notice Owner is able to deposit tokens in SC under the owner's withdrawbales, to use the owner withdrawables in user's bets /** * Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only owner can deposit amount to SC. * @param _amount amount of tokens. * @param _token token address. */ function debitInSC(uint256 _amount, address _token) external payable onlyOwner { require(_allowedTokens.contains(_token), "3"); require(_amount > 0, "28"); if (_token == address(0)) { require(msg.value == _amount, "29"); } else { require(msg.value == 0, "34"); IERC20(_token).safeTransferFrom(msg.sender, address(this), _amount); } _withdrawables[msg.sender][_token] += _amount; emit DebitedInSC(_withdrawables[msg.sender][_token], msg.sender); } /// @notice Retrieves the administrative share rules for either the STMX token or other tokens /** @dev This function provides external access to the administrative share rules that have been set up for either * the STMX token (if `isSTMX` is true) or for other tokens (if `isSTMX` is false). These rules define the thresholds * and corresponding percentages that determine how administrative shares are calculated from betting winnings. * The function returns two arrays: one for the thresholds and one for the percentages, which together outline the * structure of admin shares based on the amount of winnings. * @param token A boolean flag indicating whether to retrieve the rules for the STMX token (`true`) or other tokens (`false`). * @return thresholds An array of uint256 representing the winnings thresholds for admin shares calculation. * @return sharesInUSD An array of uint256 representing the admin share in USD for each corresponding threshold. */ function getAdminShareRules( address token ) external view returns (uint256[] memory thresholds, uint256[] memory sharesInUSD, bool isSTMX) { AdminShareRule storage rule = _adminShareRules[token]; return (rule.thresholds, rule.sharesInUSD, rule.isSTMX); } /// @notice Retrieves the list of tokens currently allowed for betting /** @dev This function provides external visibility into which tokens are currently permitted for use in betting within the platform. * It leverages the EnumerableSet library from OpenZeppelin to handle the dynamic array of addresses representing the allowed tokens. * This is particularly useful for interfaces or external contracts that need to verify or display the tokens users can bet with. * @return An array of addresses, each representing a token that is allowed for betting. */ function getAllowedTokens() external view returns (address[] memory) { return _allowedTokens.values(); } /// @notice Fetches detailed information about a specific challenge by its ID /** @dev This function provides access to the details of a given challenge, including its current status, which is * dynamically determined based on the challenge's timing and resolution state. It's essential for external callers * to be able to retrieve comprehensive data on a challenge, such as its participants, status, and betting amounts, * to properly interact with or display information about the challenge. The function checks that the requested * challenge exists before attempting to access its details. * @param challengeId The unique identifier of the challenge for which details are requested. * @return challengeDetails A `Challenge` struct containing all relevant data about the challenge, including an updated status. * * Requirements: * - The challenge must exist, as indicated by its ID being within the range of created challenges. */ function getChallengeDetails( uint256 challengeId ) external view returns (Challenge memory challengeDetails) { _assertChallengeExistence(challengeId); challengeDetails = _challenges[challengeId]; challengeDetails.status = _challengeStatus(challengeId); } /// @notice Retrieves the bet details placed by a specific user on a particular challenge /** @dev This function allows anyone to view the details of a bet made by a user on a specific challenge, * including the amount bet and the side the user has chosen. It's crucial for enabling users or interfaces * to confirm the details of participation in challenges and to understand the stakes involved. This function * directly accesses the mapping of user bets based on the user address and challenge ID, returning the * corresponding `UserBet` struct. * @param challengeId The ID of the challenge for which the bet details are being queried. * @param user The address of the user whose bet details are requested. * @return A `UserBet` struct containing the amount of the bet and the decision (side chosen) by the user for the specified challenge. */ function getUserBet(uint256 challengeId, address user) external view returns (UserBet memory) { return _userChallengeBets[user][challengeId]; } /// @notice Provides a list of tokens and corresponding amounts available for withdrawal by a specific user /** @dev This function compiles a comprehensive view of all tokens that a user has available to withdraw, * including winnings, refunds, or other credits due to the user. It iterates over the entire list of tokens * recognized by the contract (not just those currently allowed for betting) to ensure that users can access * any funds owed to them, regardless of whether a token's betting status has changed. This is essential for * maintaining transparency and access to funds for users within the platform. * @param user The address of the user for whom withdrawable balances are being queried. * @return tokens An array of token addresses, representing each token that the user has a balance of. * @return amounts An array of uint256 values, each corresponding to the balance of the token at the same index in the `tokens` array. */ function getUserWithdrawables( address user ) external view returns (address[] memory tokens, uint256[] memory amounts) { uint256 allTokensLength = _allTokens.length(); tokens = new address[](allTokensLength); amounts = new uint256[](allTokensLength); for (uint256 i = 0; i < allTokensLength; ++i) { tokens[i] = _allTokens.at(i); amounts[i] = _withdrawables[user][tokens[i]]; } } /// @notice verify the membership proof from merkle tree /** @dev If merkle proof got verified it will return true otherwise false * @param proof leaf nood proof * @param membershipLevel user membership level * @param feePercentage percentage amount reduced from admin share * @param referrer referrer address * @param referralCommision referral will get the comission from admin share * @return bool Returns true if proof got verified, false otherwise. */ function verifyMembership( bytes32[] memory proof, uint256 membershipLevel, uint256 feePercentage, address referrer, uint256 referralCommision ) internal view returns (bool) { bytes32 leaf = keccak256( bytes.concat( keccak256( abi.encode( msg.sender, membershipLevel, feePercentage, referrer, referralCommision ) ) ) ); require(MerkleProof.verify(proof, merkleRoot, leaf), "45"); return true; } /** * @dev Allows a user to join a challenge, handling the financial transactions involved, including admin fees. * This internal function processes a user's bet on a challenge, taking into account amounts from the user's wallet and * withdrawable balance. It calculates and deducts an admin share based on the total bet amount and updates the challenge * and user's records accordingly. * * @param challengeId The unique identifier of the challenge the user wishes to join. * @param amountFromWallet The portion of the user's bet that will be taken from their wallet. * @param amountFromWithdrawables The portion of the user's bet that will be taken from their withdrawable balance. * @param decision Indicates whether the user is betting for (1) or against (2) in the challenge; for group challenges, this is ignored. * @param membershipLevel user membership level * @param feePercentage percentage amount reduced from admin share * @param referrer referrer address * @param referralCommision referral will get the comission from admin share * @param proof leaf nood proof * * The function enforces several checks and conditions: * - The total bet amount must exceed the admin share calculated for the transaction. * - The user must have sufficient withdrawable balance if opting to use it. * - Transfers the required amount from the user's wallet if applicable. * - Updates the admin's withdrawable balance with the admin share. * - Adds the user to the challenge participants and updates the challenge's total amount for or against based on the user's decision. * - Ensures the number of participants does not exceed the maximum allowed. * - Records the user's bet details. * * Emits a `ChallengeJoined` event upon successful joining of the challenge. * Emits an `AdminShareCalculated` event to indicate the admin share calculated from the user's bet. * * Requirements: * - The sum of `amountFromWallet` and `amountFromWithdrawables` must be greater than the admin share. * - If using withdrawables, the user must have enough balance. * - The challenge token must be transferred successfully from the user's wallet if necessary. * - The challenge's participants count for either side must not exceed `maxChallengersEachSide`. * * Notes: * - This function uses the nonReentrant modifier to prevent reentry attacks. * - It supports participation in both individual and group challenges. * - Admin shares are calculated and deducted from the user's total bet amount to ensure fair administration fees. */ function _joinChallenge( uint256 challengeId, uint256 amountFromWallet, uint256 amountFromWithdrawables, uint8 decision, uint8 membershipLevel, uint256 feePercentage, address referrer, uint256 referralCommision, bytes32[] memory proof ) internal nonReentrant { Challenge storage _challenge = _challenges[challengeId]; ( uint256 amount, uint256 adminShare, uint256 referralCommisionAmount ) = _calculateChallengeAmounts( _challenge.token, amountFromWallet, amountFromWithdrawables, challengeId, membershipLevel, feePercentage, referrer, referralCommision, proof, true ); uint256 participants; // Depending on the decision, update challenge state and user bet details if (decision == 1 || _challenge.challengeType == ChallengeType.Group) { _challenge.usersFor.push(msg.sender); participants = _challenge.usersFor.length; _challenge.amountFor += amount; } else { _challenge.usersAgainst.push(msg.sender); participants = _challenge.usersAgainst.length; _challenge.amountAgainst += amount; } // Ensure the number of participants does not exceed the maximum allowed per side if (_challenge.challengeType == ChallengeType.Group) { require(participants <= maxChallengersForPickem, "30"); } else { require(participants <= maxChallengersEachSide, "44"); } // Record user's bet details for the challenge if (_challenge.challengeType == ChallengeType.Group) { decision = 1; } _userChallengeBets[msg.sender][challengeId] = UserBet({ amount: amount, decision: decision, adminShare: adminShare, referrer: referrer, referralCommision: referralCommisionAmount }); // Emit events for challenge joined and admin received shares emit ChallengeJoined( challengeId, amount, msg.sender, _challenge.token, amountFromWallet + amountFromWithdrawables ); emit AdminShareCalculated(challengeId, _challenge.token, adminShare); } /// @notice calculate the challenge bet amount /** * @param challengeToken token in which user tried to bet * @param amountFromWallet amount which will be deducted from the users wallet * @param amountFromWithdrawables amount which will be deducted from the users withdrawables * @param membershipLevel user membership level * @param feePercentage percentage amount reduced from admin share * @param referrer referrer address * @param referralCommision referral will get the comission from admin share * @param proof leaf nood proof * @return amount of the bet * @return admin share on bet amount */ function _calculateChallengeAmounts( address challengeToken, uint256 amountFromWallet, uint256 amountFromWithdrawables, uint256 challengeId, uint8 membershipLevel, uint256 feePercentage, address referrer, uint256 referralCommision, bytes32[] memory proof, bool checkMinUsdAmounts ) internal returns (uint256, uint256, uint256) { require(bettingAllowed, "31"); require(_challengeStatus(challengeId) == ChallengeStatus.Betting, "32"); if (challengeToken == address(0)) { require(amountFromWallet == msg.value, "33"); } else { require(msg.value == 0, "34"); } uint256 amount = amountFromWallet + amountFromWithdrawables; uint256 adminShare = _calculateAdminShare(challengeToken, amount); uint256 referralCommisionAmount; if (applyMembershipValues) { verifyMembership(proof, membershipLevel, feePercentage, referrer, referralCommision); adminShare = (adminShare * ((100 * 10 ** 20) - feePercentage)) / (100 * 10 ** 20); if (referrer != address(0)) { referralCommisionAmount = adminShare; // admin share amount with refferral commision amount // deduct the referral commission from admin share adminShare = (adminShare * ((100 * 10 ** 20) - referralCommision)) / (100 * 10 ** 20); referralCommisionAmount -= adminShare; } } // Ensure that the total amount is greater than the admin share per challenge require(amount > adminShare, "35"); uint256 valueAmount = (_getValue(challengeToken) * amount) / 10 ** (challengeToken == address(0) ? 18 : challengeToken.decimals()); if (_oraclessTokens.contains(challengeToken)) { require(valueAmount >= _oraclessTokensMinBetAmount[challengeToken], "28"); } else { require(!checkMinUsdAmounts || valueAmount >= minUSDBetAmount, "28"); } // Deduct the amount from the withdrawables if bet amount is from withdrawables if (amountFromWithdrawables > 0) { require(_withdrawables[msg.sender][challengeToken] >= amountFromWithdrawables, "36"); _withdrawables[msg.sender][challengeToken] -= amountFromWithdrawables; } // Transfer the amount from the user's wallet to the contract if (challengeToken != address(0)) { IERC20(challengeToken).safeTransferFrom(msg.sender, address(this), amountFromWallet); } amount -= (adminShare + referralCommisionAmount); return (amount, adminShare, referralCommisionAmount); } /** * @dev Calculates the results of a challenge based on the final outcome and updates the participants' balances accordingly. * This internal function takes the final outcome of a challenge and determines the winners and losers, redistributing the * pooled amounts between participants based on their initial bets. It ensures that the winnings are proportionally distributed * to the winners from the total amount bet by the losers. * * @param challengeId The unique identifier of the challenge to calculate results for. * @param finalOutcome The final outcome of the challenge represented as a uint8 value. A value of `1` indicates * that the original "for" side wins, while `2` indicates that the "against" side wins. * * The function performs the following steps: * - Identifies the winning and losing sides based on `finalOutcome`. * - Calculates the total winning amount for each winning participant based on their bet proportion. * - Updates the `_withdrawables` mapping to reflect the winnings for each winning participant. * - Prepares data for the losing participants but does not adjust their balances as their amounts are considered lost. * * Emits a `ChallengeFundsMoved` event indicating the redistribution of funds following the challenge's conclusion. * This event provides detailed arrays of winning and losing users, alongside the amounts won or lost. * * Requirements: * - The challenge identified by `challengeId` must exist within the `_challenges` mapping. * - The `finalOutcome` must correctly reflect the challenge's outcome, with `1` for a win by the original "for" side * and `2` for a win by the "against" side. * * Notes: * - This function is critical for ensuring fair payout to the winners based on the total amount bet by the losers. * - It assumes that the `finalOutcome` has been determined by an external process or oracle that is not part of this function. */ function _calculateChallenge(uint256 challengeId, uint8 finalOutcome) internal { Challenge storage _challenge = _challenges[challengeId]; address challengeToken = _challenge.token; uint256 adminShare; // Determine the arrays of winning and losing users, and their respective amounts address[] storage usersWin = _challenge.usersFor; address[] storage usersLose = _challenge.usersAgainst; uint256 winAmount = _challenge.amountFor; uint256 loseAmount = _challenge.amountAgainst; if (finalOutcome == 2) { // If final outcome is lose, swap win and lose arrays (usersWin, usersLose) = (usersLose, usersWin); (winAmount, loseAmount) = (loseAmount, winAmount); } uint256 usersWinLength = usersWin.length; uint256 usersLoseLength = usersLose.length; uint256[] memory winAmounts = new uint256[](usersWinLength); address[] memory referrers = new address[](usersWinLength + usersLoseLength); uint256[] memory referrelCommissions = new uint256[](usersWinLength + usersLoseLength); uint256 j = 0; // Distribute winnings to winning users for (uint256 i = 0; i < usersWinLength; ++i) { address user = usersWin[i]; UserBet storage bet = _userChallengeBets[user][challengeId]; uint256 userWinAmount = bet.amount + ((loseAmount * bet.amount) / winAmount); winAmounts[i] = userWinAmount; referrers[j] = bet.referrer; referrelCommissions[j] = bet.referralCommision; _withdrawables[user][challengeToken] += userWinAmount; _withdrawables[bet.referrer][challengeToken] += bet.referralCommision; adminShare += bet.adminShare; ++j; } uint256[] memory loseAmounts = new uint256[](usersLoseLength); // Record losing amounts for (uint256 i = 0; i < usersLoseLength; ++i) { UserBet storage bet = _userChallengeBets[usersLose[i]][challengeId]; loseAmounts[i] = bet.amount; referrers[j] = bet.referrer; referrelCommissions[j] = bet.referralCommision; _withdrawables[bet.referrer][challengeToken] += bet.referralCommision; adminShare += bet.adminShare; ++j; } _withdrawables[owner()][challengeToken] += adminShare; // Emit event for funds distribution emit ReferralsEarned(challengeId, challengeToken, referrers, referrelCommissions); emit AdminReceived(challengeId, challengeToken, adminShare); emit ChallengeFundsMoved( challengeId, usersWin, winAmounts, usersLose, loseAmounts, MethodType.ResolveChallenge, challengeToken ); } /** * @dev Cancels a user's participation in a given challenge, refunding their bet and updating the challenge's state. * This internal function handles the cancellation process for both individual and group challenges. * It adjusts the challenge's total bet amount and participant list based on the user's decision (for or against). * Additionally, it increments the user's withdrawable balance by the amount of their canceled bet. * * @param user The address of the user whose participation is being canceled. * @param challengeId The unique identifier of the challenge from which the user is withdrawing. * * The function performs the following operations: * - Identifies whether the user was betting for or against the challenge, or if it's a group challenge. * - Removes the user from the appropriate participant list (`usersFor` or `usersAgainst`) and adjusts the challenge's * total amount for or against accordingly. * - Increases the user's withdrawable balance by the amount of their bet. * - Emits a `CancelParticipation` event signaling the user's cancellation from the challenge. * - Emits a `ChallengeFundsMoved` event to indicate the movement of funds due to the cancellation, for consistency and tracking. * * Notes: * - This function is designed to work with both individual and group challenges, modifying the challenge's state * to reflect the user's cancellation and ensuring the integrity of the challenge's betting totals. * - It utilizes the `contains` function to find the user's position in the participant lists and handles their removal efficiently. * - The adjustment of the challenge's betting totals and participant lists is crucial for maintaining accurate and fair * challenge outcomes and balances. */ function _cancelParticipation(address user, uint256 challengeId, uint8 cancelType) internal { Challenge storage _challenge = _challenges[challengeId]; address challengeToken = _challenge.token; uint256 usersForLength = _challenge.usersFor.length; uint256 usersAgainstLength = _challenge.usersAgainst.length; address[] memory users = new address[](1); uint256[] memory amounts = new uint256[](1); address[] memory referrers = new address[](1); uint256[] memory referrelCommissions = new uint256[](1); uint256 amount = _userChallengeBets[user][challengeId].amount; uint256 adminShare = _userChallengeBets[user][challengeId].adminShare; uint256 referralCommision = _userChallengeBets[user][challengeId].referralCommision; address referrer = _userChallengeBets[user][challengeId].referrer; if ( (_challenge.challengeType == ChallengeType.Individual && _userChallengeBets[user][challengeId].decision == 1) || _challenge.challengeType == ChallengeType.Group ) { // If user is for the challenge or it's a group challenge, handle accordingly uint256 i = contains(_challenge.usersFor, user); if (cancelType == 1) { amount += adminShare + referralCommision; } else { _withdrawables[owner()][challengeToken] += adminShare; _withdrawables[referrer][challengeToken] += referralCommision; } _withdrawables[user][challengeToken] += amount; _challenge.amountFor -= amount; _challenge.usersFor[i] = _challenge.usersFor[usersForLength - 1]; _challenge.usersFor.pop(); } else { // If user is against the challenge, handle accordingly uint256 i = contains(_challenge.usersAgainst, user); if (cancelType == 1) { amount += adminShare + referralCommision; referralCommision = 0; adminShare = 0; } else { _withdrawables[owner()][challengeToken] += adminShare; _withdrawables[referrer][challengeToken] += referralCommision; } _withdrawables[user][challengeToken] += amount; _challenge.amountAgainst -= amount; _challenge.usersAgainst[i] = _challenge.usersAgainst[usersAgainstLength - 1]; _challenge.usersAgainst.pop(); } // Prepare data for event emission users[0] = user; amounts[0] = amount; referrers[0] = referrer; referrelCommissions[0] = referralCommision; // Clear user's bet for the challenge delete _userChallengeBets[user][challengeId]; // Emit events for cancellation of participation and fund movement emit CancelParticipation(user, challengeId); emit ReferralsEarned(challengeId, _challenge.token, referrers, referrelCommissions); emit AdminReceived(challengeId, _challenge.token, adminShare); emit ChallengeFundsMoved( challengeId, users, amounts, new address[](0), new uint256[](0), MethodType.CancelParticipation, _challenge.token ); } /** * @dev Calculates and allocates winnings and losses for a group challenge. * This internal function determines the amounts won by each winning user and the amounts lost by each losing user * within a challenge. It updates the `_withdrawables` mapping to reflect the winnings for each winning user based * on their share of the profits. Losing users' bet amounts are noted but not immediately acted upon in this function. * * @param challengeId The unique identifier of the challenge being calculated. * @param usersWin An array of addresses for users who won in the challenge. * @param profits An array of profit percentages corresponding to each winning user. * * Requirements: * - `usersWin` and `profits` arrays must be of the same length, with each entry in `profits` representing * the percentage of the total winnings that the corresponding user in `usersWin` should receive. * - This function does not directly handle the transfer of funds but updates the `_withdrawables` mapping to * reflect the amounts that winning users are able to withdraw. * - Losing users' details are aggregated but are used primarily for event emission. * * Emits a `ChallengeFundsMoved` event indicating the challenge ID, winning users and their win amounts, * and losing users with the amounts they bet and lost. This helps in tracking the outcome and settlements * of group challenges. * * Note: * - The actual transfer of funds from losing to winning users is not performed in this function. Instead, it calculates * and updates balances that users can later withdraw. */ function _calculateGroupChallenge( uint256 challengeId, address[] calldata usersWin, uint256[] calldata profits ) internal { Challenge storage _challenge = _challenges[challengeId]; address challengeToken = _challenge.token; uint256 userWinLength = usersWin.length; address[] storage usersFor = _challenge.usersFor; uint256 challengeUserForLength = usersFor.length; uint256[] memory winAmounts = new uint256[](userWinLength); uint256[] memory loseAmounts = new uint256[](challengeUserForLength - userWinLength); address[] memory usersLose = new address[](challengeUserForLength - userWinLength); address[] memory referrers = new address[](challengeUserForLength); uint256[] memory referrelCommissions = new uint256[](challengeUserForLength); uint256 j = 0; uint256 adminShare; for (uint256 i = 0; i < challengeUserForLength; ++i) { uint256 index = contains(usersWin, _challenge.usersFor[i]); UserBet storage bet = _userChallengeBets[usersFor[i]][challengeId]; if (index == userWinLength) { usersLose[j] = usersFor[i]; loseAmounts[j] = bet.amount; j++; } else { uint256 winAmount = (_challenge.amountFor * profits[index]) / (100 * DECIMAL); _withdrawables[usersWin[index]][challengeToken] += winAmount; winAmounts[index] = winAmount; } adminShare += bet.adminShare; _withdrawables[bet.referrer][_challenge.token] += bet.referralCommision; referrers[i] = bet.referrer; referrelCommissions[i] = bet.referralCommision; } _withdrawables[owner()][_challenge.token] += adminShare; // Emit event for fund movement in the group challenge emit ReferralsEarned(challengeId, challengeToken, referrers, referrelCommissions); emit AdminReceived(challengeId, challengeToken, adminShare); emit ChallengeFundsMoved( challengeId, usersWin, winAmounts, usersLose, loseAmounts, MethodType.ResolveGroupChallenge, challengeToken ); } /** * @dev Searches for an element in an address array and returns its index if found. * This internal pure function iterates through an array of addresses to find a specified element. * It's designed to check the presence of an address in a given array and identify its position. * * @param array The array of addresses to search through. * @param element The address to search for within the array. * @return The index of the element within the array if found; otherwise, returns the length of the array. * This means that if the return value is equal to the array's length, the element is not present in the array. */ function contains(address[] memory array, address element) internal pure returns (uint256) { uint256 arrayLength = array.length; for (uint256 i = 0; i < arrayLength; ++i) { if (array[i] == element) { return i; } } return arrayLength; } /** * @dev Cancels all bets placed on a challenge, refunding the bet amounts to the bettors. * This internal function handles the process of cancelling bets for both "for" and "against" positions in a given challenge. * It aggregates users and their respective bet amounts from both positions, updates their withdrawable balances, * and emits an event indicating the movement of funds due to the challenge's cancellation. * * The function iterates through all bets placed "for" and "against" the challenge, compiles lists of users and their bet amounts, * and credits the bet amounts back to the users' withdrawable balances in the form of the challenge's token. * * @param challengeId The unique identifier of the challenge whose bets are to be cancelled. * * Emits a `ChallengeFundsMoved` event with details about the challengeId, users involved, their refunded amounts, * and empty arrays for new users and new amounts as no new bets are created during the cancellation process. * * Requirements: * - The function is internal and expected to be called in scenarios where a challenge needs to be cancelled, such as * when a challenge is deemed invalid or when conditions for the challenge's execution are not met. * - It assumes that `_challenges` maps `challengeId` to a valid `Challenge` struct containing arrays of users who have bet "for" and "against". * - The function updates `_withdrawables`, a mapping of user addresses to another mapping of token addresses and their withdrawable amounts, ensuring users can withdraw their bet amounts after the bets are cancelled. */ function _cancelBets(uint256 challengeId, uint8 cancelType) internal { Challenge storage _challenge = _challenges[challengeId]; address challengeToken = _challenge.token; uint256 usersForLength = _challenge.usersFor.length; uint256 usersAgainstLength = _challenge.usersAgainst.length; address[] memory users = new address[](usersForLength + usersAgainstLength); uint256[] memory amounts = new uint256[](usersForLength + usersAgainstLength); address[] memory referrers = new address[](usersForLength + usersAgainstLength); uint256[] memory referrelCommissions = new uint256[](usersForLength + usersAgainstLength); uint256 j = 0; uint256 totalAdminShare = 0; for (uint256 i = 0; i < usersForLength; ++i) { address user = _challenge.usersFor[i]; users[i] = user; uint256 returnAmount = _userChallengeBets[user][challengeId].amount; uint256 adminShare = _userChallengeBets[user][challengeId].adminShare; uint256 referralCommision = _userChallengeBets[user][challengeId].referralCommision; address referrer = _userChallengeBets[user][challengeId].referrer; if (cancelType == 1) { returnAmount += adminShare + referralCommision; adminShare = 0; referralCommision = 0; } else { _withdrawables[owner()][challengeToken] += adminShare; _withdrawables[referrer][challengeToken] += referralCommision; } amounts[i] = returnAmount; referrers[j] = referrer; referrelCommissions[j] = referralCommision; _withdrawables[user][challengeToken] += amounts[i]; totalAdminShare += adminShare; ++j; } for (uint256 i = 0; i < usersAgainstLength; ++i) { address user = _challenge.usersAgainst[i]; uint256 index = i + usersForLength; users[index] = user; uint256 returnAmount = _userChallengeBets[user][challengeId].amount; uint256 adminShare = _userChallengeBets[user][challengeId].adminShare; uint256 referralCommision = _userChallengeBets[user][challengeId].referralCommision; address referrer = _userChallengeBets[user][challengeId].referrer; if (cancelType == 1) { returnAmount += adminShare + referralCommision; adminShare = 0; referralCommision = 0; } else { _withdrawables[owner()][challengeToken] += adminShare; _withdrawables[referrer][challengeToken] += referralCommision; } amounts[index] = returnAmount; referrers[j] = referrer; referrelCommissions[j] = referralCommision; _withdrawables[user][challengeToken] += amounts[index]; totalAdminShare += adminShare; ++j; } emit ReferralsEarned(challengeId, challengeToken, referrers, referrelCommissions); emit AdminReceived(challengeId, challengeToken, totalAdminShare); emit ChallengeFundsMoved( challengeId, users, amounts, new address[](0), new uint256[](0), MethodType.CancelChallenge, challengeToken ); } /** * @dev Withdraws an amount of native cryptocurrency (e.g., ETH) or an ERC-20 token and sends it to a specified address. * This internal function handles the transfer of both native cryptocurrency and ERC-20 tokens based on the token address provided. * If the `token` parameter is the zero address, it treats the transfer as a native cryptocurrency transaction. * Otherwise, it performs a safe transfer of an ERC-20 token. * * @param token The address of the token to withdraw. If the address is `0x0`, the withdrawal is processed as a native cryptocurrency transaction. * @param to The recipient address to which the currency or tokens are sent. * @param amount The amount of currency or tokens to send. The function ensures that this amount is securely transferred to the `to` address. * * Requirements: * - For native cryptocurrency transfers: * - The transaction must succeed. If it fails, the function reverts with "Failed to send ETH". * - For ERC-20 token transfers: * - The function uses `safeTransfer` from the IERC20 interface to prevent issues related to double spending or errors in transfer. * - The ERC-20 token contract must implement `safeTransfer` correctly according to the ERC-20 standard. */ function _withdraw(address token, address to, uint256 amount) internal { if (token == address(0)) { // Native cryptocurrency transfer (bool ok, ) = to.call{value: amount}(""); require(ok, "37"); } else { // ERC-20 token transfer IERC20(token).safeTransfer(to, amount); } } /** * @dev Calculates the administrator's share of a challenge based on the challenge's token and the amount. * This internal view function determines the admin's share by first converting the `amount` of the challenge's * token into a standardized value (using `_getValue` function to get the token's value in a common denomination). * It then uses this value to find the applicable admin share percentage from a predefined set of rules (`_adminShareRules`). * * @param token The token of the challenge to calculate the value amount. * @param amount The amount involved in the challenge for which the admin's share is to be calculated. * @return The calculated admin share as a uint256, based on the challenge's conditions and predefined rules. * * Logic: * - Determines the value of the `amount` of tokens by fetching the token's current value and adjusting for decimal places. * - Uses the calculated value to find the corresponding admin share percentage from `_adminShareRules`. * - The share is computed based on thresholds which determine the percentage rate applicable to the value amount. * - If the value amount does not meet the minimum threshold, the function returns 0, indicating no admin share. * - If applicable, the admin share is calculated by multiplying the `amount` by the determined percentage * and dividing by `PERCENTAGE_100` to ensure the result is in the correct scale. * * Requirements: * - The function dynamically adjusts for the token's decimals, using 18 decimals for the native currency (e.g., ETH) or * querying the token contract for ERC-20 tokens. * - It handles special cases, such as when the token is the platform's specific token (e.g. STMX), * by applying predefined rules for calculating the admin share. */ function _calculateAdminShare(address token, uint256 amount) internal view returns (uint256) { require(_allTokens.contains(token), "48"); uint256 valueAmount = (_getValue(token) * amount) / 10 ** (token == address(0) ? 18 : token.decimals()); AdminShareRule storage rule = _adminShareRules[token]; uint256 index = rule.thresholds.upperBound(valueAmount); if (index == 0) { return 0; } // Get the admin share in USD for the corresponding threshold uint256 shareInUSD = rule.sharesInUSD[index - 1]; if (rule.isSTMX) { return shareInUSD; } // Convert the USD share back into the equivalent token amount uint256 adminShareInTokens = (shareInUSD * 10 ** (token == address(0) ? 18 : token.decimals())) / _getValue(token); return adminShareInTokens; } /** * @dev Retrieves the current value of a given token, based on oracle data. * This internal view function queries the value of the specified token from a price feed oracle. * If the token is recognized by a preset list of oracles (_oraclessTokens), it returns a default value. * Otherwise, it fetches the latest round data from the token's associated price feed. * The function requires that the oracle's reported value be positive and updated within the last day, * indicating no oracle malfunction. * It adjusts the oracle's value based on a default decimal precision, to ensure consistency across different oracles. * * @param token The address of the token for which the value is being queried. * @return The current value of the token as a uint256, adjusted for default decimal precision. * The value is adjusted to match the `defaultOracleDecimals` precision if necessary. * * Requirements: * - The oracle's latest value for the token must be positive and updated within the last 24 hours. * - If the token is not recognized by the _oraclessTokens set, but has a price feed, the function normalizes the * value to a standard decimal precision (defaultOracleDecimals) for consistency. * - Throws "Oracle malfunction" if the oracle's latest data does not meet the requirements. */ function _getValue(address token) internal view returns (uint256) { int256 value; uint256 updatedAt; if (_oraclessTokens.contains(token)) { value = int256(10 ** defaultOracleDecimals); } else { (, value, , updatedAt, ) = _priceFeeds[token].latestRoundData(); require(value > 0 && updatedAt >= block.timestamp - 1 days, "43"); uint256 oracleDecimals = _priceFeeds[token].decimals(); if (oracleDecimals > defaultOracleDecimals) { value = value / int256(10 ** (oracleDecimals - defaultOracleDecimals)); } else if (oracleDecimals < defaultOracleDecimals) { value = value * int256(10 ** (defaultOracleDecimals - oracleDecimals)); } } return uint256(value); } /** * @dev Determines the current status of a specific challenge by its ID. * This internal view function assesses the challenge's status based on its current state and timing. * It checks if the challenge is in a final state (Canceled, ResolvedFor, ResolvedAgainst, or ResolvedDraw). * If not, it then checks whether the challenge's end time has passed to determine if it's in the Awaiting state. * Otherwise, it defaults to the Betting state, implying that the challenge is still active and accepting bets. * * @param challengeId The unique identifier for the challenge whose status is being queried. * @return ChallengeStatus The current status of the challenge. This can be one of the following: * - Canceled: The challenge has been canceled. * - ResolvedFor: The challenge has been resolved in favor of the proposer. * - ResolvedAgainst: The challenge has been resolved against the proposer. * - ResolvedDraw: The challenge has been resolved as a draw. * - Awaiting: The challenge is awaiting resolution, but betting is closed due to the end time having passed. * - Betting: The challenge is open for bets. */ function _challengeStatus(uint256 challengeId) internal view returns (ChallengeStatus) { ChallengeStatus status = _challenges[challengeId].status; uint256 endTime = _challenges[challengeId].endTime; if ( status == ChallengeStatus.Canceled || status == ChallengeStatus.ResolvedFor || status == ChallengeStatus.ResolvedAgainst || status == ChallengeStatus.ResolvedDraw ) { return status; } if (block.timestamp > endTime) { return ChallengeStatus.Awaiting; } return ChallengeStatus.Betting; } /** * @dev Checks if the challenge id is valid or not. * * @param challengeId The unique identifier for the challenge. */ function _assertChallengeExistence(uint256 challengeId) internal view { require(challengeId > 0 && challengeId <= latestChallengeId, "38"); } /** * @dev Checks if the challenge is resolved or not. * * @param challengeId The unique identifier for the challenge. */ function _assertResolveableStatus(uint256 challengeId) internal view { require(_challengeStatus(challengeId) == ChallengeStatus.Awaiting, "39"); } /** * @dev Checks if the challenge is canceled or not. * * @param challengeId The unique identifier for the challenge. */ function _assertCancelableStatus(uint256 challengeId) internal view { ChallengeStatus status = _challengeStatus(challengeId); require(status == ChallengeStatus.Awaiting || status == ChallengeStatus.Betting, "40"); } /** * @dev Ensures that the function is only callable by the designated backend address. * This internal view function checks if the `msg.sender` is the same as the stored `backend` address. * It should be used as a modifier in functions that are meant to be accessible only by the backend. * Reverts with a "Not a backend" error message if the `msg.sender` is not the backend address. */ function _onlyBackend() internal view { require(msg.sender == backend, "41"); } /** * @dev Ensures that the function is only callable by the designated backend address or owner address. * This internal view function checks if the `msg.sender` is the same as the stored `backend` address or owner address. * It should be used as a modifier in functions that are meant to be accessible by the backend and owner. * Reverts with a "Not a backend or owner" error message if the `msg.sender` is neither the backend address, nor the owner address. */ function _onlyBackendOrOwner() internal view { require(msg.sender == backend || msg.sender == owner(), "42"); } /** * @dev Overrides the renounceOwnership function to disable the ability to renounce ownership. * This ensures that the contract always has an owner. */ function renounceOwnership() public view override onlyOwner { revert("Renouncing ownership is disabled"); } /** * @dev Checks if the price feed from a given address is valid within a specified error margin. * @param priceFeedAddress The address of the price feed. * @param errorMarginPercent The acceptable error margin in percentage. * @return bool Returns true if the price feed is valid, otherwise reverts. */ function isValidPriceFeed( address priceFeedAddress, uint256 errorMarginPercent ) internal view returns (bool) { AggregatorV3Interface priceFeed = AggregatorV3Interface(priceFeedAddress); (, int256 price, , uint256 updatedAt, ) = priceFeed.latestRoundData(); require(price > 0, "19"); require(block.timestamp - updatedAt <= 1 days, "25"); int256 expectedPrice = getExpectedPrice(priceFeedAddress); int256 lowerBound = expectedPrice - ((expectedPrice * int256(errorMarginPercent)) / 100); int256 upperBound = expectedPrice + ((expectedPrice * int256(errorMarginPercent)) / 100); require(price >= lowerBound && price <= upperBound, "27"); return true; } /** * @dev Computes the expected price from the historical price feed data. * @param priceFeedAddress The address of the price feed. * @return int256 The average price calculated from the last few rounds. */ function getExpectedPrice(address priceFeedAddress) internal view returns (int256) { AggregatorV3Interface priceFeed = AggregatorV3Interface(priceFeedAddress); // Fetch the latest round data (uint80 roundID, , , , ) = priceFeed.latestRoundData(); // Calculate the average price over the last few rounds int256 sum = 0; uint256 count = 0; uint80 currentRoundId = roundID; // Assuming we want to average the last 5 rounds uint256 roundsToAverage = 5; for (uint256 i = 0; i < roundsToAverage; ++i) { (, int256 historicalPrice, , , ) = priceFeed.getRoundData(currentRoundId); sum += historicalPrice; count += 1; if (currentRoundId > 0) { currentRoundId -= 1; } else { break; } } int256 averagePrice = sum / int256(count); return averagePrice; } }
// 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 v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. 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 { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol) pragma solidity ^0.8.0; import "./Ownable.sol"; /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2Step is Ownable { address private _pendingOwner; event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return _pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual override onlyOwner { _pendingOwner = newOwner; emit OwnershipTransferStarted(owner(), newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { delete _pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() public virtual { address sender = _msgSender(); require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner"); _transferOwnership(sender); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer(address from, address to, uint256 amount) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. 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.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.2) (utils/cryptography/MerkleProof.sol) pragma solidity ^0.8.0; /** * @dev These functions deal with verification of Merkle Tree proofs. * * The tree and the proofs can be generated using our * https://github.com/OpenZeppelin/merkle-tree[JavaScript library]. * You will find a quickstart guide in the readme. * * WARNING: You should avoid using leaf values that are 64 bytes long prior to * hashing, or use a hash function other than keccak256 for hashing leaves. * This is because the concatenation of a sorted pair of internal nodes in * the merkle tree could be reinterpreted as a leaf value. * OpenZeppelin's JavaScript library generates merkle trees that are safe * against this attack out of the box. */ library MerkleProof { /** * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree * defined by `root`. For this, a `proof` must be provided, containing * sibling hashes on the branch from the leaf to the root of the tree. Each * pair of leaves and each pair of pre-images are assumed to be sorted. */ function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProof(proof, leaf) == root; } /** * @dev Calldata version of {verify} * * _Available since v4.7._ */ function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) { return processProofCalldata(proof, leaf) == root; } /** * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt * hash matches the root of the tree. When processing the proof, the pairs * of leafs & pre-images are assumed to be sorted. * * _Available since v4.4._ */ function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Calldata version of {processProof} * * _Available since v4.7._ */ function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) { bytes32 computedHash = leaf; for (uint256 i = 0; i < proof.length; i++) { computedHash = _hashPair(computedHash, proof[i]); } return computedHash; } /** * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a merkle tree defined by * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}. * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function multiProofVerify( bytes32[] memory proof, bool[] memory proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProof(proof, proofFlags, leaves) == root; } /** * @dev Calldata version of {multiProofVerify} * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function multiProofVerifyCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32 root, bytes32[] memory leaves ) internal pure returns (bool) { return processMultiProofCalldata(proof, proofFlags, leaves) == root; } /** * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false * respectively. * * CAUTION: Not all merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer). * * _Available since v4.7._ */ function processMultiProof( bytes32[] memory proof, bool[] memory proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof"); // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { require(proofPos == proofLen, "MerkleProof: invalid multiproof"); unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } /** * @dev Calldata version of {processMultiProof}. * * CAUTION: Not all merkle trees admit multiproofs. See {processMultiProof} for details. * * _Available since v4.7._ */ function processMultiProofCalldata( bytes32[] calldata proof, bool[] calldata proofFlags, bytes32[] memory leaves ) internal pure returns (bytes32 merkleRoot) { // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of // the merkle tree. uint256 leavesLen = leaves.length; uint256 proofLen = proof.length; uint256 totalHashes = proofFlags.length; // Check proof validity. require(leavesLen + proofLen - 1 == totalHashes, "MerkleProof: invalid multiproof"); // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop". bytes32[] memory hashes = new bytes32[](totalHashes); uint256 leafPos = 0; uint256 hashPos = 0; uint256 proofPos = 0; // At each step, we compute the next hash using two values: // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we // get the next hash. // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the // `proof` array. for (uint256 i = 0; i < totalHashes; i++) { bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]; bytes32 b = proofFlags[i] ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++]) : proof[proofPos++]; hashes[i] = _hashPair(a, b); } if (totalHashes > 0) { require(proofPos == proofLen, "MerkleProof: invalid multiproof"); unchecked { return hashes[totalHashes - 1]; } } else if (leavesLen > 0) { return leaves[0]; } else { return proof[0]; } } function _hashPair(bytes32 a, bytes32 b) private pure returns (bytes32) { return a < b ? _efficientHash(a, b) : _efficientHash(b, a); } function _efficientHash(bytes32 a, bytes32 b) private pure returns (bytes32 value) { /// @solidity memory-safe-assembly assembly { mstore(0x00, a) mstore(0x20, b) value := keccak256(0x00, 0x40) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; /** * @notice A simple library to work with arrays */ library ArrayHelper { /** * @notice The function that searches for the index of the first occurring element, which is * greater than or equal to the `element_`. The time complexity is O(log n) * @param array the array to search in * @param element_ the element * @return index_ the index of the found element or the length of the `array` if no such element */ function lowerBound( uint256[] storage array, uint256 element_ ) internal view returns (uint256 index_) { (uint256 low_, uint256 high_) = (0, array.length); while (low_ < high_) { uint256 mid_ = Math.average(low_, high_); if (array[mid_] >= element_) { high_ = mid_; } else { low_ = mid_ + 1; } } return high_; } /** * @notice The function that searches for the index of the first occurring element, which is * greater than the `element_`. The time complexity is O(log n) * @param array the array to search in * @param element_ the element * @return index_ the index of the found element or the length of the `array` if no such element */ function upperBound( uint256[] storage array, uint256 element_ ) internal view returns (uint256 index_) { (uint256 low_, uint256 high_) = (0, array.length); while (low_ < high_) { uint256 mid_ = Math.average(low_, high_); if (array[mid_] > element_) { high_ = mid_; } else { low_ = mid_ + 1; } } return high_; } /** * @notice The function that calculates the sum of all array elements from `beginIndex_` to * `endIndex_` inclusive using its prefix sum array * @param beginIndex_ the index of the first range element * @param endIndex_ the index of the last range element * @return the sum of all elements of the range */ function getRangeSum( uint256[] storage prefixes, uint256 beginIndex_, uint256 endIndex_ ) internal view returns (uint256) { require(beginIndex_ <= endIndex_, "ArrayHelper: wrong range"); if (beginIndex_ == 0) { return prefixes[endIndex_]; } return prefixes[endIndex_] - prefixes[beginIndex_ - 1]; } /** * @notice The function to compute the prefix sum array * @param arr_ the initial array to be turned into the prefix sum array * @return prefixes_ the prefix sum array */ function countPrefixes( uint256[] memory arr_ ) internal pure returns (uint256[] memory prefixes_) { if (arr_.length == 0) { return prefixes_; } prefixes_ = new uint256[](arr_.length); prefixes_[0] = arr_[0]; for (uint256 i = 1; i < prefixes_.length; i++) { prefixes_[i] = prefixes_[i - 1] + arr_[i]; } } /** * @notice The function to reverse an array * @param arr_ the array to reverse * @return reversed_ the reversed array */ function reverse(uint256[] memory arr_) internal pure returns (uint256[] memory reversed_) { reversed_ = new uint256[](arr_.length); uint256 i = arr_.length; while (i > 0) { i--; reversed_[arr_.length - 1 - i] = arr_[i]; } } function reverse(address[] memory arr_) internal pure returns (address[] memory reversed_) { reversed_ = new address[](arr_.length); uint256 i = arr_.length; while (i > 0) { i--; reversed_[arr_.length - 1 - i] = arr_[i]; } } function reverse(string[] memory arr_) internal pure returns (string[] memory reversed_) { reversed_ = new string[](arr_.length); uint256 i = arr_.length; while (i > 0) { i--; reversed_[arr_.length - 1 - i] = arr_[i]; } } function reverse(bytes32[] memory arr_) internal pure returns (bytes32[] memory reversed_) { reversed_ = new bytes32[](arr_.length); uint256 i = arr_.length; while (i > 0) { i--; reversed_[arr_.length - 1 - i] = arr_[i]; } } /** * @notice The function to insert an array into the other array * @param to_ the array to insert into * @param index_ the insertion starting index * @param what_ the array to be inserted * @return the index to start the next insertion from */ function insert( uint256[] memory to_, uint256 index_, uint256[] memory what_ ) internal pure returns (uint256) { for (uint256 i = 0; i < what_.length; i++) { to_[index_ + i] = what_[i]; } return index_ + what_.length; } function insert( address[] memory to_, uint256 index_, address[] memory what_ ) internal pure returns (uint256) { for (uint256 i = 0; i < what_.length; i++) { to_[index_ + i] = what_[i]; } return index_ + what_.length; } function insert( string[] memory to_, uint256 index_, string[] memory what_ ) internal pure returns (uint256) { for (uint256 i = 0; i < what_.length; i++) { to_[index_ + i] = what_[i]; } return index_ + what_.length; } function insert( bytes32[] memory to_, uint256 index_, bytes32[] memory what_ ) internal pure returns (uint256) { for (uint256 i = 0; i < what_.length; i++) { to_[index_ + i] = what_[i]; } return index_ + what_.length; } /** * @notice The function that free memory that was allocated for array * @param array_ the array to crop * @param newLength_ the new length of the array * @return ref to cropped array */ function crop( uint256[] memory array_, uint256 newLength_ ) internal pure returns (uint256[] memory) { if (newLength_ < array_.length) { assembly { mstore(array_, newLength_) } } return array_; } function crop( address[] memory array_, uint256 newLength_ ) internal pure returns (address[] memory) { if (newLength_ < array_.length) { assembly { mstore(array_, newLength_) } } return array_; } function crop(bool[] memory array_, uint256 newLength_) internal pure returns (bool[] memory) { if (newLength_ < array_.length) { assembly { mstore(array_, newLength_) } } return array_; } function crop( string[] memory array_, uint256 newLength_ ) internal pure returns (string[] memory) { if (newLength_ < array_.length) { assembly { mstore(array_, newLength_) } } return array_; } function crop( bytes32[] memory array_, uint256 newLength_ ) internal pure returns (bytes32[] memory) { if (newLength_ < array_.length) { assembly { mstore(array_, newLength_) } } return array_; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import {StringSet} from "../data-structures/StringSet.sol"; /** * @notice A simple library to work with sets */ library SetHelper { using EnumerableSet for EnumerableSet.UintSet; using EnumerableSet for EnumerableSet.AddressSet; using StringSet for StringSet.Set; /** * @notice The function to insert an array of elements into the set * @param set the set to insert the elements into * @param array_ the elements to be inserted */ function add(EnumerableSet.AddressSet storage set, address[] memory array_) internal { for (uint256 i = 0; i < array_.length; i++) { set.add(array_[i]); } } function add(EnumerableSet.UintSet storage set, uint256[] memory array_) internal { for (uint256 i = 0; i < array_.length; i++) { set.add(array_[i]); } } function add(StringSet.Set storage set, string[] memory array_) internal { for (uint256 i = 0; i < array_.length; i++) { set.add(array_[i]); } } /** * @notice The function to remove an array of elements from the set * @param set the set to remove the elements from * @param array_ the elements to be removed */ function remove(EnumerableSet.AddressSet storage set, address[] memory array_) internal { for (uint256 i = 0; i < array_.length; i++) { set.remove(array_[i]); } } function remove(EnumerableSet.UintSet storage set, uint256[] memory array_) internal { for (uint256 i = 0; i < array_.length; i++) { set.remove(array_[i]); } } function remove(StringSet.Set storage set, string[] memory array_) internal { for (uint256 i = 0; i < array_.length; i++) { set.remove(array_[i]); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /** * @notice ## Usage example: * * ``` * using StringSet for StringSet.Set; * * StringSet.Set internal set; * ``` */ library StringSet { struct Set { string[] _values; mapping(string => uint256) _indexes; } /** * @notice The function add value to set * @param set the set object * @param value_ the value to add */ function add(Set storage set, string memory value_) internal returns (bool) { if (!contains(set, value_)) { set._values.push(value_); set._indexes[value_] = set._values.length; return true; } else { return false; } } /** * @notice The function remove value to set * @param set the set object * @param value_ the value to remove */ function remove(Set storage set, string memory value_) internal returns (bool) { uint256 valueIndex_ = set._indexes[value_]; if (valueIndex_ != 0) { uint256 toDeleteIndex_ = valueIndex_ - 1; uint256 lastIndex_ = set._values.length - 1; if (lastIndex_ != toDeleteIndex_) { string memory lastValue_ = set._values[lastIndex_]; set._values[toDeleteIndex_] = lastValue_; set._indexes[lastValue_] = valueIndex_; } set._values.pop(); delete set._indexes[value_]; return true; } else { return false; } } /** * @notice The function returns true if value in the set * @param set the set object * @param value_ the value to search in set * @return true if value is in the set, false otherwise */ function contains(Set storage set, string memory value_) internal view returns (bool) { return set._indexes[value_] != 0; } /** * @notice The function returns length of set * @param set the set object * @return the the number of elements in the set */ function length(Set storage set) internal view returns (uint256) { return set._values.length; } /** * @notice The function returns value from set by index * @param set the set object * @param index_ the index of slot in set * @return the value at index */ function at(Set storage set, uint256 index_) internal view returns (string memory) { return set._values[index_]; } /** * @notice The function that returns values the set stores, can be very expensive to call * @param set the set object * @return the memory array of values */ function values(Set storage set) internal view returns (string[] memory) { return set._values; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import {ERC20, IERC20, IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; /** * @notice This library is used to convert numbers that use token's N decimals to M decimals. * Comes extremely handy with standardizing the business logic that is intended to work with many different ERC20 tokens * that have different precision (decimals). One can perform calculations with 18 decimals only and resort to convertion * only when the payouts (or interactions) with the actual tokes have to be made. * * The best usage scenario involves accepting and calculating values with 18 decimals throughout the project, despite the tokens decimals. * * Also it is recommended to call `round18()` function on the first execution line in order to get rid of the * trailing numbers if the destination decimals are less than 18 * * ## Usage example: * * ``` * contract Taker { * ERC20 public USDC; * uint256 public paid; * * . . . * * function pay(uint256 amount) external { * uint256 decimals = USDC.decimals(); * amount = amount.round18(decimals); * * paid += amount; * USDC.transferFrom(msg.sender, address(this), amount.from18(decimals)); * } * } * ``` */ library DecimalsConverter { /** * @notice The function to get the decimals of ERC20 token. Needed for bytecode optimization * @param token_ the ERC20 token * @return the decimals of provided token */ function decimals(address token_) internal view returns (uint8) { return ERC20(token_).decimals(); } /** * @notice The function to bring the number to 18 decimals of precision * @param amount_ the number to convert * @param token_ the token, whose decimals will be precised to 18 * @return the number brought to 18 decimals of precision */ function to18(uint256 amount_, address token_) internal view returns (uint256) { return to18(amount_, decimals(token_)); } /** * @notice The function to bring the number to 18 decimals of precision * @param amount_ the number to convert * @param baseDecimals_ the current precision of the number * @return the number brought to 18 decimals of precision */ function to18(uint256 amount_, uint256 baseDecimals_) internal pure returns (uint256) { return convert(amount_, baseDecimals_, 18); } /** * @notice The function to bring the number to 18 decimals of precision. Reverts if output is zero * @param amount_ the number to convert * @param token_ the token, whose decimals will be precised to 18 * @return the number brought to 18 decimals of precision */ function to18Safe(uint256 amount_, address token_) internal view returns (uint256) { return to18Safe(amount_, decimals(token_)); } /** * @notice The function to bring the number to 18 decimals of precision. Reverts if output is zero * @param amount_ the number to convert * @param baseDecimals_ the current precision of the number * @return the number brought to 18 decimals of precision */ function to18Safe(uint256 amount_, uint256 baseDecimals_) internal pure returns (uint256) { return _convertSafe(amount_, baseDecimals_, _to18); } /** * @notice The function to bring the number from 18 decimals to the desired decimals of precision * @param amount_ the number to covert * @param token_ the token, whose decimals will be used as desired decimals of precision * @return the number brought from 18 to desired decimals of precision */ function from18(uint256 amount_, address token_) internal view returns (uint256) { return from18(amount_, decimals(token_)); } /** * @notice The function to bring the number from 18 decimals to the desired decimals of precision * @param amount_ the number to covert * @param destDecimals_ the desired precision decimals * @return the number brought from 18 to desired decimals of precision */ function from18(uint256 amount_, uint256 destDecimals_) internal pure returns (uint256) { return convert(amount_, 18, destDecimals_); } /** * @notice The function to bring the number from 18 decimals to the desired decimals of precision. * Reverts if output is zero * @param amount_ the number to covert * @param token_ the token, whose decimals will be used as desired decimals of precision * @return the number brought from 18 to desired decimals of precision */ function from18Safe(uint256 amount_, address token_) internal view returns (uint256) { return from18Safe(amount_, decimals(token_)); } /** * @notice The function to bring the number from 18 decimals to the desired decimals of precision. * Reverts if output is zero * @param amount_ the number to covert * @param destDecimals_ the desired precision decimals * @return the number brought from 18 to desired decimals of precision */ function from18Safe(uint256 amount_, uint256 destDecimals_) internal pure returns (uint256) { return _convertSafe(amount_, destDecimals_, _from18); } /** * @notice The function to substitute the trailing digits of a number with zeros * @param amount_ the number to round. Should be with 18 precision decimals * @param decimals_ the required number precision * @return the rounded number. Comes with 18 precision decimals */ function round18(uint256 amount_, uint256 decimals_) internal pure returns (uint256) { return to18(from18(amount_, decimals_), decimals_); } /** * @notice The function to substitute the trailing digits of a number with zeros. Reverts if output is zero * @param amount_ the number to round. Should be with 18 precision decimals * @param decimals_ the required number precision * @return the rounded number. Comes with 18 precision decimals */ function round18Safe(uint256 amount_, uint256 decimals_) internal pure returns (uint256) { return _convertSafe(amount_, decimals_, round18); } /** * @notice The function to do the token precision convertion * @param amount_ the amount to convert * @param baseToken_ current token * @param destToken_ desired token * @return the converted number */ function convert( uint256 amount_, address baseToken_, address destToken_ ) internal view returns (uint256) { return convert(amount_, uint256(decimals(baseToken_)), uint256(decimals(destToken_))); } /** * @notice The function to do the precision convertion * @param amount_ the amount to covert * @param baseDecimals_ current number precision * @param destDecimals_ desired number precision * @return the converted number */ function convert( uint256 amount_, uint256 baseDecimals_, uint256 destDecimals_ ) internal pure returns (uint256) { if (baseDecimals_ > destDecimals_) { amount_ = amount_ / 10 ** (baseDecimals_ - destDecimals_); } else if (baseDecimals_ < destDecimals_) { amount_ = amount_ * 10 ** (destDecimals_ - baseDecimals_); } return amount_; } /** * @notice The function to do the token precision convertion. Reverts if output is zero * @param amount_ the amount to convert * @param baseToken_ current token * @param destToken_ desired token * @return the converted number */ function convertTokensSafe( uint256 amount_, address baseToken_, address destToken_ ) internal view returns (uint256) { return _convertTokensSafe(amount_, baseToken_, destToken_, _convertTokens); } /** * @notice The function to bring the number to 18 decimals of precision * @param amount_ the number to convert * @param baseDecimals_ the current precision of the number * @return the number brought to 18 decimals of precision */ function _to18(uint256 amount_, uint256 baseDecimals_) private pure returns (uint256) { return convert(amount_, baseDecimals_, 18); } /** * @notice The function to bring the number from 18 decimals to the desired decimals of precision * @param amount_ the number to covert * @param destDecimals_ the desired precision decimals * @return the number brought from 18 to desired decimals of precision */ function _from18(uint256 amount_, uint256 destDecimals_) private pure returns (uint256) { return convert(amount_, 18, destDecimals_); } /** * @notice The function to do the token precision convertion * @param amount_ the amount to convert * @param baseToken_ current token * @param destToken_ desired token * @return the converted number */ function _convertTokens( uint256 amount_, address baseToken_, address destToken_ ) private view returns (uint256) { return convert(amount_, uint256(decimals(baseToken_)), uint256(decimals(destToken_))); } /** * @notice The function wrapper to do the safe precision convertion. Reverts if output is zero * @param amount_ the amount to covert * @param decimals_ the precision decimals * @param _convertFunc the internal function pointer to "from", "to", or "round" functions * @return conversionResult_ the convertion result */ function _convertSafe( uint256 amount_, uint256 decimals_, function(uint256, uint256) internal pure returns (uint256) _convertFunc ) private pure returns (uint256 conversionResult_) { conversionResult_ = _convertFunc(amount_, decimals_); require(conversionResult_ > 0, "DecimalsConverter: conversion failed"); } /** * @notice The function wrapper to do the safe precision convertion for ERC20 tokens. Reverts if output is zero * @param amount_ the amount to covert * @param baseToken_ current token * @param destToken_ desired token * @param _convertFunc the internal function pointer to "from", "to", or "round" functions * @return conversionResult_ the convertion result */ function _convertTokensSafe( uint256 amount_, address baseToken_, address destToken_, function(uint256, address, address) internal view returns (uint256) _convertFunc ) private view returns (uint256 conversionResult_) { conversionResult_ = _convertFunc(amount_, baseToken_, destToken_); require(conversionResult_ > 0, "DecimalsConverter: conversion failed"); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; /** * @notice This library simplifies non-obvious type castings */ library TypeCaster { /** * @notice The function that casts the list of `X`-type elements to the list of uint256 * @param from_ the list of `X`-type elements * @return array_ the list of uint256 */ function asUint256Array( bytes32[] memory from_ ) internal pure returns (uint256[] memory array_) { assembly { array_ := from_ } } function asUint256Array( address[] memory from_ ) internal pure returns (uint256[] memory array_) { assembly { array_ := from_ } } /** * @notice The function that casts the list of `X`-type elements to the list of addresses * @param from_ the list of `X`-type elements * @return array_ the list of addresses */ function asAddressArray( bytes32[] memory from_ ) internal pure returns (address[] memory array_) { assembly { array_ := from_ } } function asAddressArray( uint256[] memory from_ ) internal pure returns (address[] memory array_) { assembly { array_ := from_ } } /** * @notice The function that casts the list of `X`-type elements to the list of bytes32 * @param from_ the list of `X`-type elements * @return array_ the list of bytes32 */ function asBytes32Array( uint256[] memory from_ ) internal pure returns (bytes32[] memory array_) { assembly { array_ := from_ } } function asBytes32Array( address[] memory from_ ) internal pure returns (bytes32[] memory array_) { assembly { array_ := from_ } } /** * @notice The function to transform an element into an array * @param from_ the element * @return array_ the element as an array */ function asSingletonArray(uint256 from_) internal pure returns (uint256[] memory array_) { array_ = new uint256[](1); array_[0] = from_; } function asSingletonArray(address from_) internal pure returns (address[] memory array_) { array_ = new address[](1); array_[0] = from_; } function asSingletonArray(bool from_) internal pure returns (bool[] memory array_) { array_ = new bool[](1); array_[0] = from_; } function asSingletonArray(string memory from_) internal pure returns (string[] memory array_) { array_ = new string[](1); array_[0] = from_; } function asSingletonArray(bytes32 from_) internal pure returns (bytes32[] memory array_) { array_ = new bytes32[](1); array_[0] = from_; } /** * @notice The function to convert static array to dynamic * @param static_ the static array to convert * @return dynamic_ the converted dynamic array */ function asDynamic( uint256[1] memory static_ ) internal pure returns (uint256[] memory dynamic_) { return asSingletonArray(static_[0]); } function asDynamic( uint256[2] memory static_ ) internal pure returns (uint256[] memory dynamic_) { dynamic_ = new uint256[](2); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 2); } function asDynamic( uint256[3] memory static_ ) internal pure returns (uint256[] memory dynamic_) { dynamic_ = new uint256[](3); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 3); } function asDynamic( uint256[4] memory static_ ) internal pure returns (uint256[] memory dynamic_) { dynamic_ = new uint256[](4); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 4); } function asDynamic( uint256[5] memory static_ ) internal pure returns (uint256[] memory dynamic_) { dynamic_ = new uint256[](5); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 5); } function asDynamic( address[1] memory static_ ) internal pure returns (address[] memory dynamic_) { return asSingletonArray(static_[0]); } function asDynamic( address[2] memory static_ ) internal pure returns (address[] memory dynamic_) { dynamic_ = new address[](2); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 2); } function asDynamic( address[3] memory static_ ) internal pure returns (address[] memory dynamic_) { dynamic_ = new address[](3); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 3); } function asDynamic( address[4] memory static_ ) internal pure returns (address[] memory dynamic_) { dynamic_ = new address[](4); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 4); } function asDynamic( address[5] memory static_ ) internal pure returns (address[] memory dynamic_) { dynamic_ = new address[](5); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 5); } function asDynamic(bool[1] memory static_) internal pure returns (bool[] memory dynamic_) { return asSingletonArray(static_[0]); } function asDynamic(bool[2] memory static_) internal pure returns (bool[] memory dynamic_) { dynamic_ = new bool[](2); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 2); } function asDynamic(bool[3] memory static_) internal pure returns (bool[] memory dynamic_) { dynamic_ = new bool[](3); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 3); } function asDynamic(bool[4] memory static_) internal pure returns (bool[] memory dynamic_) { dynamic_ = new bool[](4); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 4); } function asDynamic(bool[5] memory static_) internal pure returns (bool[] memory dynamic_) { dynamic_ = new bool[](5); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 5); } function asDynamic(string[1] memory static_) internal pure returns (string[] memory dynamic_) { return asSingletonArray(static_[0]); } function asDynamic(string[2] memory static_) internal pure returns (string[] memory dynamic_) { dynamic_ = new string[](2); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 2); } function asDynamic(string[3] memory static_) internal pure returns (string[] memory dynamic_) { dynamic_ = new string[](3); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 3); } function asDynamic(string[4] memory static_) internal pure returns (string[] memory dynamic_) { dynamic_ = new string[](4); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 4); } function asDynamic(string[5] memory static_) internal pure returns (string[] memory dynamic_) { dynamic_ = new string[](5); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 5); } function asDynamic( bytes32[1] memory static_ ) internal pure returns (bytes32[] memory dynamic_) { return asSingletonArray(static_[0]); } function asDynamic( bytes32[2] memory static_ ) internal pure returns (bytes32[] memory dynamic_) { dynamic_ = new bytes32[](2); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 2); } function asDynamic( bytes32[3] memory static_ ) internal pure returns (bytes32[] memory dynamic_) { dynamic_ = new bytes32[](3); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 3); } function asDynamic( bytes32[4] memory static_ ) internal pure returns (bytes32[] memory dynamic_) { dynamic_ = new bytes32[](4); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 4); } function asDynamic( bytes32[5] memory static_ ) internal pure returns (bytes32[] memory dynamic_) { dynamic_ = new bytes32[](5); uint256 pointerS_; uint256 pointerD_; assembly { pointerS_ := static_ pointerD_ := dynamic_ } _copy(pointerS_, pointerD_, 5); } function _copy(uint256 locationS_, uint256 locationD_, uint256 length_) private pure { assembly { for { let i := 0 } lt(i, length_) { i := add(i, 1) } { locationD_ := add(locationD_, 0x20) mstore(locationD_, mload(locationS_)) locationS_ := add(locationS_, 0x20) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.4; uint256 constant PRECISION = 10 ** 25; uint256 constant DECIMAL = 10 ** 18; uint256 constant PERCENTAGE_100 = 10 ** 27;
//SPDX-License-Identifier: MIT pragma solidity ^0.8.19; /** * @title IP2PSports * @dev Interface for a peer-to-peer sports betting platform. * This interface defines the basic events, enums, and structs required for creating, joining, resolving, * and canceling challenges, as well as managing and withdrawing bets and admin shares in a decentralized sports betting platform. */ interface IP2PSports { /** * @dev Emitted when the backend address changes. * @param backend The new backend address. * @param by The address of the user who changed the backend address. */ event BackendChanged(address backend, address by); /** * @dev Emitted when the merkle root is updated. * @param root The new merkle root. * @param by The address of the user who updated the merkle root. */ event MerkleRootUpdated(bytes32 root, address by); /** * @dev Emitted when the maximum number of challengers is updated. * @param maxChallengersEachSide The maximum number of challengers on each side. * @param maxChallengersForPickem The maximum number of challengers for pickem. * @param by The address of the user who updated the maximum number of challengers. */ event MaxChallengersUpdated( uint256 maxChallengersEachSide, uint256 maxChallengersForPickem, address by ); /** * @dev Emitted when the minimum USD bet amount is updated. * @param amount The new minimum USD bet amount. * @param by The address of the user who updated the minimum USD bet amount. */ event MinUSDBettingAmountUpdated(uint256 amount, address by); /** * @dev Emitted when the membership application status is updated. * @param value The new membership application status (true or false). * @param by The address of the user who updated the membership application status. */ event MembershipApplied(bool value, address by); /** * @dev Emitted when betting is enabled or disabled. * @param value The new betting status (true or false). * @param by The address of the user who updated the betting status. */ event BettingAllowed(bool value, address by); /** * @dev Emitted when the admin share rules are updated. * @param adminShareRules The new admin share rules. * @param by The address of the user who updated the admin share rules. */ event AdminShareRulesUpdated(AdminShareRule adminShareRules, address by); /** * @dev Emitted when an amount is debited in SC. * @param amount The amount to be debited. * @param by The address of the user who initiated the debit. */ event DebitedInSC(uint256 amount, address by); /** * @dev Emitted when a token is allowed. * @param tokens The addresses of the allowed tokens. * @param priceFeeds The addresses of the price feeds for the tokens. * @param minBetAmounts The minimum bet amounts for the tokens. * @param by The address of the user who allowed the tokens. */ event TokenAllowed( address[] tokens, address[] priceFeeds, uint256[] minBetAmounts, address by ); /** * @dev Emitted when tokens are restricted. * @param tokens The addresses of the restricted tokens. * @param by The address of the user who restricted the tokens. */ event TokenRestricted(address[] tokens, address by); /** * @dev Emitted when a new challenge is created. * @param challengeId Unique identifier for the challenge. * @param token Address of the token used for betting. * @param by Address of the user who created the challenge. * @param inputStakedQty The original amount input by user, without any deductions */ event ChallengeCreated(uint256 challengeId, address token, address by, uint256 inputStakedQty); /** * @dev Emitted when a user joins an existing challenge. * @param challengeId Unique identifier for the challenge. * @param amount Amount of the token bet by the user. * @param by Address of the user who joined the challenge. * @param inputStakedQty The original amount input by user, without any deductions * @param token Address of the token used for betting. */ event ChallengeJoined( uint256 challengeId, uint256 amount, address by, address token, uint256 inputStakedQty ); /** * @dev Emitted when a user increases amount for an already joined challenge. * @param challengeId Unique identifier for the challenge. * @param increasedAmount Amount that is added to the previous amount of the user for the specified bet. * @param newTotalAmount The new total amount of the user's participation in the bet. * @param by Address of the user who increased the challenge amount. * @param token Address of the token used for betting. */ event BetAmountIncreased( uint256 challengeId, uint256 increasedAmount, uint256 newTotalAmount, address by, address token ); /** * @dev Emitted when a challenge is resolved. * @param challengeId Unique identifier for the challenge. * @param finalOutcome Final outcome of the challenge (1 for win, 2 for loss, etc.). */ event ChallengeResolved(uint256 challengeId, uint8 finalOutcome); /** * @dev Emitted when a challenge is canceled. * @param challengeId Unique identifier for the canceled challenge. */ event ChallengeCanceled(uint256 challengeId); /** * @dev Emitted when a user cancels their participation in a challenge. * @param user Address of the user canceling their participation. * @param challengeId Unique identifier for the challenge. */ event CancelParticipation(address user, uint256 challengeId); /** * @dev Emitted after the resolution of a challenge, detailing the redistribution of funds. * @param challengeId Unique identifier for the challenge. * @param winners Array of addresses of the winning users. * @param winnersProfit Array of profits earned by each winning user. * @param losers Array of addresses of the losing users. * @param losersLoss Array of amounts lost by each losing user. */ event ChallengeFundsMoved( uint256 challengeId, address[] winners, uint256[] winnersProfit, address[] losers, uint256[] losersLoss, MethodType mothodType, address token ); /** * @dev Emitted when a user withdraws their winnings or funds. * @param token Address of the token being withdrawn. * @param amount Amount of the token being withdrawn. * @param by Address of the user performing the withdrawal. */ event UserWithdrawn(address token, uint256 amount, address by); /** * @dev Emitted when the admin shares is calculated from challenge participation fees. * @param challengeId Unique identifier for the challenge from which the fees were taken. * @param token Address of the token in which the fees were paid. * @param amount Amount of the fees received. */ event AdminShareCalculated(uint256 challengeId, address token, uint256 amount); /** * @dev Emitted when the admin receives a share from challenge participation fees. * @param challengeId Unique identifier for the challenge from which the fees were taken. * @param token Address of the token in which the fees were paid. * @param amount Amount of the fees received. */ event AdminReceived(uint256 challengeId, address token, uint256 amount); /** * @dev Emitted when the referrel commission is earned by the referrer from challenge participation fees. * @param challengeId Unique identifier for the challenge from which the fees were taken. * @param token Address of the token in which the fees were paid. * @param referrers addresses of the referrers. * @param referrelCommissions Amount of the fees received. */ event ReferralsEarned( uint256 challengeId, address token, address[] referrers, uint256[] referrelCommissions ); /** * @dev Emitted when the admin withdraws their accumulated shares. * @param token Address of the token being withdrawn. * @param amount Amount of the token being withdrawn. */ event AdminWithdrawn(address token, uint256 amount); /** * @dev Enum for tracking the status of a challenge. */ enum ChallengeStatus { None, CanBeCreated, Betting, Awaiting, Canceled, ResolvedFor, ResolvedAgainst, ResolvedDraw } /** * @dev Enum for distinguishing between individual and group challenges. */ enum ChallengeType { Individual, Group } /** * @dev Enum for the functions in which the fund are being distributed. */ enum MethodType { ResolveChallenge, ResolveGroupChallenge, CancelChallenge, CancelParticipation } /** * @dev Struct for storing details about a challenge. */ struct Challenge { address token; // Token used for betting. address[] usersFor; // Users betting for the outcome. address[] usersAgainst; // Users betting against the outcome. uint256 amountFor; // Total amount bet for the outcome. uint256 amountAgainst; // Total amount bet against the outcome. ChallengeStatus status; // Current status of the challenge. ChallengeType challengeType; // Type of challenge (individual or group). uint256 startTime; // Start time of the challenge. uint256 endTime; // End time of the challenge. } /** * @dev Struct for storing a user's bet on a challenge. */ struct UserBet { uint256 amount; // Amount of the bet. uint8 decision; // User's decision (for or against). uint256 adminShare; //Admin's share calculated for this bet amount address referrer; uint256 referralCommision; } /** * @dev Struct for defining admin share rules based on bet thresholds. */ struct AdminShareRule { uint256[] thresholds; // Bet amount thresholds for different share percentages. uint256[] sharesInUSD; // Admin share in USD for corresponding thresholds. bool isSTMX; //To define if this is an STMX or some other } /** * External Methods */ /** @dev Emits a `ChallengeCreated` event and calls `joinChallenge` for the challenge creator. * @param token Address of the token used for betting (zero address for native currency) * @param amountFromWallet Amount to be bet from the creator's wallet * @param amountFromWithdrawables Amount to be bet from the creator's withdrawable balance * @param decision The side of the bet the creator is taking * @param challengeType The type of challenge (Individual or Group) * @param startTime Start time of the challenge * @param endTime End time of the challenge * @param membershipLevel user membership level * @param feePercentage percentage amount reduced from admin share * @param referrer referrer address * @param referralCommision referral will get the comission from admin share * @param proof leaf nood proof */ function createChallenge( address token, uint256 amountFromWallet, uint256 amountFromWithdrawables, uint8 decision, ChallengeType challengeType, uint256 startTime, uint256 endTime, uint8 membershipLevel, uint256 feePercentage, address referrer, uint256 referralCommision, bytes32[] memory proof ) external payable; /** @dev This function allows users to withdraw their available tokens from the contract. It uses the * nonReentrant modifier from OpenZeppelin to prevent reentrancy attacks. A `UserWithdrawn` event is * emitted upon a successful withdrawal. * @param token The address of the token to be withdrawn. Use the zero address for the native currency. */ function withdraw(address token) external; /** @dev This function is called by the backend to resolve challenges that have reached their end time * and are in the awaiting status. It updates the status of each challenge based on its final outcome. * Only challenges of type `Individual` can be resolved using this function. A `ChallengeResolved` event is * emitted for each challenge that is resolved. This function uses the `onlyBackend` modifier to ensure * that only authorized backend addresses can call it, and `nonReentrant` to prevent reentrancy attacks. * @param challengeIds Array of IDs of the challenges to be resolved. * @param finalOutcomes Array of final outcomes for each challenge, where outcomes are defined as follows: * - 1: Side A wins, * - 2: Side B wins, * - 3: Draw. */ function resolveChallenge( uint256[] memory challengeIds, uint8[] memory finalOutcomes ) external; /** @dev This function allows the backend to cancel a user's participation in a challenge, refunding their bet. * It can only be called by the backend and is protected against reentrancy attacks. The function checks if the * challenge exists and ensures that the challenge is either in the `Awaiting` or `Betting` status, implying that * it has not been resolved yet. Additionally, it verifies that the user has indeed placed a bet on the challenge. * After these checks, it calls an internal function `_cancelParticipation` to handle the logic for cancelling the * user's participation and processing the refund. * @param user The address of the user whose participation is to be cancelled. * @param challengeId The ID of the challenge from which the user's participation is to be cancelled. */ function cancelParticipation(address user, uint256 challengeId, uint8 cancelType) external; /** @dev This function is used for resolving group challenges specifically, where multiple participants can win. * It can only be executed by the backend and is protected against reentrancy. The function ensures that the * challenge exists, is currently awaiting resolution, and is of the `Group` challenge type. It then validates * that the lengths of the winners and profits arrays match and do not exceed the maximum number of winners allowed. * Each winner's address must have participated in the challenge, and winners must be unique. The total of the profits * percentages must equal 100. Once validated, the challenge status is updated, and profits are calculated and * distributed to the winners based on the provided profits percentages. * @param challengeId The ID of the group challenge to resolve. * @param winners An array of addresses of the winners of the challenge. * @param profits An array of profit percentages corresponding to each winner, summing to 100. */ function resolveGroupChallenge( uint256 challengeId, address[] calldata winners, uint256[] calldata profits ) external; /** @dev This function allows the backend to cancel a challenge if it's either awaiting resolution or still open for betting. * It ensures that the challenge exists and is in a cancelable state (either `Awaiting` or `Betting`). Upon cancellation, * the challenge's status is updated to `Canceled`, and all bets placed on the challenge are refunded to the participants. * This function is protected by the `onlyBackend` modifier to restrict access to the backend address, and `nonReentrant` * to prevent reentrancy attacks. * @param challengeId The ID of the challenge to be cancelled. * @param cancelType 0-Return bet amount without admin shares 1-Return bet amount with admin shares. */ function cancelChallenge(uint256 challengeId, uint8 cancelType) external; /** @dev This function allows the contract owner to enable or disable betting across the platform. * It's a straightforward toggle that sets the `bettingAllowed` state variable based on the input. * Access to this function is restricted to the contract owner through the `onlyOwner` modifier from * OpenZeppelin's Ownable contract, ensuring that only the owner can change the betting policy. * @param value_ A boolean indicating whether betting should be allowed (`true`) or not (`false`). */ function allowBetting(bool value_) external; /** @dev This function will allow the owner to toggle the apply membership values * @param value_ true to apply membership values and false for disable membership values */ function updateApplyMembershipValues(bool value_) external; /** @dev Can only be called by the contract owner. * @param value_ The new minimum betting amount in USD. */ function changeMinUSDBettingAmount(uint256 value_) external; /** @dev This function allows the contract owner to change the backend address to a new one. * Ensures the new backend address is not the zero address to prevent rendering the contract unusable. * The function is protected by the `onlyOwner` modifier, ensuring that only the contract owner has the authority * to update the backend address. This is crucial for maintaining the integrity and security of the contract's * administrative functions. * @param backend_ The new address to be set as the backend. It must be a non-zero address. */ function changeBackend(address backend_) external; /** @dev This function is designed to adjust the timing of a challenge, allowing the backend to * modify the start and end times as necessary. It's particularly useful for correcting mistakes * or accommodating changes in event schedules. The function checks for the existence of the challenge * and validates that the new end time is indeed after the new start time to maintain logical consistency. * Access is restricted to the backend through the `onlyBackend` modifier to ensure that only authorized * personnel can make such adjustments. * @param challengeId The ID of the challenge whose timings are to be changed. * @param startTime The new start time for the challenge. * @param endTime The new end time for the challenge. */ // function changeChallengeTime( // uint256 challengeId, // uint256 startTime, // uint256 endTime // ) external; /** @dev This function enables the contract owner to restrict certain tokens from being used in betting activities. * It involves removing tokens from the list of allowed tokens, potentially removing them from the list of tokens * without a Chainlink price feed (oracless tokens), and deleting their associated price feeds if any were set. * This is a crucial administrative function for managing the tokens that can be used on the platform, allowing * for adjustments based on compliance, liquidity, or other operational considerations. * Execution is restricted to the contract's owner through the `onlyOwner` modifier, ensuring that token restrictions * can only be imposed by authorized parties. * @param tokens An array of token addresses that are to be restricted from use in betting. */ function restrictTokens(address[] memory tokens) external; /** @dev Allows the contract owner to define how administrative shares (a portion of betting winnings) are calculated. * This can be configured differently for the STMX token versus other tokens, as indicated by the `isSTMX` flag. * Each entry in the `thresholds` and `percentages` arrays defines a tier: if the winnings fall into a certain threshold, * the corresponding percentage is applied as the administrative share. The function enforces ascending order for thresholds * and ensures that the share percentages do not exceed a maximum limit. This setup allows for flexible configuration * of administrative fees based on the amount won. * Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only they can set these rules. * @param thresholds An array of threshold values, each representing the lower bound of a winnings bracket. * @param percentages An array of percentages corresponding to each threshold, defining the admin share for that bracket. * @param token Token address. * @param isSTMX A boolean flag indicating whether these rules apply to the STMX token (`true`) or other tokens (`false`). */ function setAdminShareRules( uint256[] memory thresholds, uint256[] memory percentages, address token, bool isSTMX ) external; /** * Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only they can set these rules. * @param _maxChallengersEachSide maximun limit of challengers can join in each side. * @param _maxChallengersForPickem maximun limit of challengers can join for pickem. */ function updateMaxChallengers( uint256 _maxChallengersEachSide, uint256 _maxChallengersForPickem ) external; /** * Access is restricted to the contract owner through the `onlyOwner` modifier, ensuring that only owner can deposit amount to SC. * @param _amount amount of tokens. * @param _token token address. */ function debitInSC(uint256 _amount, address _token) external payable; /** @dev This function provides external access to the administrative share rules that have been set up for either * the STMX token (if `isSTMX` is true) or for other tokens (if `isSTMX` is false). These rules define the thresholds * and corresponding percentages that determine how administrative shares are calculated from betting winnings. * The function returns two arrays: one for the thresholds and one for the percentages, which together outline the * structure of admin shares based on the amount of winnings. * @param token A boolean flag indicating whether to retrieve the rules for the STMX token (`true`) or other tokens (`false`). * @return thresholds An array of uint256 representing the winnings thresholds for admin shares calculation. * @return percentages An array of uint256 representing the admin share percentages for each corresponding threshold. */ function getAdminShareRules( address token ) external view returns (uint256[] memory thresholds, uint256[] memory percentages, bool isSTMX); /** @dev This function provides external visibility into which tokens are currently permitted for use in betting within the platform. * It leverages the EnumerableSet library from OpenZeppelin to handle the dynamic array of addresses representing the allowed tokens. * This is particularly useful for interfaces or external contracts that need to verify or display the tokens users can bet with. * @return An array of addresses, each representing a token that is allowed for betting. */ function getAllowedTokens() external view returns (address[] memory); /** @dev This function provides access to the details of a given challenge, including its current status, which is * dynamically determined based on the challenge's timing and resolution state. It's essential for external callers * to be able to retrieve comprehensive data on a challenge, such as its participants, status, and betting amounts, * to properly interact with or display information about the challenge. The function checks that the requested * challenge exists before attempting to access its details. * @param challengeId The unique identifier of the challenge for which details are requested. * @return challengeDetails A `Challenge` struct containing all relevant data about the challenge, including an updated status. * * Requirements: * - The challenge must exist, as indicated by its ID being within the range of created challenges. */ function getChallengeDetails( uint256 challengeId ) external view returns (Challenge memory challengeDetails); /** @dev This function allows anyone to view the details of a bet made by a user on a specific challenge, * including the amount bet and the side the user has chosen. It's crucial for enabling users or interfaces * to confirm the details of participation in challenges and to understand the stakes involved. This function * directly accesses the mapping of user bets based on the user address and challenge ID, returning the * corresponding `UserBet` struct. * @param challengeId The ID of the challenge for which the bet details are being queried. * @param user The address of the user whose bet details are requested. * @return A `UserBet` struct containing the amount of the bet and the decision (side chosen) by the user for the specified challenge. */ function getUserBet(uint256 challengeId, address user) external view returns (UserBet memory); /** @dev This function compiles a comprehensive view of all tokens that a user has available to withdraw, * including winnings, refunds, or other credits due to the user. It iterates over the entire list of tokens * recognized by the contract (not just those currently allowed for betting) to ensure that users can access * any funds owed to them, regardless of whether a token's betting status has changed. This is essential for * maintaining transparency and access to funds for users within the platform. * @param user The address of the user for whom withdrawable balances are being queried. * @return tokens An array of token addresses, representing each token that the user has a balance of. * @return amounts An array of uint256 values, each corresponding to the balance of the token at the same index in the `tokens` array. */ function getUserWithdrawables( address user ) external view returns (address[] memory tokens, uint256[] memory amounts); /** @dev Emits a `ChallengeJoined` event if the join is successful. * @param challengeId ID of the challenge to join * @param amountFromWallet Amount to be bet from the user's wallet * @param amountFromWithdrawables Amount to be bet from the user's withdrawable balance * @param decision The side of the bet the user is taking * @param membershipLevel user membership level * @param feePercentage percentage amount reduced from admin share * @param referrer referrer address * @param referralCommision referral will get the comission from admin share * @param proof leaf nood proof */ function joinChallenge( uint256 challengeId, uint256 amountFromWallet, uint256 amountFromWithdrawables, uint8 decision, uint8 membershipLevel, uint256 feePercentage, address referrer, uint256 referralCommision, bytes32[] memory proof ) external payable; /** @dev Emits a `BetAmountIncreased` event if the join is successful. * @param challengeId ID of the challenge for which user wants to increase the bet amount * @param amountFromWallet Amount to be bet from the user's wallet * @param amountFromWithdrawables Amount to be bet from the user's withdrawable balance * @param membershipLevel user membership level * @param feePercentage percentage amount reduced from admin share * @param referrer referrer address * @param referralCommision referral will get the comission from admin share * @param proof leaf nood proof */ function increaseBetAmount( uint256 challengeId, uint256 amountFromWallet, uint256 amountFromWithdrawables, uint8 membershipLevel, uint256 feePercentage, address referrer, uint256 referralCommision, bytes32[] memory proof ) external payable; /** @dev A challenge is considered to exist if its ID is greater than 0 and less than or equal to the latest challenge ID. * @param challengeId The ID of the challenge to check. * @return bool Returns true if the challenge exists, false otherwise. */ function challengeExists(uint256 challengeId) external view returns (bool); /** @dev This function will allow the owner to update the root node of merkle tree * @param _root root node of merkle tree */ function updateRoot(bytes32 _root) external; /** @dev This function permits the contract owner to add tokens to the list of those allowed for betting. * It also associates Chainlink price feeds with tokens, enabling the conversion of bets to a common value basis for calculations. * Tokens without a specified price feed (address(0)) are considered to have fixed or known values and are added to a separate list. * The function ensures that each token in the input array has a corresponding price feed address (which can be the zero address). * The `onlyOwner` modifier restricts this function's execution to the contract's owner, safeguarding against unauthorized token addition. * @param tokens An array of token addresses to be allowed for betting. * @param priceFeeds An array of Chainlink price feed addresses corresponding to the tokens. Use address(0) for tokens without a need for price feeds. * @param minBetAmounts An array of amount corresponding to every token being allowed, the value for oracless tokens will be considers only in this method. * Requirements: * - The lengths of the `tokens` and `priceFeeds` arrays must match to ensure each token has a corresponding price feed address. */ function allowTokens( address[] memory tokens, address[] memory priceFeeds, uint256[] memory minBetAmounts ) external; }
{ "optimizer": { "enabled": true, "runs": 200, "details": { "yul": true } }, "viaIR": true, "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":"backend_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"challengeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AdminReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"challengeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AdminShareCalculated","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256[]","name":"thresholds","type":"uint256[]"},{"internalType":"uint256[]","name":"sharesInUSD","type":"uint256[]"},{"internalType":"bool","name":"isSTMX","type":"bool"}],"indexed":false,"internalType":"struct IP2PSports.AdminShareRule","name":"adminShareRules","type":"tuple"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"AdminShareRulesUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"AdminWithdrawn","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"backend","type":"address"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"BackendChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"challengeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"increasedAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newTotalAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"BetAmountIncreased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"value","type":"bool"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"BettingAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256","name":"challengeId","type":"uint256"}],"name":"CancelParticipation","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"challengeId","type":"uint256"}],"name":"ChallengeCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"challengeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"uint256","name":"inputStakedQty","type":"uint256"}],"name":"ChallengeCreated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"challengeId","type":"uint256"},{"indexed":false,"internalType":"address[]","name":"winners","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"winnersProfit","type":"uint256[]"},{"indexed":false,"internalType":"address[]","name":"losers","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"losersLoss","type":"uint256[]"},{"indexed":false,"internalType":"enum IP2PSports.MethodType","name":"mothodType","type":"uint8"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"ChallengeFundsMoved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"challengeId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"by","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"inputStakedQty","type":"uint256"}],"name":"ChallengeJoined","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"challengeId","type":"uint256"},{"indexed":false,"internalType":"uint8","name":"finalOutcome","type":"uint8"}],"name":"ChallengeResolved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"DebitedInSC","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"maxChallengersEachSide","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"maxChallengersForPickem","type":"uint256"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"MaxChallengersUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"value","type":"bool"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"MembershipApplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"root","type":"bytes32"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"MerkleRootUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"MinUSDBettingAmountUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","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":"challengeId","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address[]","name":"referrers","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"referrelCommissions","type":"uint256[]"}],"name":"ReferralsEarned","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"address[]","name":"priceFeeds","type":"address[]"},{"indexed":false,"internalType":"uint256[]","name":"minBetAmounts","type":"uint256[]"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"TokenAllowed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address[]","name":"tokens","type":"address[]"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"TokenRestricted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"by","type":"address"}],"name":"UserWithdrawn","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"value_","type":"bool"}],"name":"allowBetting","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"address[]","name":"priceFeeds","type":"address[]"},{"internalType":"uint256[]","name":"minBetAmounts","type":"uint256[]"}],"name":"allowTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"applyMembershipValues","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"awaitingTimeForPublicCancel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"backend","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bettingAllowed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"challengeId","type":"uint256"},{"internalType":"uint8","name":"cancelType","type":"uint8"}],"name":"cancelChallenge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"challengeId","type":"uint256"},{"internalType":"uint8","name":"cancelType","type":"uint8"}],"name":"cancelParticipation","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"challengeId","type":"uint256"}],"name":"challengeExists","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"backend_","type":"address"}],"name":"changeBackend","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"value_","type":"uint256"}],"name":"changeMinUSDBettingAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amountFromWallet","type":"uint256"},{"internalType":"uint256","name":"amountFromWithdrawables","type":"uint256"},{"internalType":"uint8","name":"decision","type":"uint8"},{"internalType":"enum IP2PSports.ChallengeType","name":"challengeType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint8","name":"membershipLevel","type":"uint8"},{"internalType":"uint256","name":"feePercentage","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"referralCommision","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"createChallenge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_token","type":"address"}],"name":"debitInSC","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"defaultOracleDecimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"getAdminShareRules","outputs":[{"internalType":"uint256[]","name":"thresholds","type":"uint256[]"},{"internalType":"uint256[]","name":"sharesInUSD","type":"uint256[]"},{"internalType":"bool","name":"isSTMX","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllowedTokens","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"challengeId","type":"uint256"}],"name":"getChallengeDetails","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address[]","name":"usersFor","type":"address[]"},{"internalType":"address[]","name":"usersAgainst","type":"address[]"},{"internalType":"uint256","name":"amountFor","type":"uint256"},{"internalType":"uint256","name":"amountAgainst","type":"uint256"},{"internalType":"enum IP2PSports.ChallengeStatus","name":"status","type":"uint8"},{"internalType":"enum IP2PSports.ChallengeType","name":"challengeType","type":"uint8"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"}],"internalType":"struct IP2PSports.Challenge","name":"challengeDetails","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"challengeId","type":"uint256"},{"internalType":"address","name":"user","type":"address"}],"name":"getUserBet","outputs":[{"components":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint8","name":"decision","type":"uint8"},{"internalType":"uint256","name":"adminShare","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"referralCommision","type":"uint256"}],"internalType":"struct IP2PSports.UserBet","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"}],"name":"getUserWithdrawables","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"challengeId","type":"uint256"},{"internalType":"uint256","name":"amountFromWallet","type":"uint256"},{"internalType":"uint256","name":"amountFromWithdrawables","type":"uint256"},{"internalType":"uint8","name":"membershipLevel","type":"uint8"},{"internalType":"uint256","name":"feePercentage","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"referralCommision","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"increaseBetAmount","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"uint256","name":"challengeId","type":"uint256"},{"internalType":"uint256","name":"amountFromWallet","type":"uint256"},{"internalType":"uint256","name":"amountFromWithdrawables","type":"uint256"},{"internalType":"uint8","name":"decision","type":"uint8"},{"internalType":"uint8","name":"membershipLevel","type":"uint8"},{"internalType":"uint256","name":"feePercentage","type":"uint256"},{"internalType":"address","name":"referrer","type":"address"},{"internalType":"uint256","name":"referralCommision","type":"uint256"},{"internalType":"bytes32[]","name":"proof","type":"bytes32[]"}],"name":"joinChallenge","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"latestChallengeId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAdminShareInUsd","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAdminShareSTMX","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxAdminShareThresholds","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxChallengersEachSide","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxChallengersForPickem","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxChallengesToResolve","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxForMinUSDBetAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxWinnersGroupChallenge","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minUSDBetAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"priceFeedErrorMargin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"challengeIds","type":"uint256[]"},{"internalType":"uint8[]","name":"finalOutcomes","type":"uint8[]"}],"name":"resolveChallenge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"challengeId","type":"uint256"},{"internalType":"address[]","name":"winners","type":"address[]"},{"internalType":"uint256[]","name":"profits","type":"uint256[]"}],"name":"resolveGroupChallenge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"name":"restrictTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"thresholds","type":"uint256[]"},{"internalType":"uint256[]","name":"sharesInUSD","type":"uint256[]"},{"internalType":"address","name":"token","type":"address"},{"internalType":"bool","name":"isSTMX","type":"bool"}],"name":"setAdminShareRules","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"value_","type":"bool"}],"name":"updateApplyMembershipValues","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxChallengersEachSide","type":"uint256"},{"internalType":"uint256","name":"_maxChallengersForPickem","type":"uint256"}],"name":"updateMaxChallengers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"_root","type":"bytes32"}],"name":"updateRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"withdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]
Contract Creation Code
6080346200010957601f620059d438819003918201601f19168301916001600160401b038311848410176200010e578084926020946040528339810103126200010957516001600160a01b0380821691829003620001095760018060a01b03199081600154166001556000543383821617600055604051913391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a360016002558215620000e35750600454161760045560326005556032600655600160ff19600a541617600a55633b9aca006007556040516158af9081620001258239f35b62461bcd60e51b81526020600482015260016024820152603160f81b6044820152606490fd5b600080fd5b634e487b7160e01b600052604160045260246000fdfe61016080604052600436101561001e575b50361561001c57600080fd5b005b60003560e01c908163024ece8914613c5f575080630306c19a1461363d57806307c9f4661461361d578063099e4133146135f4578063130894e0146135865780631411b438146135685780631f2a8dd6146135455780631fb05b33146135275780632179e5fe14612fd357806321ff997014612f825780633d40a54f14612dda5780634655471914612da257806346d1cdc014612cdf5780634c9f166d14612cbc57806351cff8d914612b46578063615b2c7814612793578063715018a61461273657806375b1510c146115bf57806378a29c09146126c45780637946b7811461262457806379ba5097146125615780638288c80e146124e25780638770912f1461235b5780638bbed8ef1461233d5780638c2bd69214611cef5780638da5cb5b14611cc65780638edfe78714611caa5780639474aef614611c8c57806395e737c014611c1e578063a8037e0e14611b34578063a8f3f408146115e9578063ab04663b146115c4578063ab535f8f146115bf578063b9f433a614610bbe578063bf26931f14610ba0578063c03fb87c14610acc578063ca44bb5314610ab0578063d0339fee14610922578063d89b6dc014610906578063def08d26146107ed578063e30c3978146107c4578063eaecfca714610727578063ef2104a2146102aa578063f030f0131461028a5763f2fde38b1461021a5738610010565b3461028557602036600319011261028557610233613d74565b61023b613f96565b60018060a01b0380911690816001600160601b0360a01b6001541617600155600054167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700600080a3005b600080fd5b3461028557600036600319011261028557602060405164174876e8008152f35b346102855760403660031901126102855760243560ff81168103610285576004546001600160a01b031633148015610713575b156106e9576102ea614802565b6102f56004356156d6565b610300600435615764565b6004356000526012602052604060002060018060a01b03600054163314610691575b600501600460ff198254161790557ff0fbc916c90170d09d494b840e0bb03ab2e82065a8e1990d3673211b45182a8e60206040516004358152a1600435600052601260205260406000209060018060a01b0382541690600183015491600284015491610396610391848661402d565b614fd3565b926103a4610391828761402d565b946103b2610391838361402d565b916103c0610391828461402d565b976000926000958a89898c6000935b86851061063e5750505050506000925b84841061049d575060008051602061585a8339815191528a8a6104938b8f6104608d60008051602061581a83398151915260008051602061583a833981519152938f61043460405192839289600435856150f4565b0390a16040805160043581526001600160a01b0386166020820152908101919091529081906060820190565b0390a160405161046f81613e21565b600081526040519061048082613e21565b60008252604051958695600435876151b5565b0390a16001600255005b9091929394966105bb6105b5886105c1938f8f8f8f918f8b908e958e956104d46104cb8f60028b91016149a0565b9890549961402d565b956104ed8760018060a01b038b8b60031b1c16926148a9565b526001600160a01b03600388811b8a901c82166000908152601360209081526040808320600480358552925290912080546002820154928201549190930154909316969193909160ff166001036105cc575061057a9796959492610558610574959361055e9361402d565b9061402d565b936105748360009d6000975b6105748b8b6148a9565b526148a9565b519160018060a01b039160031b1c1660005260146020528d6040600020906000526020526105ae604060002091825461402d565b905561402d565b98613fee565b94613fee565b9291909493946103df565b85829d610574969497956105749361057a9c9b9a9560018060a01b036000541660005260149081602052604060002083600052602052610612604060002091825461402d565b90558660005260205260406000209060005260205260406000206106378a825461402d565b905561056a565b9a6105bb936105b593868d948d9f99829d9e8d9b610665839e9f9c60016106819e016149a0565b969054976104ed8760018060a01b038b8b60031b1c16926148a9565b89898c8e989798969594966103cf565b60078101546202a30081018091116106d35742116103225760405162461bcd60e51b8152602060048201526002602482015261062760f31b6044820152606490fd5b634e487b7160e01b600052601160045260246000fd5b60405162461bcd60e51b81526020600482015260026024820152611a1960f11b6044820152606490fd5b506000546001600160a01b031633146102dd565b3461028557602036600319011261028557610740613d74565b610748613f96565b6001600160a01b0316801561079b57600480546001600160a01b03191682179055604080519182523360208301527fea8ca364cc9c0e44ae5ceab245cececcc710724873a9ecfc89a0559983e4473991a1005b60405162461bcd60e51b81526020600482015260016024820152603160f81b6044820152606490fd5b34610285576000366003190112610285576001546040516001600160a01b039091168152602090f35b604036600319011261028557600435610804613d8a565b61080c613f96565b60018060a01b03169081600052602090601082526108306040600020541515613ffd565b61083b811515614901565b826108eb578034036108c1575b3360005260148252604060002083600052825261086b604060002091825461402d565b9055336000818152601483526040808220948252939092529082902054825190815260208101919091527fc6eb93583634faca0ac6216e0725f08457b08e39adbbf4b9484973740f23327791819081015b0390a1005b60405162461bcd60e51b8152600481018390526002602482015261323960f01b6044820152606490fd5b6108f53415614d51565b61090181303386614d82565b610848565b3461028557600036600319011261028557602060405160058152f35b61018036600319011261028557610937613d74565b604435602435610945613d64565b6084359060028210156102855760a4359360c4359160e4359160ff831683036102855761012435946001600160a01b03808716870361028557610164356001600160401b0381116102855761099e903690600401613e74565b986109aa600954613fee565b96876009554282111580610aa7575b15610a7e5760077fd2f7931a802085b3d0234d4c320ce7ee0041da96678ce2bf5c93e8d3d7e65f529460809461001c9e1693610a0a610a05866000526010602052604060002054151590565b613ffd565b8a6000526012602052604060002091856001600160601b0360a01b8454161783556002600584019161ff0083549160081b169061ffff19161717905560068201550155610a57848a61402d565b6040519188835260208301523360408301526060820152a1610144359561010435946140a0565b60405162461bcd60e51b81526020600482015260016024820152601960f91b6044820152606490fd5b504281116109b9565b3461028557600036600319011261028557602060405160088152f35b346102855760403660031901126102855760a0610ae7613d8a565b60006080604051610af781613dd0565b8281528260208201528260408201528260608201520152600180831b038091166000526013602052604060002060043560005260205260406000209060405190610b4082613dd0565b82549283835260ff6001820154166020840190815260ff6002830154916040860192835260806004866003870154169560608901968752015496019586526040519687525116602086015251604085015251166060830152516080820152f35b34610285576000366003190112610285576020600954604051908152f35b34610285576040366003190112610285576001600160401b0360043581811161028557610bef903690600401613e74565b6024359182116102855736602383011215610285578160040135610c1281613e5d565b92610c206040519485613e3c565b8184526024602085019260051b8201019036821161028557602401915b8183106115a557505050610c4f6157bf565b610c57614802565b805190600a8211158061159b575b610c729093929193614856565b6000925b818410610c84576001600255005b610c8e84826148a9565b5193846000526012602052600560406000200180549060ff916008928082851c166002811015611527576115715780610cc7868a6148a9565b51169384151580611567575b1561153d57610ce18a6156d6565b610cea8a61571f565b60048501908282116106d357828216101561152757169060ff19161790557fb6171b34500ba874839b220ee75cbfbeb25bba762c6a57c158e19a3c27af89ae60408051888152836020820152a1600381036111aa57509091929380600052601260205260406000209060018060a01b03825416600183015492600281015490610d76610391838761402d565b94610d84610391848361402d565b92610d92610391828461402d565b90610da0610391828561402d565b9260009160009560005b83811061101857506000925b828410610e7b5750505050509260008051602061583a833981519152610e3b610e6b9460008051602061581a833981519152610e739b9a989560008051602061585a8339815191529a98610e116040519283928a8d856150f4565b0390a1604080518881526001600160a01b0387166020820152908101919091529081906060820190565b0390a1604051610e4a81613e21565b6000815260405191610e5b83613e21565b60008352604051968796876151b5565b0390a1613fee565b929190610c76565b90919293968a8c8b876002870190610e92916149a0565b905460039190911b1c6001600160a01b03169182610eb0878b61402d565b9182610ebb916148a9565b52826000526013602052604060002084600052602052604060002054938360005260136020526040600020816000526020528d8d6040600020600201549686600052601360205260406000208460005260205260406000206004015493876000526013602052604060002090600052602052600160a01b600190036040600020600301541692600160a01b60019003600054166000526014968760205260406000208160005260205260406000208a815490610f769161402d565b9055846000528760205260406000209060005260205285604060002086815490610f9f9161402d565b9055610faa916148a9565b52610fb5908d6148a9565b52610fc08d8d6148a9565b52610fcb908d6148a9565b519160005260205260406000208c600052602052604060002090815490610ff19161402d565b9055610ffc9161402d565b9661100690613fee565b9361101090613fee565b929190610db6565b9396919290918b8561102d81600187016149a0565b905460039190911b1c6001600160a01b031691829161104b916148a9565b5280600052601360205260406000208c6000526020526040600020549080600052601360205260406000208d6000526020528b8d8c6040600020600201549484600052601360205260406000208360005260205260406000206004015492856000526013602052604060002090600052602052600160a01b600190036040600020600301541691600160a01b6001900360005416600052601494856020526040600020816000526020526040600020888154906111079161402d565b905583600052856020526040600020906000526020528b6040600020858154906111309161402d565b905561113b916148a9565b526111468d8c6148a9565b526111518c8c6148a9565b5261115c888d6148a9565b519160005260205260406000208c6000526020526040600020908154906111829161402d565b905561118d9161402d565b9661119790613fee565b936111a190613fee565b92919092610daa565b85600052601260205260406000209560018060a01b038754166000600189019360028a019182600460038d01549c0154926002849114611519575b5050855483549b92908c906111f981614fd3565b9461121461039161120d610391868661402d565b948461402d565b936000926000925b81841061141857505050506112388e9798999a9b9c9d9e614fd3565b966000905b80821061135757505050936113266113429460008051602061583a8339815191526112f98b999660008051602061581a833981519152610e739f9e9c60008051602061585a8339815191529e6112cf6113349a839e60018060a01b0360005416600052601460205260406000208560005260205260406000206112c188825461402d565b9055604051948594856150f4565b0390a1604080518c81526001600160a01b038c166020820152908101919091529081906060820190565b0390a1611318604051998a998a5260e060208b015260e08a019061512c565b9088820360408a0152613eee565b90868203606088015261512c565b908482036080860152613eee565b90600060a084015260c08301520390a1613fee565b90919461140c6114066114129260028c8f8f8c918f8b611376916149a0565b60018060a01b0391549060031b1c1660005260136020526040600020906000526020526040600020926113ab8b8554926148a9565b5260038301546001600160a01b0316806113c5848e6148a9565b528c6113d760048601549485926148a9565b5260005260146020526040600020906000526020526113fc604060002091825461402d565b905501549061402d565b96613fee565b92613fee565b9061123d565b90919293966105bb6105b5611511928f8f8f8b908f8f8f948b958f938f61148761148f916114488860029d6149a0565b60018060a01b0391549060031b1c169586600052601360205260406000209060005260205261055860406000209b6114828d5480946148ee565b61504d565b9586926148a9565b5260038701956114a98460018060a01b03895416926148a9565b526114ba60048801938454926148a9565b52600052601491826020526040600020846000526020526114e1604060002091825461402d565b9055549260018060a01b039054166000526020526040600020906000526020526113fc604060002091825461402d565b92919061121c565b9b9096935091508b806111e5565b634e487b7160e01b600052602160045260246000fd5b60405162461bcd60e51b8152602060048201526002602482015261313160f01b6044820152606490fd5b5060048510610cd3565b60405162461bcd60e51b8152602060048201526002602482015261031360f41b6044820152606490fd5b5082518214610c65565b823560ff8116810361028557815260209283019201610c3d565b613ed2565b3461028557600036600319011261028557602060405169152d02c7e14af68000008152f35b34610285576060366003190112610285576001600160401b036004358181116102855761161a903690600401613f78565b9060243581811161028557611633903690600401613f78565b906044359081116102855761164c903690600401613e74565b90611655613f96565b825161166e825184519083149081611b2a575b50614856565b60005b81811061174c57505060005b83518110156116b1576116ac906116a66001600160a01b0361169f83886148a9565b5116614a50565b50613fee565b61167d565b509060005b83518110156116e4576116df906116a66001600160a01b036116d883886148a9565b5116614ab4565b6116b6565b7fee34c4fff4314ef30863a798e2773a9aa5251ff332d9de421fdddcabfc423778611725856117418561173388604051958695608087526080870190613ce8565b908582036020870152613ce8565b908382036040850152613eee565b3360608301520390a1005b6117766001600160a01b0361176183886148a9565b51166000526010602052604060002054151590565b611b00576001600160a01b0361178c82856148a9565b511661181f5761179c81856148a9565b51156117f557806117b06117f092866148a9565b516001600160a01b036117c383896148a9565b51166000908152601660205260409020556116a66001600160a01b036117e983896148a9565b51166149b8565b611671565b60405162461bcd60e51b8152602060048201526002602482015261032360f41b6044820152606490fd5b6001600160a01b0361183182856148a9565b511660405190633fabe5a360e21b9081835260a083600481845afa8015611a5357600093600091611ad8575b506000841315611aae576118756201518091426148e1565b11611a845760405191825260a082600481845afa918215611a5357600092611a5f575b506000908190815b60058110611987575b50506118b592506153fd565b90606491826118c3826153e7565b059060008282039212818312811690828413901516176106d35780846118eb6118f2936153e7565b05906157fd565b90821215918261197c575b50501561195557506117f0906001600160a01b0361191b82866148a9565b51166001600160a01b0361192f83896148a9565b511660005260116020526040600020906001600160601b0360a01b825416179055613fee565b60405162461bcd60e51b8152602060048201526002602482015261323760f01b6044820152fd5b1315905087806118fd565b909160405190639a6fc8f560e01b82526001600160501b038616600483015260a082602481875afa908115611a53576119c892600092611a1d575b506157fd565b92600181018091116106d357936001600160501b03811615611a14576001600160501b036000199116016001600160501b0381116106d357611a0a9091613fee565b93929190936118a0565b849392506118a9565b611a4091925060a03d60a011611a4c575b611a388183613e3c565b8101906153a7565b5050509050908d6119c2565b503d611a2e565b6040513d6000823e3d90fd5b611a7991925060a03d60a011611a4c57611a388183613e3c565b505050509088611898565b60405162461bcd60e51b8152602060048201526002602482015261323560f01b6044820152606490fd5b60405162461bcd60e51b8152602060048201526002602482015261313960f01b6044820152606490fd5b9050611af491935060a03d60a011611a4c57611a388183613e3c565b5094925050928961185d565b60405162461bcd60e51b81526020600482015260026024820152611a1b60f11b6044820152606490fd5b9050821486611668565b346102855760208060031936011261028557611b4e613d74565b90600b8054611b5c81614fd3565b93611b6682614fd3565b926001600160a01b039182169060005b848110611bab57611b9a88611ba78989604051948594604086526040860190613ce8565b9184830390850152613eee565b0390f35b611c19908260005284817f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9015416611be3828b6148a9565b528360005260148852604060002085611bfc838c6148a9565b51166000528852604060002054611c1382896148a9565b52613fee565b611b76565b34610285576020366003190112610285577f39d65726005dded9e32c1c37c38c433a478814ed5df9a080f9788a8861cc383a6108bc611c5b613d55565b611c63613f96565b600a805460ff191660ff9215159283161790556040805191825233602083015290918291820190565b34610285576000366003190112610285576020600754604051908152f35b3461028557600036600319011261028557602060405160148152f35b34610285576000366003190112610285576000546040516001600160a01b039091168152602090f35b3461028557606036600319011261028557611d08613d74565b6044359060ff8216820361028557611d1e6157bf565b611d26614802565b611d316024356156d6565b611d3c602435615764565b60018060a01b0381166000526013602052604060002060243560005260205260ff600160406000200154161561231357602435600052601260205260406000209060018060a01b0382541691600181015491600282015460405193611da085613e06565b600185526020368187013760405191611db883613e06565b600183526020368185013760405194611dd086613e06565b600186526020368188013760405192611de884613e06565b60018452602036818601376001600160a01b038681166000908152601360209081526040808320602435845290915290208054600280830154600484015460039094015460058801549499951697909693909160089190911c60ff169081101561152757801590816122de575b81156122d3575b50156121395750600160ff611e7b8b611e7684870161403a565b6151f6565b9e16036120db57611e9090610558888761402d565b9a5b60018060a01b03891660005260146020526040600020906000526020526040600020611ebf8c825461402d565b905560038101611ed08c82546148e1565b9055826000198101116106d3577f679f73fac18389f339b511a489b811473ff4d0f3d4937002ed9951c7636cce3a6120236120c49960008051602061583a8339815191529860008051602061585a8339815191529f986113189f8f8f9091611fb88f611fbe946120709f611f9660008051602061581a8339815191529f8e926001611f64611f7993600019018287016149a0565b828060a01b0391549060031b1c1694016149a0565b819391549060031b9160018060a01b03809116831b921b19161790565b9055611fa460018d01615172565b6001600160a01b03891690611fb890614886565b52614886565b52611fc886614886565b526001600160a01b031660008181526013602090815260408083206024358085529083528184208481556001810185905560028101859055600381018590556004019390935580519384529083019190915290918291820190565b0390a160018060a01b03905416976120446040519283928b602435856150f4565b0390a16040805160243581526001600160a01b0388166020820152908101919091529081906060820190565b0390a161133460405161208281613e21565b600081526120b66040519361209685613e21565b600085526040519889986024358a5260e060208b015260e08a0190613ce8565b908682036060880152613ce8565b90600360a084015260c08301520390a16001600255005b9a60018060a01b03600054166000526014602052604060002081600052602052604060002061210b86825461402d565b9055856000526014602052604060002081600052602052604060002061213288825461402d565b9055611e92565b9350919a93600160ff6121548b611e766002879c970161403a565b9e160361226f5761216992916105589161402d565b986000946000935b60018060a01b0389166000526014602052604060002090600052602052604060002061219e8c825461402d565b9055600481016121af8c82546148e1565b9055826000198101116106d3577f679f73fac18389f339b511a489b811473ff4d0f3d4937002ed9951c7636cce3a6120236120c49960008051602061583a8339815191529860008051602061585a8339815191529f986113189f8f8f9091611fb88f611fbe946120709f61225c60008051602061581a8339815191529f8e926002612243611f7993600019018287016149a0565b905460039190911b1c6001600160a01b031694016149a0565b905561226a60028d01615172565b611fa4565b9360018060a09d949d9893981b0360005416600052601460205260406000208160005260205260406000206122a586825461402d565b905585600052601460205260406000208160005260205260406000206122cc88825461402d565b9055612171565b60019150148f611e5c565b905060018060a01b038b1660005260136020526040600020602435600052602052600160ff8160406000200154161490611e55565b60405162461bcd60e51b8152602060048201526002602482015261189960f11b6044820152606490fd5b34610285576000366003190112610285576020600654604051908152f35b3461028557602080600319360112610285576004356001600160401b0381116102855761238c903690600401613f78565b90612395613f96565b815160005b81811061244b57505060005b82518110156123d4576123cf906116a66001600160a01b036123c883876148a9565b5116614b1f565b6123a6565b5060005b825181101561240657612401906116a66001600160a01b036123fa83876148a9565b5116614c09565b6123d8565b7fe315bdee05329e2e69a5253f25814537fb86d11c4c808142d2ea502822516e7f6124408484604051928392604084526040840190613ce8565b9033908301520390a1005b6001600160a01b036124618161176184886148a9565b156124b85790816124b39261247683886148a9565b51166000526011855260406000206001600160601b0360a01b815416905561249e82876148a9565b51166000526016845260006040812055613fee565b61239a565b60405162461bcd60e51b8152600481018590526002602482015261343760f01b6044820152606490fd5b34610285576020366003190112610285577f1952e249d74df2e2b0c9a7de47fdd506d63de48981dfbffad2b925a1152928f96108bc600435612522613f96565b60075481101580612552575b61253790614901565b60078190556040805191825233602083015290918291820190565b506402540be40081111561252e565b34610285576000366003190112610285576001546001600160a01b0333818316036125cd576001600160601b0360a01b8092166001556000549133908316176000553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608490fd5b34610285576020366003190112610285576001600160a01b03612645613d74565b1660005260156020526126aa604060002060ff6002820154166126b860016126966040519461267f866126788184614d14565b0387613e3c565b61268f6040518094819301614d14565b0382613e3c565b604051948594606086526060860190613eee565b908482036020860152613eee565b90151560408301520390f35b610120366003190112610285576126d9613d64565b6084359060ff821682036102855760c435916001600160a01b03831683036102855761010435926001600160401b0384116102855761271f61001c943690600401613e74565b9260e4359260a435916044356024356004356140a0565b346102855760003660031901126102855761274f613f96565b606460405162461bcd60e51b815260206004820152602060248201527f52656e6f756e63696e67206f776e6572736869702069732064697361626c65646044820152fd5b34610285576080366003190112610285576004356001600160401b038111610285576127c3903690600401613e74565b6024356001600160401b038111610285576127e2903690600401613e74565b906044356001600160a01b0381168103610285576064351515606435036102855761280b613f96565b6001600160a01b0381166000908152600c602052604090205461282f901515614cb2565b81519183519183151580612b3d575b80612b32575b61284d90614856565b64174876e8009260005b856000198101116106d35760001986018110156128fc5761287881856148a9565b51600182018083116106d35761288e90866148a9565b51106128d2576064356128c0575b806128b6866128ae6128bb948b6148a9565b511115614ce3565b613fee565b612857565b69152d02c7e14af6800000945061289c565b60405162461bcd60e51b8152602060048201526002602482015261323160f01b6044820152606490fd5b5091859284606435612b21575b60001982019182116106d3576128ae61292292866148a9565b60405190606082018281106001600160401b03821117612ace5760405281526020810192835260408101906064351515825260018060a01b03831660005260156020526040600020905180516001600160401b038111612ace57600160401b91828211612ace578354828555808310612af8575b5060200183600052602060002060005b838110612ae4575050505060019485830190518051926001600160401b038411612ace578311612ace578154838355808410612aa3575b5060200190600052602060002060005b838110612a90577fbf48b0820ca81715d6b1acb25649f7ba1d18bd574712fcc4788a22a3f9e49229612a7a89898960028a019051151560ff8019835416911617905560018060a01b03166000526015602052604060002060ff60026040519485946040865260606040870152612a6660a0870186614d14565b868103603f19016060880152908501614d14565b92015416151560808301523360208301520390a1005b87906020845194019381840155016129ed565b826000528784602060002092830192015b828110612ac25750506129dd565b60008155018890612ab4565b634e487b7160e01b600052604160045260246000fd5b6001906020845194019381840155016129a6565b8460005282602060002091820191015b818110612b155750612996565b60008155600101612b08565b5069152d02c7e14af6800000612909565b506014841115612844565b5082841461283e565b346102855760208060031936011261028557612b60613d74565b612b68614802565b3360009081526014835260408082206001600160a01b039093168083529284529020548015612c935733600052601483526040600020826000528352600060408120558115600014612c3357600080808084335af1612bc5614ec0565b5015612c0957906060917f30f0f2f1c6b25d25ce05404ece9a5feb949e2e6f459f939cc1f01c6512428066935b604051928352820152336040820152a16001600255005b60405162461bcd60e51b8152600481018490526002602482015261333760f01b6044820152606490fd5b60405163a9059cbb60e01b84820152336024820152604480820183905281527f30f0f2f1c6b25d25ce05404ece9a5feb949e2e6f459f939cc1f01c6512428066936060939291612c8e90612c88606482613e3c565b84614dc4565b612bf2565b60405162461bcd60e51b81526004810184905260016024820152600760fb1b6044820152606490fd5b3461028557600036600319011261028557602060ff600a54166040519015158152f35b3461028557604036600319011261028557600435602435612cfe613f96565b80151580612d97575b80612d8e575b80612d83575b15612d59577f42a3e0b2ac4ef58d5c467d7199e859b2b40ee8a57507885e2d4539a9ae0a05da9181606092600655816005556040519182526020820152336040820152a1005b60405162461bcd60e51b8152602060048201526002602482015261323360f01b6044820152606490fd5b506032821115612d13565b50811515612d0d565b506032811115612d07565b346102855760203660031901126102855760206004358015159081612dcd575b506040519015158152f35b9050600954101582612dc2565b34610285576020806003193601126102855760043590604051612dfc81613db4565b60008152606082820152606060408201526000606082015260006080820152600060a0820152600060c0820152600060e0820152600061010080920152612e42836156d6565b826000526012825260406000209160405193612e5d85613db4565b83546001600160a01b03908116865292612e796001860161403a565b93838701948552612e8c6002870161403a565b93604088019485526003870154956060890196875260048801549060808a0191825260058901549260ff60a08c0194612ec782821687614094565b60081c169460c08c0195600281101561152757865260068b01549a60e08d019b8c526007015497878d01988952612efd90615639565b612f079086614094565b6040519b828d525116908b01525195610120968760408c01526101408b01612f2e91613ce8565b90518a8203601f190160608c0152612f469190613ce8565b965160808a01525160a08901525160088110156115275760c08801525194600286101561152757869560e0870152519085015251908301520390f35b34610285576020366003190112610285577fe2c8b1f4bcb8086dc9805a9868ee616914948de41a09786213a2284a8ffba27b6040600435612fc16157bf565b806003558151908152336020820152a1005b61010036600319011261028557612fe8613d64565b60a435906001600160a01b03821682036102855760e4356001600160401b0381116102855761301b903690600401613e74565b916130276004356156d6565b3360005260136020526040600020600435600052602052604060002060ff600182015416156134fe57600435600052601260205260406000209260018060a01b038454169460ff600a5416156134d457613082600435615639565b6008811015611527576002036134aa578561349b573460243503613471575b6130af60443560243561402d565b906130ba828861523d565b9460009360ff6008541661330e575b505050838111156132e4576130fe6130e9826130e489615418565b6148ee565b876132d3576130f8601261506d565b9061504d565b6000878152600e6020526040902054156132cd576131289060166020526040600020541115614901565b60443561321d575b9061317360a095949392877f0b17a6125c8c06bcd40d6a58de2c6f4e8815d9f52c8f8b6999ca7493c0fea31a98613206575b5061316d838661402d565b906148e1565b9261317f84845461402d565b83556131906002840191825461402d565b90556131a16004830191825461402d565b905560018181015460ff16036131f057600383016131c083825461402d565b90555b5491600180851b0390541690604051926004358452602084015260408301523360608301526080820152a1005b600483016131ff83825461402d565b90556131c3565b613217906024359030903390614d82565b88613162565b93929190336000526014602052604060002086600052602052604435604060002054106132a35761317360a0957f0b17a6125c8c06bcd40d6a58de2c6f4e8815d9f52c8f8b6999ca7493c0fea31a97336000526014602052604060002081600052602052604060002061329360443582546148e1565b9055975091929394955050613130565b60405162461bcd60e51b8152602060048201526002602482015261199b60f11b6044820152606490fd5b50613128565b6130f86132df89615097565b61506d565b60405162461bcd60e51b8152602060048201526002602482015261333560f01b6044820152606490fd5b60ff6040999693999895929794985191336020840152166040820152608435606082015260018060a01b038716608082015260c43560a082015260a0815261335581613deb565b60208151910120604051602081019182526020815261337381613e06565b5190209660035497966000975b8a518910156133cb57613393898c6148a9565b5190818110156133b7576000526020526133b1604060002098613fee565b97613380565b906000526020526133b160406000206105b5565b919497509295985096909396036134475769021e19e0c9bab2400000946084358603908682116106d3578691613400916148ee565b049485916001600160a01b0316613418575b806130c9565b909491925060c4358103918183116106d35761343f92613437916148ee565b0480946148e1565b908680613412565b60405162461bcd60e51b8152602060048201526002602482015261343560f01b6044820152606490fd5b60405162461bcd60e51b8152602060048201526002602482015261333360f01b6044820152606490fd5b6134a53415614d51565b6130a1565b60405162461bcd60e51b8152602060048201526002602482015261199960f11b6044820152606490fd5b60405162461bcd60e51b8152602060048201526002602482015261333160f01b6044820152606490fd5b60405162461bcd60e51b81526020600482015260016024820152603760f81b6044820152606490fd5b346102855760003660031901126102855760206040516202a3008152f35b3461028557600036600319011261028557602060ff600854166040519015158152f35b34610285576000366003190112610285576020600554604051908152f35b34610285576020366003190112610285577f46eb189395b90ab0cfd42a309b25f46e015815a01e7e676f6f804ef62466127d6108bc6135c3613d55565b6135cb613f96565b6008805460ff191660ff9215159283161790556040805191825233602083015290918291820190565b34610285576000366003190112610285576004546040516001600160a01b039091168152602090f35b346102855760003660031901126102855760206040516402540be4008152f35b34610285576060366003190112610285576001600160401b036024358181116102855761366e903690600401613d25565b909160443590811161028557613688903690600401613d25565b6136906157bf565b613698614802565b6136a36004356156d6565b6136ae60043561571f565b60043560005260126020526005604060002001805460ff8160081c16600281101561152757600103613c355782851480613c2a575b15613c00576000805b868210613b065768056bc75e2d63100000915003613adc5760059060ff19161790557fb6171b34500ba874839b220ee75cbfbeb25bba762c6a57c158e19a3c27af89ae60408051600435815260056020820152a1600435600052601260205260406000209160018060a01b0383541660c05260018301549161376d85614fd3565b9161377b61039187866148e1565b948761378a61039189886148e1565b9561379481614fd3565b9361379e82614fd3565b60a052600095600096898c6000965b8688106139125750505050505050509160008051602061581a8339815191526138346138629360008051602061583a8339815191529560018060a01b0360005416600052601460205260406000209060018060a01b03905416600052602052604060002061381c85825461402d565b905560405191829160a0519060c051600435856150f4565b0390a16040805160c05160043582526001600160a01b03166020820152908101919091529081906060820190565b0390a1604051938060e08601600435875260e06020880152526101008501959060005b8181106138ec5760008051602061585a83398151915287806138d3896138c58a6138b78f8c8782036040890152613eee565b908582036060870152613ce8565b908382036080850152613eee565b600160a083015260c05160c08301520390a16001600255005b909196602080600192838060a01b036139048c613da0565b168152019801929101613885565b879a969791899161394661392985600186016149a0565b905460039190911b1c6001600160a01b0316611e76368585613f22565b9061395485600186016149a0565b60018060a01b0391549060031b1c16600052601360205260406000206004356000526020526040600020608052828214600014613a5657505050926139e1926139c2826139aa6139d2956001613a4c99016149a0565b905460039190911b1c6001600160a01b0316926148a9565b528d611c138260805154926148a9565b945b600260805101549061402d565b9760805160048101906003825491019060018060a01b038254166000526014602052604060002060018060a01b038b5416600052602052613a28604060002091825461402d565b9055546001600160a01b0316613a3e838b6148a9565b5254611c138260a0516148a9565b9392898c8e6137ad565b6139e19550613a4c96999450613aa7613aa28368056bc75e2d63100000613a928d613a8b848f6003613ad69c9d0154936148bd565b35906148ee565b04966001600160a01b03946148bd565b6148cd565b166000526014602052604060002060c0516000526020526040600020613ace84825461402d565b90558d6148a9565b526139d4565b60405162461bcd60e51b8152602060048201526002602482015261313760f01b6044820152606490fd5b613b1c90613b158387896148bd565b359061402d565b9080613b9a575b6001600160a01b03613b39613aa2838a8c6148bd565b166000526013602052604060002060043560005260205260ff6001604060002001541615613b7057613b6a90613fee565b906136ec565b60405162461bcd60e51b8152602060048201526002602482015261313560f01b6044820152606490fd5b613ba8613aa282898b6148bd565b60001982018281116106d3576001600160a01b03908190613bce90613aa2908c8e6148bd565b16911611613b235760405162461bcd60e51b8152602060048201526002602482015261189b60f11b6044820152606490fd5b60405162461bcd60e51b81526020600482015260026024820152610c4d60f21b6044820152606490fd5b50600a8511156136e3565b60405162461bcd60e51b8152602060048201526002602482015261313360f01b6044820152606490fd5b3461028557600036600319011261028557600f5480825260209082828101600f6000527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac8029260005b85828210613cd257505050613cbe92500383613e3c565b611ba7604051928284938452830190613ce8565b8554845260019586019588955093019201613ca7565b90815180825260208080930193019160005b828110613d08575050505090565b83516001600160a01b031685529381019392810192600101613cfa565b9181601f84011215610285578235916001600160401b038311610285576020808501948460051b01011161028557565b60043590811515820361028557565b6064359060ff8216820361028557565b600435906001600160a01b038216820361028557565b602435906001600160a01b038216820361028557565b35906001600160a01b038216820361028557565b61012081019081106001600160401b03821117612ace57604052565b60a081019081106001600160401b03821117612ace57604052565b60c081019081106001600160401b03821117612ace57604052565b604081019081106001600160401b03821117612ace57604052565b602081019081106001600160401b03821117612ace57604052565b90601f801991011681019081106001600160401b03821117612ace57604052565b6001600160401b038111612ace5760051b60200190565b81601f8201121561028557803591613e8b83613e5d565b92613e996040519485613e3c565b808452602092838086019260051b820101928311610285578301905b828210613ec3575050505090565b81358152908301908301613eb5565b34610285576000366003190112610285576020604051600a8152f35b90815180825260208080930193019160005b828110613f0e575050505090565b835185529381019392810192600101613f00565b9291613f2d82613e5d565b91613f3b6040519384613e3c565b829481845260208094019160051b810192831161028557905b828210613f615750505050565b838091613f6d84613da0565b815201910190613f54565b9080601f8301121561028557816020613f9393359101613f22565b90565b6000546001600160a01b03163303613faa57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b60001981146106d35760010190565b1561400457565b60405162461bcd60e51b81526020600482015260016024820152603360f81b6044820152606490fd5b919082018092116106d357565b9060405191828154918282526020928383019160005283600020936000905b8282106140715750505061406f92500383613e3c565b565b85546001600160a01b031684526001958601958895509381019390910190614059565b60088210156115275752565b959492909693919760e05285600052601260205260406000206040516140c581613db4565b81546001600160a01b031681526140de6001830161403a565b60208201526140ef6002830161403a565b60408201526003820154606082015260048201546080820152600582015461411d60ff821660a08401614094565b600260ff8260081c1610156115275760c09260ff60079260081c1684840152600681015460e08401520154610100820152614157886156d6565b01516002811015611527576001036147b957600160ff821603614790575b33600052601360205260406000208660005260205260ff60016040600020015416614767576141a2614802565b80928660005260126020526040600020806101405260018060a01b039054169060ff600a5416156134d4576141d688615639565b6008811015611527576002036134aa578161475857348903613471575b6141fd8a8a61402d565b90614208828461523d565b9660009560ff600854166145d1575b505050858111156132e457614241614232826130e485615418565b836145c5576130f8601261506d565b6000838152600e6020526040902054156145b25761426f908360005260166020526040600020541115614901565b89614556575b61429160ff91838b600195614542575b505061316d868961402d565b9216148015614522575b156144f757600161014051016142b13382615005565b54600361014051016142c483825461402d565b90555b60ff600561014051015460081c16906002821015611527576001820361446f5760065410614445577f010fe3fa2b3358b8306447cc0a08515aa6c99e255d3d63b181fee6b377b6682398614432967ff6d42021a4549bed2c5e686f3a0e138917db52e05ad24e28d9239f7caef635ca9560a09560016143ec955b1461443c575b6040519260049261435785613dd0565b87855260ff602086019116815260408501908b825260608601926001808c1b03168352608086019384523360005260136020528d60406000209060005260205260406000209551865560ff6001870191511660ff1982541617905551600285015560038401906001808a1b039051166001600160601b03891b82541617905551910155600180851b036101405154169861402d565b6040519187835260208301523360408301528760608301526080820152a1604080519384526001600160a01b039094166020840152928201929092529081906060820190565b0390a16001600255565b60019150614347565b60405162461bcd60e51b8152602060048201526002602482015261033360f41b6044820152606490fd5b600554106144cd577f010fe3fa2b3358b8306447cc0a08515aa6c99e255d3d63b181fee6b377b6682398614432967ff6d42021a4549bed2c5e686f3a0e138917db52e05ad24e28d9239f7caef635ca9560a09560016143ec95614341565b60405162461bcd60e51b81526020600482015260026024820152610d0d60f21b6044820152606490fd5b600261014051016145083382615005565b546004610140510161451b83825461402d565b90556142c7565b5060ff600561014051015460081c1660028110156115275760011461429b565b61454f9130903390614d82565b388b614285565b33600052601460205260406000208260005260205289604060002054106132a35761429160ff916001933360005260146020526040600020816000526020528c6145a660406000209182546148e1565b90559350915050614275565b6145c0906007541115614901565b61426f565b6130f86132df85615097565b60409c9a989c9b99979593919694929b5160ff60208201923384521660408201528c606082015260018060a01b03891660808201528760a082015260a0815261461981613deb565b519020604051602081019182526020815261463381613e06565b5190209b6003549c610120526000610100525b60e05180519061010051918210156146aa5790614662916148a9565b51610120518181101561469957506101205160005260205260406000205b6101205261469061010051613fee565b61010052614646565b906000526020526040600020614680565b505091939597999b9a90929496989a61012051036134475769021e19e0c9bab2400000039069021e19e0c9bab240000082116106d35769021e19e0c9bab2400000916146f5916148ee565b049586906001600160a01b03891661470e575b80614217565b909691945069021e19e0c9bab24000000369021e19e0c9bab240000081116106d35761474869021e19e0c9bab240000091614750936148ee565b0480966148e1565b923880614708565b6147623415614d51565b6141f3565b60405162461bcd60e51b81526020600482015260016024820152601b60f91b6044820152606490fd5b60405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606490fd5b60ff8116600181149081156147f7575b506141755760405162461bcd60e51b81526020600482015260016024820152603560f81b6044820152606490fd5b6002915014386147c9565b60028054146148115760028055565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b1561485d57565b60405162461bcd60e51b81526020600482015260016024820152603960f81b6044820152606490fd5b8051156148935760200190565b634e487b7160e01b600052603260045260246000fd5b80518210156148935760209160051b010190565b91908110156148935760051b0190565b356001600160a01b03811681036102855790565b919082039182116106d357565b818102929181159184041417156106d357565b1561490857565b60405162461bcd60e51b8152602060048201526002602482015261064760f31b6044820152606490fd5b600f5481101561489357600f6000527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac8020190600090565b600d5481101561489357600d6000527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50190600090565b80548210156148935760005260206000200190600090565b6000818152600e6020526040812054614a4b57600d54600160401b811015614a37576001810180600d55811015614a235790826040927fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50155600d54928152600e6020522055600190565b634e487b7160e01b82526032600452602482fd5b634e487b7160e01b82526041600452602482fd5b905090565b600081815260106020526040812054614a4b57600f54600160401b811015614a37579082614aa0614a8984600160409601600f55614932565b819391549060031b91821b91600019901b19161790565b9055600f5492815260106020522055600190565b6000818152600c6020526040812054614a4b57600b54600160401b811015614a37576001810180600b55811015614a235790826040927f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90155600b54928152600c6020522055600190565b6000818152601060205260408120549091908015614c045760001990808201818111614bf057600f5490838201918211614bdc57808203614ba8575b505050600f548015614b9457810190614b7382614932565b909182549160031b1b19169055600f55815260106020526040812055600190565b634e487b7160e01b84526031600452602484fd5b614bc6614bb7614a8993614932565b90549060031b1c928392614932565b9055845260106020526040842055388080614b5b565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b85526011600452602485fd5b505090565b6000818152600e60205260408120549091908015614c045760001990808201818111614bf057600d5490838201918211614bdc57808203614c7e575b505050600d548015614b9457810190614c5d82614969565b909182549160031b1b19169055600d558152600e6020526040812055600190565b614c9c614c8d614a8993614969565b90549060031b1c928392614969565b90558452600e6020526040842055388080614c45565b15614cb957565b60405162461bcd60e51b8152602060048201526002602482015261068760f31b6044820152606490fd5b15614cea57565b60405162461bcd60e51b8152602060048201526002602482015261191960f11b6044820152606490fd5b90815480825260208092019260005281600020916000905b828210614d3a575050505090565b835485529384019360019384019390910190614d2c565b15614d5857565b60405162461bcd60e51b81526020600482015260026024820152610ccd60f21b6044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815261406f91614dc482613dd0565b60018060a01b031690614e23604051614ddc81613e06565b6020938482527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564858301526000808587829751910182855af1614e1d614ec0565b91614eff565b805191821591848315614e95575b505050905015614e3e5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b919381809450010312614ebc57820151908115158203614eb9575080388084614e31565b80fd5b5080fd5b3d15614efa573d906001600160401b038211612ace5760405191614eee601f8201601f191660200184613e3c565b82523d6000602084013e565b606090565b91929015614f615750815115614f13575090565b3b15614f1c5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015614f745750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b828510614fba575050604492506000838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350614f97565b90614fdd82613e5d565b614fea6040519182613e3c565b8281528092614ffb601f1991613e5d565b0190602036910137565b805490600160401b821015612ace5781615027916001615049940181556149a0565b815491936001600160a01b0360039290921b82811b19909316911690911b1790565b9055565b8115615057570490565b634e487b7160e01b600052601260045260246000fd5b60ff16604d81116106d357600a0a90565b90816020910312610285575160ff811681036102855790565b60405163313ce56760e01b815290602090829060049082906001600160a01b03165afa908115611a53576000916150cc575090565b613f93915060203d81116150ed575b6150e58183613e3c565b81019061507e565b503d6150db565b92613f93949261511e92855260018060a01b03166020850152608060408501526080840190613ce8565b916060818403910152613eee565b90815480825260208092019260005281600020916000905b828210615152575050505090565b83546001600160a01b031685529384019360019384019390910190615144565b8054801561519f57600019019061518982826149a0565b81549060018060a01b039060031b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b9491936120b66151df9461131860c097611334959b9a9b8a5260e060208b015260e08a0190613ce8565b600260a08401526001600160a01b03909416910152565b9081519060005b82811061520b575050905090565b6001600160a01b038061521e83876148a9565b5116908316146152365761523190613fee565b6151fd565b9250505090565b60018060a01b0381169161526d600091848352600c60205261526460408420541515614cb2565b6130e484615418565b9061528684159283600014615387576130f8601261506d565b9381526015602052604081209384548290915b82821061532c57505080156153245760001981019081116153105760026152c560ff92600188016149a0565b90549060031b1c9501541661530a57613f93936130f8926152f892506000146152fe576152f2601261506d565b906148ee565b91615418565b6152f26132df85615097565b50505090565b634e487b7160e01b82526011600452602482fd5b509250505090565b9091600190615341818518831c82861661402d565b918361534d848b6149a0565b90549060031b1c11600014615366575050915b90615299565b90935081018091111561536057634e487b7160e01b84526011600452602484fd5b6130f86132df86615097565b51906001600160501b038216820361028557565b908160a0910312610285576153bb81615393565b91602082015191604081015191613f93608060608401519301615393565b604d81116106d357600a0a90565b906005820291808305600514901517156106d357565b811561505757600160ff1b81146000198314166106d3570590565b6001600160a01b039081166000818152600e602090815260408083205491949293911561544c5750505050506305f5e10090565b60a09460119384825280838720541693835197888096633fabe5a360e21b825260049788915afa97881561562f578798615606575b5086976000881390816155da575b50156155b257918091859360005286825284600020541684519384809263313ce56760e01b82525afa9283156155a857509060ff929160009261558b575b50501692600884111561550e575060071983019283116154fc575050906154f6613f93926153d9565b906153fd565b634e487b7160e01b6000525260246000fd5b926008811061551f575b5050505090565b90919293506008036008811161557857615538906153d9565b838102939060008212600160ff1b8214166155645781850514901517156154fc57505038808080615518565b5050634e487b7160e01b6000525260246000fd5b50634e487b7160e01b6000525260246000fd5b6155a19250803d106150ed576150e58183613e3c565b38806154cd565b513d6000823e3d90fd5b835162461bcd60e51b81528086018490526002602482015261343360f01b6044820152606490fd5b426201517f198101925082116155f25710153861548f565b8787634e487b7160e01b6000525260246000fd5b90975061562291965060a03d8111611a4c57611a388183613e3c565b5097925050959638615481565b84513d89823e3d90fd5b6000908152601260205260408120600760ff60058301541691015491600882101590816156a457600483149182156156c9575b82156156b8575b8215615691575b5050614a4b5750421161568c57600290565b600390565b9091506156a4575060078114388061567a565b634e487b7160e01b81526021600452602490fd5b8092506156a4576006831491615673565b506005831491508061566c565b8015159081615712575b50156156e857565b60405162461bcd60e51b8152602060048201526002602482015261066760f31b6044820152606490fd5b90506009541015386156e0565b61572890615639565b60088110156115275760030361573a57565b60405162461bcd60e51b8152602060048201526002602482015261333960f01b6044820152606490fd5b61576d90615639565b600881101561152757600381149081156157b4575b501561578a57565b60405162461bcd60e51b8152602060048201526002602482015261034360f41b6044820152606490fd5b600291501438615782565b6004546001600160a01b031633036157d357565b60405162461bcd60e51b8152602060048201526002602482015261343160f01b6044820152606490fd5b919091600083820193841291129080158216911516176106d35756fe7620380c554abbc2f2c8551155ba9b12b3fcd40700f9a718a43678a914753048a933c5f2ff3ab65b8004e747a48707dab64e66636a6ff5247f0431bc16881774eb0cc7968044d5a32d1b2d53427cbc63a081ddf73f5afb1637a4dfbdea2f7db9a26469706673582212205cb023200ab77eb6105b029109f1d79a2294df8d75da4112a2520090bd92d02964736f6c63430008130033000000000000000000000000cd854bd769287bee0b9ac77712c32b12b27cb8e4
Deployed Bytecode
0x61016080604052600436101561001e575b50361561001c57600080fd5b005b60003560e01c908163024ece8914613c5f575080630306c19a1461363d57806307c9f4661461361d578063099e4133146135f4578063130894e0146135865780631411b438146135685780631f2a8dd6146135455780631fb05b33146135275780632179e5fe14612fd357806321ff997014612f825780633d40a54f14612dda5780634655471914612da257806346d1cdc014612cdf5780634c9f166d14612cbc57806351cff8d914612b46578063615b2c7814612793578063715018a61461273657806375b1510c146115bf57806378a29c09146126c45780637946b7811461262457806379ba5097146125615780638288c80e146124e25780638770912f1461235b5780638bbed8ef1461233d5780638c2bd69214611cef5780638da5cb5b14611cc65780638edfe78714611caa5780639474aef614611c8c57806395e737c014611c1e578063a8037e0e14611b34578063a8f3f408146115e9578063ab04663b146115c4578063ab535f8f146115bf578063b9f433a614610bbe578063bf26931f14610ba0578063c03fb87c14610acc578063ca44bb5314610ab0578063d0339fee14610922578063d89b6dc014610906578063def08d26146107ed578063e30c3978146107c4578063eaecfca714610727578063ef2104a2146102aa578063f030f0131461028a5763f2fde38b1461021a5738610010565b3461028557602036600319011261028557610233613d74565b61023b613f96565b60018060a01b0380911690816001600160601b0360a01b6001541617600155600054167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e22700600080a3005b600080fd5b3461028557600036600319011261028557602060405164174876e8008152f35b346102855760403660031901126102855760243560ff81168103610285576004546001600160a01b031633148015610713575b156106e9576102ea614802565b6102f56004356156d6565b610300600435615764565b6004356000526012602052604060002060018060a01b03600054163314610691575b600501600460ff198254161790557ff0fbc916c90170d09d494b840e0bb03ab2e82065a8e1990d3673211b45182a8e60206040516004358152a1600435600052601260205260406000209060018060a01b0382541690600183015491600284015491610396610391848661402d565b614fd3565b926103a4610391828761402d565b946103b2610391838361402d565b916103c0610391828461402d565b976000926000958a89898c6000935b86851061063e5750505050506000925b84841061049d575060008051602061585a8339815191528a8a6104938b8f6104608d60008051602061581a83398151915260008051602061583a833981519152938f61043460405192839289600435856150f4565b0390a16040805160043581526001600160a01b0386166020820152908101919091529081906060820190565b0390a160405161046f81613e21565b600081526040519061048082613e21565b60008252604051958695600435876151b5565b0390a16001600255005b9091929394966105bb6105b5886105c1938f8f8f8f918f8b908e958e956104d46104cb8f60028b91016149a0565b9890549961402d565b956104ed8760018060a01b038b8b60031b1c16926148a9565b526001600160a01b03600388811b8a901c82166000908152601360209081526040808320600480358552925290912080546002820154928201549190930154909316969193909160ff166001036105cc575061057a9796959492610558610574959361055e9361402d565b9061402d565b936105748360009d6000975b6105748b8b6148a9565b526148a9565b519160018060a01b039160031b1c1660005260146020528d6040600020906000526020526105ae604060002091825461402d565b905561402d565b98613fee565b94613fee565b9291909493946103df565b85829d610574969497956105749361057a9c9b9a9560018060a01b036000541660005260149081602052604060002083600052602052610612604060002091825461402d565b90558660005260205260406000209060005260205260406000206106378a825461402d565b905561056a565b9a6105bb936105b593868d948d9f99829d9e8d9b610665839e9f9c60016106819e016149a0565b969054976104ed8760018060a01b038b8b60031b1c16926148a9565b89898c8e989798969594966103cf565b60078101546202a30081018091116106d35742116103225760405162461bcd60e51b8152602060048201526002602482015261062760f31b6044820152606490fd5b634e487b7160e01b600052601160045260246000fd5b60405162461bcd60e51b81526020600482015260026024820152611a1960f11b6044820152606490fd5b506000546001600160a01b031633146102dd565b3461028557602036600319011261028557610740613d74565b610748613f96565b6001600160a01b0316801561079b57600480546001600160a01b03191682179055604080519182523360208301527fea8ca364cc9c0e44ae5ceab245cececcc710724873a9ecfc89a0559983e4473991a1005b60405162461bcd60e51b81526020600482015260016024820152603160f81b6044820152606490fd5b34610285576000366003190112610285576001546040516001600160a01b039091168152602090f35b604036600319011261028557600435610804613d8a565b61080c613f96565b60018060a01b03169081600052602090601082526108306040600020541515613ffd565b61083b811515614901565b826108eb578034036108c1575b3360005260148252604060002083600052825261086b604060002091825461402d565b9055336000818152601483526040808220948252939092529082902054825190815260208101919091527fc6eb93583634faca0ac6216e0725f08457b08e39adbbf4b9484973740f23327791819081015b0390a1005b60405162461bcd60e51b8152600481018390526002602482015261323960f01b6044820152606490fd5b6108f53415614d51565b61090181303386614d82565b610848565b3461028557600036600319011261028557602060405160058152f35b61018036600319011261028557610937613d74565b604435602435610945613d64565b6084359060028210156102855760a4359360c4359160e4359160ff831683036102855761012435946001600160a01b03808716870361028557610164356001600160401b0381116102855761099e903690600401613e74565b986109aa600954613fee565b96876009554282111580610aa7575b15610a7e5760077fd2f7931a802085b3d0234d4c320ce7ee0041da96678ce2bf5c93e8d3d7e65f529460809461001c9e1693610a0a610a05866000526010602052604060002054151590565b613ffd565b8a6000526012602052604060002091856001600160601b0360a01b8454161783556002600584019161ff0083549160081b169061ffff19161717905560068201550155610a57848a61402d565b6040519188835260208301523360408301526060820152a1610144359561010435946140a0565b60405162461bcd60e51b81526020600482015260016024820152601960f91b6044820152606490fd5b504281116109b9565b3461028557600036600319011261028557602060405160088152f35b346102855760403660031901126102855760a0610ae7613d8a565b60006080604051610af781613dd0565b8281528260208201528260408201528260608201520152600180831b038091166000526013602052604060002060043560005260205260406000209060405190610b4082613dd0565b82549283835260ff6001820154166020840190815260ff6002830154916040860192835260806004866003870154169560608901968752015496019586526040519687525116602086015251604085015251166060830152516080820152f35b34610285576000366003190112610285576020600954604051908152f35b34610285576040366003190112610285576001600160401b0360043581811161028557610bef903690600401613e74565b6024359182116102855736602383011215610285578160040135610c1281613e5d565b92610c206040519485613e3c565b8184526024602085019260051b8201019036821161028557602401915b8183106115a557505050610c4f6157bf565b610c57614802565b805190600a8211158061159b575b610c729093929193614856565b6000925b818410610c84576001600255005b610c8e84826148a9565b5193846000526012602052600560406000200180549060ff916008928082851c166002811015611527576115715780610cc7868a6148a9565b51169384151580611567575b1561153d57610ce18a6156d6565b610cea8a61571f565b60048501908282116106d357828216101561152757169060ff19161790557fb6171b34500ba874839b220ee75cbfbeb25bba762c6a57c158e19a3c27af89ae60408051888152836020820152a1600381036111aa57509091929380600052601260205260406000209060018060a01b03825416600183015492600281015490610d76610391838761402d565b94610d84610391848361402d565b92610d92610391828461402d565b90610da0610391828561402d565b9260009160009560005b83811061101857506000925b828410610e7b5750505050509260008051602061583a833981519152610e3b610e6b9460008051602061581a833981519152610e739b9a989560008051602061585a8339815191529a98610e116040519283928a8d856150f4565b0390a1604080518881526001600160a01b0387166020820152908101919091529081906060820190565b0390a1604051610e4a81613e21565b6000815260405191610e5b83613e21565b60008352604051968796876151b5565b0390a1613fee565b929190610c76565b90919293968a8c8b876002870190610e92916149a0565b905460039190911b1c6001600160a01b03169182610eb0878b61402d565b9182610ebb916148a9565b52826000526013602052604060002084600052602052604060002054938360005260136020526040600020816000526020528d8d6040600020600201549686600052601360205260406000208460005260205260406000206004015493876000526013602052604060002090600052602052600160a01b600190036040600020600301541692600160a01b60019003600054166000526014968760205260406000208160005260205260406000208a815490610f769161402d565b9055846000528760205260406000209060005260205285604060002086815490610f9f9161402d565b9055610faa916148a9565b52610fb5908d6148a9565b52610fc08d8d6148a9565b52610fcb908d6148a9565b519160005260205260406000208c600052602052604060002090815490610ff19161402d565b9055610ffc9161402d565b9661100690613fee565b9361101090613fee565b929190610db6565b9396919290918b8561102d81600187016149a0565b905460039190911b1c6001600160a01b031691829161104b916148a9565b5280600052601360205260406000208c6000526020526040600020549080600052601360205260406000208d6000526020528b8d8c6040600020600201549484600052601360205260406000208360005260205260406000206004015492856000526013602052604060002090600052602052600160a01b600190036040600020600301541691600160a01b6001900360005416600052601494856020526040600020816000526020526040600020888154906111079161402d565b905583600052856020526040600020906000526020528b6040600020858154906111309161402d565b905561113b916148a9565b526111468d8c6148a9565b526111518c8c6148a9565b5261115c888d6148a9565b519160005260205260406000208c6000526020526040600020908154906111829161402d565b905561118d9161402d565b9661119790613fee565b936111a190613fee565b92919092610daa565b85600052601260205260406000209560018060a01b038754166000600189019360028a019182600460038d01549c0154926002849114611519575b5050855483549b92908c906111f981614fd3565b9461121461039161120d610391868661402d565b948461402d565b936000926000925b81841061141857505050506112388e9798999a9b9c9d9e614fd3565b966000905b80821061135757505050936113266113429460008051602061583a8339815191526112f98b999660008051602061581a833981519152610e739f9e9c60008051602061585a8339815191529e6112cf6113349a839e60018060a01b0360005416600052601460205260406000208560005260205260406000206112c188825461402d565b9055604051948594856150f4565b0390a1604080518c81526001600160a01b038c166020820152908101919091529081906060820190565b0390a1611318604051998a998a5260e060208b015260e08a019061512c565b9088820360408a0152613eee565b90868203606088015261512c565b908482036080860152613eee565b90600060a084015260c08301520390a1613fee565b90919461140c6114066114129260028c8f8f8c918f8b611376916149a0565b60018060a01b0391549060031b1c1660005260136020526040600020906000526020526040600020926113ab8b8554926148a9565b5260038301546001600160a01b0316806113c5848e6148a9565b528c6113d760048601549485926148a9565b5260005260146020526040600020906000526020526113fc604060002091825461402d565b905501549061402d565b96613fee565b92613fee565b9061123d565b90919293966105bb6105b5611511928f8f8f8b908f8f8f948b958f938f61148761148f916114488860029d6149a0565b60018060a01b0391549060031b1c169586600052601360205260406000209060005260205261055860406000209b6114828d5480946148ee565b61504d565b9586926148a9565b5260038701956114a98460018060a01b03895416926148a9565b526114ba60048801938454926148a9565b52600052601491826020526040600020846000526020526114e1604060002091825461402d565b9055549260018060a01b039054166000526020526040600020906000526020526113fc604060002091825461402d565b92919061121c565b9b9096935091508b806111e5565b634e487b7160e01b600052602160045260246000fd5b60405162461bcd60e51b8152602060048201526002602482015261313160f01b6044820152606490fd5b5060048510610cd3565b60405162461bcd60e51b8152602060048201526002602482015261031360f41b6044820152606490fd5b5082518214610c65565b823560ff8116810361028557815260209283019201610c3d565b613ed2565b3461028557600036600319011261028557602060405169152d02c7e14af68000008152f35b34610285576060366003190112610285576001600160401b036004358181116102855761161a903690600401613f78565b9060243581811161028557611633903690600401613f78565b906044359081116102855761164c903690600401613e74565b90611655613f96565b825161166e825184519083149081611b2a575b50614856565b60005b81811061174c57505060005b83518110156116b1576116ac906116a66001600160a01b0361169f83886148a9565b5116614a50565b50613fee565b61167d565b509060005b83518110156116e4576116df906116a66001600160a01b036116d883886148a9565b5116614ab4565b6116b6565b7fee34c4fff4314ef30863a798e2773a9aa5251ff332d9de421fdddcabfc423778611725856117418561173388604051958695608087526080870190613ce8565b908582036020870152613ce8565b908382036040850152613eee565b3360608301520390a1005b6117766001600160a01b0361176183886148a9565b51166000526010602052604060002054151590565b611b00576001600160a01b0361178c82856148a9565b511661181f5761179c81856148a9565b51156117f557806117b06117f092866148a9565b516001600160a01b036117c383896148a9565b51166000908152601660205260409020556116a66001600160a01b036117e983896148a9565b51166149b8565b611671565b60405162461bcd60e51b8152602060048201526002602482015261032360f41b6044820152606490fd5b6001600160a01b0361183182856148a9565b511660405190633fabe5a360e21b9081835260a083600481845afa8015611a5357600093600091611ad8575b506000841315611aae576118756201518091426148e1565b11611a845760405191825260a082600481845afa918215611a5357600092611a5f575b506000908190815b60058110611987575b50506118b592506153fd565b90606491826118c3826153e7565b059060008282039212818312811690828413901516176106d35780846118eb6118f2936153e7565b05906157fd565b90821215918261197c575b50501561195557506117f0906001600160a01b0361191b82866148a9565b51166001600160a01b0361192f83896148a9565b511660005260116020526040600020906001600160601b0360a01b825416179055613fee565b60405162461bcd60e51b8152602060048201526002602482015261323760f01b6044820152fd5b1315905087806118fd565b909160405190639a6fc8f560e01b82526001600160501b038616600483015260a082602481875afa908115611a53576119c892600092611a1d575b506157fd565b92600181018091116106d357936001600160501b03811615611a14576001600160501b036000199116016001600160501b0381116106d357611a0a9091613fee565b93929190936118a0565b849392506118a9565b611a4091925060a03d60a011611a4c575b611a388183613e3c565b8101906153a7565b5050509050908d6119c2565b503d611a2e565b6040513d6000823e3d90fd5b611a7991925060a03d60a011611a4c57611a388183613e3c565b505050509088611898565b60405162461bcd60e51b8152602060048201526002602482015261323560f01b6044820152606490fd5b60405162461bcd60e51b8152602060048201526002602482015261313960f01b6044820152606490fd5b9050611af491935060a03d60a011611a4c57611a388183613e3c565b5094925050928961185d565b60405162461bcd60e51b81526020600482015260026024820152611a1b60f11b6044820152606490fd5b9050821486611668565b346102855760208060031936011261028557611b4e613d74565b90600b8054611b5c81614fd3565b93611b6682614fd3565b926001600160a01b039182169060005b848110611bab57611b9a88611ba78989604051948594604086526040860190613ce8565b9184830390850152613eee565b0390f35b611c19908260005284817f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9015416611be3828b6148a9565b528360005260148852604060002085611bfc838c6148a9565b51166000528852604060002054611c1382896148a9565b52613fee565b611b76565b34610285576020366003190112610285577f39d65726005dded9e32c1c37c38c433a478814ed5df9a080f9788a8861cc383a6108bc611c5b613d55565b611c63613f96565b600a805460ff191660ff9215159283161790556040805191825233602083015290918291820190565b34610285576000366003190112610285576020600754604051908152f35b3461028557600036600319011261028557602060405160148152f35b34610285576000366003190112610285576000546040516001600160a01b039091168152602090f35b3461028557606036600319011261028557611d08613d74565b6044359060ff8216820361028557611d1e6157bf565b611d26614802565b611d316024356156d6565b611d3c602435615764565b60018060a01b0381166000526013602052604060002060243560005260205260ff600160406000200154161561231357602435600052601260205260406000209060018060a01b0382541691600181015491600282015460405193611da085613e06565b600185526020368187013760405191611db883613e06565b600183526020368185013760405194611dd086613e06565b600186526020368188013760405192611de884613e06565b60018452602036818601376001600160a01b038681166000908152601360209081526040808320602435845290915290208054600280830154600484015460039094015460058801549499951697909693909160089190911c60ff169081101561152757801590816122de575b81156122d3575b50156121395750600160ff611e7b8b611e7684870161403a565b6151f6565b9e16036120db57611e9090610558888761402d565b9a5b60018060a01b03891660005260146020526040600020906000526020526040600020611ebf8c825461402d565b905560038101611ed08c82546148e1565b9055826000198101116106d3577f679f73fac18389f339b511a489b811473ff4d0f3d4937002ed9951c7636cce3a6120236120c49960008051602061583a8339815191529860008051602061585a8339815191529f986113189f8f8f9091611fb88f611fbe946120709f611f9660008051602061581a8339815191529f8e926001611f64611f7993600019018287016149a0565b828060a01b0391549060031b1c1694016149a0565b819391549060031b9160018060a01b03809116831b921b19161790565b9055611fa460018d01615172565b6001600160a01b03891690611fb890614886565b52614886565b52611fc886614886565b526001600160a01b031660008181526013602090815260408083206024358085529083528184208481556001810185905560028101859055600381018590556004019390935580519384529083019190915290918291820190565b0390a160018060a01b03905416976120446040519283928b602435856150f4565b0390a16040805160243581526001600160a01b0388166020820152908101919091529081906060820190565b0390a161133460405161208281613e21565b600081526120b66040519361209685613e21565b600085526040519889986024358a5260e060208b015260e08a0190613ce8565b908682036060880152613ce8565b90600360a084015260c08301520390a16001600255005b9a60018060a01b03600054166000526014602052604060002081600052602052604060002061210b86825461402d565b9055856000526014602052604060002081600052602052604060002061213288825461402d565b9055611e92565b9350919a93600160ff6121548b611e766002879c970161403a565b9e160361226f5761216992916105589161402d565b986000946000935b60018060a01b0389166000526014602052604060002090600052602052604060002061219e8c825461402d565b9055600481016121af8c82546148e1565b9055826000198101116106d3577f679f73fac18389f339b511a489b811473ff4d0f3d4937002ed9951c7636cce3a6120236120c49960008051602061583a8339815191529860008051602061585a8339815191529f986113189f8f8f9091611fb88f611fbe946120709f61225c60008051602061581a8339815191529f8e926002612243611f7993600019018287016149a0565b905460039190911b1c6001600160a01b031694016149a0565b905561226a60028d01615172565b611fa4565b9360018060a09d949d9893981b0360005416600052601460205260406000208160005260205260406000206122a586825461402d565b905585600052601460205260406000208160005260205260406000206122cc88825461402d565b9055612171565b60019150148f611e5c565b905060018060a01b038b1660005260136020526040600020602435600052602052600160ff8160406000200154161490611e55565b60405162461bcd60e51b8152602060048201526002602482015261189960f11b6044820152606490fd5b34610285576000366003190112610285576020600654604051908152f35b3461028557602080600319360112610285576004356001600160401b0381116102855761238c903690600401613f78565b90612395613f96565b815160005b81811061244b57505060005b82518110156123d4576123cf906116a66001600160a01b036123c883876148a9565b5116614b1f565b6123a6565b5060005b825181101561240657612401906116a66001600160a01b036123fa83876148a9565b5116614c09565b6123d8565b7fe315bdee05329e2e69a5253f25814537fb86d11c4c808142d2ea502822516e7f6124408484604051928392604084526040840190613ce8565b9033908301520390a1005b6001600160a01b036124618161176184886148a9565b156124b85790816124b39261247683886148a9565b51166000526011855260406000206001600160601b0360a01b815416905561249e82876148a9565b51166000526016845260006040812055613fee565b61239a565b60405162461bcd60e51b8152600481018590526002602482015261343760f01b6044820152606490fd5b34610285576020366003190112610285577f1952e249d74df2e2b0c9a7de47fdd506d63de48981dfbffad2b925a1152928f96108bc600435612522613f96565b60075481101580612552575b61253790614901565b60078190556040805191825233602083015290918291820190565b506402540be40081111561252e565b34610285576000366003190112610285576001546001600160a01b0333818316036125cd576001600160601b0360a01b8092166001556000549133908316176000553391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a3005b60405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608490fd5b34610285576020366003190112610285576001600160a01b03612645613d74565b1660005260156020526126aa604060002060ff6002820154166126b860016126966040519461267f866126788184614d14565b0387613e3c565b61268f6040518094819301614d14565b0382613e3c565b604051948594606086526060860190613eee565b908482036020860152613eee565b90151560408301520390f35b610120366003190112610285576126d9613d64565b6084359060ff821682036102855760c435916001600160a01b03831683036102855761010435926001600160401b0384116102855761271f61001c943690600401613e74565b9260e4359260a435916044356024356004356140a0565b346102855760003660031901126102855761274f613f96565b606460405162461bcd60e51b815260206004820152602060248201527f52656e6f756e63696e67206f776e6572736869702069732064697361626c65646044820152fd5b34610285576080366003190112610285576004356001600160401b038111610285576127c3903690600401613e74565b6024356001600160401b038111610285576127e2903690600401613e74565b906044356001600160a01b0381168103610285576064351515606435036102855761280b613f96565b6001600160a01b0381166000908152600c602052604090205461282f901515614cb2565b81519183519183151580612b3d575b80612b32575b61284d90614856565b64174876e8009260005b856000198101116106d35760001986018110156128fc5761287881856148a9565b51600182018083116106d35761288e90866148a9565b51106128d2576064356128c0575b806128b6866128ae6128bb948b6148a9565b511115614ce3565b613fee565b612857565b69152d02c7e14af6800000945061289c565b60405162461bcd60e51b8152602060048201526002602482015261323160f01b6044820152606490fd5b5091859284606435612b21575b60001982019182116106d3576128ae61292292866148a9565b60405190606082018281106001600160401b03821117612ace5760405281526020810192835260408101906064351515825260018060a01b03831660005260156020526040600020905180516001600160401b038111612ace57600160401b91828211612ace578354828555808310612af8575b5060200183600052602060002060005b838110612ae4575050505060019485830190518051926001600160401b038411612ace578311612ace578154838355808410612aa3575b5060200190600052602060002060005b838110612a90577fbf48b0820ca81715d6b1acb25649f7ba1d18bd574712fcc4788a22a3f9e49229612a7a89898960028a019051151560ff8019835416911617905560018060a01b03166000526015602052604060002060ff60026040519485946040865260606040870152612a6660a0870186614d14565b868103603f19016060880152908501614d14565b92015416151560808301523360208301520390a1005b87906020845194019381840155016129ed565b826000528784602060002092830192015b828110612ac25750506129dd565b60008155018890612ab4565b634e487b7160e01b600052604160045260246000fd5b6001906020845194019381840155016129a6565b8460005282602060002091820191015b818110612b155750612996565b60008155600101612b08565b5069152d02c7e14af6800000612909565b506014841115612844565b5082841461283e565b346102855760208060031936011261028557612b60613d74565b612b68614802565b3360009081526014835260408082206001600160a01b039093168083529284529020548015612c935733600052601483526040600020826000528352600060408120558115600014612c3357600080808084335af1612bc5614ec0565b5015612c0957906060917f30f0f2f1c6b25d25ce05404ece9a5feb949e2e6f459f939cc1f01c6512428066935b604051928352820152336040820152a16001600255005b60405162461bcd60e51b8152600481018490526002602482015261333760f01b6044820152606490fd5b60405163a9059cbb60e01b84820152336024820152604480820183905281527f30f0f2f1c6b25d25ce05404ece9a5feb949e2e6f459f939cc1f01c6512428066936060939291612c8e90612c88606482613e3c565b84614dc4565b612bf2565b60405162461bcd60e51b81526004810184905260016024820152600760fb1b6044820152606490fd5b3461028557600036600319011261028557602060ff600a54166040519015158152f35b3461028557604036600319011261028557600435602435612cfe613f96565b80151580612d97575b80612d8e575b80612d83575b15612d59577f42a3e0b2ac4ef58d5c467d7199e859b2b40ee8a57507885e2d4539a9ae0a05da9181606092600655816005556040519182526020820152336040820152a1005b60405162461bcd60e51b8152602060048201526002602482015261323360f01b6044820152606490fd5b506032821115612d13565b50811515612d0d565b506032811115612d07565b346102855760203660031901126102855760206004358015159081612dcd575b506040519015158152f35b9050600954101582612dc2565b34610285576020806003193601126102855760043590604051612dfc81613db4565b60008152606082820152606060408201526000606082015260006080820152600060a0820152600060c0820152600060e0820152600061010080920152612e42836156d6565b826000526012825260406000209160405193612e5d85613db4565b83546001600160a01b03908116865292612e796001860161403a565b93838701948552612e8c6002870161403a565b93604088019485526003870154956060890196875260048801549060808a0191825260058901549260ff60a08c0194612ec782821687614094565b60081c169460c08c0195600281101561152757865260068b01549a60e08d019b8c526007015497878d01988952612efd90615639565b612f079086614094565b6040519b828d525116908b01525195610120968760408c01526101408b01612f2e91613ce8565b90518a8203601f190160608c0152612f469190613ce8565b965160808a01525160a08901525160088110156115275760c08801525194600286101561152757869560e0870152519085015251908301520390f35b34610285576020366003190112610285577fe2c8b1f4bcb8086dc9805a9868ee616914948de41a09786213a2284a8ffba27b6040600435612fc16157bf565b806003558151908152336020820152a1005b61010036600319011261028557612fe8613d64565b60a435906001600160a01b03821682036102855760e4356001600160401b0381116102855761301b903690600401613e74565b916130276004356156d6565b3360005260136020526040600020600435600052602052604060002060ff600182015416156134fe57600435600052601260205260406000209260018060a01b038454169460ff600a5416156134d457613082600435615639565b6008811015611527576002036134aa578561349b573460243503613471575b6130af60443560243561402d565b906130ba828861523d565b9460009360ff6008541661330e575b505050838111156132e4576130fe6130e9826130e489615418565b6148ee565b876132d3576130f8601261506d565b9061504d565b6000878152600e6020526040902054156132cd576131289060166020526040600020541115614901565b60443561321d575b9061317360a095949392877f0b17a6125c8c06bcd40d6a58de2c6f4e8815d9f52c8f8b6999ca7493c0fea31a98613206575b5061316d838661402d565b906148e1565b9261317f84845461402d565b83556131906002840191825461402d565b90556131a16004830191825461402d565b905560018181015460ff16036131f057600383016131c083825461402d565b90555b5491600180851b0390541690604051926004358452602084015260408301523360608301526080820152a1005b600483016131ff83825461402d565b90556131c3565b613217906024359030903390614d82565b88613162565b93929190336000526014602052604060002086600052602052604435604060002054106132a35761317360a0957f0b17a6125c8c06bcd40d6a58de2c6f4e8815d9f52c8f8b6999ca7493c0fea31a97336000526014602052604060002081600052602052604060002061329360443582546148e1565b9055975091929394955050613130565b60405162461bcd60e51b8152602060048201526002602482015261199b60f11b6044820152606490fd5b50613128565b6130f86132df89615097565b61506d565b60405162461bcd60e51b8152602060048201526002602482015261333560f01b6044820152606490fd5b60ff6040999693999895929794985191336020840152166040820152608435606082015260018060a01b038716608082015260c43560a082015260a0815261335581613deb565b60208151910120604051602081019182526020815261337381613e06565b5190209660035497966000975b8a518910156133cb57613393898c6148a9565b5190818110156133b7576000526020526133b1604060002098613fee565b97613380565b906000526020526133b160406000206105b5565b919497509295985096909396036134475769021e19e0c9bab2400000946084358603908682116106d3578691613400916148ee565b049485916001600160a01b0316613418575b806130c9565b909491925060c4358103918183116106d35761343f92613437916148ee565b0480946148e1565b908680613412565b60405162461bcd60e51b8152602060048201526002602482015261343560f01b6044820152606490fd5b60405162461bcd60e51b8152602060048201526002602482015261333360f01b6044820152606490fd5b6134a53415614d51565b6130a1565b60405162461bcd60e51b8152602060048201526002602482015261199960f11b6044820152606490fd5b60405162461bcd60e51b8152602060048201526002602482015261333160f01b6044820152606490fd5b60405162461bcd60e51b81526020600482015260016024820152603760f81b6044820152606490fd5b346102855760003660031901126102855760206040516202a3008152f35b3461028557600036600319011261028557602060ff600854166040519015158152f35b34610285576000366003190112610285576020600554604051908152f35b34610285576020366003190112610285577f46eb189395b90ab0cfd42a309b25f46e015815a01e7e676f6f804ef62466127d6108bc6135c3613d55565b6135cb613f96565b6008805460ff191660ff9215159283161790556040805191825233602083015290918291820190565b34610285576000366003190112610285576004546040516001600160a01b039091168152602090f35b346102855760003660031901126102855760206040516402540be4008152f35b34610285576060366003190112610285576001600160401b036024358181116102855761366e903690600401613d25565b909160443590811161028557613688903690600401613d25565b6136906157bf565b613698614802565b6136a36004356156d6565b6136ae60043561571f565b60043560005260126020526005604060002001805460ff8160081c16600281101561152757600103613c355782851480613c2a575b15613c00576000805b868210613b065768056bc75e2d63100000915003613adc5760059060ff19161790557fb6171b34500ba874839b220ee75cbfbeb25bba762c6a57c158e19a3c27af89ae60408051600435815260056020820152a1600435600052601260205260406000209160018060a01b0383541660c05260018301549161376d85614fd3565b9161377b61039187866148e1565b948761378a61039189886148e1565b9561379481614fd3565b9361379e82614fd3565b60a052600095600096898c6000965b8688106139125750505050505050509160008051602061581a8339815191526138346138629360008051602061583a8339815191529560018060a01b0360005416600052601460205260406000209060018060a01b03905416600052602052604060002061381c85825461402d565b905560405191829160a0519060c051600435856150f4565b0390a16040805160c05160043582526001600160a01b03166020820152908101919091529081906060820190565b0390a1604051938060e08601600435875260e06020880152526101008501959060005b8181106138ec5760008051602061585a83398151915287806138d3896138c58a6138b78f8c8782036040890152613eee565b908582036060870152613ce8565b908382036080850152613eee565b600160a083015260c05160c08301520390a16001600255005b909196602080600192838060a01b036139048c613da0565b168152019801929101613885565b879a969791899161394661392985600186016149a0565b905460039190911b1c6001600160a01b0316611e76368585613f22565b9061395485600186016149a0565b60018060a01b0391549060031b1c16600052601360205260406000206004356000526020526040600020608052828214600014613a5657505050926139e1926139c2826139aa6139d2956001613a4c99016149a0565b905460039190911b1c6001600160a01b0316926148a9565b528d611c138260805154926148a9565b945b600260805101549061402d565b9760805160048101906003825491019060018060a01b038254166000526014602052604060002060018060a01b038b5416600052602052613a28604060002091825461402d565b9055546001600160a01b0316613a3e838b6148a9565b5254611c138260a0516148a9565b9392898c8e6137ad565b6139e19550613a4c96999450613aa7613aa28368056bc75e2d63100000613a928d613a8b848f6003613ad69c9d0154936148bd565b35906148ee565b04966001600160a01b03946148bd565b6148cd565b166000526014602052604060002060c0516000526020526040600020613ace84825461402d565b90558d6148a9565b526139d4565b60405162461bcd60e51b8152602060048201526002602482015261313760f01b6044820152606490fd5b613b1c90613b158387896148bd565b359061402d565b9080613b9a575b6001600160a01b03613b39613aa2838a8c6148bd565b166000526013602052604060002060043560005260205260ff6001604060002001541615613b7057613b6a90613fee565b906136ec565b60405162461bcd60e51b8152602060048201526002602482015261313560f01b6044820152606490fd5b613ba8613aa282898b6148bd565b60001982018281116106d3576001600160a01b03908190613bce90613aa2908c8e6148bd565b16911611613b235760405162461bcd60e51b8152602060048201526002602482015261189b60f11b6044820152606490fd5b60405162461bcd60e51b81526020600482015260026024820152610c4d60f21b6044820152606490fd5b50600a8511156136e3565b60405162461bcd60e51b8152602060048201526002602482015261313360f01b6044820152606490fd5b3461028557600036600319011261028557600f5480825260209082828101600f6000527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac8029260005b85828210613cd257505050613cbe92500383613e3c565b611ba7604051928284938452830190613ce8565b8554845260019586019588955093019201613ca7565b90815180825260208080930193019160005b828110613d08575050505090565b83516001600160a01b031685529381019392810192600101613cfa565b9181601f84011215610285578235916001600160401b038311610285576020808501948460051b01011161028557565b60043590811515820361028557565b6064359060ff8216820361028557565b600435906001600160a01b038216820361028557565b602435906001600160a01b038216820361028557565b35906001600160a01b038216820361028557565b61012081019081106001600160401b03821117612ace57604052565b60a081019081106001600160401b03821117612ace57604052565b60c081019081106001600160401b03821117612ace57604052565b604081019081106001600160401b03821117612ace57604052565b602081019081106001600160401b03821117612ace57604052565b90601f801991011681019081106001600160401b03821117612ace57604052565b6001600160401b038111612ace5760051b60200190565b81601f8201121561028557803591613e8b83613e5d565b92613e996040519485613e3c565b808452602092838086019260051b820101928311610285578301905b828210613ec3575050505090565b81358152908301908301613eb5565b34610285576000366003190112610285576020604051600a8152f35b90815180825260208080930193019160005b828110613f0e575050505090565b835185529381019392810192600101613f00565b9291613f2d82613e5d565b91613f3b6040519384613e3c565b829481845260208094019160051b810192831161028557905b828210613f615750505050565b838091613f6d84613da0565b815201910190613f54565b9080601f8301121561028557816020613f9393359101613f22565b90565b6000546001600160a01b03163303613faa57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b60001981146106d35760010190565b1561400457565b60405162461bcd60e51b81526020600482015260016024820152603360f81b6044820152606490fd5b919082018092116106d357565b9060405191828154918282526020928383019160005283600020936000905b8282106140715750505061406f92500383613e3c565b565b85546001600160a01b031684526001958601958895509381019390910190614059565b60088210156115275752565b959492909693919760e05285600052601260205260406000206040516140c581613db4565b81546001600160a01b031681526140de6001830161403a565b60208201526140ef6002830161403a565b60408201526003820154606082015260048201546080820152600582015461411d60ff821660a08401614094565b600260ff8260081c1610156115275760c09260ff60079260081c1684840152600681015460e08401520154610100820152614157886156d6565b01516002811015611527576001036147b957600160ff821603614790575b33600052601360205260406000208660005260205260ff60016040600020015416614767576141a2614802565b80928660005260126020526040600020806101405260018060a01b039054169060ff600a5416156134d4576141d688615639565b6008811015611527576002036134aa578161475857348903613471575b6141fd8a8a61402d565b90614208828461523d565b9660009560ff600854166145d1575b505050858111156132e457614241614232826130e485615418565b836145c5576130f8601261506d565b6000838152600e6020526040902054156145b25761426f908360005260166020526040600020541115614901565b89614556575b61429160ff91838b600195614542575b505061316d868961402d565b9216148015614522575b156144f757600161014051016142b13382615005565b54600361014051016142c483825461402d565b90555b60ff600561014051015460081c16906002821015611527576001820361446f5760065410614445577f010fe3fa2b3358b8306447cc0a08515aa6c99e255d3d63b181fee6b377b6682398614432967ff6d42021a4549bed2c5e686f3a0e138917db52e05ad24e28d9239f7caef635ca9560a09560016143ec955b1461443c575b6040519260049261435785613dd0565b87855260ff602086019116815260408501908b825260608601926001808c1b03168352608086019384523360005260136020528d60406000209060005260205260406000209551865560ff6001870191511660ff1982541617905551600285015560038401906001808a1b039051166001600160601b03891b82541617905551910155600180851b036101405154169861402d565b6040519187835260208301523360408301528760608301526080820152a1604080519384526001600160a01b039094166020840152928201929092529081906060820190565b0390a16001600255565b60019150614347565b60405162461bcd60e51b8152602060048201526002602482015261033360f41b6044820152606490fd5b600554106144cd577f010fe3fa2b3358b8306447cc0a08515aa6c99e255d3d63b181fee6b377b6682398614432967ff6d42021a4549bed2c5e686f3a0e138917db52e05ad24e28d9239f7caef635ca9560a09560016143ec95614341565b60405162461bcd60e51b81526020600482015260026024820152610d0d60f21b6044820152606490fd5b600261014051016145083382615005565b546004610140510161451b83825461402d565b90556142c7565b5060ff600561014051015460081c1660028110156115275760011461429b565b61454f9130903390614d82565b388b614285565b33600052601460205260406000208260005260205289604060002054106132a35761429160ff916001933360005260146020526040600020816000526020528c6145a660406000209182546148e1565b90559350915050614275565b6145c0906007541115614901565b61426f565b6130f86132df85615097565b60409c9a989c9b99979593919694929b5160ff60208201923384521660408201528c606082015260018060a01b03891660808201528760a082015260a0815261461981613deb565b519020604051602081019182526020815261463381613e06565b5190209b6003549c610120526000610100525b60e05180519061010051918210156146aa5790614662916148a9565b51610120518181101561469957506101205160005260205260406000205b6101205261469061010051613fee565b61010052614646565b906000526020526040600020614680565b505091939597999b9a90929496989a61012051036134475769021e19e0c9bab2400000039069021e19e0c9bab240000082116106d35769021e19e0c9bab2400000916146f5916148ee565b049586906001600160a01b03891661470e575b80614217565b909691945069021e19e0c9bab24000000369021e19e0c9bab240000081116106d35761474869021e19e0c9bab240000091614750936148ee565b0480966148e1565b923880614708565b6147623415614d51565b6141f3565b60405162461bcd60e51b81526020600482015260016024820152601b60f91b6044820152606490fd5b60405162461bcd60e51b81526020600482015260016024820152600d60fa1b6044820152606490fd5b60ff8116600181149081156147f7575b506141755760405162461bcd60e51b81526020600482015260016024820152603560f81b6044820152606490fd5b6002915014386147c9565b60028054146148115760028055565b60405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606490fd5b1561485d57565b60405162461bcd60e51b81526020600482015260016024820152603960f81b6044820152606490fd5b8051156148935760200190565b634e487b7160e01b600052603260045260246000fd5b80518210156148935760209160051b010190565b91908110156148935760051b0190565b356001600160a01b03811681036102855790565b919082039182116106d357565b818102929181159184041417156106d357565b1561490857565b60405162461bcd60e51b8152602060048201526002602482015261064760f31b6044820152606490fd5b600f5481101561489357600f6000527f8d1108e10bcb7c27dddfc02ed9d693a074039d026cf4ea4240b40f7d581ac8020190600090565b600d5481101561489357600d6000527fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50190600090565b80548210156148935760005260206000200190600090565b6000818152600e6020526040812054614a4b57600d54600160401b811015614a37576001810180600d55811015614a235790826040927fd7b6990105719101dabeb77144f2a3385c8033acd3af97e9423a695e81ad1eb50155600d54928152600e6020522055600190565b634e487b7160e01b82526032600452602482fd5b634e487b7160e01b82526041600452602482fd5b905090565b600081815260106020526040812054614a4b57600f54600160401b811015614a37579082614aa0614a8984600160409601600f55614932565b819391549060031b91821b91600019901b19161790565b9055600f5492815260106020522055600190565b6000818152600c6020526040812054614a4b57600b54600160401b811015614a37576001810180600b55811015614a235790826040927f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90155600b54928152600c6020522055600190565b6000818152601060205260408120549091908015614c045760001990808201818111614bf057600f5490838201918211614bdc57808203614ba8575b505050600f548015614b9457810190614b7382614932565b909182549160031b1b19169055600f55815260106020526040812055600190565b634e487b7160e01b84526031600452602484fd5b614bc6614bb7614a8993614932565b90549060031b1c928392614932565b9055845260106020526040842055388080614b5b565b634e487b7160e01b86526011600452602486fd5b634e487b7160e01b85526011600452602485fd5b505090565b6000818152600e60205260408120549091908015614c045760001990808201818111614bf057600d5490838201918211614bdc57808203614c7e575b505050600d548015614b9457810190614c5d82614969565b909182549160031b1b19169055600d558152600e6020526040812055600190565b614c9c614c8d614a8993614969565b90549060031b1c928392614969565b90558452600e6020526040842055388080614c45565b15614cb957565b60405162461bcd60e51b8152602060048201526002602482015261068760f31b6044820152606490fd5b15614cea57565b60405162461bcd60e51b8152602060048201526002602482015261191960f11b6044820152606490fd5b90815480825260208092019260005281600020916000905b828210614d3a575050505090565b835485529384019360019384019390910190614d2c565b15614d5857565b60405162461bcd60e51b81526020600482015260026024820152610ccd60f21b6044820152606490fd5b6040516323b872dd60e01b60208201526001600160a01b03928316602482015292909116604483015260648083019390935291815261406f91614dc482613dd0565b60018060a01b031690614e23604051614ddc81613e06565b6020938482527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564858301526000808587829751910182855af1614e1d614ec0565b91614eff565b805191821591848315614e95575b505050905015614e3e5750565b6084906040519062461bcd60e51b82526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152fd5b919381809450010312614ebc57820151908115158203614eb9575080388084614e31565b80fd5b5080fd5b3d15614efa573d906001600160401b038211612ace5760405191614eee601f8201601f191660200184613e3c565b82523d6000602084013e565b606090565b91929015614f615750815115614f13575090565b3b15614f1c5790565b60405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606490fd5b825190915015614f745750805190602001fd5b6040519062461bcd60e51b82528160208060048301528251908160248401526000935b828510614fba575050604492506000838284010152601f80199101168101030190fd5b8481018201518686016044015293810193859350614f97565b90614fdd82613e5d565b614fea6040519182613e3c565b8281528092614ffb601f1991613e5d565b0190602036910137565b805490600160401b821015612ace5781615027916001615049940181556149a0565b815491936001600160a01b0360039290921b82811b19909316911690911b1790565b9055565b8115615057570490565b634e487b7160e01b600052601260045260246000fd5b60ff16604d81116106d357600a0a90565b90816020910312610285575160ff811681036102855790565b60405163313ce56760e01b815290602090829060049082906001600160a01b03165afa908115611a53576000916150cc575090565b613f93915060203d81116150ed575b6150e58183613e3c565b81019061507e565b503d6150db565b92613f93949261511e92855260018060a01b03166020850152608060408501526080840190613ce8565b916060818403910152613eee565b90815480825260208092019260005281600020916000905b828210615152575050505090565b83546001600160a01b031685529384019360019384019390910190615144565b8054801561519f57600019019061518982826149a0565b81549060018060a01b039060031b1b1916905555565b634e487b7160e01b600052603160045260246000fd5b9491936120b66151df9461131860c097611334959b9a9b8a5260e060208b015260e08a0190613ce8565b600260a08401526001600160a01b03909416910152565b9081519060005b82811061520b575050905090565b6001600160a01b038061521e83876148a9565b5116908316146152365761523190613fee565b6151fd565b9250505090565b60018060a01b0381169161526d600091848352600c60205261526460408420541515614cb2565b6130e484615418565b9061528684159283600014615387576130f8601261506d565b9381526015602052604081209384548290915b82821061532c57505080156153245760001981019081116153105760026152c560ff92600188016149a0565b90549060031b1c9501541661530a57613f93936130f8926152f892506000146152fe576152f2601261506d565b906148ee565b91615418565b6152f26132df85615097565b50505090565b634e487b7160e01b82526011600452602482fd5b509250505090565b9091600190615341818518831c82861661402d565b918361534d848b6149a0565b90549060031b1c11600014615366575050915b90615299565b90935081018091111561536057634e487b7160e01b84526011600452602484fd5b6130f86132df86615097565b51906001600160501b038216820361028557565b908160a0910312610285576153bb81615393565b91602082015191604081015191613f93608060608401519301615393565b604d81116106d357600a0a90565b906005820291808305600514901517156106d357565b811561505757600160ff1b81146000198314166106d3570590565b6001600160a01b039081166000818152600e602090815260408083205491949293911561544c5750505050506305f5e10090565b60a09460119384825280838720541693835197888096633fabe5a360e21b825260049788915afa97881561562f578798615606575b5086976000881390816155da575b50156155b257918091859360005286825284600020541684519384809263313ce56760e01b82525afa9283156155a857509060ff929160009261558b575b50501692600884111561550e575060071983019283116154fc575050906154f6613f93926153d9565b906153fd565b634e487b7160e01b6000525260246000fd5b926008811061551f575b5050505090565b90919293506008036008811161557857615538906153d9565b838102939060008212600160ff1b8214166155645781850514901517156154fc57505038808080615518565b5050634e487b7160e01b6000525260246000fd5b50634e487b7160e01b6000525260246000fd5b6155a19250803d106150ed576150e58183613e3c565b38806154cd565b513d6000823e3d90fd5b835162461bcd60e51b81528086018490526002602482015261343360f01b6044820152606490fd5b426201517f198101925082116155f25710153861548f565b8787634e487b7160e01b6000525260246000fd5b90975061562291965060a03d8111611a4c57611a388183613e3c565b5097925050959638615481565b84513d89823e3d90fd5b6000908152601260205260408120600760ff60058301541691015491600882101590816156a457600483149182156156c9575b82156156b8575b8215615691575b5050614a4b5750421161568c57600290565b600390565b9091506156a4575060078114388061567a565b634e487b7160e01b81526021600452602490fd5b8092506156a4576006831491615673565b506005831491508061566c565b8015159081615712575b50156156e857565b60405162461bcd60e51b8152602060048201526002602482015261066760f31b6044820152606490fd5b90506009541015386156e0565b61572890615639565b60088110156115275760030361573a57565b60405162461bcd60e51b8152602060048201526002602482015261333960f01b6044820152606490fd5b61576d90615639565b600881101561152757600381149081156157b4575b501561578a57565b60405162461bcd60e51b8152602060048201526002602482015261034360f41b6044820152606490fd5b600291501438615782565b6004546001600160a01b031633036157d357565b60405162461bcd60e51b8152602060048201526002602482015261343160f01b6044820152606490fd5b919091600083820193841291129080158216911516176106d35756fe7620380c554abbc2f2c8551155ba9b12b3fcd40700f9a718a43678a914753048a933c5f2ff3ab65b8004e747a48707dab64e66636a6ff5247f0431bc16881774eb0cc7968044d5a32d1b2d53427cbc63a081ddf73f5afb1637a4dfbdea2f7db9a26469706673582212205cb023200ab77eb6105b029109f1d79a2294df8d75da4112a2520090bd92d02964736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000cd854bd769287bee0b9ac77712c32b12b27cb8e4
-----Decoded View---------------
Arg [0] : backend_ (address): 0xcd854Bd769287Bee0b9ac77712C32B12B27Cb8e4
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000cd854bd769287bee0b9ac77712c32b12b27cb8e4
Loading...
Loading
Loading...
Loading
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.