/** * Source Code first verified at https://etherscan.io on Tuesday, April 30, 2019 (UTC) */ pragma solidity ^0.5.5; //DCC VERSION 5 /// @title Interface for contracts conforming to ERC-721: Non-Fungible Tokens /// @author Dieter Shirley <[email protected]> (https://github.com/dete) interface ERC721 { // Required methods function totalSupply() external view returns (uint256 total); function balanceOf(address _owner) external view returns (uint256 balance); function ownerOf(uint256 _tokenId) external view returns (address owner); function exists(uint256 _tokenId) external view returns (bool _exists); function approve(address _to, uint256 _tokenId) external; function transfer(address _to, uint256 _tokenId) external; function transferFrom(address _from, address _to, uint256 _tokenId) external; // Events event Transfer(address from, address to, uint256 tokenId); event Approval(address owner, address approved, uint256 tokenId); function supportsInterface(bytes4 _interfaceID) external view returns (bool); } /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md */ contract ERC721Metadata is ERC721 { function name() external view returns (string memory _name); function symbol() external view returns (string memory _symbol); function tokenURI(uint256 _tokenId) public view returns (string memory); } /** * These are required for the DCC contract to interact with the WLC contract */ interface WLCCompatible { function getWLCReward(uint256 _boughtWLCAmount, address _owner) external returns (uint256 _remaining); function setWLCParams(address _address, uint256 _reward) external; function resetWLCParams() external; function getForWLC(address _owner) external; function getWLCRewardAmount() external view returns (uint256 _amount); function getWLCAddress() external view returns (address _address); } contract DreamCarToken3 is ERC721, ERC721Metadata, WLCCompatible { string internal constant tokenName = 'DreamCarCoin3'; string internal constant tokenSymbol = 'DCC3'; uint8 public constant decimals = 0; //ERC721 VARIABLES //the total count of wishes uint256 internal totalTokenSupply; //this address is the CEO address payable public CEO; bytes4 constant InterfaceSignature_ERC165 = bytes4(keccak256('supportsInterface(bytes4)')); bytes4 constant InterfaceSignature_ERC721 = bytes4(keccak256('name()')) ^ bytes4(keccak256('symbol()')) ^ bytes4(keccak256('totalTokenSupply()')) ^ bytes4(keccak256('balanceOf(address)')) ^ bytes4(keccak256('ownerOf(uint256)')) ^ bytes4(keccak256('approve(address,uint256)')) ^ bytes4(keccak256('transfer(address,uint256)')) ^ bytes4(keccak256('transferFrom(address,address,uint256)')) ^ bytes4(keccak256('tokensOfOwner(address)')) ^ bytes4(keccak256('tokenMetadata(uint256,string)')); // Mapping from owner to number of owned tokens //mapping (address => uint256) internal tokensBalanceOf; // Mapping from token ID to owner mapping (uint256 => address) internal tokenOwner; // Optional mapping for token URIs mapping(uint256 => string) internal tokenURIs; //TOKEN SPECIFIC VARIABLES mapping (address => uint256) internal tokenBallanceOf; //Token price in WEI uint256 public tokenPrice; //A list of price admins; they can change price, in addition to the CEO address[] public priceAdmins; //Next id that will be assigned to token uint256 internal nextTokenId = 1; //The winning token id uint256 public winningTokenId = 0; //The winner's address, it will be empty, until the reward is claimed address public winnerAddress; //WLC CONTRACT INTERACTION VARIABLES //WLC tokens in a single purchase to earn a DCC token uint256 internal WLCRewardAmount; //WLC deployed contract address address internal WLCAdress; //ERC721 FUNCTIONS IMPLEMENTATIONS function supportsInterface(bytes4 _interfaceID) external view returns (bool) { return ((_interfaceID == InterfaceSignature_ERC165) || (_interfaceID == InterfaceSignature_ERC721)); } /** * Gets the total amount of tokens stored by the contract * @return uint256 representing the total amount of tokens */ function totalSupply() public view returns (uint256 total) { return totalTokenSupply; } /** * Gets the balance of the specified address * @param _owner address to query the balance of * @return uint256 representing the amount owned by the passed address */ function balanceOf(address _owner) public view returns (uint256 _balance) { return tokenBallanceOf[_owner]; } /** * Gets the owner of the specified token ID * @param _tokenId uint256 ID of the token to query the owner of * @return owner address currently marked as the owner of the given token ID */ function ownerOf(uint256 _tokenId) public view returns (address _owner) { return tokenOwner[_tokenId]; } /** * Returns whether the specified token exists * @param _tokenId uint256 ID of the token to query the existence of * @return whether the token exists */ function exists(uint256 _tokenId) public view returns (bool) { address owner = tokenOwner[_tokenId]; return owner != address(0); } /** * Not necessary in the contract */ function transfer(address _to, uint256 _tokenId) external { } /** * Not necessary in the contract */ function approve(address _to, uint256 _tokenId) external { } /** * Not necessary in the contract - reverts */ function transferFrom(address _from, address _to, uint256 _tokenId) external { } /** * Internal function to set the token URI for a given token * Reverts if the token ID does not exist * @param _tokenId uint256 ID of the token to set its URI * @param _uri string URI to assign */ function _setTokenURI(uint256 _tokenId, string storage _uri) internal { require(exists(_tokenId)); tokenURIs[_tokenId] = _uri; } //ERC721Metadata FUNCTIONS IMPLEMENTATIONS /** * Gets the token name * @return string representing the token name */ function name() external view returns (string memory _name) { return tokenName; } /** * Gets the token symbol * @return string representing the token symbol */ function symbol() external view returns (string memory _symbol) { return tokenSymbol; } /** * Returns an URI for a given token ID * Throws if the token ID does not exist. May return an empty string. * @param _tokenId uint256 ID of the token to query */ function tokenURI(uint256 _tokenId) public view returns (string memory) { require(exists(_tokenId)); return tokenURIs[_tokenId]; } //TOKEN SPECIFIC FUNCTIONS event Buy(address indexed from, uint256 amount, uint256 fromTokenId, uint256 toTokenId); event RewardIsClaimed(address indexed from, uint256 tokenId); event WinnerIsChosen(address indexed from, uint256 tokenId); /** * Ensures that the caller of the function is the CEO of contract */ modifier onlyCEO { require(msg.sender == CEO, 'You need to be the CEO to do that!'); _; } /** * Constructor of the contract * @param _ceo address the CEO (owner) of the contract */ constructor (address payable _ceo) public { CEO = _ceo; totalTokenSupply = 20000; tokenPrice = 41501723917762739; // (if eth = 156.62USD, 6.5 USD for token) } /** * Gets the last existing token ids * @return uint256 the id of the token */ function lastTokenId() public view returns (uint256 tokenId) { return nextTokenId - 1; } /** * Sets a new price for the tokensExchangedBy * @param _newPrice uint256 the new price in WEI */ function setTokenPriceInWEI(uint256 _newPrice) public { bool transactionAllowed = false; if (msg.sender == CEO) { transactionAllowed = true; } else { for (uint256 i = 0; i < priceAdmins.length; i++) { if (msg.sender == priceAdmins[i]) { transactionAllowed = true; break; } } } require((transactionAllowed == true), 'You cannot do that!'); tokenPrice = _newPrice; } /** * Add a new price admin address to the list * @param _newPriceAdmin address the address of the new price admin */ function addPriceAdmin(address _newPriceAdmin) onlyCEO public { priceAdmins.push(_newPriceAdmin); } /** * Remove existing price admin address from the list * @param _existingPriceAdmin address the address of the existing price admin */ function removePriceAdmin(address _existingPriceAdmin) onlyCEO public { for (uint256 i = 0; i < priceAdmins.length; i++) { if (_existingPriceAdmin == priceAdmins[i]) { delete priceAdmins[i]; break; } } } /** * Adds the specified number of tokens to the specified address * Internal method, used when creating new tokens * @param _to address The address, which is going to own the tokens * @param _amount uint256 The number of tokens */ function _addTokensToAddress(address _to, uint256 _amount) internal { for (uint256 i = 0; i < _amount; i++) { tokenOwner[nextTokenId + i] = _to; } tokenBallanceOf[_to] += _amount; nextTokenId += _amount; } /** * Checks if the specified token is owned by the transaction sender */ function ensureAddressIsTokenOwner(address _owner, uint256 _tokenId) internal view { require(balanceOf(_owner) >= 1, 'You do not own any tokens!'); require(tokenOwner[_tokenId] == _owner, 'You do not own this token!'); } /** * Generates a random number between 1 and totalTokenSupply variable * This is used to choose the winning token id */ function getRandomNumber() internal view returns (uint16) { return uint16( uint256( keccak256( abi.encodePacked(block.timestamp, block.difficulty, block.number) ) )%totalTokenSupply ) + 1; } /** * Chooses a winning token id, if all tokens are purchased */ function chooseWinner() internal { if ((nextTokenId - 1) == totalTokenSupply) { winningTokenId = getRandomNumber(); emit WinnerIsChosen(tokenOwner[winningTokenId], winningTokenId); } } /** * Scales the amount of tokens in a purchase, to ensure it will be less or equal to the amount of unsold tokens * If there are no tokens left, it will return 0 * @param _amount uint256 the amout of tokens in the purchase attempt * @return _exactAmount uint256 */ function scalePurchaseTokenAmountToMatchRemainingTokens(uint256 _amount) internal view returns (uint256 _exactAmount) { if (nextTokenId + _amount - 1 > totalTokenSupply) { _amount = totalTokenSupply - nextTokenId + 1; } return _amount; } /** * Buy new tokens with ETH * Calculates the nubmer of tokens for the given ETH amount * Creates the new tokens when they are purchased * Returns the excessive ETH (if any) to the transaction sender */ function buy() payable public { require(msg.value >= tokenPrice, "You did't send enough ETH"); uint256 amount = scalePurchaseTokenAmountToMatchRemainingTokens(msg.value / tokenPrice); require(amount > 0, "Not enough tokens are available for purchase!"); _addTokensToAddress(msg.sender, amount); emit Buy(msg.sender, amount, nextTokenId - amount, nextTokenId - 1); //transfer ETH to CEO CEO.transfer((amount * tokenPrice)); //returns excessive ETH msg.sender.transfer(msg.value - (amount * tokenPrice)); chooseWinner(); } /** * Allows user to destroy a specified token * This would allow a user to claim his prize for the destroyed token * @param _tokenId uint256 ID of the token */ function claimReward(uint256 _tokenId) public { require(winningTokenId > 0, "The is not winner yet!"); require(_tokenId == winningTokenId, "This token is not the winner!"); ensureAddressIsTokenOwner(msg.sender, _tokenId); winnerAddress = msg.sender; emit RewardIsClaimed(msg.sender, _tokenId); } //WLC INTERACTION FUNCTIONS /** * Allows the CEO to set the address and the reward values for a connected WishListToken * @param _address address the address of the deployed contract * @param _reward uint256 how many tokens need to be bought in a single transaction to the one DCC token */ function setWLCParams(address _address, uint256 _reward) public onlyCEO { WLCAdress = _address; WLCRewardAmount = _reward; } /** * Allows the CEO to revmove a connected WishListToken * This revokes the reward and exchange functions */ function resetWLCParams() public onlyCEO { WLCAdress = address(0); WLCRewardAmount = 0; } /** * How many WLC tokens need to be bought in a single transaction to the one DCC token * @return _amount uint256 */ function getWLCRewardAmount() public view returns (uint256 _amount) { return WLCRewardAmount; } /** * The address of the deployed WLC contract * @return _address address */ function getWLCAddress() public view returns (address _address) { return WLCAdress; } /** * Allows the buyer of at least the number of WLC tokens, specified in WLCRewardAmount * to receive a DCC as a bonus. * This can only be called by the deployed WLC contract, by the address specified in WLCAdress * @param _boughtWLCAmount uint256 the number of bought WLC tokens * @param _owner address the address of the buyer */ function getWLCReward(uint256 _boughtWLCAmount, address _owner) public returns (uint256 _remaining) { if (WLCAdress != address(0) && WLCRewardAmount > 0 && _boughtWLCAmount >= WLCRewardAmount) { require(WLCAdress == msg.sender, "You cannot invoke this function directly!"); uint256 DCCAmount = scalePurchaseTokenAmountToMatchRemainingTokens(_boughtWLCAmount / WLCRewardAmount); if (DCCAmount > 0) { _addTokensToAddress(_owner, DCCAmount); emit Buy(_owner, DCCAmount, nextTokenId - DCCAmount, nextTokenId - 1); chooseWinner(); return _boughtWLCAmount - (DCCAmount * WLCRewardAmount); } } return _boughtWLCAmount; } /** * Allows an onwer of WLC token to excange it for DCC token * This can only be called by the deployed WLC contract, by the address specified in WLCAdress * @param _owner address the address of the exchanger */ function getForWLC(address _owner) public { require(WLCAdress == msg.sender, "You cannot invoke this function directly!"); require(nextTokenId <= totalTokenSupply, "Not enough tokens are available for purchase!"); _addTokensToAddress(_owner, 1); emit Buy(_owner, 1, nextTokenId - 1, nextTokenId - 1); chooseWinner(); } }