Token Polky
Overview ERC721
Total Supply:
130 POLKY
Holders:
46 addresses
Contract:
Balance
1 POLKY
[ Download CSV Export ]
[ Download CSV Export ]
# | Exchange | Pair | Price | 24H Volume | % Volume |
---|
Contract Name:
Achievements
Compiler Version
v0.6.2+commit.bacdbe57
Optimization Enabled:
Yes with 1 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "./RealitioERC20.sol"; import "./PredictionMarket.sol"; // openzeppelin imports import "@openzeppelin/contracts/math/SafeMath.sol"; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; /// @title Prediction Market Achievements Contract contract Achievements is ERC721 { using Counters for Counters.Counter; Counters.Counter private _tokenIds; /// @dev protocol is immutable and has no ownership RealitioERC20 public realitioERC20; PredictionMarket public predictionMarket; event LogNewAchievement( uint256 indexed achievementId, address indexed user, string content ); // Buy: Claim through market[x].outcome[y].shares.holders[msg.sender] > 0 // AddLiquidity: Claim through market[x].liquidityShares[msg.sender] > 0 // CreateMarket: RealitioERC20 question arbitrator == msg.sender // ClaimWinnings: Claim through market[x].outcome[y].shares.holders[msg.sender] > 0 && y == market.resolution.outcomeId // Bond: Use RealitioERC20 historyHashes logic value and ensure addrs.includes(msg.sender) enum Action { Buy, AddLiquidity, Bond, ClaimWinnings, CreateMarket } struct Achievement { Action action; uint256 occurrences; mapping(address => bool) claims; } uint256 public achievementIndex = 0; mapping(uint256 => Achievement) public achievements; mapping(uint256 => uint256) public tokens; // tokenId => achievementId constructor(string memory token, string memory ticker) public ERC721(token, ticker) {} function setContracts(PredictionMarket _predictionMarket, RealitioERC20 _realitioERC20) public { require(address(predictionMarket) == address(0), "predictionMarket can only be initialized once"); require(address(realitioERC20) == address(0), "realitioERC20 can only be initialized once"); require(address(_predictionMarket) != address(0), "_predictionMarket address is 0"); require(address(_realitioERC20) != address(0), "_realitioERC20 address is 0"); predictionMarket = _predictionMarket; realitioERC20 = _realitioERC20; } function setBaseURI(string memory _baseURI) public { require(bytes(baseURI()).length == 0, "baseURI can only be initialized once"); _setBaseURI(_baseURI); } function createAchievement(Action action, uint256 occurrences, string memory content) public returns (uint256) { require(occurrences > 0, "occurrences has to be greater than 0"); uint256 achievementId = achievementIndex; Achievement storage achievement = achievements[achievementId]; achievement.action = action; achievement.occurrences = occurrences; emit LogNewAchievement(achievementId, msg.sender, content); achievementIndex = achievementId + 1; return achievementId; } function hasUserClaimedAchievement(address user, uint256 achievementId) public view returns (bool) { Achievement storage achievement = achievements[achievementId]; return achievement.claims[user]; } function hasUserPlacedPrediction(address user, uint256 marketId) public view returns (bool) { uint256[2] memory outcomeShares; (, outcomeShares[0], outcomeShares[1]) = predictionMarket.getUserMarketShares(marketId, user); require(outcomeShares[0] > 0 || outcomeShares[1] > 0, "user does not hold outcome shares"); return true; } function hasUserAddedLiquidity(address user, uint256 marketId) public view returns (bool) { uint256 liquidityShares; (liquidityShares, , ) = predictionMarket.getUserMarketShares(marketId, user); require(liquidityShares > 0, "user does not hold liquidity shares"); return true; } function hasUserPlacedBond( address user, uint256 marketId, bytes32[] memory history_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers ) public view returns (bool) { bytes32 last_history_hash; bytes32 questionId; (, questionId, ) = predictionMarket.getMarketAltData(marketId); (, , , , , , , , last_history_hash, ) = realitioERC20.questions(questionId); bool bonded; uint256 i; for (i = 0; i < history_hashes.length; i++) { require( last_history_hash == keccak256(abi.encodePacked(history_hashes[i], answers[i], bonds[i], addrs[i], false)) ); if (addrs[i] == user) bonded = true; last_history_hash = history_hashes[i]; } require(bonded == true, "user has not placed a bond in market"); return true; } function hasUserClaimedWinnings(address user, uint256 marketId) public view returns (bool) { uint256[2] memory outcomeShares; (, outcomeShares[0], outcomeShares[1]) = predictionMarket.getUserMarketShares(marketId, user); int256 resolvedOutcomeId = predictionMarket.getMarketResolvedOutcome(marketId); require(resolvedOutcomeId >= 0, "market is still not resolved"); require(outcomeShares[uint256(resolvedOutcomeId)] > 0, "user does not hold winning outcome shares"); return true; } function hasUserCreatedMarket(address user, uint256 marketId) public view returns (bool) { require(user != address(0), "user address is 0x0"); bytes32 questionId; address arbitrator; (, questionId, ) = predictionMarket.getMarketAltData(marketId); (, arbitrator, , , , , , , , ) = realitioERC20.questions(questionId); require(user == arbitrator, "user did not create market"); return true; } function claimAchievement(uint256 achievementId, uint256[] memory marketIds) public returns (uint256) { Achievement storage achievement = achievements[achievementId]; require(achievement.claims[msg.sender] == false, "Achievement already claimed"); require(achievement.action != Action.Bond, "Method not used for bond placement achievements"); require(marketIds.length == achievement.occurrences, "Markets count and occurrences don't match"); for (uint256 i = 0; i < marketIds.length; i++) { uint256 marketId = marketIds[i]; if (achievement.action == Action.Buy) { hasUserPlacedPrediction(msg.sender, marketId); } else if (achievement.action == Action.AddLiquidity) { hasUserAddedLiquidity(msg.sender, marketId); } else if (achievement.action == Action.ClaimWinnings) { hasUserClaimedWinnings(msg.sender, marketId); } else if (achievement.action == Action.CreateMarket) { hasUserCreatedMarket(msg.sender, marketId); } else { revert("Invalid achievement action"); } } mintAchievement(msg.sender, achievementId); } function claimAchievement( uint256 achievementId, uint256[] memory marketIds, uint256[] memory lengths, bytes32[] memory history_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers ) public { Achievement storage achievement = achievements[achievementId]; require(achievement.claims[msg.sender] == false, "Achievement already claimed"); require(achievement.action == Action.Bond, "Method only used for bond placement achievements"); require(marketIds.length > 0, "No actions provided"); require(marketIds.length == achievement.occurrences, "Markets count and occurrences don't match"); uint256 qi; for (uint256 i = 0; i < marketIds.length; i++) { uint256 marketId = marketIds[i]; uint256 ln = lengths[i]; bytes32[] memory hh = new bytes32[](ln); address[] memory ad = new address[](ln); uint256[] memory bo = new uint256[](ln); bytes32[] memory an = new bytes32[](ln); uint256 j; for (j = 0; j < ln; j++) { hh[j] = history_hashes[qi]; ad[j] = addrs[qi]; bo[j] = bonds[qi]; an[j] = answers[qi]; qi++; } hasUserPlacedBond(msg.sender, marketId, hh, ad, bo, an); } mintAchievement(msg.sender, achievementId); } function mintAchievement(address user, uint256 achievementId) private returns (uint256) { _tokenIds.increment(); uint256 tokenId = _tokenIds.current(); _mint(user, tokenId); tokens[tokenId] = achievementId; Achievement storage achievement = achievements[achievementId]; achievement.claims[user] = true; return tokenId; } function tokenIndex() public view returns (uint256) { return _tokenIds.current(); } }
/** *Submitted for verification at Etherscan.io on 2021-06-09 */ pragma solidity >0.4.24; /** * @title ERC20 interface * @dev see https://github.com/ethereum/EIPs/issues/20 */ interface IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address who) external view returns (uint256); function allowance(address owner, address spender) external view returns (uint256); function transfer(address to, uint256 value) external returns (bool); function approve(address spender, uint256 value) external returns (bool); function transferFrom(address from, address to, uint256 value) external returns (bool); event Transfer(address indexed from, address indexed to, uint256 value); event Approval(address indexed owner, address indexed spender, uint256 value); } pragma solidity >0.4.24; /** * @title ReailtioSafeMath256 * @dev Math operations with safety checks that throw on error */ library RealitioSafeMath256 { function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) { return 0; } uint256 c = a * b; assert(c / a == b); return c; } function div(uint256 a, uint256 b) internal pure returns (uint256) { // assert(b > 0); // Solidity automatically throws when dividing by 0 uint256 c = a / b; // assert(a == b * c + a % b); // There is no case in which this doesn't hold return c; } function sub(uint256 a, uint256 b) internal pure returns (uint256) { assert(b <= a); return a - b; } function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; assert(c >= a); return c; } } pragma solidity >0.4.24; /** * @title RealitioSafeMath32 * @dev Math operations with safety checks that throw on error * @dev Copy of SafeMath but for uint32 instead of uint256 * @dev Deleted functions we don't use */ library RealitioSafeMath32 { function add(uint32 a, uint32 b) internal pure returns (uint32) { uint32 c = a + b; assert(c >= a); return c; } } pragma solidity >0.4.18; contract BalanceHolder { IERC20 public token; mapping(address => uint256) public balanceOf; event LogWithdraw( address indexed user, uint256 amount ); function withdraw() public { uint256 bal = balanceOf[msg.sender]; balanceOf[msg.sender] = 0; require(token.transfer(msg.sender, bal)); emit LogWithdraw(msg.sender, bal); } } pragma solidity >0.4.24; contract RealitioERC20 is BalanceHolder { using RealitioSafeMath256 for uint256; using RealitioSafeMath32 for uint32; address constant NULL_ADDRESS = address(0); // History hash when no history is created, or history has been cleared bytes32 constant NULL_HASH = bytes32(0); // An unitinalized finalize_ts for a question will indicate an unanswered question. uint32 constant UNANSWERED = 0; // An unanswered reveal_ts for a commitment will indicate that it does not exist. uint256 constant COMMITMENT_NON_EXISTENT = 0; // Commit->reveal timeout is 1/8 of the question timeout (rounded down). uint32 constant COMMITMENT_TIMEOUT_RATIO = 8; event LogSetQuestionFee( address arbitrator, uint256 amount ); event LogNewTemplate( uint256 indexed template_id, address indexed user, string question_text ); event LogNewQuestion( bytes32 indexed question_id, address indexed user, uint256 template_id, string question, bytes32 indexed content_hash, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 created ); event LogFundAnswerBounty( bytes32 indexed question_id, uint256 bounty_added, uint256 bounty, address indexed user ); event LogNewAnswer( bytes32 answer, bytes32 indexed question_id, bytes32 history_hash, address indexed user, uint256 bond, uint256 ts, bool is_commitment ); event LogAnswerReveal( bytes32 indexed question_id, address indexed user, bytes32 indexed answer_hash, bytes32 answer, uint256 nonce, uint256 bond ); event LogNotifyOfArbitrationRequest( bytes32 indexed question_id, address indexed user ); event LogFinalize( bytes32 indexed question_id, bytes32 indexed answer ); event LogClaim( bytes32 indexed question_id, address indexed user, uint256 amount ); struct Question { bytes32 content_hash; address arbitrator; uint32 opening_ts; uint32 timeout; uint32 finalize_ts; bool is_pending_arbitration; uint256 bounty; bytes32 best_answer; bytes32 history_hash; uint256 bond; } // Stored in a mapping indexed by commitment_id, a hash of commitment hash, question, bond. struct Commitment { uint32 reveal_ts; bool is_revealed; bytes32 revealed_answer; } // Only used when claiming more bonds than fits into a transaction // Stored in a mapping indexed by question_id. struct Claim { address payee; uint256 last_bond; uint256 queued_funds; } uint256 nextTemplateID = 0; mapping(uint256 => uint256) public templates; mapping(uint256 => bytes32) public template_hashes; mapping(bytes32 => Question) public questions; mapping(bytes32 => Claim) public question_claims; mapping(bytes32 => Commitment) public commitments; mapping(address => uint256) public arbitrator_question_fees; modifier onlyArbitrator(bytes32 question_id) { require(msg.sender == questions[question_id].arbitrator, "msg.sender must be arbitrator"); _; } modifier stateAny() { _; } modifier stateNotCreated(bytes32 question_id) { require(questions[question_id].timeout == 0, "question must not exist"); _; } modifier stateOpen(bytes32 question_id) { require(questions[question_id].timeout > 0, "question must exist"); require(!questions[question_id].is_pending_arbitration, "question must not be pending arbitration"); uint32 finalize_ts = questions[question_id].finalize_ts; require(finalize_ts == UNANSWERED || finalize_ts > uint32(now), "finalization deadline must not have passed"); uint32 opening_ts = questions[question_id].opening_ts; require(opening_ts == 0 || opening_ts <= uint32(now), "opening date must have passed"); _; } modifier statePendingArbitration(bytes32 question_id) { require(questions[question_id].is_pending_arbitration, "question must be pending arbitration"); _; } modifier stateOpenOrPendingArbitration(bytes32 question_id) { require(questions[question_id].timeout > 0, "question must exist"); uint32 finalize_ts = questions[question_id].finalize_ts; require(finalize_ts == UNANSWERED || finalize_ts > uint32(now), "finalization dealine must not have passed"); uint32 opening_ts = questions[question_id].opening_ts; require(opening_ts == 0 || opening_ts <= uint32(now), "opening date must have passed"); _; } modifier stateFinalized(bytes32 question_id) { require(isFinalized(question_id), "question must be finalized"); _; } modifier bondMustDouble(bytes32 question_id, uint256 tokens) { require(tokens > 0, "bond must be positive"); require(tokens >= (questions[question_id].bond.mul(2)), "bond must be double at least previous bond"); _; } modifier previousBondMustNotBeatMaxPrevious(bytes32 question_id, uint256 max_previous) { if (max_previous > 0) { require(questions[question_id].bond <= max_previous, "bond must exceed max_previous"); } _; } function setToken(IERC20 _token) public { require(token == IERC20(0x0), "Token can only be initialized once"); token = _token; } /// @notice Constructor, sets up some initial templates /// @dev Creates some generalized templates for different question types used in the DApp. constructor() public { createTemplate('{"title": "%s", "type": "bool", "category": "%s", "lang": "%s"}'); createTemplate('{"title": "%s", "type": "uint", "decimals": 18, "category": "%s", "lang": "%s"}'); createTemplate('{"title": "%s", "type": "single-select", "outcomes": [%s], "category": "%s", "lang": "%s"}'); createTemplate('{"title": "%s", "type": "multiple-select", "outcomes": [%s], "category": "%s", "lang": "%s"}'); createTemplate('{"title": "%s", "type": "datetime", "category": "%s", "lang": "%s"}'); } /// @notice Function for arbitrator to set an optional per-question fee. /// @dev The per-question fee, charged when a question is asked, is intended as an anti-spam measure. /// @param fee The fee to be charged by the arbitrator when a question is asked function setQuestionFee(uint256 fee) stateAny() external { arbitrator_question_fees[msg.sender] = fee; emit LogSetQuestionFee(msg.sender, fee); } /// @notice Create a reusable template, which should be a JSON document. /// Placeholders should use gettext() syntax, eg %s. /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage. /// @param content The template content /// @return The ID of the newly-created template, which is created sequentially. function createTemplate(string memory content) stateAny() public returns (uint256) { uint256 id = nextTemplateID; templates[id] = block.number; template_hashes[id] = keccak256(abi.encodePacked(content)); emit LogNewTemplate(id, msg.sender, content); nextTemplateID = id.add(1); return id; } /// @notice Create a new reusable template and use it to ask a question /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage. /// @param content The template content /// @param question A string containing the parameters that will be passed into the template to make the question /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer /// @param opening_ts If set, the earliest time it should be possible to answer the question. /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question. /// @return The ID of the newly-created template, which is created sequentially. function createTemplateAndAskQuestion( string memory content, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce ) // stateNotCreated is enforced by the internal _askQuestion public returns (bytes32) { uint256 template_id = createTemplate(content); return askQuestion(template_id, question, arbitrator, timeout, opening_ts, nonce); } /// @notice Ask a new question without a bounty and return the ID /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage. /// @dev Calling without the token param will only work if there is no arbitrator-set question fee. /// @dev This has the same function signature as askQuestion() in the non-ERC20 version, which is optionally payable. /// @param template_id The ID number of the template the question will use /// @param question A string containing the parameters that will be passed into the template to make the question /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer /// @param opening_ts If set, the earliest time it should be possible to answer the question. /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question. /// @return The ID of the newly-created question, created deterministically. function askQuestion(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce) // stateNotCreated is enforced by the internal _askQuestion public returns (bytes32) { require(templates[template_id] > 0, "template must exist"); bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question)); bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, msg.sender, nonce)); _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, 0); emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, now); return question_id; } /// @notice Ask a new question with a bounty and return the ID /// @dev Template data is only stored in the event logs, but its block number is kept in contract storage. /// @param template_id The ID number of the template the question will use /// @param question A string containing the parameters that will be passed into the template to make the question /// @param arbitrator The arbitration contract that will have the final word on the answer if there is a dispute /// @param timeout How long the contract should wait after the answer is changed before finalizing on that answer /// @param opening_ts If set, the earliest time it should be possible to answer the question. /// @param nonce A user-specified nonce used in the question ID. Change it to repeat a question. /// @param tokens The combined initial question bounty and question fee /// @return The ID of the newly-created question, created deterministically. function askQuestionERC20(uint256 template_id, string memory question, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 nonce, uint256 tokens) // stateNotCreated is enforced by the internal _askQuestion public returns (bytes32) { _deductTokensOrRevert(tokens); require(templates[template_id] > 0, "template must exist"); bytes32 content_hash = keccak256(abi.encodePacked(template_id, opening_ts, question)); bytes32 question_id = keccak256(abi.encodePacked(content_hash, arbitrator, timeout, msg.sender, nonce)); _askQuestion(question_id, content_hash, arbitrator, timeout, opening_ts, tokens); emit LogNewQuestion(question_id, msg.sender, template_id, question, content_hash, arbitrator, timeout, opening_ts, nonce, now); return question_id; } function _deductTokensOrRevert(uint256 tokens) internal { if (tokens == 0) { return; } uint256 bal = balanceOf[msg.sender]; // Deduct any tokens you have in your internal balance first if (bal > 0) { if (bal >= tokens) { balanceOf[msg.sender] = bal.sub(tokens); return; } else { tokens = tokens.sub(bal); balanceOf[msg.sender] = 0; } } // Now we need to charge the rest from require(token.transferFrom(msg.sender, address(this), tokens), "Transfer of tokens failed, insufficient approved balance?"); return; } function _askQuestion(bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 timeout, uint32 opening_ts, uint256 tokens) stateNotCreated(question_id) internal { uint256 bounty = tokens; // A timeout of 0 makes no sense, and we will use this to check existence require(timeout > 0, "timeout must be positive"); require(timeout < 365 days, "timeout must be less than 365 days"); require(arbitrator != NULL_ADDRESS, "arbitrator must be set"); // The arbitrator can set a fee for asking a question. // This is intended as an anti-spam defence. // The fee is waived if the arbitrator is asking the question. // This allows them to set an impossibly high fee and make users proxy the question through them. // This would allow more sophisticated pricing, question whitelisting etc. if (msg.sender != arbitrator) { uint256 question_fee = arbitrator_question_fees[arbitrator]; require(bounty >= question_fee, "Tokens provided must cover question fee"); bounty = bounty.sub(question_fee); balanceOf[arbitrator] = balanceOf[arbitrator].add(question_fee); } questions[question_id].content_hash = content_hash; questions[question_id].arbitrator = arbitrator; questions[question_id].opening_ts = opening_ts; questions[question_id].timeout = timeout; questions[question_id].bounty = bounty; } /// @notice Add funds to the bounty for a question /// @dev Add bounty funds after the initial question creation. Can be done any time until the question is finalized. /// @param question_id The ID of the question you wish to fund /// @param tokens The number of tokens to fund function fundAnswerBountyERC20(bytes32 question_id, uint256 tokens) stateOpen(question_id) external { _deductTokensOrRevert(tokens); questions[question_id].bounty = questions[question_id].bounty.add(tokens); emit LogFundAnswerBounty(question_id, tokens, questions[question_id].bounty, msg.sender); } /// @notice Submit an answer for a question. /// @dev Adds the answer to the history and updates the current "best" answer. /// May be subject to front-running attacks; Substitute submitAnswerCommitment()->submitAnswerReveal() to prevent them. /// @param question_id The ID of the question /// @param answer The answer, encoded into bytes32 /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction. /// @param tokens The amount of tokens to submit function submitAnswerERC20(bytes32 question_id, bytes32 answer, uint256 max_previous, uint256 tokens) stateOpen(question_id) bondMustDouble(question_id, tokens) previousBondMustNotBeatMaxPrevious(question_id, max_previous) external { _deductTokensOrRevert(tokens); _addAnswerToHistory(question_id, answer, msg.sender, tokens, false); _updateCurrentAnswer(question_id, answer, questions[question_id].timeout); } // @notice Verify and store a commitment, including an appropriate timeout // @param question_id The ID of the question to store // @param commitment The ID of the commitment function _storeCommitment(bytes32 question_id, bytes32 commitment_id) internal { require(commitments[commitment_id].reveal_ts == COMMITMENT_NON_EXISTENT, "commitment must not already exist"); uint32 commitment_timeout = questions[question_id].timeout / COMMITMENT_TIMEOUT_RATIO; commitments[commitment_id].reveal_ts = uint32(now).add(commitment_timeout); } /// @notice Submit the hash of an answer, laying your claim to that answer if you reveal it in a subsequent transaction. /// @dev Creates a hash, commitment_id, uniquely identifying this answer, to this question, with this bond. /// The commitment_id is stored in the answer history where the answer would normally go. /// Does not update the current best answer - this is left to the later submitAnswerReveal() transaction. /// @param question_id The ID of the question /// @param answer_hash The hash of your answer, plus a nonce that you will later reveal /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction. /// @param _answerer If specified, the address to be given as the question answerer. Defaults to the sender. /// @param tokens Number of tokens sent /// @dev Specifying the answerer is useful if you want to delegate the commit-and-reveal to a third-party. function submitAnswerCommitmentERC20(bytes32 question_id, bytes32 answer_hash, uint256 max_previous, address _answerer, uint256 tokens) stateOpen(question_id) bondMustDouble(question_id, tokens) previousBondMustNotBeatMaxPrevious(question_id, max_previous) external { _deductTokensOrRevert(tokens); bytes32 commitment_id = keccak256(abi.encodePacked(question_id, answer_hash, tokens)); address answerer = (_answerer == NULL_ADDRESS) ? msg.sender : _answerer; _storeCommitment(question_id, commitment_id); _addAnswerToHistory(question_id, commitment_id, answerer, tokens, true); } /// @notice Submit the answer whose hash you sent in a previous submitAnswerCommitment() transaction /// @dev Checks the parameters supplied recreate an existing commitment, and stores the revealed answer /// Updates the current answer unless someone has since supplied a new answer with a higher bond /// msg.sender is intentionally not restricted to the user who originally sent the commitment; /// For example, the user may want to provide the answer+nonce to a third-party service and let them send the tx /// NB If we are pending arbitration, it will be up to the arbitrator to wait and see any outstanding reveal is sent /// @param question_id The ID of the question /// @param answer The answer, encoded as bytes32 /// @param nonce The nonce that, combined with the answer, recreates the answer_hash you gave in submitAnswerCommitment() /// @param bond The bond that you paid in your submitAnswerCommitment() transaction function submitAnswerReveal(bytes32 question_id, bytes32 answer, uint256 nonce, uint256 bond) stateOpenOrPendingArbitration(question_id) external { bytes32 answer_hash = keccak256(abi.encodePacked(answer, nonce)); bytes32 commitment_id = keccak256(abi.encodePacked(question_id, answer_hash, bond)); require(!commitments[commitment_id].is_revealed, "commitment must not have been revealed yet"); require(commitments[commitment_id].reveal_ts > uint32(now), "reveal deadline must not have passed"); commitments[commitment_id].revealed_answer = answer; commitments[commitment_id].is_revealed = true; if (bond == questions[question_id].bond) { _updateCurrentAnswer(question_id, answer, questions[question_id].timeout); } emit LogAnswerReveal(question_id, msg.sender, answer_hash, answer, nonce, bond); } function _addAnswerToHistory(bytes32 question_id, bytes32 answer_or_commitment_id, address answerer, uint256 bond, bool is_commitment) internal { bytes32 new_history_hash = keccak256(abi.encodePacked(questions[question_id].history_hash, answer_or_commitment_id, bond, answerer, is_commitment)); // Update the current bond level, if there's a bond (ie anything except arbitration) if (bond > 0) { questions[question_id].bond = bond; } questions[question_id].history_hash = new_history_hash; emit LogNewAnswer(answer_or_commitment_id, question_id, new_history_hash, answerer, bond, now, is_commitment); } function _updateCurrentAnswer(bytes32 question_id, bytes32 answer, uint32 timeout_secs) internal { questions[question_id].best_answer = answer; questions[question_id].finalize_ts = uint32(now).add(timeout_secs); } /// @notice Notify the contract that the arbitrator has been paid for a question, freezing it pending their decision. /// @dev The arbitrator contract is trusted to only call this if they've been paid, and tell us who paid them. /// @param question_id The ID of the question /// @param requester The account that requested arbitration /// @param max_previous If specified, reverts if a bond higher than this was submitted after you sent your transaction. function notifyOfArbitrationRequest(bytes32 question_id, address requester, uint256 max_previous) onlyArbitrator(question_id) stateOpen(question_id) previousBondMustNotBeatMaxPrevious(question_id, max_previous) external { require(questions[question_id].bond > 0, "Question must already have an answer when arbitration is requested"); questions[question_id].is_pending_arbitration = true; emit LogNotifyOfArbitrationRequest(question_id, requester); } /// @notice Submit the answer for a question, for use by the arbitrator. /// @dev Doesn't require (or allow) a bond. /// If the current final answer is correct, the account should be whoever submitted it. /// If the current final answer is wrong, the account should be whoever paid for arbitration. /// However, the answerer stipulations are not enforced by the contract. /// @param question_id The ID of the question /// @param answer The answer, encoded into bytes32 /// @param answerer The account credited with this answer for the purpose of bond claims function submitAnswerByArbitrator(bytes32 question_id, bytes32 answer, address answerer) onlyArbitrator(question_id) statePendingArbitration(question_id) external { require(answerer != NULL_ADDRESS, "answerer must be provided"); emit LogFinalize(question_id, answer); questions[question_id].is_pending_arbitration = false; _addAnswerToHistory(question_id, answer, answerer, 0, false); _updateCurrentAnswer(question_id, answer, 0); } /// @notice Report whether the answer to the specified question is finalized /// @param question_id The ID of the question /// @return Return true if finalized function isFinalized(bytes32 question_id) view public returns (bool) { uint32 finalize_ts = questions[question_id].finalize_ts; return ( !questions[question_id].is_pending_arbitration && (finalize_ts > UNANSWERED) && (finalize_ts <= uint32(now)) ); } /// @notice (Deprecated) Return the final answer to the specified question, or revert if there isn't one /// @param question_id The ID of the question /// @return The answer formatted as a bytes32 function getFinalAnswer(bytes32 question_id) stateFinalized(question_id) external view returns (bytes32) { return questions[question_id].best_answer; } /// @notice Return the final answer to the specified question, or revert if there isn't one /// @param question_id The ID of the question /// @return The answer formatted as a bytes32 function resultFor(bytes32 question_id) stateFinalized(question_id) external view returns (bytes32) { return questions[question_id].best_answer; } /// @notice Return the final answer to the specified question, provided it matches the specified criteria. /// @dev Reverts if the question is not finalized, or if it does not match the specified criteria. /// @param question_id The ID of the question /// @param content_hash The hash of the question content (template ID + opening time + question parameter string) /// @param arbitrator The arbitrator chosen for the question (regardless of whether they are asked to arbitrate) /// @param min_timeout The timeout set in the initial question settings must be this high or higher /// @param min_bond The bond sent with the final answer must be this high or higher /// @return The answer formatted as a bytes32 function getFinalAnswerIfMatches( bytes32 question_id, bytes32 content_hash, address arbitrator, uint32 min_timeout, uint256 min_bond ) stateFinalized(question_id) external view returns (bytes32) { require(content_hash == questions[question_id].content_hash, "content hash must match"); require(arbitrator == questions[question_id].arbitrator, "arbitrator must match"); require(min_timeout <= questions[question_id].timeout, "timeout must be long enough"); require(min_bond <= questions[question_id].bond, "bond must be high enough"); return questions[question_id].best_answer; } /// @notice Assigns the winnings (bounty and bonds) to everyone who gave the accepted answer /// Caller must provide the answer history, in reverse order /// @dev Works up the chain and assign bonds to the person who gave the right answer /// If someone gave the winning answer earlier, they must get paid from the higher bond /// That means we can't pay out the bond added at n until we have looked at n-1 /// The first answer is authenticated by checking against the stored history_hash. /// One of the inputs to history_hash is the history_hash before it, so we use that to authenticate the next entry, etc /// Once we get to a null hash we'll know we're done and there are no more answers. /// Usually you would call the whole thing in a single transaction, but if not then the data is persisted to pick up later. /// @param question_id The ID of the question /// @param history_hashes Second-last-to-first, the hash of each history entry. (Final one should be empty). /// @param addrs Last-to-first, the address of each answerer or commitment sender /// @param bonds Last-to-first, the bond supplied with each answer or commitment /// @param answers Last-to-first, each answer supplied, or commitment ID if the answer was supplied with commit->reveal function claimWinnings( bytes32 question_id, bytes32[] memory history_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers ) stateFinalized(question_id) public { require(history_hashes.length > 0, "at least one history hash entry must be provided"); // These are only set if we split our claim over multiple transactions. address payee = question_claims[question_id].payee; uint256 last_bond = question_claims[question_id].last_bond; uint256 queued_funds = question_claims[question_id].queued_funds; // Starts as the hash of the final answer submitted. It'll be cleared when we're done. // If we're splitting the claim over multiple transactions, it'll be the hash where we left off last time bytes32 last_history_hash = questions[question_id].history_hash; bytes32 best_answer = questions[question_id].best_answer; uint256 i; for (i = 0; i < history_hashes.length; i++) { // Check input against the history hash, and see which of 2 possible values of is_commitment fits. bool is_commitment = _verifyHistoryInputOrRevert(last_history_hash, history_hashes[i], answers[i], bonds[i], addrs[i]); queued_funds = queued_funds.add(last_bond); (queued_funds, payee) = _processHistoryItem( question_id, best_answer, queued_funds, payee, addrs[i], bonds[i], answers[i], is_commitment); // Line the bond up for next time, when it will be added to somebody's queued_funds last_bond = bonds[i]; last_history_hash = history_hashes[i]; } if (last_history_hash != NULL_HASH) { // We haven't yet got to the null hash (1st answer), ie the caller didn't supply the full answer chain. // Persist the details so we can pick up later where we left off later. // If we know who to pay we can go ahead and pay them out, only keeping back last_bond // (We always know who to pay unless all we saw were unrevealed commits) if (payee != NULL_ADDRESS) { _payPayee(question_id, payee, queued_funds); queued_funds = 0; } question_claims[question_id].payee = payee; question_claims[question_id].last_bond = last_bond; question_claims[question_id].queued_funds = queued_funds; } else { // There is nothing left below us so the payee can keep what remains _payPayee(question_id, payee, queued_funds.add(last_bond)); delete question_claims[question_id]; } questions[question_id].history_hash = last_history_hash; } function _payPayee(bytes32 question_id, address payee, uint256 value) internal { balanceOf[payee] = balanceOf[payee].add(value); emit LogClaim(question_id, payee, value); } function _verifyHistoryInputOrRevert( bytes32 last_history_hash, bytes32 history_hash, bytes32 answer, uint256 bond, address addr ) internal pure returns (bool) { if (last_history_hash == keccak256(abi.encodePacked(history_hash, answer, bond, addr, true)) ) { return true; } if (last_history_hash == keccak256(abi.encodePacked(history_hash, answer, bond, addr, false)) ) { return false; } revert("History input provided did not match the expected hash"); } function _processHistoryItem( bytes32 question_id, bytes32 best_answer, uint256 queued_funds, address payee, address addr, uint256 bond, bytes32 answer, bool is_commitment ) internal returns (uint256, address) { // For commit-and-reveal, the answer history holds the commitment ID instead of the answer. // We look at the referenced commitment ID and switch in the actual answer. if (is_commitment) { bytes32 commitment_id = answer; // If it's a commit but it hasn't been revealed, it will always be considered wrong. if (!commitments[commitment_id].is_revealed) { delete commitments[commitment_id]; return (queued_funds, payee); } else { answer = commitments[commitment_id].revealed_answer; delete commitments[commitment_id]; } } if (answer == best_answer) { if (payee == NULL_ADDRESS) { // The entry is for the first payee we come to, ie the winner. // They get the question bounty. payee = addr; queued_funds = queued_funds.add(questions[question_id].bounty); questions[question_id].bounty = 0; } else if (addr != payee) { // Answerer has changed, ie we found someone lower down who needs to be paid // The lower answerer will take over receiving bonds from higher answerer. // They should also be paid the takeover fee, which is set at a rate equivalent to their bond. // (This is our arbitrary rule, to give consistent right-answerers a defence against high-rollers.) // There should be enough for the fee, but if not, take what we have. // There's an edge case involving weird arbitrator behaviour where we may be short. uint256 answer_takeover_fee = (queued_funds >= bond) ? bond : queued_funds; // Settle up with the old (higher-bonded) payee _payPayee(question_id, payee, queued_funds.sub(answer_takeover_fee)); // Now start queued_funds again for the new (lower-bonded) payee payee = addr; queued_funds = answer_takeover_fee; } } return (queued_funds, payee); } /// @notice Convenience function to assign bounties/bonds for multiple questions in one go, then withdraw all your funds. /// Caller must provide the answer history for each question, in reverse order /// @dev Can be called by anyone to assign bonds/bounties, but funds are only withdrawn for the user making the call. /// @param question_ids The IDs of the questions you want to claim for /// @param lengths The number of history entries you will supply for each question ID /// @param hist_hashes In a single list for all supplied questions, the hash of each history entry. /// @param addrs In a single list for all supplied questions, the address of each answerer or commitment sender /// @param bonds In a single list for all supplied questions, the bond supplied with each answer or commitment /// @param answers In a single list for all supplied questions, each answer supplied, or commitment ID function claimMultipleAndWithdrawBalance( bytes32[] memory question_ids, uint256[] memory lengths, bytes32[] memory hist_hashes, address[] memory addrs, uint256[] memory bonds, bytes32[] memory answers ) stateAny() // The finalization checks are done in the claimWinnings function public { uint256 qi; uint256 i; for (qi = 0; qi < question_ids.length; qi++) { bytes32 qid = question_ids[qi]; uint256 ln = lengths[qi]; bytes32[] memory hh = new bytes32[](ln); address[] memory ad = new address[](ln); uint256[] memory bo = new uint256[](ln); bytes32[] memory an = new bytes32[](ln); uint256 j; for (j = 0; j < ln; j++) { hh[j] = hist_hashes[i]; ad[j] = addrs[i]; bo[j] = bonds[i]; an[j] = answers[i]; i++; } claimWinnings(qid, hh, ad, bo, an); } withdraw(); } /// @notice Returns the questions's content hash, identifying the question content /// @param question_id The ID of the question function getContentHash(bytes32 question_id) public view returns(bytes32) { return questions[question_id].content_hash; } /// @notice Returns the arbitrator address for the question /// @param question_id The ID of the question function getArbitrator(bytes32 question_id) public view returns(address) { return questions[question_id].arbitrator; } /// @notice Returns the timestamp when the question can first be answered /// @param question_id The ID of the question function getOpeningTS(bytes32 question_id) public view returns(uint32) { return questions[question_id].opening_ts; } /// @notice Returns the timeout in seconds used after each answer /// @param question_id The ID of the question function getTimeout(bytes32 question_id) public view returns(uint32) { return questions[question_id].timeout; } /// @notice Returns the timestamp at which the question will be/was finalized /// @param question_id The ID of the question function getFinalizeTS(bytes32 question_id) public view returns(uint32) { return questions[question_id].finalize_ts; } /// @notice Returns whether the question is pending arbitration /// @param question_id The ID of the question function isPendingArbitration(bytes32 question_id) public view returns(bool) { return questions[question_id].is_pending_arbitration; } /// @notice Returns the current total unclaimed bounty /// @dev Set back to zero once the bounty has been claimed /// @param question_id The ID of the question function getBounty(bytes32 question_id) public view returns(uint256) { return questions[question_id].bounty; } /// @notice Returns the current best answer /// @param question_id The ID of the question function getBestAnswer(bytes32 question_id) public view returns(bytes32) { return questions[question_id].best_answer; } /// @notice Returns the history hash of the question /// @param question_id The ID of the question /// @dev Updated on each answer, then rewound as each is claimed function getHistoryHash(bytes32 question_id) public view returns(bytes32) { return questions[question_id].history_hash; } /// @notice Returns the highest bond posted so far for a question /// @param question_id The ID of the question function getBond(bytes32 question_id) public view returns(uint256) { return questions[question_id].bond; } }
pragma solidity ^0.6.0; pragma experimental ABIEncoderV2; import "./RealitioERC20.sol"; // openzeppelin imports import "@openzeppelin/contracts/math/SafeMath.sol"; library CeilDiv { // calculates ceil(x/y) function ceildiv(uint256 x, uint256 y) internal pure returns (uint256) { if (x > 0) return ((x - 1) / y) + 1; return x / y; } } /// @title Market Contract Factory contract PredictionMarket { using SafeMath for uint256; using CeilDiv for uint256; // ------ Events ------ event MarketCreated(address indexed user, uint256 indexed marketId, uint256 outcomes, string question, string image); event MarketActionTx( address indexed user, MarketAction indexed action, uint256 indexed marketId, uint256 outcomeId, uint256 shares, uint256 value, uint256 timestamp ); event MarketOutcomePrice(uint256 indexed marketId, uint256 indexed outcomeId, uint256 value, uint256 timestamp); event MarketLiquidity( uint256 indexed marketId, uint256 value, // total liquidity uint256 price, // value of one liquidity share; max: 1 (50-50 situation) uint256 timestamp ); event MarketResolved(address indexed user, uint256 indexed marketId, uint256 outcomeId, uint256 timestamp); // ------ Events End ------ uint256 public constant MAX_UINT_256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935; uint256 public constant ONE = 10**18; enum MarketState { open, closed, resolved } enum MarketAction { buy, sell, addLiquidity, removeLiquidity, claimWinnings, claimLiquidity, claimFees, claimVoided } struct Market { // market details uint256 closesAtTimestamp; uint256 balance; // total stake uint256 liquidity; // stake held uint256 sharesAvailable; // shares held (all outcomes) mapping(address => uint256) liquidityShares; mapping(address => bool) liquidityClaims; // wether user has claimed liquidity earnings MarketState state; // resolution variables MarketResolution resolution; // fees MarketFees fees; // market outcomes uint256[] outcomeIds; mapping(uint256 => MarketOutcome) outcomes; } struct MarketFees { uint256 value; // fee % taken from every transaction uint256 poolWeight; // internal var used to ensure pro-rate fee distribution mapping(address => uint256) claimed; } struct MarketResolution { bool resolved; uint256 outcomeId; bytes32 questionId; // realitio questionId } struct MarketOutcome { uint256 marketId; uint256 id; Shares shares; } struct Shares { uint256 total; // number of shares uint256 available; // available shares mapping(address => uint256) holders; mapping(address => bool) claims; // wether user has claimed winnings mapping(address => bool) voidedClaims; // wether user has claimed voided market shares } uint256[] marketIds; mapping(uint256 => Market) markets; uint256 public marketIndex; // governance uint256 public fee; // fee % taken from every transaction // realitio configs address public realitioAddress; uint256 public realitioTimeout; // market creation IERC20 public token; // token used for rewards / market creation uint256 public requiredBalance; // required balance for market creation // ------ Modifiers ------ modifier isMarket(uint256 marketId) { require(marketId < marketIndex, "Market not found"); _; } modifier timeTransitions(uint256 marketId) { if (now > markets[marketId].closesAtTimestamp && markets[marketId].state == MarketState.open) { nextState(marketId); } _; } modifier atState(uint256 marketId, MarketState state) { require(markets[marketId].state == state, "Market in incorrect state"); _; } modifier notAtState(uint256 marketId, MarketState state) { require(markets[marketId].state != state, "Market in incorrect state"); _; } modifier transitionNext(uint256 marketId) { _; nextState(marketId); } modifier mustHoldRequiredBalance() { require(token.balanceOf(msg.sender) >= requiredBalance, "msg.sender must hold minimum erc20 balance"); _; } // ------ Modifiers End ------ /// @dev protocol is immutable and has no ownership constructor( uint256 _fee, IERC20 _token, uint256 _requiredBalance, address _realitioAddress, uint256 _realitioTimeout ) public { require(_realitioAddress != address(0), "_realitioAddress is address 0"); require(_realitioTimeout > 0, "timeout must be positive"); fee = _fee; token = _token; requiredBalance = _requiredBalance; realitioAddress = _realitioAddress; realitioTimeout = _realitioTimeout; } // ------ Core Functions ------ /// @dev Creates a market, initializes the outcome shares pool and submits a question in Realitio function createMarket( string calldata question, string calldata image, uint256 closesAt, address arbitrator, uint256 outcomes ) external payable mustHoldRequiredBalance returns (uint256) { uint256 marketId = marketIndex; marketIds.push(marketId); Market storage market = markets[marketId]; require(msg.value > 0, "stake needs to be > 0"); require(closesAt > now, "market must resolve after the current date"); require(arbitrator != address(0), "invalid arbitrator address"); // v1 - only binary markets require(outcomes == 2, "number of outcomes has to be 2"); market.closesAtTimestamp = closesAt; market.state = MarketState.open; market.fees.value = fee; // setting intial value to an integer that does not map to any outcomeId market.resolution.outcomeId = MAX_UINT_256; // creating market outcomes for (uint256 i = 0; i < outcomes; i++) { market.outcomeIds.push(i); MarketOutcome storage outcome = market.outcomes[i]; outcome.marketId = marketId; outcome.id = i; } // creating question in realitio RealitioERC20 realitio = RealitioERC20(realitioAddress); market.resolution.questionId = realitio.askQuestionERC20( 2, question, arbitrator, uint32(realitioTimeout), uint32(closesAt), 0, 0 ); addLiquidity(marketId, msg.value); // emiting initial price events emitMarketOutcomePriceEvents(marketId); emit MarketCreated(msg.sender, marketId, outcomes, question, image); // incrementing market array index marketIndex = marketIndex + 1; return marketId; } /// @dev Calculates the number of shares bought with "amount" balance function calcBuyAmount( uint256 amount, uint256 marketId, uint256 outcomeId ) public view returns (uint256) { Market storage market = markets[marketId]; uint256[] memory outcomesShares = getMarketOutcomesShares(marketId); uint256 amountMinusFees = amount.sub(amount.mul(market.fees.value) / ONE); uint256 buyTokenPoolBalance = outcomesShares[outcomeId]; uint256 endingOutcomeBalance = buyTokenPoolBalance.mul(ONE); for (uint256 i = 0; i < outcomesShares.length; i++) { if (i != outcomeId) { uint256 outcomeShares = outcomesShares[i]; endingOutcomeBalance = endingOutcomeBalance.mul(outcomeShares).ceildiv(outcomeShares.add(amountMinusFees)); } } require(endingOutcomeBalance > 0, "must have non-zero balances"); return buyTokenPoolBalance.add(amountMinusFees).sub(endingOutcomeBalance.ceildiv(ONE)); } /// @dev Calculates the number of shares needed to be sold in order to receive "amount" in balance function calcSellAmount( uint256 amount, uint256 marketId, uint256 outcomeId ) public view returns (uint256 outcomeTokenSellAmount) { Market storage market = markets[marketId]; uint256[] memory outcomesShares = getMarketOutcomesShares(marketId); uint256 amountPlusFees = amount.mul(ONE) / ONE.sub(market.fees.value); uint256 sellTokenPoolBalance = outcomesShares[outcomeId]; uint256 endingOutcomeBalance = sellTokenPoolBalance.mul(ONE); for (uint256 i = 0; i < outcomesShares.length; i++) { if (i != outcomeId) { uint256 outcomeShares = outcomesShares[i]; endingOutcomeBalance = endingOutcomeBalance.mul(outcomeShares).ceildiv(outcomeShares.sub(amountPlusFees)); } } require(endingOutcomeBalance > 0, "must have non-zero balances"); return amountPlusFees.add(endingOutcomeBalance.ceildiv(ONE)).sub(sellTokenPoolBalance); } /// @dev Buy shares of a market outcome function buy( uint256 marketId, uint256 outcomeId, uint256 minOutcomeSharesToBuy ) external payable timeTransitions(marketId) atState(marketId, MarketState.open) { Market storage market = markets[marketId]; uint256 value = msg.value; uint256 shares = calcBuyAmount(value, marketId, outcomeId); require(shares >= minOutcomeSharesToBuy, "minimum buy amount not reached"); require(shares > 0, "shares amount is 0"); // subtracting fee from transaction value uint256 feeAmount = value.mul(market.fees.value) / ONE; market.fees.poolWeight = market.fees.poolWeight.add(feeAmount); uint256 valueMinusFees = value.sub(feeAmount); MarketOutcome storage outcome = market.outcomes[outcomeId]; // Funding market shares with received funds addSharesToMarket(marketId, valueMinusFees); require(outcome.shares.available >= shares, "outcome shares pool balance is too low"); transferOutcomeSharesfromPool(msg.sender, marketId, outcomeId, shares); emit MarketActionTx(msg.sender, MarketAction.buy, marketId, outcomeId, shares, value, now); emitMarketOutcomePriceEvents(marketId); } /// @dev Sell shares of a market outcome function sell( uint256 marketId, uint256 outcomeId, uint256 value, uint256 maxOutcomeSharesToSell ) external payable timeTransitions(marketId) atState(marketId, MarketState.open) { Market storage market = markets[marketId]; MarketOutcome storage outcome = market.outcomes[outcomeId]; uint256 shares = calcSellAmount(value, marketId, outcomeId); require(shares <= maxOutcomeSharesToSell, "maximum sell amount exceeded"); require(shares > 0, "shares amount is 0"); require(outcome.shares.holders[msg.sender] >= shares, "user does not have enough balance"); transferOutcomeSharesToPool(msg.sender, marketId, outcomeId, shares); // adding fee to transaction value uint256 feeAmount = value.mul(market.fees.value) / (ONE.sub(fee)); market.fees.poolWeight = market.fees.poolWeight.add(feeAmount); uint256 valuePlusFees = value.add(feeAmount); require(market.balance >= valuePlusFees, "market does not have enough balance"); // Rebalancing market shares removeSharesFromMarket(marketId, valuePlusFees); // Transferring funds to user msg.sender.transfer(value); emit MarketActionTx(msg.sender, MarketAction.sell, marketId, outcomeId, shares, value, now); emitMarketOutcomePriceEvents(marketId); } /// @dev Adds liquidity to a market - external function addLiquidity(uint256 marketId) external payable timeTransitions(marketId) atState(marketId, MarketState.open) { addLiquidity(marketId, msg.value); } /// @dev Private function, used by addLiquidity and CreateMarket function addLiquidity(uint256 marketId, uint256 value) private timeTransitions(marketId) atState(marketId, MarketState.open) { Market storage market = markets[marketId]; require(value > 0, "stake has to be greater than 0."); uint256 liquidityAmount; uint256[] memory outcomesShares = getMarketOutcomesShares(marketId); uint256[] memory sendBackAmounts = new uint256[](outcomesShares.length); uint256 poolWeight = 0; if (market.liquidity > 0) { // part of the liquidity is exchanged for outcome shares if market is not balanced for (uint256 i = 0; i < outcomesShares.length; i++) { uint256 outcomeShares = outcomesShares[i]; if (poolWeight < outcomeShares) poolWeight = outcomeShares; } for (uint256 i = 0; i < outcomesShares.length; i++) { uint256 remaining = value.mul(outcomesShares[i]) / poolWeight; sendBackAmounts[i] = value.sub(remaining); } liquidityAmount = value.mul(market.liquidity) / poolWeight; // re-balancing fees pool rebalanceFeesPool(marketId, liquidityAmount, MarketAction.addLiquidity); } else { // funding market with no liquidity liquidityAmount = value; } // funding market market.liquidity = market.liquidity.add(liquidityAmount); market.liquidityShares[msg.sender] = market.liquidityShares[msg.sender].add(liquidityAmount); addSharesToMarket(marketId, value); // transform sendBackAmounts to array of amounts added for (uint256 i = 0; i < sendBackAmounts.length; i++) { if (sendBackAmounts[i] > 0) { uint256 marketShares = market.sharesAvailable; uint256 outcomeShares = market.outcomes[i].shares.available; transferOutcomeSharesfromPool(msg.sender, marketId, i, sendBackAmounts[i]); emit MarketActionTx( msg.sender, MarketAction.buy, marketId, i, sendBackAmounts[i], (marketShares.sub(outcomeShares)).mul(sendBackAmounts[i]).div(market.sharesAvailable), // price * shares now ); } } uint256 liquidityPrice = getMarketLiquidityPrice(marketId); uint256 liquidityValue = liquidityPrice.mul(liquidityAmount) / ONE; emit MarketActionTx(msg.sender, MarketAction.addLiquidity, marketId, 0, liquidityAmount, liquidityValue, now); emit MarketLiquidity(marketId, market.liquidity, liquidityPrice, now); } /// @dev Removes liquidity to a market - external function removeLiquidity(uint256 marketId, uint256 shares) external payable timeTransitions(marketId) atState(marketId, MarketState.open) { Market storage market = markets[marketId]; require(market.liquidityShares[msg.sender] >= shares, "user does not have enough balance"); // claiming any pending fees claimFees(marketId); // re-balancing fees pool rebalanceFeesPool(marketId, shares, MarketAction.removeLiquidity); uint256[] memory outcomesShares = getMarketOutcomesShares(marketId); uint256[] memory sendAmounts = new uint256[](outcomesShares.length); uint256 poolWeight = MAX_UINT_256; // part of the liquidity is exchanged for outcome shares if market is not balanced for (uint256 i = 0; i < outcomesShares.length; i++) { uint256 outcomeShares = outcomesShares[i]; if (poolWeight > outcomeShares) poolWeight = outcomeShares; } uint256 liquidityAmount = shares.mul(poolWeight).div(market.liquidity); for (uint256 i = 0; i < outcomesShares.length; i++) { sendAmounts[i] = outcomesShares[i].mul(shares) / market.liquidity; sendAmounts[i] = sendAmounts[i].sub(liquidityAmount); } // removing liquidity from market removeSharesFromMarket(marketId, liquidityAmount); market.liquidity = market.liquidity.sub(shares); // removing liquidity tokens from market creator market.liquidityShares[msg.sender] = market.liquidityShares[msg.sender].sub(shares); for (uint256 i = 0; i < outcomesShares.length; i++) { if (sendAmounts[i] > 0) { uint256 marketShares = market.sharesAvailable; uint256 outcomeShares = market.outcomes[i].shares.available; transferOutcomeSharesfromPool(msg.sender, marketId, i, sendAmounts[i]); emit MarketActionTx( msg.sender, MarketAction.buy, marketId, i, sendAmounts[i], (marketShares.sub(outcomeShares)).mul(sendAmounts[i]).div(market.sharesAvailable), // price * shares now ); } } // transferring user funds from liquidity removed msg.sender.transfer(liquidityAmount); emit MarketActionTx(msg.sender, MarketAction.removeLiquidity, marketId, 0, shares, liquidityAmount, now); emit MarketLiquidity(marketId, market.liquidity, getMarketLiquidityPrice(marketId), now); } /// @dev Fetches winning outcome from Realitio and resolves the market function resolveMarketOutcome(uint256 marketId) external timeTransitions(marketId) atState(marketId, MarketState.closed) transitionNext(marketId) returns (uint256) { Market storage market = markets[marketId]; RealitioERC20 realitio = RealitioERC20(realitioAddress); // will fail if question is not finalized uint256 outcomeId = uint256(realitio.resultFor(market.resolution.questionId)); market.resolution.outcomeId = outcomeId; emit MarketResolved(msg.sender, marketId, outcomeId, now); emitMarketOutcomePriceEvents(marketId); return market.resolution.outcomeId; } /// @dev Allows holders of resolved outcome shares to claim earnings. function claimWinnings(uint256 marketId) external atState(marketId, MarketState.resolved) { Market storage market = markets[marketId]; MarketOutcome storage resolvedOutcome = market.outcomes[market.resolution.outcomeId]; require(resolvedOutcome.shares.holders[msg.sender] > 0, "user does not hold resolved outcome shares"); require(resolvedOutcome.shares.claims[msg.sender] == false, "user already claimed resolved outcome winnings"); // 1 share => price = 1 uint256 value = resolvedOutcome.shares.holders[msg.sender]; // assuring market has enough funds require(market.balance >= value, "Market does not have enough balance"); market.balance = market.balance.sub(value); resolvedOutcome.shares.claims[msg.sender] = true; emit MarketActionTx( msg.sender, MarketAction.claimWinnings, marketId, market.resolution.outcomeId, resolvedOutcome.shares.holders[msg.sender], value, now ); msg.sender.transfer(value); } /// @dev Allows holders of voided outcome shares to claim balance back. function claimVoidedOutcomeShares(uint256 marketId, uint256 outcomeId) external atState(marketId, MarketState.resolved) { Market storage market = markets[marketId]; MarketOutcome storage outcome = market.outcomes[outcomeId]; require(outcome.shares.holders[msg.sender] > 0, "user does not hold outcome shares"); require(outcome.shares.voidedClaims[msg.sender] == false, "user already claimed outcome shares"); // voided market - shares are valued at last market price uint256 price = getMarketOutcomePrice(marketId, outcomeId); uint256 value = price.mul(outcome.shares.holders[msg.sender]).div(ONE); // assuring market has enough funds require(market.balance >= value, "Market does not have enough balance"); market.balance = market.balance.sub(value); outcome.shares.voidedClaims[msg.sender] = true; emit MarketActionTx( msg.sender, MarketAction.claimVoided, marketId, outcomeId, outcome.shares.holders[msg.sender], value, now ); msg.sender.transfer(value); } /// @dev Allows liquidity providers to claim earnings from liquidity providing. function claimLiquidity(uint256 marketId) external atState(marketId, MarketState.resolved) { Market storage market = markets[marketId]; // claiming any pending fees claimFees(marketId); require(market.liquidityShares[msg.sender] > 0, "user does not hold liquidity shares"); require(market.liquidityClaims[msg.sender] == false, "user already claimed liquidity winnings"); // value = total resolved outcome pool shares * pool share (%) uint256 liquidityPrice = getMarketLiquidityPrice(marketId); uint256 value = liquidityPrice.mul(market.liquidityShares[msg.sender]) / ONE; // assuring market has enough funds require(market.balance >= value, "Market does not have enough balance"); market.balance = market.balance.sub(value); market.liquidityClaims[msg.sender] = true; emit MarketActionTx( msg.sender, MarketAction.claimLiquidity, marketId, 0, market.liquidityShares[msg.sender], value, now ); msg.sender.transfer(value); } /// @dev Allows liquidity providers to claim their fees share from fees pool function claimFees(uint256 marketId) public payable { Market storage market = markets[marketId]; uint256 claimableFees = getUserClaimableFees(marketId, msg.sender); if (claimableFees > 0) { market.fees.claimed[msg.sender] = market.fees.claimed[msg.sender].add(claimableFees); msg.sender.transfer(claimableFees); } emit MarketActionTx( msg.sender, MarketAction.claimFees, marketId, 0, market.liquidityShares[msg.sender], claimableFees, now ); } /// @dev Rebalances the fees pool. Needed in every AddLiquidity / RemoveLiquidity call function rebalanceFeesPool( uint256 marketId, uint256 liquidityShares, MarketAction action ) private returns (uint256) { Market storage market = markets[marketId]; uint256 poolWeight = liquidityShares.mul(market.fees.poolWeight).div(market.liquidity); if (action == MarketAction.addLiquidity) { market.fees.poolWeight = market.fees.poolWeight.add(poolWeight); market.fees.claimed[msg.sender] = market.fees.claimed[msg.sender].add(poolWeight); } else { market.fees.poolWeight = market.fees.poolWeight.sub(poolWeight); market.fees.claimed[msg.sender] = market.fees.claimed[msg.sender].sub(poolWeight); } } /// @dev Transitions market to next state function nextState(uint256 marketId) private { Market storage market = markets[marketId]; market.state = MarketState(uint256(market.state) + 1); } /// @dev Emits a outcome price event for every outcome function emitMarketOutcomePriceEvents(uint256 marketId) private { Market storage market = markets[marketId]; for (uint256 i = 0; i < market.outcomeIds.length; i++) { emit MarketOutcomePrice(marketId, i, getMarketOutcomePrice(marketId, i), now); } // liquidity shares also change value emit MarketLiquidity(marketId, market.liquidity, getMarketLiquidityPrice(marketId), now); } /// @dev Adds outcome shares to shares pool function addSharesToMarket(uint256 marketId, uint256 shares) private { Market storage market = markets[marketId]; for (uint256 i = 0; i < market.outcomeIds.length; i++) { MarketOutcome storage outcome = market.outcomes[i]; outcome.shares.available = outcome.shares.available.add(shares); outcome.shares.total = outcome.shares.total.add(shares); // only adding to market total shares, the available remains market.sharesAvailable = market.sharesAvailable.add(shares); } market.balance = market.balance.add(shares); } /// @dev Removes outcome shares from shares pool function removeSharesFromMarket(uint256 marketId, uint256 shares) private { Market storage market = markets[marketId]; for (uint256 i = 0; i < market.outcomeIds.length; i++) { MarketOutcome storage outcome = market.outcomes[i]; outcome.shares.available = outcome.shares.available.sub(shares); outcome.shares.total = outcome.shares.total.sub(shares); // only subtracting from market total shares, the available remains market.sharesAvailable = market.sharesAvailable.sub(shares); } market.balance = market.balance.sub(shares); } /// @dev Transfer outcome shares from pool to user balance function transferOutcomeSharesfromPool( address user, uint256 marketId, uint256 outcomeId, uint256 shares ) private { Market storage market = markets[marketId]; MarketOutcome storage outcome = market.outcomes[outcomeId]; // transfering shares from shares pool to user outcome.shares.holders[user] = outcome.shares.holders[user].add(shares); outcome.shares.available = outcome.shares.available.sub(shares); market.sharesAvailable = market.sharesAvailable.sub(shares); } /// @dev Transfer outcome shares from user balance back to pool function transferOutcomeSharesToPool( address user, uint256 marketId, uint256 outcomeId, uint256 shares ) private { Market storage market = markets[marketId]; MarketOutcome storage outcome = market.outcomes[outcomeId]; // adding shares back to pool outcome.shares.holders[user] = outcome.shares.holders[user].sub(shares); outcome.shares.available = outcome.shares.available.add(shares); market.sharesAvailable = market.sharesAvailable.add(shares); } // ------ Core Functions End ------ // ------ Getters ------ function getUserMarketShares(uint256 marketId, address user) external view returns ( uint256, uint256, uint256 ) { Market storage market = markets[marketId]; return ( market.liquidityShares[user], market.outcomes[0].shares.holders[user], market.outcomes[1].shares.holders[user] ); } function getUserClaimStatus(uint256 marketId, address user) external view returns ( bool, bool, bool, bool, uint256 ) { Market storage market = markets[marketId]; // market still not resolved if (market.state != MarketState.resolved) { return (false, false, false, false, getUserClaimableFees(marketId, user)); } MarketOutcome storage outcome = market.outcomes[market.resolution.outcomeId]; return ( outcome.shares.holders[user] > 0, outcome.shares.claims[user], market.liquidityShares[user] > 0, market.liquidityClaims[user], getUserClaimableFees(marketId, user) ); } function getUserLiquidityPoolShare(uint256 marketId, address user) external view returns (uint256) { Market storage market = markets[marketId]; return market.liquidityShares[user].mul(ONE).div(market.liquidity); } function getUserClaimableFees(uint256 marketId, address user) public view returns (uint256) { Market storage market = markets[marketId]; uint256 rawAmount = market.fees.poolWeight.mul(market.liquidityShares[user]).div(market.liquidity); // No fees left to claim if (market.fees.claimed[user] > rawAmount) return 0; return rawAmount.sub(market.fees.claimed[user]); } function getMarkets() external view returns (uint256[] memory) { return marketIds; } function getMarketData(uint256 marketId) external view returns ( MarketState, uint256, uint256, uint256, uint256, int256 ) { Market storage market = markets[marketId]; return ( market.state, market.closesAtTimestamp, market.liquidity, market.balance, market.sharesAvailable, getMarketResolvedOutcome(marketId) ); } function getMarketAltData(uint256 marketId) external view returns ( uint256, bytes32, uint256 ) { Market storage market = markets[marketId]; return (market.fees.value, market.resolution.questionId, uint256(market.resolution.questionId)); } function getMarketQuestion(uint256 marketId) external view returns (bytes32) { Market storage market = markets[marketId]; return (market.resolution.questionId); } function getMarketPrices(uint256 marketId) external view returns ( uint256, uint256, uint256 ) { return (getMarketLiquidityPrice(marketId), getMarketOutcomePrice(marketId, 0), getMarketOutcomePrice(marketId, 1)); } function getMarketLiquidityPrice(uint256 marketId) public view returns (uint256) { Market storage market = markets[marketId]; if (market.state == MarketState.resolved && !isMarketVoided(marketId)) { // resolved market, price is either 0 or 1 // final liquidity price = outcome shares / liquidity shares return market.outcomes[market.resolution.outcomeId].shares.available.mul(ONE).div(market.liquidity); } // liquidity price = # liquidity shares / # outcome shares * # outcomes return market.liquidity.mul(ONE * market.outcomeIds.length).div(market.sharesAvailable); } function getMarketResolvedOutcome(uint256 marketId) public view returns (int256) { Market storage market = markets[marketId]; // returning -1 if market still not resolved if (market.state != MarketState.resolved) { return -1; } return int256(market.resolution.outcomeId); } function isMarketVoided(uint256 marketId) public view returns (bool) { Market storage market = markets[marketId]; // market still not resolved, still in valid state if (market.state != MarketState.resolved) { return false; } // resolved market id does not match any of the market ids return market.resolution.outcomeId >= market.outcomeIds.length; } // ------ Outcome Getters ------ function getMarketOutcomeIds(uint256 marketId) external view returns (uint256[] memory) { Market storage market = markets[marketId]; return market.outcomeIds; } function getMarketOutcomePrice(uint256 marketId, uint256 outcomeId) public view returns (uint256) { Market storage market = markets[marketId]; MarketOutcome storage outcome = market.outcomes[outcomeId]; if (market.state == MarketState.resolved && !isMarketVoided(marketId)) { // resolved market, price is either 0 or 1 return outcomeId == market.resolution.outcomeId ? ONE : 0; } return (market.sharesAvailable.sub(outcome.shares.available)).mul(ONE).div(market.sharesAvailable); } function getMarketOutcomeData(uint256 marketId, uint256 outcomeId) external view returns ( uint256, uint256, uint256 ) { Market storage market = markets[marketId]; MarketOutcome storage outcome = market.outcomes[outcomeId]; return (getMarketOutcomePrice(marketId, outcomeId), outcome.shares.available, outcome.shares.total); } function getMarketOutcomesShares(uint256 marketId) private view returns (uint256[] memory) { Market storage market = markets[marketId]; uint256[] memory shares = new uint256[](market.outcomeIds.length); for (uint256 i = 0; i < market.outcomeIds.length; i++) { shares[i] = market.outcomes[i].shares.available; } return shares; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev String operations. */ library Strings { /** * @dev Converts a `uint256` to its ASCII `string` representation. */ function toString(uint256 value) internal pure returns (string memory) { // Inspired by OraclizeAPI's implementation - MIT licence // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol if (value == 0) { return "0"; } uint256 temp = value; uint256 digits; while (temp != 0) { digits++; temp /= 10; } bytes memory buffer = new bytes(digits); uint256 index = digits - 1; temp = value; while (temp != 0) { buffer[index--] = bytes1(uint8(48 + temp % 10)); temp /= 10; } return string(buffer); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <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. * * ``` * 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. */ 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; // When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. 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] = toDeleteIndex + 1; // All indexes are 1-based // 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) { require(set._values.length > index, "EnumerableSet: index out of bounds"); return set._values[index]; } // 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); } // 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)))); } // 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 on 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)); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Library for managing an enumerable variant of Solidity's * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`] * type. * * Maps have the following properties: * * - Entries are added, removed, and checked for existence in constant time * (O(1)). * - Entries are enumerated in O(n). No guarantees are made on the ordering. * * ``` * contract Example { * // Add the library methods * using EnumerableMap for EnumerableMap.UintToAddressMap; * * // Declare a set state variable * EnumerableMap.UintToAddressMap private myMap; * } * ``` * * As of v3.0.0, only maps of type `uint256 -> address` (`UintToAddressMap`) are * supported. */ library EnumerableMap { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Map type with // bytes32 keys and values. // The Map implementation uses private functions, and user-facing // implementations (such as Uint256ToAddressMap) are just wrappers around // the underlying Map. // This means that we can only create new EnumerableMaps for types that fit // in bytes32. struct MapEntry { bytes32 _key; bytes32 _value; } struct Map { // Storage of map keys and values MapEntry[] _entries; // Position of the entry defined by a key in the `entries` array, plus 1 // because index 0 means a key is not in the map. mapping (bytes32 => uint256) _indexes; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function _set(Map storage map, bytes32 key, bytes32 value) private returns (bool) { // We read and store the key's index to prevent multiple reads from the same storage slot uint256 keyIndex = map._indexes[key]; if (keyIndex == 0) { // Equivalent to !contains(map, key) map._entries.push(MapEntry({ _key: key, _value: value })); // The entry is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value map._indexes[key] = map._entries.length; return true; } else { map._entries[keyIndex - 1]._value = value; return false; } } /** * @dev Removes a key-value pair from a map. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function _remove(Map storage map, bytes32 key) private returns (bool) { // We read and store the key's index to prevent multiple reads from the same storage slot uint256 keyIndex = map._indexes[key]; if (keyIndex != 0) { // Equivalent to contains(map, key) // To delete a key-value pair from the _entries array in O(1), we swap the entry to delete with the last one // in the array, and then remove the last entry (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = keyIndex - 1; uint256 lastIndex = map._entries.length - 1; // When the entry to delete is the last one, the swap operation is unnecessary. However, since this occurs // so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement. MapEntry storage lastEntry = map._entries[lastIndex]; // Move the last entry to the index where the entry to delete is map._entries[toDeleteIndex] = lastEntry; // Update the index for the moved entry map._indexes[lastEntry._key] = toDeleteIndex + 1; // All indexes are 1-based // Delete the slot where the moved entry was stored map._entries.pop(); // Delete the index for the deleted slot delete map._indexes[key]; return true; } else { return false; } } /** * @dev Returns true if the key is in the map. O(1). */ function _contains(Map storage map, bytes32 key) private view returns (bool) { return map._indexes[key] != 0; } /** * @dev Returns the number of key-value pairs in the map. O(1). */ function _length(Map storage map) private view returns (uint256) { return map._entries.length; } /** * @dev Returns the key-value pair stored at position `index` in the map. O(1). * * Note that there are no guarantees on the ordering of entries inside the * array, and it may change when more entries are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Map storage map, uint256 index) private view returns (bytes32, bytes32) { require(map._entries.length > index, "EnumerableMap: index out of bounds"); MapEntry storage entry = map._entries[index]; return (entry._key, entry._value); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. */ function _tryGet(Map storage map, bytes32 key) private view returns (bool, bytes32) { uint256 keyIndex = map._indexes[key]; if (keyIndex == 0) return (false, 0); // Equivalent to contains(map, key) return (true, map._entries[keyIndex - 1]._value); // All indexes are 1-based } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function _get(Map storage map, bytes32 key) private view returns (bytes32) { uint256 keyIndex = map._indexes[key]; require(keyIndex != 0, "EnumerableMap: nonexistent key"); // Equivalent to contains(map, key) return map._entries[keyIndex - 1]._value; // All indexes are 1-based } /** * @dev Same as {_get}, with a custom error message when `key` is not in the map. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {_tryGet}. */ function _get(Map storage map, bytes32 key, string memory errorMessage) private view returns (bytes32) { uint256 keyIndex = map._indexes[key]; require(keyIndex != 0, errorMessage); // Equivalent to contains(map, key) return map._entries[keyIndex - 1]._value; // All indexes are 1-based } // UintToAddressMap struct UintToAddressMap { Map _inner; } /** * @dev Adds a key-value pair to a map, or updates the value for an existing * key. O(1). * * Returns true if the key was added to the map, that is if it was not * already present. */ function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) { return _set(map._inner, bytes32(key), bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the key was removed from the map, that is if it was present. */ function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) { return _remove(map._inner, bytes32(key)); } /** * @dev Returns true if the key is in the map. O(1). */ function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) { return _contains(map._inner, bytes32(key)); } /** * @dev Returns the number of elements in the map. O(1). */ function length(UintToAddressMap storage map) internal view returns (uint256) { return _length(map._inner); } /** * @dev Returns the element 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(UintToAddressMap storage map, uint256 index) internal view returns (uint256, address) { (bytes32 key, bytes32 value) = _at(map._inner, index); return (uint256(key), address(uint160(uint256(value)))); } /** * @dev Tries to returns the value associated with `key`. O(1). * Does not revert if `key` is not in the map. * * _Available since v3.4._ */ function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool, address) { (bool success, bytes32 value) = _tryGet(map._inner, bytes32(key)); return (success, address(uint160(uint256(value)))); } /** * @dev Returns the value associated with `key`. O(1). * * Requirements: * * - `key` must be in the map. */ function get(UintToAddressMap storage map, uint256 key) internal view returns (address) { return address(uint160(uint256(_get(map._inner, bytes32(key))))); } /** * @dev Same as {get}, with a custom error message when `key` is not in the map. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryGet}. */ function get(UintToAddressMap storage map, uint256 key, string memory errorMessage) internal view returns (address) { return address(uint160(uint256(_get(map._inner, bytes32(key), errorMessage)))); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../math/SafeMath.sol"; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath} * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never * directly accessed. */ library Counters { using SafeMath for uint256; struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { // The {SafeMath} overflow check can be skipped here, see the comment at the top counter._value += 1; } function decrement(Counter storage counter) internal { counter._value = counter._value.sub(1); } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <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 GSN 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 payable) { return msg.sender; } function _msgData() internal view virtual returns (bytes memory) { this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 return msg.data; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; /** * @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 * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize, which returns 0 for contracts in // construction, since the code is only stored at the end of the // constructor execution. uint256 size; // solhint-disable-next-line no-inline-assembly assembly { size := extcodesize(account) } return size > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); // solhint-disable-next-line avoid-low-level-calls, avoid-call-value (bool success, ) = recipient.call{ value: amount }(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain`call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCall(target, data, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); require(isContract(target), "Address: call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.call{ value: value }(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data, string memory errorMessage) internal view returns (bytes memory) { require(isContract(target), "Address: static call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.staticcall(data); return _verifyCallResult(success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) { require(isContract(target), "Address: delegate call to non-contract"); // solhint-disable-next-line avoid-low-level-calls (bool success, bytes memory returndata) = target.delegatecall(data); return _verifyCallResult(success, returndata, errorMessage); } function _verifyCallResult(bool success, bytes memory returndata, string memory errorMessage) private pure returns(bytes memory) { if (success) { return returndata; } else { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly // solhint-disable-next-line no-inline-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. * * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. */ function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; import "./IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; import "./IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional enumeration extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Enumerable is IERC721 { /** * @dev Returns the total amount of tokens stored by the contract. */ function totalSupply() external view returns (uint256); /** * @dev Returns a token ID owned by `owner` at a given `index` of its token list. * Use along with {balanceOf} to enumerate all of ``owner``'s tokens. */ function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256 tokenId); /** * @dev Returns a token ID at a given `index` of all the tokens stored by the contract. * Use along with {totalSupply} to enumerate all tokens. */ function tokenByIndex(uint256 index) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.8.0; import "../../introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the caller. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool _approved) external; /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "../../utils/Context.sol"; import "./IERC721.sol"; import "./IERC721Metadata.sol"; import "./IERC721Enumerable.sol"; import "./IERC721Receiver.sol"; import "../../introspection/ERC165.sol"; import "../../math/SafeMath.sol"; import "../../utils/Address.sol"; import "../../utils/EnumerableSet.sol"; import "../../utils/EnumerableMap.sol"; import "../../utils/Strings.sol"; /** * @title ERC721 Non-Fungible Token Standard basic implementation * @dev see https://eips.ethereum.org/EIPS/eip-721 */ contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable { using SafeMath for uint256; using Address for address; using EnumerableSet for EnumerableSet.UintSet; using EnumerableMap for EnumerableMap.UintToAddressMap; using Strings for uint256; // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; // Mapping from holder address to their (enumerable) set of owned tokens mapping (address => EnumerableSet.UintSet) private _holderTokens; // Enumerable mapping from token ids to their owners EnumerableMap.UintToAddressMap private _tokenOwners; // Mapping from token ID to approved address mapping (uint256 => address) private _tokenApprovals; // Mapping from owner to operator approvals mapping (address => mapping (address => bool)) private _operatorApprovals; // Token name string private _name; // Token symbol string private _symbol; // Optional mapping for token URIs mapping (uint256 => string) private _tokenURIs; // Base URI string private _baseURI; /* * bytes4(keccak256('balanceOf(address)')) == 0x70a08231 * bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e * bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3 * bytes4(keccak256('getApproved(uint256)')) == 0x081812fc * bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465 * bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5 * bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde * * => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^ * 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd */ bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; /* * bytes4(keccak256('name()')) == 0x06fdde03 * bytes4(keccak256('symbol()')) == 0x95d89b41 * bytes4(keccak256('tokenURI(uint256)')) == 0xc87b56dd * * => 0x06fdde03 ^ 0x95d89b41 ^ 0xc87b56dd == 0x5b5e139f */ bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f; /* * bytes4(keccak256('totalSupply()')) == 0x18160ddd * bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) == 0x2f745c59 * bytes4(keccak256('tokenByIndex(uint256)')) == 0x4f6ccce7 * * => 0x18160ddd ^ 0x2f745c59 ^ 0x4f6ccce7 == 0x780e9d63 */ bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63; /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ constructor (string memory name_, string memory symbol_) public { _name = name_; _symbol = symbol_; // register the supported interfaces to conform to ERC721 via ERC165 _registerInterface(_INTERFACE_ID_ERC721); _registerInterface(_INTERFACE_ID_ERC721_METADATA); _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view virtual override returns (uint256) { require(owner != address(0), "ERC721: balance query for the zero address"); return _holderTokens[owner].length(); } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view virtual override returns (address) { return _tokenOwners.get(tokenId, "ERC721: owner query for nonexistent token"); } /** * @dev See {IERC721Metadata-name}. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); string memory _tokenURI = _tokenURIs[tokenId]; string memory base = baseURI(); // If there is no base URI, return the token URI. if (bytes(base).length == 0) { return _tokenURI; } // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). if (bytes(_tokenURI).length > 0) { return string(abi.encodePacked(base, _tokenURI)); } // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI. return string(abi.encodePacked(base, tokenId.toString())); } /** * @dev Returns the base URI set via {_setBaseURI}. This will be * automatically added as a prefix in {tokenURI} to each token's URI, or * to the token ID if no specific URI is set for that token ID. */ function baseURI() public view virtual returns (string memory) { return _baseURI; } /** * @dev See {IERC721Enumerable-tokenOfOwnerByIndex}. */ function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) { return _holderTokens[owner].at(index); } /** * @dev See {IERC721Enumerable-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { // _tokenOwners are indexed by tokenIds, so .length() returns the number of tokenIds return _tokenOwners.length(); } /** * @dev See {IERC721Enumerable-tokenByIndex}. */ function tokenByIndex(uint256 index) public view virtual override returns (uint256) { (uint256 tokenId, ) = _tokenOwners.at(index); return tokenId; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual override { address owner = ERC721.ownerOf(tokenId); require(to != owner, "ERC721: approval to current owner"); require(_msgSender() == owner || ERC721.isApprovedForAll(owner, _msgSender()), "ERC721: approve caller is not owner nor approved for all" ); _approve(to, tokenId); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view virtual override returns (address) { require(_exists(tokenId), "ERC721: approved query for nonexistent token"); return _tokenApprovals[tokenId]; } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual override { require(operator != _msgSender(), "ERC721: approve to caller"); _operatorApprovals[_msgSender()][operator] = approved; emit ApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { return _operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom(address from, address to, uint256 tokenId) public virtual override { //solhint-disable-next-line max-line-length require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _transfer(from, to, tokenId); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override { require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); _safeTransfer(from, to, tokenId, _data); } /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * `_data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual { _transfer(from, to, tokenId); require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Returns whether `tokenId` exists. * * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. * * Tokens start existing when they are minted (`_mint`), * and stop existing when they are burned (`_burn`). */ function _exists(uint256 tokenId) internal view virtual returns (bool) { return _tokenOwners.contains(tokenId); } /** * @dev Returns whether `spender` is allowed to manage `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { require(_exists(tokenId), "ERC721: operator query for nonexistent token"); address owner = ERC721.ownerOf(tokenId); return (spender == owner || getApproved(tokenId) == spender || ERC721.isApprovedForAll(owner, spender)); } /** * @dev Safely mints `tokenId` and transfers it to `to`. * * Requirements: d* * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal virtual { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual { _mint(to, tokenId); require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal virtual { require(to != address(0), "ERC721: mint to the zero address"); require(!_exists(tokenId), "ERC721: token already minted"); _beforeTokenTransfer(address(0), to, tokenId); _holderTokens[to].add(tokenId); _tokenOwners.set(tokenId, to); emit Transfer(address(0), to, tokenId); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal virtual { address owner = ERC721.ownerOf(tokenId); // internal owner _beforeTokenTransfer(owner, address(0), tokenId); // Clear approvals _approve(address(0), tokenId); // Clear metadata (if any) if (bytes(_tokenURIs[tokenId]).length != 0) { delete _tokenURIs[tokenId]; } _holderTokens[owner].remove(tokenId); _tokenOwners.remove(tokenId); emit Transfer(owner, address(0), tokenId); } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer(address from, address to, uint256 tokenId) internal virtual { require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); // internal owner require(to != address(0), "ERC721: transfer to the zero address"); _beforeTokenTransfer(from, to, tokenId); // Clear approvals from the previous owner _approve(address(0), tokenId); _holderTokens[from].remove(tokenId); _holderTokens[to].add(tokenId); _tokenOwners.set(tokenId, to); emit Transfer(from, to, tokenId); } /** * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. * * Requirements: * * - `tokenId` must exist. */ function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { require(_exists(tokenId), "ERC721Metadata: URI set of nonexistent token"); _tokenURIs[tokenId] = _tokenURI; } /** * @dev Internal function to set the base URI for all token IDs. It is * automatically added as a prefix to the value returned in {tokenURI}, * or to the token ID if {tokenURI} is empty. */ function _setBaseURI(string memory baseURI_) internal virtual { _baseURI = baseURI_; } /** * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. * The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param _data bytes optional data to send along with the call * @return bool whether the call correctly returned the expected magic value */ function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) private returns (bool) { if (!to.isContract()) { return true; } bytes memory returndata = to.functionCall(abi.encodeWithSelector( IERC721Receiver(to).onERC721Received.selector, _msgSender(), from, tokenId, _data ), "ERC721: transfer to non ERC721Receiver implementer"); bytes4 retval = abi.decode(returndata, (bytes4)); return (retval == _ERC721_RECEIVED); } /** * @dev Approve `to` to operate on `tokenId` * * Emits an {Approval} event. */ function _approve(address to, uint256 tokenId) internal virtual { _tokenApprovals[tokenId] = to; emit Approval(ERC721.ownerOf(tokenId), to, tokenId); // internal owner } /** * @dev Hook that is called before any token transfer. This includes minting * and burning. * * Calling conditions: * * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be * transferred to `to`. * - When `from` is zero, `tokenId` will be minted for `to`. * - When `to` is zero, ``from``'s `tokenId` will be burned. * - `from` cannot be the zero address. * - `to` cannot be the zero address. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual { } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Wrappers over Solidity's arithmetic operations with added overflow * checks. * * Arithmetic operations in Solidity wrap on overflow. This can easily result * in bugs, because programmers usually assume that an overflow raises an * error, which is the standard behavior in high level programming languages. * `SafeMath` restores this intuition by reverting the transaction when an * operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeMath { /** * @dev Returns the addition of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } /** * @dev Returns the substraction of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b > a) return (false, 0); return (true, a - b); } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. * * _Available since v3.4._ */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a / b); } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. * * _Available since v3.4._ */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { if (b == 0) return (false, 0); return (true, a % b); } /** * @dev Returns the addition of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `+` operator. * * Requirements: * * - Addition cannot overflow. */ function add(uint256 a, uint256 b) internal pure returns (uint256) { uint256 c = a + b; require(c >= a, "SafeMath: addition overflow"); return c; } /** * @dev Returns the subtraction of two unsigned integers, reverting on * overflow (when the result is negative). * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b) internal pure returns (uint256) { require(b <= a, "SafeMath: subtraction overflow"); return a - b; } /** * @dev Returns the multiplication of two unsigned integers, reverting on * overflow. * * Counterpart to Solidity's `*` operator. * * Requirements: * * - Multiplication cannot overflow. */ function mul(uint256 a, uint256 b) internal pure returns (uint256) { if (a == 0) return 0; uint256 c = a * b; require(c / a == b, "SafeMath: multiplication overflow"); return c; } /** * @dev Returns the integer division of two unsigned integers, reverting on * division by zero. The result is rounded towards zero. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: division by zero"); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting when dividing by zero. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b) internal pure returns (uint256) { require(b > 0, "SafeMath: modulo by zero"); return a % b; } /** * @dev Returns the subtraction of two unsigned integers, reverting with custom message on * overflow (when the result is negative). * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {trySub}. * * Counterpart to Solidity's `-` operator. * * Requirements: * * - Subtraction cannot overflow. */ function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b <= a, errorMessage); return a - b; } /** * @dev Returns the integer division of two unsigned integers, reverting with custom message on * division by zero. The result is rounded towards zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryDiv}. * * Counterpart to Solidity's `/` operator. Note: this function uses a * `revert` opcode (which leaves remaining gas untouched) while Solidity * uses an invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a / b; } /** * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), * reverting with custom message when dividing by zero. * * CAUTION: This function is deprecated because it requires allocating memory for the error * message unnecessarily. For custom revert reasons use {tryMod}. * * Counterpart to Solidity's `%` operator. This function uses a `revert` * opcode (which leaves remaining gas untouched) while Solidity uses an * invalid opcode to revert (consuming all remaining gas). * * Requirements: * * - The divisor cannot be zero. */ function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { require(b > 0, errorMessage); return a % b; } }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.8.0; import "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts may inherit from this and call {_registerInterface} to declare * their support of an interface. */ abstract contract ERC165 is IERC165 { /* * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 */ bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; /** * @dev Mapping of interface ids to whether or not it's supported. */ mapping(bytes4 => bool) private _supportedInterfaces; constructor () internal { // Derived contracts need only register support for their own interfaces, // we register support for ERC165 itself here _registerInterface(_INTERFACE_ID_ERC165); } /** * @dev See {IERC165-supportsInterface}. * * Time complexity O(1), guaranteed to always use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return _supportedInterfaces[interfaceId]; } /** * @dev Registers the contract as an implementer of the interface defined by * `interfaceId`. Support of the actual ERC165 interface is automatic and * registering its interface id is not required. * * See {IERC165-supportsInterface}. * * Requirements: * * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). */ function _registerInterface(bytes4 interfaceId) internal virtual { require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); _supportedInterfaces[interfaceId] = true; } }
{ "remappings": [], "optimizer": { "enabled": true, "runs": 1 }, "evmVersion": "istanbul", "libraries": {}, "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"string","name":"token","type":"string"},{"internalType":"string","name":"ticker","type":"string"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"approved","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"operator","type":"address"},{"indexed":false,"internalType":"bool","name":"approved","type":"bool"}],"name":"ApprovalForAll","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"achievementId","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"string","name":"content","type":"string"}],"name":"LogNewAchievement","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"achievementIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"achievements","outputs":[{"internalType":"enum Achievements.Action","name":"action","type":"uint8"},{"internalType":"uint256","name":"occurrences","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"achievementId","type":"uint256"},{"internalType":"uint256[]","name":"marketIds","type":"uint256[]"}],"name":"claimAchievement","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"achievementId","type":"uint256"},{"internalType":"uint256[]","name":"marketIds","type":"uint256[]"},{"internalType":"uint256[]","name":"lengths","type":"uint256[]"},{"internalType":"bytes32[]","name":"history_hashes","type":"bytes32[]"},{"internalType":"address[]","name":"addrs","type":"address[]"},{"internalType":"uint256[]","name":"bonds","type":"uint256[]"},{"internalType":"bytes32[]","name":"answers","type":"bytes32[]"}],"name":"claimAchievement","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum Achievements.Action","name":"action","type":"uint8"},{"internalType":"uint256","name":"occurrences","type":"uint256"},{"internalType":"string","name":"content","type":"string"}],"name":"createAchievement","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"getApproved","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"hasUserAddedLiquidity","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"achievementId","type":"uint256"}],"name":"hasUserClaimedAchievement","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"hasUserClaimedWinnings","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"hasUserCreatedMarket","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"},{"internalType":"bytes32[]","name":"history_hashes","type":"bytes32[]"},{"internalType":"address[]","name":"addrs","type":"address[]"},{"internalType":"uint256[]","name":"bonds","type":"uint256[]"},{"internalType":"bytes32[]","name":"answers","type":"bytes32[]"}],"name":"hasUserPlacedBond","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"user","type":"address"},{"internalType":"uint256","name":"marketId","type":"uint256"}],"name":"hasUserPlacedPrediction","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"operator","type":"address"}],"name":"isApprovedForAll","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"ownerOf","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"predictionMarket","outputs":[{"internalType":"contract PredictionMarket","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"realitioERC20","outputs":[{"internalType":"contract RealitioERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"safeTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"bool","name":"approved","type":"bool"}],"name":"setApprovalForAll","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_baseURI","type":"string"}],"name":"setBaseURI","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract PredictionMarket","name":"_predictionMarket","type":"address"},{"internalType":"contract RealitioERC20","name":"_realitioERC20","type":"address"}],"name":"setContracts","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"tokenOfOwnerByIndex","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"tokenURI","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"transferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60806040526000600d553480156200001657600080fd5b506040516200369138038062003691833981016040819052620000399162000284565b8181620000566301ffc9a760e01b6001600160e01b03620000dd16565b81516200006b90600690602085019062000138565b5080516200008190600790602084019062000138565b506200009d6380ac58cd60e01b6001600160e01b03620000dd16565b620000b8635b5e139f60e01b6001600160e01b03620000dd16565b620000d363780e9d6360e01b6001600160e01b03620000dd16565b5050505062000322565b6001600160e01b03198082161415620001135760405162461bcd60e51b81526004016200010a90620002eb565b60405180910390fd5b6001600160e01b0319166000908152602081905260409020805460ff19166001179055565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106200017b57805160ff1916838001178555620001ab565b82800160010185558215620001ab579182015b82811115620001ab5782518255916020019190600101906200018e565b50620001b9929150620001bd565b5090565b620001da91905b80821115620001b95760008155600101620001c4565b90565b600082601f830112620001ee578081fd5b81516001600160401b038082111562000205578283fd5b6040516020601f8401601f191682018101838111838210171562000227578586fd5b806040525081945083825286818588010111156200024457600080fd5b600092505b8383101562000268578583018101518284018201529182019162000249565b838311156200027a5760008185840101525b5050505092915050565b6000806040838503121562000297578182fd5b82516001600160401b0380821115620002ae578384fd5b620002bc86838701620001dd565b93506020850151915080821115620002d2578283fd5b50620002e185828601620001dd565b9150509250929050565b6020808252601c908201527f4552433136353a20696e76616c696420696e7465726661636520696400000000604082015260600190565b61335f80620003326000396000f3fe608060405234801561001057600080fd5b50600436106101965760003560e01c806301ffc9a71461019b57806306fdde03146101c4578063081812fc146101d9578063095ea7b3146101f95780630c04f83b1461020e57806318160ddd1461022157806323b872dd1461023657806327aed72e146102495780632f745c591461026a57806342842e0e1461027d57806342da0beb14610290578063497e4096146102a35780634f64b2be146102b65780634f6ccce7146102c957806355f804b3146102dc5780636352211e146102ef5780636be5179f146103025780636c0360eb1461031557806370a082311461031d5780638c8b8f4e14610330578063947f53a21461034357806395d89b4114610356578063a22cb4651461035e578063b88d4fde14610371578063c87b56dd14610384578063c8c787ae14610397578063c97c8976146103aa578063c99aea33146103b2578063d55f9273146103ba578063d8952a49146103c2578063def114b6146103d5578063e7c38730146103dd578063e985e9c5146103f0578063ecf5f69b14610403575b600080fd5b6101ae6101a936600461252d565b610416565b6040516101bb9190612898565b60405180910390f35b6101cc610439565b6040516101bb91906128c4565b6101ec6101e736600461261a565b6104d0565b6040516101bb9190612847565b61020c61020736600461239b565b61051c565b005b6101ae61021c36600461239b565b6105b4565b610229610682565b6040516101bb91906128a3565b61020c6102443660046122c5565b610693565b61025c61025736600461261a565b6106cb565b6040516101bb9291906128ac565b61022961027836600461239b565b6106ea565b61020c61028b3660046122c5565b610719565b6101ae61029e36600461239b565b610734565b6102296102b1366004612632565b610762565b6102296102c436600461261a565b6108e5565b6102296102d736600461261a565b6108f7565b61020c6102ea3660046125e8565b610913565b6101ec6102fd36600461261a565b610945565b61020c610310366004612676565b610973565b6101cc610c32565b61022961032b366004612271565b610c93565b6101ae61033e3660046123c6565b610cdc565b6101ae61035136600461239b565b610f0c565b6101cc61107f565b61020c61036c36600461236e565b6110e0565b61020c61037f366004612305565b6111ae565b6101cc61039236600461261a565b6111ed565b6102296103a5366004612577565b611333565b6101ec6113d5565b6102296113e4565b6102296113ea565b61020c6103d0366004612565565b6113f6565b6101ec6114c2565b6101ae6103eb36600461239b565b6114d1565b6101ae6103fe36600461228d565b61157a565b6101ae61041136600461239b565b6115a8565b6001600160e01b0319811660009081526020819052604090205460ff165b919050565b60068054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104c55780601f1061049a576101008083540402835291602001916104c5565b820191906000526020600020905b8154815290600101906020018083116104a857829003601f168201915b505050505090505b90565b60006104db82611712565b6105005760405162461bcd60e51b81526004016104f790612d96565b60405180910390fd5b506000908152600460205260409020546001600160a01b031690565b600061052782610945565b9050806001600160a01b0316836001600160a01b0316141561055b5760405162461bcd60e51b81526004016104f790612f5a565b806001600160a01b031661056d611725565b6001600160a01b031614806105895750610589816103fe611725565b6105a55760405162461bcd60e51b81526004016104f790612bb3565b6105af8383611729565b505050565b60006105be612073565b600c5460405163b31eb89560e01b81526001600160a01b039091169063b31eb895906105f090869088906004016131db565b60606040518083038186803b15801561060857600080fd5b505afa15801561061c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610640919081019061276e565b6020840152808352151590508061065a5750602081015115155b6106765760405162461bcd60e51b81526004016104f7906129ea565b60019150505b92915050565b600061068e6002611797565b905090565b6106a461069e611725565b826117a2565b6106c05760405162461bcd60e51b81526004016104f790612ffe565b6105af838383611827565b600e602052600090815260409020805460019091015460ff9091169082565b6001600160a01b0382166000908152600160205260408120610712908363ffffffff61193516565b9392505050565b6105af838383604051806020016040528060008152506111ae565b6000908152600e602090815260408083206001600160a01b0394909416835260029093019052205460ff1690565b6000828152600e602090815260408083203384526002810190925282205460ff16156107a05760405162461bcd60e51b81526004016104f79061304f565b6002815460ff1660048111156107b257fe5b14156107d05760405162461bcd60e51b81526004016104f790613148565b80600101548351146107f45760405162461bcd60e51b81526004016104f79061296b565b60005b83518110156108d257600084828151811061080e57fe5b602002602001015190506000600481111561082557fe5b835460ff16600481111561083557fe5b141561084b5761084533826105b4565b506108c9565b6001835460ff16600481111561085d57fe5b141561086d5761084533826114d1565b6003835460ff16600481111561087f57fe5b141561088f576108453382610f0c565b6004835460ff1660048111156108a157fe5b14156108b15761084533826115a8565b60405162461bcd60e51b81526004016104f790612ee3565b506001016107f7565b506108dd3385611941565b505092915050565b600f6020526000908152604090205481565b60008061090b60028463ffffffff6119aa16565b509392505050565b61091b610c32565b51156109395760405162461bcd60e51b81526004016104f790612aa2565b610942816119c6565b50565b600061067c826040518060600160405280602981526020016132e1602991396002919063ffffffff6119dd16565b6000878152600e60209081526040808320338452600281019092529091205460ff16156109b25760405162461bcd60e51b81526004016104f79061304f565b6002815460ff1660048111156109c457fe5b146109e15760405162461bcd60e51b81526004016104f790612d46565b6000875111610a025760405162461bcd60e51b81526004016104f790612c55565b8060010154875114610a265760405162461bcd60e51b81526004016104f79061296b565b6000805b8851811015610c1b576000898281518110610a4157fe5b602002602001015190506000898381518110610a5957fe5b60200260200101519050606081604051908082528060200260200182016040528015610a8f578160200160208202803883390190505b509050606082604051908082528060200260200182016040528015610abe578160200160208202803883390190505b509050606083604051908082528060200260200182016040528015610aed578160200160208202803883390190505b509050606084604051908082528060200260200182016040528015610b1c578160200160208202803883390190505b50905060005b85811015610bf9578d8981518110610b3657fe5b6020026020010151858281518110610b4a57fe5b6020026020010181815250508c8981518110610b6257fe5b6020026020010151848281518110610b7657fe5b60200260200101906001600160a01b031690816001600160a01b0316815250508b8981518110610ba257fe5b6020026020010151838281518110610bb657fe5b6020026020010181815250508a8981518110610bce57fe5b6020026020010151828281518110610be257fe5b602090810291909101015260019889019801610b22565b610c07338887878787610cdc565b505060019096019550610a2a945050505050565b50610c26338a611941565b50505050505050505050565b60098054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104c55780601f1061049a576101008083540402835291602001916104c5565b60006001600160a01b038216610cbb5760405162461bcd60e51b81526004016104f790612c0b565b6001600160a01b038216600090815260016020526040902061067c90611797565b600c54604051630ebc905560e11b8152600091829182916001600160a01b031690631d7920aa90610d11908b906004016128a3565b60606040518083038186803b158015610d2957600080fd5b505afa158015610d3d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610d61919081019061276e565b50600b5460405163095addb960e41b81529193506001600160a01b031691506395addb9090610d949084906004016128a3565b6101406040518083038186803b158015610dad57600080fd5b505afa158015610dc1573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610de59190810190612487565b509950600097508796505050505050505b8851811015610ed957888181518110610e0b57fe5b6020026020010151868281518110610e1f57fe5b6020026020010151888381518110610e3357fe5b60200260200101518a8481518110610e4757fe5b60200260200101516000604051602001610e659594939291906127c7565b604051602081830303815290604052805190602001208414610e8657600080fd5b8a6001600160a01b0316888281518110610e9c57fe5b60200260200101516001600160a01b03161415610eb857600191505b888181518110610ec457fe5b60209081029190910101519350600101610df6565b600182151514610efb5760405162461bcd60e51b81526004016104f7906130bb565b5060019a9950505050505050505050565b6000610f16612073565b600c5460405163b31eb89560e01b81526001600160a01b039091169063b31eb89590610f4890869088906004016131db565b60606040518083038186803b158015610f6057600080fd5b505afa158015610f74573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250610f98919081019061276e565b6020840152825250600c5460405163bf45572160e01b81526000916001600160a01b03169063bf45572190610fd19087906004016128a3565b60206040518083038186803b158015610fe957600080fd5b505afa158015610ffd573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525061102191908101906125d0565b905060008112156110445760405162461bcd60e51b81526004016104f790612f9b565b600082826002811061105257fe5b6020020151116110745760405162461bcd60e51b81526004016104f7906130ff565b506001949350505050565b60078054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156104c55780601f1061049a576101008083540402835291602001916104c5565b6110e8611725565b6001600160a01b0316826001600160a01b031614156111195760405162461bcd60e51b81526004016104f790612a6f565b8060056000611126611725565b6001600160a01b03908116825260208083019390935260409182016000908120918716808252919093529120805460ff19169215159290921790915561116a611725565b6001600160a01b03167f17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31836040516111a29190612898565b60405180910390a35050565b6111bf6111b9611725565b836117a2565b6111db5760405162461bcd60e51b81526004016104f790612ffe565b6111e7848484846119ea565b50505050565b60606111f882611712565b6112145760405162461bcd60e51b81526004016104f790612e2b565b60008281526008602090815260409182902080548351601f60026000196101006001861615020190931692909204918201849004840281018401909452808452606093928301828280156112a95780601f1061127e576101008083540402835291602001916112a9565b820191906000526020600020905b81548152906001019060200180831161128c57829003601f168201915b5050505050905060606112ba610c32565b90508051600014156112ce57509050610434565b8151156113005780826040516020016112e8929190612819565b60405160208183030381529060405292505050610434565b8061130a85611a1d565b60405160200161131b929190612819565b60405160208183030381529060405292505050919050565b60008083116113545760405162461bcd60e51b81526004016104f790613197565b600d546000818152600e6020526040902080548690829060ff1916600183600481111561137d57fe5b021790555060018101859055604051339083907ff12dd2cec284c7695fa5ff87a683b64add6d47d7a74e190171798ee5e70a744c906113bd9088906128c4565b60405180910390a35060018101600d55949350505050565b600b546001600160a01b031681565b600d5481565b600061068e600a611ae1565b600c546001600160a01b03161561141f5760405162461bcd60e51b81526004016104f790612cf9565b600b546001600160a01b0316156114485760405162461bcd60e51b81526004016104f790612b69565b6001600160a01b03821661146e5760405162461bcd60e51b81526004016104f790612b32565b6001600160a01b0381166114945760405162461bcd60e51b81526004016104f790612eae565b600c80546001600160a01b039384166001600160a01b031991821617909155600b8054929093169116179055565b600c546001600160a01b031681565b600c5460405163b31eb89560e01b815260009182916001600160a01b039091169063b31eb8959061150890869088906004016131db565b60606040518083038186803b15801561152057600080fd5b505afa158015611534573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611558919081019061276e565b5090915050806106765760405162461bcd60e51b81526004016104f790612f17565b6001600160a01b03918216600090815260056020908152604080832093909416825291909152205460ff1690565b60006001600160a01b0383166115d05760405162461bcd60e51b81526004016104f790612fd1565b600c54604051630ebc905560e11b815260009182916001600160a01b0390911690631d7920aa906116059087906004016128a3565b60606040518083038186803b15801561161d57600080fd5b505afa158015611631573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250611655919081019061276e565b50600b5460405163095addb960e41b81529194506001600160a01b031691506395addb90906116889085906004016128a3565b6101406040518083038186803b1580156116a157600080fd5b505afa1580156116b5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506116d99190810190612487565b509698505050506001600160a01b038a811690871614945061107493505050505760405162461bcd60e51b81526004016104f790612e7a565b600061067c60028363ffffffff611ae516565b3390565b600081815260046020526040902080546001600160a01b0319166001600160a01b038416908117909155819061175e82610945565b6001600160a01b03167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560405160405180910390a45050565b600061067c82611ae1565b60006117ad82611712565b6117c95760405162461bcd60e51b81526004016104f790612ae6565b60006117d483610945565b9050806001600160a01b0316846001600160a01b0316148061180f5750836001600160a01b0316611804846104d0565b6001600160a01b0316145b8061181f575061181f818561157a565b949350505050565b826001600160a01b031661183a82610945565b6001600160a01b0316146118605760405162461bcd60e51b81526004016104f790612de2565b6001600160a01b0382166118865760405162461bcd60e51b81526004016104f790612a2b565b6118918383836105af565b61189c600082611729565b6001600160a01b03831660009081526001602052604090206118c4908263ffffffff611af116565b506001600160a01b03821660009081526001602052604090206118ed908263ffffffff611afd16565b506119006002828463ffffffff611b0916565b5080826001600160a01b0316846001600160a01b031660008051602061330a83398151915260405160405180910390a4505050565b60006107128383611b1f565b600061194d600a611b64565b6000611959600a611ae1565b90506119658482611b6d565b6000818152600f60209081526040808320869055858352600e82528083206001600160a01b03881684526002019091529020805460ff19166001179055905092915050565b60008080806119b98686611c2b565b9097909650945050505050565b80516119d9906009906020840190612091565b5050565b600061181f848484611c87565b6119f5848484611827565b611a0184848484611ce6565b6111e75760405162461bcd60e51b81526004016104f790612919565b606081611a4257506040805180820190915260018152600360fc1b6020820152610434565b8160005b8115611a5a57600101600a82049150611a46565b6060816040519080825280601f01601f191660200182016040528015611a87576020820181803883390190505b50859350905060001982015b8315611ad857600a840660300160f81b82828060019003935081518110611ab657fe5b60200101906001600160f81b031916908160001a905350600a84049350611a93565b50949350505050565b5490565b60006107128383611dcb565b60006107128383611de3565b60006107128383611ea9565b600061181f84846001600160a01b038516611ef3565b81546000908210611b425760405162461bcd60e51b81526004016104f7906128d7565b826000018281548110611b5157fe5b9060005260206000200154905092915050565b80546001019055565b6001600160a01b038216611b935760405162461bcd60e51b81526004016104f790612cc4565b611b9c81611712565b15611bb95760405162461bcd60e51b81526004016104f7906129b4565b611bc5600083836105af565b6001600160a01b0382166000908152600160205260409020611bed908263ffffffff611afd16565b50611c006002828463ffffffff611b0916565b5060405181906001600160a01b0384169060009060008051602061330a833981519152908290a45050565b815460009081908310611c505760405162461bcd60e51b81526004016104f790612c82565b6000846000018481548110611c6157fe5b906000526020600020906002020190508060000154816001015492509250509250929050565b60008281526001840160205260408120548281611cb75760405162461bcd60e51b81526004016104f791906128c4565b50846000016001820381548110611cca57fe5b9060005260206000209060020201600101549150509392505050565b6000611cfa846001600160a01b0316611f8a565b611d065750600161181f565b6060611d94630a85bd0160e11b611d1b611725565b888787604051602401611d31949392919061285b565b604051602081830303815290604052906001600160e01b0319166020820180516001600160e01b0383818316178352505050506040518060600160405280603281526020016132af603291396001600160a01b038816919063ffffffff611f9016565b9050600081806020019051611dac9190810190612549565b6001600160e01b031916630a85bd0160e11b1492505050949350505050565b60009081526001919091016020526040902054151590565b60008181526001830160205260408120548015611e9f5783546000198083019190810190600090879083908110611e1657fe5b9060005260206000200154905080876000018481548110611e3357fe5b600091825260208083209091019290925582815260018981019092526040902090840190558654879080611e6357fe5b6001900381819060005260206000200160009055905586600101600087815260200190815260200160002060009055600194505050505061067c565b600091505061067c565b6000611eb58383611dcb565b611eeb5750815460018181018455600084815260208082209093018490558454848252828601909352604090209190915561067c565b50600061067c565b600082815260018401602052604081205480611f58575050604080518082018252838152602080820184815286546001818101895560008981528481209551600290930290950191825591519082015586548684528188019092529290912055610712565b82856000016001830381548110611f6b57fe5b9060005260206000209060020201600101819055506000915050610712565b3b151590565b606061181f848460008585611fa485611f8a565b611fc05760405162461bcd60e51b81526004016104f790613084565b60006060866001600160a01b03168587604051611fdd91906127fd565b60006040518083038185875af1925050503d806000811461201a576040519150601f19603f3d011682016040523d82523d6000602084013e61201f565b606091505b509150915061202f82828661203a565b979650505050505050565b60608315612049575081610712565b8251156120595782518084602001fd5b8160405162461bcd60e51b81526004016104f791906128c4565b60405180604001604052806002906020820280388339509192915050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106120d257805160ff19168380011785556120ff565b828001600101855582156120ff579182015b828111156120ff5782518255916020019190600101906120e4565b5061210b92915061210f565b5090565b6104cd91905b8082111561210b5760008155600101612115565b803561067c81613263565b600082601f830112612144578081fd5b813561215761215282613218565b6131f2565b81815291506020808301908481018184028601820187101561217857600080fd5b60005b848110156121a057813561218e81613263565b8452928201929082019060010161217b565b505050505092915050565b600082601f8301126121bb578081fd5b81356121c961215282613218565b8181529150602080830190848101818402860182018710156121ea57600080fd5b60005b848110156121a0578135845292820192908201906001016121ed565b600082601f830112612219578081fd5b81356001600160401b0381111561222e578182fd5b612241601f8201601f19166020016131f2565b915080825283602082850101111561225857600080fd5b8060208401602084013760009082016020015292915050565b600060208284031215612282578081fd5b813561071281613263565b6000806040838503121561229f578081fd5b82356122aa81613263565b915060208301356122ba81613263565b809150509250929050565b6000806000606084860312156122d9578081fd5b83356122e481613263565b925060208401356122f481613263565b929592945050506040919091013590565b6000806000806080858703121561231a578081fd5b843561232581613263565b9350602085013561233581613263565b92506040850135915060608501356001600160401b03811115612356578182fd5b61236287828801612209565b91505092959194509250565b60008060408385031215612380578182fd5b823561238b81613263565b915060208301356122ba81613278565b600080604083850312156123ad578182fd5b82356123b881613263565b946020939093013593505050565b60008060008060008060c087890312156123de578384fd5b6123e88888612129565b95506020870135945060408701356001600160401b038082111561240a578586fd5b6124168a838b016121ab565b9550606089013591508082111561242b578384fd5b6124378a838b01612134565b9450608089013591508082111561244c578384fd5b6124588a838b016121ab565b935060a089013591508082111561246d578283fd5b5061247a89828a016121ab565b9150509295509295509295565b6000806000806000806000806000806101408b8d0312156124a6578788fd5b8a51995060208b01516124b881613263565b60408c01519099506124c98161329c565b60608c01519098506124da8161329c565b60808c01519097506124eb8161329c565b60a08c01519096506124fc81613278565b8095505060c08b0151935060e08b015192506101008b015191506101208b015190509295989b9194979a5092959850565b60006020828403121561253e578081fd5b813561071281613286565b60006020828403121561255a578081fd5b815161071281613286565b6000806040838503121561229f578182fd5b60008060006060848603121561258b578081fd5b833560058110612599578182fd5b92506020840135915060408401356001600160401b038111156125ba578182fd5b6125c686828701612209565b9150509250925092565b6000602082840312156125e1578081fd5b5051919050565b6000602082840312156125f9578081fd5b81356001600160401b0381111561260e578182fd5b61181f84828501612209565b60006020828403121561262b578081fd5b5035919050565b60008060408385031215612644578182fd5b8235915060208301356001600160401b03811115612660578182fd5b61266c858286016121ab565b9150509250929050565b600080600080600080600060e0888a031215612690578081fd5b8735965060208801356001600160401b03808211156126ad578283fd5b6126b98b838c016121ab565b975060408a01359150808211156126ce578283fd5b6126da8b838c016121ab565b965060608a01359150808211156126ef578283fd5b6126fb8b838c016121ab565b955060808a0135915080821115612710578283fd5b61271c8b838c01612134565b945060a08a0135915080821115612731578283fd5b61273d8b838c016121ab565b935060c08a0135915080821115612752578283fd5b5061275f8a828b016121ab565b91505092959891949750929550565b600080600060608486031215612782578081fd5b8351925060208401519150604084015190509250925092565b600081518084526127b3816020860160208601613237565b601f01601f19169290920160200192915050565b94855260208501939093526040840191909152606090811b6001600160601b03191690830152151560f81b607482015260750190565b6000825161280f818460208701613237565b9190910192915050565b6000835161282b818460208801613237565b835190830161283e828260208801613237565b01949350505050565b6001600160a01b0391909116815260200190565b6001600160a01b038581168252841660208201526040810183905260806060820181905260009061288e9083018461279b565b9695505050505050565b901515815260200190565b90815260200190565b60408101600584106128ba57fe5b9281526020015290565b600060208252610712602083018461279b565b60208082526022908201527f456e756d657261626c655365743a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b60208082526032908201527f4552433732313a207472616e7366657220746f206e6f6e20455243373231526560408201527131b2b4bb32b91034b6b83632b6b2b73a32b960711b606082015260800190565b60208082526029908201527f4d61726b65747320636f756e7420616e64206f6363757272656e63657320646f6040820152680dc4ee840dac2e8c6d60bb1b606082015260800190565b6020808252601c908201527b115490cdcc8c4e881d1bdad95b88185b1c9958591e481b5a5b9d195960221b604082015260600190565b60208082526021908201527f7573657220646f6573206e6f7420686f6c64206f7574636f6d652073686172656040820152607360f81b606082015260800190565b60208082526024908201527f4552433732313a207472616e7366657220746f20746865207a65726f206164646040820152637265737360e01b606082015260800190565b60208082526019908201527822a9219b99189d1030b8383937bb32903a379031b0b63632b960391b604082015260600190565b60208082526024908201527f626173655552492063616e206f6e6c7920626520696e697469616c697a6564206040820152636f6e636560e01b606082015260800190565b6020808252602c908201527f4552433732313a206f70657261746f7220717565727920666f72206e6f6e657860408201526b34b9ba32b73a103a37b5b2b760a11b606082015260800190565b6020808252601e908201527f5f70726564696374696f6e4d61726b6574206164647265737320697320300000604082015260600190565b6020808252602a908201527f7265616c6974696f45524332302063616e206f6e6c7920626520696e697469616040820152696c697a6564206f6e636560b01b606082015260800190565b60208082526038908201527f4552433732313a20617070726f76652063616c6c6572206973206e6f74206f776040820152771b995c881b9bdc88185c1c1c9bdd995908199bdc88185b1b60421b606082015260800190565b6020808252602a908201527f4552433732313a2062616c616e636520717565727920666f7220746865207a65604082015269726f206164647265737360b01b606082015260800190565b602080825260139082015272139bc81858dd1a5bdb9cc81c1c9bdd9a591959606a1b604082015260600190565b60208082526022908201527f456e756d657261626c654d61703a20696e646578206f7574206f6620626f756e604082015261647360f01b606082015260800190565b6020808252818101527f4552433732313a206d696e7420746f20746865207a65726f2061646472657373604082015260600190565b6020808252602d908201527f70726564696374696f6e4d61726b65742063616e206f6e6c7920626520696e6960408201526c7469616c697a6564206f6e636560981b606082015260800190565b60208082526030908201527f4d6574686f64206f6e6c79207573656420666f7220626f6e6420706c6163656d60408201526f656e7420616368696576656d656e747360801b606082015260800190565b6020808252602c908201527f4552433732313a20617070726f76656420717565727920666f72206e6f6e657860408201526b34b9ba32b73a103a37b5b2b760a11b606082015260800190565b60208082526029908201527f4552433732313a207472616e73666572206f6620746f6b656e2074686174206960408201526839903737ba1037bbb760b91b606082015260800190565b6020808252602f908201527f4552433732314d657461646174613a2055524920717565727920666f72206e6f60408201526e3732bc34b9ba32b73a103a37b5b2b760891b606082015260800190565b6020808252601a90820152791d5cd95c88191a59081b9bdd0818dc99585d19481b585c9ad95d60321b604082015260600190565b6020808252601b908201527a05f7265616c6974696f45524332302061646472657373206973203602c1b604082015260600190565b6020808252601a908201527924b73b30b634b21030b1b434b2bb32b6b2b73a1030b1ba34b7b760311b604082015260600190565b60208082526023908201527f7573657220646f6573206e6f7420686f6c64206c69717569646974792073686160408201526272657360e81b606082015260800190565b60208082526021908201527f4552433732313a20617070726f76616c20746f2063757272656e74206f776e656040820152603960f91b606082015260800190565b6020808252601c908201527b1b585c9ad95d081a5cc81cdd1a5b1b081b9bdd081c995cdbdb1d995960221b604082015260600190565b60208082526013908201527207573657220616464726573732069732030783606c1b604082015260600190565b60208082526031908201527f4552433732313a207472616e736665722063616c6c6572206973206e6f74206f6040820152701ddb995c881b9bdc88185c1c1c9bdd9959607a1b606082015260800190565b6020808252601b908201527a1058da1a595d995b595b9d08185b1c9958591e4818db185a5b5959602a1b604082015260600190565b6020808252601d908201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604082015260600190565b60208082526024908201527f7573657220686173206e6f7420706c61636564206120626f6e6420696e206d616040820152631c9ad95d60e21b606082015260800190565b60208082526029908201527f7573657220646f6573206e6f7420686f6c642077696e6e696e67206f7574636f6040820152686d652073686172657360b81b606082015260800190565b6020808252602f908201527f4d6574686f64206e6f74207573656420666f7220626f6e6420706c6163656d6560408201526e6e7420616368696576656d656e747360881b606082015260800190565b60208082526024908201527f6f6363757272656e6365732068617320746f20626520677265617465722074686040820152630616e20360e41b606082015260800190565b9182526001600160a01b0316602082015260400190565b6040518181016001600160401b038111828210171561321057600080fd5b604052919050565b60006001600160401b0382111561322d578081fd5b5060209081020190565b60005b8381101561325257818101518382015260200161323a565b838111156111e75750506000910152565b6001600160a01b038116811461094257600080fd5b801515811461094257600080fd5b6001600160e01b03198116811461094257600080fd5b63ffffffff8116811461094257600080fdfe4552433732313a207472616e7366657220746f206e6f6e20455243373231526563656976657220696d706c656d656e7465724552433732313a206f776e657220717565727920666f72206e6f6e6578697374656e7420746f6b656eddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122083c8a681c2f5e884dafe32f7d82a2686645ddaaad672eb8d85e7f81a84d67a2b64736f6c63430006020033000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005506f6c6b790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005504f4c4b59000000000000000000000000000000000000000000000000000000
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000005506f6c6b790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005504f4c4b59000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : token (string): Polky
Arg [1] : ticker (string): POLKY
-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [3] : 506f6c6b79000000000000000000000000000000000000000000000000000000
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [5] : 504f4c4b59000000000000000000000000000000000000000000000000000000