├── LICENSE ├── abis ├── BasicTournament.json ├── InauguralGateKeeper.json ├── ThreeAffinityDuelResolver.json ├── WizardGuild.json └── WizardPresale.json ├── contracts ├── BasicTournament.sol ├── InauguralGateKeeper.sol ├── ThreeAffinityDuelResolver.sol ├── WizardGuild.sol └── WizardPresale.sol └── readme.md /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2019 Dapper Labs, Inc. 2 | 3 | All rights reserved. The contents of this repository is provided for review and educational purposes ONLY. 4 | -------------------------------------------------------------------------------- /contracts/InauguralGateKeeper.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.6 <0.6.0; 2 | 3 | /// @title Shared constants used throughout the Cheeze Wizards contracts 4 | contract WizardConstants { 5 | // Wizards normally have their affinity set when they are first created, 6 | // but for example Exclusive Wizards can be created with no set affinity. 7 | // In this case the affinity can be set by the owner. 8 | uint8 internal constant ELEMENT_NOTSET = 0; //000 9 | // A neutral Wizard has no particular strength or weakness with specific 10 | // elements. 11 | uint8 internal constant ELEMENT_NEUTRAL = 1; //001 12 | // The fire, water and wind elements are used both to reflect an affinity 13 | // of Elemental Wizards for a specific element, and as the moves a 14 | // Wizard can make during a duel. 15 | // Note that if these values change then `moveMask` and `moveDelta` in 16 | // ThreeAffinityDuelResolver would need to be updated accordingly. 17 | uint8 internal constant ELEMENT_FIRE = 2; //010 18 | uint8 internal constant ELEMENT_WATER = 3; //011 19 | uint8 internal constant ELEMENT_WIND = 4; //100 20 | uint8 internal constant MAX_ELEMENT = ELEMENT_WIND; 21 | } 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | /** 31 | * @title IERC165 32 | * @dev https://eips.ethereum.org/EIPS/eip-165 33 | */ 34 | interface IERC165 { 35 | /** 36 | * @notice Query if a contract implements an interface 37 | * @param interfaceId The interface identifier, as specified in ERC-165 38 | * @dev Interface identification is specified in ERC-165. This function 39 | * uses less than 30,000 gas. 40 | */ 41 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 42 | } 43 | 44 | 45 | /** 46 | * @title ERC721 Non-Fungible Token Standard basic interface 47 | * @dev see https://eips.ethereum.org/EIPS/eip-721 48 | */ 49 | contract IERC721 is IERC165 { 50 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 51 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 52 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 53 | 54 | function balanceOf(address owner) public view returns (uint256 balance); 55 | function ownerOf(uint256 tokenId) public view returns (address owner); 56 | 57 | function approve(address to, uint256 tokenId) public; 58 | function getApproved(uint256 tokenId) public view returns (address operator); 59 | 60 | function setApprovalForAll(address operator, bool _approved) public; 61 | function isApprovedForAll(address owner, address operator) public view returns (bool); 62 | 63 | function transferFrom(address from, address to, uint256 tokenId) public; 64 | function safeTransferFrom(address from, address to, uint256 tokenId) public; 65 | 66 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public; 67 | } 68 | 69 | 70 | contract WizardGuildInterfaceId { 71 | bytes4 internal constant _INTERFACE_ID_WIZARDGUILD = 0x41d4d437; 72 | } 73 | 74 | /// @title The public interface of the Wizard Guild 75 | /// @notice The methods listed in this interface (including the inherited ERC-721 interface), 76 | /// make up the public interface of the Wizard Guild contract. Any contracts that wish 77 | /// to make use of Cheeze Wizard NFTs (such as Cheeze Wizards Tournaments!) should use 78 | /// these methods to ensure they are working correctly with the base NFTs. 79 | contract WizardGuildInterface is IERC721, WizardGuildInterfaceId { 80 | 81 | /// @notice Returns the information associated with the given Wizard 82 | /// owner - The address that owns this Wizard 83 | /// innatePower - The innate power level of this Wizard, set when minted and entirely 84 | /// immutable 85 | /// affinity - The Elemental Affinity of this Wizard. For most Wizards, this is set 86 | /// when they are minted, but some exclusive Wizards are minted with an affinity 87 | /// of 0 (ELEMENT_NOTSET). A Wizard with an NOTSET affinity should NOT be able 88 | /// to participate in Tournaments. Once the affinity of a Wizard is set to a non-zero 89 | /// value, it can never be changed again. 90 | /// metadata - A 256-bit hash of the Wizard's metadata, which is stored off chain. This 91 | /// contract doesn't specify format of this hash, nor the off-chain storage mechanism 92 | /// but, let's be honest, it's probably an IPFS SHA-256 hash. 93 | /// 94 | /// NOTE: Series zero Wizards have one of four Affinities: Neutral (1), Fire (2), Water (3) 95 | /// or Air (4, sometimes called "Wind" in the code). Future Wizard Series may have 96 | /// additional Affinities, and clients of this API should be prepared for that 97 | /// eventuality. 98 | function getWizard(uint256 id) external view returns (address owner, uint88 innatePower, uint8 affinity, bytes32 metadata); 99 | 100 | /// @notice Sets the affinity for a Wizard that doesn't already have its elemental affinity chosen. 101 | /// Only usable for Exclusive Wizards (all non-Exclusives must have their affinity chosen when 102 | /// conjured.) Even Exclusives can't change their affinity once it's been chosen. 103 | /// 104 | /// NOTE: This function can only be called by the series minter, and (therefore) only while the 105 | /// series is open. A Wizard that has no affinity when a series is closed will NEVER have an Affinity. 106 | /// BTW- This implies that a minter is responsible for either never minting ELEMENT_NOTSET 107 | /// Wizards, or having some public mechanism for a Wizard owner to set the Affinity after minting. 108 | /// @param wizardId The id of the wizard 109 | /// @param newAffinity The new affinity of the wizard 110 | function setAffinity(uint256 wizardId, uint8 newAffinity) external; 111 | 112 | /// @notice A function to be called that conjures a whole bunch of Wizards at once! You know how 113 | /// there's "a pride of lions", "a murder of crows", and "a parliament of owls"? Well, with this 114 | /// here function you can conjure yourself "a stench of Cheeze Wizards"! 115 | /// 116 | /// Unsurprisingly, this method can only be called by the registered minter for a Series. 117 | /// @param powers the power level of each wizard 118 | /// @param affinities the Elements of the wizards to create 119 | /// @param owner the address that will own the newly created Wizards 120 | function mintWizards( 121 | uint88[] calldata powers, 122 | uint8[] calldata affinities, 123 | address owner 124 | ) external returns (uint256[] memory wizardIds); 125 | 126 | /// @notice A function to be called that conjures a series of Wizards in the reserved ID range. 127 | /// @param wizardIds the ID values to use for each Wizard, must be in the reserved range of the current Series 128 | /// @param affinities the Elements of the wizards to create 129 | /// @param powers the power level of each wizard 130 | /// @param owner the address that will own the newly created Wizards 131 | function mintReservedWizards( 132 | uint256[] calldata wizardIds, 133 | uint88[] calldata powers, 134 | uint8[] calldata affinities, 135 | address owner 136 | ) external; 137 | 138 | /// @notice Sets the metadata values for a list of Wizards. The metadata for a Wizard can only be set once, 139 | /// can only be set by the COO or Minter, and can only be set while the Series is still open. Once 140 | /// a Series is closed, the metadata is locked forever! 141 | /// @param wizardIds the ID values of the Wizards to apply metadata changes to. 142 | /// @param metadata the raw metadata values for each Wizard. This contract does not define how metadata 143 | /// should be interpreted, but it is likely to be a 256-bit hash of a complete metadata package 144 | /// accessible via IPFS or similar. 145 | function setMetadata(uint256[] calldata wizardIds, bytes32[] calldata metadata) external; 146 | 147 | /// @notice Returns true if the given "spender" address is allowed to manipulate the given token 148 | /// (either because it is the owner of that token, has been given approval to manage that token) 149 | function isApprovedOrOwner(address spender, uint256 tokenId) external view returns (bool); 150 | 151 | /// @notice Verifies that a given signature represents authority to control the given Wizard ID, 152 | /// reverting otherwise. It handles three cases: 153 | /// - The simplest case: The signature was signed with the private key associated with 154 | /// an external address that is the owner of this Wizard. 155 | /// - The signature was generated with the private key associated with an external address 156 | /// that is "approved" for working with this Wizard ID. (See the Wizard Guild and/or 157 | /// the ERC-721 spec for more information on "approval".) 158 | /// - The owner or approval address (as in cases one or two) is a smart contract 159 | /// that conforms to ERC-1654, and accepts the given signature as being valid 160 | /// using its own internal logic. 161 | /// 162 | /// NOTE: This function DOES NOT accept a signature created by an address that was given "operator 163 | /// status" (as granted by ERC-721's setApprovalForAll() functionality). Doing so is 164 | /// considered an extreme edge case that can be worked around where necessary. 165 | /// @param wizardId The Wizard ID whose control is in question 166 | /// @param hash The message hash we are authenticating against 167 | /// @param sig the signature data; can be longer than 65 bytes for ERC-1654 168 | function verifySignature(uint256 wizardId, bytes32 hash, bytes calldata sig) external view; 169 | 170 | /// @notice Convenience function that verifies signatures for two wizards using equivalent logic to 171 | /// verifySignature(). Included to save on cross-contract calls in the common case where we 172 | /// are verifying the signatures of two Wizards who wish to enter into a Duel. 173 | /// @param wizardId1 The first Wizard ID whose control is in question 174 | /// @param wizardId2 The second Wizard ID whose control is in question 175 | /// @param hash1 The message hash we are authenticating against for the first Wizard 176 | /// @param hash2 The message hash we are authenticating against for the first Wizard 177 | /// @param sig1 the signature data corresponding to the first Wizard; can be longer than 65 bytes for ERC-1654 178 | /// @param sig2 the signature data corresponding to the second Wizard; can be longer than 65 bytes for ERC-1654 179 | function verifySignatures( 180 | uint256 wizardId1, 181 | uint256 wizardId2, 182 | bytes32 hash1, 183 | bytes32 hash2, 184 | bytes calldata sig1, 185 | bytes calldata sig2) external view; 186 | } 187 | 188 | 189 | 190 | 191 | 192 | 193 | /// @title ERC165Interface 194 | /// @dev https://eips.ethereum.org/EIPS/eip-165 195 | interface ERC165Interface { 196 | /// @notice Query if a contract implements an interface 197 | /// @param interfaceId The interface identifier, as specified in ERC-165 198 | /// @dev Interface identification is specified in ERC-165. This function 199 | /// uses less than 30,000 gas. 200 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 201 | } 202 | 203 | 204 | // This is kind of a hacky way to expose this constant, but it's the best that Solidity offers! 205 | contract TournamentInterfaceId { 206 | bytes4 internal constant _INTERFACE_ID_TOURNAMENT = 0xbd059098; 207 | } 208 | 209 | /// @title Tournament interface, known to GateKeeper 210 | contract TournamentInterface is TournamentInterfaceId, ERC165Interface { 211 | 212 | // function enter(uint256 tokenId, uint96 power, uint8 affinity) external payable; 213 | function revive(uint256 wizardId) external payable; 214 | 215 | function enterWizards(uint256[] calldata wizardIds, uint88[] calldata powers) external payable; 216 | 217 | // Returns true if the Tournament is currently running and active. 218 | function isActive() external view returns (bool); 219 | 220 | function powerScale() external view returns (uint256); 221 | 222 | function destroy() external; 223 | } 224 | 225 | 226 | 227 | /// @title Contract that manages addresses and access modifiers for certain operations. 228 | /// @author Dapper Labs Inc. (https://www.dapperlabs.com) 229 | contract AccessControl { 230 | 231 | /// @dev The address of the master administrator account that has the power to 232 | /// update itself and all of the other administrator addresses. 233 | /// The CEO account is not expected to be used regularly, and is intended to 234 | /// be stored offline (i.e. a hardware device kept in a safe). 235 | address public ceoAddress; 236 | 237 | /// @dev The address of the "day-to-day" operator of various privileged 238 | /// functions inside the smart contract. Although the CEO has the power 239 | /// to replace the COO, the CEO address doesn't actually have the power 240 | /// to do "COO-only" operations. This is to discourage the regular use 241 | /// of the CEO account. 242 | address public cooAddress; 243 | 244 | /// @dev The address that is allowed to move money around. Kept separate from 245 | /// the COO because the COO address typically lives on an internet-connected 246 | /// computer. 247 | address payable public cfoAddress; 248 | 249 | // Events to indicate when access control role addresses are updated. 250 | event CEOTransferred(address previousCeo, address newCeo); 251 | event COOTransferred(address previousCoo, address newCoo); 252 | event CFOTransferred(address previousCfo, address newCfo); 253 | 254 | /// @dev The AccessControl constructor sets the `ceoAddress` to the sender account. Also 255 | /// initializes the COO and CFO to the passed values (CFO is optional and can be address(0)). 256 | /// @param newCooAddress The initial COO address to set 257 | /// @param newCfoAddress The initial CFO to set (optional) 258 | constructor(address newCooAddress, address payable newCfoAddress) public { 259 | _setCeo(msg.sender); 260 | setCoo(newCooAddress); 261 | 262 | if (newCfoAddress != address(0)) { 263 | setCfo(newCfoAddress); 264 | } 265 | } 266 | 267 | /// @notice Access modifier for CEO-only functionality 268 | modifier onlyCEO() { 269 | require(msg.sender == ceoAddress, "Only CEO"); 270 | _; 271 | } 272 | 273 | /// @notice Access modifier for COO-only functionality 274 | modifier onlyCOO() { 275 | require(msg.sender == cooAddress, "Only COO"); 276 | _; 277 | } 278 | 279 | /// @notice Access modifier for CFO-only functionality 280 | modifier onlyCFO() { 281 | require(msg.sender == cfoAddress, "Only CFO"); 282 | _; 283 | } 284 | 285 | function checkControlAddress(address newController) internal view { 286 | require(newController != address(0) && newController != ceoAddress, "Invalid CEO address"); 287 | } 288 | 289 | /// @notice Assigns a new address to act as the CEO. Only available to the current CEO. 290 | /// @param newCeo The address of the new CEO 291 | function setCeo(address newCeo) external onlyCEO { 292 | checkControlAddress(newCeo); 293 | _setCeo(newCeo); 294 | } 295 | 296 | /// @dev An internal utility function that updates the CEO variable and emits the 297 | /// transfer event. Used from both the public setCeo function and the constructor. 298 | function _setCeo(address newCeo) private { 299 | emit CEOTransferred(ceoAddress, newCeo); 300 | ceoAddress = newCeo; 301 | } 302 | 303 | /// @notice Assigns a new address to act as the COO. Only available to the current CEO. 304 | /// @param newCoo The address of the new COO 305 | function setCoo(address newCoo) public onlyCEO { 306 | checkControlAddress(newCoo); 307 | emit COOTransferred(cooAddress, newCoo); 308 | cooAddress = newCoo; 309 | } 310 | 311 | /// @notice Assigns a new address to act as the CFO. Only available to the current CEO. 312 | /// @param newCfo The address of the new CFO 313 | function setCfo(address payable newCfo) public onlyCEO { 314 | checkControlAddress(newCfo); 315 | emit CFOTransferred(cfoAddress, newCfo); 316 | cfoAddress = newCfo; 317 | } 318 | } 319 | 320 | 321 | 322 | 323 | /// @title WizardPresaleInterface 324 | /// @notice This interface represents the single method that the final tournament and master Wizard contracts 325 | /// will use to import the presale wizards when those contracts have been finalized a released on 326 | /// mainnet. Once all presale Wizards have been absorbed, this temporary pre-sale contract can be 327 | /// destroyed. 328 | contract WizardPresaleInterface { 329 | 330 | // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md on how 331 | // to calculate this 332 | bytes4 public constant _INTERFACE_ID_WIZARDPRESALE = 0x4df71efb; 333 | 334 | /// @notice This function is used to bring a presale Wizard into the final contracts. It can 335 | /// ONLY be called by the official gatekeeper contract (as set by the Owner of the presale 336 | /// contract). It does a number of things: 337 | /// 1. Check that the presale Wizard exists, and has not already been absorbed 338 | /// 2. Transfer the Eth used to create the presale Wizard to the caller 339 | /// 3. Mark the Wizard as having been absorbed, reclaiming the storage used by the presale info 340 | /// 4. Return the Wizard information (its owner, minting price, and elemental alignment) 341 | /// @param id the id of the presale Wizard to be absorbed 342 | function absorbWizard(uint256 id) external returns (address owner, uint256 power, uint8 affinity); 343 | 344 | /// @notice A convenience function that allows multiple Wizards to be moved to the final contracts 345 | /// simultaneously, works the same as the previous function, but in a batch. 346 | /// @param ids An array of ids indicating which presale Wizards are to be absorbed 347 | function absorbWizardMulti(uint256[] calldata ids) external 348 | returns (address[] memory owners, uint256[] memory powers, uint8[] memory affinities); 349 | 350 | function powerToCost(uint256 power) public pure returns (uint256 cost); 351 | function costToPower(uint256 cost) public pure returns (uint256 power); 352 | } 353 | 354 | 355 | 356 | 357 | /** 358 | * @title ERC721 token receiver interface 359 | * @dev Interface for any contract that wants to support safeTransfers 360 | * from ERC721 asset contracts. 361 | */ 362 | contract IERC721Receiver { 363 | /** 364 | * @notice Handle the receipt of an NFT 365 | * @dev The ERC721 smart contract calls this function on the recipient 366 | * after a `safeTransfer`. This function MUST return the function selector, 367 | * otherwise the caller will revert the transaction. The selector to be 368 | * returned can be obtained as `this.onERC721Received.selector`. This 369 | * function MAY throw to revert and reject the transfer. 370 | * Note: the ERC721 contract address is always the message sender. 371 | * @param operator The address which called `safeTransferFrom` function 372 | * @param from The address which previously owned the token 373 | * @param tokenId The NFT identifier which is being transferred 374 | * @param data Additional data with no specified format 375 | * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 376 | */ 377 | function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) 378 | public returns (bytes4); 379 | } 380 | 381 | 382 | 383 | /// Utility library of inline functions on address payables. 384 | /// Modified from original by OpenZeppelin. 385 | contract Address { 386 | /// @notice Returns whether the target address is a contract. 387 | /// @dev This function will return false if invoked during the constructor of a contract, 388 | /// as the code is not actually created until after the constructor finishes. 389 | /// @param account address of the account to check 390 | /// @return whether the target address is a contract 391 | function isContract(address account) internal view returns (bool) { 392 | uint256 size; 393 | // XXX Currently there is no better way to check if there is a contract in an address 394 | // than to check the size of the code at that address. 395 | // See https://ethereum.stackexchange.com/a/14016/36603 396 | // for more details about how this works. 397 | // TODO Check this again before the Serenity release, because all addresses will be 398 | // contracts then. 399 | // solhint-disable-next-line no-inline-assembly 400 | assembly { size := extcodesize(account) } // solium-disable-line security/no-inline-assembly 401 | return size > 0; 402 | } 403 | } 404 | 405 | 406 | /// @title The Inaugural Cheeze Wizards Gate Keeper contract 407 | /// @notice The GateKeeper is responsible for determining which Wizards are allowed to enter into 408 | /// a Tournament. This particular GateKeeper is designed to manage the first, official 409 | /// Cheeze Wizards Tournament! It's much more complicated than a typical GateKeeper 410 | /// implementation because it needs to juggle two additional issues that most GateKeepers 411 | /// don't need to deal with: Importing Wizards from the Presale contract and minting tokens 412 | /// to represent newly conjured Wizards. 413 | contract InauguralGateKeeper is AccessControl, WizardConstants, Address, WizardGuildInterfaceId, TournamentInterfaceId { 414 | // The Tournament contract. 415 | TournamentInterface public tournament; 416 | 417 | // The Wizard guild contract. 418 | // TODO: Replace with the address of the contract once it's deployed. 419 | WizardGuildInterface public constant WIZARD_GUILD = WizardGuildInterface(address(0x0d8c864DA1985525e0af0acBEEF6562881827bd5)); 420 | 421 | // The Wizard presale contract. 422 | WizardPresaleInterface public constant WIZARD_PRESALE = WizardPresaleInterface(address(0)); 423 | 424 | /// @dev The ratio between the cost of a Wizard (in wei) and the power of the Wizard. 425 | /// power = cost / MAX_POWER_SCALE 426 | /// cost = power * MAX_POWER_SCALE 427 | uint256 internal constant MAX_POWER_SCALE = 1000; 428 | uint256 internal tournamentPowerScale; 429 | 430 | function getTournamentPowerScale() external view returns (uint256) { 431 | return tournamentPowerScale; 432 | } 433 | 434 | /// @dev The constant conversion factor used for elementalWizardIncrement 435 | uint256 private constant TENTH_BASIS_POINTS = 100000; 436 | 437 | // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 438 | // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` 439 | bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; 440 | 441 | // We pack these values together to save storage costs (and since they are all access together) 442 | struct WizardCosts { 443 | /// @dev The cost of Neutral Wizards (in wei). 444 | uint96 neutralWizardCost; 445 | 446 | /// @dev The cost of the _next_ Elemental Wizard (in wei); increases with each Elemental Wizard 447 | /// sold, at the rate defined in `elementalWizardIncrement` 448 | uint96 elementalWizardCost; 449 | 450 | /// @dev The increment ratio in cost between sequential Elemental Wizards, multiplied by 100k for 451 | /// greater granularity (TENTH_BASIS_POINTS) 452 | uint32 elementalWizardIncrement; 453 | } 454 | 455 | WizardCosts public wizardCosts; 456 | 457 | /// @param setCooAddress The initial COO address. 458 | /// @param setCfoAddress The initial CFO address. 459 | /// @param setNeutralWizardCost The cost of the Neutral Wizards, in wei. 460 | /// @param setElementalWizardCost The starting cost of the Elemental Wizards, in wei. 461 | /// @param setElementalWizardIncrement The rate at which the Elemental Wizards cost increases. 462 | constructor( 463 | address setCooAddress, 464 | address payable setCfoAddress, 465 | uint256 setNeutralWizardCost, 466 | uint256 setElementalWizardCost, 467 | uint256 setElementalWizardIncrement) 468 | public AccessControl(setCooAddress, setCfoAddress) 469 | { 470 | wizardCosts = WizardCosts ({ 471 | neutralWizardCost: uint96(setNeutralWizardCost), 472 | elementalWizardCost: uint96(setElementalWizardCost), 473 | elementalWizardIncrement: uint32(setElementalWizardIncrement) 474 | }); 475 | } 476 | 477 | modifier onlyWizardController(uint256 wizardId) { 478 | require(WIZARD_GUILD.isApprovedOrOwner(msg.sender, wizardId), "Must be Wizard controller"); 479 | _; 480 | } 481 | 482 | /// @dev The presale contract will be sending Ether directly to this contract, 483 | /// so we need it to have a payable fallback. 484 | function() external payable { 485 | require(msg.sender == address(WIZARD_PRESALE) || msg.sender == address(tournament), "Don't send funds to GateKeeper"); 486 | } 487 | 488 | /// @notice Registers the address of the Tournament with the GateKeeper. This can't be passed 489 | /// into the constructor, because the Tournament itself needs a reference to the GateKeeper 490 | /// in its constructor! 491 | /// 492 | /// NOTE: Technically, most of the functions below should have some kind of modifier 493 | /// that ensures that the Tournament is set before they are called, but that 494 | /// just adds gas. In practice, multiple draft Gatekeeper contracts may be 495 | /// deployed, so what matters is that we call this function before sharing 496 | /// the address of this contract publicly. No need to enforce that on chain, 497 | /// with its associated gas costs! 498 | function registerTournament(address setTournament) external onlyCOO { 499 | require(address(tournament) == address(0), "Tournament already registered"); 500 | tournament = TournamentInterface(setTournament); 501 | require( 502 | (setTournament != address(0)) && 503 | tournament.supportsInterface(_INTERFACE_ID_TOURNAMENT), "Invalid Tournament"); 504 | 505 | tournamentPowerScale = tournament.powerScale(); 506 | require(tournamentPowerScale <= MAX_POWER_SCALE, "Power scale too high"); 507 | } 508 | 509 | /// @notice This is it folks, the main event! The way for the world to get new Wizards! Does 510 | /// pretty much what it says on the box; let's you conjure a new Wizard with a specified 511 | /// elemental affinity. The call must include enough Ether to cover the cost of the new 512 | /// Wizard, and any excess is refunded. The power of the Wizard is derived from 513 | /// the cost. YOU CAN NOT PAY EXTRA TO RAISE INITIAL POWER LATER. Returns the ID 514 | /// of the newly conjured Wizard. 515 | /// 516 | /// While you cannot increase power later, you can conjure some more Wizards! 517 | /// @param affinity The elemental affinity you want for the Wizard. Valid elements are 518 | /// defined in `WizardConstants`, see the constants with names starting `ELEMENT_`. 519 | /// ELEMENT_NOTSET is not valid for regular Wizards (unlike Exclusive Wizards). 520 | function conjureWizard(uint8 affinity) external payable returns (uint256) { 521 | uint8[] memory affinities = new uint8[](1); 522 | 523 | affinities[0] = affinity; 524 | 525 | uint256[] memory wizardIds = conjureWizardMulti(affinities); 526 | 527 | return wizardIds[0]; 528 | } 529 | 530 | /// @notice A convenience function that allows you to conjure a whole bunch of Wizards at once! You know how 531 | /// there's "a pride of lions", "a murder of crows", and "a parliament of owls"? Well, with this 532 | /// here function you can conjure yourself "a stench of Cheeze Wizards"! 533 | /// @dev This function is careful to bundle all of the external calls (_transferRefund() and onERC721Received()) 534 | /// at the end of the function to limit the risk of reentrancy attacks. 535 | /// @param affinities The elemental affinities of the Wizards, can mix and match any valid types. 536 | /// Valid elements are defined in `WizardConstants`, see the constants with names starting 537 | /// `ELEMENT_`. ELEMENT_NOTSET is not valid for regular Wizards (unlike Exclusive Wizards). 538 | function conjureWizardMulti(uint8[] memory affinities) public payable 539 | returns (uint256[] memory wizardIds) 540 | { 541 | (uint256 totalCost, uint256 contribution, uint88[] memory powers) = _computeWizardPowers(affinities); 542 | 543 | require(msg.value >= totalCost, "Insufficient funds"); 544 | 545 | // Mint the requested Wizards in the guild contract, assigning ownership to the sender 546 | wizardIds = WIZARD_GUILD.mintWizards(powers, affinities, msg.sender); 547 | 548 | // Enter the new Wizards into the Tournament 549 | tournament.enterWizards.value(contribution)(wizardIds, powers); 550 | 551 | // Ensure the Wizards are being assigned to an ERC-721 aware address (either an external address, 552 | // or a smart contract that implements onERC721Received()). We must call onERC721Received() for 553 | // each token minted because it's allowed for an ERC-721 receiving contract to reject the 554 | // transfer based on the properties of the token. 555 | if (isContract(msg.sender)) { 556 | for (uint256 i = 0; i < wizardIds.length; i++) { 557 | bytes4 transferAccepted = IERC721Receiver(msg.sender).onERC721Received(msg.sender, address(0), wizardIds[i], ""); 558 | require(transferAccepted == _ERC721_RECEIVED, "Contract owner didn't accept ERC721 transfer"); 559 | } 560 | } 561 | 562 | // NOTE: _transferRefund() is only safe if msg.value >= totalCost. See the require() near the 563 | // beginning of the function to feel better about this fact. 564 | _transferRefund(totalCost); 565 | } 566 | 567 | /// @notice Allows for the creation of Exclusive Wizards. This can only be done by the COO, who still has 568 | /// to pay for the power imbued in these Wizards! Reverts if the owner address is a smart contract 569 | /// that is not ERC-721 aware. 570 | /// @param wizardIds An array of IDs of the Wizards being conjured. 571 | /// @param powers The power levels of the Wizards, corresponding 1:1 to Wizard IDs. 572 | /// @param affinities The elemental affinities of the Wizards, corresponding 1:1 to Wizard IDs. 573 | /// Valid elements are defined in `WizardConstants`, see the constants with 574 | /// names starting `ELEMENT_`. ELEMENT_NOTSET is valid for Exclusive Wizards, 575 | /// unlike regular Wizards. 576 | /// @param owner The recipient address of the newly conjured Cheeze Wizards. 577 | function conjureExclusiveMulti( 578 | uint256[] calldata wizardIds, 579 | uint256[] calldata powers, 580 | uint8[] calldata affinities, 581 | address owner 582 | ) 583 | external payable onlyCOO 584 | { 585 | // Ensure the arrays are all of the same length 586 | require(wizardIds.length == powers.length && powers.length == affinities.length, "Inconsistent parameter lengths"); 587 | 588 | uint256 totalCost = 0; 589 | uint256 contribution = 0; 590 | uint88[] memory localPowers = new uint88[](powers.length); 591 | 592 | for (uint256 i = 0; i < powers.length; i++) { 593 | require(affinities[i] <= MAX_ELEMENT, "Invalid affinity"); 594 | 595 | require(powers[i] < (1 << 88), "Invalid power level"); 596 | localPowers[i] = uint88(powers[i]); 597 | uint256 wizardCost = powerToCost(localPowers[i]); 598 | 599 | totalCost += wizardCost; 600 | contribution += _potContribution(localPowers[i]); 601 | } 602 | 603 | require(msg.value >= totalCost, "Insufficient funds"); 604 | 605 | // Mint the requested Wizards via the guild contract 606 | WIZARD_GUILD.mintReservedWizards(wizardIds, localPowers, affinities, owner); 607 | 608 | // Enter the new Wizards into the Tournament 609 | tournament.enterWizards.value(contribution)(wizardIds, localPowers); 610 | 611 | // Ensure the Wizards are being assigned to an ERC-721 aware address (either an external address, 612 | // or a smart contract that implements onERC721Received()). We must call onERC721Received for 613 | // each token minted because it's allowed for an ERC-721 receiving contract to reject the 614 | // transfer based on the properties of the token. 615 | if (isContract(owner)) { 616 | for (uint256 i = 0; i < wizardIds.length; i++) { 617 | bytes4 transferAccepted = IERC721Receiver(owner).onERC721Received(msg.sender, address(0), wizardIds[i], ""); 618 | require(transferAccepted == _ERC721_RECEIVED, "Contract owner didn't accept ERC721 transfer"); 619 | } 620 | } 621 | 622 | // NOTE: _transferRefund() is only safe if msg.value >= totalCost. See the require() near the 623 | // middle of the function to feel better about this fact. 624 | _transferRefund(totalCost); 625 | } 626 | 627 | /// @notice Computes the powers, total cost, and prize pot contribution for an array of new Wizards 628 | /// based on the provided affinities. This also checks that the affinity values are valid 629 | /// for the Tournament, and updates the elementalWizardCost storage variable as relevant. 630 | function _computeWizardPowers(uint8[] memory affinities) internal 631 | returns(uint256 totalCost, uint256 contribution, uint88[] memory powers) 632 | { 633 | // Cache the Wizard costs in order to reduce the gas costs from reads and writes to storage. 634 | uint256 neutralWizardCost = wizardCosts.neutralWizardCost; 635 | uint256 elementalWizardCost = wizardCosts.elementalWizardCost; 636 | uint256 elementalWizardIncrement = wizardCosts.elementalWizardIncrement; 637 | 638 | totalCost = 0; 639 | contribution = 0; 640 | powers = new uint88[](affinities.length); 641 | 642 | for (uint256 i = 0; i < affinities.length; i++) { 643 | uint8 affinity = affinities[i]; 644 | uint256 wizardCost; 645 | 646 | require(affinity > ELEMENT_NOTSET && affinity <= MAX_ELEMENT, "Invalid affinity"); 647 | 648 | // Determine the price of the Wizard 649 | if (affinity == ELEMENT_NEUTRAL) { 650 | wizardCost = neutralWizardCost; 651 | } else { 652 | wizardCost = elementalWizardCost; 653 | 654 | // Update the cost of the next Elemental Wizard 655 | // NOTE: This math can't overflow because the total Ether supply in wei is well less than 656 | // 2^128. Multiplying a valid cost in wei by some number <100k 657 | // cannot possibly overflow 256 bits. As cost is calculated 658 | // ourselves (rather than user provided) we know it must 659 | // be in the valid range. 660 | elementalWizardCost += (elementalWizardCost * elementalWizardIncrement) / TENTH_BASIS_POINTS; 661 | } 662 | 663 | powers[i] = costToPower(wizardCost); 664 | 665 | // IMPORTANT! Mathematically, you'd think we could just compute the pot contribution at the 666 | // end of the loop, but this risks rounding differences between 667 | // conjuring Wizards individually compared to conjuring them as a stench. 668 | contribution += _potContribution(powers[i]); 669 | totalCost += wizardCost; 670 | } 671 | 672 | // Update the cached elemental cost to storage. Conveniently this costs very little if 673 | // the value isn't actually changed. 674 | wizardCosts.elementalWizardCost = uint96(elementalWizardCost); 675 | } 676 | 677 | /// @notice Absorbs a number of presale Wizards into the final NFT contract, while also entering them into 678 | /// the Tournament. Can handle any number of Wizards in a batch BUT THEY MUST ALL HAVE THE SAME OWNER. 679 | /// Callable by anyone. 680 | /// @param wizardIds The IDs of the presale Wizards; note that all Wizards MUST have the same owner. 681 | function absorbPresaleWizards(uint256[] calldata wizardIds) external { 682 | // Bulk fetch the Wizards from the presale contract. Note that this will also delete those Wizards from the 683 | // presale contract, and will also transfer the funds used to purchase those Wizards to this contract. 684 | // Obviously, a failed require() statement later in this function will undo that transfer and those deletes. 685 | ( 686 | address[] memory owners, 687 | uint256[] memory powers, 688 | uint8[] memory affinities 689 | ) = WIZARD_PRESALE.absorbWizardMulti(wizardIds); 690 | 691 | uint256 contribution = 0; 692 | address theOwner = owners[0]; 693 | uint88[] memory localPowers = new uint88[](powers.length); 694 | 695 | for (uint256 i = 0; i < powers.length; i++) { 696 | require(owners[i] == theOwner, "All Wizards must have same owner"); 697 | localPowers[i] = uint88(powers[i]); 698 | contribution += _potContribution(localPowers[i]); 699 | } 700 | 701 | // Mint the requested Wizards in the guild contract 702 | WIZARD_GUILD.mintReservedWizards(wizardIds, localPowers, affinities, theOwner); 703 | 704 | // Enter the new Wizards into the Tournament 705 | tournament.enterWizards.value(contribution)(wizardIds, localPowers); 706 | } 707 | 708 | /// @notice Revive a tired Wizard so they can duel again. The caller must own 709 | /// the Wizard. 710 | /// @param wizardId The ID of the Wizard to revive. 711 | function revive(uint256 wizardId) external payable onlyWizardController(wizardId) { 712 | // We don't need to do any validation here, we can let the Tournament decide 713 | // if the pot contribution amount derived from msg.value represents a valid 714 | // power level to use for reviving this Wizard. 715 | uint88 purchasedPower = costToPower(msg.value); 716 | uint256 potContributionValue = _potContribution(purchasedPower); 717 | 718 | tournament.revive.value(potContributionValue)(wizardId); 719 | } 720 | 721 | /// @notice Sets the affinity for a Wizard that doesn't already have its elemental affinity chosen. 722 | /// As a rule this is only ever expected to apply to Exclusive Wizards, as regular wizards 723 | /// have their affinity set when they are conjured. 724 | function setAffinity(uint256 wizardId, uint8 newAffinity) external onlyWizardController(wizardId) { 725 | require(newAffinity > ELEMENT_NOTSET && newAffinity <= MAX_ELEMENT, "Must choose a valid affinity"); 726 | 727 | // The guild will enforce the Wizard doesn't already have an affinity set. 728 | WIZARD_GUILD.setAffinity(wizardId, newAffinity); 729 | } 730 | 731 | /// @notice Determine the power of a Wizard based on their price. 732 | /// @param cost The price of the Wizard in wei. 733 | /// @return The power of the Wizard (cast to uint88). 734 | function costToPower(uint256 cost) public pure returns (uint88 power) { 735 | return uint88(cost / MAX_POWER_SCALE); 736 | } 737 | 738 | /// @param power The power of the Wizard. 739 | /// @return The cost of the Wizard in wei. 740 | function powerToCost(uint88 power) public pure returns (uint256 cost) { 741 | return power * MAX_POWER_SCALE; 742 | } 743 | 744 | /// @notice Computes the number of wei required to be sent to the Tournament 745 | /// in order to match a given power level. 746 | /// @param wizardPower The power level 747 | /// @return The prize pot contribution necessary to match the given power 748 | function _potContribution(uint88 wizardPower) internal view returns (uint256) { 749 | return wizardPower * tournamentPowerScale; 750 | } 751 | 752 | /// @notice Allows the CFO to withdraw funds from this contract. 753 | function withdraw() external onlyCFO { 754 | // All of the pot contributions go directly into the Tournament contract, so 755 | // we can safely withdraw everything, with no hold-backs. 756 | msg.sender.transfer(address(this).balance); 757 | } 758 | 759 | 760 | /// @notice Allows the COO to destroy this contract if it's not needed anymore. 761 | /// @notice Can't be destoryed if the Tournament still exists. 762 | function destroy() external onlyCOO { 763 | require(address(this).balance == 0, "Drain the funds first"); 764 | require(address(tournament) == address(0), "Destroy Tournament first"); 765 | 766 | selfdestruct(msg.sender); 767 | } 768 | 769 | /// @notice Allow the COO to destroy the Tournament contract. 770 | function destroyTournament() external onlyCOO { 771 | if (address(tournament) != address(0)) { 772 | require(tournament.isActive() == false, "Tournament active"); 773 | tournament.destroy(); 774 | tournament = TournamentInterface(0); 775 | } 776 | } 777 | 778 | /// @notice Utility function that refunds any overpayment to the sender; smart 779 | /// enough to only send the excess if the amount we are returning is more than the 780 | /// cost of sending it! 781 | /// @dev Warning! This does not check for underflows (msg.value < actualPrice) - so 782 | /// be sure to call this with correct values! 783 | /// @param actualPrice The actual price owed for the conjured Wizards. 784 | function _transferRefund(uint256 actualPrice) private { 785 | uint256 refund = msg.value - actualPrice; 786 | 787 | // Make sure the amount we're trying to refund is less than the actual cost of sending it! 788 | // See https://github.com/ethereum/wiki/wiki/Subtleties for magic values costs. We can 789 | // safely ignore the 25000 additional gas cost for new accounts, as msg.sender is 790 | // guaranteed to exist at this point! 791 | if (refund > (tx.gasprice * (9000+700))) { 792 | msg.sender.transfer(refund); 793 | } 794 | } 795 | } 796 | -------------------------------------------------------------------------------- /contracts/ThreeAffinityDuelResolver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.6 <0.6.0; 2 | 3 | 4 | 5 | 6 | 7 | 8 | /// @title ERC165Interface 9 | /// @dev https://eips.ethereum.org/EIPS/eip-165 10 | interface ERC165Interface { 11 | /// @notice Query if a contract implements an interface 12 | /// @param interfaceId The interface identifier, as specified in ERC-165 13 | /// @dev Interface identification is specified in ERC-165. This function 14 | /// uses less than 30,000 gas. 15 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 16 | } 17 | 18 | 19 | // We use a contract and multiple inheritance to expose this constant. 20 | // It's the best that Solidity offers at the moment. 21 | contract DuelResolverInterfaceId { 22 | /// @notice The erc165 interface ID 23 | bytes4 internal constant _INTERFACE_ID_DUELRESOLVER = 0x41fc4f1e; 24 | } 25 | 26 | /// @notice An interface for contracts that resolve duels between Cheeze Wizards. Abstracting this out 27 | /// into its own interface and instance allows for different tournaments to use 28 | /// different duel mechanics while keeping the core tournament logic unchanged. 29 | contract DuelResolverInterface is DuelResolverInterfaceId, ERC165Interface { 30 | /// @notice Indicates if the given move set is a valid input for this duel resolver. 31 | /// It's important that this method is called before a move set is committed to 32 | /// because resolveDuel() will abort if it the moves are invalid, making it 33 | /// impossible to resolve the duel. 34 | function isValidMoveSet(bytes32 moveSet) public pure returns(bool); 35 | 36 | /// @notice Indicates that a particular affinity is a valid input for this duel resolver. 37 | /// Should be called before a Wizard is entered into a tournament. As a rule, Wizard 38 | /// Affinities don't change, so there's not point in checking for each duel. 39 | /// 40 | /// @dev This method should _only_ return false for affinities that are 41 | /// known to cause problems with your duel resolver. If your resolveDuel() function 42 | /// can safely work with any affinity value (even if it just ignores the values that 43 | /// it doesn't know about), it should return true. 44 | function isValidAffinity(uint256 affinity) external pure returns(bool); 45 | 46 | /// @notice Resolves the duel between two Cheeze Wizards given their chosen move sets, their 47 | /// powers, and each Wizard's affinity. It is the responsibility of the Tournament contract 48 | /// to ensure that ALL Wizards in a Tournament have an affinity value that is compatible with 49 | /// the logic of this DuelResolver. It must also ensure that both move sets are valid before 50 | /// those move sets are locked in, otherwise the duel can never be resolved! 51 | /// 52 | /// Returns the amount of power to be transferred from the first Wizard to the second Wizard 53 | /// (which will be a negative number if the second Wizard wins the duel), zero in the case of 54 | /// a tie. 55 | /// @param moveSet1 The move set for the first Wizard. The interpretation and therefore valid 56 | /// values for this are determined by the individual duel resolver. 57 | /// @param moveSet2 The move set for the second Wizard. 58 | function resolveDuel( 59 | bytes32 moveSet1, 60 | bytes32 moveSet2, 61 | uint256 power1, 62 | uint256 power2, 63 | uint256 affinity1, 64 | uint256 affinity2) 65 | public pure returns(int256); 66 | } 67 | 68 | 69 | 70 | contract ThreeAffinityDuelResolver is DuelResolverInterface { 71 | /// @dev A bitmask that filters all but the bits that are significant as part of a valid move set for this 72 | /// duel resolver. 73 | bytes32 public constant MOVE_MASK = 0x0303030303000000000000000000000000000000000000000000000000000000; 74 | 75 | /// @dev The moves come in from the front end as 2, 3, and 4; the logic below is simpler if the valid 76 | /// moves are 0, 1, 2. Thus, we subtract 2 from each value to put things in the range that works well for us. 77 | /// See WizardConstants for the element values, to understand where 2, 3 and 4 come from. 78 | uint256 public constant MOVE_DELTA = 0x0202020202000000000000000000000000000000000000000000000000000000; 79 | 80 | /// @dev The relative weight applied to each round in the duel. We ramp up the weight as the duel progresses 81 | /// to make the later rounds more impactful, and to minimize the probability of a dead tie (which is super 82 | /// boring!) 83 | uint256 public constant WEIGHT_SUM = 78 + 79 + 81 + 86 + 100; 84 | 85 | /// @notice Query if a contract implements an interface 86 | /// @param interfaceId The interface identifier, as specified in ERC-165 87 | /// @dev Interface identification is specified in ERC-165. This function 88 | /// uses less than 30,000 gas. 89 | function supportsInterface(bytes4 interfaceId) external view returns (bool) { 90 | return 91 | interfaceId == this.supportsInterface.selector || // ERC165 92 | interfaceId == _INTERFACE_ID_DUELRESOLVER; // DuelResolverInterface 93 | } 94 | 95 | /// @notice Checks to see that the given move set is valid for the RPS game. Valid format is 5 set bytes followed by 96 | /// 27 zero bytes. Each set byte can be 2, 3, or 4. All other values are rejected as invalid. 97 | /// @param moveSet the moveset to be validated 98 | function isValidMoveSet(bytes32 moveSet) public pure returns(bool) { // solium-disable-line security/no-assign-params 99 | // Map the input values 2, 3, 4 onto 0, 1, 2. 100 | moveSet = bytes32(uint256(moveSet) - MOVE_DELTA); 101 | 102 | // Fails if any bit is set outside the allowed mask 103 | if (moveSet != (moveSet & MOVE_MASK)) { 104 | return false; 105 | } 106 | 107 | // The previous line ensures that all values are 0, 1, 2, or 3, but 108 | // 3 isn’t actually valid. The following check ensures that no two 109 | // adjacent bits are set, which excludes any threes. 110 | if ((moveSet & (moveSet << 1)) != bytes32(0)) { 111 | return false; 112 | } 113 | 114 | return true; 115 | } 116 | 117 | /// @notice Any affinity value is acceptable with this resolver, we just safely ignore any affinities 118 | /// that don't match Fire, Air, or Water. 119 | function isValidAffinity(uint256) public pure returns(bool) { 120 | return true; 121 | } 122 | 123 | function resolveDuel( 124 | bytes32 moveSet1, 125 | bytes32 moveSet2, 126 | uint256 power1, 127 | uint256 power2, 128 | uint256 affinity1, 129 | uint256 affinity2) 130 | public pure returns(int256 power) 131 | { 132 | require(isValidMoveSet(moveSet1) && isValidMoveSet(moveSet2), "Invalid moveset"); 133 | 134 | int256 score = _duelScore(moveSet1, moveSet2, affinity1, affinity2); 135 | power = _powerTransfer(score, power1, power2); 136 | } 137 | 138 | /// @notice Calculate the score for a duel. A positive score means the first wizard 139 | /// won the match, a negative score means the second wizard won, and zero 140 | /// means they tied (boooooring). 141 | /// @dev A NOTE ON VARIABLE NAMING: Throughout this file, you will find variables that end with 142 | /// Qn, where n is 6 or 10. This is used to indicate that these are fixed point fractional 143 | /// values, with a denominator of 64 (2^6) or 1024 (2^10). Wikipedia has a decent description 144 | /// of fixed-point arithmetic if you are unfamiliar (https://en.wikipedia.org/wiki/Fixed-point_arithmetic) 145 | // solium-disable-next-line security/no-assign-params 146 | function _duelScore(bytes32 moveSet1, bytes32 moveSet2, uint256 affinity1, uint256 affinity2) internal pure returns (int256 score) { 147 | // Although these values are essentially constants, we have to keep them 148 | // inside the function for it to be pure. 149 | int256[5] memory weights = [int256(78), int256(79), int256(81), int256(86), int256(100)]; 150 | 151 | // Map the input values 2, 3, 4 onto 0, 1, 2 to make the calculations easier. 152 | moveSet1 = bytes32(uint256(moveSet1) - MOVE_DELTA); 153 | moveSet2 = bytes32(uint256(moveSet2) - MOVE_DELTA); 154 | affinity1 -= 2; 155 | affinity2 -= 2; 156 | 157 | for (uint256 i = 0; i < 5; i++) { 158 | // So this bit of casting is a bit weird. Subscripting a bytes32 value gives you a byte object, 159 | // which seems like something that could be cast into an integer. But the Solidity docs point out 160 | // that "byte" is actually just an alias for bytes1, which is more like an array type. What Solidity 161 | // does allow is a cast between a bytesX object and an integer of the same size, so we have to cast 162 | // to an 8-bit integer before we are allowed to cast to a full word. ¯\_(ツ)_/¯ 163 | int256 move1 = int256(uint8(moveSet1[i])); 164 | int256 move2 = int256(uint8(moveSet2[i])); 165 | int256 diff = move1 - move2; 166 | 167 | if (diff == 0) { 168 | continue; 169 | } 170 | 171 | // Results in 1 if p1 beats p2, -1 if p2 beats p1 172 | // 1 (water) beats 0 (fire) = (1) | 0 (fire) loses to 1 (water) = (-1) 173 | // 2 (wind) beats 1 (water) = (1) | 1 (water) loses to 2 (wind) = (-1) 174 | // 0 (fire) beats 2 (wind) = (-2) | 2 (wind) loses to 0 (fire) = (2) 175 | if (diff*diff == 4) { 176 | diff = -(diff >> 1); 177 | } 178 | 179 | // Switch to a fixed-point math with a numerator of 100 180 | diff *= 100; 181 | 182 | // The result is 30% larger if the a wizard is playing their affinity 183 | // This effect can compound! 184 | if (move1 == int256(affinity1)) { 185 | diff = diff * 130 / 100; 186 | } 187 | 188 | if (move2 == int256(affinity2)) { 189 | diff = diff * 130 / 100; 190 | } 191 | 192 | score += diff * weights[i]; 193 | } 194 | 195 | score /= 100; 196 | } 197 | 198 | /// @dev Returns the amount of power to be transferred from the first wizard to the second wizard. The value 199 | /// is negative if power is actually transferred from second to first. 200 | /// @param score The result of the RPS minigame, a positive score means the first wizard 201 | /// won the match, a negative score means the second wizard won. 202 | // solium-disable-next-line security/no-assign-params 203 | function _powerTransfer(int256 score, uint256 power1, uint256 power2) private pure returns(int256) { 204 | // if the score is negative, switch the positions of the wizards so that the rest of the 205 | // logic in this function only has to handle the case where the first wizard is the winner, 206 | // and the power transfer is positive. We do this before verifying the inputs so they 207 | // are not verified twice. 208 | if (score < 0) { 209 | return -_powerTransfer(-score, power2, power1); 210 | } 211 | 212 | // Cap the power of each player so the arithmetic cannot possibly overflow. 213 | require((power1 < (1<<245)) && power2 < (1<<245), "Invalid power value"); 214 | 215 | // Wizard power values must be strictly positive or something has gone very wrong. 216 | require(power1 > 0 && power2 > 0, "Invalid power value"); 217 | 218 | if (score == 0) { 219 | // Handle the simple case of a tie first 220 | return 0; 221 | } 222 | 223 | // From here on out in the function, we can assume that the first wizard is the winner, and the 224 | // power transfer will be > 0. 225 | 226 | // Convert the score into a number 0-1 227 | uint256 normalizedScoreQ10 = 1024 * uint256(score) / WEIGHT_SUM; 228 | 229 | // Because elemental wizards get a bonus (or penalty) when using their affinity spells, there is 230 | // a chance that the total score for an Elemental Wizard will exceed 100%. We cap it at 100%... 231 | if (normalizedScoreQ10 > 1024) { 232 | normalizedScoreQ10 = 1024; 233 | } 234 | 235 | // The base transfer ratio is the square root of the normalized score. We use our fakePow 236 | // function to get an approximation of the square root. x^(1/2) == sqrt(x) 237 | uint256 baseTransferRatioQ10 = _fakePowQ10(normalizedScoreQ10, 1024 * 1/2); 238 | 239 | // The base transfer ratio (BTR) is now, more or less, the inverse of the linearized probability 240 | // of the outcome. By "inverse probability", we mean the BTR will be small if the outcome was 241 | // quite likely (winning by a little), and large if the outcome was unlikely (winning by a lot). 242 | // By "linearized" we mean that if the outcome was twice as unlikely, the BTR will be doubled. 243 | // (Please don't ignore the phrase "more or less" here! The true probability distribution depends 244 | // on the wizard alignments and is close to a gaussian density curve. Approximating this curve 245 | // using a parabola makes it feasible to compute on-chain, and has an error term we can live with...) 246 | // 247 | // However, this BTR computed above is only appropriate in a "fair fight"; that is, when the 248 | // wizards have equal power. When one wizard has more power than the other, we have to do something 249 | // a bit different! 250 | // 251 | // The goal here is to keep the fight fair (in that neither the stronger nor weaker wizard has 252 | // a systemic advantage), but to have as much power as possible to be at stake for both wizards. This 253 | // seems like a paradox! If we're playing a symmetric game akin to RPS, and each player has the same 254 | // probability distribution for the outcomes, how can keep the match fair while having one player have 255 | // more value at risk? 256 | // 257 | // The answer is to "bend" the odds so that small wins (which are much more common) favour the stronger 258 | // wizard, since large wins will favour the weaker wizard. The math below is more magic than science (appropriate 259 | // for wizards, I suppose!), but has the impact that -- even though the stronger wizards has "more to 260 | // lose" -- the fight is still balanced. Most of the time, the winning margin will be small, and stronger 261 | // wizard will get a bigger reward than the weaker wizard would get from winning with the same margin. 262 | // BUT, if they win by a large enough margin, the weak wizard can drain a wizard even 7 times more powerful! 263 | // 264 | // We do cap the "at risk" power of the stronger wizard to 7x the weaker wizard, otherwise the 265 | // probability curves get WAAAAY out of whack (basically, the battle quickly devolves into an "all or 266 | // nothing" exchange with next to zero odds of winning anything for the weaker wizard). 267 | 268 | if (power2 > power1 * 7) { 269 | power2 = power1 * 7; 270 | } else if (power1 > power2 * 7) { 271 | power1 = power2 * 7; 272 | } 273 | 274 | // Use our power function to bend the curve to something very close to a zero EV for each player. 275 | uint256 transferRatioQ10 = _fakePowQ10(baseTransferRatioQ10, 1024 * power2 / power1); 276 | 277 | // Return the actual power transferred, which is determined as a fraction of the loser's power 278 | // in the preceding math. 279 | return int256((power2 * transferRatioQ10) >> 10); 280 | } 281 | 282 | /// @dev A function that approximates x^y, where x and y are fixed point values with a denominator 283 | /// of 1024 (10 bits). x must be in the range 0-1 (0-1024 as an int) and y can be any 284 | /// value > 1/128 (>8 as an int). For efficiency, y values are rounded to the nearest 1/64th 285 | /// (which seemed in our testing to be the sweet spot between accuracy and efficiency). 286 | /// 287 | /// This function will produce wholly accurate results for all integer powers (i.e. 288 | /// when y is a multiple of 1024 when viewed as an int), and will produce reasonable results 289 | /// when y has a fractional component. The error term on this approximation isn't tiny, but 290 | /// over the range 0-1 the errors tend to cancel out, which means the integral of the approximation 291 | /// is VERY close to the integral of the real power function. This is critical, because we use 292 | /// this approximation for determining the power transfer after a duel. This approximation can 293 | /// result in errors, but systemically - across a large number of fights - those errors will 294 | /// cancel out and result in a system very close to the expected equilibrium. 295 | /// 296 | /// tl;dr We fake the power function in a way that means that any error the approximation introduces 297 | /// is just as likely to help you as hurt you, while saving a butt-ton of gas. 298 | function _fakePowQ10(uint256 xQ10, uint256 yQ10) private pure returns(uint256) { 299 | 300 | // Round the y value to the nearest 1/64th, while also converting from 301 | // a denominator of 1024 to a denominator of 64. 302 | // Apologies for how unclear this is, but it makes for tighter code! 303 | // A more explicit version would look like this: 304 | // float y = float(xQ10) / 1024.0; 305 | // y = floor(y + 0.5); 306 | // int xQ6 = int(y * 64); 307 | uint256 yQ6 = (yQ10 + 8) >> 4; 308 | 309 | // Call the recursive internal function to do that actual math 310 | return _fakePowInternal(xQ10, yQ6, 64, 5); 311 | } 312 | 313 | 314 | /// @dev An internal helper function for the fakePow function. X must be a value between 0-1 315 | /// expressed as a fixed point value with a denominator of 1024. numerator and denominator 316 | /// can be any integers. Returns the value x^(n/d). 317 | function _fakePowInternal(uint256 xQ10, uint256 numerator, uint256 denominator, uint256 iterations) private pure returns (uint256) { 318 | // Initialize the result to 1 (1024 is 1 in Q10) 319 | uint256 resultQ10 = 1024; 320 | 321 | // Grab the integer part of the exponent, which we can compute exactly 322 | uint256 integerExponent = numerator / denominator; 323 | 324 | // We have to be careful with overflow here. We know that x fits in 11 bits, because 325 | // we require x to be in the range 0-1. We also know that if x fits in n bits, then 326 | // x^y fits in n*y bits. So, any power <23 will safely fit into 256-bits. Any power 327 | // >= 23 might not! We can loop over powers >22 (which will be very rare!) and safely 328 | // compute them in chunks without overflow. 329 | while (integerExponent >= 22) { 330 | resultQ10 *= xQ10 ** 22; 331 | resultQ10 >>= 220; // back out the 22 extra multiples of 1024 332 | integerExponent -= 22; 333 | } 334 | 335 | // Handle any remaining integer part of the power function without the possibility 336 | // of overflow 337 | if (integerExponent > 0) { 338 | resultQ10 *= xQ10 ** integerExponent; 339 | resultQ10 >>= (integerExponent * 10); 340 | } 341 | 342 | uint256 fractionalExponent = numerator % denominator; 343 | 344 | // If we've run out of iterations, or there is no fractional part, we can 345 | // just return the value we've computed to this point. 346 | if ((iterations == 0) || (fractionalExponent == 0)) { 347 | return resultQ10; 348 | } 349 | 350 | // This is the magic, and -- I'll be honest -- I don't have much other than some 351 | // empirical testing to defend it. If we have a fractional power n/d, where n < d 352 | // we recursively call this function "flipping" everything. We flip the base value 353 | // x, by using 1-x. And we flip the fractional exponent and use d/n which will 354 | // result in a power > 1. And then we flip the result again by using 1-result. 355 | // 356 | // The result is a value that is _quite close_ to x^(n/d), but is much cheaper to 357 | // to compute on a VM that only has an integer unit. 358 | resultQ10 *= (1024 - _fakePowInternal(1024 - xQ10, denominator, fractionalExponent, iterations - 1)); 359 | 360 | return resultQ10 >> 10; 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /contracts/WizardGuild.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.5.6 <0.6.0; 2 | 3 | /// @title Shared constants used throughout the Cheeze Wizards contracts 4 | contract WizardConstants { 5 | // Wizards normally have their affinity set when they are first created, 6 | // but for example Exclusive Wizards can be created with no set affinity. 7 | // In this case the affinity can be set by the owner. 8 | uint8 internal constant ELEMENT_NOTSET = 0; //000 9 | // A neutral Wizard has no particular strength or weakness with specific 10 | // elements. 11 | uint8 internal constant ELEMENT_NEUTRAL = 1; //001 12 | // The fire, water and wind elements are used both to reflect an affinity 13 | // of Elemental Wizards for a specific element, and as the moves a 14 | // Wizard can make during a duel. 15 | // Note that if these values change then `moveMask` and `moveDelta` in 16 | // ThreeAffinityDuelResolver would need to be updated accordingly. 17 | uint8 internal constant ELEMENT_FIRE = 2; //010 18 | uint8 internal constant ELEMENT_WATER = 3; //011 19 | uint8 internal constant ELEMENT_WIND = 4; //100 20 | uint8 internal constant MAX_ELEMENT = ELEMENT_WIND; 21 | } 22 | 23 | 24 | 25 | /// @title ERC165Query example 26 | /// @notice see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md 27 | contract ERC165Query { 28 | bytes4 constant _INTERFACE_ID_INVALID = 0xffffffff; 29 | bytes4 constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; 30 | 31 | function doesContractImplementInterface( 32 | address _contract, 33 | bytes4 _interfaceId 34 | ) 35 | internal 36 | view 37 | returns (bool) 38 | { 39 | uint256 success; 40 | uint256 result; 41 | 42 | (success, result) = noThrowCall(_contract, _INTERFACE_ID_ERC165); 43 | if ((success == 0) || (result == 0)) { 44 | return false; 45 | } 46 | 47 | (success, result) = noThrowCall(_contract, _INTERFACE_ID_INVALID); 48 | if ((success == 0) || (result != 0)) { 49 | return false; 50 | } 51 | 52 | (success, result) = noThrowCall(_contract, _interfaceId); 53 | if ((success == 1) && (result == 1)) { 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | function noThrowCall( 60 | address _contract, 61 | bytes4 _interfaceId 62 | ) 63 | internal 64 | view 65 | returns ( 66 | uint256 success, 67 | uint256 result 68 | ) 69 | { 70 | bytes memory encodedParams = abi.encodeWithSelector(_INTERFACE_ID_ERC165, _interfaceId); 71 | 72 | // solhint-disable-next-line no-inline-assembly 73 | assembly { // solium-disable-line security/no-inline-assembly 74 | let encodedParams_data := add(0x20, encodedParams) 75 | let encodedParams_size := mload(encodedParams) 76 | 77 | let output := mload(0x40) // Find empty storage location using "free memory pointer" 78 | mstore(output, 0x0) 79 | 80 | success := staticcall( 81 | 30000, // 30k gas 82 | _contract, // To addr 83 | encodedParams_data, 84 | encodedParams_size, 85 | output, 86 | 0x20 // Outputs are 32 bytes long 87 | ) 88 | 89 | result := mload(output) // Load the result 90 | } 91 | } 92 | } 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | /** 102 | * @title IERC165 103 | * @dev https://eips.ethereum.org/EIPS/eip-165 104 | */ 105 | interface IERC165 { 106 | /** 107 | * @notice Query if a contract implements an interface 108 | * @param interfaceId The interface identifier, as specified in ERC-165 109 | * @dev Interface identification is specified in ERC-165. This function 110 | * uses less than 30,000 gas. 111 | */ 112 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 113 | } 114 | 115 | 116 | /** 117 | * @title ERC721 Non-Fungible Token Standard basic interface 118 | * @dev see https://eips.ethereum.org/EIPS/eip-721 119 | */ 120 | contract IERC721 is IERC165 { 121 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 122 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 123 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 124 | 125 | function balanceOf(address owner) public view returns (uint256 balance); 126 | function ownerOf(uint256 tokenId) public view returns (address owner); 127 | 128 | function approve(address to, uint256 tokenId) public; 129 | function getApproved(uint256 tokenId) public view returns (address operator); 130 | 131 | function setApprovalForAll(address operator, bool _approved) public; 132 | function isApprovedForAll(address owner, address operator) public view returns (bool); 133 | 134 | function transferFrom(address from, address to, uint256 tokenId) public; 135 | function safeTransferFrom(address from, address to, uint256 tokenId) public; 136 | 137 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public; 138 | } 139 | 140 | 141 | 142 | /** 143 | * @title ERC721 token receiver interface 144 | * @dev Interface for any contract that wants to support safeTransfers 145 | * from ERC721 asset contracts. 146 | */ 147 | contract IERC721Receiver { 148 | /** 149 | * @notice Handle the receipt of an NFT 150 | * @dev The ERC721 smart contract calls this function on the recipient 151 | * after a `safeTransfer`. This function MUST return the function selector, 152 | * otherwise the caller will revert the transaction. The selector to be 153 | * returned can be obtained as `this.onERC721Received.selector`. This 154 | * function MAY throw to revert and reject the transfer. 155 | * Note: the ERC721 contract address is always the message sender. 156 | * @param operator The address which called `safeTransferFrom` function 157 | * @param from The address which previously owned the token 158 | * @param tokenId The NFT identifier which is being transferred 159 | * @param data Additional data with no specified format 160 | * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 161 | */ 162 | function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) 163 | public returns (bytes4); 164 | } 165 | 166 | 167 | 168 | 169 | /// @title ERC165Interface 170 | /// @dev https://eips.ethereum.org/EIPS/eip-165 171 | interface ERC165Interface { 172 | /// @notice Query if a contract implements an interface 173 | /// @param interfaceId The interface identifier, as specified in ERC-165 174 | /// @dev Interface identification is specified in ERC-165. This function 175 | /// uses less than 30,000 gas. 176 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 177 | } 178 | 179 | 180 | 181 | /// Utility library of inline functions on address payables. 182 | /// Modified from original by OpenZeppelin. 183 | contract Address { 184 | /// @notice Returns whether the target address is a contract. 185 | /// @dev This function will return false if invoked during the constructor of a contract, 186 | /// as the code is not actually created until after the constructor finishes. 187 | /// @param account address of the account to check 188 | /// @return whether the target address is a contract 189 | function isContract(address account) internal view returns (bool) { 190 | uint256 size; 191 | // XXX Currently there is no better way to check if there is a contract in an address 192 | // than to check the size of the code at that address. 193 | // See https://ethereum.stackexchange.com/a/14016/36603 194 | // for more details about how this works. 195 | // TODO Check this again before the Serenity release, because all addresses will be 196 | // contracts then. 197 | // solhint-disable-next-line no-inline-assembly 198 | assembly { size := extcodesize(account) } // solium-disable-line security/no-inline-assembly 199 | return size > 0; 200 | } 201 | } 202 | 203 | 204 | 205 | 206 | /// @title Wizard Non-Fungible Token 207 | /// @notice The basic ERC-721 functionality for storing Cheeze Wizard NFTs. 208 | /// Derived from: https://github.com/OpenZeppelin/openzeppelin-solidity/tree/v2.2.0 209 | contract WizardNFT is ERC165Interface, IERC721, WizardConstants, Address { 210 | 211 | /// @notice Emitted when a wizard token is created. 212 | event WizardConjured(uint256 wizardId, uint8 affinity, uint256 innatePower); 213 | 214 | /// @notice Emitted when a Wizard's affinity is set. This only applies for 215 | /// Exclusive Wizards who can have the ELEMENT_NOT_SET affinity, 216 | /// and should only happen once for each Wizard. 217 | event WizardAffinityAssigned(uint256 wizardId, uint8 affinity); 218 | 219 | // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 220 | // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` 221 | bytes4 internal constant _ERC721_RECEIVED = 0x150b7a02; 222 | 223 | /// @dev The base Wizard structure. 224 | /// Designed to fit in two words. 225 | struct Wizard { 226 | // NOTE: Changing the order or meaning of any of these fields requires an update 227 | // to the _createWizard() function which assumes a specific order for these fields. 228 | uint8 affinity; 229 | uint88 innatePower; 230 | address owner; 231 | bytes32 metadata; 232 | } 233 | 234 | // Mapping from Wizard ID to Wizard struct 235 | mapping (uint256 => Wizard) public wizardsById; 236 | 237 | // Mapping from Wizard ID to address approved to control them 238 | mapping (uint256 => address) private wizardApprovals; 239 | 240 | // Mapping from owner address to number of owned Wizards 241 | mapping (address => uint256) internal ownedWizardsCount; 242 | 243 | // Mapping from owner to Wizard controllers 244 | mapping (address => mapping (address => bool)) private _operatorApprovals; 245 | 246 | /// @dev 0x80ac58cd === 247 | /// bytes4(keccak256('balanceOf(address)')) ^ 248 | /// bytes4(keccak256('ownerOf(uint256)')) ^ 249 | /// bytes4(keccak256('approve(address,uint256)')) ^ 250 | /// bytes4(keccak256('getApproved(uint256)')) ^ 251 | /// bytes4(keccak256('setApprovalForAll(address,bool)')) ^ 252 | /// bytes4(keccak256('isApprovedForAll(address,address)')) ^ 253 | /// bytes4(keccak256('transferFrom(address,address,uint256)')) ^ 254 | /// bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^ 255 | /// bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) 256 | bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; 257 | 258 | /// @notice Query if a contract implements an interface 259 | /// @param interfaceId The interface identifier, as specified in ERC-165 260 | /// @dev Interface identification is specified in ERC-165. This function 261 | /// uses less than 30,000 gas. 262 | function supportsInterface(bytes4 interfaceId) public view returns (bool) { 263 | return 264 | interfaceId == this.supportsInterface.selector || // ERC165 265 | interfaceId == _INTERFACE_ID_ERC721; // ERC721 266 | } 267 | 268 | /// @notice Gets the number of Wizards owned by the specified address. 269 | /// @param owner Address to query the balance of. 270 | /// @return uint256 representing the amount of Wizards owned by the address. 271 | function balanceOf(address owner) public view returns (uint256) { 272 | require(owner != address(0), "ERC721: balance query for the zero address"); 273 | return ownedWizardsCount[owner]; 274 | } 275 | 276 | /// @notice Gets the owner of the specified Wizard 277 | /// @param wizardId ID of the Wizard to query the owner of 278 | /// @return address currently marked as the owner of the given Wizard 279 | function ownerOf(uint256 wizardId) public view returns (address) { 280 | address owner = wizardsById[wizardId].owner; 281 | require(owner != address(0), "ERC721: owner query for nonexistent token"); 282 | return owner; 283 | } 284 | 285 | /// @notice Approves another address to transfer the given Wizard 286 | /// The zero address indicates there is no approved address. 287 | /// There can only be one approved address per Wizard at a given time. 288 | /// Can only be called by the Wizard owner or an approved operator. 289 | /// @param to address to be approved for the given Wizard 290 | /// @param wizardId ID of the Wizard to be approved 291 | function approve(address to, uint256 wizardId) public { 292 | address owner = ownerOf(wizardId); 293 | require(to != owner, "ERC721: approval to current owner"); 294 | require( 295 | msg.sender == owner || isApprovedForAll(owner, msg.sender), 296 | "ERC721: approve caller is not owner nor approved for all" 297 | ); 298 | 299 | wizardApprovals[wizardId] = to; 300 | emit Approval(owner, to, wizardId); 301 | } 302 | 303 | /// @notice Gets the approved address for a Wizard, or zero if no address set 304 | /// Reverts if the Wizard does not exist. 305 | /// @param wizardId ID of the Wizard to query the approval of 306 | /// @return address currently approved for the given Wizard 307 | function getApproved(uint256 wizardId) public view returns (address) { 308 | require(_exists(wizardId), "ERC721: approved query for nonexistent token"); 309 | return wizardApprovals[wizardId]; 310 | } 311 | 312 | /// @notice Sets or unsets the approval of a given operator. 313 | /// An operator is allowed to transfer all Wizards of the sender on their behalf. 314 | /// @param to operator address to set the approval 315 | /// @param approved representing the status of the approval to be set 316 | function setApprovalForAll(address to, bool approved) public { 317 | require(to != msg.sender, "ERC721: approve to caller"); 318 | _operatorApprovals[msg.sender][to] = approved; 319 | emit ApprovalForAll(msg.sender, to, approved); 320 | } 321 | 322 | /// @notice Tells whether an operator is approved by a given owner. 323 | /// @param owner owner address which you want to query the approval of 324 | /// @param operator operator address which you want to query the approval of 325 | /// @return bool whether the given operator is approved by the given owner 326 | function isApprovedForAll(address owner, address operator) public view returns (bool) { 327 | return _operatorApprovals[owner][operator]; 328 | } 329 | 330 | /// @notice Transfers the ownership of a given Wizard to another address. 331 | /// Usage of this method is discouraged, use `safeTransferFrom` whenever possible. 332 | /// Requires the msg.sender to be the owner, approved, or operator. 333 | /// @param from current owner of the Wizard. 334 | /// @param to address to receive the ownership of the given Wizard. 335 | /// @param wizardId ID of the Wizard to be transferred. 336 | function transferFrom(address from, address to, uint256 wizardId) public { 337 | require(_isApprovedOrOwner(msg.sender, wizardId), "ERC721: transfer caller is not owner nor approved"); 338 | 339 | _transferFrom(from, to, wizardId); 340 | } 341 | 342 | /// @notice Safely transfers the ownership of a given Wizard to another address 343 | /// If the target address is a contract, it must implement `onERC721Received`, 344 | /// which is called upon a safe transfer, and return the magic value 345 | /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 346 | /// the transfer is reverted. 347 | /// Requires the msg.sender to be the owner, approved, or operator. 348 | /// @param from current owner of the Wizard. 349 | /// @param to address to receive the ownership of the given Wizard. 350 | /// @param wizardId ID of the Wizard to be transferred. 351 | function safeTransferFrom(address from, address to, uint256 wizardId) public { 352 | safeTransferFrom(from, to, wizardId, ""); 353 | } 354 | 355 | /// @notice Safely transfers the ownership of a given Wizard to another address 356 | /// If the target address is a contract, it must implement `onERC721Received`, 357 | /// which is called upon a safe transfer, and return the magic value 358 | /// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 359 | /// the transfer is reverted. 360 | /// Requires the msg.sender to be the owner, approved, or operator 361 | /// @param from current owner of the Wizard. 362 | /// @param to address to receive the ownership of the given Wizard. 363 | /// @param wizardId ID of the Wizard to be transferred. 364 | /// @param _data bytes data to send along with a safe transfer check 365 | function safeTransferFrom(address from, address to, uint256 wizardId, bytes memory _data) public { 366 | transferFrom(from, to, wizardId); 367 | require(_checkOnERC721Received(from, to, wizardId, _data), "ERC721: transfer to non ERC721Receiver implementer"); 368 | } 369 | 370 | /// @notice Returns whether the specified Wizard exists. 371 | /// @param wizardId ID of the Wizard to query the existence of.. 372 | /// @return bool whether the Wizard exists. 373 | function _exists(uint256 wizardId) internal view returns (bool) { 374 | address owner = wizardsById[wizardId].owner; 375 | return owner != address(0); 376 | } 377 | 378 | /// @notice Returns whether the given spender can transfer a given Wizard. 379 | /// @param spender address of the spender to query 380 | /// @param wizardId ID of the Wizard to be transferred 381 | /// @return bool whether the msg.sender is approved for the given Wizard, 382 | /// is an operator of the owner, or is the owner of the Wizard. 383 | function _isApprovedOrOwner(address spender, uint256 wizardId) internal view returns (bool) { 384 | require(_exists(wizardId), "ERC721: operator query for nonexistent token"); 385 | address owner = ownerOf(wizardId); 386 | return (spender == owner || getApproved(wizardId) == spender || isApprovedForAll(owner, spender)); 387 | } 388 | 389 | /** @dev Internal function to create a new Wizard; reverts if the Wizard ID is taken. 390 | * NOTE: This function heavily depends on the internal format of the Wizard struct 391 | * and should always be reassessed if anything about that structure changes. 392 | * @param wizardId ID of the new Wizard. 393 | * @param owner The address that will own the newly conjured Wizard. 394 | * @param innatePower The power level associated with the new Wizard. 395 | * @param affinity The elemental affinity of the new Wizard. 396 | */ 397 | function _createWizard(uint256 wizardId, address owner, uint88 innatePower, uint8 affinity) internal { 398 | require(owner != address(0), "ERC721: mint to the zero address"); 399 | require(!_exists(wizardId), "ERC721: token already minted"); 400 | require(wizardId > 0, "No 0 token allowed"); 401 | require(innatePower > 0, "Wizard power must be non-zero"); 402 | 403 | // Create the Wizard! 404 | wizardsById[wizardId] = Wizard({ 405 | affinity: affinity, 406 | innatePower: innatePower, 407 | owner: owner, 408 | metadata: 0 409 | }); 410 | 411 | ownedWizardsCount[owner]++; 412 | 413 | // Tell the world! 414 | emit Transfer(address(0), owner, wizardId); 415 | emit WizardConjured(wizardId, affinity, innatePower); 416 | } 417 | 418 | /// @notice Internal function to burn a specific Wizard. 419 | /// Reverts if the Wizard does not exist. 420 | /// Deprecated, use _burn(uint256) instead. 421 | /// @param owner owner of the Wizard to burn. 422 | /// @param wizardId ID of the Wizard being burned 423 | function _burn(address owner, uint256 wizardId) internal { 424 | require(ownerOf(wizardId) == owner, "ERC721: burn of token that is not own"); 425 | 426 | _clearApproval(wizardId); 427 | 428 | ownedWizardsCount[owner]--; 429 | // delete the entire object to recover the most gas 430 | delete wizardsById[wizardId]; 431 | 432 | // required for ERC721 compatibility 433 | emit Transfer(owner, address(0), wizardId); 434 | } 435 | 436 | /// @notice Internal function to burn a specific Wizard. 437 | /// Reverts if the Wizard does not exist. 438 | /// @param wizardId ID of the Wizard being burned 439 | function _burn(uint256 wizardId) internal { 440 | _burn(ownerOf(wizardId), wizardId); 441 | } 442 | 443 | /// @notice Internal function to transfer ownership of a given Wizard to another address. 444 | /// As opposed to transferFrom, this imposes no restrictions on msg.sender. 445 | /// @param from current owner of the Wizard. 446 | /// @param to address to receive the ownership of the given Wizard 447 | /// @param wizardId ID of the Wizard to be transferred 448 | function _transferFrom(address from, address to, uint256 wizardId) internal { 449 | require(ownerOf(wizardId) == from, "ERC721: transfer of token that is not own"); 450 | require(to != address(0), "ERC721: transfer to the zero address"); 451 | 452 | _clearApproval(wizardId); 453 | 454 | ownedWizardsCount[from]--; 455 | ownedWizardsCount[to]++; 456 | 457 | wizardsById[wizardId].owner = to; 458 | 459 | emit Transfer(from, to, wizardId); 460 | } 461 | 462 | /// @notice Internal function to invoke `onERC721Received` on a target address. 463 | /// The call is not executed if the target address is not a contract 464 | /// @param from address representing the previous owner of the given Wizard 465 | /// @param to target address that will receive the Wizards. 466 | /// @param wizardId ID of the Wizard to be transferred 467 | /// @param _data bytes optional data to send along with the call 468 | /// @return bool whether the call correctly returned the expected magic value 469 | function _checkOnERC721Received(address from, address to, uint256 wizardId, bytes memory _data) 470 | internal returns (bool) 471 | { 472 | if (!isContract(to)) { 473 | return true; 474 | } 475 | 476 | bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, wizardId, _data); 477 | return (retval == _ERC721_RECEIVED); 478 | } 479 | 480 | /// @notice Private function to clear current approval of a given Wizard. 481 | /// @param wizardId ID of the Wizard to be transferred 482 | function _clearApproval(uint256 wizardId) private { 483 | if (wizardApprovals[wizardId] != address(0)) { 484 | wizardApprovals[wizardId] = address(0); 485 | } 486 | } 487 | } 488 | 489 | 490 | 491 | 492 | 493 | contract WizardGuildInterfaceId { 494 | bytes4 internal constant _INTERFACE_ID_WIZARDGUILD = 0x41d4d437; 495 | } 496 | 497 | /// @title The public interface of the Wizard Guild 498 | /// @notice The methods listed in this interface (including the inherited ERC-721 interface), 499 | /// make up the public interface of the Wizard Guild contract. Any contracts that wish 500 | /// to make use of Cheeze Wizard NFTs (such as Cheeze Wizards Tournaments!) should use 501 | /// these methods to ensure they are working correctly with the base NFTs. 502 | contract WizardGuildInterface is IERC721, WizardGuildInterfaceId { 503 | 504 | /// @notice Returns the information associated with the given Wizard 505 | /// owner - The address that owns this Wizard 506 | /// innatePower - The innate power level of this Wizard, set when minted and entirely 507 | /// immutable 508 | /// affinity - The Elemental Affinity of this Wizard. For most Wizards, this is set 509 | /// when they are minted, but some exclusive Wizards are minted with an affinity 510 | /// of 0 (ELEMENT_NOTSET). A Wizard with an NOTSET affinity should NOT be able 511 | /// to participate in Tournaments. Once the affinity of a Wizard is set to a non-zero 512 | /// value, it can never be changed again. 513 | /// metadata - A 256-bit hash of the Wizard's metadata, which is stored off chain. This 514 | /// contract doesn't specify format of this hash, nor the off-chain storage mechanism 515 | /// but, let's be honest, it's probably an IPFS SHA-256 hash. 516 | /// 517 | /// NOTE: Series zero Wizards have one of four Affinities: Neutral (1), Fire (2), Water (3) 518 | /// or Air (4, sometimes called "Wind" in the code). Future Wizard Series may have 519 | /// additional Affinities, and clients of this API should be prepared for that 520 | /// eventuality. 521 | function getWizard(uint256 id) external view returns (address owner, uint88 innatePower, uint8 affinity, bytes32 metadata); 522 | 523 | /// @notice Sets the affinity for a Wizard that doesn't already have its elemental affinity chosen. 524 | /// Only usable for Exclusive Wizards (all non-Exclusives must have their affinity chosen when 525 | /// conjured.) Even Exclusives can't change their affinity once it's been chosen. 526 | /// 527 | /// NOTE: This function can only be called by the series minter, and (therefore) only while the 528 | /// series is open. A Wizard that has no affinity when a series is closed will NEVER have an Affinity. 529 | /// BTW- This implies that a minter is responsible for either never minting ELEMENT_NOTSET 530 | /// Wizards, or having some public mechanism for a Wizard owner to set the Affinity after minting. 531 | /// @param wizardId The id of the wizard 532 | /// @param newAffinity The new affinity of the wizard 533 | function setAffinity(uint256 wizardId, uint8 newAffinity) external; 534 | 535 | /// @notice A function to be called that conjures a whole bunch of Wizards at once! You know how 536 | /// there's "a pride of lions", "a murder of crows", and "a parliament of owls"? Well, with this 537 | /// here function you can conjure yourself "a stench of Cheeze Wizards"! 538 | /// 539 | /// Unsurprisingly, this method can only be called by the registered minter for a Series. 540 | /// @param powers the power level of each wizard 541 | /// @param affinities the Elements of the wizards to create 542 | /// @param owner the address that will own the newly created Wizards 543 | function mintWizards( 544 | uint88[] calldata powers, 545 | uint8[] calldata affinities, 546 | address owner 547 | ) external returns (uint256[] memory wizardIds); 548 | 549 | /// @notice A function to be called that conjures a series of Wizards in the reserved ID range. 550 | /// @param wizardIds the ID values to use for each Wizard, must be in the reserved range of the current Series 551 | /// @param affinities the Elements of the wizards to create 552 | /// @param powers the power level of each wizard 553 | /// @param owner the address that will own the newly created Wizards 554 | function mintReservedWizards( 555 | uint256[] calldata wizardIds, 556 | uint88[] calldata powers, 557 | uint8[] calldata affinities, 558 | address owner 559 | ) external; 560 | 561 | /// @notice Sets the metadata values for a list of Wizards. The metadata for a Wizard can only be set once, 562 | /// can only be set by the COO or Minter, and can only be set while the Series is still open. Once 563 | /// a Series is closed, the metadata is locked forever! 564 | /// @param wizardIds the ID values of the Wizards to apply metadata changes to. 565 | /// @param metadata the raw metadata values for each Wizard. This contract does not define how metadata 566 | /// should be interpreted, but it is likely to be a 256-bit hash of a complete metadata package 567 | /// accessible via IPFS or similar. 568 | function setMetadata(uint256[] calldata wizardIds, bytes32[] calldata metadata) external; 569 | 570 | /// @notice Returns true if the given "spender" address is allowed to manipulate the given token 571 | /// (either because it is the owner of that token, has been given approval to manage that token) 572 | function isApprovedOrOwner(address spender, uint256 tokenId) external view returns (bool); 573 | 574 | /// @notice Verifies that a given signature represents authority to control the given Wizard ID, 575 | /// reverting otherwise. It handles three cases: 576 | /// - The simplest case: The signature was signed with the private key associated with 577 | /// an external address that is the owner of this Wizard. 578 | /// - The signature was generated with the private key associated with an external address 579 | /// that is "approved" for working with this Wizard ID. (See the Wizard Guild and/or 580 | /// the ERC-721 spec for more information on "approval".) 581 | /// - The owner or approval address (as in cases one or two) is a smart contract 582 | /// that conforms to ERC-1654, and accepts the given signature as being valid 583 | /// using its own internal logic. 584 | /// 585 | /// NOTE: This function DOES NOT accept a signature created by an address that was given "operator 586 | /// status" (as granted by ERC-721's setApprovalForAll() functionality). Doing so is 587 | /// considered an extreme edge case that can be worked around where necessary. 588 | /// @param wizardId The Wizard ID whose control is in question 589 | /// @param hash The message hash we are authenticating against 590 | /// @param sig the signature data; can be longer than 65 bytes for ERC-1654 591 | function verifySignature(uint256 wizardId, bytes32 hash, bytes calldata sig) external view; 592 | 593 | /// @notice Convenience function that verifies signatures for two wizards using equivalent logic to 594 | /// verifySignature(). Included to save on cross-contract calls in the common case where we 595 | /// are verifying the signatures of two Wizards who wish to enter into a Duel. 596 | /// @param wizardId1 The first Wizard ID whose control is in question 597 | /// @param wizardId2 The second Wizard ID whose control is in question 598 | /// @param hash1 The message hash we are authenticating against for the first Wizard 599 | /// @param hash2 The message hash we are authenticating against for the first Wizard 600 | /// @param sig1 the signature data corresponding to the first Wizard; can be longer than 65 bytes for ERC-1654 601 | /// @param sig2 the signature data corresponding to the second Wizard; can be longer than 65 bytes for ERC-1654 602 | function verifySignatures( 603 | uint256 wizardId1, 604 | uint256 wizardId2, 605 | bytes32 hash1, 606 | bytes32 hash2, 607 | bytes calldata sig1, 608 | bytes calldata sig2) external view; 609 | } 610 | 611 | 612 | 613 | /// @title Contract that manages addresses and access modifiers for certain operations. 614 | /// @author Dapper Labs Inc. (https://www.dapperlabs.com) 615 | contract AccessControl { 616 | 617 | /// @dev The address of the master administrator account that has the power to 618 | /// update itself and all of the other administrator addresses. 619 | /// The CEO account is not expected to be used regularly, and is intended to 620 | /// be stored offline (i.e. a hardware device kept in a safe). 621 | address public ceoAddress; 622 | 623 | /// @dev The address of the "day-to-day" operator of various privileged 624 | /// functions inside the smart contract. Although the CEO has the power 625 | /// to replace the COO, the CEO address doesn't actually have the power 626 | /// to do "COO-only" operations. This is to discourage the regular use 627 | /// of the CEO account. 628 | address public cooAddress; 629 | 630 | /// @dev The address that is allowed to move money around. Kept separate from 631 | /// the COO because the COO address typically lives on an internet-connected 632 | /// computer. 633 | address payable public cfoAddress; 634 | 635 | // Events to indicate when access control role addresses are updated. 636 | event CEOTransferred(address previousCeo, address newCeo); 637 | event COOTransferred(address previousCoo, address newCoo); 638 | event CFOTransferred(address previousCfo, address newCfo); 639 | 640 | /// @dev The AccessControl constructor sets the `ceoAddress` to the sender account. Also 641 | /// initializes the COO and CFO to the passed values (CFO is optional and can be address(0)). 642 | /// @param newCooAddress The initial COO address to set 643 | /// @param newCfoAddress The initial CFO to set (optional) 644 | constructor(address newCooAddress, address payable newCfoAddress) public { 645 | _setCeo(msg.sender); 646 | setCoo(newCooAddress); 647 | 648 | if (newCfoAddress != address(0)) { 649 | setCfo(newCfoAddress); 650 | } 651 | } 652 | 653 | /// @notice Access modifier for CEO-only functionality 654 | modifier onlyCEO() { 655 | require(msg.sender == ceoAddress, "Only CEO"); 656 | _; 657 | } 658 | 659 | /// @notice Access modifier for COO-only functionality 660 | modifier onlyCOO() { 661 | require(msg.sender == cooAddress, "Only COO"); 662 | _; 663 | } 664 | 665 | /// @notice Access modifier for CFO-only functionality 666 | modifier onlyCFO() { 667 | require(msg.sender == cfoAddress, "Only CFO"); 668 | _; 669 | } 670 | 671 | function checkControlAddress(address newController) internal view { 672 | require(newController != address(0) && newController != ceoAddress, "Invalid CEO address"); 673 | } 674 | 675 | /// @notice Assigns a new address to act as the CEO. Only available to the current CEO. 676 | /// @param newCeo The address of the new CEO 677 | function setCeo(address newCeo) external onlyCEO { 678 | checkControlAddress(newCeo); 679 | _setCeo(newCeo); 680 | } 681 | 682 | /// @dev An internal utility function that updates the CEO variable and emits the 683 | /// transfer event. Used from both the public setCeo function and the constructor. 684 | function _setCeo(address newCeo) private { 685 | emit CEOTransferred(ceoAddress, newCeo); 686 | ceoAddress = newCeo; 687 | } 688 | 689 | /// @notice Assigns a new address to act as the COO. Only available to the current CEO. 690 | /// @param newCoo The address of the new COO 691 | function setCoo(address newCoo) public onlyCEO { 692 | checkControlAddress(newCoo); 693 | emit COOTransferred(cooAddress, newCoo); 694 | cooAddress = newCoo; 695 | } 696 | 697 | /// @notice Assigns a new address to act as the CFO. Only available to the current CEO. 698 | /// @param newCfo The address of the new CFO 699 | function setCfo(address payable newCfo) public onlyCEO { 700 | checkControlAddress(newCfo); 701 | emit CFOTransferred(cfoAddress, newCfo); 702 | cfoAddress = newCfo; 703 | } 704 | } 705 | 706 | 707 | 708 | 709 | /// @title Signature utility library 710 | library SigTools { 711 | 712 | /// @notice Splits a signature into r & s values, and v (the verification value). 713 | /// @dev Note: This does not verify the version, but does require signature length = 65 714 | /// @param signature the packed signature to be split 715 | function _splitSignature(bytes memory signature) internal pure returns (bytes32 r, bytes32 s, uint8 v) { 716 | // Check signature length 717 | require(signature.length == 65, "Invalid signature length"); 718 | 719 | // We need to unpack the signature, which is given as an array of 65 bytes (like eth.sign) 720 | // solium-disable-next-line security/no-inline-assembly 721 | assembly { 722 | r := mload(add(signature, 32)) 723 | s := mload(add(signature, 64)) 724 | v := and(mload(add(signature, 65)), 255) 725 | } 726 | 727 | if (v < 27) { 728 | v += 27; // Ethereum versions are 27 or 28 as opposed to 0 or 1 which is submitted by some signing libs 729 | } 730 | 731 | // check for valid version 732 | // removed for now, done in another function 733 | //require((v == 27 || v == 28), "Invalid signature version"); 734 | 735 | return (r, s, v); 736 | } 737 | } 738 | 739 | 740 | 741 | contract ERC1654 { 742 | 743 | /// @dev bytes4(keccak256("isValidSignature(bytes32,bytes)") 744 | bytes4 public constant ERC1654_VALIDSIGNATURE = 0x1626ba7e; 745 | 746 | /// @dev Should return whether the signature provided is valid for the provided data 747 | /// @param hash 32-byte hash of the data that is signed 748 | /// @param _signature Signature byte array associated with _data 749 | /// MUST return the bytes4 magic value 0x1626ba7e when function passes. 750 | /// MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5) 751 | /// MUST allow external calls 752 | function isValidSignature( 753 | bytes32 hash, 754 | bytes calldata _signature) 755 | external 756 | view 757 | returns (bytes4); 758 | } 759 | 760 | 761 | 762 | /// @title The master organization behind all Cheeze Wizardry. The source of all them Wiz. 763 | contract WizardGuild is AccessControl, WizardNFT, WizardGuildInterface, ERC165Query { 764 | 765 | /// @notice Emitted when a new Series is opened or closed. 766 | event SeriesOpen(uint64 seriesIndex, uint256 reservedIds); 767 | event SeriesClose(uint64 seriesIndex); 768 | 769 | /// @notice Emitted when metadata is associated with a Wizard 770 | event MetadataSet(uint256 indexed wizardId, bytes32 metadata); 771 | 772 | /// @notice The index of the current Series (zero-based). When no Series is open, this value 773 | /// indicates the index of the _upcoming_ Series. (i.e. it is incremented when the 774 | /// Series is closed. This makes it easier to bootstrap the first Series.) 775 | uint64 internal seriesIndex; 776 | 777 | /// @notice The address which is allowed to mint new Wizards in the current Series. When this 778 | /// is set to address(0), there is no open Series. 779 | address internal seriesMinter; 780 | 781 | /// @notice The index number of the next Wizard to be created (Neutral or Elemental). 782 | /// NOTE: There is a subtle distinction between a Wizard "ID" and a Wizard "index". 783 | /// We use the term "ID" to refer to a value that includes the Series number in the 784 | /// top 64 bits, while the term "index" refers to the Wizard number _within_ its 785 | /// Series. This is especially confusing when talking about Wizards in the first 786 | /// Series (Series 0), because the two values are identical in that case! 787 | /// 788 | /// |---------------|--------------------------| 789 | /// | Wizard ID (256 bits) | 790 | /// |---------------|--------------------------| 791 | /// | Series Index | Wizard Index | 792 | /// | (64 bits) | (192 bits) | 793 | /// |---------------|--------------------------| 794 | uint256 internal nextWizardIndex; 795 | 796 | function getNextWizardIndex() external view returns (uint256) { 797 | return nextWizardIndex; 798 | } 799 | 800 | // NOTE: uint256(-1) maps to a value with all bits set, both the << and >> operators will fill 801 | // in with zeros when acting on an unsigned value. So, "uint256(-1) << 192" resolves to "a bunch 802 | /// of ones, followed by 192 zeros" 803 | uint256 internal constant SERIES_OFFSET = 192; 804 | uint256 internal constant SERIES_MASK = uint256(-1) << SERIES_OFFSET; 805 | uint256 internal constant INDEX_MASK = uint256(-1) >> 64; 806 | 807 | // The ERC1654 function selector value 808 | bytes4 internal constant ERC1654_VALIDSIGNATURE = 0x1626ba7e; 809 | 810 | /// @notice The Guild constructor. 811 | /// @param _cooAddress The COO has the ability to create new Series and to update 812 | /// the metadata on the currently open Series (if any). It has no other special 813 | /// abilities, and (in particular), ALL Wizards in a closed series can never be 814 | /// modified or deleted. If the CEO and COO values are ever set to invalid addresses 815 | /// (such as address(1)), then no new Series can ever be created, either. 816 | constructor(address _cooAddress) public AccessControl(_cooAddress, address(0)) { 817 | } 818 | 819 | /// @notice Require that a Tournament Series is currently open. For example closing 820 | /// a Series does not make sense if none is open. 821 | /// @dev While in other contracts we use separate checking functions to avoid having the same 822 | /// string inlined in multiple places, given this modifier is scarcely used it doesn't seem 823 | /// worth the per-call gas cost here. 824 | modifier duringSeries() { 825 | require(seriesMinter != address(0), "No series is currently open"); 826 | _; 827 | } 828 | 829 | /// @notice Require that the caller is the minter of the current series. This implicitely 830 | /// requires that a Series is open, or the minter address would be invalid (can never 831 | /// be matched). 832 | /// @dev While in other contracts we use separate checking functions to avoid having the same 833 | /// string inlined in multiple places, given this modifier is scarcely used it doesn't seem 834 | /// worth the per-call gas cost here. 835 | modifier onlyMinter() { 836 | require(msg.sender == seriesMinter, "Only callable by minter"); 837 | _; 838 | } 839 | 840 | /// @notice Open a new Series of Cheeze Wizards! Can only be called by the COO when no Series is open. 841 | /// @param minter The address which is allowed to mint Wizards in this series. This contract does not 842 | /// assume that the minter is a smart contract, but it will presumably be in the vast majority 843 | /// of the cases. A minter has absolute control over the creation of new Wizards in an open 844 | /// Series, but CAN NOT manipulate a Series after it has been closed, and CAN NOT manipulate 845 | /// any Wizards that don't belong to its own Series. (Even if the same minting address is used 846 | /// for multiple Series, the Minter only has power over the currently open Series.) 847 | /// @param reservedIds The number of IDs (from 1 to reservedIds, inclusive) that are reserved for minting 848 | /// reserved Wizards. (We use the term "reserved" here, instead of Exclusive, because there 849 | /// are times -- such as during the importation of the Presale -- when we need to reserve a 850 | /// block of IDs for Wizards that aren't what a user would think of as "exclusive". In Series 851 | /// 0, the reserved IDs will include all Exclusive Wizards and Presale Wizards. In other Series 852 | /// it might also be the case that the set of "reserved IDs" doesn't exactly match the set of 853 | /// "exclusive" IDs.) 854 | function openSeries(address minter, uint256 reservedIds) external onlyCOO returns (uint64 seriesId) { 855 | require(seriesMinter == address(0), "A series is already open"); 856 | require(minter != address(0), "Minter address cannot be 0"); 857 | 858 | if (seriesIndex == 0) { 859 | // The last wizard sold in the unpasteurized Tournament at the time the Presale contract 860 | // was destroyed is 6133. 861 | // 862 | // The unpasteurized Tournament contract is the Tournament contract that doesn't have the 863 | // "Same Wizard" check in the resolveTimedOutDuel function. 864 | 865 | // The wizards, which were minted in the unpasteurized Tournament before the Presale contract 866 | // was destroyed, will be minted again in the new Tournament contract with their ID reserved. 867 | // 868 | // So the reason the reservedIds is hardcoded here is to ensure: 869 | // 1) The next Wizard minted will have its ID continued from this above wizard ID. 870 | // 2) The Presale wizards and some wizards minted in the unpasteurized Tournament contract, 871 | // can be minted in this contract with their ID reserved. 872 | require(reservedIds == 6133, "Invalid reservedIds for 1st series"); 873 | } else { 874 | require(reservedIds < 1 << 192, "Invalid reservedIds"); 875 | } 876 | 877 | // NOTE: The seriesIndex is updated when the Series is _closed_, not when it's opened. 878 | // (The first Series is Series #0.) So in this function, we just leave the seriesIndex alone. 879 | 880 | seriesMinter = minter; 881 | nextWizardIndex = reservedIds + 1; 882 | 883 | emit SeriesOpen(seriesIndex, reservedIds); 884 | 885 | return seriesIndex; 886 | } 887 | 888 | /// @notice Closes the current Wizard Series. Once a Series has been closed, it is forever sealed and 889 | /// no more Wizards in that Series can ever be minted! Can only be called by the COO when a Series 890 | /// is open. 891 | /// 892 | /// NOTE: A series can be closed by the COO or the Minter. (It's assumed that some minters will 893 | /// know when they are done, and others will need to be shut off manually by the COO.) 894 | function closeSeries() external duringSeries { 895 | require( 896 | msg.sender == seriesMinter || msg.sender == cooAddress, 897 | "Only Minter or COO can close a Series"); 898 | 899 | seriesMinter = address(0); 900 | emit SeriesClose(seriesIndex); 901 | 902 | // Set up the next series. 903 | seriesIndex += 1; 904 | nextWizardIndex = 0; 905 | } 906 | 907 | /// @notice ERC-165 Query Function. 908 | function supportsInterface(bytes4 interfaceId) public view returns (bool) { 909 | return interfaceId == _INTERFACE_ID_WIZARDGUILD || super.supportsInterface(interfaceId); 910 | } 911 | 912 | /// @notice Returns the information associated with the given Wizard 913 | /// owner - The address that owns this Wizard 914 | /// innatePower - The innate power level of this Wizard, set when minted and entirely 915 | /// immutable 916 | /// affinity - The Elemental Affinity of this Wizard. For most Wizards, this is set 917 | /// when they are minted, but some exclusive Wizards are minted with an affinity 918 | /// of 0 (ELEMENT_NOTSET). A Wizard with an NOTSET affinity should NOT be able 919 | /// to participate in Tournaments. Once the affinity of a Wizard is set to a non-zero 920 | /// value, it can never be changed again. 921 | /// metadata - A 256-bit hash of the Wizard's metadata, which is stored off chain. This 922 | /// contract doesn't specify format of this hash, nor the off-chain storage mechanism 923 | /// but, let's be honest, it's probably an IPFS SHA-256 hash. 924 | /// 925 | /// NOTE: Series zero Wizards have one of four Affinities: Neutral (1), Fire (2), Water (3) 926 | /// or Air (4, sometimes called "Wind" in the code). Future Wizard Series may have 927 | /// additional Affinities, and clients of this API should be prepared for that 928 | /// eventuality. 929 | function getWizard(uint256 id) public view returns (address owner, uint88 innatePower, uint8 affinity, bytes32 metadata) { 930 | Wizard memory wizard = wizardsById[id]; 931 | require(wizard.owner != address(0), "Wizard does not exist"); 932 | (owner, innatePower, affinity, metadata) = (wizard.owner, wizard.innatePower, wizard.affinity, wizard.metadata); 933 | } 934 | 935 | /// @notice A function to be called that conjures a whole bunch of Wizards at once! You know how 936 | /// there's "a pride of lions", "a murder of crows", and "a parliament of owls"? Well, with this 937 | /// here function you can conjure yourself "a stench of Cheeze Wizards"! 938 | /// 939 | /// Unsurprisingly, this method can only be called by the registered minter for a Series. 940 | /// @dev This function DOES NOT CALL onERC721Received() as required by the ERC-721 standard. It is 941 | /// REQUIRED that the Minter calls onERC721Received() after calling this function. The following 942 | /// code snippet should suffice: 943 | /// // Ensure the Wizard is being assigned to an ERC-721 aware address (either an external address, 944 | /// // or a smart contract that implements onERC721Received()). We must call onERC721Received for 945 | /// // each token created because it's allowed for an ERC-721 receiving contract to reject the 946 | /// // transfer based on the properties of the token. 947 | /// if (isContract(owner)) { 948 | /// for (uint256 i = 0; i < wizardIds.length; i++) { 949 | /// bytes4 retval = IERC721Receiver(owner).onERC721Received(owner, address(0), wizardIds[i], ""); 950 | /// require(retval == _ERC721_RECEIVED, "Contract owner didn't accept ERC721 transfer"); 951 | /// } 952 | /// } 953 | /// Although it would be convenient for mintWizards to call onERC721Received, it opens us up to potential 954 | /// reentrancy attacks if the Minter needs to do more state updates after mintWizards() returns. 955 | /// @param powers the power level of each wizard 956 | /// @param affinities the Elements of the wizards to create 957 | /// @param owner the address that will own the newly created Wizards 958 | function mintWizards( 959 | uint88[] calldata powers, 960 | uint8[] calldata affinities, 961 | address owner 962 | ) external onlyMinter returns (uint256[] memory wizardIds) 963 | { 964 | require(affinities.length == powers.length, "Inconsistent parameter lengths"); 965 | 966 | // allocate result array 967 | wizardIds = new uint256[](affinities.length); 968 | 969 | // We take this storage variables, and turn it into a local variable for the course 970 | // of this loop to save about 5k gas per wizard. 971 | uint256 tempWizardId = (uint256(seriesIndex) << SERIES_OFFSET) + nextWizardIndex; 972 | 973 | for (uint256 i = 0; i < affinities.length; i++) { 974 | wizardIds[i] = tempWizardId; 975 | tempWizardId++; 976 | 977 | _createWizard(wizardIds[i], owner, powers[i], affinities[i]); 978 | } 979 | 980 | nextWizardIndex = tempWizardId & INDEX_MASK; 981 | } 982 | 983 | /// @notice A function to be called that mints a Series of Wizards in the reserved ID range, can only 984 | /// be called by the Minter for this Series. 985 | /// @dev This function DOES NOT CALL onERC721Received() as required by the ERC-721 standard. It is 986 | /// REQUIRED that the Minter calls onERC721Received() after calling this function. See the note 987 | /// above on mintWizards() for more info. 988 | /// @param wizardIds the ID values to use for each Wizard, must be in the reserved range of the current Series. 989 | /// @param powers the power level of each Wizard. 990 | /// @param affinities the Elements of the Wizards to create. 991 | /// @param owner the address that will own the newly created Wizards. 992 | function mintReservedWizards( 993 | uint256[] calldata wizardIds, 994 | uint88[] calldata powers, 995 | uint8[] calldata affinities, 996 | address owner 997 | ) 998 | external onlyMinter 999 | { 1000 | require( 1001 | wizardIds.length == affinities.length && 1002 | wizardIds.length == powers.length, "Inconsistent parameter lengths"); 1003 | 1004 | for (uint256 i = 0; i < wizardIds.length; i++) { 1005 | uint256 currentId = wizardIds[i]; 1006 | 1007 | require((currentId & SERIES_MASK) == (uint256(seriesIndex) << SERIES_OFFSET), "Wizards not in current series"); 1008 | require((currentId & INDEX_MASK) > 0, "Wizards id cannot be zero"); 1009 | 1010 | // Ideally, we would compare the requested Wizard index against the reserved range directly. However, 1011 | // it's a bit wasteful to spend storage on a reserved range variable when we can combine some known 1012 | // true facts instead: 1013 | // - nextWizardIndex is initialized to reservedRange + 1 when the Series was opened 1014 | // - nextWizardIndex is only incremented when a new Wizard is created 1015 | // - therefore, the only empty Wizard IDs less than nextWizardIndex are in the reserved range. 1016 | // - _conjureWizard() will abort if we try to reuse an ID. 1017 | // Combining all of the above, we know that, if the requested index is less than the next index, it 1018 | // either points to a reserved slot or an occupied slot. Trying to reuse an occupied slot will fail, 1019 | // so just checking against nextWizardIndex is sufficient to ensure we're pointing at a reserved slot. 1020 | require((currentId & INDEX_MASK) < nextWizardIndex, "Wizards not in reserved range"); 1021 | 1022 | _createWizard(currentId, owner, powers[i], affinities[i]); 1023 | } 1024 | } 1025 | 1026 | /// @notice Sets the metadata values for a list of Wizards. The metadata for a Wizard can only be set once, 1027 | /// can only be set by the COO or Minter, and can only be set while the Series is still open. Once 1028 | /// a Series is closed, the metadata is locked forever! 1029 | /// @param wizardIds the ID values of the Wizards to apply metadata changes to. 1030 | /// @param metadata the raw metadata values for each Wizard. This contract does not define how metadata 1031 | /// should be interpreted, but it is likely to be a 256-bit hash of a complete metadata package 1032 | /// accessible via IPFS or similar. 1033 | function setMetadata(uint256[] calldata wizardIds, bytes32[] calldata metadata) external duringSeries { 1034 | require(msg.sender == seriesMinter || msg.sender == cooAddress, "Only Minter or COO can set metadata"); 1035 | require(wizardIds.length == metadata.length, "Inconsistent parameter lengths"); 1036 | 1037 | for (uint256 i = 0; i < wizardIds.length; i++) { 1038 | uint256 currentId = wizardIds[i]; 1039 | bytes32 currentMetadata = metadata[i]; 1040 | 1041 | require((currentId & SERIES_MASK) == (uint256(seriesIndex) << SERIES_OFFSET), "Wizards not in current series"); 1042 | 1043 | require(wizardsById[currentId].metadata == bytes32(0), "Metadata already set"); 1044 | 1045 | require(currentMetadata != bytes32(0), "Invalid metadata"); 1046 | 1047 | wizardsById[currentId].metadata = currentMetadata; 1048 | 1049 | emit MetadataSet(currentId, currentMetadata); 1050 | } 1051 | } 1052 | 1053 | /// @notice Sets the affinity for a Wizard that doesn't already have its elemental affinity chosen. 1054 | /// Only usable for Exclusive Wizards (all non-Exclusives must have their affinity chosen when 1055 | /// conjured.) Even Exclusives can't change their affinity once it's been chosen. 1056 | /// 1057 | /// NOTE: This function can only be called by the Series minter, and (therefore) only while the 1058 | /// Series is open. A Wizard that has no affinity when a Series is closed will NEVER have an Affinity. 1059 | /// @param wizardId The ID of the Wizard to update affinity of. 1060 | /// @param newAffinity The new affinity of the Wizard. 1061 | function setAffinity(uint256 wizardId, uint8 newAffinity) external onlyMinter { 1062 | require((wizardId & SERIES_MASK) == (uint256(seriesIndex) << SERIES_OFFSET), "Wizard not in current series"); 1063 | 1064 | Wizard storage wizard = wizardsById[wizardId]; 1065 | 1066 | require(wizard.affinity == ELEMENT_NOTSET, "Affinity can only be chosen once"); 1067 | 1068 | // set the affinity 1069 | wizard.affinity = newAffinity; 1070 | 1071 | // Tell the world this wizards now has an affinity! 1072 | emit WizardAffinityAssigned(wizardId, newAffinity); 1073 | } 1074 | 1075 | /// @notice Returns true if the given "spender" address is allowed to manipulate the given token 1076 | /// (either because it is the owner of that token, has been given approval to manage that token) 1077 | function isApprovedOrOwner(address spender, uint256 tokenId) external view returns (bool) { 1078 | return _isApprovedOrOwner(spender, tokenId); 1079 | } 1080 | 1081 | /// @notice Verifies that a given signature represents authority to control the given Wizard ID, 1082 | /// reverting otherwise. It handles three cases: 1083 | /// - The simplest case: The signature was signed with the private key associated with 1084 | /// an external address that is the owner of this Wizard. 1085 | /// - The signature was generated with the private key associated with an external address 1086 | /// that is "approved" for working with this Wizard ID. (See the Wizard Guild and/or 1087 | /// the ERC-721 spec for more information on "approval".) 1088 | /// - The owner or approval address (as in cases one or two) is a smart contract 1089 | /// that conforms to ERC-1654, and accepts the given signature as being valid 1090 | /// using its own internal logic. 1091 | /// 1092 | /// NOTE: This function DOES NOT accept a signature created by an address that was given "operator 1093 | /// status" (as granted by ERC-721's setApprovalForAll() functionality). Doing so is 1094 | /// considered an extreme edge case that can be worked around where necessary. 1095 | /// @param wizardId The Wizard ID whose control is in question 1096 | /// @param hash The message hash we are authenticating against 1097 | /// @param sig the signature data; can be longer than 65 bytes for ERC-1654 1098 | function verifySignature(uint256 wizardId, bytes32 hash, bytes memory sig) public view { 1099 | // First see if the signature belongs to the owner (the most common case) 1100 | address owner = ownerOf(wizardId); 1101 | 1102 | if (_validSignatureForAddress(owner, hash, sig)) { 1103 | return; 1104 | } 1105 | 1106 | // Next check if the signature belongs to the approved address 1107 | address approved = getApproved(wizardId); 1108 | 1109 | if (_validSignatureForAddress(approved, hash, sig)) { 1110 | return; 1111 | } 1112 | 1113 | revert("Invalid signature"); 1114 | } 1115 | 1116 | /// @notice Convenience function that verifies signatures for two wizards using equivalent logic to 1117 | /// verifySignature(). Included to save on cross-contract calls in the common case where we 1118 | /// are verifying the signatures of two Wizards who wish to enter into a Duel. 1119 | /// @param wizardId1 The first Wizard ID whose control is in question 1120 | /// @param wizardId2 The second Wizard ID whose control is in question 1121 | /// @param hash1 The message hash we are authenticating against for the first Wizard 1122 | /// @param hash2 The message hash we are authenticating against for the first Wizard 1123 | /// @param sig1 the signature data corresponding to the first Wizard; can be longer than 65 bytes for ERC-1654 1124 | /// @param sig2 the signature data corresponding to the second Wizard; can be longer than 65 bytes for ERC-1654 1125 | function verifySignatures( 1126 | uint256 wizardId1, 1127 | uint256 wizardId2, 1128 | bytes32 hash1, 1129 | bytes32 hash2, 1130 | bytes calldata sig1, 1131 | bytes calldata sig2) external view 1132 | { 1133 | verifySignature(wizardId1, hash1, sig1); 1134 | verifySignature(wizardId2, hash2, sig2); 1135 | } 1136 | 1137 | /// @notice An internal function that checks if a given signature is a valid signature for a 1138 | /// specific address on a particular hash value. Checks for ERC-1654 compatibility 1139 | /// first (where the possibleSigner is a smart contract that implements its own 1140 | /// signature validation), and falls back to ecrecover() otherwise. 1141 | function _validSignatureForAddress(address possibleSigner, bytes32 hash, bytes memory signature) 1142 | internal view returns(bool) 1143 | { 1144 | if (possibleSigner == address(0)) { 1145 | // The most basic Bozo check: The zero address can never be a valid signer! 1146 | return false; 1147 | } else if (Address.isContract(possibleSigner)) { 1148 | // If the address is a contract, it either implements ERC-1654 (and will validate the signature 1149 | // itself), or we have no way of confirming that this signature matches this address. In other words, 1150 | // if this address is a contract, there's no point in "falling back" to ecrecover(). 1151 | if (doesContractImplementInterface(possibleSigner, ERC1654_VALIDSIGNATURE)) { 1152 | // cast to ERC1654 1153 | ERC1654 tso = ERC1654(possibleSigner); 1154 | bytes4 result = tso.isValidSignature(keccak256(abi.encodePacked(hash)), signature); 1155 | if (result == ERC1654_VALIDSIGNATURE) { 1156 | return true; 1157 | } 1158 | } 1159 | 1160 | return false; 1161 | } else { 1162 | // Not a contract, check for a match against an external address 1163 | // assume EIP 191 signature here 1164 | (bytes32 r, bytes32 s, uint8 v) = SigTools._splitSignature(signature); 1165 | address signer = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash)), v, r, s); 1166 | 1167 | // Note: Signer could be address(0) here, but we already checked that possibleSigner isn't zero 1168 | return (signer == possibleSigner); 1169 | } 1170 | } 1171 | 1172 | } 1173 | -------------------------------------------------------------------------------- /contracts/WizardPresale.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.2; 2 | 3 | /** 4 | * @title IERC165 5 | * @dev https://eips.ethereum.org/EIPS/eip-165 6 | */ 7 | interface IERC165 { 8 | /** 9 | * @notice Query if a contract implements an interface 10 | * @param interfaceId The interface identifier, as specified in ERC-165 11 | * @dev Interface identification is specified in ERC-165. This function 12 | * uses less than 30,000 gas. 13 | */ 14 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 15 | } 16 | 17 | 18 | /// @title Shared constants used throughout the Cheeze Wizards contracts 19 | contract WizardConstants { 20 | // Wizards normally have their affinity set when they are first created, 21 | // but for example Exclusive Wizards can be created with no set affinity. 22 | // In this case the affinity can be set by the owner. 23 | uint8 internal constant ELEMENT_NOTSET = 0; //000 24 | // A neutral Wizard has no particular strength or weakness with specific 25 | // elements. 26 | uint8 internal constant ELEMENT_NEUTRAL = 1; //001 27 | // The fire, water and wind elements are used both to reflect an affinity 28 | // of Elemental Wizards for a specific element, and as the moves a 29 | // Wizard can make during a duel. 30 | // Note that if these values change then `moveMask` and `moveDelta` in 31 | // ThreeAffinityDuelResolver would need to be updated accordingly. 32 | uint8 internal constant ELEMENT_FIRE = 2; //010 33 | uint8 internal constant ELEMENT_WATER = 3; //011 34 | uint8 internal constant ELEMENT_WIND = 4; //100 35 | uint8 internal constant MAX_ELEMENT = ELEMENT_WIND; 36 | } 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | /** 46 | * @title ERC721 Non-Fungible Token Standard basic interface 47 | * @dev see https://eips.ethereum.org/EIPS/eip-721 48 | */ 49 | contract IERC721 is IERC165 { 50 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 51 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 52 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 53 | 54 | function balanceOf(address owner) public view returns (uint256 balance); 55 | function ownerOf(uint256 tokenId) public view returns (address owner); 56 | 57 | function approve(address to, uint256 tokenId) public; 58 | function getApproved(uint256 tokenId) public view returns (address operator); 59 | 60 | function setApprovalForAll(address operator, bool _approved) public; 61 | function isApprovedForAll(address owner, address operator) public view returns (bool); 62 | 63 | function transferFrom(address from, address to, uint256 tokenId) public; 64 | function safeTransferFrom(address from, address to, uint256 tokenId) public; 65 | 66 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public; 67 | } 68 | 69 | 70 | 71 | /** 72 | * @title ERC721 token receiver interface 73 | * @dev Interface for any contract that wants to support safeTransfers 74 | * from ERC721 asset contracts. 75 | */ 76 | contract IERC721Receiver { 77 | /** 78 | * @notice Handle the receipt of an NFT 79 | * @dev The ERC721 smart contract calls this function on the recipient 80 | * after a `safeTransfer`. This function MUST return the function selector, 81 | * otherwise the caller will revert the transaction. The selector to be 82 | * returned can be obtained as `this.onERC721Received.selector`. This 83 | * function MAY throw to revert and reject the transfer. 84 | * Note: the ERC721 contract address is always the message sender. 85 | * @param operator The address which called `safeTransferFrom` function 86 | * @param from The address which previously owned the token 87 | * @param tokenId The NFT identifier which is being transferred 88 | * @param data Additional data with no specified format 89 | * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 90 | */ 91 | function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) 92 | public returns (bytes4); 93 | } 94 | 95 | 96 | 97 | 98 | 99 | /** 100 | * @title ERC165 101 | * @author Matt Condon (@shrugs) 102 | * @dev Implements ERC165 using a lookup table. 103 | */ 104 | contract ERC165 is IERC165 { 105 | bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; 106 | /* 107 | * 0x01ffc9a7 === 108 | * bytes4(keccak256('supportsInterface(bytes4)')) 109 | */ 110 | 111 | /** 112 | * @dev a mapping of interface id to whether or not it's supported 113 | */ 114 | mapping(bytes4 => bool) private _supportedInterfaces; 115 | 116 | /** 117 | * @dev A contract implementing SupportsInterfaceWithLookup 118 | * implement ERC165 itself 119 | */ 120 | constructor () internal { 121 | _registerInterface(_INTERFACE_ID_ERC165); 122 | } 123 | 124 | /** 125 | * @dev implement supportsInterface(bytes4) using a lookup table 126 | */ 127 | function supportsInterface(bytes4 interfaceId) external view returns (bool) { 128 | return _supportedInterfaces[interfaceId]; 129 | } 130 | 131 | /** 132 | * @dev internal method for registering an interface 133 | */ 134 | function _registerInterface(bytes4 interfaceId) internal { 135 | require(interfaceId != 0xffffffff); 136 | _supportedInterfaces[interfaceId] = true; 137 | } 138 | } 139 | 140 | 141 | 142 | /// Utility library of inline functions on address payables. 143 | /// Modified from original by OpenZeppelin. 144 | contract Address { 145 | /// @notice Returns whether the target address is a contract. 146 | /// @dev This function will return false if invoked during the constructor of a contract, 147 | /// as the code is not actually created until after the constructor finishes. 148 | /// @param account address of the account to check 149 | /// @return whether the target address is a contract 150 | function isContract(address account) internal view returns (bool) { 151 | uint256 size; 152 | // XXX Currently there is no better way to check if there is a contract in an address 153 | // than to check the size of the code at that address. 154 | // See https://ethereum.stackexchange.com/a/14016/36603 155 | // for more details about how this works. 156 | // TODO Check this again before the Serenity release, because all addresses will be 157 | // contracts then. 158 | // solhint-disable-next-line no-inline-assembly 159 | assembly { size := extcodesize(account) } // solium-disable-line security/no-inline-assembly 160 | return size > 0; 161 | } 162 | } 163 | 164 | 165 | 166 | /** 167 | * @title WizardPresaleNFT 168 | * @notice The basic ERC-721 functionality for storing the presale Wizard NFTs. 169 | * Derived from: https://github.com/OpenZeppelin/openzeppelin-solidity/tree/v2.2.0 170 | */ 171 | contract WizardPresaleNFT is ERC165, IERC721, Address { 172 | 173 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 174 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 175 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 176 | 177 | /// @notice Emitted when a wizard token is created. 178 | event WizardSummoned(uint256 indexed tokenId, uint8 element, uint256 power); 179 | 180 | /// @notice Emitted when a wizard change element. Should only happen once and for wizards 181 | /// that previously had the element undefined. 182 | event WizardAlignmentAssigned(uint256 indexed tokenId, uint8 element); 183 | 184 | // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 185 | // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` 186 | bytes4 internal constant _ERC721_RECEIVED = 0x150b7a02; 187 | 188 | /// @dev The presale Wizard structure. 189 | /// Fits in one word 190 | struct Wizard { 191 | // NOTE: Changing the order or meaning of any of these fields requires an update 192 | // to the _createWizard() function which assumes a specific order for these fields. 193 | uint8 affinity; 194 | uint88 power; 195 | address owner; 196 | } 197 | 198 | // Mapping from Wizard ID to Wizard struct 199 | mapping (uint256 => Wizard) public _wizardsById; 200 | 201 | // Mapping from token ID to approved address 202 | mapping (uint256 => address) private _tokenApprovals; 203 | 204 | // Mapping from owner to number of owned token 205 | mapping (address => uint256) internal _ownedTokensCount; 206 | 207 | // Mapping from owner to operator approvals 208 | mapping (address => mapping (address => bool)) private _operatorApprovals; 209 | 210 | bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; 211 | /* 212 | * 0x80ac58cd === 213 | * bytes4(keccak256('balanceOf(address)')) ^ 214 | * bytes4(keccak256('ownerOf(uint256)')) ^ 215 | * bytes4(keccak256('approve(address,uint256)')) ^ 216 | * bytes4(keccak256('getApproved(uint256)')) ^ 217 | * bytes4(keccak256('setApprovalForAll(address,bool)')) ^ 218 | * bytes4(keccak256('isApprovedForAll(address,address)')) ^ 219 | * bytes4(keccak256('transferFrom(address,address,uint256)')) ^ 220 | * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) ^ 221 | * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) 222 | */ 223 | 224 | constructor () public { 225 | // register the supported interfaces to conform to ERC721 via ERC165 226 | _registerInterface(_INTERFACE_ID_ERC721); 227 | } 228 | 229 | /** 230 | * @dev Gets the balance of the specified address 231 | * @param owner address to query the balance of 232 | * @return uint256 representing the amount owned by the passed address 233 | */ 234 | function balanceOf(address owner) public view returns (uint256) { 235 | require(owner != address(0), "ERC721: balance query for the zero address"); 236 | return _ownedTokensCount[owner]; 237 | } 238 | 239 | /** 240 | * @dev Gets the owner of the specified token ID 241 | * @param tokenId uint256 ID of the token to query the owner of 242 | * @return address currently marked as the owner of the given token ID 243 | */ 244 | function ownerOf(uint256 tokenId) public view returns (address) { 245 | address owner = _wizardsById[tokenId].owner; 246 | require(owner != address(0), "ERC721: owner query for nonexistent token"); 247 | return owner; 248 | } 249 | 250 | /** 251 | * @dev Approves another address to transfer the given token ID 252 | * The zero address indicates there is no approved address. 253 | * There can only be one approved address per token at a given time. 254 | * Can only be called by the token owner or an approved operator. 255 | * @param to address to be approved for the given token ID 256 | * @param tokenId uint256 ID of the token to be approved 257 | */ 258 | function approve(address to, uint256 tokenId) public { 259 | address owner = ownerOf(tokenId); 260 | require(to != owner, "ERC721: approval to current owner"); 261 | require( 262 | msg.sender == owner || isApprovedForAll(owner, msg.sender), 263 | "ERC721: approve caller is not owner nor approved for all" 264 | ); 265 | 266 | _tokenApprovals[tokenId] = to; 267 | emit Approval(owner, to, tokenId); 268 | } 269 | 270 | /** 271 | * @dev Gets the approved address for a token ID, or zero if no address set 272 | * Reverts if the token ID does not exist. 273 | * @param tokenId uint256 ID of the token to query the approval of 274 | * @return address currently approved for the given token ID 275 | */ 276 | function getApproved(uint256 tokenId) public view returns (address) { 277 | require(_exists(tokenId), "ERC721: approved query for nonexistent token"); 278 | return _tokenApprovals[tokenId]; 279 | } 280 | 281 | /** 282 | * @dev Sets or unsets the approval of a given operator 283 | * An operator is allowed to transfer all tokens of the sender on their behalf 284 | * @param to operator address to set the approval 285 | * @param approved representing the status of the approval to be set 286 | */ 287 | function setApprovalForAll(address to, bool approved) public { 288 | require(to != msg.sender, "ERC721: approve to caller"); 289 | _operatorApprovals[msg.sender][to] = approved; 290 | emit ApprovalForAll(msg.sender, to, approved); 291 | } 292 | 293 | /** 294 | * @dev Tells whether an operator is approved by a given owner 295 | * @param owner owner address which you want to query the approval of 296 | * @param operator operator address which you want to query the approval of 297 | * @return bool whether the given operator is approved by the given owner 298 | */ 299 | function isApprovedForAll(address owner, address operator) public view returns (bool) { 300 | return _operatorApprovals[owner][operator]; 301 | } 302 | 303 | /** 304 | * @dev Transfers the ownership of a given token ID to another address 305 | * Usage of this method is discouraged, use `safeTransferFrom` whenever possible 306 | * Requires the msg.sender to be the owner, approved, or operator 307 | * @param from current owner of the token 308 | * @param to address to receive the ownership of the given token ID 309 | * @param tokenId uint256 ID of the token to be transferred 310 | */ 311 | function transferFrom(address from, address to, uint256 tokenId) public { 312 | require(_isApprovedOrOwner(msg.sender, tokenId), "ERC721: transfer caller is not owner nor approved"); 313 | 314 | _transferFrom(from, to, tokenId); 315 | } 316 | 317 | /** 318 | * @dev Safely transfers the ownership of a given token ID to another address 319 | * If the target address is a contract, it must implement `onERC721Received`, 320 | * which is called upon a safe transfer, and return the magic value 321 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 322 | * the transfer is reverted. 323 | * Requires the msg.sender to be the owner, approved, or operator 324 | * @param from current owner of the token 325 | * @param to address to receive the ownership of the given token ID 326 | * @param tokenId uint256 ID of the token to be transferred 327 | */ 328 | function safeTransferFrom(address from, address to, uint256 tokenId) public { 329 | safeTransferFrom(from, to, tokenId, ""); 330 | } 331 | 332 | /** 333 | * @dev Safely transfers the ownership of a given token ID to another address 334 | * If the target address is a contract, it must implement `onERC721Received`, 335 | * which is called upon a safe transfer, and return the magic value 336 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 337 | * the transfer is reverted. 338 | * Requires the msg.sender to be the owner, approved, or operator 339 | * @param from current owner of the token 340 | * @param to address to receive the ownership of the given token ID 341 | * @param tokenId uint256 ID of the token to be transferred 342 | * @param _data bytes data to send along with a safe transfer check 343 | */ 344 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public { 345 | transferFrom(from, to, tokenId); 346 | require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); 347 | } 348 | 349 | /** 350 | * @dev Returns whether the specified token exists 351 | * @param tokenId uint256 ID of the token to query the existence of 352 | * @return bool whether the token exists 353 | */ 354 | function _exists(uint256 tokenId) internal view returns (bool) { 355 | address owner = _wizardsById[tokenId].owner; 356 | return owner != address(0); 357 | } 358 | 359 | /** 360 | * @dev Returns whether the given spender can transfer a given token ID 361 | * @param spender address of the spender to query 362 | * @param tokenId uint256 ID of the token to be transferred 363 | * @return bool whether the msg.sender is approved for the given token ID, 364 | * is an operator of the owner, or is the owner of the token 365 | */ 366 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { 367 | require(_exists(tokenId), "ERC721: operator query for nonexistent token"); 368 | address owner = ownerOf(tokenId); 369 | return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); 370 | } 371 | 372 | /** 373 | * @dev Internal function to burn a specific token 374 | * Reverts if the token does not exist 375 | * Deprecated, use _burn(uint256) instead. 376 | * @param owner owner of the token to burn 377 | * @param tokenId uint256 ID of the token being burned 378 | */ 379 | function _burn(address owner, uint256 tokenId) internal { 380 | require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own"); 381 | 382 | _clearApproval(tokenId); 383 | 384 | _ownedTokensCount[owner]--; 385 | // delete the entire object to recover the most gas 386 | delete _wizardsById[tokenId]; 387 | 388 | // required for ERC721 compatibility 389 | emit Transfer(owner, address(0), tokenId); 390 | } 391 | 392 | /** 393 | * @dev Internal function to burn a specific token 394 | * Reverts if the token does not exist 395 | * @param tokenId uint256 ID of the token being burned 396 | */ 397 | function _burn(uint256 tokenId) internal { 398 | _burn(ownerOf(tokenId), tokenId); 399 | } 400 | 401 | /** 402 | * @dev Internal function to transfer ownership of a given token ID to another address. 403 | * As opposed to transferFrom, this imposes no restrictions on msg.sender. 404 | * @param from current owner of the token 405 | * @param to address to receive the ownership of the given token ID 406 | * @param tokenId uint256 ID of the token to be transferred 407 | */ 408 | function _transferFrom(address from, address to, uint256 tokenId) internal { 409 | require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); 410 | require(to != address(0), "ERC721: transfer to the zero address"); 411 | 412 | _clearApproval(tokenId); 413 | 414 | _ownedTokensCount[from]--; 415 | _ownedTokensCount[to]++; 416 | 417 | _wizardsById[tokenId].owner = to; 418 | 419 | emit Transfer(from, to, tokenId); 420 | } 421 | 422 | /** 423 | * @dev Internal function to invoke `onERC721Received` on a target address 424 | * The call is not executed if the target address is not a contract 425 | * @param from address representing the previous owner of the given token ID 426 | * @param to target address that will receive the tokens 427 | * @param tokenId uint256 ID of the token to be transferred 428 | * @param _data bytes optional data to send along with the call 429 | * @return bool whether the call correctly returned the expected magic value 430 | */ 431 | function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) 432 | internal returns (bool) 433 | { 434 | if (!isContract(to)) { 435 | return true; 436 | } 437 | 438 | bytes4 retval = IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data); 439 | return (retval == _ERC721_RECEIVED); 440 | } 441 | 442 | /** 443 | * @dev Private function to clear current approval of a given token ID 444 | * @param tokenId uint256 ID of the token to be transferred 445 | */ 446 | function _clearApproval(uint256 tokenId) private { 447 | if (_tokenApprovals[tokenId] != address(0)) { 448 | _tokenApprovals[tokenId] = address(0); 449 | } 450 | } 451 | } 452 | 453 | 454 | 455 | 456 | /// @title WizardPresaleInterface 457 | /// @notice This interface represents the single method that the final tournament and master Wizard contracts 458 | /// will use to import the presale wizards when those contracts have been finalized a released on 459 | /// mainnet. Once all presale Wizards have been absorbed, this temporary pre-sale contract can be 460 | /// destroyed. 461 | contract WizardPresaleInterface { 462 | 463 | // See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-165.md on how 464 | // to calculate this 465 | bytes4 public constant _INTERFACE_ID_WIZARDPRESALE = 0x4df71efb; 466 | 467 | /// @notice This function is used to bring a presale Wizard into the final contracts. It can 468 | /// ONLY be called by the official gatekeeper contract (as set by the Owner of the presale 469 | /// contract). It does a number of things: 470 | /// 1. Check that the presale Wizard exists, and has not already been absorbed 471 | /// 2. Transfer the Eth used to create the presale Wizard to the caller 472 | /// 3. Mark the Wizard as having been absorbed, reclaiming the storage used by the presale info 473 | /// 4. Return the Wizard information (its owner, minting price, and elemental alignment) 474 | /// @param id the id of the presale Wizard to be absorbed 475 | function absorbWizard(uint256 id) external returns (address owner, uint256 power, uint8 affinity); 476 | 477 | /// @notice A convenience function that allows multiple Wizards to be moved to the final contracts 478 | /// simultaneously, works the same as the previous function, but in a batch. 479 | /// @param ids An array of ids indicating which presale Wizards are to be absorbed 480 | function absorbWizardMulti(uint256[] calldata ids) external 481 | returns (address[] memory owners, uint256[] memory powers, uint8[] memory affinities); 482 | 483 | function powerToCost(uint256 power) public pure returns (uint256 cost); 484 | function costToPower(uint256 cost) public pure returns (uint256 power); 485 | } 486 | 487 | 488 | 489 | /// @title WizardPresale - Making Cheeze Wizards available for sale! 490 | /// @notice Allows for the creation and sale of Cheeze Wizards before the final tournament 491 | /// contract has been reviewed and released on mainnet. There are three main types 492 | /// of Wizards that are managed by this contract: 493 | /// - Neutral Wizards: Available in unlimited quantities and all have the same 494 | /// innate power. Don't have a natural affinity for any particular elemental 495 | /// spell... or the corresponding weakness! 496 | /// - Elemental Wizards: Available in unlimited quantities, but with a steadily increasing 497 | /// power; the power of an Elemental Wizard is always _slightly_ higher than the power 498 | /// of the previously created Elemental Wizard. Each Elemental Wizard has an Elemental 499 | /// Affinity that gives it a power multiplier when using the associated spell, but also 500 | /// gives it a weakness for the opposing element. 501 | /// - Exclusive Wizards: Only available in VERY limited quantities, with a hard cap set at 502 | /// contract creation time. Exclusive Wizards can ONLY be created by the Guild Master 503 | /// address (the address that created this contract), and are assigned the first N 504 | /// Wizard IDs, starting with 1 (where N is the hard cap on Exclusive Wizards). The first 505 | /// non-exclusive Wizard is assigned the ID N+1. Exclusive Wizards have no starting 506 | /// affinity, and their owners much choose an affinity before they can be entered into a 507 | /// Battle. The affinity CAN NOT CHANGE once it has been selected. The power of Exclusive 508 | /// Wizards is not set by the Guild Master and is not required to follow any pattern (although 509 | /// it can't be lower than the power of Neutral Wizards). 510 | contract WizardPresale is WizardPresaleNFT, WizardPresaleInterface, WizardConstants { 511 | 512 | /// @dev The ratio between the cost of a Wizard (in wei) and the power of the wizard. 513 | /// power = cost / POWER_SCALE 514 | /// cost = power * POWER_SCALE 515 | uint256 private constant POWER_SCALE = 1000; 516 | 517 | /// @dev The unit conversion for tenths of basis points 518 | uint256 private constant TENTH_BASIS_POINTS = 100000; 519 | 520 | /// @dev The address used to create this smart contract, has permission to conjure Exclusive Wizards, 521 | /// set the gatekeeper address, and destroy this contract once the sale is finished and all Presale 522 | /// Wizards have been absorbed into the main contracts. 523 | address payable public guildmaster; 524 | 525 | /// @dev The start block and duration (in blocks) of the sale. 526 | /// ACT NOW! For a limited time only! 527 | uint256 public saleStartBlock; 528 | uint256 public saleDuration; 529 | 530 | /// @dev The cost of Neutral Wizards (in wei). 531 | uint256 public neutralWizardCost; 532 | 533 | /// @dev The cost of the _next_ Elemental Wizard (in wei); increases with each Elemental Wizard sold 534 | uint256 public elementalWizardCost; 535 | 536 | /// @dev The increment ratio in price between sequential Elemental Wizards, multiplied by 100k for 537 | /// greater granularity (0 == 0% increase, 100000 == 100% increase, 100 = 0.1% increase, etc.) 538 | /// NOTE: This is NOT percentage points, or basis points. It's tenths of a basis point. 539 | uint256 public elementalWizardIncrement; 540 | 541 | /// @dev The hard cap on how many Exclusive Wizards can be created 542 | uint256 public maxExclusives; 543 | 544 | /// @dev The ID number of the next Wizard to be created (Neutral or Elemental) 545 | uint256 public nextWizardId; 546 | 547 | /// @dev The address of the Gatekeeper for the tournament, initially set to address(0). 548 | /// To be set by the Guild Master when the final Tournament Contract is deployed on mainnet 549 | address payable public gatekeeper; 550 | 551 | /// @notice Emitted whenever the start of the sale changes. 552 | event StartBlockChanged(uint256 oldStartBlock, uint256 newStartBlock); 553 | 554 | /// @param startingCost The minimum cost of a Wizard, used as the price for all Neutral Wizards, and the 555 | /// cost of the first Elemental Wizard. Also used as a minimum value for Exclusive Wizards. 556 | /// @param costIncremement The rate (in tenths of a basis point) at which the price of Elemental Wizards increases 557 | /// @param exclusiveCount The hard cap on Exclusive Wizards, also dictates the ID of the first non-Exclusive 558 | /// @param startBlock The starting block of the presale. 559 | /// @param duration The duration of the presale. Not changeable! 560 | constructor(uint128 startingCost, 561 | uint16 costIncremement, 562 | uint256 exclusiveCount, 563 | uint128 startBlock, 564 | uint128 duration) public 565 | { 566 | require(startBlock > block.number, "Start must be greater than current block"); 567 | 568 | guildmaster = msg.sender; 569 | saleStartBlock = startBlock; 570 | saleDuration = duration; 571 | neutralWizardCost = startingCost; 572 | elementalWizardCost = startingCost; 573 | elementalWizardIncrement = costIncremement; 574 | maxExclusives = exclusiveCount; 575 | nextWizardId = exclusiveCount + 1; 576 | 577 | _registerInterface(_INTERFACE_ID_WIZARDPRESALE); 578 | } 579 | 580 | /// @dev Throws if called by any account other than the gatekeeper. 581 | modifier onlyGatekeeper() { 582 | require(msg.sender == gatekeeper, "Must be gatekeeper"); 583 | _; 584 | } 585 | 586 | /// @dev Throws if called by any account other than the guildmaster. 587 | modifier onlyGuildmaster() { 588 | require(msg.sender == guildmaster, "Must be guildmaster"); 589 | _; 590 | } 591 | 592 | /// @dev Checks to see that the current block number is within the range 593 | /// [saleStartBlock, saleStartBlock + saleDuraction) indicating that the sale 594 | /// is currently active 595 | modifier onlyDuringSale() { 596 | // The addtion of start and duration can't overflow since they can only be set from 597 | // 128-bit arguments. 598 | require(block.number >= saleStartBlock, "Sale not open yet"); 599 | require(block.number < saleStartBlock + saleDuration, "Sale closed"); 600 | _; 601 | } 602 | 603 | /// @dev Sets the address of the Gatekeeper contract once the final Tournament contract is live. 604 | /// Can only be set once. 605 | /// @param gc The gatekeeper address to set 606 | function setGatekeeper(address payable gc) external onlyGuildmaster { 607 | require(gatekeeper == address(0) && gc != address(0), "Can only set once and must not be 0"); 608 | gatekeeper = gc; 609 | } 610 | 611 | /// @dev Updates the start block of the sale. The sale can only be postponed; it can't be made earlier. 612 | /// @param newStart the new start block. 613 | function postponeSale(uint128 newStart) external onlyGuildmaster { 614 | require(block.number < saleStartBlock, "Sale start time only adjustable before previous start time"); 615 | require(newStart > saleStartBlock, "New start time must be later than previous start time"); 616 | 617 | emit StartBlockChanged(saleStartBlock, newStart); 618 | 619 | saleStartBlock = newStart; 620 | } 621 | 622 | /// @dev Returns true iff the sale is currently active 623 | function isDuringSale() external view returns (bool) { 624 | return (block.number >= saleStartBlock && block.number < saleStartBlock + saleDuration); 625 | } 626 | 627 | /// @dev Convenience method for getting a presale wizard's data 628 | /// @param id The wizard id 629 | function getWizard(uint256 id) public view returns (address owner, uint88 power, uint8 affinity) { 630 | Wizard memory wizard = _wizardsById[id]; 631 | (owner, power, affinity) = (wizard.owner, wizard.power, wizard.affinity); 632 | require(wizard.owner != address(0), "Wizard does not exist"); 633 | } 634 | 635 | /// @param cost The price of the wizard in wei 636 | /// @return The power of the wizard (left as uint256) 637 | function costToPower(uint256 cost) public pure returns (uint256 power) { 638 | return cost / POWER_SCALE; 639 | } 640 | 641 | /// @param power The power of the wizard 642 | /// @return The cost of the wizard in wei 643 | function powerToCost(uint256 power) public pure returns (uint256 cost) { 644 | return power * POWER_SCALE; 645 | } 646 | 647 | /// @notice This function is used to bring a presale Wizard into the final contracts. It can 648 | /// ONLY be called by the official gatekeeper contract (as set by the Owner of the presale 649 | /// contract). It does a number of things: 650 | /// 1. Check that the presale Wizard exists, and has not already been absorbed 651 | /// 2. Transfer the Eth used to create the presale Wizard to the caller 652 | /// 3. Mark the Wizard as having been absorbed, reclaiming the storage used by the presale info 653 | /// 4. Return the Wizard information (its owner, minting price, and elemental alignment) 654 | /// @param id the id of the presale Wizard to be absorbed 655 | function absorbWizard(uint256 id) external onlyGatekeeper returns (address owner, uint256 power, uint8 affinity) { 656 | (owner, power, affinity) = getWizard(id); 657 | 658 | // Free up the storage used by this wizard 659 | _burn(owner, id); 660 | 661 | // send the price paid to the gatekeeper to be used in the tournament prize pool 662 | msg.sender.transfer(powerToCost(power)); 663 | } 664 | 665 | /// @notice A convenience function that allows multiple Wizards to be moved to the final contracts 666 | /// simultaneously, works the same as the previous function, but in a batch. 667 | /// @param ids An array of ids indicating which presale Wizards are to be absorbed 668 | function absorbWizardMulti(uint256[] calldata ids) external onlyGatekeeper 669 | returns (address[] memory owners, uint256[] memory powers, uint8[] memory affinities) 670 | { 671 | // allocate arrays 672 | owners = new address[](ids.length); 673 | powers = new uint256[](ids.length); 674 | affinities = new uint8[](ids.length); 675 | 676 | // The total eth to send (sent in a batch to save gas) 677 | uint256 totalTransfer; 678 | 679 | // Put the data for each Wizard into the returned arrays 680 | for (uint256 i = 0; i < ids.length; i++) { 681 | (owners[i], powers[i], affinities[i]) = getWizard(ids[i]); 682 | 683 | // Free up the storage used by this wizard 684 | _burn(owners[i], ids[i]); 685 | 686 | // add the amount to transfer 687 | totalTransfer += powerToCost(powers[i]); 688 | } 689 | 690 | // Send all the eth together 691 | msg.sender.transfer(totalTransfer); 692 | } 693 | 694 | /// @dev Internal function to create a new Wizard; reverts if the Wizard ID is taken. 695 | /// NOTE: This function heavily depends on the internal format of the Wizard struct 696 | /// and should always be reassessed if anything about that structure changes. 697 | /// @param tokenId ID of the new Wizard 698 | /// @param owner The address that will own the newly conjured Wizard 699 | /// @param power The power level associated with the new Wizard 700 | /// @param affinity The elemental affinity of the new Wizard 701 | function _createWizard(uint256 tokenId, address owner, uint256 power, uint8 affinity) internal { 702 | require(!_exists(tokenId), "Can't reuse Wizard ID"); 703 | require(owner != address(0), "Owner address must exist"); 704 | require(power > 0, "Wizard power must be non-0"); 705 | require(power < (1<<88), "Wizard power must fit in 88 bits of storage"); 706 | require(affinity <= MAX_ELEMENT, "Invalid elemental affinity"); 707 | 708 | // Create the Wizard! 709 | _wizardsById[tokenId] = Wizard(affinity, uint88(power), owner); 710 | _ownedTokensCount[owner]++; 711 | 712 | // Tell the world! 713 | emit Transfer(address(0), owner, tokenId); 714 | emit WizardSummoned(tokenId, affinity, power); 715 | } 716 | 717 | /// @dev A private utility function that refunds any overpayment to the sender; smart 718 | /// enough to only send the excess if the amount we are returning is more than the 719 | /// cost of sending it! 720 | /// @dev Warning! This does not check for underflows (msg.value < actualPrice) - so 721 | /// be sure to call this with correct values! 722 | /// @param actualPrice the actual price owed 723 | function _transferRefund(uint256 actualPrice) private { 724 | uint256 refund = msg.value - actualPrice; 725 | 726 | // Make sure the amount we're trying to refund is less than the actual cost of sending it! 727 | // See https://github.com/ethereum/wiki/wiki/Subtleties for magic values costs. We can 728 | // safely ignore the 25000 additional gas cost for new accounts, as msg.sender is 729 | // guaranteed to exist at this point! 730 | if (refund > (tx.gasprice * (9000+700))) { 731 | msg.sender.transfer(refund); 732 | } 733 | } 734 | 735 | /// @notice Conjures an Exclusive Wizard with a specific element and ID. This can only be done by 736 | /// the Guildmaster, who still has to pay for the power imbued in that Wizard! The power level 737 | /// is inferred by the amount of Eth sent. MUST ONLY BE USED FOR EXTERNAL OWNER ADDRESSES. 738 | /// @param id The ID of the new Wizard; must be in the Exclusive range, and can't already be allocated 739 | /// @param owner The address which will own the new Wizard 740 | /// @param affinity The elemental affinity of the new Wizard, can be ELEMENT_NOTSET for Exclusives! 741 | function conjureExclusiveWizard(uint256 id, address owner, uint8 affinity) public payable onlyGuildmaster { 742 | require(id > 0 && id <= maxExclusives, "Invalid exclusive ID"); 743 | _createWizard(id, owner, costToPower(msg.value), affinity); 744 | } 745 | 746 | /// @notice Same as conjureExclusiveWizard(), but reverts if the owner address is a smart 747 | /// contract that is not ERC-721 aware. 748 | /// @param id The ID of the new Wizard; must be in the Exclusive range, and can't already be allocated 749 | /// @param owner The address which will own the new Wizard 750 | /// @param affinity The elemental affinity of the new Wizard, can be ELEMENT_NOTSET for Exclusives! 751 | function safeConjureExclusiveWizard(uint256 id, address owner, uint8 affinity) external payable onlyGuildmaster { 752 | conjureExclusiveWizard(id, owner, affinity); 753 | require(_checkOnERC721Received(address(0), owner, id, ""), "Must support erc721"); 754 | } 755 | 756 | /// @notice Allows for the batch creation of Exclusive Wizards. Same rules apply as above, but the 757 | /// powers are specified instead of being inferred. The message still needs to have enough 758 | /// value to pay for all the newly conjured Wizards! MUST ONLY BE USED FOR EXTERNAL OWNER ADDRESSES. 759 | /// @param ids An array of IDs of the new Wizards 760 | /// @param owners An array of owners 761 | /// @param powers An array of power levels 762 | /// @param affinities An array of elemental affinities 763 | function conjureExclusiveWizardMulti( 764 | uint256[] calldata ids, 765 | address[] calldata owners, 766 | uint256[] calldata powers, 767 | uint8[] calldata affinities) external payable onlyGuildmaster 768 | { 769 | // Ensure the arrays are all of the same length 770 | require( 771 | ids.length == owners.length && 772 | owners.length == powers.length && 773 | owners.length == affinities.length, 774 | "Must have equal array lengths" 775 | ); 776 | 777 | uint256 totalPower = 0; 778 | 779 | for (uint256 i = 0; i < ids.length; i++) { 780 | require(ids[i] > 0 && ids[i] <= maxExclusives, "Invalid exclusive ID"); 781 | require(affinities[i] <= MAX_ELEMENT, "Must choose a valid elemental affinity"); 782 | 783 | _createWizard(ids[i], owners[i], powers[i], affinities[i]); 784 | 785 | totalPower += powers[i]; 786 | } 787 | 788 | // Ensure that the message includes enough eth to cover the total power of all Wizards 789 | // If this check fails, all the Wizards that we just created will be deleted, and we'll just 790 | // have wasted a bunch of gas. Don't be dumb, Guildmaster! 791 | // If the guildMaster has managed to overflow totalPower, well done! 792 | require(powerToCost(totalPower) <= msg.value, "Must pay for power in all Wizards"); 793 | 794 | // We don't return "change" if the caller overpays, because the caller is the Guildmaster and 795 | // shouldn't be dumb like that. How many times do I have to say it? Don't be dumb, Guildmaster! 796 | } 797 | 798 | /// @notice Sets the affinity for a Wizard that doesn't already have its elemental affinity chosen. 799 | /// Only usable for Exclusive Wizards (all non-Exclusives must have their affinity chosen when 800 | /// conjured.) Even Exclusives can't change their affinity once it's been chosen. 801 | /// @param wizardId The id of the wizard 802 | /// @param newAffinity The new affinity of the wizard 803 | function setAffinity(uint256 wizardId, uint8 newAffinity) external { 804 | require(newAffinity > ELEMENT_NOTSET && newAffinity <= MAX_ELEMENT, "Must choose a valid affinity"); 805 | (address owner, , uint8 affinity) = getWizard(wizardId); 806 | require(msg.sender == owner, "Affinity can only be set by owner"); 807 | require(affinity == ELEMENT_NOTSET, "Affinity can only be chosen once"); 808 | 809 | _wizardsById[wizardId].affinity = newAffinity; 810 | 811 | // Tell the world this wizards now has an affinity! 812 | emit WizardAlignmentAssigned(wizardId, newAffinity); 813 | } 814 | 815 | /// @dev An internal convenience function used by conjureWizard and conjureWizardMulti that takes care 816 | /// of the work that is shared between them. 817 | /// The use of tempElementalWizardCost and updatedElementalWizardCost deserves some explanation here. 818 | /// Using elementalWizardCost directly would be very expensive in the case where this function is 819 | /// called repeatedly by conjureWizardMulti. Buying an elemental wizard would update the elementalWizardCost 820 | /// each time through this function _which would cost 5000 gas each time_. Of course, we don't actually 821 | /// need to store the new value each time, only once at the very end. So we go through this very annoying 822 | /// process of passing the elementalWizardCost in as an argument (tempElementalWizardCost) and returning 823 | /// the updated value as a return value (updatedElementalWizardCost). It's enough to make one want 824 | /// tear one's hair out. But! What's done is done, and hopefully SOMEONE will realize how much trouble 825 | /// we went to to save them _just that little bit_ of gas cost when they decided to buy a schwack of 826 | /// Wizards. 827 | function _conjureWizard( 828 | uint256 wizardId, 829 | address owner, 830 | uint8 affinity, 831 | uint256 tempElementalWizardCost) private 832 | returns (uint256 wizardCost, uint256 updatedElementalWizardCost) 833 | { 834 | // Check for a valid elemental affinity 835 | require(affinity > ELEMENT_NOTSET && affinity <= MAX_ELEMENT, "Non-exclusive Wizards need a real affinity"); 836 | 837 | updatedElementalWizardCost = tempElementalWizardCost; 838 | 839 | // Determine the price 840 | if (affinity == ELEMENT_NEUTRAL) { 841 | wizardCost = neutralWizardCost; 842 | } else { 843 | wizardCost = updatedElementalWizardCost; 844 | 845 | // Update the elemental Wizard cost 846 | // NOTE: This math can't overflow because the total Ether supply in wei is well less than 847 | // 2^128. Multiplying a price in wei by some number <100k can't possibly overflow 256 bits. 848 | updatedElementalWizardCost += (updatedElementalWizardCost * elementalWizardIncrement) / TENTH_BASIS_POINTS; 849 | } 850 | 851 | // Bring the new Wizard into existence! 852 | _createWizard(wizardId, owner, costToPower(wizardCost), affinity); 853 | } 854 | 855 | /// @notice This is it folks, the main event! The way for the world to get new Wizards! Does 856 | /// pretty much what it says on the box: Let's you conjure a new Wizard with a specified 857 | /// elemental affinity. The call must include enough eth to cover the cost of the new 858 | /// Wizard, and any excess is refunded. The power of the Wizard is derived from 859 | /// the sale price. YOU CAN NOT PAY EXTRA TO GET MORE POWER. (But you always have the option 860 | /// to conjure some more Wizards!) Returns the ID of the newly conjured Wizard. 861 | /// @param affinity The elemental affinity you want for your wizard. 862 | function conjureWizard(uint8 affinity) external payable onlyDuringSale returns (uint256 wizardId) { 863 | 864 | wizardId = nextWizardId; 865 | nextWizardId++; 866 | 867 | uint256 wizardCost; 868 | 869 | (wizardCost, elementalWizardCost) = _conjureWizard(wizardId, msg.sender, affinity, elementalWizardCost); 870 | 871 | require(msg.value >= wizardCost, "Not enough eth to pay"); 872 | 873 | // Refund any overpayment 874 | _transferRefund(wizardCost); 875 | 876 | // Ensure the Wizard is being assigned to an ERC-721 aware address (either an external address, 877 | // or a smart contract that implements onERC721Reived()) 878 | require(_checkOnERC721Received(address(0), msg.sender, wizardId, ""), "Must support erc721"); 879 | } 880 | 881 | /// @notice A convenience function that allows you to get a whole bunch of Wizards at once! You know how 882 | /// there's "a pride of lions", "a murder of crows", and "a parliament of owls"? Well, with this 883 | /// here function you can conjure yourself "a stench of Cheeze Wizards"! 884 | /// @dev This function is careful to bundle all of the external calls (the refund and onERC721Received) 885 | /// at the end of the function to limit the risk of reentrancy attacks. 886 | /// @param affinities the elements of the wizards 887 | function conjureWizardMulti(uint8[] calldata affinities) external payable onlyDuringSale 888 | returns (uint256[] memory wizardIds) 889 | { 890 | // allocate result array 891 | wizardIds = new uint256[](affinities.length); 892 | 893 | uint256 totalCost = 0; 894 | 895 | // We take these two storage variables, and turn them into local variables for the course 896 | // of this loop to save about 10k gas per wizard. It's kind of ugly, but that's a lot of 897 | // gas! Won't somebody please think of the children!! 898 | uint256 tempWizardId = nextWizardId; 899 | uint256 tempElementalWizardCost = elementalWizardCost; 900 | 901 | for (uint256 i = 0; i < affinities.length; i++) { 902 | wizardIds[i] = tempWizardId; 903 | tempWizardId++; 904 | 905 | uint256 wizardCost; 906 | 907 | (wizardCost, tempElementalWizardCost) = _conjureWizard( 908 | wizardIds[i], 909 | msg.sender, 910 | affinities[i], 911 | tempElementalWizardCost); 912 | 913 | totalCost += wizardCost; 914 | } 915 | 916 | elementalWizardCost = tempElementalWizardCost; 917 | nextWizardId = tempWizardId; 918 | 919 | // check to see if there's enough eth 920 | require(msg.value >= totalCost, "Not enough eth to pay"); 921 | 922 | // Ensure the Wizard is being assigned to an ERC-721 aware address (either an external address, 923 | // or a smart contract that implements onERC721Received()). We unwind the logic of _checkOnERC721Received 924 | // because called address.isContract() every time through this loop can get reasonably expensive. We do 925 | // need to call this function for each token created, however, because it's allowed for an ERC-721 receiving 926 | // contract to reject the transfer based on the properties of the token. 927 | if (isContract(msg.sender)) { 928 | for (uint256 i = 0; i < wizardIds.length; i++) { 929 | bytes4 retval = IERC721Receiver(msg.sender).onERC721Received(msg.sender, address(0), wizardIds[i], ""); 930 | require(retval == _ERC721_RECEIVED, "Contract owner didn't accept ERC721 transfer"); 931 | } 932 | } 933 | 934 | // Refund any excess funds 935 | _transferRefund(totalCost); 936 | } 937 | 938 | /// @dev Transfers the current balance to the owner and terminates the contract. 939 | function destroy() external onlyGuildmaster { 940 | selfdestruct(guildmaster); 941 | } 942 | } 943 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # Welcome to Cheeze Wizards 3 | 4 | Cheeze Wizards is the world's first blockchain battle royale (with cheese) 5 | 6 | One of blockchain’s best qualities is the freedom for anyone to build on top of their favorite dapps––all without having to ask for parmesan! 7 | 8 | We thought we'd lean in: this repo contains final source code for the five core Cheeze Wizards smart contracts 9 | 10 | We tried to make everything easy so you can build awesome things on top 11 | 12 | 13 | # Cheeze Wizards Resources 14 | 15 | ## Design Assets 16 | 17 | Key design assets and an awesome brand guideline PDF: 18 | 19 | https://drive.google.com/a/axiomzen.co/file/d/1-N-SLJYYLzeN5pMEXPm0vqpU5Kyhx71_/view?usp=sharing 20 | 21 | 22 | ## Smart Contracts 23 | 24 | Here are the addresses where these contracts have been deployed on Rinkeby 25 | 26 | ``` 27 | WizardPresale: 0xd8E4C31D8EB7baD28909a3D2E2dCa6AACDaB1563 28 | WizardGuild: 0xd3d2Cc1a89307358DB3e81Ca6894442b2DB36CE8 29 | GateKeeper: 0xF46aEEF279A6d5A411E16D87D3767fDa0cEC320E 30 | BasicTournament: 0xA90c237A6D68978a5cBD2e6dE375dbe242821698 31 | DuelResolver: 0xB789e4047f5DF6cf5Fdd035AF44205092a275d33 32 | ``` 33 | 34 | 35 | ## Cheeze Wizards API 36 | 37 | The Cheeze Wizards API is hosted by our partner Alchemy: 38 | * https://docs.alchemyapi.io/docs/cheeze-wizards-api 39 | 40 | 41 | ## Jumpstart 42 | 43 | Ideas for what to build: 44 | * https://medium.com/dapperlabs/a-list-of-things-you-can-build-on-top-of-cheeze-wizards-da9d94851121 45 | 46 | CheezyVerse homepage: 47 | * https://www.cheezewizards.com/cheezyverse 48 | 49 | Welcome to Cheeze Wizards and best of luck! 50 | --------------------------------------------------------------------------------