├── README.md └── contracts ├── Conservation.sol └── Laboratory.sol /README.md: -------------------------------------------------------------------------------- 1 | Lab 2 | -------------------------------------------------------------------------------- /contracts/Conservation.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma experimental ABIEncoderV2; 4 | pragma solidity 0.8.10; 5 | 6 | import "../Rent/IRent.sol"; 7 | import "../../Utils/Owner.sol"; 8 | import "../../Digibytes/Interfaces/IBEP20.sol"; 9 | import "../../DigitalGolems/Interfaces/ICard.sol"; 10 | import "../Interfaces/IConservation.sol"; 11 | 12 | contract Conservation is Owner, IConservation { 13 | 14 | struct Preservation { 15 | uint256 whenPreserve; 16 | bool preservated; 17 | uint8 preservatedAbility; 18 | } 19 | mapping (uint256 => Preservation) golemToPreserve; 20 | uint256 preservePrice = 1 * 10**18; //DBT 21 | uint256 feedingPrice = 1 * 10**18;//DBT 22 | IBEP20 public DBT; 23 | ICard public card; 24 | IRent public rent; 25 | 26 | function setDBT(address _DBT) public isOwner { 27 | DBT = IBEP20(_DBT); 28 | } 29 | 30 | function setRent(address _rent) public isOwner { 31 | rent = IRent(_rent); 32 | } 33 | 34 | function setCard(address _card) public isOwner { 35 | card = ICard(_card); 36 | } 37 | 38 | function changePreservePrice(uint256 _newPrice) public isOwner { 39 | preservePrice = _newPrice; 40 | emit ChangePreservePrice(_newPrice); 41 | } 42 | 43 | function getPreservePrice() public view returns(uint256) { 44 | return preservePrice; 45 | } 46 | 47 | function changeFeedingPrice(uint256 _newPrice) public isOwner { 48 | feedingPrice = _newPrice; 49 | emit ChangeFeedingPrice(_newPrice); 50 | } 51 | 52 | function getFeedingPrice() public view returns(uint256) { 53 | return feedingPrice; 54 | } 55 | 56 | function feeding(uint256 _ID) external { 57 | DBT.transferFrom(msg.sender, address(this), feedingPrice); 58 | card.increaseNumAbilityAfterFeeding(_ID); 59 | emit Feeding(_ID); 60 | } 61 | 62 | //send it to preservation 63 | function preservation(uint256 _ID, uint8 _num) external onlyCardOwner(_ID) isOnRent(_ID) { 64 | require(golemToPreserve[_ID].preservated != true, "Already"); 65 | require(msg.sender == card.cardOwner(_ID), "Not Owner"); 66 | DBT.transferFrom(msg.sender, address(this), preservePrice); 67 | golemToPreserve[_ID].whenPreserve = block.timestamp; 68 | golemToPreserve[_ID].preservated = true; 69 | golemToPreserve[_ID].preservatedAbility = _num; 70 | emit Preserveted(golemToPreserve[_ID].whenPreserve, _ID); 71 | } 72 | 73 | //increase ability after 1 week 74 | function preserveIncrease(uint256 _ID) external { 75 | require(isPreservated(_ID) == true, "Wasnt preserved"); 76 | require(block.timestamp > golemToPreserve[_ID].whenPreserve + 7 days, "Still preserve"); 77 | require(msg.sender == card.cardOwner(_ID), "Not owner"); 78 | golemToPreserve[_ID].preservated = false; 79 | card.increaseNumAbilityAfterPreservation(_ID, golemToPreserve[_ID].preservatedAbility); 80 | emit PreservetedIncreased(_ID, golemToPreserve[_ID].preservatedAbility); 81 | } 82 | 83 | function getPreserve(uint256 _ID) external view returns(uint256 timeWhenEnded) { 84 | require(golemToPreserve[_ID].preservated == true, "Card did not preserve"); 85 | timeWhenEnded = golemToPreserve[_ID].whenPreserve + 7 days; 86 | } 87 | 88 | function isPreservated(uint256 _ID) public view returns(bool) { 89 | return golemToPreserve[_ID].preservated; 90 | } 91 | 92 | function mockTimePreservated(uint256 _ID, uint256 _time) public isOwner { 93 | golemToPreserve[_ID].whenPreserve = _time; 94 | } 95 | 96 | modifier onlyCardOwner(uint256 _ID) { 97 | require(msg.sender == card.cardOwner(_ID), "Only Owner"); 98 | _; 99 | } 100 | 101 | modifier isOnRent(uint256 _cardID) { 102 | bool cardRentExist; 103 | uint256 cardItemID; 104 | (cardItemID, cardRentExist) = rent.getItemIDByCardID(_cardID); 105 | if (cardRentExist) { 106 | require(rent.isClosed(cardItemID), "Close your rent order"); 107 | } 108 | _; 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /contracts/Laboratory.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: GPL-3.0 2 | 3 | pragma experimental ABIEncoderV2; 4 | pragma solidity 0.8.10; 5 | 6 | import "../Interfaces/IAsset.sol"; 7 | import "../Interfaces/IPsychospheres.sol"; 8 | import "../Rent/IRent.sol"; 9 | import "../../Utils/Owner.sol"; 10 | import "../../Utils/ControlledAccess.sol"; 11 | import "../../Digibytes/Interfaces/IBEP20.sol"; 12 | import "../../DigitalGolems/Interfaces/IDigitalGolems.sol"; 13 | import "../../DigitalGolems/Card.sol"; 14 | import "@chainlink/contracts/src/v0.8/VRFConsumerBase.sol"; 15 | import "@openzeppelin/contracts/utils/Counters.sol"; 16 | 17 | contract Laboratory is Owner, VRFConsumerBase, ControlledAccess { 18 | using Counters for Counters.Counter; 19 | 20 | Counters.Counter private combiningIds; 21 | // Counters.Counter private orders; 22 | 23 | bytes32 internal keyHash; 24 | uint256 internal fee; 25 | 26 | struct Order { 27 | address owner; 28 | uint16[] assets; 29 | uint16 generation; 30 | uint32 soil; 31 | bool canMint; 32 | bool ended; 33 | } 34 | Order[] orders; 35 | mapping (uint16 => uint16[]) generationNeedsAssets; 36 | uint16 generationAmount; 37 | mapping (bytes32 => uint256) requestIDToOrder; 38 | mapping (address => uint256) userToOrders; 39 | mapping (address => mapping(uint16 => uint16)) userToChance; 40 | uint256 chance50Price = 1 * 10**18; //DBT 41 | uint256 chance75Price = 2 * 10**18; //DBT 42 | IBEP20 public DBT; 43 | IDigitalGolems public DIG; 44 | IAsset public assetsContract; 45 | IPsychospheres public psycho; 46 | IRent public rent; 47 | 48 | event Combine(address to, uint16 generationID, uint16[] assetsIDs); 49 | event FalledCombine(address to, uint16 generationID, uint16[] assetsIDs); 50 | event BuyChance(address who, uint16 percent); 51 | event ChangeChancePrice(uint256 afterPrice, uint16 chance); 52 | 53 | function setDBT(address _DBT) public isOwner { 54 | DBT = IBEP20(_DBT); 55 | } 56 | 57 | function setDIG(address _DIG) public isOwner { 58 | DIG = IDigitalGolems(_DIG); 59 | } 60 | 61 | function setAssets(address _assets) public isOwner { 62 | assetsContract = IAsset(_assets); 63 | } 64 | 65 | function setPsycho(address _psycho) public isOwner { 66 | psycho = IPsychospheres(_psycho); 67 | } 68 | 69 | function setRent(address _rent) public isOwner { 70 | rent = IRent(_rent); 71 | } 72 | 73 | constructor () 74 | VRFConsumerBase( 75 | 0xa555fC018435bef5A13C6c6870a9d4C11DEC329C, // VRF Coordinator 76 | 0x84b9B910527Ad5C03A9Ca831909E21e236EA7b06 // LINK Token 77 | ) 78 | { 79 | keyHash = 0xcaf3c3727e033261d383b315559476f48034c13b18f8cafed4d871abe5049186; //BST TEST 80 | fee = 0.1 * 10 ** 18; // 0.1 LINK (Varies by network) //BST TEST 81 | // DBT = Digibytes(_DBT); 82 | // DIG = DigitalGolems(_DIG); 83 | generationNeedsAssets[0] = [0, 1, 2, 3, 4]; //0 generation 84 | //MAIN 85 | //base model Torso id - 0 86 | //base model Head id - 1 87 | //base model Left hand id - 2 88 | //base model Right hand id - 3 89 | //base model Legs id -4 90 | //OPTIONAL 91 | //Burning/smoking eyes id - 5 92 | generationNeedsAssets[1] = [0, 1, 2, 3, 4];//1 generation 93 | //MAIN 94 | //base model Torso id - 0 95 | //base model Head id - 1 96 | //base model Left hand id - 2 97 | //base model Right hand id - 3 98 | //base model Legs id -4 99 | generationNeedsAssets[2] = [0, 1, 2, 3, 4];//2 generation 100 | //MAIN 101 | //base model Torso id - 0 102 | //base model Head id - 1 103 | //base model Left hand id - 2 104 | //base model Right hand id - 3 105 | //base model Legs id -4 106 | //OPTIONAL 107 | //Burning/smoking eyes id - 5 108 | generationNeedsAssets[3] = [0, 1, 2, 3, 4, 6, 7, 8, 9];//3 generation 109 | //MAIN 110 | //base model Torso id - 0 111 | //base model Head id - 1 112 | //base model Left hand id - 2 113 | //base model Right hand id - 3 114 | //base model Legs id -4 115 | //Masks id - 6 116 | //Bib id - 7 117 | //Shoulder pads id -8 118 | //Belt id - 9 119 | generationNeedsAssets[4] = [0, 1, 2, 3, 4];//New generation 120 | //MAIN 121 | //base model Torso id - 0 122 | //base model Head id - 1 123 | //base model Left hand id - 2 124 | //base model Right hand id - 3 125 | //base model Legs id -4 126 | //OPTIONAL 127 | //Hind limbs/wings id - 10 128 | //Limbs on chest id - 11 129 | //Details on the head id - 12 130 | generationNeedsAssets[5] = [0, 1, 2, 3, 4];//Lost generation 131 | //MAIN 132 | //base model Torso id - 0 133 | //base model Head id - 1 134 | //base model Left hand id - 2 135 | //base model Right hand id - 3 136 | //base model Legs id -4 137 | //OPTIONAL 138 | //Nimbus id - 13 139 | generationAmount = 6; 140 | } 141 | 142 | function createGenerationNeedsAsset(uint16[] memory _assetsParts) public isOwner { 143 | generationNeedsAssets[generationAmount] = _assetsParts; 144 | generationAmount = generationAmount + 1; 145 | } 146 | 147 | //isOwner 148 | function updateGenerationNeedsAsset(uint16 generationID, uint16[] memory _assetsParts) public generationExist(generationID) isOwner { 149 | generationNeedsAssets[generationID] = _assetsParts; 150 | } 151 | 152 | function getGenerationNeedsAsset(uint16 generationID) public view generationExist(generationID) returns(uint16[] memory) { 153 | return generationNeedsAssets[generationID]; 154 | } 155 | 156 | //WHEN ADDING ASSETS REQUIRED ADD IN SEQUENCE THAT IN generationNeedsAssets 157 | //IF IT POSSIBLE TO ADD OTHER ASSETS - IN RANDOM SEQUENCE 158 | function combiningGeneration( 159 | uint16 generationID, 160 | uint16[] memory assetsIDs, 161 | uint32 soil 162 | ) 163 | external 164 | enoughAssets(generationID, assetsIDs) 165 | enoughSubstrat(soil) 166 | { 167 | orders.push( 168 | Order( 169 | msg.sender, 170 | assetsIDs, 171 | generationID, 172 | soil, 173 | false, 174 | false 175 | ) 176 | ); 177 | userToOrders[msg.sender] = userToOrders[msg.sender] + 1; 178 | uint256 id = orders.length - 1; 179 | //real random 180 | // require(LINK.balanceOf(address(this)) >= fee, "Not enough LINK"); CHAINLINK 181 | // requestIDToGeneration[requestRandomness(keyHash, fee)] = generationID; CHAINLINK 182 | uint256 rand = uint256(keccak256(abi.encodePacked(block.timestamp))); 183 | bytes32 requestId = bytes32(keccak256(abi.encodePacked(block.timestamp))); 184 | requestIDToOrder[requestId] = id; 185 | fulfillRandomnessTest(requestId, rand); 186 | } 187 | 188 | /** 189 | * Callback function used by VRF Coordinator 190 | */ 191 | function fulfillRandomness(bytes32 requestId, uint256 randomness) internal override { 192 | // uint16 chance = 0; 193 | // if (userToChance[msg.sender] == 0) { 194 | // chance = 25; 195 | // } else { 196 | // chance = userToChance[msg.sender]; 197 | // } 198 | // //if rand is less than chance, we can mint 199 | // if ((randomness % 100) < chance) { 200 | // userCanMint[msg.sender] = true; 201 | // emit Combine(msg.sender, requestIDToGeneration[requestId], generationNeedsAssets[requestIDToGeneration[requestId]]); 202 | // } else {//deleting assets 203 | // for (uint16 i = 0; i < generationNeedsAssets[requestIDToGeneration[requestId]].length; i++) { 204 | // assetsContract.deleteUserFromAsset(generationNeedsAssets[requestIDToGeneration[requestId]][i], msg.sender); 205 | // } 206 | // emit FalledCombine(msg.sender, requestIDToGeneration[requestId], generationNeedsAssets[requestIDToGeneration[requestId]]); 207 | // } 208 | } 209 | 210 | function fulfillRandomnessTest(bytes32 requestIdTest, uint256 randomness) internal { 211 | uint16 chance = 0; 212 | Order memory _order = orders[requestIDToOrder[requestIdTest]]; 213 | uint16 userChanceThisGeneration = userToChance[msg.sender][_order.generation]; 214 | uint16[] memory assets = _order.assets; 215 | if (userChanceThisGeneration == 0) { 216 | chance = 25; 217 | } else { 218 | chance = userChanceThisGeneration; 219 | } 220 | //if rand is less than chance, we can mint 221 | if (((randomness % 100) + 1) < chance) { 222 | _order.canMint = true; 223 | orders[requestIDToOrder[requestIdTest]] = _order; 224 | for (uint16 i = 0; i < assets.length; i++) { 225 | assetsContract.deleteUserFromAsset(assets[i], msg.sender); 226 | } 227 | psycho.decreaseSubstrate(msg.sender, _order.soil); 228 | emit Combine(msg.sender, _order.generation, assets); 229 | } else { 230 | //deleting assets 231 | for (uint16 i = 0; i < assets.length; i++) { 232 | assetsContract.deleteUserFromAsset(assets[i], msg.sender); 233 | } 234 | emit FalledCombine(msg.sender, _order.generation, assets); 235 | } 236 | } 237 | 238 | function buyChance50(uint16 generationID) external { 239 | DBT.transferFrom(msg.sender, address(this), chance50Price); 240 | userToChance[msg.sender][generationID] = 50; 241 | emit BuyChance(msg.sender, 50); 242 | } 243 | 244 | function buyChance75(uint16 generationID) external { 245 | DBT.transferFrom(msg.sender, address(this), chance75Price); 246 | userToChance[msg.sender][generationID] = 75; 247 | emit BuyChance(msg.sender, 75); 248 | } 249 | 250 | function getUserToChance(uint16 generationID) public view returns(uint16) { 251 | return userToChance[msg.sender][generationID]; 252 | } 253 | 254 | function buyChance100TEST(address user, uint16 generationID) external isOwner { 255 | userToChance[user][generationID] = 100; 256 | emit BuyChance(user, 100); 257 | } 258 | 259 | function buyChance1TEST(address user, uint16 generationID) external isOwner { 260 | userToChance[user][generationID] = 1; 261 | emit BuyChance(user, 1); 262 | } 263 | 264 | function award( 265 | uint256 orderID, 266 | string memory tokenURI, 267 | uint8 _v, 268 | bytes32 _r, 269 | bytes32 _s 270 | ) 271 | public 272 | onlyValidMint(_v, _r, _s, tokenURI, orderID) 273 | { 274 | require(orders[orderID].owner == msg.sender, "Not your order"); 275 | require(orders[orderID].canMint == true, "You cant mint"); 276 | require(orders[orderID].ended == false, "This order already minted"); 277 | combiningIds.increment(); 278 | DIG.awardItemLaboratory( 279 | msg.sender, 280 | combiningIds.current() 281 | ); 282 | orders[orderID].ended = true; 283 | } 284 | 285 | function getCanMint(uint256 orderID) public view returns(bool) { 286 | return orders[orderID].canMint; 287 | } 288 | 289 | function changeChance50Price(uint256 _newPrice) public isOwner { 290 | chance50Price = _newPrice; 291 | emit ChangeChancePrice(_newPrice, 50); 292 | } 293 | 294 | function changeChance75Price(uint256 _newPrice) public isOwner { 295 | chance75Price = _newPrice; 296 | emit ChangeChancePrice(_newPrice, 75); 297 | } 298 | 299 | function getChance50Price() public view returns(uint256) { 300 | return chance50Price; 301 | } 302 | 303 | function getChance75Price() public view returns(uint256) { 304 | return chance75Price; 305 | } 306 | 307 | function getUserOrders(address user) public view returns(Order[] memory) { 308 | Order[] memory userOrders = new Order[](userToOrders[user]); 309 | uint256 counter = 0; 310 | for (uint256 i = 0; i < orders.length; i++) { 311 | if (orders[i].owner == user) { 312 | userOrders[counter] = orders[i]; 313 | counter++; 314 | } 315 | } 316 | return userOrders; 317 | } 318 | 319 | modifier enoughAssets(uint16 generationID, uint16[] memory _assetsIDs) { 320 | require(generationNeedsAssets[generationID].length <= _assetsIDs.length, "Not enough assets"); 321 | uint16[] memory needsAssets = generationNeedsAssets[generationID]; //assets that need for this generation 322 | for (uint16 i = 0; i < generationNeedsAssets[generationID].length; i++) { 323 | uint16 part = assetsContract.getAssetPart(_assetsIDs[i]); 324 | //checking if part that generation needs 325 | //exist in users assets 326 | require(part == needsAssets[i], "Part of golem needs"); 327 | require(assetsContract.assetToOwner(msg.sender, _assetsIDs[i]) != 0, "You not owner"); 328 | } 329 | _; 330 | } 331 | 332 | modifier enoughSubstrat(uint32 _soil) { 333 | require(psycho.checkHasEnoughOneTypeOfSubstrate(msg.sender, _soil) == true, "Not enough substrate"); 334 | _; 335 | } 336 | 337 | modifier generationExist(uint16 generationID) { 338 | bool flag; 339 | for (uint16 i = 0; i < generationNeedsAssets[generationID].length; i++) { 340 | if (generationNeedsAssets[generationID][i] == 0) { 341 | flag = true; 342 | } else { 343 | flag = false; 344 | } 345 | } 346 | require(flag != true); 347 | _; 348 | } 349 | 350 | } 351 | --------------------------------------------------------------------------------