├── .gitignore ├── LICENSE ├── README.md └── contracts ├── DGSGToken.sol ├── DGSToken.sol ├── DebloxNFT.sol ├── GameSlot.sol └── NFTAirdrop.sol /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | 9 | # Diagnostic reports (https://nodejs.org/api/report.html) 10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 11 | 12 | # Runtime data 13 | pids 14 | *.pid 15 | *.seed 16 | *.pid.lock 17 | 18 | # Directory for instrumented libs generated by jscoverage/JSCover 19 | lib-cov 20 | 21 | # Coverage directory used by tools like istanbul 22 | coverage 23 | *.lcov 24 | 25 | # nyc test coverage 26 | .nyc_output 27 | 28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 29 | .grunt 30 | 31 | # Bower dependency directory (https://bower.io/) 32 | bower_components 33 | 34 | # node-waf configuration 35 | .lock-wscript 36 | 37 | # Compiled binary addons (https://nodejs.org/api/addons.html) 38 | build/Release 39 | 40 | # Dependency directories 41 | node_modules/ 42 | jspm_packages/ 43 | 44 | # TypeScript v1 declaration files 45 | typings/ 46 | 47 | # TypeScript cache 48 | *.tsbuildinfo 49 | 50 | # Optional npm cache directory 51 | .npm 52 | 53 | # Optional eslint cache 54 | .eslintcache 55 | 56 | # Microbundle cache 57 | .rpt2_cache/ 58 | .rts2_cache_cjs/ 59 | .rts2_cache_es/ 60 | .rts2_cache_umd/ 61 | 62 | # Optional REPL history 63 | .node_repl_history 64 | 65 | # Output of 'npm pack' 66 | *.tgz 67 | 68 | # Yarn Integrity file 69 | .yarn-integrity 70 | 71 | # dotenv environment variables file 72 | .env 73 | .env.test 74 | 75 | # parcel-bundler cache (https://parceljs.org/) 76 | .cache 77 | 78 | # Next.js build output 79 | .next 80 | 81 | # Nuxt.js build / generate output 82 | .nuxt 83 | dist 84 | 85 | # Gatsby files 86 | .cache/ 87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 88 | # https://nextjs.org/blog/next-9-1#public-directory-support 89 | # public 90 | 91 | # vuepress build output 92 | .vuepress/dist 93 | 94 | # Serverless directories 95 | .serverless/ 96 | 97 | # FuseBox cache 98 | .fusebox/ 99 | 100 | # DynamoDB Local files 101 | .dynamodb/ 102 | 103 | # TernJS port file 104 | .tern-port 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Deblox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # contracts 2 | Deblox Smart Contracts 3 | -------------------------------------------------------------------------------- /contracts/DGSGToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.6.12; 3 | 4 | import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/BEP20.sol"; 5 | 6 | // DGS Token 7 | contract DGSGToken is BEP20('DGS-G Token', 'DGSG') { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /contracts/DGSToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.6.12; // Certik DCK-01 3 | 4 | import "@pancakeswap/pancake-swap-lib/contracts/access/Ownable.sol"; 5 | import "@pancakeswap/pancake-swap-lib/contracts/GSN/Context.sol"; 6 | import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol"; 7 | import "@pancakeswap/pancake-swap-lib/contracts/math/SafeMath.sol"; 8 | import "@pancakeswap/pancake-swap-lib/contracts/utils/Address.sol"; 9 | 10 | /** 11 | * @dev Implementation of the {IBEP20} interface. 12 | * 13 | * This implementation is agnostic to the way tokens are created. This means 14 | * that a supply mechanism has to be added in a derived contract using {_mint}. 15 | * For a generic mechanism see {BEP20PresetMinterPauser}. 16 | * 17 | * TIP: For a detailed writeup see our guide 18 | * https://forum.zeppelin.solutions/t/how-to-implement-BEP20-supply-mechanisms/226[How 19 | * to implement supply mechanisms]. 20 | * 21 | * We have followed general OpenZeppelin guidelines: functions revert instead 22 | * of returning `false` on failure. This behavior is nonetheless conventional 23 | * and does not conflict with the expectations of BEP20 applications. 24 | * 25 | * Additionally, an {Approval} event is emitted on calls to {transferFrom}. 26 | * This allows applications to reconstruct the allowance for all accounts just 27 | * by listening to said events. Other implementations of the EIP may not emit 28 | * these events, as it isn't required by the specification. 29 | * 30 | * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} 31 | * functions have been added to mitigate the well-known issues around setting 32 | * allowances. See {IBEP20-approve}. 33 | */ 34 | 35 | //DGS updated code 36 | contract DGSToken is Context, IBEP20, Ownable { 37 | using SafeMath for uint256; 38 | using Address for address; 39 | 40 | mapping(address => uint256) private _balances; 41 | 42 | mapping(address => mapping(address => uint256)) private _allowances; 43 | 44 | //DGS updated code 45 | uint256 private _totalSupply; 46 | uint8 private _decimals; 47 | string private _symbol; 48 | string private _name; 49 | 50 | 51 | /** 52 | * @dev Sets the values for {name} and {symbol}, initializes {decimals} with 53 | * a default value of 18. 54 | * 55 | * To select a different value for {decimals}, use {_setupDecimals}. 56 | * 57 | * All three of these values are immutable: they can only be set once during 58 | * construction. 59 | */ 60 | constructor(string memory name, string memory symbol, uint8 decimals, uint256 totalSupply) public { 61 | _name = name; 62 | _symbol = symbol; 63 | _decimals = decimals; 64 | _totalSupply = totalSupply; 65 | _balances[msg.sender] = _totalSupply; // Certik Wrong Update for _totalSupply 66 | emit Transfer(address(0), msg.sender, _totalSupply); 67 | } 68 | 69 | /** 70 | * @dev Returns the bep token owner. 71 | */ 72 | function getOwner() external override view returns (address) { 73 | return owner(); 74 | } 75 | 76 | /** 77 | * @dev Returns the token name. 78 | */ 79 | function name() public override view returns (string memory) { 80 | return _name; 81 | } 82 | 83 | /** 84 | * @dev Returns the token decimals. 85 | */ 86 | function decimals() public override view returns (uint8) { 87 | return _decimals; 88 | } 89 | 90 | /** 91 | * @dev Returns the token symbol. 92 | */ 93 | function symbol() public override view returns (string memory) { 94 | return _symbol; 95 | } 96 | 97 | /** 98 | * @dev See {BEP20-totalSupply}. 99 | */ 100 | function totalSupply() public override view returns (uint256) { 101 | 102 | return _totalSupply; 103 | } 104 | 105 | /** 106 | * @dev See {BEP20-balanceOf}. 107 | */ 108 | function balanceOf(address account) public override view returns (uint256) { 109 | return _balances[account]; 110 | } 111 | 112 | /** 113 | * @dev See {BEP20-transfer}. 114 | * 115 | * Requirements: 116 | * 117 | * - `recipient` cannot be the zero address. 118 | * - the caller must have a balance of at least `amount`. 119 | */ 120 | function transfer(address recipient, uint256 amount) public override returns (bool) { 121 | _transfer(_msgSender(), recipient, amount); 122 | return true; 123 | } 124 | 125 | /** 126 | * @dev See {BEP20-allowance}. 127 | */ 128 | function allowance(address owner, address spender) public override view returns (uint256) { 129 | return _allowances[owner][spender]; 130 | } 131 | 132 | /** 133 | * @dev See {BEP20-approve}. 134 | * 135 | * Requirements: 136 | * 137 | * - `spender` cannot be the zero address. 138 | */ 139 | function approve(address spender, uint256 amount) public override returns (bool) { 140 | _approve(_msgSender(), spender, amount); 141 | return true; 142 | } 143 | 144 | /** 145 | * @dev See {BEP20-transferFrom}. 146 | * 147 | * Emits an {Approval} event indicating the updated allowance. This is not 148 | * required by the EIP. See the note at the beginning of {BEP20}; 149 | * 150 | * Requirements: 151 | * - `sender` and `recipient` cannot be the zero address. 152 | * - `sender` must have a balance of at least `amount`. 153 | * - the caller must have allowance for `sender`'s tokens of at least 154 | * `amount`. 155 | */ 156 | function transferFrom( 157 | address sender, 158 | address recipient, 159 | uint256 amount 160 | ) public override returns (bool) { 161 | _transfer(sender, recipient, amount); 162 | _approve( 163 | sender, 164 | _msgSender(), 165 | _allowances[sender][_msgSender()].sub(amount, 'BEP20: transfer amount exceeds allowance') 166 | ); 167 | return true; 168 | } 169 | 170 | /** 171 | * @dev Atomically increases the allowance granted to `spender` by the caller. 172 | * 173 | * This is an alternative to {approve} that can be used as a mitigation for 174 | * problems described in {BEP20-approve}. 175 | * 176 | * Emits an {Approval} event indicating the updated allowance. 177 | * 178 | * Requirements: 179 | * 180 | * - `spender` cannot be the zero address. 181 | */ 182 | function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { 183 | _approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue)); 184 | return true; 185 | } 186 | 187 | /** 188 | * @dev Atomically decreases the allowance granted to `spender` by the caller. 189 | * 190 | * This is an alternative to {approve} that can be used as a mitigation for 191 | * problems described in {BEP20-approve}. 192 | * 193 | * Emits an {Approval} event indicating the updated allowance. 194 | * 195 | * Requirements: 196 | * 197 | * - `spender` cannot be the zero address. 198 | * - `spender` must have allowance for the caller of at least 199 | * `subtractedValue`. 200 | */ 201 | function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { 202 | _approve( 203 | _msgSender(), 204 | spender, 205 | _allowances[_msgSender()][spender].sub(subtractedValue, 'BEP20: decreased allowance below zero') 206 | ); 207 | return true; 208 | } 209 | 210 | /** 211 | * @dev Creates `amount` tokens and assigns them to `msg.sender`, increasing 212 | * the total supply. 213 | * 214 | * Requirements 215 | * 216 | * - `msg.sender` must be the token owner 217 | */ 218 | // function mint(uint256 amount) public onlyOwner returns (bool) { // Certik Wrong Update for _totalSupply 219 | // _mint(_msgSender(), amount); 220 | // return true; 221 | // } 222 | 223 | /** 224 | * @dev Moves tokens `amount` from `sender` to `recipient`. 225 | * 226 | * This is internal function is equivalent to {transfer}, and can be used to 227 | * e.g. implement automatic token fees, slashing mechanisms, etc. 228 | * 229 | * Emits a {Transfer} event. 230 | * 231 | * Requirements: 232 | * 233 | * - `sender` cannot be the zero address. 234 | * - `recipient` cannot be the zero address. 235 | * - `sender` must have a balance of at least `amount`. 236 | */ 237 | function _transfer( 238 | address sender, 239 | address recipient, 240 | uint256 amount 241 | ) internal { 242 | require(sender != address(0), 'BEP20: transfer from the zero address'); 243 | require(recipient != address(0), 'BEP20: transfer to the zero address'); 244 | 245 | _balances[sender] = _balances[sender].sub(amount, 'BEP20: transfer amount exceeds balance'); 246 | _balances[recipient] = _balances[recipient].add(amount); 247 | emit Transfer(sender, recipient, amount); 248 | } 249 | 250 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing 251 | * the total supply. 252 | * 253 | * Emits a {Transfer} event with `from` set to the zero address. 254 | * 255 | * Requirements 256 | * 257 | * - `to` cannot be the zero address. 258 | */ 259 | // function _mint(address account, uint256 amount) internal { // Certik Wrong Update for _totalSupply 260 | // require(account != address(0), 'BEP20: mint to the zero address'); 261 | 262 | // _totalSupply = _totalSupply.add(amount); 263 | // _balances[account] = _balances[account].add(amount); 264 | // emit Transfer(address(0), account, amount); 265 | // } 266 | 267 | /** 268 | * @dev Destroys `amount` tokens from `account`, reducing the 269 | * total supply. 270 | * 271 | * Emits a {Transfer} event with `to` set to the zero address. 272 | * 273 | * Requirements 274 | * 275 | * - `account` cannot be the zero address. 276 | * - `account` must have at least `amount` tokens. 277 | */ 278 | function _burn(address account, uint256 amount) internal { 279 | require(account != address(0), 'BEP20: burn from the zero address'); 280 | 281 | _balances[account] = _balances[account].sub(amount, 'BEP20: burn amount exceeds balance'); 282 | _totalSupply = _totalSupply.sub(amount); 283 | emit Transfer(account, address(0), amount); 284 | } 285 | 286 | /** 287 | * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. 288 | * 289 | * This is internal function is equivalent to `approve`, and can be used to 290 | * e.g. set automatic allowances for certain subsystems, etc. 291 | * 292 | * Emits an {Approval} event. 293 | * 294 | * Requirements: 295 | * 296 | * - `owner` cannot be the zero address. 297 | * - `spender` cannot be the zero address. 298 | */ 299 | function _approve( 300 | address owner, 301 | address spender, 302 | uint256 amount 303 | ) internal { 304 | require(owner != address(0), 'BEP20: approve from the zero address'); 305 | require(spender != address(0), 'BEP20: approve to the zero address'); 306 | 307 | _allowances[owner][spender] = amount; 308 | emit Approval(owner, spender, amount); 309 | } 310 | 311 | /** 312 | * @dev Destroys `amount` tokens from `account`.`amount` is then deducted 313 | * from the caller's allowance. 314 | * 315 | * See {_burn} and {_approve}. 316 | */ 317 | function _burnFrom(address account, uint256 amount) internal { 318 | _burn(account, amount); 319 | _approve( 320 | account, 321 | _msgSender(), 322 | _allowances[account][_msgSender()].sub(amount, 'BEP20: burn amount exceeds allowance') 323 | ); 324 | } 325 | } 326 | -------------------------------------------------------------------------------- /contracts/DebloxNFT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.8.7; // Certik DCK-01 3 | 4 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; 5 | import "@openzeppelin/contracts/access/Ownable.sol"; 6 | import "@openzeppelin/contracts/utils/Counters.sol"; 7 | 8 | contract DebloxNFT is ERC721Enumerable, Ownable { 9 | using Counters for Counters.Counter; 10 | Counters.Counter private _tokenIds; 11 | 12 | using Strings for uint256; 13 | 14 | string private _URI; // Certik DNF-04 15 | string public baseExtension = ".json"; 16 | string public PROVENANCE; 17 | 18 | uint256 public price; 19 | uint256 public maxSupply; 20 | uint256 public maxMint; 21 | uint256 public addressLimit; // Certik DNF-05 22 | 23 | bool public sale = false; 24 | bool public locked = false; 25 | bool private reserved = false; 26 | uint256 public rNFT; 27 | 28 | bool public revealed = false; 29 | string public nonURI; 30 | 31 | bool public onlyWL; 32 | mapping(address => bool) public wlAddr; 33 | mapping(address => uint256) public addrMinted; 34 | 35 | event Base(); 36 | event Reveal(); 37 | event Price(); 38 | event Non(); 39 | event Limit(); 40 | event MintLimit(); 41 | event MaxSupply(); 42 | event FlipSale(); 43 | event FR(); 44 | event FlipWL(); 45 | event LockMD(); 46 | event Add(address indexed entry); 47 | event Remove(address indexed entry); 48 | event Reserve(); 49 | event Withdraw(uint256 balance); 50 | 51 | modifier notLocked { 52 | require(!locked, "NA9"); 53 | _; 54 | } 55 | 56 | constructor( 57 | string memory _name, 58 | string memory _symbol, 59 | string memory _RU, 60 | string memory _NRU, 61 | bool _OWL, 62 | uint256 _r 63 | ) ERC721(_name, _symbol) { 64 | setBaseURI(_RU); 65 | setNotRevealedURI(_NRU); 66 | onlyWL = _OWL; 67 | rNFT = _r; 68 | } 69 | 70 | function setBaseURI(string memory URI) public onlyOwner notLocked { 71 | _URI = URI; 72 | emit Base(); 73 | } 74 | 75 | function reveal() public onlyOwner { 76 | require(!sale, "NC1"); 77 | revealed = true; 78 | emit Reveal(); 79 | } 80 | 81 | function setPrice(uint256 _c) public onlyOwner { 82 | require(!sale, "NC2"); 83 | price = _c; 84 | emit Price(); 85 | } 86 | 87 | function setNotRevealedURI(string memory _NRU) public onlyOwner { 88 | require(!sale, "NC3"); 89 | nonURI = _NRU; 90 | emit Non(); 91 | } 92 | 93 | function setAddressLimit(uint256 _l) public onlyOwner { 94 | require(!sale, "NC4"); 95 | addressLimit = _l; 96 | emit Limit(); 97 | } 98 | 99 | function setMaxMint(uint256 _l) public onlyOwner { 100 | require(!sale, "NC5"); 101 | maxMint = _l; 102 | emit MintLimit(); 103 | } 104 | 105 | function setMaxSupply(uint256 _l) public onlyOwner { 106 | require(!sale, "NC6"); 107 | maxSupply = _l; 108 | emit MaxSupply(); 109 | } 110 | 111 | function flipSaleState() public onlyOwner { 112 | sale = !sale; 113 | emit FlipSale(); 114 | } 115 | 116 | // This function is only used for exceptional scenario 117 | function flipReserved() public onlyOwner { 118 | reserved = !reserved; 119 | emit FR(); 120 | } 121 | 122 | function flipWhitelisted() public onlyOwner { 123 | require(!sale, "NC7"); 124 | onlyWL = !onlyWL; 125 | emit FlipWL(); 126 | } 127 | 128 | // Owner functions for enabling presale, sale, revealing and setting the provenance hash 129 | function lockMetadata() external onlyOwner { 130 | locked = true; 131 | emit LockMD(); 132 | } 133 | 134 | function addToWhiteList(address[] calldata _e) external onlyOwner { 135 | for(uint256 i = 0; i < _e.length; i++) { 136 | address entry = _e[i]; 137 | require(entry != address(0), "NA0"); 138 | wlAddr[entry] = true; 139 | emit Add(entry); 140 | } 141 | } 142 | 143 | function removeFromWhiteList(address[] calldata _e) external onlyOwner { 144 | for(uint256 i = 0; i < _e.length; i++) { 145 | address entry = _e[i]; 146 | require(entry != address(0), "NA0"); 147 | wlAddr[entry] = false; 148 | emit Remove(entry); 149 | } 150 | } 151 | 152 | /** 153 | * @dev One time reserve function 154 | * And the reserved amount can be only set when contract is created 155 | */ 156 | function reserve() public onlyOwner { 157 | require(!reserved, "NA1"); 158 | 159 | reserved = true; 160 | 161 | uint i; 162 | for (i = 0; i < rNFT; i++) { 163 | _tokenIds.increment(); 164 | uint256 newItemId = _tokenIds.current(); 165 | 166 | addrMinted[msg.sender]++; 167 | _safeMint(msg.sender, newItemId); 168 | } 169 | 170 | emit Reserve(); 171 | } 172 | 173 | /** 174 | * @dev To mint given mint amount of NFT 175 | */ 176 | function mint(uint256 _a) external payable { 177 | require(sale, "NA2"); 178 | require(_a > 0, "NA3"); 179 | require(_a <= maxMint, "NA4"); 180 | require(price * _a <= msg.value, "NA5"); 181 | uint256 supply = totalSupply(); 182 | require(supply + _a <= maxSupply, "NA6"); 183 | 184 | uint256 ownerMintedCount = addrMinted[msg.sender]; 185 | require(ownerMintedCount + _a <= addressLimit, "NA7"); 186 | 187 | if(onlyWL == true) { 188 | require(wlAddr[msg.sender], "NA8"); 189 | } 190 | 191 | for (uint256 i = 1; i <= _a; i++) { 192 | _tokenIds.increment(); 193 | uint256 newItemId = _tokenIds.current(); 194 | 195 | addrMinted[msg.sender]++; 196 | _safeMint(msg.sender, newItemId); 197 | } 198 | } 199 | 200 | function withdraw() public onlyOwner { 201 | (bool os, ) = payable(owner()).call{value: address(this).balance}(""); 202 | require(os); 203 | 204 | emit Withdraw(address(this).balance); 205 | } 206 | 207 | // internal 208 | function _baseURI() internal view virtual override returns (string memory) { 209 | return _URI; 210 | } 211 | 212 | // View functions 213 | function isWhitelisted(address _a) external view returns (bool) { 214 | return wlAddr[_a]; 215 | } 216 | 217 | function walletOfOwner(address _owner) 218 | public 219 | view 220 | returns (uint256[] memory) 221 | { 222 | uint256 c = balanceOf(_owner); 223 | uint256[] memory tokenIds = new uint256[](c); 224 | for (uint256 i; i < c; i++) { 225 | tokenIds[i] = tokenOfOwnerByIndex(_owner, i); 226 | } 227 | return tokenIds; 228 | } 229 | 230 | function currentId() external view returns (uint256) { 231 | return _tokenIds.current(); 232 | } 233 | 234 | function getSaleStatus() external view returns (bool) { 235 | return sale; 236 | } 237 | 238 | function tokenURI(uint256 _t) public view override returns (string memory) { 239 | require(_exists(_t), "NC8"); 240 | 241 | if(revealed == false) { 242 | return nonURI; 243 | } 244 | 245 | string memory u = _baseURI(); 246 | return bytes(u).length > 0 247 | ? string(abi.encodePacked(u, _t.toString(), baseExtension)) 248 | : ""; 249 | } 250 | 251 | } 252 | -------------------------------------------------------------------------------- /contracts/GameSlot.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity 0.6.12; // Certik DCK-01 3 | 4 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; 6 | import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; 7 | import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol"; 8 | import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/SafeBEP20.sol"; 9 | import "@openzeppelin/contracts/access/Ownable.sol"; 10 | 11 | // Refer to Pancake SmartChef Contract: https://bscscan.com/address/0xCc2D359c3a99d9cfe8e6F31230142efF1C828e6D#readContract 12 | contract GameSlot is Ownable, ReentrancyGuard, IERC721Receiver { 13 | using SafeMath for uint256; 14 | using SafeBEP20 for IBEP20; 15 | 16 | IBEP20 public nativeToken; 17 | IBEP20 public gamePointToken; 18 | 19 | // The address of the manager 20 | address public SLOT_MANAGER; 21 | 22 | // Whether it is initialized 23 | bool public isInitialized; 24 | 25 | // The reserved tokens in the slot 26 | uint256 public reservedAmount; 27 | uint256 public unlockLimit; // GSD-03 28 | 29 | uint256 public constant MAX_PERFORMANCE_FEE = 5000; // 50% 30 | uint256 public performanceFee = 200; // 2% 31 | 32 | // Whether it is suspended 33 | bool public suspended = false; 34 | uint256 public tokenId; 35 | 36 | address public ownerAccount; 37 | address public admin; 38 | IERC721 public NFT; // The NFT token address that each game provider should hold 39 | 40 | // address public treasury; 41 | address public treasury; 42 | 43 | // Send funds to blacklisted addresses are not allowed 44 | mapping(address => bool) public blacklistAddresses; 45 | 46 | event AdminTokenRecovery(address indexed tokenRecovered, uint256 amount); // Certik GSD-01 47 | event EmergencyWithdraw(address indexed user, uint256 amount); 48 | event AddSlotEvent(uint256 indexed tokenId, address indexed gameAccount); 49 | event StatusChanged(address indexed slotAddress, bool suspended); 50 | event SlotUnlocked(address indexed user, address indexed gameAccount, uint256 amount); 51 | 52 | event AdminUpdated(address indexed user, address indexed admin); 53 | event Payout(address indexed user, address indexed receiver, uint256 amount); 54 | event CashoutPoints(address indexed user, uint256 amount); 55 | event AddToWhitelist(address indexed entry); 56 | event RemoveFromWhitelist(address indexed entry); 57 | event SetReservedAmount(uint256 amount); 58 | event SetPerformanceFee(uint256 amount); 59 | event SetUnlockLimitAmount(uint256 amount); 60 | event SetTreasury(address indexed amount); // Certik 61 | 62 | constructor( 63 | address _NFT, 64 | address _nativeToken, 65 | address _gamePointToken, 66 | uint256 _reservedAmount, 67 | address _manager 68 | ) public { 69 | require(_NFT != address(0), "_NFT is a zero address"); // Certik GSD-02 70 | require(_nativeToken != address(0), "_nativeToken is a zero address"); 71 | require(_gamePointToken != address(0), "_gamePointToken is a zero address"); 72 | 73 | NFT = IERC721(_NFT); 74 | // Set manager to SlotManager rather than factory 75 | SLOT_MANAGER = _manager; 76 | nativeToken = IBEP20(_nativeToken); 77 | gamePointToken = IBEP20(_gamePointToken); 78 | reservedAmount = _reservedAmount; 79 | 80 | treasury = _manager; 81 | } 82 | 83 | /** 84 | * @dev See {IERC721Receiver-onERC721Received}. 85 | * 86 | * Always returns `IERC721Receiver.onERC721Received.selector`. 87 | */ 88 | function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) { 89 | return this.onERC721Received.selector; 90 | } 91 | 92 | /* 93 | * @notice Initialize the contract, owner may not be the msg.sender 94 | * @param _tokenId: tokenId of the NFT 95 | * @param _owner: The current owner of NFT 96 | */ 97 | function initialize( 98 | uint256 _tokenId, 99 | address _owner 100 | ) external { 101 | require(!isInitialized, "Already initialized"); 102 | require(msg.sender == SLOT_MANAGER, "Not manager"); 103 | 104 | tokenId = _tokenId; 105 | _add(_tokenId, _owner); 106 | 107 | // Make this contract initialized 108 | isInitialized = true; 109 | 110 | emit AddSlotEvent(_tokenId, msg.sender); 111 | } 112 | 113 | /** 114 | * Game provider to bid slot 115 | * @param _owner is the NFT owner (Game provider) 116 | * @param _tokenId is the token owned by owner and to be transffered into this slot 117 | */ 118 | function _add(uint256 _tokenId, address _owner) private nonReentrant { 119 | require(NFT.ownerOf(_tokenId) != address(this), "Already owner"); 120 | NFT.safeTransferFrom( 121 | _owner, 122 | address(this), 123 | _tokenId 124 | ); 125 | // require(NFT.ownerOf(_tokenId) == address(this), "Not received"); // Certik GSD-09 126 | ownerAccount = _owner; 127 | // Admin account is set to same account of ownerAccount first 128 | admin = _owner; 129 | } 130 | 131 | /** 132 | * @notice This function is private and not privilege checking 133 | * Safe token transfer function for nativeToken, just in case if rounding error causes pool to not have enough tokens. 134 | */ 135 | function safeTransfer(address _to, uint256 _amount) private { 136 | uint256 bal = nativeToken.balanceOf(address(this)); 137 | if (_amount > bal) { 138 | nativeToken.safeTransfer(_to, bal); // Certik GSD-07 139 | } else { 140 | nativeToken.safeTransfer(_to, _amount); // Certik GSD-07 141 | } 142 | } 143 | 144 | /** 145 | * @notice This function is private and not privilege checking 146 | * Safe token transfer function for point token, just in case if rounding error causes pool to not have enough tokens. 147 | */ 148 | function safeTransferPoints(address _to, uint256 _amount) private { 149 | uint256 bal = gamePointToken.balanceOf(address(this)); 150 | if (_amount > bal) { 151 | gamePointToken.safeTransfer(_to, bal); // Certik GSD-07 152 | } else { 153 | gamePointToken.safeTransfer(_to, _amount); // Certik GSD-07 154 | } 155 | } 156 | 157 | // Owner is the GameSlot, and triggered by Game Slot owner / admin 158 | function payout(address _to, uint256 _amount) external nonReentrant { 159 | require(_to != address(0), "Cannot send to 0 address"); 160 | require(_amount > 0, "Must more than 0"); 161 | require(!suspended, "Slot is suspended"); 162 | require(msg.sender == admin, "Only the game admin can payout"); 163 | require(_balance() > reservedAmount, "Balance must more than reserved"); 164 | require(_amount <= (_balance() - reservedAmount), "Exceeded max payout-able amount"); 165 | require(!blacklistAddresses[_to], "user is blacklisted"); 166 | 167 | uint256 currentPerformanceFee = _amount.mul(performanceFee).div(10000); 168 | safeTransfer(treasury, currentPerformanceFee); 169 | safeTransfer(_to, _amount.sub(currentPerformanceFee)); 170 | 171 | emit Payout(msg.sender, _to, _amount); 172 | } 173 | 174 | /** 175 | * @notice Owner is the GameSlot, and triggered by Game Slot owner 176 | * There is no theshold to cashout 177 | */ 178 | function cashoutPoints(uint256 _amount) external nonReentrant { 179 | require(_amount > 0, "Must more than 0"); 180 | require(!suspended, "Slot is suspended"); 181 | require(msg.sender == admin || msg.sender == ownerAccount, "Only the game owner or admin can cashout"); 182 | require(_amount <= _balanceOfPoints(), "Exceeded max game points amount"); 183 | 184 | safeTransferPoints(ownerAccount, _amount); 185 | 186 | emit CashoutPoints(msg.sender, _amount); 187 | } 188 | 189 | /** 190 | * Unlock NFT and return the slot back 191 | * Only return the current tokenId back to ownerAccount 192 | * 193 | * TODO: need more rules to unlock slot 194 | */ 195 | function unlock() external onlyOwner { 196 | require(_balance() < unlockLimit, "Balance must be less than balance before unlock"); // Certik GSD-03 197 | 198 | NFT.transferFrom( 199 | address(this), 200 | ownerAccount, 201 | tokenId 202 | ); 203 | 204 | isInitialized = false; 205 | 206 | emit SlotUnlocked(msg.sender, ownerAccount, tokenId); 207 | } 208 | 209 | function addToBlacklist(address[] calldata entries) external onlyOwner { 210 | for(uint256 i = 0; i < entries.length; i++) { 211 | address entry = entries[i]; 212 | require(entry != address(0), "NULL_ADDRESS"); 213 | 214 | blacklistAddresses[entry] = true; 215 | emit AddToWhitelist(entry); 216 | } 217 | } 218 | 219 | function removeFromBlacklist(address[] calldata entries) external onlyOwner { 220 | for(uint256 i = 0; i < entries.length; i++) { 221 | address entry = entries[i]; 222 | require(entry != address(0), "NULL_ADDRESS"); 223 | 224 | blacklistAddresses[entry] = false; 225 | emit RemoveFromWhitelist(entry); 226 | } 227 | } 228 | 229 | /* 230 | * @notice Withdraw staked tokens without caring other factor 231 | * The funds will be sent to treasury, and the team need to manually refund back to users affected 232 | * 233 | * @dev TODO: Needs to be for emergency. 234 | */ 235 | function emergencyWithdraw() external onlyOwner { 236 | uint256 balance = _balance(); 237 | nativeToken.safeTransfer(treasury, balance); 238 | 239 | emit EmergencyWithdraw(msg.sender, balance); 240 | } 241 | 242 | /** 243 | * @notice It allows the admin to recover wrong tokens sent to the contract 244 | * @param _tokenAddress: the address of the token to withdraw 245 | * @param _tokenAmount: the number of tokens to withdraw 246 | * @dev This function is only callable by admin. 247 | */ 248 | function recoverWrongTokens(address _tokenAddress, uint256 _tokenAmount) external onlyOwner { 249 | require(_tokenAddress != address(nativeToken), "Cannot be native token"); 250 | require(_tokenAddress != address(gamePointToken), "Cannot be point token"); 251 | IBEP20(_tokenAddress).safeTransfer(treasury, _tokenAmount); 252 | 253 | emit AdminTokenRecovery(_tokenAddress, _tokenAmount); 254 | } 255 | 256 | /** 257 | * @notice Sets Reserved Amount of native token 258 | * Can be zero only if the owner want to leave the slot 259 | * 260 | * @dev Only callable by the contract admin. 261 | */ 262 | function setAdmin(address _admin) external { 263 | require(_admin != address(0), "Address cannot be 0"); 264 | require(msg.sender == ownerAccount, "Only the game owener can update admin"); 265 | admin = _admin; 266 | 267 | emit AdminUpdated(msg.sender, _admin); 268 | } 269 | 270 | /** 271 | * @notice Sets Reserved Amount of native token 272 | * Can be zero only if the owner want to leave the slot 273 | * 274 | * @dev Only callable by the contract admin. 275 | */ 276 | function setReservedAmount(uint256 _reservedAmount) external onlyOwner { 277 | reservedAmount = _reservedAmount; 278 | emit SetReservedAmount(reservedAmount); 279 | } 280 | 281 | /** 282 | * @notice Only if unlockLimit is set and the balance is less than unlockLimit 283 | * Then it's able to unlock 284 | */ 285 | function setUnlockLimitAmount(uint256 _unlockLimit) external onlyOwner { 286 | unlockLimit = _unlockLimit; 287 | emit SetUnlockLimitAmount(_unlockLimit); 288 | } 289 | 290 | /** 291 | * @notice Sets performance fee 292 | * @dev Only callable by the contract admin. 293 | */ 294 | function setPerformanceFee(uint256 _performanceFee) external onlyOwner { 295 | require(_performanceFee <= MAX_PERFORMANCE_FEE, "performanceFee cannot be more than MAX_PERFORMANCE_FEE"); 296 | performanceFee = _performanceFee; 297 | emit SetPerformanceFee(performanceFee); 298 | } 299 | 300 | /** 301 | * @notice Sets treasury address 302 | * @dev Only callable by the contract owner. 303 | */ 304 | function setTreasury(address _treasury) external onlyOwner { 305 | require(_treasury != address(0), "Cannot be zero address"); 306 | treasury = _treasury; 307 | emit SetTreasury(treasury); 308 | } 309 | 310 | /** 311 | * @notice Sets treasury address 312 | * @dev Only callable by the contract owner. 313 | */ 314 | function flipSuspendState() external onlyOwner { 315 | suspended = !suspended; 316 | 317 | emit StatusChanged(msg.sender, suspended); 318 | } 319 | 320 | /** View functions */ 321 | function initialized() external view returns (bool) { 322 | return isInitialized; 323 | } 324 | 325 | function isSuspended() external view returns (bool) { 326 | return suspended; 327 | } 328 | 329 | function balanceOfPoints() external view returns (uint256) { 330 | return _balanceOfPoints(); 331 | } 332 | 333 | function _balanceOfPoints() private view returns (uint256) { 334 | return gamePointToken.balanceOf(address(this)); 335 | } 336 | 337 | function balance() external view returns (uint256) { 338 | return _balance(); 339 | } 340 | 341 | function _balance() private view returns (uint256) { 342 | return nativeToken.balanceOf(address(this)); 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /contracts/NFTAirdrop.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0-only 2 | pragma solidity 0.6.12; // Certik DCK-01 3 | 4 | import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/IBEP20.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/IERC721.sol"; 6 | import "@pancakeswap/pancake-swap-lib/contracts/token/BEP20/SafeBEP20.sol"; 7 | import "@openzeppelin/contracts/access/Ownable.sol"; 8 | 9 | contract NFTAirdrop is Ownable { 10 | using SafeBEP20 for IBEP20; 11 | 12 | IERC721 internal immutable nft; 13 | IBEP20 public tokenToClaim; 14 | uint256 public immutable tokensPerClaim; 15 | 16 | event Claimed(uint256 indexed tokenId, address indexed claimer); 17 | event FlipClaimState(); 18 | 19 | mapping(uint256 => bool) public hasClaimed; 20 | bool public claimable = false; 21 | 22 | constructor( 23 | address _nft, // Certik NFT-04 24 | address _tokenToClaim, 25 | uint256 _tokensPerClaim 26 | ) public { 27 | require(_nft != address(0), "_nft is a zero address"); // Certik NFT-04 28 | require(_tokenToClaim != address(0), "_tokenToClaim is a zero address"); 29 | 30 | nft = IERC721(_nft); 31 | tokenToClaim = IBEP20(_tokenToClaim); 32 | tokensPerClaim = _tokensPerClaim; 33 | } 34 | 35 | function flipClaimState() public onlyOwner { 36 | claimable = !claimable; 37 | emit FlipClaimState(); // Certik NFT-03 38 | } 39 | 40 | function claim(uint256 tokenId) external { // Certik Unncessary payable Modifier 41 | require(claimable, "Claim is not available!"); 42 | require(_balance() > 0, "Run out of tokens, please contact admin"); 43 | 44 | require(!hasClaimed[tokenId], "Already claimed"); 45 | require(nft.ownerOf(tokenId) == msg.sender, "Not onwer"); 46 | 47 | hasClaimed[tokenId] = true; 48 | emit Claimed(tokenId, msg.sender); 49 | 50 | tokenToClaim.safeTransfer(msg.sender, tokensPerClaim); 51 | } 52 | 53 | function batchClaim(uint256[] memory tokenIds) external { // Certik Unncessary payable Modifier 54 | require(claimable, "Claim is not available!"); 55 | require(_balance() > 0, "Run out of tokens, please contact admin"); 56 | 57 | for (uint256 index = 0; index < tokenIds.length; index++) { 58 | uint256 tokenId = tokenIds[index]; 59 | 60 | require(!hasClaimed[tokenId], "Already claimed"); 61 | require(nft.ownerOf(tokenId) == msg.sender, "Not onwer"); 62 | 63 | hasClaimed[tokenId] = true; 64 | emit Claimed(tokenId, msg.sender); 65 | } 66 | 67 | tokenToClaim.safeTransfer(msg.sender, tokensPerClaim * tokenIds.length); 68 | } 69 | 70 | function claimableTokenIds(uint256[] memory tokenIds) 71 | public 72 | view 73 | returns (uint256[] memory) 74 | { 75 | uint256 length = tokenIds.length; 76 | uint256[] memory claimableIds = new uint256[](length); 77 | for (uint256 i; i < length; i++) { 78 | uint256 tokenId = tokenIds[i]; 79 | if(!hasClaimed[tokenId]) { 80 | claimableIds[i] = tokenId; 81 | } 82 | } 83 | return claimableIds; 84 | } 85 | 86 | function balance() public view returns (uint256) { 87 | return _balance(); 88 | } 89 | 90 | function _balance() internal view returns (uint256) { 91 | return tokenToClaim.balanceOf(address(this)); 92 | } 93 | } 94 | --------------------------------------------------------------------------------