├── README.md └── contracts ├── BEP721URIStorage.sol ├── DigitalGolems.sol ├── Card.sol └── BEP721.sol /README.md: -------------------------------------------------------------------------------- 1 | DIG 2 | -------------------------------------------------------------------------------- /contracts/BEP721URIStorage.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "./BEP721.sol"; 6 | 7 | /** 8 | * @dev ERC721 token with storage based token URI management. 9 | */ 10 | abstract contract BEP721URIStorage is BEP721 { 11 | using Strings for uint256; 12 | 13 | // Optional mapping for token URIs 14 | mapping(uint256 => string) private _tokenURIs; 15 | 16 | /** 17 | * @dev See {IERC721Metadata-tokenURI}. 18 | */ 19 | function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { 20 | require(_exists(tokenId), "URI of nonexistent token"); 21 | 22 | string memory _tokenURI = _tokenURIs[tokenId]; 23 | string memory base = _baseURI(); 24 | 25 | // If there is no base URI, return the token URI. 26 | if (bytes(base).length == 0) { 27 | return _tokenURI; 28 | } 29 | // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). 30 | if (bytes(_tokenURI).length > 0) { 31 | return string(abi.encodePacked(base, _tokenURI)); 32 | } 33 | 34 | return super.tokenURI(tokenId); 35 | } 36 | 37 | /** 38 | * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. 39 | * 40 | * Requirements: 41 | * 42 | * - `tokenId` must exist. 43 | */ 44 | function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { 45 | require(_exists(tokenId), "URI of nonexistent token"); 46 | _tokenURIs[tokenId] = _tokenURI; 47 | } 48 | 49 | /** 50 | * @dev Destroys `tokenId`. 51 | * The approval is cleared when the token is burned. 52 | * 53 | * Requirements: 54 | * 55 | * - `tokenId` must exist. 56 | * 57 | * Emits a {Transfer} event. 58 | */ 59 | function _burn(uint256 tokenId) internal virtual override { 60 | super._burn(tokenId); 61 | 62 | if (bytes(_tokenURIs[tokenId]).length != 0) { 63 | delete _tokenURIs[tokenId]; 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /contracts/DigitalGolems.sol: -------------------------------------------------------------------------------- 1 | // contracts/GameItem.sol 2 | // SPDX-License-Identifier: MIT 3 | pragma solidity ^0.8.10; 4 | 5 | import "./BEP721URIStorage.sol"; 6 | import "@openzeppelin/contracts/utils/Counters.sol"; 7 | import "@openzeppelin/contracts/utils/Strings.sol"; 8 | import "../Utils/Owner.sol"; 9 | import "../Utils/SafeMath.sol"; 10 | import "../Utils/ControlledAccess.sol"; 11 | import "./Interfaces/ICard.sol"; 12 | import "@chainlink/contracts/src/v0.8/interfaces/LinkTokenInterface.sol"; 13 | import "./Interfaces/IDigitalGolems.sol"; 14 | 15 | contract DigitalGolems is BEP721URIStorage, Owner, ControlledAccess, IDigitalGolems { 16 | using Counters for Counters.Counter; 17 | using Strings for uint256; 18 | Counters.Counter private _tokenIds; 19 | using SafeMath for uint; 20 | using SafeMath32 for uint32; 21 | using SafeMath16 for uint16; 22 | 23 | string presaleBaseCID = "Qmb7pUXwe5G4fUMV7ghDx4GkyQw8ZNYfkssEpCFnsV6ui6"; 24 | string presaleBaseURISuffix = ".json"; 25 | 26 | string laboratoryBaseCID = ""; 27 | string laboratoryBaseURISuffix = ".json"; 28 | 29 | string stakingBaseCID = ""; 30 | string stakingBaseURISuffix = ".json"; 31 | 32 | address private _stakingAddress; 33 | address private _presaleAddress; 34 | address private _labAddress; 35 | 36 | constructor() BEP721("Digital.Golems", "DIG") { 37 | } 38 | 39 | function changePresaleBaseCID(string memory newCID) public isOwner { 40 | presaleBaseCID = newCID; 41 | } 42 | 43 | function changeLaboratoryCID(string memory newCID) public isOwner { 44 | laboratoryBaseCID = newCID; 45 | } 46 | 47 | function changeStakingBaseCID(string memory newCID) public isOwner { 48 | stakingBaseCID = newCID; 49 | } 50 | 51 | function setStakingAddress(address _staking) public isOwner { 52 | _stakingAddress = _staking; 53 | } 54 | 55 | function setLabAddress(address _lab) public isOwner { 56 | _labAddress = _lab; 57 | } 58 | 59 | function setPresaleAddress(address _presale) public isOwner { 60 | _presaleAddress = _presale; 61 | } 62 | 63 | function setCard(address _card) public isOwner { 64 | card = ICard(_card); 65 | } 66 | 67 | function awardItemLaboratory( 68 | address player, 69 | uint256 orderID 70 | ) 71 | public 72 | onlyValidAddresses 73 | { 74 | _tokenIds.increment(); 75 | 76 | uint256 newItemId = _tokenIds.current(); 77 | _mint(player, newItemId); 78 | string memory id = orderID.toString(); 79 | string memory tokenURI = string(abi.encodePacked( 80 | "https://ipfs.io/ipfs/", 81 | laboratoryBaseCID, 82 | "/", 83 | id, 84 | laboratoryBaseURISuffix 85 | )); 86 | _setTokenURI(newItemId, tokenURI); 87 | card.createCard( 88 | player, 89 | newItemId 90 | ); 91 | } 92 | 93 | function awardItemStaking( 94 | address player, 95 | uint256 orderID 96 | ) 97 | public 98 | onlyValidAddresses 99 | { 100 | _tokenIds.increment(); 101 | 102 | uint256 newItemId = _tokenIds.current(); 103 | _mint(player, newItemId); 104 | string memory id = orderID.toString(); 105 | string memory tokenURI = string(abi.encodePacked( 106 | "https://ipfs.io/ipfs/", 107 | stakingBaseCID, 108 | "/", 109 | id, 110 | stakingBaseURISuffix 111 | )); 112 | _setTokenURI(newItemId, tokenURI); 113 | card.createCard( 114 | player, 115 | newItemId 116 | ); 117 | } 118 | 119 | //for presale 120 | //only presale address 121 | function mintPresale( 122 | address player 123 | ) 124 | public 125 | onlyValidAddresses 126 | { 127 | _tokenIds.increment(); 128 | 129 | uint256 newItemId = _tokenIds.current(); 130 | _mint(player, newItemId); 131 | string memory id = newItemId.toString(); 132 | string memory tokenURI = string(abi.encodePacked( 133 | "https://ipfs.io/ipfs/", 134 | presaleBaseCID, 135 | "/", 136 | id, 137 | presaleBaseURISuffix 138 | )); 139 | _setTokenURI(newItemId, tokenURI); 140 | card.createCard( 141 | player, 142 | newItemId 143 | ); 144 | } 145 | 146 | function ownerMint( 147 | address player, 148 | string memory tokenURI 149 | ) 150 | public 151 | isOwner 152 | { 153 | _tokenIds.increment(); 154 | 155 | uint256 newItemId = _tokenIds.current(); 156 | _mint(player, newItemId); 157 | _setTokenURI(newItemId, tokenURI); 158 | card.createCard( 159 | player, 160 | newItemId 161 | ); 162 | } 163 | 164 | modifier onlyValidAddresses() { 165 | require( 166 | (msg.sender == _presaleAddress) 167 | || 168 | (msg.sender == _stakingAddress) 169 | || 170 | (msg.sender == _labAddress) 171 | , "only Valid Addresses"); 172 | _; 173 | } 174 | 175 | } 176 | -------------------------------------------------------------------------------- /contracts/Card.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma experimental ABIEncoderV2; 4 | pragma solidity ^0.8.10; 5 | 6 | import "../Utils/SafeMath.sol"; 7 | import "../Utils/Owner.sol"; 8 | import "@openzeppelin/contracts/utils/Counters.sol"; 9 | import "../Game/Rent/IRent.sol"; 10 | import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol"; 11 | import "./Interfaces/ICard.sol"; 12 | 13 | contract Card is VRFConsumerBase, Owner, ICard { 14 | using Counters for Counters.Counter; 15 | Counters.Counter private _cardIds; 16 | bytes32 internal keyHash; 17 | uint256 internal fee; 18 | address public _LINK = 0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06; // LINK Token 19 | address VRFC = 0xa555fC018435bef5A13C6c6870a9d4C11DEC329C; // VRF Coordinator 20 | uint256 _fee = 0.1 * 10 ** 18; // 0.1 LINK (Varies by network) //BSC TEST 21 | 22 | using SafeMath for uint; 23 | using SafeMath32 for uint32; 24 | using SafeMath16 for uint16; 25 | 26 | address private _gameAddress; 27 | address private _conservationAddress; 28 | address private _digAddress; 29 | IRent private rent; 30 | 31 | uint8 public amountOfNumAbilities = 7; 32 | uint8 public amountOfBoolAbilities= 11; 33 | uint8 public amountOfKinds = 5; 34 | uint8 public amountOfSeries = 8; 35 | 36 | //↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Number of kind 37 | // Human - 0 38 | // Amphibian - 1 39 | // Insectoid - 2 40 | // Bird - 3 41 | // Animal - 4 42 | //series 43 | //0 - 0 generation, 44 | //1 - 1 generation 45 | //2 - 2 generation, 46 | //3 - 3 generation 47 | //4 - new generation, 48 | //5 - missing models 49 | //6 - elemental/epics, 50 | //7 - gold. 51 | 52 | // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Sequence number for NUMBER ability 53 | // uint16 flashlightLength - 0 54 | // uint16 goingSpeed - 1 55 | // uint16 spentEnergy - 2 56 | // uint16 trapRadius - 3; 57 | // uint16 snareRadius - 4; 58 | // uint16 radarSize - 5; 59 | // uint16 jumpHeight - 6; 60 | 61 | // ↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓ Sequence number for BOOL ability 62 | // bool gasResistance - 0; 63 | // bool corrosionResistance - 1; 64 | // bool electoAmomalyResistance - 2; 65 | // bool coldResistance - 3; 66 | // bool heatResistance - 4; 67 | // bool sandPassing - 5; 68 | // bool waterPassing - 6; 69 | // bool firePassing - 7; 70 | // bool acidPassing; - 8; 71 | // bool seesMines - 9; 72 | // bool seesTraps - 10; 73 | mapping(uint => mapping(uint8 => uint16)) _golemToNumAbility; 74 | //always ability needs for feeding and preservation 75 | //cant feed golem more than always 76 | mapping(uint => mapping(uint8 => uint16)) _golemToAlwaysNumAbility; 77 | mapping(uint => mapping(uint8 => bool)) _golemToBoolAbility; 78 | mapping(uint => address) public golemToOwner; 79 | mapping(address => uint) public ownerGolemCount; 80 | mapping(bytes32 => uint) public requestIDToCardID; 81 | 82 | constructor() 83 | VRFConsumerBase( 84 | VRFC, 85 | _LINK 86 | ) 87 | { 88 | keyHash = 0xcaf3c3727e033261d383b315559476f48034c13b18f8cafed4d871abe5049186; 89 | fee = _fee; 90 | } 91 | 92 | function setGameAddress(address _game) public isOwner { 93 | _gameAddress = _game; 94 | } 95 | 96 | function setDIGAddress(address _dig) public isOwner { 97 | _digAddress = _dig; 98 | } 99 | 100 | function setConserveAddress(address _conserve) public isOwner { 101 | _conservationAddress = _conserve; 102 | } 103 | 104 | function setRentAddress(address _rent) public isOwner { 105 | rent = IRent(_rent); 106 | } 107 | 108 | //changing amount of abilies by owner 109 | //every has new abilities amount 110 | function changeAmountOfNumAbilities(uint8 _newAmount) public isOwner { 111 | amountOfNumAbilities = _newAmount; 112 | } 113 | 114 | //changing amount of abilies by owner 115 | //every has new abilities amount 116 | function changeAmountOfBoolAbilities(uint8 _newAmount) public isOwner { 117 | amountOfBoolAbilities = _newAmount; 118 | } 119 | 120 | function getAmountOfNumAbilities() public view returns(uint8){ 121 | return amountOfNumAbilities; 122 | } 123 | 124 | function getAmountOfBoolAbilities() public view returns(uint8){ 125 | return amountOfBoolAbilities; 126 | } 127 | 128 | //change amount of golem kinds 129 | function changeAmountOfKinds(uint8 _newAmount) public isOwner { 130 | amountOfKinds = _newAmount; 131 | } 132 | 133 | //change amount of golem series 134 | function changeAmountOfSeries(uint8 _newAmount) public isOwner { 135 | amountOfSeries = _newAmount; 136 | } 137 | 138 | function getAmountOfKinds() public view returns(uint8){ 139 | return amountOfKinds; 140 | } 141 | 142 | function getAmountOfSeries() public view returns(uint8){ 143 | return amountOfSeries; 144 | } 145 | 146 | //transfer card 147 | //with modifier onlyDIG because used only when NFT that tied to card is transfered 148 | function transferCard(uint _cardID, address _from, address _to) public onlyDIG { 149 | //checks if its rented card or just on the market 150 | (uint256 _rentItemID, bool _existRentItem) = rent.getItemIDByCardID(_cardID); 151 | //cant transfer if item with this card exist 152 | if (_existRentItem == true) { 153 | require(rent.isClosed(_rentItemID) == true, "Its on Rent Market"); 154 | } 155 | //writing data 156 | ownerGolemCount[_to] = ownerGolemCount[_to].add(1); 157 | ownerGolemCount[_from] = ownerGolemCount[_from].sub(1); 158 | golemToOwner[_cardID] = _to; 159 | emit TransferCard(_cardID, _from, _to); 160 | }//ЧЕКНУТЬ EXIST 161 | 162 | //public function of card creation 163 | //using only for NFT contract 164 | function createCard( 165 | address player, //card player 166 | uint256 ID //NFT ID 167 | ) 168 | public 169 | onlyDIG 170 | { 171 | _createCard( 172 | player, 173 | ID 174 | ); 175 | emit MintCard( 176 | ID, 177 | player 178 | ); 179 | } 180 | 181 | //private function for card creation 182 | function _createCard( 183 | address player, 184 | uint256 _ID 185 | ) 186 | private 187 | { 188 | //adding golem to player 189 | golemToOwner[_ID] = player; 190 | //player golem count +1 191 | _cardIds.increment(); 192 | ownerGolemCount[player] = ownerGolemCount[player].add(1); 193 | //reques randomness is used for randomly adding abilities to golem 194 | fulfillRandomnessTest(_ID, block.timestamp); //TESTING 195 | // require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK"); CHAINLINK 196 | // requestIDToCardID[requestRandomness(keyHash, fee)] = _ID; CHAINLINK 197 | } 198 | 199 | /** 200 | * Callback function used by VRF Coordinator 201 | */ 202 | function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override { 203 | //creating cycle for num abilities 204 | for (uint8 i = 0; i < amountOfNumAbilities; i++) { 205 | //create new random for different values 206 | uint16 rand = (uint16(uint256(keccak256(abi.encode(randomness, i))) % 10 )) + 1; 207 | //add ability to golem 208 | _golemToNumAbility[requestIDToCardID[requestId]][i] = rand; 209 | //add always ability to golem 210 | _golemToAlwaysNumAbility[requestIDToCardID[requestId]][i] = rand; 211 | } 212 | for (uint8 i = 0; i < amountOfBoolAbilities; i++) { 213 | //create new random for different values 214 | uint8 rand = (uint8(uint256(keccak256(abi.encode(randomness, i))) % 10 )) + 1; 215 | //if rand more than 5 bool ability is true 216 | //if less is false 217 | _golemToBoolAbility[requestIDToCardID[requestId]][i] = rand > 5 ? true : false; 218 | } 219 | } 220 | 221 | //TESTING 222 | function fulfillRandomnessTest(uint256 id, uint256 randomness) internal { 223 | for (uint8 i = 0; i < amountOfNumAbilities; i++) { 224 | uint16 rand = (uint16(uint256(keccak256(abi.encode(randomness, i))) % 10 )) + 1; 225 | _golemToNumAbility[id][i] = rand; 226 | _golemToAlwaysNumAbility[id][i] = rand; 227 | } 228 | for (uint8 i = 0; i < amountOfBoolAbilities; i++) { 229 | uint8 rand = (uint8(uint256(keccak256(abi.encode(randomness, i))) % 10 )) + 1; 230 | _golemToBoolAbility[id][i] = rand > 5 ? true : false; 231 | } 232 | } 233 | 234 | function isAllInitialAbilities(uint256 _ID) public view returns(bool flag) { 235 | for (uint8 i = 0; i < amountOfNumAbilities; i++) { 236 | if (_golemToNumAbility[_ID][i] == _golemToAlwaysNumAbility[_ID][i]){ 237 | flag = true; 238 | } else { 239 | flag == false; 240 | break; 241 | } 242 | } 243 | } 244 | 245 | //counts different between always ability and actual 246 | function diffrenceBetweenInitialAndActualMaxAbilities(uint256 _ID) public view returns(uint16 diff){ 247 | (uint16 maxAbility, uint8 i) = getMaxAbility(_ID); 248 | diff = maxAbility - _golemToNumAbility[_ID][i]; 249 | } 250 | 251 | //get max ability 252 | //using for creating fed deposit in rent contract 253 | //fed deposit = max ability * fed price 254 | function getMaxAbility(uint256 _ID) public view returns(uint16 max, uint8 _i) { 255 | max = 0; 256 | for (uint8 i = 0; i < amountOfNumAbilities; i++) { 257 | if (_golemToAlwaysNumAbility[_ID][i] > max){ 258 | max = _golemToAlwaysNumAbility[_ID][i]; 259 | _i = i; 260 | } 261 | } 262 | } 263 | 264 | //onlyGameOrConservation 265 | //after session each ability is decreased by one 266 | function decreaseNumAbilityAfterSession(uint256 _ID) public onlyGameOrConservation { 267 | for (uint8 i = 0; i < amountOfNumAbilities; i++) { 268 | if (_golemToNumAbility[_ID][i] != 0){ 269 | _golemToNumAbility[_ID][i] = _golemToNumAbility[_ID][i].sub(1); 270 | } 271 | } 272 | } 273 | 274 | //increasing all abilities by one after golem feeding 275 | function increaseNumAbilityAfterFeeding(uint256 _ID) public onlyGameOrConservation { 276 | require(isAllInitialAbilities(_ID) == false, "Already Fed"); 277 | for (uint8 i = 0; i < amountOfNumAbilities; i++) { 278 | //actual ability cant be more than always 279 | if (_golemToNumAbility[_ID][i] < _golemToAlwaysNumAbility[_ID][i]) { 280 | _golemToNumAbility[_ID][i] = _golemToNumAbility[_ID][i].add(1); 281 | } 282 | } 283 | } 284 | 285 | //after preservation increasing always abilities 286 | function increaseNumAbilityAfterPreservation(uint256 _ID, uint8 _num) public onlyGameOrConservation { 287 | _golemToAlwaysNumAbility[_ID][_num] = _golemToAlwaysNumAbility[_ID][_num].add(1); 288 | } 289 | 290 | //get value from number ability 291 | function getNumAbilityInt(uint256 _id, uint8 _num) public view returns(uint16 int_) { 292 | int_ = _golemToNumAbility[_id][_num]; 293 | } 294 | 295 | function getAlwaysNumAbilityInt(uint256 _id, uint8 _num) public view returns(uint16 int_) { 296 | int_ = _golemToAlwaysNumAbility[_id][_num]; 297 | } 298 | 299 | //get value from bool ability 300 | function getBoolAbilityBool(uint256 _id, uint8 _num) public view returns(bool bool_) { 301 | bool_ = _golemToBoolAbility[_id][_num]; 302 | } 303 | 304 | //card quantity of player 305 | function cardCount(address _owner) public view returns(uint) { 306 | return ownerGolemCount[_owner]; 307 | } 308 | 309 | //get owner of card 310 | function cardOwner(uint _id) public view returns(address) { 311 | return golemToOwner[_id]; 312 | } 313 | 314 | //get all cards that player have 315 | function getCardsByOwner(address _owner) external view returns(uint[] memory) { 316 | uint[] memory result = new uint[](ownerGolemCount[_owner]); 317 | uint256 counter = 0; 318 | uint256 current = _cardIds.current(); 319 | for (uint i = 1; i <= current; i++) { 320 | if (golemToOwner[i] == _owner) { 321 | result[counter] = i; 322 | counter++; 323 | } 324 | } 325 | return result; 326 | } 327 | 328 | function withdrawLINK() public isOwner{ 329 | LinkTokenInterface(_LINK).transfer(owner, LinkTokenInterface(_LINK).balanceOf(address(this))); 330 | } 331 | 332 | modifier onlyGameOrConservation() { 333 | require( 334 | (msg.sender == _gameAddress) 335 | || 336 | (msg.sender == _conservationAddress) 337 | , "Only Game,Conservation"); 338 | _; 339 | } 340 | 341 | modifier onlyDIG() { 342 | require( 343 | msg.sender == _digAddress, 344 | "Only DIG" 345 | ); 346 | _; 347 | } 348 | 349 | modifier onlyRent() { 350 | require( 351 | msg.sender == address(rent), 352 | "Only Rent" 353 | ); 354 | _; 355 | } 356 | 357 | } 358 | -------------------------------------------------------------------------------- /contracts/BEP721.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.0; 4 | 5 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 6 | import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; 7 | import "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; 8 | import "@openzeppelin/contracts/utils/Address.sol"; 9 | import "@openzeppelin/contracts/utils/Strings.sol"; 10 | import "@openzeppelin/contracts/utils/introspection/ERC165.sol"; 11 | import "./Interfaces/ICard.sol"; 12 | 13 | /** 14 | * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including 15 | * the Metadata extension, but not including the Enumerable extension, which is available separately as 16 | * {ERC721Enumerable}. 17 | */ 18 | contract BEP721 is ERC165, IERC721, IERC721Metadata { 19 | using Address for address; 20 | using Strings for uint256; 21 | 22 | // Token name 23 | string private _name; 24 | 25 | // Token symbol 26 | string private _symbol; 27 | 28 | ICard public card; 29 | 30 | // Mapping from token ID to owner address 31 | mapping(uint256 => address) private _owners; 32 | 33 | // Mapping owner address to token count 34 | mapping(address => uint256) private _balances; 35 | 36 | // Mapping from token ID to approved address 37 | mapping(uint256 => address) private _tokenApprovals; 38 | 39 | // Mapping from owner to operator approvals 40 | mapping(address => mapping(address => bool)) private _operatorApprovals; 41 | 42 | /** 43 | * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. 44 | */ 45 | constructor(string memory name_, string memory symbol_) { 46 | _name = name_; 47 | _symbol = symbol_; 48 | } 49 | 50 | /** 51 | * @dev See {IERC165-supportsInterface}. 52 | */ 53 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { 54 | return 55 | interfaceId == type(IERC721).interfaceId || 56 | interfaceId == type(IERC721Metadata).interfaceId || 57 | super.supportsInterface(interfaceId); 58 | } 59 | 60 | /** 61 | * @dev See {IERC721-balanceOf}. 62 | */ 63 | function balanceOf(address owner) public view virtual override returns (uint256) { 64 | require(owner != address(0), "Zero address"); 65 | return _balances[owner]; 66 | } 67 | 68 | /** 69 | * @dev See {IERC721-ownerOf}. 70 | */ 71 | function ownerOf(uint256 tokenId) public view virtual override returns (address) { 72 | address owner = _owners[tokenId]; 73 | require(owner != address(0), "Nonexistent token"); 74 | return owner; 75 | } 76 | 77 | /** 78 | * @dev See {IERC721Metadata-name}. 79 | */ 80 | function name() public view virtual override returns (string memory) { 81 | return _name; 82 | } 83 | 84 | /** 85 | * @dev See {IERC721Metadata-symbol}. 86 | */ 87 | function symbol() public view virtual override returns (string memory) { 88 | return _symbol; 89 | } 90 | /** 91 | * @dev See {IERC721Metadata-tokenURI}. 92 | */ 93 | function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { 94 | require(_exists(tokenId), "Nonexistent token"); 95 | 96 | string memory baseURI = _baseURI(); 97 | return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; 98 | } 99 | 100 | /** 101 | * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each 102 | * token will be the concatenation of the `baseURI` and the `tokenId`. Empty 103 | * by default, can be overriden in child contracts. 104 | */ 105 | function _baseURI() internal view virtual returns (string memory) { 106 | return ""; 107 | } 108 | 109 | /** 110 | * @dev See {IERC721-approve}. 111 | */ 112 | function approve(address to, uint256 tokenId) public virtual override { 113 | address owner = BEP721.ownerOf(tokenId); 114 | require(to != owner, "To owner"); 115 | 116 | require( 117 | msg.sender == owner || isApprovedForAll(owner, msg.sender), 118 | "Not owner nor approved for all" 119 | ); 120 | 121 | _approve(to, tokenId); 122 | } 123 | 124 | /** 125 | * @dev See {IERC721-getApproved}. 126 | */ 127 | function getApproved(uint256 tokenId) public view virtual override returns (address) { 128 | require(_exists(tokenId), "Nonexistent token"); 129 | 130 | return _tokenApprovals[tokenId]; 131 | } 132 | 133 | /** 134 | * @dev See {IERC721-setApprovalForAll}. 135 | */ 136 | function setApprovalForAll(address operator, bool approved) public virtual override { 137 | require(operator != msg.sender, "Approve to caller"); 138 | 139 | _operatorApprovals[msg.sender][operator] = approved; 140 | emit ApprovalForAll(msg.sender, operator, approved); 141 | } 142 | 143 | /** 144 | * @dev See {IERC721-isApprovedForAll}. 145 | */ 146 | function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { 147 | return _operatorApprovals[owner][operator]; 148 | } 149 | 150 | /** 151 | * @dev See {IERC721-transferFrom}. 152 | */ 153 | function transferFrom( 154 | address from, 155 | address to, 156 | uint256 tokenId 157 | ) public virtual override { 158 | //solhint-disable-next-line max-line-length 159 | require(_isApprovedOrOwner(msg.sender, tokenId), "Not owner nor approved"); 160 | 161 | _transfer(from, to, tokenId); 162 | } 163 | 164 | /** 165 | * @dev See {IERC721-safeTransferFrom}. 166 | */ 167 | function safeTransferFrom( 168 | address from, 169 | address to, 170 | uint256 tokenId 171 | ) public virtual override { 172 | safeTransferFrom(from, to, tokenId, ""); 173 | } 174 | 175 | /** 176 | * @dev See {IERC721-safeTransferFrom}. 177 | */ 178 | function safeTransferFrom( 179 | address from, 180 | address to, 181 | uint256 tokenId, 182 | bytes memory _data 183 | ) public virtual override { 184 | require(_isApprovedOrOwner(msg.sender, tokenId), "Not owner nor approved"); 185 | _safeTransfer(from, to, tokenId, _data); 186 | } 187 | 188 | /** 189 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients 190 | * are aware of the ERC721 protocol to prevent tokens from being forever locked. 191 | * 192 | * `_data` is additional data, it has no specified format and it is sent in call to `to`. 193 | * 194 | * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. 195 | * implement alternative mechanisms to perform token transfer, such as signature-based. 196 | * 197 | * Requirements: 198 | * 199 | * - `from` cannot be the zero address. 200 | * - `to` cannot be the zero address. 201 | * - `tokenId` token must exist and be owned by `from`. 202 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 203 | * 204 | * Emits a {Transfer} event. 205 | */ 206 | function _safeTransfer( 207 | address from, 208 | address to, 209 | uint256 tokenId, 210 | bytes memory _data 211 | ) internal virtual { 212 | _transfer(from, to, tokenId); 213 | require(_checkOnERC721Received(from, to, tokenId, _data), "Non receiver"); 214 | } 215 | 216 | /** 217 | * @dev Returns whether `tokenId` exists. 218 | * 219 | * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. 220 | * 221 | * Tokens start existing when they are minted (`_mint`), 222 | * and stop existing when they are burned (`_burn`). 223 | */ 224 | function _exists(uint256 tokenId) internal view virtual returns (bool) { 225 | return _owners[tokenId] != address(0); 226 | } 227 | 228 | /** 229 | * @dev Returns whether `spender` is allowed to manage `tokenId`. 230 | * 231 | * Requirements: 232 | * 233 | * - `tokenId` must exist. 234 | */ 235 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { 236 | require(_exists(tokenId), "Nonexistent token"); 237 | address owner = BEP721.ownerOf(tokenId); 238 | return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); 239 | } 240 | 241 | /** 242 | * @dev Safely mints `tokenId` and transfers it to `to`. 243 | * 244 | * Requirements: 245 | * 246 | * - `tokenId` must not exist. 247 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 248 | * 249 | * Emits a {Transfer} event. 250 | */ 251 | function _safeMint(address to, uint256 tokenId) internal virtual { 252 | _safeMint(to, tokenId, ""); 253 | } 254 | 255 | /** 256 | * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is 257 | * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. 258 | */ 259 | function _safeMint( 260 | address to, 261 | uint256 tokenId, 262 | bytes memory _data 263 | ) internal virtual { 264 | _mint(to, tokenId); 265 | require( 266 | _checkOnERC721Received(address(0), to, tokenId, _data), 267 | "Non receiver" 268 | ); 269 | } 270 | 271 | /** 272 | * @dev Mints `tokenId` and transfers it to `to`. 273 | * 274 | * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible 275 | * 276 | * Requirements: 277 | * 278 | * - `tokenId` must not exist. 279 | * - `to` cannot be the zero address. 280 | * 281 | * Emits a {Transfer} event. 282 | */ 283 | function _mint(address to, uint256 tokenId) internal virtual { 284 | require(to != address(0), "To Zero address"); 285 | require(!_exists(tokenId), "Already minted"); 286 | 287 | _beforeTokenTransfer(address(0), to, tokenId); 288 | 289 | _balances[to] += 1; 290 | _owners[tokenId] = to; 291 | 292 | emit Transfer(address(0), to, tokenId); 293 | } 294 | 295 | /** 296 | * @dev Destroys `tokenId`. 297 | * The approval is cleared when the token is burned. 298 | * 299 | * Requirements: 300 | * 301 | * - `tokenId` must exist. 302 | * 303 | * Emits a {Transfer} event. 304 | */ 305 | function _burn(uint256 tokenId) internal virtual { 306 | address owner = BEP721.ownerOf(tokenId); 307 | 308 | _beforeTokenTransfer(owner, address(0), tokenId); 309 | 310 | // Clear approvals 311 | _approve(address(0), tokenId); 312 | 313 | _balances[owner] -= 1; 314 | delete _owners[tokenId]; 315 | 316 | emit Transfer(owner, address(0), tokenId); 317 | } 318 | 319 | /** 320 | * @dev Transfers `tokenId` from `from` to `to`. 321 | * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. 322 | * 323 | * Requirements: 324 | * 325 | * - `to` cannot be the zero address. 326 | * - `tokenId` token must be owned by `from`. 327 | * 328 | * Emits a {Transfer} event. 329 | */ 330 | function _transfer( 331 | address from, 332 | address to, 333 | uint256 tokenId 334 | ) internal virtual { 335 | require(BEP721.ownerOf(tokenId) == from, "Not owner"); 336 | require(to != address(0), "To the zero address"); 337 | 338 | _beforeTokenTransfer(from, to, tokenId); 339 | 340 | card.transferCard(tokenId, from, to); 341 | // Clear approvals from the previous owner 342 | _approve(address(0), tokenId); 343 | _balances[from] -= 1; 344 | _balances[to] += 1; 345 | _owners[tokenId] = to; 346 | 347 | emit Transfer(from, to, tokenId); 348 | } 349 | 350 | /** 351 | * @dev Approve `to` to operate on `tokenId` 352 | * 353 | * Emits a {Approval} event. 354 | */ 355 | function _approve(address to, uint256 tokenId) internal virtual { 356 | _tokenApprovals[tokenId] = to; 357 | emit Approval(BEP721.ownerOf(tokenId), to, tokenId); 358 | } 359 | 360 | /** 361 | * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. 362 | * The call is not executed if the target address is not a contract. 363 | * 364 | * @param from address representing the previous owner of the given token ID 365 | * @param to target address that will receive the tokens 366 | * @param tokenId uint256 ID of the token to be transferred 367 | * @param _data bytes optional data to send along with the call 368 | * @return bool whether the call correctly returned the expected magic value 369 | */ 370 | function _checkOnERC721Received( 371 | address from, 372 | address to, 373 | uint256 tokenId, 374 | bytes memory _data 375 | ) private returns (bool) { 376 | if (to.isContract()) { 377 | try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, _data) returns (bytes4 retval) { 378 | return retval == IERC721Receiver.onERC721Received.selector; 379 | } catch (bytes memory reason) { 380 | if (reason.length == 0) { 381 | revert("Non receiver"); 382 | } else { 383 | assembly { 384 | revert(add(32, reason), mload(reason)) 385 | } 386 | } 387 | } 388 | } else { 389 | return true; 390 | } 391 | } 392 | 393 | /** 394 | * @dev Hook that is called before any token transfer. This includes minting 395 | * and burning. 396 | * 397 | * Calling conditions: 398 | * 399 | * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be 400 | * transferred to `to`. 401 | * - When `from` is zero, `tokenId` will be minted for `to`. 402 | * - When `to` is zero, ``from``'s `tokenId` will be burned. 403 | * - `from` and `to` are never both zero. 404 | * 405 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 406 | */ 407 | function _beforeTokenTransfer( 408 | address from, 409 | address to, 410 | uint256 tokenId 411 | ) internal virtual {} 412 | } 413 | --------------------------------------------------------------------------------