Transaction Hash:
Block:
12077660 at Mar-20-2021 07:42:17 PM +UTC
Transaction Fee:
0.043052322 ETH
$80.32
Gas Used:
380,994 Gas / 113 Gwei
Emitted Events:
152 |
Trollbox.VoteOccurred( tournamentId=1, roundId=20, voterId=160, choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], weights=[5, 4, 2, 5, 2, 2, 3, 4, 5, 5], metadata=0000000000000000000000000000000000000000000000000000000000000000 )
|
Account State Difference:
Address | Before | After | State Difference | ||
---|---|---|---|---|---|
0xE319d04F...F6df3FB47 |
0.096958738161977194 Eth
Nonce: 31
|
0.053906416161977194 Eth
Nonce: 32
| 0.043052322 | ||
0xEa6556E3...f6f1808BA | |||||
0xEA674fdD...16B898ec8
Miner
| (Ethermine) | 869.824951339242070511 Eth | 869.868003661242070511 Eth | 0.043052322 |
Execution Trace
Trollbox.vote( voterId=160, tournamentId=1, choices=[1, 2, 3, 4, 5, 6, 7, 8, 9, 10], weights=[5, 4, 2, 5, 2, 2, 3, 4, 5, 5], hash=0000000000000000000000000000000000000000000000000000000000000000, updateRoundId=0 )
-
Identity.ownerOf( tokenId=160 ) => ( 0xE319d04FEd54c473A956209cC90eCEBF6df3FB47 )
-
SafeMathLib.plus( a=116, b=5 ) => ( 121 )
-
SafeMathLib.times( a=5, b=5 ) => ( 25 )
-
SafeMathLib.plus( a=0, b=25 ) => ( 25 )
-
SafeMathLib.plus( a=70, b=4 ) => ( 74 )
-
SafeMathLib.times( a=4, b=4 ) => ( 16 )
-
SafeMathLib.plus( a=25, b=16 ) => ( 41 )
-
SafeMathLib.plus( a=64, b=2 ) => ( 66 )
-
SafeMathLib.times( a=2, b=2 ) => ( 4 )
-
SafeMathLib.plus( a=41, b=4 ) => ( 45 )
-
SafeMathLib.plus( a=153, b=5 ) => ( 158 )
-
SafeMathLib.times( a=5, b=5 ) => ( 25 )
-
SafeMathLib.plus( a=45, b=25 ) => ( 70 )
-
SafeMathLib.plus( a=59, b=2 ) => ( 61 )
-
SafeMathLib.times( a=2, b=2 ) => ( 4 )
-
SafeMathLib.plus( a=70, b=4 ) => ( 74 )
-
SafeMathLib.plus( a=45, b=2 ) => ( 47 )
-
SafeMathLib.times( a=2, b=2 ) => ( 4 )
-
SafeMathLib.plus( a=74, b=4 ) => ( 78 )
-
SafeMathLib.plus( a=33, b=3 ) => ( 36 )
-
SafeMathLib.times( a=3, b=3 ) => ( 9 )
-
SafeMathLib.plus( a=78, b=9 ) => ( 87 )
-
SafeMathLib.plus( a=67, b=4 ) => ( 71 )
-
SafeMathLib.times( a=4, b=4 ) => ( 16 )
-
SafeMathLib.plus( a=87, b=16 ) => ( 103 )
-
SafeMathLib.plus( a=72, b=5 ) => ( 77 )
-
SafeMathLib.times( a=5, b=5 ) => ( 25 )
-
SafeMathLib.plus( a=103, b=25 ) => ( 128 )
-
SafeMathLib.plus( a=72, b=5 ) => ( 77 )
-
SafeMathLib.times( a=5, b=5 ) => ( 25 )
-
SafeMathLib.plus( a=128, b=25 ) => ( 153 )
File 1 of 3: Trollbox
File 2 of 3: Identity
File 3 of 3: SafeMathLib
{"SafeMathLib.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.7.4;\n\nlibrary SafeMathLib {\n function times(uint a, uint b) public pure returns (uint) {\n uint c = a * b;\n require(a == 0 || c / a == b, \u0027Overflow detected\u0027);\n return c;\n }\n\n function minus(uint a, uint b) public pure returns (uint) {\n require(b \u003c= a, \u0027Underflow detected\u0027);\n return a - b;\n }\n\n function plus(uint a, uint b) public pure returns (uint) {\n uint c = a + b;\n require(c\u003e=a \u0026\u0026 c\u003e=b, \u0027Overflow detected\u0027);\n return c;\n }\n\n}\n"},"TrollboxComplete.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.7.4;\n\nimport \"./SafeMathLib.sol\";\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller\u0027s account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller\u0027s tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender\u0027s allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller\u0027s\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n/**\n * @dev Required interface of an ERC721 compliant contract.\n */\ninterface IERC721 {\n /**\n * @dev Emitted when `tokenId` token is transferred from `from` to `to`.\n */\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.\n */\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /**\n * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.\n */\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n /**\n * @dev Returns the number of tokens in ``owner``\u0027s account.\n */\n function balanceOf(address owner) external view returns (uint256 balance);\n\n /**\n * @dev Returns the owner of the `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function ownerOf(uint256 tokenId) external view returns (address owner);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients\n * are aware of the ERC721 protocol to prevent tokens from being forever locked.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(address from, address to, uint256 tokenId) external;\n\n /**\n * @dev Transfers `tokenId` token from `from` to `to`.\n *\n * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address from, address to, uint256 tokenId) external;\n\n /**\n * @dev Gives permission to `to` to transfer `tokenId` token to another account.\n * The approval is cleared when the token is transferred.\n *\n * Only a single account can be approved at a time, so approving the zero address clears previous approvals.\n *\n * Requirements:\n *\n * - The caller must own the token or be an approved operator.\n * - `tokenId` must exist.\n *\n * Emits an {Approval} event.\n */\n function approve(address to, uint256 tokenId) external;\n\n /**\n * @dev Returns the account approved for `tokenId` token.\n *\n * Requirements:\n *\n * - `tokenId` must exist.\n */\n function getApproved(uint256 tokenId) external view returns (address operator);\n\n /**\n * @dev Approve or remove `operator` as an operator for the caller.\n * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.\n *\n * Requirements:\n *\n * - The `operator` cannot be the caller.\n *\n * Emits an {ApprovalForAll} event.\n */\n function setApprovalForAll(address operator, bool _approved) external;\n\n /**\n * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.\n *\n * See {setApprovalForAll}\n */\n function isApprovedForAll(address owner, address operator) external view returns (bool);\n\n /**\n * @dev Safely transfers `tokenId` token from `from` to `to`.\n *\n * Requirements:\n *\n * - `from` cannot be the zero address.\n * - `to` cannot be the zero address.\n * - `tokenId` token must exist and be owned by `from`.\n * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.\n * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.\n *\n * Emits a {Transfer} event.\n */\n function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;\n}\n\n\ncontract Trollbox {\n using SafeMathLib for uint;\n\n /**\n Votes are a mapping from choices to weights, plus a metadataHash, which references an arbitrary bit of metadata\n stored on IPFS. The meaning of these choices is not stored on chain, only the index. For example, if the choices\n are [\"BTC\", \"ETH\", \"DASH\"], and the user wants to put 3 votes on BTC, 5 votes on ETH and 4 on DASH, then this\n will be recorded as weights[1] = 3; weights[2] = 5; weights[3] = 4; The choices are indexed starting on 1 to\n prevent confusion caused by empty votes.\n **/\n struct Vote {\n mapping(uint =\u003e uint) weights;\n bytes32 metadataHash;\n }\n\n /**\n Rounds occur with some frequency and represent a complete cycle of prediction-\u003eresolution. Each round has an id,\n which represents it\u0027s location in a linear sequence of rounds of the same type. It stores a mapping of voter\n ids to votes and records the winning option when the round is resolved.\n **/\n struct Round {\n uint roundId;\n mapping (uint =\u003e Vote) votes;\n mapping (uint =\u003e uint) voteTotals;\n uint winningOption;\n }\n\n /**\n A tournament is a linear sequence of rounds of the same type. Tournaments are identified by an integer that\n increases sequentially with each tournament. Tournaments also have hash for storing off-chain metadata about the\n tournament. A tournament has a set wavelength and phase, called roundLengthSeconds and startDate, respectively. Each\n tournament also has it\u0027s own set of voice credits, which is a mapping from address to balance. The rounds\n mapping takes a round id and spits out a Round struct. The tokenRoundBonus attribute describes how much IERC20 to be\n distributed to the voters each round. The tokenListENS stores the ENS address of a token list that forms the\n choices of the tournament.\n **/\n struct Tournament {\n uint tournamentId;\n bytes32 metadataHash; // ipfs hash of more verbose description, possibly multimedia\n uint startTime;\n uint roundLengthSeconds;\n uint tokenRoundBonus;\n uint minimumRank;\n uint voiceUBI; // number of voice credits available to spend each round\n bytes32 tokenListENS;\n address winnerOracle; // address that sets the winner for a tournament\n mapping (uint =\u003e uint) voiceCredits;\n mapping (uint =\u003e Round) rounds;\n }\n\n /**\n An identity is purchased with IERC20 and stores the creation time and a mapping of tournament id to the last round\n id that the identity voted in, which is used for deferred reward computation.\n **/\n struct IdMetadata {\n mapping (uint =\u003e uint) lastRoundVoted;\n// uint firstTimeVoted;\n// uint timesVoted;\n uint cumulativeBonus;\n uint rank;\n }\n\n address public management; // authoritative key that can make important decisions, can be DAO address later\n address public rankManager;\n IERC20 public token;\n IERC721 public identity;\n\n uint public numTournaments = 0; // a counter to know what index to assign to new tournaments\n bytes32 public siteHash;\n\n mapping (uint =\u003e Tournament) public tournaments; // mapping from tournament id to tournament struct\n mapping (uint =\u003e IdMetadata) public identities; // mapping from address to identity struct\n mapping (uint =\u003e uint) public tokensWon; // tokensWon[voterId] = fvt-wei owed\n mapping (uint =\u003e mapping (uint =\u003e mapping (uint =\u003e bool))) public syncMap; // syncMap[voterId][tournamentId][roundId] = true/false\n\n // events for consumption by off chain systems\n event VoteOccurred(uint indexed tournamentId, uint indexed roundId, uint indexed voterId, uint[] choices, uint[] weights, bytes32 metadata);\n event RoundResolved(uint indexed tournamentId, uint roundId, uint winningChoice);\n event TournamentCreated(uint tournamentId, bytes32 metadataHash, uint startTime, uint roundLengthSeconds, uint tokenRoundBonus, uint minimumRank, uint voiceUBI, bytes32 tokenListENS, address winnerOracle);\n event ManagementUpdated(address oldManagement, address newManagement);\n event SiteHashUpdated(bytes32 oldSiteHash, bytes32 newSiteHash);\n event RankUpdated(uint voterId, uint oldRank, uint newRank);\n event RankManagerUpdated(address oldManager, address newManager);\n event TournamentUpdated(uint tournamentId, bytes32 metadataHash, uint tokenRoundBonus, uint minimumRank, uint voiceUBI, bytes32 tokenListENS, address winnerOracle);\n event AccountSynced(uint tournamentId, uint voterId);\n\n modifier managementOnly() {\n require (msg.sender == management, \u0027Only management may call this\u0027);\n _;\n }\n\n constructor(address mgmt, address rankMgmt, address id) {\n management = mgmt;\n rankManager = rankMgmt;\n identity = IERC721(id);\n }\n\n // this function creates a new tournament type, only management can call it\n function createTournament(\n bytes32 hash,\n uint startTime,\n uint roundLengthSeconds,\n uint tokenRoundBonus,\n bytes32 tokenListENS,\n address oracle,\n uint minRank,\n uint voiceUBI) public managementOnly {\n numTournaments = numTournaments.plus(1);\n Tournament storage tournament = tournaments[numTournaments];\n tournament.metadataHash = hash;\n tournament.startTime = startTime == 0 ? block.timestamp : startTime;\n tournament.tournamentId = numTournaments;\n tournament.roundLengthSeconds = roundLengthSeconds;\n tournament.tokenRoundBonus = tokenRoundBonus;\n tournament.minimumRank = minRank;\n tournament.voiceUBI = voiceUBI;\n tournament.tokenListENS = tokenListENS;\n tournament.winnerOracle = oracle;\n emit TournamentCreated(numTournaments, hash, startTime, roundLengthSeconds, tokenRoundBonus, minRank, voiceUBI, tokenListENS, oracle);\n }\n\n\n\n // this completes the round, and assigns it a winning choice, which enables deferred updates to voice credits\n function resolveRound(uint tournamentId, uint roundId, uint winningOption) public {\n Tournament storage tournament = tournaments[tournamentId];\n require(msg.sender == tournament.winnerOracle, \u0027Only winner oracle can call this\u0027);\n uint currentRoundId = getCurrentRoundId(tournamentId);\n Round storage round = tournament.rounds[roundId];\n require(roundAlreadyResolved(tournamentId, roundId) == false, \u0027Round already resolved\u0027);\n require(currentRoundId \u003e roundId + 1, \u0027Too early to resolve\u0027);\n round.roundId = roundId;\n round.winningOption = winningOption;\n emit RoundResolved(tournamentId, roundId, winningOption);\n }\n\n function voteCheck(uint voterId, uint tournamentId, uint roundId) internal view {\n require(roundId \u003e 0, \u0027Tournament not started yet\u0027);\n require(identity.ownerOf(voterId) == msg.sender, \u0027Must own identity to vote with it\u0027);\n require(roundId \u003e identities[voterId].lastRoundVoted[tournamentId], \u0027Can only vote one time per round\u0027);\n require(tournaments[tournamentId].minimumRank \u003c= identities[voterId].rank, \u0027Insufficient rank to participate in this tournament\u0027);\n }\n\n // this is called by an identity that wishes to vote on a given tournament, with the choices and weights\n function vote(\n uint voterId,\n uint tournamentId,\n uint[] memory choices,\n uint[] memory weights,\n bytes32 hash,\n uint updateRoundId\n ) public {\n uint roundId = getCurrentRoundId(tournamentId);\n Round storage currentRound = tournaments[tournamentId].rounds[roundId];\n\n voteCheck(voterId, tournamentId, roundId);\n require(choices.length == weights.length, \u0027Mismatched choices and lengths\u0027);\n\n updateAccount(voterId, tournamentId, updateRoundId);\n\n identities[voterId].lastRoundVoted[tournamentId] = roundId;\n\n Vote storage currentVote = currentRound.votes[voterId];\n currentVote.metadataHash = hash;\n uint balance = getVoiceCredits(tournamentId, voterId);\n uint sum = 0;\n\n for (uint i = 0; i \u003c weights.length; i++) {\n currentVote.weights[choices[i]] = weights[i];\n currentRound.voteTotals[choices[i]] = currentRound.voteTotals[choices[i]].plus(weights[i]);\n sum = sum.plus(weights[i].times(weights[i]));\n }\n require(sum \u003c= balance, \u0027Must not spend more than your balance\u0027);\n\n emit VoteOccurred(tournamentId, roundId, voterId, choices, weights, hash);\n }\n\n function withdrawWinnings(uint voterId) public {\n uint winnings = tokensWon[voterId];\n address owner = identity.ownerOf(voterId);\n require(winnings \u003e 0, \u0027Nothing to withdraw\u0027);\n // doing it this way out of re-entry avoidance habit, not because it\u0027s actually possible here\n tokensWon[voterId] = 0;\n token.transfer(owner, winnings);\n }\n\n // this actually updates the voice credit balance to include the reward\n function updateAccount(uint voterId, uint tournamentId, uint roundId) public {\n IdMetadata storage id = identities[voterId];\n Tournament storage tournament = tournaments[tournamentId];\n bool roundResolved = roundAlreadyResolved(tournamentId, roundId);\n bool shouldSync = isSynced(voterId, tournamentId, roundId) == false;\n\n if (shouldSync \u0026\u0026 roundResolved) {\n // idempotent condition, call twice, update once, since this function is public\n syncMap[voterId][tournamentId][roundId] = true; // idempotence\n\n (uint voiceCreditBonus, uint tokenBonus) = getRoundBonus(voterId, tournamentId, roundId);\n tournament.voiceCredits[voterId] = getVoiceCredits(tournamentId, voterId).plus(voiceCreditBonus);\n tokensWon[voterId] = tokensWon[voterId].plus(tokenBonus);\n id.cumulativeBonus = id.cumulativeBonus.plus(voiceCreditBonus);\n emit AccountSynced(tournamentId, voterId);\n }\n }\n\n\n/**\n====================================== GETTERS ==========================================================\n**/\n function getRound(uint tournamentId, uint roundId) public view returns (uint[2] memory) {\n Round storage round = tournaments[tournamentId].rounds[roundId];\n return [round.roundId, round.winningOption];\n }\n\n // this computes the id of the current round for a given tournament, starting with round 1 on the startTime\n function getCurrentRoundId(uint tournamentId) public view returns (uint) {\n Tournament storage tournament = tournaments[tournamentId];\n uint startTime = tournament.startTime;\n uint roundLengthSeconds = tournament.roundLengthSeconds;\n if (block.timestamp \u003e= startTime) {\n return 1 + ((block.timestamp - startTime) / roundLengthSeconds);\n } else {\n return 0;\n }\n }\n\n function getVoiceCredits(uint tournamentId, uint voterId) public view returns (uint) {\n Tournament storage tournament = tournaments[tournamentId];\n uint voiceCredits = tournament.voiceCredits[voterId];\n if (voiceCredits \u003e 0) {\n return voiceCredits;\n } else {\n return tournament.voiceUBI;\n }\n }\n\n function getLastRoundVoted(uint tournamentId, uint voterId) public view returns (uint) {\n return identities[voterId].lastRoundVoted[tournamentId];\n }\n\n function getVoteTotals(uint tournamentId, uint roundId, uint option) public view returns (uint) {\n return tournaments[tournamentId].rounds[roundId].voteTotals[option];\n }\n\n function getVoteMetadata(uint tournamentId, uint roundId, uint voterId) public view returns (bytes32) {\n return tournaments[tournamentId].rounds[roundId].votes[voterId].metadataHash;\n }\n\n function getVoiceUBI(uint tournamentId) public view returns (uint) {\n return tournaments[tournamentId].voiceUBI;\n }\n\n function getRoundResults(uint voterId, uint tournamentId, uint roundId) public view returns (uint, uint) {\n Tournament storage tournament = tournaments[tournamentId];\n Round storage round = tournament.rounds[roundId];\n Vote storage thisVote = round.votes[voterId];\n return (thisVote.weights[round.winningOption], round.voteTotals[round.winningOption]);\n }\n\n // this actually updates the voice credit balance to include the reward\n function getRoundBonus(uint voterId, uint tournamentId, uint roundId) public view returns (uint, uint) {\n Tournament storage tournament = tournaments[tournamentId];\n (uint voteWeight, uint totalVotes) = getRoundResults(voterId, tournamentId, roundId);\n uint tokenBonus = 0;\n // if this is the first round voterId has voted in, totalVotes will be 0\n if (totalVotes \u003e 0) {\n tokenBonus = tournament.tokenRoundBonus.times(voteWeight) / totalVotes;\n }\n uint voiceCreditBonus = voteWeight.times(voteWeight);\n return (voiceCreditBonus, tokenBonus);\n }\n\n function isSynced(uint voterId, uint tournamentId, uint roundId) public view returns (bool) {\n return syncMap[voterId][tournamentId][roundId];\n }\n\n function roundAlreadyResolved(uint tournamentId, uint roundId) public view returns (bool) {\n return tournaments[tournamentId].rounds[roundId].winningOption \u003e 0;\n }\n\n/**\n====================================== SETTERS ==========================================================\n**/\n\n // change the site hash\n function setSiteHash(bytes32 newHash) public managementOnly {\n bytes32 oldHash = siteHash;\n siteHash = newHash;\n emit SiteHashUpdated(oldHash, newHash);\n }\n\n function setRank(uint voterId, uint newRank) public {\n require(msg.sender == rankManager, \u0027Only rankManager may call this\u0027);\n IdMetadata storage id = identities[voterId];\n uint oldRank = id.rank;\n id.rank = newRank;\n emit RankUpdated(voterId, oldRank, newRank);\n }\n\n function setToken(address tokenAddr) public managementOnly {\n token = IERC20(tokenAddr);\n }\n\n function updateTournament(uint tournamentId, bytes32 newMetadata, uint newBonus, uint newMinRank, uint newUBI, bytes32 newTokenList, address newOracle) public managementOnly {\n Tournament storage tournament = tournaments[tournamentId];\n tournament.metadataHash = newMetadata;\n // no changing round length\n tournament.tokenRoundBonus = newBonus;\n tournament.minimumRank = newMinRank;\n tournament.voiceUBI = newUBI;\n tournament.tokenListENS = newTokenList;\n tournament.winnerOracle = newOracle;\n emit TournamentUpdated(tournamentId, newMetadata, newBonus, newMinRank, newUBI, newTokenList, newOracle);\n }\n\n function setRankManager(address newManager) public managementOnly {\n address oldManager = rankManager;\n rankManager = newManager;\n emit RankManagerUpdated(oldManager, newManager);\n }\n\n // change the management key\n function setManagement(address newMgmt) public managementOnly {\n address oldMgmt = management;\n management = newMgmt;\n emit ManagementUpdated(oldMgmt, newMgmt);\n }\n\n\n}\n"}}
File 2 of 3: Identity
{"IdentityComplete.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.7.4;\n\nimport \"./SafeMathLib.sol\";\n\n\n/**\n * @dev Interface of the ERC20 standard as defined in the EIP.\n */\ninterface IERC20 {\n /**\n * @dev Returns the amount of tokens in existence.\n */\n function totalSupply() external view returns (uint256);\n\n /**\n * @dev Returns the amount of tokens owned by `account`.\n */\n function balanceOf(address account) external view returns (uint256);\n\n /**\n * @dev Moves `amount` tokens from the caller\u0027s account to `recipient`.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Returns the remaining number of tokens that `spender` will be\n * allowed to spend on behalf of `owner` through {transferFrom}. This is\n * zero by default.\n *\n * This value changes when {approve} or {transferFrom} are called.\n */\n function allowance(address owner, address spender) external view returns (uint256);\n\n /**\n * @dev Sets `amount` as the allowance of `spender` over the caller\u0027s tokens.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * IMPORTANT: Beware that changing an allowance with this method brings the risk\n * that someone may use both the old and the new allowance by unfortunate\n * transaction ordering. One possible solution to mitigate this race\n * condition is to first reduce the spender\u0027s allowance to 0 and set the\n * desired value afterwards:\n * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729\n *\n * Emits an {Approval} event.\n */\n function approve(address spender, uint256 amount) external returns (bool);\n\n /**\n * @dev Moves `amount` tokens from `sender` to `recipient` using the\n * allowance mechanism. `amount` is then deducted from the caller\u0027s\n * allowance.\n *\n * Returns a boolean value indicating whether the operation succeeded.\n *\n * Emits a {Transfer} event.\n */\n function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);\n\n /**\n * @dev Emitted when `value` tokens are moved from one account (`from`) to\n * another (`to`).\n *\n * Note that `value` may be zero.\n */\n event Transfer(address indexed from, address indexed to, uint256 value);\n\n /**\n * @dev Emitted when the allowance of a `spender` for an `owner` is set by\n * a call to {approve}. `value` is the new allowance.\n */\n event Approval(address indexed owner, address indexed spender, uint256 value);\n}\n\n\n\n/**\n * @title ERC721 token receiver interface\n * @dev Interface for any contract that wants to support safeTransfers\n * from ERC721 asset contracts.\n */\ninterface IERC721Receiver {\n /**\n * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}\n * by `operator` from `from`, this function is called.\n *\n * It must return its Solidity selector to confirm the token transfer.\n * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.\n *\n * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.\n */\n function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);\n}\n\n// ERC 721\ncontract Identity {\n using SafeMathLib for uint;\n\n mapping (uint =\u003e address) public owners;\n mapping (address =\u003e uint) public balances;\n\n // Mapping from owner to operator approvals\n mapping (address =\u003e mapping (address =\u003e bool)) public operatorApprovals;\n mapping (uint =\u003e address) public tokenApprovals;\n\n // Equals to `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`\n // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector`\n bytes4 private constant ERC721_RECEIVED = 0x150b7a02;\n\n /*\n * bytes4(keccak256(\u0027balanceOf(address)\u0027)) == 0x70a08231\n * bytes4(keccak256(\u0027ownerOf(uint256)\u0027)) == 0x6352211e\n * bytes4(keccak256(\u0027approve(address,uint256)\u0027)) == 0x095ea7b3\n * bytes4(keccak256(\u0027getApproved(uint256)\u0027)) == 0x081812fc\n * bytes4(keccak256(\u0027setApprovalForAll(address,bool)\u0027)) == 0xa22cb465\n * bytes4(keccak256(\u0027isApprovedForAll(address,address)\u0027)) == 0xe985e9c5\n * bytes4(keccak256(\u0027transferFrom(address,address,uint256)\u0027)) == 0x23b872dd\n * bytes4(keccak256(\u0027safeTransferFrom(address,address,uint256)\u0027)) == 0x42842e0e\n * bytes4(keccak256(\u0027safeTransferFrom(address,address,uint256,bytes)\u0027)) == 0xb88d4fde\n *\n * =\u003e 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^\n * 0xa22cb465 ^ 0xe985e9c5 ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd\n */\n bytes4 private constant INTERFACE_ID_ERC721 = 0x80ac58cd;\n\n /*\n * bytes4(keccak256(\u0027supportsInterface(bytes4)\u0027)) == 0x01ffc9a7\n */\n bytes4 private constant INTERFACE_ID_ERC165 = 0x01ffc9a7;\n\n uint public identityIncreaseFactor = 2;\n uint public identityIncreaseDenominator = 1;\n uint public lastIdentityPrice = 100 * 1 ether; // burn cost of making an identity, in IERC20\n uint public identityDecayFactor = 1 ether / 100;\n uint public identityPriceFloor = 100 * 1 ether;\n uint public numIdentities = 0;\n uint public lastPurchaseBlock;\n\n address public management;\n\n IERC20 public token;\n\n event ManagementUpdated(address oldManagement, address newManagement);\n event TokenSet(address token);\n event IdentityIncreaseFactorUpdated(uint oldIdIncreaseFactor, uint newIdIncreaseFactor);\n event IdentityIncreaseDenominatorUpdated(uint oldIdIncreaseDenominator, uint newIdIncreaseDenominator);\n event IdentityDecayFactorUpdated(uint oldIdDecayFactor, uint newIdDecayFactor);\n event IdentityPriceFloorUpdated(uint oldIdPriceFloor, uint newIdPriceFloor);\n event IdentityCreated(address indexed owner, uint indexed token);\n\n\n /// @dev This emits when ownership of any NFT changes by any mechanism.\n /// This event emits when NFTs are created (`from` == 0) and destroyed\n /// (`to` == 0). Exception: during contract creation, any number of NFTs\n /// may be created and assigned without emitting Transfer. At the time of\n /// any transfer, the approved address for that NFT (if any) is reset to none.\n event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);\n\n /// @dev This emits when the approved address for an NFT is changed or\n /// reaffirmed. The zero address indicates there is no approved address.\n /// When a Transfer event emits, this also indicates that the approved\n /// address for that NFT (if any) is reset to none.\n event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);\n\n /// @dev This emits when an operator is enabled or disabled for an owner.\n /// The operator can manage all NFTs of the owner.\n event ApprovalForAll(address indexed owner, address indexed operator, bool approved);\n\n modifier managementOnly() {\n require (msg.sender == management, \u0027Identity: Only management may call this\u0027);\n _;\n }\n\n constructor(address mgmt) {\n management = mgmt;\n lastPurchaseBlock = block.number;\n }\n\n function setToken(address tokenAddr) public managementOnly {\n token = IERC20(tokenAddr);\n emit TokenSet(tokenAddr);\n }\n\n // this function creates an identity by burning the IERC20. Anyone can call it.\n function createMyIdentity(uint maxPrice) public {\n uint identityPrice = getIdentityPrice();\n require(maxPrice \u003e= identityPrice || maxPrice == 0, \"Identity: current price exceeds user maximum\");\n token.transferFrom(msg.sender, address(0), identityPrice);\n createIdentity(msg.sender);\n lastIdentityPrice = identityPrice.times(identityIncreaseFactor) / identityIncreaseDenominator;\n lastPurchaseBlock = block.number;\n }\n\n // this function creates an identity for free. Only management can call it.\n function createIdentityFor(address newId) public managementOnly {\n createIdentity(newId);\n }\n\n function createIdentity(address owner) internal {\n numIdentities = numIdentities.plus(1);\n owners[numIdentities] = owner;\n balances[owner] = balances[owner].plus(1);\n emit Transfer(address(0), owner, numIdentities);\n emit IdentityCreated(owner, numIdentities);\n }\n\n function getIdentityPrice() public view returns (uint) {\n uint decay = identityDecayFactor.times(block.number.minus(lastPurchaseBlock));\n if (lastIdentityPrice \u003c decay.plus(identityPriceFloor)) {\n return identityPriceFloor;\n } else {\n return lastIdentityPrice.minus(decay);\n }\n }\n\n /// ================= SETTERS =======================================\n\n // change the management key\n function setManagement(address newMgmt) public managementOnly {\n address oldMgmt = management;\n management = newMgmt;\n emit ManagementUpdated(oldMgmt, newMgmt);\n }\n\n function setIdentityIncreaseFactor(uint newIncreaseFactor) public managementOnly {\n uint oldIncreaseFactor = identityIncreaseFactor;\n identityIncreaseFactor = newIncreaseFactor;\n emit IdentityIncreaseFactorUpdated(oldIncreaseFactor, newIncreaseFactor);\n }\n\n function setIdentityIncreaseDenominator(uint newIncreaseDenominator) public managementOnly {\n uint oldIncreaseDenominator = identityIncreaseDenominator;\n identityIncreaseDenominator = newIncreaseDenominator;\n emit IdentityIncreaseDenominatorUpdated(oldIncreaseDenominator, newIncreaseDenominator);\n }\n\n function setIdentityDecayFactor(uint newDecayFactor) public managementOnly {\n uint oldDecayFactor = identityDecayFactor;\n identityDecayFactor = newDecayFactor;\n emit IdentityDecayFactorUpdated(oldDecayFactor, newDecayFactor);\n }\n\n function setIdentityPriceFloor(uint newPriceFloor) public managementOnly {\n uint oldFloor = identityPriceFloor;\n identityPriceFloor = newPriceFloor;\n emit IdentityPriceFloorUpdated(oldFloor, newPriceFloor);\n }\n\n /// ================= ERC 721 FUNCTIONS =============================================\n\n /// @notice Count all NFTs assigned to an owner\n /// @dev NFTs assigned to the zero address are considered invalid, and this\n /// function throws for queries about the zero address.\n /// @param owner An address for whom to query the balance\n /// @return The number of NFTs owned by `owner`, possibly zero\n function balanceOf(address owner) external view returns (uint256) {\n return balances[owner];\n }\n\n /// @notice Find the owner of an NFT\n /// @dev NFTs assigned to zero address are considered invalid, and queries\n /// about them do throw.\n /// @param tokenId The identifier for an NFT\n /// @return The address of the owner of the NFT\n function ownerOf(uint256 tokenId) external view returns (address) {\n address owner = owners[tokenId];\n require(owner != address(0), \u0027No such token\u0027);\n return owner;\n }\n\n /// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE\n /// TO CONFIRM THAT `to` IS CAPABLE OF RECEIVING NFTS OR ELSE\n /// THEY MAY BE PERMANENTLY LOST\n /// @dev Throws unless `msg.sender` is the current owner, an authorized\n /// operator, or the approved address for this NFT. Throws if `from` is\n /// not the current owner. Throws if `to` is the zero address. Throws if\n /// `tokenId` is not a valid NFT.\n /// @param from The current owner of the NFT\n /// @param to The new owner\n /// @param tokenId The NFT to transfer\n function transferFrom(address from, address to, uint256 tokenId) public {\n require(isApproved(msg.sender, tokenId), \u0027Identity: Unapproved transfer\u0027);\n transfer(from, to, tokenId);\n }\n\n /// @notice Transfers the ownership of an NFT from one address to another address\n /// @dev Throws unless `msg.sender` is the current owner, an authorized\n /// operator, or the approved address for this NFT. Throws if `from` is\n /// not the current owner. Throws if `to` is the zero address. Throws if\n /// `tokenId` is not a valid NFT. When transfer is complete, this function\n /// checks if `to` is a smart contract (code size \u003e 0). If so, it calls\n /// `onERC721Received` on `to` and throws if the return value is not\n /// `bytes4(keccak256(\"onERC721Received(address,address,uint256,bytes)\"))`.\n /// @param from The current owner of the NFT\n /// @param to The new owner\n /// @param tokenId The NFT to transfer\n /// @param data Additional data with no specified format, sent in call to `to`\n function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public {\n transferFrom(from, to, tokenId);\n require(checkOnERC721Received(from, to, tokenId, data), \"Identity: transfer to non ERC721Receiver implementer\");\n }\n\n /// @notice Transfers the ownership of an NFT from one address to another address\n /// @dev This works identically to the other function with an extra data parameter,\n /// except this function just sets data to \"\".\n /// @param from The current owner of the NFT\n /// @param to The new owner\n /// @param tokenId The NFT to transfer\n function safeTransferFrom(address from, address to, uint256 tokenId) public {\n safeTransferFrom(from, to, tokenId, \u0027\u0027);\n }\n\n\n /// @notice Change or reaffirm the approved address for an NFT\n /// @dev The zero address indicates there is no approved address.\n /// Throws unless `msg.sender` is the current NFT owner, or an authorized\n /// operator of the current owner.\n /// @param approved The new approved NFT controller\n /// @param tokenId The NFT to approve\n function approve(address approved, uint256 tokenId) public {\n address owner = owners[tokenId];\n require(isApproved(msg.sender, tokenId), \u0027Identity: Not authorized to approve\u0027);\n require(owner != approved, \u0027Identity: Approving self not allowed\u0027);\n tokenApprovals[tokenId] = approved;\n emit Approval(owner, approved, tokenId);\n }\n\n /// @notice Enable or disable approval for a third party (\"operator\") to manage\n /// all of `msg.sender`\u0027s assets\n /// @dev Emits the ApprovalForAll event. The contract MUST allow\n /// multiple operators per owner.\n /// @param operator Address to add to the set of authorized operators\n /// @param approved True if the operator is approved, false to revoke approval\n function setApprovalForAll(address operator, bool approved) external {\n operatorApprovals[msg.sender][operator] = approved;\n emit ApprovalForAll(msg.sender, operator, approved);\n }\n\n /// @notice Get the approved address for a single NFT\n /// @dev Throws if `tokenId` is not a valid NFT.\n /// @param tokenId The NFT to find the approved address for\n /// @return The approved address for this NFT, or the zero address if there is none\n function getApproved(uint256 tokenId) external view returns (address) {\n address owner = owners[tokenId];\n require(owner != address(0), \u0027Identity: Invalid tokenId\u0027);\n return tokenApprovals[tokenId];\n }\n\n /// @notice Query if an address is an authorized operator for another address\n /// @param owner The address that owns the NFTs\n /// @param operator The address that acts on behalf of the owner\n /// @return True if `operator` is an approved operator for `owner`, false otherwise\n function isApprovedForAll(address owner, address operator) public view returns (bool) {\n return operatorApprovals[owner][operator];\n }\n\n /// ================ UTILS =========================\n function isApproved(address operator, uint tokenId) public view returns (bool) {\n address owner = owners[tokenId];\n return (\n operator == owner ||\n operatorApprovals[owner][operator] ||\n tokenApprovals[tokenId] == operator\n );\n }\n\n /**\n * @dev Transfers `tokenId` from `from` to `to`.\n *\n * Requirements:\n *\n * - `to` cannot be the zero address.\n * - `tokenId` token must be owned by `from`.\n *\n * Emits a {Transfer} event.\n */\n function transfer(address from, address to, uint256 tokenId) internal {\n require(owners[tokenId] == from, \"Identity: Transfer of token that is not own\");\n require(to != address(0), \"Identity: transfer to the zero address\");\n\n // Clear approvals from the previous owner\n approve(address(0), tokenId);\n\n owners[tokenId] = to;\n balances[from] = balances[from].minus(1);\n balances[to] = balances[to].plus(1);\n\n emit Transfer(from, to, tokenId);\n }\n\n function isContract(address account) internal view returns (bool) {\n // This method relies on extcodesize, which returns 0 for non-contract addresses\n\n uint256 size;\n // solhint-disable-next-line no-inline-assembly\n assembly { size := extcodesize(account) }\n return size \u003e 0;\n }\n\n /**\n * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address.\n * The call is not executed if the target address is not a contract.\n *\n * @param from address representing the previous owner of the given token ID\n * @param to target address that will receive the tokens\n * @param tokenId uint256 ID of the token to be transferred\n * @param data bytes optional data to send along with the call\n * @return bool whether the call correctly returned the expected magic value\n */\n function checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data)\n private returns (bool)\n {\n if (!isContract(to)) {\n return true;\n }\n IERC721Receiver target = IERC721Receiver(to);\n bytes4 retval = target.onERC721Received(from, to, tokenId, data);\n return ERC721_RECEIVED == retval;\n }\n\n /**\n * @dev Returns true if this contract implements the interface defined by\n * `interfaceId`. See the corresponding\n * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]\n * to learn more about how these ids are created.\n *\n * This function call must use less than 30 000 gas.\n */\n function supportsInterface(bytes4 interfaceId) external pure returns (bool) {\n return (\n interfaceId == INTERFACE_ID_ERC721 ||\n interfaceId == INTERFACE_ID_ERC165\n );\n }\n\n}\n"},"SafeMathLib.sol":{"content":"// SPDX-License-Identifier: GPL-3.0-only\n\npragma solidity 0.7.4;\n\nlibrary SafeMathLib {\n function times(uint a, uint b) public pure returns (uint) {\n uint c = a * b;\n require(a == 0 || c / a == b, \u0027Overflow detected\u0027);\n return c;\n }\n\n function minus(uint a, uint b) public pure returns (uint) {\n require(b \u003c= a, \u0027Underflow detected\u0027);\n return a - b;\n }\n\n function plus(uint a, uint b) public pure returns (uint) {\n uint c = a + b;\n require(c\u003e=a \u0026\u0026 c\u003e=b, \u0027Overflow detected\u0027);\n return c;\n }\n\n}\n"}}
File 3 of 3: SafeMathLib
// SPDX-License-Identifier: GPL-3.0-only pragma solidity 0.7.4; library SafeMathLib { function times(uint a, uint b) public pure returns (uint) { uint c = a * b; require(a == 0 || c / a == b, 'Overflow detected'); return c; } function minus(uint a, uint b) public pure returns (uint) { require(b <= a, 'Underflow detected'); return a - b; } function plus(uint a, uint b) public pure returns (uint) { uint c = a + b; require(c>=a && c>=b, 'Overflow detected'); return c; } }