├── .gitignore ├── LICENSE ├── README.md ├── alexa-skill ├── .gitignore ├── README.md ├── lambda │ ├── index.js │ ├── local-debugger.js │ ├── package-lock.json │ ├── package.json │ └── util.js └── skill-package │ ├── interactionModels │ └── custom │ │ └── pt-BR.json │ └── skill.json ├── blockchain ├── .gitignore ├── Block.js ├── Blockchain.js ├── README.md ├── index.js ├── package-lock.json └── package.json ├── brave-transfer ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── ABI.json │ ├── App.js │ ├── BraveService.js │ └── index.js ├── brave-web3-react ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── ABI.json │ ├── App.js │ ├── BraveService.js │ └── index.js ├── cryptobubbles ├── README.md └── index.html ├── dapp-event-ethers ├── .env.example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── index.css │ └── index.js ├── dapp-event-web3 ├── .env.example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── abi.json │ └── index.js ├── dapp-react-ethers ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── abi.json │ ├── index.css │ └── index.js ├── dapp-react-web3 ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── abi.json │ ├── index.css │ └── index.js ├── faucet ├── .env.example ├── README.md ├── package.json └── src │ ├── SecurityProvider.js │ ├── Web3Provider.js │ └── index.js ├── metamask-ethers-node ├── .env.example ├── .gitignore ├── README.md ├── abi.example.json ├── index.js ├── package-lock.json └── package.json ├── metamask-login ├── .env.example ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.css │ ├── App.js │ ├── AppService.js │ ├── index.css │ └── index.js ├── metamask-transfer ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── ABI.json │ ├── App.js │ ├── MetaMaskService.js │ └── index.js ├── metamask-web3-node ├── .env.example ├── .gitignore ├── README.md ├── abi.example.json ├── index.js ├── package-lock.json └── package.json ├── metamask-web3-react ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.js │ ├── MetaMaskService.js │ └── index.js ├── nodejs-event-ethers ├── .env.example ├── README.md ├── index.js ├── package-lock.json └── package.json ├── nodejs-event-web3 ├── .env.example ├── README.md ├── abi.json ├── index.js ├── package-lock.json └── package.json ├── pinata-ipfs-example ├── .env.example ├── .gitignore ├── README.md ├── jsconfig.json ├── next.config.js ├── package.json ├── public │ ├── favicon.ico │ ├── next.svg │ └── vercel.svg └── src │ ├── pages │ ├── _app.js │ ├── _document.js │ └── index.js │ └── styles │ ├── Home.module.css │ └── globals.css ├── storage-oracle-example ├── README.md ├── storage-oracle-backend │ ├── .env.example │ ├── README.md │ ├── abi.json │ ├── index.js │ ├── package-lock.json │ └── package.json ├── storage-oracle-consumer │ ├── OracleConsumer.sol │ └── README.md └── storage-oracle-contract │ ├── README.md │ └── StorageOracle.sol ├── wallet-btcjs ├── README.md ├── WalletService.js ├── createWallet.js ├── index.js ├── package-lock.json └── package.json └── wallet-ethers ├── .env.example ├── README.md ├── WalletService.js ├── index.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .secret 3 | 4 | # Logs 5 | logs 6 | *.log 7 | npm-debug.log* 8 | yarn-debug.log* 9 | yarn-error.log* 10 | lerna-debug.log* 11 | 12 | # Diagnostic reports (https://nodejs.org/api/report.html) 13 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 14 | 15 | # Runtime data 16 | pids 17 | *.pid 18 | *.seed 19 | *.pid.lock 20 | 21 | # Directory for instrumented libs generated by jscoverage/JSCover 22 | lib-cov 23 | 24 | # Coverage directory used by tools like istanbul 25 | coverage 26 | *.lcov 27 | 28 | # nyc test coverage 29 | .nyc_output 30 | 31 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 32 | .grunt 33 | 34 | # Bower dependency directory (https://bower.io/) 35 | bower_components 36 | 37 | # node-waf configuration 38 | .lock-wscript 39 | 40 | # Compiled binary addons (https://nodejs.org/api/addons.html) 41 | build/Release 42 | 43 | # Dependency directories 44 | node_modules/ 45 | jspm_packages/ 46 | 47 | # TypeScript v1 declaration files 48 | typings/ 49 | 50 | # TypeScript cache 51 | *.tsbuildinfo 52 | 53 | # Optional npm cache directory 54 | .npm 55 | 56 | # Optional eslint cache 57 | .eslintcache 58 | 59 | # Microbundle cache 60 | .rpt2_cache/ 61 | .rts2_cache_cjs/ 62 | .rts2_cache_es/ 63 | .rts2_cache_umd/ 64 | 65 | # Optional REPL history 66 | .node_repl_history 67 | 68 | # Output of 'npm pack' 69 | *.tgz 70 | 71 | # Yarn Integrity file 72 | .yarn-integrity 73 | 74 | # dotenv environment variables file 75 | .env 76 | .env.test 77 | 78 | # parcel-bundler cache (https://parceljs.org/) 79 | .cache 80 | 81 | # Next.js build output 82 | .next 83 | 84 | # Nuxt.js build / generate output 85 | .nuxt 86 | dist 87 | 88 | # Gatsby files 89 | .cache/ 90 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 91 | # https://nextjs.org/blog/next-9-1#public-directory-support 92 | # public 93 | 94 | # vuepress build output 95 | .vuepress/dist 96 | 97 | # Serverless directories 98 | .serverless/ 99 | 100 | # FuseBox cache 101 | .fusebox/ 102 | 103 | # DynamoDB Local files 104 | .dynamodb/ 105 | 106 | # TernJS port file 107 | .tern-port 108 | -------------------------------------------------------------------------------- /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 | # web3-examples 2 | Simple Web3/dapps and crypto website examples. 3 | Use the respective readmes to understand and run the projects. 4 | 5 | ## More 6 | 7 | Follow me on social networks: https://about.me/luiztools 8 | 9 | Receive news on Telegram: https://t.me/luiznews 10 | 11 | Know my web3/blockchain course (portuguese): https://www.luiztools.com.br/curso-web23 -------------------------------------------------------------------------------- /alexa-skill/.gitignore: -------------------------------------------------------------------------------- 1 | ask-resources.json 2 | .ask 3 | .vscode 4 | lambda/node_modules -------------------------------------------------------------------------------- /alexa-skill/README.md: -------------------------------------------------------------------------------- 1 | # alexa-skill 2 | A simple Alexa skill (Amazon Echo assistant) to obtain crypto market info. 3 | 4 | ## Instruções 5 | 6 | Execute npm install na pasta do projeto que vai executar para baixar dependências 7 | 8 | Leia o tutorial da Alexa Skill (inclui vídeo): https://www.luiztools.com.br/post/tutorial-de-skill-para-amazon-alexa-em-node-js/ 9 | 10 | Conheça meus livros: https://www.luiztools.com.br/meus-livros 11 | 12 | Conheça meu curso de bot cripto: https://www.luiztools.com.br/curso-beholder 13 | 14 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 15 | 16 | Me siga nas redes sociais: https://about.me/luiztools 17 | 18 | Receba novidades no Telegram: https://t.me/luiznews 19 | -------------------------------------------------------------------------------- /alexa-skill/lambda/local-debugger.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. 3 | * Licensed under the Apache License, Version 2.0 (the "License"). 4 | * You may not use this file except in compliance with the License. 5 | * A copy of the License is located at 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * or in the "license" file accompanying this file. This file is distributed 9 | * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either 10 | * express or implied. See the License for the specific language governing 11 | * permissions and limitations under the License. 12 | */ 13 | 14 | /* ## DEPRECATION NOTICE 15 | 16 | This script has been deprecated and is no longer supported. 17 | Please use the [ASK Toolkit for VS Code] 18 | (https://marketplace.visualstudio.com/items?itemName=ask-toolkit.alexa-skills-kit-toolkit), 19 | which provides a more end-to-end integration with Visual Studio Code. If you 20 | use another editor/IDE, please check out the [ASK SDK Local Debug package at npm] 21 | (https://www.npmjs.com/package/ask-sdk-local-debug). 22 | 23 | */ 24 | 25 | const net = require('net'); 26 | const fs = require('fs'); 27 | 28 | const localDebugger = net.createServer(); 29 | 30 | const httpHeaderDelimeter = '\r\n'; 31 | const httpBodyDelimeter = '\r\n\r\n'; 32 | const defaultHandlerName = 'handler'; 33 | const host = 'localhost'; 34 | const defaultPort = 0; 35 | 36 | /** 37 | * Resolves the skill invoker class dependency from the user provided 38 | * skill entry file. 39 | */ 40 | 41 | // eslint-disable-next-line import/no-dynamic-require 42 | const skillInvoker = require(getAndValidateSkillInvokerFile()); 43 | const portNumber = getAndValidatePortNumber(); 44 | const lambdaHandlerName = getLambdaHandlerName(); 45 | 46 | /** 47 | * Starts listening on the port for incoming skill requests. 48 | */ 49 | 50 | localDebugger.listen(portNumber, host, () => { 51 | console.log(`Starting server on port: ${localDebugger.address().port}.`); 52 | }); 53 | 54 | /** 55 | * For a new incoming skill request a new socket connection is established. 56 | * From the data received on the socket the request body is extracted, parsed into 57 | * JSON and passed to the skill invoker's lambda handler. 58 | * The response from the lambda handler is parsed as a HTTP 200 message format as specified 59 | * here - https://developer.amazon.com/docs/custom-skills/request-and-response-json-reference.html#http-header-1 60 | * The response is written onto the socket connection. 61 | */ 62 | 63 | localDebugger.on('connection', (socket) => { 64 | console.log(`Connection from: ${socket.remoteAddress}:${socket.remotePort}`); 65 | socket.on('data', (data) => { 66 | const body = JSON.parse(data.toString().split(httpBodyDelimeter).pop()); 67 | console.log(`Request envelope: ${JSON.stringify(body)}`); 68 | skillInvoker[lambdaHandlerName](body, null, (_invokeErr, response) => { 69 | response = JSON.stringify(response); 70 | console.log(`Response envelope: ${response}`); 71 | socket.write(`HTTP/1.1 200 OK${httpHeaderDelimeter}Content-Type: application/json;charset=UTF-8${httpHeaderDelimeter}Content-Length: ${response.length}${httpBodyDelimeter}${response}`); 72 | }); 73 | }); 74 | }); 75 | 76 | /** 77 | * Validates user specified port number is in legal range [0, 65535]. 78 | * Defaults to 0. 79 | */ 80 | 81 | function getAndValidatePortNumber() { 82 | const portNumberArgument = Number(getArgument('portNumber', defaultPort)); 83 | if (!Number.isInteger(portNumberArgument)) { 84 | throw new Error(`Port number has to be an integer - ${portNumberArgument}.`); 85 | } 86 | if (portNumberArgument < 0 || portNumberArgument > 65535) { 87 | throw new Error(`Port out of legal range: ${portNumberArgument}. The port number should be in the range [0, 65535]`); 88 | } 89 | if (portNumberArgument === 0) { 90 | console.log('The TCP server will listen on a port that is free.' 91 | + 'Check logs to find out what port number is being used'); 92 | } 93 | return portNumberArgument; 94 | } 95 | 96 | /** 97 | * Gets the lambda handler name. 98 | * Defaults to "handler". 99 | */ 100 | 101 | function getLambdaHandlerName() { 102 | return getArgument('lambdaHandler', defaultHandlerName); 103 | } 104 | 105 | /** 106 | * Validates that the skill entry file exists on the path specified. 107 | * This is a required field. 108 | */ 109 | 110 | // eslint-disable-next-line consistent-return 111 | function getAndValidateSkillInvokerFile() { 112 | const fileNameArgument = getArgument('skillEntryFile'); 113 | if (!fs.existsSync(fileNameArgument)) { 114 | throw new Error(`File not found: ${fileNameArgument}`); 115 | } 116 | return fileNameArgument; 117 | } 118 | 119 | /** 120 | * Helper function to fetch the value for a given argument 121 | * @param {argumentName} argumentName name of the argument for which the value needs to be fetched 122 | * @param {defaultValue} defaultValue default value of the argument that is returned if the value doesn't exist 123 | */ 124 | 125 | function getArgument(argumentName, defaultValue) { 126 | const index = process.argv.indexOf(`--${argumentName}`); 127 | if (index === -1 || typeof process.argv[index + 1] === 'undefined') { 128 | if (defaultValue === undefined) { 129 | throw new Error(`Required argument - ${argumentName} not provided.`); 130 | } else { 131 | return defaultValue; 132 | } 133 | } 134 | return process.argv[index + 1]; 135 | } 136 | -------------------------------------------------------------------------------- /alexa-skill/lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hello-world", 3 | "version": "1.2.0", 4 | "description": "alexa utility for quickly building skills", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "Amazon Alexa", 10 | "license": "Apache License", 11 | "dependencies": { 12 | "ask-sdk-core": "^2.7.0", 13 | "ask-sdk-model": "^1.19.0", 14 | "aws-sdk": "^2.326.0", 15 | "axios": "^0.24.0" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /alexa-skill/lambda/util.js: -------------------------------------------------------------------------------- 1 | const AWS = require('aws-sdk'); 2 | 3 | const s3SigV4Client = new AWS.S3({ 4 | signatureVersion: 'v4', 5 | region: process.env.S3_PERSISTENCE_REGION 6 | }); 7 | 8 | module.exports.getS3PreSignedUrl = function getS3PreSignedUrl(s3ObjectKey) { 9 | 10 | const bucketName = process.env.S3_PERSISTENCE_BUCKET; 11 | const s3PreSignedUrl = s3SigV4Client.getSignedUrl('getObject', { 12 | Bucket: bucketName, 13 | Key: s3ObjectKey, 14 | Expires: 60*1 // the Expires is capped for 1 minute 15 | }); 16 | console.log(`Util.s3PreSignedUrl: ${s3ObjectKey} URL ${s3PreSignedUrl}`); 17 | return s3PreSignedUrl; 18 | 19 | } -------------------------------------------------------------------------------- /alexa-skill/skill-package/interactionModels/custom/pt-BR.json: -------------------------------------------------------------------------------- 1 | { 2 | "interactionModel": { 3 | "languageModel": { 4 | "invocationName": "abrir teste cripto", 5 | "intents": [ 6 | { 7 | "name": "AMAZON.CancelIntent", 8 | "samples": [] 9 | }, 10 | { 11 | "name": "AMAZON.HelpIntent", 12 | "samples": [] 13 | }, 14 | { 15 | "name": "AMAZON.StopIntent", 16 | "samples": [] 17 | }, 18 | { 19 | "name": "GetCryptoIntent", 20 | "slots": [ 21 | { 22 | "name": "crypto", 23 | "type": "AMAZON.SearchQuery" 24 | } 25 | ], 26 | "samples": [ 27 | "cotação do {crypto}", 28 | "valor do {crypto}", 29 | "preço do {crypto}" 30 | ] 31 | }, 32 | { 33 | "name": "AMAZON.NavigateHomeIntent", 34 | "samples": [] 35 | } 36 | ], 37 | "types": [] 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /alexa-skill/skill-package/skill.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest": { 3 | "apis": { 4 | "custom": { 5 | "endpoint": { 6 | "uri": "arn:aws:lambda:us-east-1:398930324784:function:79ebf50c-d253-42dd-8349-a2ff88285fbd:Release_0" 7 | }, 8 | "regions": { 9 | "EU": { 10 | "endpoint": { 11 | "uri": "arn:aws:lambda:eu-west-1:398930324784:function:79ebf50c-d253-42dd-8349-a2ff88285fbd:Release_0" 12 | } 13 | }, 14 | "NA": { 15 | "endpoint": { 16 | "uri": "arn:aws:lambda:us-east-1:398930324784:function:79ebf50c-d253-42dd-8349-a2ff88285fbd:Release_0" 17 | } 18 | }, 19 | "FE": { 20 | "endpoint": { 21 | "uri": "arn:aws:lambda:us-west-2:398930324784:function:79ebf50c-d253-42dd-8349-a2ff88285fbd:Release_0" 22 | } 23 | } 24 | } 25 | } 26 | }, 27 | "publishingInformation": { 28 | "locales": { 29 | "pt-BR": { 30 | "name": "LuizTools2" 31 | } 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /blockchain/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules -------------------------------------------------------------------------------- /blockchain/Block.js: -------------------------------------------------------------------------------- 1 | const sha256 = require('crypto-js/sha256'); 2 | 3 | module.exports = class Block { 4 | constructor(index = 0, previousHash = null, data = 'Genesis Block', difficulty = 1) { 5 | this.index = index; 6 | this.previousHash = previousHash; 7 | this.data = data; 8 | this.timestamp = new Date(); 9 | this.nonce = 0; 10 | 11 | const prefix = new Array(difficulty + 1).join("0"); 12 | this.mine(prefix); 13 | } 14 | 15 | mine(prefix) { 16 | do { 17 | this.nonce++; 18 | this.hash = this.generateHash(); 19 | } 20 | while (!this.hash.startsWith(prefix)); 21 | } 22 | 23 | generateHash() { 24 | return sha256(this.index + this.previousHash + JSON.stringify(this.data) + this.timestamp + this.nonce).toString(); 25 | } 26 | } -------------------------------------------------------------------------------- /blockchain/Blockchain.js: -------------------------------------------------------------------------------- 1 | const Block = require('./block') 2 | 3 | module.exports = class Blockchain { 4 | constructor() { 5 | this.blocks = [new Block()]; 6 | this.nextIndex = 1; 7 | } 8 | 9 | getLastHash() { 10 | return this.blocks[this.blocks.length - 1].hash; 11 | } 12 | 13 | addBlock(data, difficulty = 1) { 14 | const hash = this.getLastHash(); 15 | const block = new Block(this.nextIndex++, hash, data, difficulty); 16 | this.blocks.push(block); 17 | } 18 | 19 | isValid() { 20 | for (let i = this.blocks.length - 1; i > 0; i--) { 21 | const currentBlock = this.blocks[i]; 22 | const previousBlock = this.blocks[i - 1]; 23 | 24 | if (currentBlock.hash !== currentBlock.generateHash() 25 | || currentBlock.previousHash !== previousBlock.hash 26 | || currentBlock.index !== previousBlock.index + 1) { 27 | return false 28 | } 29 | } 30 | return true 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /blockchain/README.md: -------------------------------------------------------------------------------- 1 | # blockchain 2 | 3 | A simple blockchain example written in JavaScript/Node.js for teaching purposes. 4 | 5 | ## How to run 6 | 1. Enter in blockchain folder 7 | 2. npm install 8 | 3. node index (adjust as you like) 9 | 10 | ## Versions 11 | - Part 1: git checkout c9d549964f761538963af1ebc022fbad416e8e85 12 | - Part 2: latest 13 | 14 | Leia o tutorial ou assista aos vídeos em meu blog: https://www.luiztools.com.br/post/entendendo-a-blockchain-na-pratica/ 15 | 16 | Conheça meus livros: https://www.luiztools.com.br/meus-livros 17 | 18 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 19 | 20 | Me siga nas redes sociais: https://about.me/luiztools 21 | 22 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /blockchain/index.js: -------------------------------------------------------------------------------- 1 | //TESTE 1 2 | // const Block = require("./Block"); 3 | // const bloco1 = new Block(); 4 | // console.log(bloco1); 5 | 6 | //TESTE 2 7 | // const Blockchain = require("./Blockchain"); 8 | // const blockchain = new Blockchain(); 9 | // blockchain.addBlock([{ from: 'a', to: 'b', amount: 10 }]); 10 | // console.log(blockchain); 11 | 12 | //TESTE 3 13 | // const Blockchain = require("./Blockchain"); 14 | // const blockchain = new Blockchain(); 15 | // blockchain.addBlock([{ from: 'a', to: 'b', amount: 10 }]); 16 | // blockchain.addBlock([{ from: 'b', to: 'c', amount: 15 }]); 17 | // console.log(blockchain); 18 | // blockchain.blocks[1].data = { from: 'a', to: 'b', amount: 1000 }; 19 | // console.log(JSON.stringify(blockchain)); 20 | 21 | //TESTE 4 22 | // const Blockchain = require("./Blockchain"); 23 | 24 | // const blockchain = new Blockchain(); 25 | // blockchain.addBlock([{ from: 'a', to: 'b', amount: 10 }]); 26 | // blockchain.addBlock([{ from: 'b', to: 'c', amount: 15 }]); 27 | // console.log(blockchain); 28 | // console.log(blockchain.isValid()); 29 | 30 | // blockchain.blocks[1].data = { from: 'a', to: 'b', amount: 1000 }; 31 | // console.log(blockchain); 32 | // console.log(blockchain.isValid()); 33 | 34 | //TESTE 5 35 | const Blockchain = require("./Blockchain"); 36 | 37 | const blockchain = new Blockchain(); 38 | blockchain.addBlock([{ from: 'a', to: 'b', amount: 10 }], 4); 39 | blockchain.addBlock([{ from: 'b', to: 'c', amount: 15 }], 4); 40 | console.log(blockchain); 41 | console.log(blockchain.isValid()); -------------------------------------------------------------------------------- /blockchain/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockchain", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "blockchain", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "crypto-js": "^4.1.1" 13 | } 14 | }, 15 | "node_modules/crypto-js": { 16 | "version": "4.1.1", 17 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", 18 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 19 | } 20 | }, 21 | "dependencies": { 22 | "crypto-js": { 23 | "version": "4.1.1", 24 | "resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.1.1.tgz", 25 | "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==" 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /blockchain/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blockchain", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "crypto-js": "^4.1.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /brave-transfer/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /brave-transfer/README.md: -------------------------------------------------------------------------------- 1 | # brave-transfer 2 | Simple Brave Wallet balance and transfer example. 3 | 4 | ## Instruções 5 | 6 | 1. npm install 7 | 2. npm start 8 | 9 | ## Mais informações 10 | 11 | Leia o tutorial da Brave (inclui vídeo): https://www.luiztools.com.br/post/integracao-com-brave-wallet-via-ethersjs 12 | 13 | Conheça meu curso de web3/blockchain: https://www.luiztools.com.br/curso-web23 14 | 15 | Conheça meus livros: https://www.luiztools.com.br/meus-livros 16 | 17 | Me siga nas redes sociais: https://about.me/luiztools 18 | 19 | Receba novidades no Telegram: https://t.me/luiznews 20 | -------------------------------------------------------------------------------- /brave-transfer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "brave-transfer", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.17.0", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "ethers": "^6.13.0", 10 | "react": "^18.3.1", 11 | "react-dom": "^18.3.1", 12 | "react-scripts": "5.0.1", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /brave-transfer/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/brave-transfer/public/favicon.ico -------------------------------------------------------------------------------- /brave-transfer/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /brave-transfer/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/brave-transfer/public/logo192.png -------------------------------------------------------------------------------- /brave-transfer/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/brave-transfer/public/logo512.png -------------------------------------------------------------------------------- /brave-transfer/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /brave-transfer/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /brave-transfer/src/ABI.json: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}] 2 | -------------------------------------------------------------------------------- /brave-transfer/src/App.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { getTokenBalance, getBnbBalance, transferBnb, transferToken, getTransaction } from './BraveService'; 3 | 4 | function App() { 5 | 6 | const [address, setAddress] = useState("0x4822D23cF608556a13d6173998864A521FCd79B5"); 7 | const [contract, setContract] = useState("BNB"); 8 | const [balance, setBalance] = useState(''); 9 | 10 | const [toAddress, setToAddress] = useState(""); 11 | const [quantity, setQuantity] = useState(""); 12 | const [message, setMessage] = useState(''); 13 | const [transaction, setTransaction] = useState("0x739c13460f1bc11911cfbd157b61d5b871b7686381a39682a1a31cf5c8f8e50e"); 14 | 15 | async function checkBalance() { 16 | let balance; 17 | 18 | if (contract === "BNB") 19 | balance = await getBnbBalance(address); 20 | else 21 | balance = await getTokenBalance(address, contract); 22 | 23 | setBalance(balance); 24 | setMessage(``); 25 | } 26 | 27 | async function checkTransaction() { 28 | const result = await getTransaction(transaction); 29 | setMessage(` 30 | Status: ${result.status} 31 | Confirmations: ${await result.confirmations()}`); 32 | } 33 | 34 | async function transfer() { 35 | let result; 36 | if (contract === "BNB") 37 | result = await transferBnb(toAddress, quantity); 38 | else 39 | result = await transferToken(toAddress, contract, quantity); 40 | 41 | setMessage(JSON.stringify(result)); 42 | } 43 | 44 | return ( 45 |
46 |

47 | My Address : setAddress(evt.target.value)} value={address} /> 48 |

49 |

50 | 56 | checkBalance()} /> 57 |

58 |

59 | Balance: {balance} 60 |

61 |
62 |

63 | To Address: setToAddress(evt.target.value)} /> 64 |

65 |

66 | Qty: setQuantity(evt.target.value)} /> 67 |

68 |

69 | transfer()} /> 70 |

71 |
72 |

73 | Transaction: setTransaction(evt.target.value)} /> 74 | checkTransaction()} /> 75 |

76 |

77 | {message} 78 |

79 |
80 | ); 81 | } 82 | 83 | export default App; 84 | -------------------------------------------------------------------------------- /brave-transfer/src/BraveService.js: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | 3 | import CONTRACT_ABI from "./ABI.json"; 4 | 5 | async function getBraveProvider() { 6 | if (!window.ethereum) throw new Error(`No Brave Wallet found!`); 7 | const provider = new ethers.BrowserProvider(window.ethereum, "any"); 8 | 9 | const accounts = await provider.send('eth_requestAccounts'); 10 | if(!accounts || !accounts.length) throw new Error(`Brave not allowed!`); 11 | 12 | return provider; 13 | } 14 | 15 | export async function getBnbBalance(address) { 16 | const provider = await getBraveProvider(); 17 | const balance = await provider.getBalance(address); 18 | return ethers.formatEther(balance.toString()); 19 | } 20 | 21 | export async function transferBnb(toAddress, quantity) { 22 | const provider = await getBraveProvider(); 23 | const signer = await provider.getSigner(); 24 | 25 | ethers.getAddress(toAddress);//valida endereço 26 | 27 | const tx = await signer.sendTransaction({ 28 | to: toAddress, 29 | value: ethers.parseEther(quantity) 30 | }) 31 | await tx.wait() 32 | 33 | return tx; 34 | } 35 | 36 | export async function getTokenBalance(address, contractAddress, decimals = 18) { 37 | const provider = await getBraveProvider(); 38 | const contract = new ethers.Contract(contractAddress, CONTRACT_ABI, provider); 39 | const balance = await contract.balanceOf(address); 40 | return ethers.formatUnits(balance, decimals); 41 | } 42 | 43 | export async function transferToken(toAddress, contractAddress, quantity, decimals = 18) { 44 | const provider = await getBraveProvider(); 45 | const signer = await provider.getSigner(); 46 | 47 | const contract = new ethers.Contract(contractAddress, CONTRACT_ABI, signer); 48 | 49 | ethers.getAddress(toAddress);//valida endereço 50 | 51 | const tx = await contract.transfer(toAddress, ethers.parseUnits(quantity, decimals)); 52 | await tx.wait(); 53 | 54 | return tx; 55 | } 56 | 57 | export async function getTransaction(hash) { 58 | const provider = await getBraveProvider(); 59 | const tx = await provider.getTransactionReceipt(hash); 60 | return tx; 61 | } -------------------------------------------------------------------------------- /brave-transfer/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | const root = ReactDOM.createRoot(document.getElementById('root')); 6 | root.render( 7 | 8 | 9 | 10 | ); -------------------------------------------------------------------------------- /brave-web3-react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /brave-web3-react/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /brave-web3-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "brave-web3-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.17.0", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.3.1", 10 | "react-dom": "^18.3.1", 11 | "react-scripts": "5.0.1", 12 | "web-vitals": "^2.1.4", 13 | "web3": "^4.11.0" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /brave-web3-react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/brave-web3-react/public/favicon.ico -------------------------------------------------------------------------------- /brave-web3-react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /brave-web3-react/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/brave-web3-react/public/logo192.png -------------------------------------------------------------------------------- /brave-web3-react/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/brave-web3-react/public/logo512.png -------------------------------------------------------------------------------- /brave-web3-react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /brave-web3-react/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /brave-web3-react/src/ABI.json: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}] 2 | -------------------------------------------------------------------------------- /brave-web3-react/src/App.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { getBnbBalance, transferBnb, getTokenBalance, transferToken } from "./BraveService"; 3 | 4 | function App() { 5 | 6 | const [address, setAddress] = useState("0x7C3aEBdD11A2a86270db7a43CA9762f78bED9E0C"); 7 | const [contract, setContract] = useState("BNB"); 8 | const [balance, setBalance] = useState(''); 9 | 10 | const [toAddress, setToAddress] = useState(""); 11 | const [quantity, setQuantity] = useState(""); 12 | const [message, setMessage] = useState(''); 13 | 14 | async function checkBalance() { 15 | let balance; 16 | 17 | if (contract === "BNB") 18 | balance = await getBnbBalance(address); 19 | else 20 | balance = await getTokenBalance(address, contract); 21 | 22 | setBalance(balance); 23 | setMessage(``); 24 | } 25 | 26 | async function transfer() { 27 | let result; 28 | if (contract === "BNB") 29 | result = await transferBnb(toAddress, quantity); 30 | else 31 | result = await transferToken(toAddress, contract, quantity); 32 | 33 | setMessage(result.transactionHash); 34 | } 35 | 36 | return ( 37 |
38 |

39 | My Address : setAddress(evt.target.value)} /> 40 |

41 |

42 | 48 | checkBalance()} /> 49 |

50 |

51 | Balance: {balance} 52 |

53 |
54 |

55 | To Address: setToAddress(evt.target.value)} /> 56 |

57 |

58 | Qty: setQuantity(evt.target.value)} /> 59 |

60 |

61 | transfer()} /> 62 |

63 |
64 |

65 | {message} 66 |

67 |
68 | ); 69 | } 70 | 71 | export default App; 72 | -------------------------------------------------------------------------------- /brave-web3-react/src/BraveService.js: -------------------------------------------------------------------------------- 1 | import Web3 from 'web3'; 2 | import CONTRACT_ABI from "./ABI.json"; 3 | 4 | async function connect() { 5 | if (!window.ethereum) throw new Error('No Brave Wallet'); 6 | 7 | const web3 = new Web3(window.ethereum); 8 | const accounts = await web3.eth.requestAccounts(); 9 | if (!accounts || !accounts.length) throw new Error('Wallet not found/allowed!'); 10 | 11 | return web3; 12 | } 13 | 14 | export async function getBnbBalance(address) { 15 | const web3 = await connect(); 16 | const balance = await web3.eth.getBalance(address); 17 | return web3.utils.fromWei(balance, "ether"); 18 | } 19 | 20 | export async function transferBnb(toAddress, quantity) { 21 | const web3 = await connect(); 22 | const myAddress = window.ethereum.selectedAddress; 23 | const value = web3.utils.toWei(quantity, "ether"); 24 | const nonce = await web3.eth.getTransactionCount(myAddress, 'latest'); 25 | const transaction = { from: myAddress, to: toAddress, value, gas: 21000, nonce }; 26 | return web3.eth.sendTransaction(transaction); 27 | } 28 | 29 | export async function getTokenBalance(address, contractAddress) { 30 | const web3 = await connect(); 31 | const contract = new web3.eth.Contract(CONTRACT_ABI, contractAddress); 32 | const balance = await contract.methods.balanceOf(address).call(); 33 | return web3.utils.fromWei(balance, "ether"); 34 | } 35 | 36 | export async function transferToken(toAddress, contractAddress, quantity) { 37 | const web3 = await connect(); 38 | const from = window.ethereum.selectedAddress; 39 | const value = web3.utils.toWei(quantity, "ether"); 40 | const contract = new web3.eth.Contract(CONTRACT_ABI, contractAddress, { from }); 41 | return contract.methods.transfer(toAddress, value).send(); 42 | } -------------------------------------------------------------------------------- /brave-web3-react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | const root = ReactDOM.createRoot(document.getElementById('root')); 6 | root.render( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /cryptobubbles/README.md: -------------------------------------------------------------------------------- 1 | # cryptobubbles 2 | A Cryptobubbles-clone website to monitor crypto market. 3 | 4 | ## Instruções 5 | 6 | Leia o tutorial do CryptoBubbles (inclui vídeo): https://www.luiztools.com.br/post/criando-um-cryptobubbles-clone-com-javascript/ 7 | 8 | Conheça meus livros: https://www.luiztools.com.br/meus-livros 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 | -------------------------------------------------------------------------------- /cryptobubbles/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

Market USDT 24h

5 |
6 | 7 | 8 | 139 | 140 | -------------------------------------------------------------------------------- /dapp-event-ethers/.env.example: -------------------------------------------------------------------------------- 1 | # Full Node WebSocket server URL. Ex: use Infura for Ethereum/Goerli or Quicknode for BSC 2 | REACT_APP_WEBSOCKET_URL= 3 | 4 | # Your contract address deployed to blockchain 5 | REACT_APP_CONTRACT_ADDRESS= -------------------------------------------------------------------------------- /dapp-event-ethers/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /dapp-event-ethers/README.md: -------------------------------------------------------------------------------- 1 | # dapp-event-ethers 2 | 3 | A simple web frontend written with JS/React to listen events in the blockchain using EthersJS. 4 | 5 | Smart Contract address (ERC-20 example token): https://testnet.bscscan.com/address/0x94a9838528E1b0022c334D3c1c7D5e684c222B07 6 | 7 | ## How to Run 8 | 1. git clone 9 | 2. npm install 10 | 3. npm start 11 | 12 | ## Referências 13 | 14 | Tutorial em: https://www.luiztools.com.br/post/como-monitorar-eventos-da-blockchain-com-reactjs-ethersjs/ 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 -------------------------------------------------------------------------------- /dapp-event-ethers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dapp-event-ethers", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "ethers": "^6.0.2", 10 | "react": "^18.2.0", 11 | "react-dom": "^18.2.0", 12 | "react-scripts": "5.0.1", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dapp-event-ethers/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-event-ethers/public/favicon.ico -------------------------------------------------------------------------------- /dapp-event-ethers/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /dapp-event-ethers/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-event-ethers/public/logo192.png -------------------------------------------------------------------------------- /dapp-event-ethers/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-event-ethers/public/logo512.png -------------------------------------------------------------------------------- /dapp-event-ethers/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /dapp-event-ethers/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /dapp-event-ethers/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dapp-event-ethers/src/App.js: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | import './App.css'; 3 | 4 | function App() { 5 | 6 | async function doListen() { 7 | const provider = new ethers.WebSocketProvider(process.env.REACT_APP_WEBSOCKET_URL); 8 | 9 | const filter = { 10 | address: process.env.REACT_APP_CONTRACT_ADDRESS, 11 | topics: [ 12 | ethers.id("Transfer(address,address,uint256)") 13 | ] 14 | } 15 | provider.on(filter, () => { 16 | console.log('fire transfer') 17 | }); 18 | } 19 | 20 | function btnClick() { 21 | doListen() 22 | .then(() => console.log("fire")) 23 | .catch(err => console.error(err)) 24 | } 25 | 26 | return ( 27 |
28 |
29 | 30 |
31 |
32 | ); 33 | } 34 | 35 | export default App; 36 | -------------------------------------------------------------------------------- /dapp-event-ethers/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /dapp-event-ethers/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render( 8 | 9 | 10 | 11 | ); -------------------------------------------------------------------------------- /dapp-event-web3/.env.example: -------------------------------------------------------------------------------- 1 | # Full Node WebSocket server URL. Ex: use Infura for Ethereum/Goerli or Quicknode for BSC 2 | REACT_APP_WEBSOCKET_URL= 3 | 4 | # Your contract address deployed to blockchain 5 | REACT_APP_CONTRACT_ADDRESS= -------------------------------------------------------------------------------- /dapp-event-web3/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /dapp-event-web3/README.md: -------------------------------------------------------------------------------- 1 | # dapp-event-web3 2 | 3 | A simple web frontend written with JS/React to listen events in the blockchain using web3.js. 4 | 5 | Smart Contract address (ERC-20 example token): https://testnet.bscscan.com/address/0x94a9838528E1b0022c334D3c1c7D5e684c222B07 6 | 7 | ## How to Run 8 | 1. git clone 9 | 2. npm install 10 | 3. npm start 11 | 12 | ## Referências 13 | 14 | Tutorial em: https://www.luiztools.com.br/post/como-monitorar-eventos-da-blockchain-com-reactjs-web3-js/ 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 -------------------------------------------------------------------------------- /dapp-event-web3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dapp-evento-web3", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "react-scripts": "5.0.1", 12 | "web-vitals": "^2.1.4", 13 | "web3": "^4.2.2" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dapp-event-web3/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-event-web3/public/favicon.ico -------------------------------------------------------------------------------- /dapp-event-web3/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /dapp-event-web3/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-event-web3/public/logo192.png -------------------------------------------------------------------------------- /dapp-event-web3/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-event-web3/public/logo512.png -------------------------------------------------------------------------------- /dapp-event-web3/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /dapp-event-web3/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /dapp-event-web3/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dapp-event-web3/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import Web3 from 'web3'; 3 | import ABI from './abi.json'; 4 | 5 | function App() { 6 | 7 | async function doListen() { 8 | const web3 = new Web3(process.env.REACT_APP_WEBSOCKET_URL); 9 | const contract = new web3.eth.Contract(ABI, process.env.REACT_APP_CONTRACT_ADDRESS); 10 | 11 | contract.events.Transfer({ 12 | fromBlock: "latest" 13 | }) 14 | .on('data', event => console.log("event: " + JSON.stringify(event))) 15 | .on('changed', changed => console.log("changed: " + changed)) 16 | .on('error', err => console.error(err)) 17 | .on('connected', str => console.log("connected: " + str)); 18 | } 19 | 20 | function btnClick() { 21 | doListen() 22 | .then(() => console.log("fire")) 23 | .catch(err => console.error(err)) 24 | } 25 | 26 | return ( 27 |
28 |
29 | 30 |
31 |
32 | ); 33 | } 34 | 35 | export default App; 36 | -------------------------------------------------------------------------------- /dapp-event-web3/src/abi.json: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"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":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","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":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","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":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /dapp-event-web3/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | const root = ReactDOM.createRoot(document.getElementById('root')); 6 | root.render( 7 | 8 | 9 | 10 | ); 11 | -------------------------------------------------------------------------------- /dapp-react-ethers/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /dapp-react-ethers/README.md: -------------------------------------------------------------------------------- 1 | # dapp-react-ethers 2 | 3 | A simple web frontend written with JS/React to integrate with a smart contract in the blockchain (BSC Testnet) and EthersJS. 4 | 5 | Smart Contract address: https://testnet.bscscan.com/address/0xE9956c971B72aD74F249E616828df613F03E858b 6 | 7 | ## How to Run 8 | 1. git clone 9 | 2. npm install 10 | 3. npm start 11 | 12 | ## Referências 13 | 14 | Tutorial em (inclui vídeo): https://www.luiztools.com.br/post/como-criar-um-frontend-web-para-smart-contract-com-reactjs/ 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 -------------------------------------------------------------------------------- /dapp-react-ethers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dapp-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "ethers": "^6.0.2", 10 | "react": "^18.2.0", 11 | "react-dom": "^18.2.0", 12 | "react-scripts": "5.0.1", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dapp-react-ethers/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-react-ethers/public/favicon.ico -------------------------------------------------------------------------------- /dapp-react-ethers/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /dapp-react-ethers/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-react-ethers/public/logo192.png -------------------------------------------------------------------------------- /dapp-react-ethers/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-react-ethers/public/logo512.png -------------------------------------------------------------------------------- /dapp-react-ethers/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /dapp-react-ethers/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /dapp-react-ethers/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /dapp-react-ethers/src/App.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { ethers } from 'ethers'; 3 | import ABI from './abi.json'; 4 | import './App.css'; 5 | 6 | function App() { 7 | 8 | const [customerId, setCustomerId] = useState("0"); 9 | const [name, setName] = useState(""); 10 | const [age, setAge] = useState(""); 11 | const [error, setError] = useState(""); 12 | 13 | const CONTRACT_ADDRESS = "0xE9956c971B72aD74F249E616828df613F03E858b"; 14 | 15 | async function getProvider() { 16 | if (!window.ethereum) throw new Error(`No MetaMask found!`); 17 | 18 | const provider = new ethers.BrowserProvider(window.ethereum); 19 | 20 | const accounts = await provider.send("eth_requestAccounts", []); 21 | if (!accounts || !accounts.length) throw new Error('Wallet not found/allowed!'); 22 | return provider; 23 | } 24 | 25 | async function doSearch() { 26 | try { 27 | const provider = await getProvider(); 28 | const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider); 29 | const customer = await contract.getCustomer(customerId); 30 | alert(JSON.stringify(customer)); 31 | } catch (err) { 32 | setError(err.message); 33 | } 34 | } 35 | 36 | async function getContractSigner() { 37 | const provider = await getProvider(); 38 | const signer = provider.getSigner(); 39 | 40 | const contract = new ethers.Contract(CONTRACT_ADDRESS, ABI, provider); 41 | return contract.connect(signer); 42 | } 43 | 44 | async function doSave() { 45 | try { 46 | const contract = await getContractSigner(); 47 | const tx = await contract.addCustomer({ name, age }); 48 | alert(JSON.stringify(tx)); 49 | } catch (err) { 50 | setError(err.message); 51 | } 52 | } 53 | 54 | function onSearchClick() { 55 | setError(''); 56 | doSearch(); 57 | } 58 | 59 | function onSaveClick() { 60 | setError(''); 61 | doSave(); 62 | } 63 | 64 | return ( 65 |
66 |
67 |

68 | 69 |

70 |

71 | 72 |

73 |
74 |

75 | 76 |

77 |

78 | 79 |

80 |

81 | 82 |

83 |

84 | {error} 85 |

86 |
87 |
88 | ); 89 | } 90 | 91 | export default App; 92 | -------------------------------------------------------------------------------- /dapp-react-ethers/src/abi.json: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint8","name":"age","type":"uint8"}],"internalType":"struct StoreCustomers.Customer","name":"customer","type":"tuple"}],"name":"addCustomer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"count","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"customers","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint8","name":"age","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint8","name":"age","type":"uint8"}],"internalType":"struct StoreCustomers.Customer","name":"newCustomer","type":"tuple"}],"name":"editCustomer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"getCustomer","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint8","name":"age","type":"uint8"}],"internalType":"struct StoreCustomers.Customer","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"removeCustomer","outputs":[],"stateMutability":"nonpayable","type":"function"}] 2 | -------------------------------------------------------------------------------- /dapp-react-ethers/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /dapp-react-ethers/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render( 8 | 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /dapp-react-web3/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /dapp-react-web3/README.md: -------------------------------------------------------------------------------- 1 | # dapp-react-web3 2 | 3 | A simple web frontend written with JS/React to integrate with a smart contract in the blockchain (BSC Testnet) using web3.js. 4 | 5 | Smart Contract address: https://testnet.bscscan.com/address/0xE9956c971B72aD74F249E616828df613F03E858b 6 | 7 | ## How to Run 8 | 1. git clone 9 | 2. npm install 10 | 3. npm start 11 | 12 | ## Referências 13 | 14 | Tutorial em: https://www.luiztools.com.br/post/como-criar-um-frontend-para-smart-contract-com-reactjs-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 -------------------------------------------------------------------------------- /dapp-react-web3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dapp-react-web3", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "react-scripts": "5.0.1", 12 | "web-vitals": "^2.1.4", 13 | "web3": "^4.2.2" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /dapp-react-web3/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-react-web3/public/favicon.ico -------------------------------------------------------------------------------- /dapp-react-web3/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /dapp-react-web3/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-react-web3/public/logo192.png -------------------------------------------------------------------------------- /dapp-react-web3/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/dapp-react-web3/public/logo512.png -------------------------------------------------------------------------------- /dapp-react-web3/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /dapp-react-web3/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /dapp-react-web3/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | 40 | .error { 41 | color: red; 42 | } 43 | -------------------------------------------------------------------------------- /dapp-react-web3/src/App.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import Web3 from 'web3'; 3 | import ABI from './abi.json'; 4 | import './App.css'; 5 | 6 | function App() { 7 | 8 | const [customerId, setCustomerId] = useState("0"); 9 | const [name, setName] = useState(""); 10 | const [age, setAge] = useState(""); 11 | const [error, setError] = useState(""); 12 | 13 | const CONTRACT_ADDRESS = "0xE9956c971B72aD74F249E616828df613F03E858b"; 14 | 15 | async function getContract() { 16 | if (!window.ethereum) return setError(`No MetaMask found!`); 17 | 18 | const web3 = new Web3(window.ethereum); 19 | const accounts = await web3.eth.requestAccounts(); 20 | if (!accounts || !accounts.length) return setError('Wallet not found/allowed!'); 21 | 22 | return new web3.eth.Contract(ABI, CONTRACT_ADDRESS, { from: accounts[0] }); 23 | } 24 | 25 | async function doSave() { 26 | try { 27 | const contract = await getContract(); 28 | const tx = await contract.methods.addCustomer({ name, age }).send(); 29 | alert(JSON.stringify(tx)); 30 | } catch (err) { 31 | setError(err.message); 32 | } 33 | } 34 | 35 | function onSaveClick() { 36 | doSave(); 37 | } 38 | 39 | async function doSearch() { 40 | try { 41 | const contract = await getContract(); 42 | const customer = await contract.methods.getCustomer(customerId).call(); 43 | alert(JSON.stringify(customer)); 44 | } catch (err) { 45 | setError(err.message); 46 | } 47 | } 48 | 49 | function onSearchClick() { 50 | setError(''); 51 | doSearch(); 52 | } 53 | 54 | return ( 55 |
56 |
57 |

58 | 59 |

60 |

61 | 62 |

63 |
64 |

65 | 66 |

67 |

68 | 69 |

70 |

71 | 72 |

73 |

74 | {error} 75 |

76 |
77 |
78 | ); 79 | } 80 | 81 | export default App; 82 | -------------------------------------------------------------------------------- /dapp-react-web3/src/abi.json: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint8","name":"age","type":"uint8"}],"internalType":"struct StoreCustomers.Customer","name":"customer","type":"tuple"}],"name":"addCustomer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"count","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"","type":"uint32"}],"name":"customers","outputs":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint8","name":"age","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"},{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint8","name":"age","type":"uint8"}],"internalType":"struct StoreCustomers.Customer","name":"newCustomer","type":"tuple"}],"name":"editCustomer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"getCustomer","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint8","name":"age","type":"uint8"}],"internalType":"struct StoreCustomers.Customer","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint32","name":"id","type":"uint32"}],"name":"removeCustomer","outputs":[],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /dapp-react-web3/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /dapp-react-web3/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render( 8 | 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /faucet/.env.example: -------------------------------------------------------------------------------- 1 | #The express server port. ex: 3000 2 | PORT= 3 | 4 | #Your MetaMask private key (owner/admin) 5 | PRIVATE_KEY= 6 | 7 | #Your MetaMask wallet public address (owner/admin). Ex: 0xE4ffEEd88111e1DFCc3a852d9334C65e38BF2880 8 | WALLET= 9 | 10 | #Your RPC Node URL 11 | RPC_NODE= 12 | 13 | # The amount of tokens to send each transfer (in ether-scale) 14 | TOKEN_AMOUNT= 15 | 16 | # Interval between withdraws 17 | INTERVAL= -------------------------------------------------------------------------------- /faucet/README.md: -------------------------------------------------------------------------------- 1 | # Backend-Based Faucet 2 | 3 | Simple example of faucet. 4 | 5 | ## How to Run 6 | 7 | 1. git clone and cd 8 | 2. npm install 9 | 3. copy .env.example as .env 10 | 4. fill .env variables 11 | 5. be sure to have funds in your wallet 12 | 6. npm start 13 | 14 | ## Referências 15 | 16 | Tutorial em https://www.luiztools.com.br/post/como-criar-seu-proprio-faucet-de-criptomoedas-com-node-js 17 | 18 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 19 | 20 | Me siga nas redes sociais: https://about.me/luiztools 21 | 22 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /faucet/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "faucet", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "dev": "npx nodemon index", 8 | "start": "node index" 9 | }, 10 | "keywords": [], 11 | "author": "LuizTools", 12 | "license": "MIT", 13 | "dependencies": { 14 | "dotenv": "^16.0.3", 15 | "ethers": "^6.13.2", 16 | "express": "^4.18.2", 17 | "morgan": "^1.10.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /faucet/src/SecurityProvider.js: -------------------------------------------------------------------------------- 1 | const nextTry = {};//wallet para timestamp 2 | const ips = {};//ip para wallet 3 | 4 | function ipCheck(clientIP, wallet) { 5 | const walletByIp = ips[clientIP]; 6 | if (walletByIp && walletByIp !== wallet) 7 | throw new Error(`IP do usuário e carteira não conferem.`); 8 | 9 | ips[clientIP] = wallet; 10 | return true; 11 | } 12 | 13 | function tryCheck(wallet) { 14 | const nextTimestamp = nextTry[wallet]; 15 | 16 | if (nextTimestamp > Date.now()) 17 | throw new Error(`Tente novamente após ${new Date(nextTimestamp)}`); 18 | 19 | nextTry[wallet] = nextTimestamp + parseInt(process.env.INTERVAL);//1d 20 | return true; 21 | } 22 | 23 | module.exports = { 24 | ipCheck, 25 | tryCheck 26 | } -------------------------------------------------------------------------------- /faucet/src/Web3Provider.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("ethers"); 2 | 3 | const provider = new ethers.JsonRpcProvider(process.env.RPC_NODE); 4 | const signer = new ethers.Wallet(`${process.env.PRIVATE_KEY}`, provider); 5 | 6 | async function withdraw(clientWallet) { 7 | 8 | const value = ethers.parseEther(`${process.env.TOKEN_AMOUNT}`); 9 | const balance = await provider.getBalance(clientWallet); 10 | if (balance >= (value / 2n)) 11 | throw new Error(`You already have enough tokens.`); 12 | 13 | const tx = await signer.sendTransaction({ 14 | from: process.env.WALLET, 15 | to: clientWallet, 16 | value 17 | }); 18 | 19 | return tx.hash; 20 | } 21 | 22 | module.exports = { withdraw } -------------------------------------------------------------------------------- /faucet/src/index.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | const express = require('express'); 4 | const morgan = require('morgan'); 5 | 6 | const { withdraw } = require('./Web3Provider'); 7 | const { ipCheck, tryCheck } = require("./SecurityProvider"); 8 | 9 | const app = express(); 10 | 11 | app.use(morgan("tiny")); 12 | 13 | app.get("/", (req, res) => { 14 | res.json({ mesage: "Hello World" }); 15 | }) 16 | 17 | app.post("/withdraw/:wallet", async (req, res) => { 18 | 19 | try { 20 | ipCheck(`${req.headers['x-forwarded-for'] || req.socket.remoteAddress}`, req.params.wallet); 21 | tryCheck(req.params.wallet); 22 | 23 | const tx = await withdraw(req.params.wallet); 24 | res.json({ 25 | tx, 26 | amount: process.env.TOKEN_AMOUNT 27 | }); 28 | } 29 | catch (err) { 30 | console.error(err); 31 | res.status(400).json({ message: err.message }); 32 | } 33 | }) 34 | 35 | const PORT = Number(process.env.PORT) || 3000; 36 | app.listen(PORT, () => console.log("Server is listening at " + PORT)); -------------------------------------------------------------------------------- /metamask-ethers-node/.env.example: -------------------------------------------------------------------------------- 1 | #Your wallet private key 2 | PRIVATE_KEY= 3 | 4 | #Your Infura API Key 5 | INFURA_API_KEY= 6 | 7 | #Your Network name (lowercase). Ex: goerli 8 | NETWORK= 9 | 10 | #Your smart contract address in blockchain. Ex: 0x6e9fea1f661c599009844a97fa63c96bfc6ad83c (tutorial part 2) 11 | CONTRACT_ADDRESS= -------------------------------------------------------------------------------- /metamask-ethers-node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | abi.json -------------------------------------------------------------------------------- /metamask-ethers-node/README.md: -------------------------------------------------------------------------------- 1 | # MetaMask Ethers Node 2 | 3 | Simple examples of Node.js app connecting to blockchain using MetaMask and Infura. 4 | 5 | ## How to Run 6 | 1. git clone the project 7 | 2. cd metamask-ethers-node 8 | 3. npm install 9 | 4. Create a .env file as .env.example with your configs (follow instructions in the examples) 10 | 5. Edit the abi.json as your contract require 11 | 6. Write the code to call the function you want in the end of index.js 12 | 7. npm start 13 | 14 | ## Referências 15 | 16 | Tutoriais em 17 | - blockchain (parte 1): https://www.luiztools.com.br/post/integracao-com-a-blockchain-com-node-js-e-ethersjs/ 18 | - smart contract (parte 2): https://www.luiztools.com.br/post/integracao-com-smart-contracts-com-node-js-e-ethersjs 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 -------------------------------------------------------------------------------- /metamask-ethers-node/abi.example.json: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"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":"remaining","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","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":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /metamask-ethers-node/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const ABI = require("./abi.json"); 3 | 4 | const { ethers } = require("ethers"); 5 | const provider = new ethers.InfuraProvider( 6 | process.env.NETWORK, 7 | process.env.INFURA_API_KEY 8 | ); 9 | 10 | async function getEthBalance(from) { 11 | const balance = await provider.getBalance(from); 12 | console.log(ethers.formatEther(balance)); 13 | } 14 | //getEthBalance(process.env.WALLET); 15 | 16 | async function transferEth(to, value) { 17 | const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider); 18 | const tx = await signer.sendTransaction({ to, value }); 19 | const receipt = await tx.wait(); 20 | console.log(tx.hash); 21 | return tx.hash; 22 | } 23 | //transferEth("0x0D1195969395B8a23dA37Dce78b823BE8cD5a0a4", 1000); 24 | 25 | async function getPrcBalance(from) { 26 | const contract = new ethers.Contract(process.env.CONTRACT_ADDRESS, ABI, provider); 27 | const balance = await contract.balanceOf(from); 28 | console.log(ethers.formatEther(balance)); 29 | } 30 | //getPrcBalance("0xE4ffEEd88111e1DFCc3a852d9334C65e38BF2880"); 31 | 32 | async function approvePrcTransfer(spender, value) { 33 | const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider); 34 | const contract = new ethers.Contract(process.env.CONTRACT_ADDRESS, ABI, signer); 35 | 36 | const tx = await contract.approve(spender, value, {from: process.env.WALLET}); 37 | const receipt = await tx.wait(); 38 | console.log(tx.hash); 39 | return tx.hash; 40 | } 41 | //approvePrcTransfer("0x0D1195969395B8a23dA37Dce78b823BE8cD5a0a4", "1000"); -------------------------------------------------------------------------------- /metamask-ethers-node/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metamask-ethers-node", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "metamask-ethers-node", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^16.0.3", 13 | "ethers": "^6.3.0" 14 | } 15 | }, 16 | "node_modules/@adraffy/ens-normalize": { 17 | "version": "1.9.0", 18 | "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.9.0.tgz", 19 | "integrity": "sha512-iowxq3U30sghZotgl4s/oJRci6WPBfNO5YYgk2cIOMCHr3LeGPcsZjCEr+33Q4N+oV3OABDAtA+pyvWjbvBifQ==" 20 | }, 21 | "node_modules/@noble/hashes": { 22 | "version": "1.1.2", 23 | "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", 24 | "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==", 25 | "funding": [ 26 | { 27 | "type": "individual", 28 | "url": "https://paulmillr.com/funding/" 29 | } 30 | ] 31 | }, 32 | "node_modules/@noble/secp256k1": { 33 | "version": "1.7.1", 34 | "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", 35 | "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", 36 | "funding": [ 37 | { 38 | "type": "individual", 39 | "url": "https://paulmillr.com/funding/" 40 | } 41 | ] 42 | }, 43 | "node_modules/aes-js": { 44 | "version": "4.0.0-beta.3", 45 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.3.tgz", 46 | "integrity": "sha512-/xJX0/VTPcbc5xQE2VUP91y1xN8q/rDfhEzLm+vLc3hYvb5+qHCnpJRuFcrKn63zumK/sCwYYzhG8HP78JYSTA==" 47 | }, 48 | "node_modules/dotenv": { 49 | "version": "16.0.3", 50 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 51 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 52 | "engines": { 53 | "node": ">=12" 54 | } 55 | }, 56 | "node_modules/ethers": { 57 | "version": "6.3.0", 58 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.3.0.tgz", 59 | "integrity": "sha512-CKFYvTne1YT4S1glTiu7TgGsj0t6c6GAD7evrIk8zbeUb6nK8dcUPAiAWM8uDX/1NmRTvLM9+1Vnn49hwKtEzw==", 60 | "funding": [ 61 | { 62 | "type": "individual", 63 | "url": "https://github.com/sponsors/ethers-io/" 64 | }, 65 | { 66 | "type": "individual", 67 | "url": "https://www.buymeacoffee.com/ricmoo" 68 | } 69 | ], 70 | "dependencies": { 71 | "@adraffy/ens-normalize": "1.9.0", 72 | "@noble/hashes": "1.1.2", 73 | "@noble/secp256k1": "1.7.1", 74 | "aes-js": "4.0.0-beta.3", 75 | "tslib": "2.4.0", 76 | "ws": "8.5.0" 77 | }, 78 | "engines": { 79 | "node": ">=14.0.0" 80 | } 81 | }, 82 | "node_modules/tslib": { 83 | "version": "2.4.0", 84 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 85 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" 86 | }, 87 | "node_modules/ws": { 88 | "version": "8.5.0", 89 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", 90 | "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", 91 | "engines": { 92 | "node": ">=10.0.0" 93 | }, 94 | "peerDependencies": { 95 | "bufferutil": "^4.0.1", 96 | "utf-8-validate": "^5.0.2" 97 | }, 98 | "peerDependenciesMeta": { 99 | "bufferutil": { 100 | "optional": true 101 | }, 102 | "utf-8-validate": { 103 | "optional": true 104 | } 105 | } 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /metamask-ethers-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metamask-ethers-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^16.0.3", 14 | "ethers": "^6.3.0" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /metamask-login/.env.example: -------------------------------------------------------------------------------- 1 | REACT_APP_CHALLENGE= -------------------------------------------------------------------------------- /metamask-login/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /metamask-login/README.md: -------------------------------------------------------------------------------- 1 | # metamask-login 2 | Simple MetaMask login example. 3 | 4 | ## Instruções 5 | 6 | 1. npm install 7 | 2. npm start 8 | 9 | ## Mais informações 10 | 11 | Leia o tutorial da MetaMask (inclui vídeo): https://www.luiztools.com.br/post/autenticacao-login-em-dapp-web3-com-metamask/ 12 | 13 | Conheça meu curso de web3/blockchain: https://www.luiztools.com.br/curso-web23 14 | 15 | Conheça meus livros: https://www.luiztools.com.br/meus-livros 16 | 17 | Me siga nas redes sociais: https://about.me/luiztools 18 | 19 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /metamask-login/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metamask-login", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.4", 7 | "@testing-library/react": "^13.2.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "ethers": "^6.7.1", 10 | "react": "^18.1.0", 11 | "react-dom": "^18.1.0", 12 | "react-scripts": "5.0.1", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /metamask-login/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/metamask-login/public/favicon.ico -------------------------------------------------------------------------------- /metamask-login/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /metamask-login/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/metamask-login/public/logo192.png -------------------------------------------------------------------------------- /metamask-login/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/metamask-login/public/logo512.png -------------------------------------------------------------------------------- /metamask-login/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /metamask-login/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /metamask-login/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /metamask-login/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import { useState, useEffect } from 'react'; 3 | import { ethers } from 'ethers'; 4 | import { doSignInBackend, doSignOutBackend, doSignUpBackend, getProfileBackend } from './AppService'; 5 | 6 | function App() { 7 | 8 | const CHALLENGE = process.env.REACT_APP_CHALLENGE; 9 | 10 | const [wallet, setWallet] = useState(''); 11 | const [profile, setProfile] = useState({}); 12 | const [balance, setBalance] = useState(''); 13 | const [error, setError] = useState(''); 14 | 15 | useEffect(() => { 16 | const address = localStorage.getItem('wallet'); 17 | setWallet(address); 18 | 19 | if (address) doSignIn(); 20 | }, []) 21 | 22 | async function connect() { 23 | setError(''); 24 | 25 | if (!window.ethereum) return setError(`No MetaMask found!`); 26 | 27 | const provider = new ethers.BrowserProvider(window.ethereum); 28 | const accounts = await provider.send("eth_requestAccounts", []); 29 | if (!accounts || !accounts.length) return setError('Wallet not found/allowed!'); 30 | 31 | localStorage.setItem('wallet', accounts[0]); 32 | 33 | setWallet(accounts[0]); 34 | 35 | const signer = await provider.getSigner(); 36 | const secret = await signer.signMessage(CHALLENGE); 37 | 38 | return { user: accounts[0], secret }; 39 | } 40 | 41 | async function loadProfile(token) { 42 | const profile = await getProfileBackend(token) 43 | setProfile(profile); 44 | } 45 | 46 | function doSignUp() { 47 | connect() 48 | .then(credentials => doSignUpBackend(credentials)) 49 | .then(result => localStorage.setItem('token', result.token)) 50 | .catch(err => setError(err.message)) 51 | } 52 | 53 | function doSignIn() { 54 | connect() 55 | .then(credentials => doSignInBackend(credentials)) 56 | .then(result => { 57 | localStorage.setItem('token', result.token); 58 | loadProfile(result.token); 59 | }) 60 | .catch(err => setError(err.message)) 61 | } 62 | 63 | function getBalance() { 64 | const provider = new ethers.BrowserProvider(window.ethereum); 65 | provider.getBalance(wallet) 66 | .then(balance => setBalance(ethers.formatEther(balance.toString()))) 67 | .catch(err => setError(err.message)) 68 | } 69 | 70 | function doLogout() { 71 | setError(''); 72 | 73 | const token = localStorage.getItem('token'); 74 | doSignOutBackend(token) 75 | .then(response => { 76 | localStorage.removeItem('wallet'); 77 | localStorage.removeItem('token'); 78 | setWallet(''); 79 | setBalance(''); 80 | }) 81 | .catch(err => setError(err.message)); 82 | } 83 | 84 | return ( 85 |
86 |
87 |

Login

88 |
89 | { 90 | !wallet 91 | ? ( 92 | <> 93 | 96 | 99 | 100 | ) 101 | : ( 102 | <> 103 |

104 | Wallet: {wallet} 105 |

106 |

107 | Name: {profile.name} 108 |

109 |

110 | {balance} 113 |

114 | 117 | 118 | ) 119 | } 120 | { 121 | error ?

{error}

: <> 122 | } 123 |
124 |
125 |
126 | ); 127 | } 128 | 129 | export default App; 130 | -------------------------------------------------------------------------------- /metamask-login/src/AppService.js: -------------------------------------------------------------------------------- 1 | export function doSignUpBackend(credentials){ 2 | return new Promise((resolve, reject) => { 3 | return resolve({ token: 'abc123' }); 4 | }) 5 | } 6 | 7 | export function doSignInBackend(credentials) { 8 | return new Promise((resolve, reject) => { 9 | if (credentials.user === '0x9cd29e15d5647e702696c90d64dfb31425738c06' 10 | && credentials.secret === '0x3e60174888e4e6a4d44bed2b7f8e8b4e9f6adecf1083e10d70770573a25bd6406108ccb27818826230d19a75fdc78f34ae89c92d7a2ba97eaee5cf106e50cb9c1b' ) 11 | return resolve({ token: 'abc123' }); 12 | return reject(`401 Unauthorized`); 13 | }) 14 | } 15 | 16 | export function doSignOutBackend(token) { 17 | return new Promise((resolve, reject) => { 18 | if (token === 'abc123') 19 | return resolve({ token: null }); 20 | return reject(`401 Unauthorized`); 21 | }) 22 | } 23 | 24 | export function getProfileBackend(token) { 25 | return new Promise((resolve, reject) => { 26 | if (token === 'abc123') 27 | return resolve({ name: 'Luiz' }); 28 | return reject(`401 Unauthorized`); 29 | }) 30 | } -------------------------------------------------------------------------------- /metamask-login/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /metamask-login/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render(); 8 | -------------------------------------------------------------------------------- /metamask-transfer/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /metamask-transfer/README.md: -------------------------------------------------------------------------------- 1 | # metamask-transfer 2 | Simple MetaMask transfer example. 3 | 4 | ## Instruções 5 | 6 | 1. npm install 7 | 2. npm start 8 | 9 | ## Mais informações 10 | 11 | Leia o tutorial da MetaMask (inclui vídeo): https://www.luiztools.com.br/post/integracao-com-metamask-via-js/ 12 | 13 | Conheça meu curso de web3/blockchain: https://www.luiztools.com.br/curso-web23 14 | 15 | Conheça meus livros: https://www.luiztools.com.br/meus-livros 16 | 17 | Me siga nas redes sociais: https://about.me/luiztools 18 | 19 | Receba novidades no Telegram: https://t.me/luiznews 20 | -------------------------------------------------------------------------------- /metamask-transfer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metamask-transfer", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.17.0", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "ethers": "^6.13.0", 10 | "react": "^18.3.1", 11 | "react-dom": "^18.3.1", 12 | "react-scripts": "5.0.1", 13 | "web-vitals": "^2.1.4" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /metamask-transfer/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/metamask-transfer/public/favicon.ico -------------------------------------------------------------------------------- /metamask-transfer/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /metamask-transfer/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/metamask-transfer/public/logo192.png -------------------------------------------------------------------------------- /metamask-transfer/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/metamask-transfer/public/logo512.png -------------------------------------------------------------------------------- /metamask-transfer/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /metamask-transfer/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /metamask-transfer/src/ABI.json: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}] 2 | -------------------------------------------------------------------------------- /metamask-transfer/src/App.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { getTokenBalance, getBnbBalance, transferBnb, transferToken, getTransaction } from './MetaMaskService'; 3 | 4 | function App() { 5 | 6 | const [address, setAddress] = useState("0x9cd29e15d5647E702696c90D64dfB31425738c06"); 7 | const [contract, setContract] = useState("BNB"); 8 | const [balance, setBalance] = useState(''); 9 | 10 | const [toAddress, setToAddress] = useState(""); 11 | const [quantity, setQuantity] = useState(""); 12 | const [message, setMessage] = useState(''); 13 | const [transaction, setTransaction] = useState("0xb2c36a4b57c2c174ff3ce2c17720ace59c960ac82c6a8c11a7c6393ba4ffcb27"); 14 | 15 | async function checkBalance() { 16 | let balance; 17 | 18 | if (contract === "BNB") 19 | balance = await getBnbBalance(address); 20 | else 21 | balance = await getTokenBalance(address, contract); 22 | 23 | setBalance(balance); 24 | setMessage(``); 25 | } 26 | 27 | async function checkTransaction() { 28 | const result = await getTransaction(transaction); 29 | setMessage(` 30 | Status: ${result.status} 31 | Confirmations: ${await result.confirmations()}`); 32 | } 33 | 34 | async function transfer() { 35 | let result; 36 | if (contract === "BNB") 37 | result = await transferBnb(toAddress, quantity); 38 | else 39 | result = await transferToken(toAddress, contract, quantity); 40 | 41 | setMessage(JSON.stringify(result)); 42 | } 43 | 44 | return ( 45 |
46 |

47 | My Address : setAddress(evt.target.value)} value={address} /> 48 |

49 |

50 | 56 | checkBalance()} /> 57 |

58 |

59 | Balance: {balance} 60 |

61 |
62 |

63 | To Address: setToAddress(evt.target.value)} /> 64 |

65 |

66 | Qty: setQuantity(evt.target.value)} /> 67 |

68 |

69 | transfer()} /> 70 |

71 |
72 |

73 | Transaction: setTransaction(evt.target.value)} /> 74 | checkTransaction()} /> 75 |

76 |

77 | {message} 78 |

79 |
80 | ); 81 | } 82 | 83 | export default App; 84 | -------------------------------------------------------------------------------- /metamask-transfer/src/MetaMaskService.js: -------------------------------------------------------------------------------- 1 | import { ethers } from 'ethers'; 2 | 3 | import CONTRACT_ABI from "./ABI.json"; 4 | 5 | async function getMetaMaskProvider() { 6 | if (!window.ethereum) throw new Error(`No MetaMask found!`); 7 | const provider = new ethers.BrowserProvider(window.ethereum); 8 | 9 | const accounts = await provider.send('eth_requestAccounts'); 10 | if(!accounts || !accounts.length) throw new Error(`No MetaMask account allowed`); 11 | 12 | return provider; 13 | } 14 | 15 | export async function getBnbBalance(address) { 16 | const provider = await getMetaMaskProvider(); 17 | const balance = await provider.getBalance(address); 18 | return ethers.formatEther(balance.toString()); 19 | } 20 | 21 | export async function transferBnb(toAddress, quantity) { 22 | const provider = await getMetaMaskProvider(); 23 | const signer = await provider.getSigner(); 24 | ethers.getAddress(toAddress);//valida endereço 25 | 26 | const tx = await signer.sendTransaction({ 27 | to: toAddress, 28 | value: ethers.parseEther(quantity) 29 | }) 30 | await tx.wait(); 31 | 32 | return tx; 33 | } 34 | 35 | export async function getTokenBalance(address, contractAddress, decimals = 18) { 36 | const provider = await getMetaMaskProvider(); 37 | const contract = new ethers.Contract(contractAddress, CONTRACT_ABI, provider); 38 | const balance = await contract.balanceOf(address); 39 | 40 | return ethers.formatUnits(balance, decimals); 41 | } 42 | 43 | export async function transferToken(toAddress, contractAddress, quantity, decimals = 18) { 44 | const provider = await getMetaMaskProvider(); 45 | const signer = await provider.getSigner(); 46 | 47 | const contract = new ethers.Contract(contractAddress, CONTRACT_ABI, signer); 48 | 49 | ethers.getAddress(toAddress);//valida endereço 50 | 51 | const tx = await contract.transfer(toAddress, ethers.parseUnits(quantity, decimals)); 52 | await tx.wait(); 53 | 54 | return tx; 55 | } 56 | 57 | export async function getTransaction(hash){ 58 | const provider = await getMetaMaskProvider(); 59 | const tx = await provider.getTransactionReceipt(hash); 60 | return tx; 61 | } -------------------------------------------------------------------------------- /metamask-transfer/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | const root = ReactDOM.createRoot(document.getElementById('root')); 6 | root.render( 7 | 8 | 9 | 10 | ); -------------------------------------------------------------------------------- /metamask-web3-node/.env.example: -------------------------------------------------------------------------------- 1 | #Your wallet private key 2 | PRIVATE_KEY= 3 | 4 | #Your Infura node URL 5 | INFURA_URL= 6 | 7 | #Your smart contract address in blockchain. Ex: 0x6e9fea1f661c599009844a97fa63c96bfc6ad83c (tutorial part 2) 8 | CONTRACT_ADDRESS= -------------------------------------------------------------------------------- /metamask-web3-node/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env -------------------------------------------------------------------------------- /metamask-web3-node/README.md: -------------------------------------------------------------------------------- 1 | # MetaMask Web3 Node 2 | 3 | Simple examples of Node.js app connecting to blockchain using MetaMask and Infura. 4 | 5 | ## How to Run 6 | 1. git clone the project 7 | 2. cd metamask-web3-node 8 | 3. npm install 9 | 4. Create a .env file as .env.example with your configs (follow instructions in the examples) 10 | 5. Edit the abi.json as your contract require 11 | 6. Write the code to call the function you want in the end of index.js 12 | 7. npm start 13 | 14 | ## Referências 15 | 16 | Tutoriais em: 17 | - blockchain (parte 1): https://www.luiztools.com.br/post/integracao-com-a-blockchain-com-node-js-e-web3-js/ 18 | - smart contracts (parte 2): https://www.luiztools.com.br/post/integracao-com-smart-contracts-com-node-js-e-web3-js/ 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 -------------------------------------------------------------------------------- /metamask-web3-node/abi.example.json: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"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":"remaining","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"balance","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":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /metamask-web3-node/index.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | const ABI = require("./abi.json"); 3 | 4 | const Web3 = require('web3'); 5 | const web3 = new Web3(process.env.INFURA_URL); 6 | 7 | //additional lines if you want to send transactions 8 | const account = web3.eth.accounts.privateKeyToAccount('0x' + process.env.PRIVATE_KEY); 9 | web3.eth.accounts.wallet.add(account); 10 | web3.eth.defaultAccount = account.address; 11 | 12 | async function getPrcBalance(from) { 13 | const contract = new web3.eth.Contract(ABI, process.env.CONTRACT_ADDRESS); 14 | const balance = await contract.methods.balanceOf(from).call(); 15 | console.log(web3.utils.fromWei(balance)); 16 | } 17 | 18 | //getPrcBalance("0xE4ffEEd88111e1DFCc3a852d9334C65e38BF2880"); 19 | 20 | async function approvePrcTransfer(spender, value) { 21 | const contract = new web3.eth.Contract(ABI, process.env.CONTRACT_ADDRESS, { from: process.env.WALLET }); 22 | const tx = await contract.methods.approve(spender, value).send(); 23 | console.log(tx.transactionHash); 24 | return tx; 25 | } 26 | 27 | approvePrcTransfer("0x0D1195969395B8a23dA37Dce78b823BE8cD5a0a4", "1000"); 28 | 29 | async function getEthBalance(from) { 30 | const balance = await web3.eth.getBalance(from); 31 | console.log(web3.utils.fromWei(balance)); 32 | } 33 | 34 | async function transferEth(to, value) { 35 | const nonce = await web3.eth.getTransactionCount(process.env.WALLET, 'latest'); 36 | 37 | const transaction = { 38 | to, 39 | value, 40 | gas: 21000, 41 | nonce 42 | }; 43 | 44 | const signedTx = await web3.eth.accounts.signTransaction(transaction, process.env.PRIVATE_KEY); 45 | 46 | const tx = await web3.eth.sendSignedTransaction(signedTx.rawTransaction); 47 | console.log(tx.transactionHash) 48 | return tx.transactionHash; 49 | } 50 | 51 | //transferEth("0x0D1195969395B8a23dA37Dce78b823BE8cD5a0a4", 1000); -------------------------------------------------------------------------------- /metamask-web3-node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metamask-web3", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index" 8 | }, 9 | "keywords": [], 10 | "author": "LuizTools", 11 | "license": "MIT", 12 | "dependencies": { 13 | "dotenv": "^16.0.3", 14 | "web3": "^4.2.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /metamask-web3-react/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /metamask-web3-react/README.md: -------------------------------------------------------------------------------- 1 | # MetaMask Web3 React 2 | 3 | Simple examples of ReactJS app connecting to blockchain using MetaMask. 4 | 5 | ## How to Run 6 | 1. git clone the project 7 | 2. cd metamask-web3-react 8 | 3. npm install 9 | 4. npm start 10 | 11 | ## Referências 12 | 13 | Tutorial em https://www.luiztools.com.br/post/integracao-com-a-blockchain-com-reactjs-e-web3-js/ 14 | 15 | Conheça meu curso de web3, blockchain e solidity: https://www.luiztools.com.br/curso-web23 16 | 17 | Me siga nas redes sociais: https://about.me/luiztools 18 | 19 | Receba novidades no Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /metamask-web3-react/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metamask-web3-react", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "react-scripts": "5.0.1", 12 | "web-vitals": "^2.1.4", 13 | "web3": "^4.2.2" 14 | }, 15 | "scripts": { 16 | "start": "react-scripts start", 17 | "build": "react-scripts build", 18 | "test": "react-scripts test", 19 | "eject": "react-scripts eject" 20 | }, 21 | "eslintConfig": { 22 | "extends": [ 23 | "react-app", 24 | "react-app/jest" 25 | ] 26 | }, 27 | "browserslist": { 28 | "production": [ 29 | ">0.2%", 30 | "not dead", 31 | "not op_mini all" 32 | ], 33 | "development": [ 34 | "last 1 chrome version", 35 | "last 1 firefox version", 36 | "last 1 safari version" 37 | ] 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /metamask-web3-react/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/metamask-web3-react/public/favicon.ico -------------------------------------------------------------------------------- /metamask-web3-react/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /metamask-web3-react/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/metamask-web3-react/public/logo192.png -------------------------------------------------------------------------------- /metamask-web3-react/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/metamask-web3-react/public/logo512.png -------------------------------------------------------------------------------- /metamask-web3-react/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /metamask-web3-react/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /metamask-web3-react/src/App.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import { getEthBalance, transferEth } from './MetaMaskService'; 3 | 4 | function App() { 5 | 6 | const [address, setAddress] = useState("0xE4ffEEd88111e1DFCc3a852d9334C65e38BF2880"); 7 | const [balance, setBalance] = useState(''); 8 | const [toAddress, setToAddress] = useState(""); 9 | const [quantity, setQuantity] = useState(""); 10 | const [message, setMessage] = useState(''); 11 | 12 | async function checkBalance() { 13 | try { 14 | setMessage(""); 15 | let balance = await getEthBalance(address); 16 | setBalance(balance); 17 | } 18 | catch (err) { 19 | setMessage(err.message); 20 | } 21 | } 22 | 23 | async function transfer() { 24 | try { 25 | setMessage(""); 26 | let result = await transferEth(address, toAddress, quantity); 27 | setMessage(JSON.stringify(result)); 28 | } 29 | catch (err) { 30 | setMessage(err.message); 31 | } 32 | } 33 | 34 | return ( 35 |
36 |

37 | My Address : setAddress(evt.target.value)} value={address} /> 38 |

39 |

40 | Balance: {balance} 41 |

42 |

checkBalance()} />

43 |
44 |

45 | To Address: setToAddress(evt.target.value)} /> 46 |

47 |

48 | Qty: setQuantity(evt.target.value)} /> 49 |

50 |

51 | transfer()} /> 52 |

53 |
54 |

55 | {message} 56 |

57 |
58 | ); 59 | } 60 | 61 | export default App; 62 | -------------------------------------------------------------------------------- /metamask-web3-react/src/MetaMaskService.js: -------------------------------------------------------------------------------- 1 | import Web3 from 'web3'; 2 | 3 | async function getMetaMaskProvider() { 4 | if (!window.ethereum) throw new Error(`No MetaMask found!`); 5 | 6 | const web3 = new Web3(window.ethereum); 7 | 8 | const accounts = await web3.eth.requestAccounts(); 9 | if (!accounts || !accounts.length) throw new Error('Wallet not found/allowed!'); 10 | 11 | return web3; 12 | } 13 | 14 | export async function getEthBalance(address) { 15 | const web3 = await getMetaMaskProvider(); 16 | const balance = await web3.eth.getBalance(address); 17 | return web3.utils.fromWei(balance); 18 | } 19 | 20 | export async function transferEth(myAddress, toAddress, quantity) { 21 | const web3 = await getMetaMaskProvider(); 22 | const value = web3.utils.toWei(quantity, "ether"); 23 | 24 | const nonce = await web3.eth.getTransactionCount(myAddress, 'latest'); 25 | const transaction = { from: myAddress, to: toAddress, value, gas: 21000, nonce }; 26 | const tx = await web3.eth.sendTransaction(transaction); 27 | 28 | return tx.transactionHash; 29 | } -------------------------------------------------------------------------------- /metamask-web3-react/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import App from './App'; 4 | 5 | const root = ReactDOM.createRoot(document.getElementById('root')); 6 | root.render( 7 | 8 | 9 | 10 | ); -------------------------------------------------------------------------------- /nodejs-event-ethers/.env.example: -------------------------------------------------------------------------------- 1 | # Full Node WebSocket server URL. Ex: use Infura for Ethereum/Goerli or Quicknode for BSC 2 | WEBSOCKET_URL= 3 | 4 | # Your contract address deployed to blockchain 5 | CONTRACT_ADDRESS= -------------------------------------------------------------------------------- /nodejs-event-ethers/README.md: -------------------------------------------------------------------------------- 1 | # nodejs-event-ethers 2 | 3 | A simple web backend written with JS/Node to listen events in the blockchain using EthersJS. 4 | 5 | Smart Contract address (ERC-20 example token): https://testnet.bscscan.com/address/0x94a9838528E1b0022c334D3c1c7D5e684c222B07 6 | 7 | ## How to Run 8 | 1. git clone 9 | 2. npm install 10 | 3. npm start 11 | 12 | ## Referências 13 | 14 | Tutorial em: https://www.luiztools.com.br/post/como-monitorar-eventos-da-blockchain-com-nodejs-ethersjs/ 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 -------------------------------------------------------------------------------- /nodejs-event-ethers/index.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("ethers"); 2 | require("dotenv").config(); 3 | 4 | async function doListen() { 5 | const provider = new ethers.WebSocketProvider(process.env.WEBSOCKET_URL); 6 | 7 | const filter = { 8 | address: process.env.CONTRACT_ADDRESS, 9 | topics: [ 10 | ethers.id("Transfer(address,address,uint256)") 11 | ] 12 | } 13 | provider.on(filter, () => { 14 | console.log('fire transfer') 15 | }); 16 | } 17 | 18 | doListen(); -------------------------------------------------------------------------------- /nodejs-event-ethers/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-event-ethers", 3 | "version": "1.0.0", 4 | "lockfileVersion": 2, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "nodejs-event-ethers", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^16.0.3", 13 | "ethers": "^6.0.2" 14 | } 15 | }, 16 | "node_modules/@adraffy/ens-normalize": { 17 | "version": "1.8.9", 18 | "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.8.9.tgz", 19 | "integrity": "sha512-93OmGCV0vO8+JQ3FHG+gZk/MPHzzMPDRiCiFcCQNTCnHaaxsacO3ScTPGlu2wX2dOtgfalbchPcw1cOYYjHCYQ==" 20 | }, 21 | "node_modules/@noble/hashes": { 22 | "version": "1.1.2", 23 | "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", 24 | "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==", 25 | "funding": [ 26 | { 27 | "type": "individual", 28 | "url": "https://paulmillr.com/funding/" 29 | } 30 | ] 31 | }, 32 | "node_modules/@noble/secp256k1": { 33 | "version": "1.7.1", 34 | "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", 35 | "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", 36 | "funding": [ 37 | { 38 | "type": "individual", 39 | "url": "https://paulmillr.com/funding/" 40 | } 41 | ] 42 | }, 43 | "node_modules/aes-js": { 44 | "version": "4.0.0-beta.3", 45 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.3.tgz", 46 | "integrity": "sha512-/xJX0/VTPcbc5xQE2VUP91y1xN8q/rDfhEzLm+vLc3hYvb5+qHCnpJRuFcrKn63zumK/sCwYYzhG8HP78JYSTA==" 47 | }, 48 | "node_modules/dotenv": { 49 | "version": "16.0.3", 50 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 51 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", 52 | "engines": { 53 | "node": ">=12" 54 | } 55 | }, 56 | "node_modules/ethers": { 57 | "version": "6.0.2", 58 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.0.2.tgz", 59 | "integrity": "sha512-YOHvShFjKordVEN7kVcKRp6xhpjq4ggZPcsAVuadXravZ7Z4qOflOEGfIaMnKp2ZQJqyR07iYVD6YVty4dI3Fw==", 60 | "funding": [ 61 | { 62 | "type": "individual", 63 | "url": "https://github.com/sponsors/ethers-io/" 64 | }, 65 | { 66 | "type": "individual", 67 | "url": "https://www.buymeacoffee.com/ricmoo" 68 | } 69 | ], 70 | "dependencies": { 71 | "@adraffy/ens-normalize": "1.8.9", 72 | "@noble/hashes": "1.1.2", 73 | "@noble/secp256k1": "1.7.1", 74 | "aes-js": "4.0.0-beta.3", 75 | "tslib": "2.4.0", 76 | "ws": "8.5.0" 77 | }, 78 | "engines": { 79 | "node": ">=14.0.0" 80 | } 81 | }, 82 | "node_modules/tslib": { 83 | "version": "2.4.0", 84 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 85 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" 86 | }, 87 | "node_modules/ws": { 88 | "version": "8.5.0", 89 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", 90 | "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", 91 | "engines": { 92 | "node": ">=10.0.0" 93 | }, 94 | "peerDependencies": { 95 | "bufferutil": "^4.0.1", 96 | "utf-8-validate": "^5.0.2" 97 | }, 98 | "peerDependenciesMeta": { 99 | "bufferutil": { 100 | "optional": true 101 | }, 102 | "utf-8-validate": { 103 | "optional": true 104 | } 105 | } 106 | } 107 | }, 108 | "dependencies": { 109 | "@adraffy/ens-normalize": { 110 | "version": "1.8.9", 111 | "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.8.9.tgz", 112 | "integrity": "sha512-93OmGCV0vO8+JQ3FHG+gZk/MPHzzMPDRiCiFcCQNTCnHaaxsacO3ScTPGlu2wX2dOtgfalbchPcw1cOYYjHCYQ==" 113 | }, 114 | "@noble/hashes": { 115 | "version": "1.1.2", 116 | "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.1.2.tgz", 117 | "integrity": "sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA==" 118 | }, 119 | "@noble/secp256k1": { 120 | "version": "1.7.1", 121 | "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", 122 | "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==" 123 | }, 124 | "aes-js": { 125 | "version": "4.0.0-beta.3", 126 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.3.tgz", 127 | "integrity": "sha512-/xJX0/VTPcbc5xQE2VUP91y1xN8q/rDfhEzLm+vLc3hYvb5+qHCnpJRuFcrKn63zumK/sCwYYzhG8HP78JYSTA==" 128 | }, 129 | "dotenv": { 130 | "version": "16.0.3", 131 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", 132 | "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==" 133 | }, 134 | "ethers": { 135 | "version": "6.0.2", 136 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.0.2.tgz", 137 | "integrity": "sha512-YOHvShFjKordVEN7kVcKRp6xhpjq4ggZPcsAVuadXravZ7Z4qOflOEGfIaMnKp2ZQJqyR07iYVD6YVty4dI3Fw==", 138 | "requires": { 139 | "@adraffy/ens-normalize": "1.8.9", 140 | "@noble/hashes": "1.1.2", 141 | "@noble/secp256k1": "1.7.1", 142 | "aes-js": "4.0.0-beta.3", 143 | "tslib": "2.4.0", 144 | "ws": "8.5.0" 145 | } 146 | }, 147 | "tslib": { 148 | "version": "2.4.0", 149 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 150 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" 151 | }, 152 | "ws": { 153 | "version": "8.5.0", 154 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", 155 | "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", 156 | "requires": {} 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /nodejs-event-ethers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-event-ethers", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index" 8 | }, 9 | "keywords": [], 10 | "author": "", 11 | "license": "ISC", 12 | "dependencies": { 13 | "dotenv": "^16.0.3", 14 | "ethers": "^6.0.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /nodejs-event-web3/.env.example: -------------------------------------------------------------------------------- 1 | # Full Node WebSocket server URL. Ex: use Infura for Ethereum/Goerli or Quicknode for BSC 2 | WEBSOCKET_URL= 3 | 4 | # Your contract address deployed to blockchain 5 | CONTRACT_ADDRESS= -------------------------------------------------------------------------------- /nodejs-event-web3/README.md: -------------------------------------------------------------------------------- 1 | # nodejs-event-web3 2 | 3 | A simple web backend written with JS/Node to listen events in the blockchain using web3.js. 4 | 5 | Smart Contract address (ERC-20 example token): https://testnet.bscscan.com/address/0x94a9838528E1b0022c334D3c1c7D5e684c222B07 6 | 7 | ## How to Run 8 | 1. git clone 9 | 2. npm install 10 | 3. npm start 11 | 12 | ## Referências 13 | 14 | Tutorial em: https://www.luiztools.com.br/post/como-monitorar-eventos-da-blockchain-com-nodejs-web3-js/ 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 -------------------------------------------------------------------------------- /nodejs-event-web3/abi.json: -------------------------------------------------------------------------------- 1 | [{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"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":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","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":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","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":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /nodejs-event-web3/index.js: -------------------------------------------------------------------------------- 1 | const ABI = require("./abi.json"); 2 | const Web3 = require("web3"); 3 | require("dotenv").config(); 4 | 5 | async function doListen() { 6 | const web3 = new Web3(process.env.WEBSOCKET_URL); 7 | const contract = new web3.eth.Contract(ABI, process.env.CONTRACT_ADDRESS); 8 | 9 | contract.events.Transfer({ 10 | filter: { 11 | from: "0xE4ffEEd88111e1DFCc3a852d9334C65e38BF2880" 12 | }, 13 | fromBlock: "latest" 14 | }) 15 | .on('data', event => console.log("event: " + JSON.stringify(event))) 16 | .on('changed', changed => console.log("changed: " + changed)) 17 | .on('error', err => console.error(err)) 18 | .on('connected', str => console.log("connected: " + str)); 19 | } 20 | 21 | doListen(); -------------------------------------------------------------------------------- /nodejs-event-web3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nodejs-evento-web3", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "keywords": [], 10 | "author": "LuizTools", 11 | "license": "MIT", 12 | "dependencies": { 13 | "dotenv": "^16.0.3", 14 | "web3": "^4.2.2" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /pinata-ipfs-example/.env.example: -------------------------------------------------------------------------------- 1 | # Your API Key. Ex: a89fdca9c0c9eea0349r 2 | API_KEY= 3 | 4 | # Your API Secret. Ex: 3d4geafcc6780fcc2b95d94713671bcfhfba0c2921a6b43e11d73045c1f41605 5 | API_SECRET= -------------------------------------------------------------------------------- /pinata-ipfs-example/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # next.js 12 | /.next/ 13 | /out/ 14 | 15 | # production 16 | /build 17 | 18 | # misc 19 | .DS_Store 20 | *.pem 21 | 22 | # debug 23 | npm-debug.log* 24 | yarn-debug.log* 25 | yarn-error.log* 26 | 27 | # local env files 28 | .env*.local 29 | 30 | # vercel 31 | .vercel 32 | 33 | # typescript 34 | *.tsbuildinfo 35 | next-env.d.ts 36 | -------------------------------------------------------------------------------- /pinata-ipfs-example/README.md: -------------------------------------------------------------------------------- 1 | # pinata-ipfs-example 2 | 3 | A simple example written in Next.js to upload files to IPFS network using Pinata API 4 | 5 | ## How to Run 6 | 1. git clone 7 | 2. npm install 8 | 3. create .env.local file using .env.example as template 9 | 4. fill your Pinata's API key and secret in .env.local 10 | 5. npm run dev 11 | 12 | ## Referências 13 | 14 | Tutorial em: https://www.luiztools.com.br/post/como-fazer-upload-de-arquivos-para-ipfs-via-pinata-api/ 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 -------------------------------------------------------------------------------- /pinata-ipfs-example/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "paths": { 4 | "@/*": ["./src/*"] 5 | } 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /pinata-ipfs-example/next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const nextConfig = { 3 | reactStrictMode: true, 4 | env: { 5 | API_KEY: process.env.API_KEY, 6 | API_SECRET: process.env.API_SECRET 7 | } 8 | } 9 | 10 | module.exports = nextConfig 11 | -------------------------------------------------------------------------------- /pinata-ipfs-example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "pinata-ipfs-example", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.3.6", 13 | "dotenv": "^16.0.3", 14 | "next": "13.3.1", 15 | "react": "18.2.0", 16 | "react-dom": "18.2.0" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /pinata-ipfs-example/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/luiztools/web3-examples/43aea72ab4c3db8b098e5bb019cd85bf83d19eec/pinata-ipfs-example/public/favicon.ico -------------------------------------------------------------------------------- /pinata-ipfs-example/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pinata-ipfs-example/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /pinata-ipfs-example/src/pages/_app.js: -------------------------------------------------------------------------------- 1 | import '@/styles/globals.css' 2 | 3 | export default function App({ Component, pageProps }) { 4 | return 5 | } 6 | -------------------------------------------------------------------------------- /pinata-ipfs-example/src/pages/_document.js: -------------------------------------------------------------------------------- 1 | import { Html, Head, Main, NextScript } from 'next/document' 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ) 13 | } 14 | -------------------------------------------------------------------------------- /pinata-ipfs-example/src/pages/index.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | import Head from 'next/head'; 3 | import styles from '@/styles/Home.module.css'; 4 | import axios from 'axios'; 5 | 6 | export default function Home() { 7 | 8 | const [image, setImage] = useState(""); 9 | const [message, setMessage] = useState(""); 10 | 11 | async function upload() { 12 | const formData = new FormData(); 13 | formData.append("file", image); 14 | 15 | const response = await axios({ 16 | method: "post", 17 | url: "https://api.pinata.cloud/pinning/pinFileToIPFS", 18 | data: formData, 19 | headers: { 20 | 'pinata_api_key': `${process.env.API_KEY}`, 21 | 'pinata_secret_api_key': `${process.env.API_SECRET}`, 22 | "Content-Type": "multipart/form-data" 23 | }, 24 | }); 25 | 26 | return `ipfs://${response.data.IpfsHash}`; 27 | } 28 | 29 | function btnUploadClick() { 30 | setMessage("uploading..."); 31 | upload() 32 | .then(result => setMessage(result)) 33 | .catch(err => setMessage(err.message)); 34 | } 35 | 36 | function onFileChange(evt) { 37 | if (evt.target.files) { 38 | setImage(evt.target.files[0]); 39 | } 40 | } 41 | 42 | return ( 43 | <> 44 | 45 | Pinata IPFS Example 46 | 47 | 48 | 49 | 50 |
51 |
52 | 53 | 54 |
55 | {message} 56 |
57 | 58 | ) 59 | } 60 | -------------------------------------------------------------------------------- /pinata-ipfs-example/src/styles/Home.module.css: -------------------------------------------------------------------------------- 1 | .main { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: space-between; 5 | align-items: center; 6 | padding: 6rem; 7 | min-height: 100vh; 8 | } 9 | 10 | .description { 11 | display: inherit; 12 | justify-content: inherit; 13 | align-items: inherit; 14 | font-size: 0.85rem; 15 | max-width: var(--max-width); 16 | width: 100%; 17 | z-index: 2; 18 | font-family: var(--font-mono); 19 | } 20 | 21 | .description a { 22 | display: flex; 23 | justify-content: center; 24 | align-items: center; 25 | gap: 0.5rem; 26 | } 27 | 28 | .description p { 29 | position: relative; 30 | margin: 0; 31 | padding: 1rem; 32 | background-color: rgba(var(--callout-rgb), 0.5); 33 | border: 1px solid rgba(var(--callout-border-rgb), 0.3); 34 | border-radius: var(--border-radius); 35 | } 36 | 37 | .code { 38 | font-weight: 700; 39 | font-family: var(--font-mono); 40 | } 41 | 42 | .grid { 43 | display: grid; 44 | grid-template-columns: repeat(4, minmax(25%, auto)); 45 | width: var(--max-width); 46 | max-width: 100%; 47 | } 48 | 49 | .card { 50 | padding: 1rem 1.2rem; 51 | border-radius: var(--border-radius); 52 | background: rgba(var(--card-rgb), 0); 53 | border: 1px solid rgba(var(--card-border-rgb), 0); 54 | transition: background 200ms, border 200ms; 55 | } 56 | 57 | .card span { 58 | display: inline-block; 59 | transition: transform 200ms; 60 | } 61 | 62 | .card h2 { 63 | font-weight: 600; 64 | margin-bottom: 0.7rem; 65 | } 66 | 67 | .card p { 68 | margin: 0; 69 | opacity: 0.6; 70 | font-size: 0.9rem; 71 | line-height: 1.5; 72 | max-width: 30ch; 73 | } 74 | 75 | .center { 76 | display: flex; 77 | justify-content: center; 78 | align-items: center; 79 | position: relative; 80 | padding: 4rem 0; 81 | } 82 | 83 | .center::before { 84 | background: var(--secondary-glow); 85 | border-radius: 50%; 86 | width: 480px; 87 | height: 360px; 88 | margin-left: -400px; 89 | } 90 | 91 | .center::after { 92 | background: var(--primary-glow); 93 | width: 240px; 94 | height: 180px; 95 | z-index: -1; 96 | } 97 | 98 | .center::before, 99 | .center::after { 100 | content: ''; 101 | left: 50%; 102 | position: absolute; 103 | filter: blur(45px); 104 | transform: translateZ(0); 105 | } 106 | 107 | .logo { 108 | position: relative; 109 | } 110 | /* Enable hover only on non-touch devices */ 111 | @media (hover: hover) and (pointer: fine) { 112 | .card:hover { 113 | background: rgba(var(--card-rgb), 0.1); 114 | border: 1px solid rgba(var(--card-border-rgb), 0.15); 115 | } 116 | 117 | .card:hover span { 118 | transform: translateX(4px); 119 | } 120 | } 121 | 122 | @media (prefers-reduced-motion) { 123 | .card:hover span { 124 | transform: none; 125 | } 126 | } 127 | 128 | /* Mobile */ 129 | @media (max-width: 700px) { 130 | .content { 131 | padding: 4rem; 132 | } 133 | 134 | .grid { 135 | grid-template-columns: 1fr; 136 | margin-bottom: 120px; 137 | max-width: 320px; 138 | text-align: center; 139 | } 140 | 141 | .card { 142 | padding: 1rem 2.5rem; 143 | } 144 | 145 | .card h2 { 146 | margin-bottom: 0.5rem; 147 | } 148 | 149 | .center { 150 | padding: 8rem 0 6rem; 151 | } 152 | 153 | .center::before { 154 | transform: none; 155 | height: 300px; 156 | } 157 | 158 | .description { 159 | font-size: 0.8rem; 160 | } 161 | 162 | .description a { 163 | padding: 1rem; 164 | } 165 | 166 | .description p, 167 | .description div { 168 | display: flex; 169 | justify-content: center; 170 | position: fixed; 171 | width: 100%; 172 | } 173 | 174 | .description p { 175 | align-items: center; 176 | inset: 0 0 auto; 177 | padding: 2rem 1rem 1.4rem; 178 | border-radius: 0; 179 | border: none; 180 | border-bottom: 1px solid rgba(var(--callout-border-rgb), 0.25); 181 | background: linear-gradient( 182 | to bottom, 183 | rgba(var(--background-start-rgb), 1), 184 | rgba(var(--callout-rgb), 0.5) 185 | ); 186 | background-clip: padding-box; 187 | backdrop-filter: blur(24px); 188 | } 189 | 190 | .description div { 191 | align-items: flex-end; 192 | pointer-events: none; 193 | inset: auto 0 0; 194 | padding: 2rem; 195 | height: 200px; 196 | background: linear-gradient( 197 | to bottom, 198 | transparent 0%, 199 | rgb(var(--background-end-rgb)) 40% 200 | ); 201 | z-index: 1; 202 | } 203 | } 204 | 205 | /* Tablet and Smaller Desktop */ 206 | @media (min-width: 701px) and (max-width: 1120px) { 207 | .grid { 208 | grid-template-columns: repeat(2, 50%); 209 | } 210 | } 211 | 212 | @media (prefers-color-scheme: dark) { 213 | .vercelLogo { 214 | filter: invert(1); 215 | } 216 | 217 | .logo { 218 | filter: invert(1) drop-shadow(0 0 0.3rem #ffffff70); 219 | } 220 | } 221 | 222 | @keyframes rotate { 223 | from { 224 | transform: rotate(360deg); 225 | } 226 | to { 227 | transform: rotate(0deg); 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /pinata-ipfs-example/src/styles/globals.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --max-width: 1100px; 3 | --border-radius: 12px; 4 | --font-mono: ui-monospace, Menlo, Monaco, 'Cascadia Mono', 'Segoe UI Mono', 5 | 'Roboto Mono', 'Oxygen Mono', 'Ubuntu Monospace', 'Source Code Pro', 6 | 'Fira Mono', 'Droid Sans Mono', 'Courier New', monospace; 7 | 8 | --foreground-rgb: 0, 0, 0; 9 | --background-start-rgb: 214, 219, 220; 10 | --background-end-rgb: 255, 255, 255; 11 | 12 | --primary-glow: conic-gradient( 13 | from 180deg at 50% 50%, 14 | #16abff33 0deg, 15 | #0885ff33 55deg, 16 | #54d6ff33 120deg, 17 | #0071ff33 160deg, 18 | transparent 360deg 19 | ); 20 | --secondary-glow: radial-gradient( 21 | rgba(255, 255, 255, 1), 22 | rgba(255, 255, 255, 0) 23 | ); 24 | 25 | --tile-start-rgb: 239, 245, 249; 26 | --tile-end-rgb: 228, 232, 233; 27 | --tile-border: conic-gradient( 28 | #00000080, 29 | #00000040, 30 | #00000030, 31 | #00000020, 32 | #00000010, 33 | #00000010, 34 | #00000080 35 | ); 36 | 37 | --callout-rgb: 238, 240, 241; 38 | --callout-border-rgb: 172, 175, 176; 39 | --card-rgb: 180, 185, 188; 40 | --card-border-rgb: 131, 134, 135; 41 | } 42 | 43 | @media (prefers-color-scheme: dark) { 44 | :root { 45 | --foreground-rgb: 255, 255, 255; 46 | --background-start-rgb: 0, 0, 0; 47 | --background-end-rgb: 0, 0, 0; 48 | 49 | --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); 50 | --secondary-glow: linear-gradient( 51 | to bottom right, 52 | rgba(1, 65, 255, 0), 53 | rgba(1, 65, 255, 0), 54 | rgba(1, 65, 255, 0.3) 55 | ); 56 | 57 | --tile-start-rgb: 2, 13, 46; 58 | --tile-end-rgb: 2, 5, 19; 59 | --tile-border: conic-gradient( 60 | #ffffff80, 61 | #ffffff40, 62 | #ffffff30, 63 | #ffffff20, 64 | #ffffff10, 65 | #ffffff10, 66 | #ffffff80 67 | ); 68 | 69 | --callout-rgb: 20, 20, 20; 70 | --callout-border-rgb: 108, 108, 108; 71 | --card-rgb: 100, 100, 100; 72 | --card-border-rgb: 200, 200, 200; 73 | } 74 | } 75 | 76 | * { 77 | box-sizing: border-box; 78 | padding: 0; 79 | margin: 0; 80 | } 81 | 82 | html, 83 | body { 84 | max-width: 100vw; 85 | overflow-x: hidden; 86 | } 87 | 88 | body { 89 | color: rgb(var(--foreground-rgb)); 90 | background: linear-gradient( 91 | to bottom, 92 | transparent, 93 | rgb(var(--background-end-rgb)) 94 | ) 95 | rgb(var(--background-start-rgb)); 96 | } 97 | 98 | a { 99 | color: inherit; 100 | text-decoration: none; 101 | } 102 | 103 | @media (prefers-color-scheme: dark) { 104 | html { 105 | color-scheme: dark; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /storage-oracle-example/README.md: -------------------------------------------------------------------------------- 1 | # Storage Oracle Example 2 | A simple project showing how to construct a storage oracle architecture. 3 | 4 | ## Storage Oracle Contract 5 | The Solidity's storage oracle contract. 6 | 7 | ## Storage Oracle Backend 8 | The Node.js backend that feed the oracle. 9 | 10 | ## Storage Oracle Consumer 11 | A Solidity contract of an oracle consumer example. 12 | 13 | ## More 14 | 15 | Transcript at https://www.luiztools.com.br/post/como-criar-oraculo-blockchain-solidity-nodejs 16 | 17 | Follow me on social networks: https://about.me/luiztools 18 | 19 | Receive news on Telegram: https://t.me/luiznews 20 | 21 | Know my web3/blockchain course (portuguese): https://www.luiztools.com.br/curso-web23 -------------------------------------------------------------------------------- /storage-oracle-example/storage-oracle-backend/.env.example: -------------------------------------------------------------------------------- 1 | # Your backend port. Ex: 3000 2 | PORT= 3 | 4 | # Your blockchain node RPC provider. Ex: https://data-seed-prebsc-1-s1.binance.org:8545/ (BNB Testnet) 5 | BLOCKCHAIN_NODE= 6 | 7 | # Your storage oracle contract address at blockchain. Ex: 0xEEEEEE... 8 | CONTRACT_ADDRESS= 9 | 10 | # The owner wallet private key, to sign transactions. Ex: abc123 11 | PRIVATE_KEY= -------------------------------------------------------------------------------- /storage-oracle-example/storage-oracle-backend/README.md: -------------------------------------------------------------------------------- 1 | # Storage Oracle Backend 2 | The Node.js backend that feed the oracle. 3 | 4 | ## How to Run 5 | 6 | 1. git clone 7 | 2. deploy storage-oracle-contract at blockchain 8 | 3. cd storage-oracle-example 9 | 4. npm install 10 | 5. copy .env.example as .env 11 | 6. fill .env variables 12 | 7. npm start 13 | 14 | ## More 15 | 16 | Transcript at https://www.luiztools.com.br/post/como-criar-oraculo-blockchain-solidity-nodejs 17 | 18 | Follow me on social networks: https://about.me/luiztools 19 | 20 | Receive news on Telegram: https://t.me/luiznews 21 | 22 | Know my web3/blockchain course (portuguese): https://www.luiztools.com.br/curso-web23 -------------------------------------------------------------------------------- /storage-oracle-example/storage-oracle-backend/abi.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "inputs": [ 9 | { 10 | "internalType": "string", 11 | "name": "gameId", 12 | "type": "string" 13 | } 14 | ], 15 | "name": "getGame", 16 | "outputs": [ 17 | { 18 | "components": [ 19 | { 20 | "internalType": "uint256", 21 | "name": "timestamp", 22 | "type": "uint256" 23 | }, 24 | { 25 | "internalType": "string", 26 | "name": "team1", 27 | "type": "string" 28 | }, 29 | { 30 | "internalType": "uint8", 31 | "name": "score1", 32 | "type": "uint8" 33 | }, 34 | { 35 | "internalType": "string", 36 | "name": "team2", 37 | "type": "string" 38 | }, 39 | { 40 | "internalType": "uint8", 41 | "name": "score2", 42 | "type": "uint8" 43 | }, 44 | { 45 | "internalType": "uint8", 46 | "name": "winner", 47 | "type": "uint8" 48 | } 49 | ], 50 | "internalType": "struct Game", 51 | "name": "", 52 | "type": "tuple" 53 | } 54 | ], 55 | "stateMutability": "view", 56 | "type": "function" 57 | }, 58 | { 59 | "inputs": [ 60 | { 61 | "internalType": "string", 62 | "name": "gameId", 63 | "type": "string" 64 | }, 65 | { 66 | "components": [ 67 | { 68 | "internalType": "uint256", 69 | "name": "timestamp", 70 | "type": "uint256" 71 | }, 72 | { 73 | "internalType": "string", 74 | "name": "team1", 75 | "type": "string" 76 | }, 77 | { 78 | "internalType": "uint8", 79 | "name": "score1", 80 | "type": "uint8" 81 | }, 82 | { 83 | "internalType": "string", 84 | "name": "team2", 85 | "type": "string" 86 | }, 87 | { 88 | "internalType": "uint8", 89 | "name": "score2", 90 | "type": "uint8" 91 | }, 92 | { 93 | "internalType": "uint8", 94 | "name": "winner", 95 | "type": "uint8" 96 | } 97 | ], 98 | "internalType": "struct Game", 99 | "name": "newGame", 100 | "type": "tuple" 101 | } 102 | ], 103 | "name": "setGame", 104 | "outputs": [], 105 | "stateMutability": "nonpayable", 106 | "type": "function" 107 | } 108 | ] -------------------------------------------------------------------------------- /storage-oracle-example/storage-oracle-backend/index.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | const { ethers } = require("ethers"); 4 | const ABI = require("./abi.json"); 5 | const provider = new ethers.JsonRpcProvider(process.env.BLOCKCHAIN_NODE); 6 | const wallet = new ethers.Wallet(`${process.env.PRIVATE_KEY}`, provider); 7 | const contract = new ethers.Contract(process.env.CONTRACT_ADDRESS, ABI, wallet); 8 | 9 | const express = require("express"); 10 | 11 | const app = express(); 12 | 13 | app.use(express.json()); 14 | 15 | app.post("/", async (req, res) => { 16 | const { gameId, newGame } = req.body; 17 | 18 | const tx = await contract.setGame(gameId, { 19 | ...newGame, 20 | timestamp: Date.now() / 1000 21 | }); 22 | await tx.wait(); 23 | res.json(tx); 24 | }) 25 | 26 | app.listen(process.env.PORT, () => console.log("Server is running.")); -------------------------------------------------------------------------------- /storage-oracle-example/storage-oracle-backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "storage-oracle-backend", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "npm start" 8 | }, 9 | "keywords": [], 10 | "author": "LuizTools", 11 | "license": "MIT", 12 | "dependencies": { 13 | "dotenv": "^16.4.5", 14 | "ethers": "^6.13.2", 15 | "express": "^4.19.2" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /storage-oracle-example/storage-oracle-consumer/OracleConsumer.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.26; 4 | 5 | struct Game { 6 | uint timestamp; 7 | string team1; 8 | uint8 score1; 9 | string team2; 10 | uint8 score2; 11 | uint8 winner; 12 | } 13 | 14 | interface IStorageOracle { 15 | function getGame(string calldata gameId) external view returns (Game memory); 16 | } 17 | 18 | contract OracleConsumer { 19 | address immutable owner; 20 | address public oracleAddress; 21 | 22 | constructor(){ 23 | owner = msg.sender; 24 | } 25 | 26 | function setOracleAddress(address newAddress) public { 27 | require(msg.sender == owner, "Unauthorized"); 28 | oracleAddress = newAddress; 29 | } 30 | 31 | //gameId = bytes32("TEAM1XTEAM2YYYYMMDDHHMM") 32 | function getGame(string calldata gameId) external view returns (Game memory) { 33 | return IStorageOracle(oracleAddress).getGame(gameId); 34 | } 35 | } -------------------------------------------------------------------------------- /storage-oracle-example/storage-oracle-consumer/README.md: -------------------------------------------------------------------------------- 1 | # Storage Oracle Consumer 2 | A Solidity contract of an oracle consumer example. 3 | 4 | ## How to Run 5 | 6 | 1. deploy storage-oracle-contract at blockchain 7 | 2. start storage-oracle-backend 8 | 3. copy OracleConsumer.sol to Remix 9 | 4. Deploy at blockchain with an wallet 10 | 11 | ## More 12 | 13 | Transcript at https://www.luiztools.com.br/post/como-criar-oraculo-blockchain-solidity-nodejs 14 | 15 | Follow me on social networks: https://about.me/luiztools 16 | 17 | Receive news on Telegram: https://t.me/luiznews 18 | 19 | Know my web3/blockchain course (portuguese): https://www.luiztools.com.br/curso-web23 -------------------------------------------------------------------------------- /storage-oracle-example/storage-oracle-contract/README.md: -------------------------------------------------------------------------------- 1 | # Storage Oracle Contract 2 | The Solidity's storage oracle contract. 3 | 4 | ## How to Run 5 | 6 | 1. copy StorageOracle.sol to Remix 7 | 2. Deploy at blockchain with an wallet 8 | 3. start the storage-oracle-backend 9 | 10 | ## More 11 | 12 | Transcript at https://www.luiztools.com.br/post/como-criar-oraculo-blockchain-solidity-nodejs 13 | 14 | Follow me on social networks: https://about.me/luiztools 15 | 16 | Receive news on Telegram: https://t.me/luiznews 17 | 18 | Know my web3/blockchain course (portuguese): https://www.luiztools.com.br/curso-web23 -------------------------------------------------------------------------------- /storage-oracle-example/storage-oracle-contract/StorageOracle.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | 3 | pragma solidity ^0.8.26; 4 | 5 | struct Game { 6 | uint timestamp; 7 | string team1; 8 | uint8 score1; 9 | string team2; 10 | uint8 score2; 11 | uint8 winner; 12 | } 13 | 14 | contract StorageOracle { 15 | address immutable owner; 16 | //gameId = "TEAM1XTEAM2YYYYMMDDHHMM" 17 | mapping(bytes32 => Game) allGames; 18 | 19 | constructor() { 20 | owner = msg.sender; 21 | } 22 | 23 | function getGame(string calldata gameId) external view returns (Game memory) { 24 | return allGames[keccak256(abi.encodePacked(gameId))]; 25 | } 26 | 27 | function setGame(string calldata gameId, Game calldata newGame) external { 28 | require(msg.sender == owner, "Unauthorized"); 29 | allGames[keccak256(abi.encodePacked(gameId))] = newGame; 30 | } 31 | } -------------------------------------------------------------------------------- /wallet-btcjs/README.md: -------------------------------------------------------------------------------- 1 | # Wallet BTC JS 2 | A simple BTC console application in JS. 3 | 4 | ## How to Run 5 | 6 | 1. git clone 7 | 2. npm install 8 | 3. npm start 9 | 10 | ## More 11 | 12 | Follow me on social networks: https://about.me/luiztools 13 | 14 | Receive news on Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /wallet-btcjs/WalletService.js: -------------------------------------------------------------------------------- 1 | //WalletService.js 2 | const BIP32Factory = require("bip32"); 3 | const ecc = require("tiny-secp256k1"); 4 | const bip32 = BIP32Factory.default(ecc); 5 | 6 | const bip39 = require("bip39"); 7 | const bitcoin = require("bitcoinjs-lib"); 8 | 9 | const NETWORK = bitcoin.networks.testnet;//ou mainnet 10 | const PATH = "m/49'/1'/0'/0";//o /1 indica testnet, ou /0 para mainnet 11 | 12 | let myWallet = null; 13 | 14 | function createWallet() { 15 | let mnemonic = bip39.generateMnemonic(); 16 | const seed = bip39.mnemonicToSeedSync(mnemonic); 17 | 18 | let root = bip32.fromSeed(seed, NETWORK); 19 | 20 | let account = root.derivePath(PATH); 21 | let node = account.derive(0).derive(0); 22 | 23 | let address = bitcoin.payments.p2pkh({ 24 | pubkey: node.publicKey, 25 | network: NETWORK 26 | }).address; 27 | 28 | myWallet = { 29 | address, 30 | privateKey: node.toWIF() 31 | } 32 | 33 | return myWallet; 34 | } 35 | 36 | function recoverFromMnemonic(mnemonic) { 37 | const seed = bip39.mnemonicToSeedSync(mnemonic); 38 | return bip32.fromSeed(seed, NETWORK); 39 | } 40 | 41 | function recoverFromPrivateKey(privateKey) { 42 | let ONES = Buffer.alloc(32, 1) 43 | return bip32.fromPrivateKey(Buffer.from(privateKey, 'hex'), ONES, NETWORK); 44 | } 45 | 46 | function recoverWallet(pkOrMnemonic) { 47 | const root = pkOrMnemonic.indexOf(" ") !== -1 48 | ? recoverFromMnemonic(pkOrMnemonic) 49 | : recoverFromPrivateKey(pkOrMnemonic); 50 | 51 | let account = root.derivePath(PATH); 52 | let node = account.derive(0).derive(0); 53 | 54 | let address = bitcoin.payments.p2pkh({ 55 | pubkey: node.publicKey, 56 | network: NETWORK 57 | }).address; 58 | 59 | myWallet = { 60 | address, 61 | privateKey: node.toWIF() 62 | } 63 | 64 | return myWallet; 65 | } 66 | 67 | module.exports = { 68 | createWallet, 69 | recoverWallet 70 | } -------------------------------------------------------------------------------- /wallet-btcjs/createWallet.js: -------------------------------------------------------------------------------- 1 | const BIP32Factory = require("bip32"); 2 | const ecc = require("tiny-secp256k1"); 3 | const bip32 = BIP32Factory.default(ecc); 4 | 5 | const bip39 = require("bip39"); 6 | const bitcoin = require("bitcoinjs-lib"); 7 | 8 | const network = bitcoin.networks.testnet;//bitcoin é a mainnet 9 | 10 | //derivação de carteiras HD - Hierarquical Deterministic - o 1 indica testnet, ou 0 para mainnet 11 | const path = "m/49'/1'/0'/0"; 12 | 13 | let mnemonic = bip39.generateMnemonic(); 14 | const seed = bip39.mnemonicToSeedSync(mnemonic); 15 | 16 | let root = bip32.fromSeed(seed, network); 17 | 18 | let account = root.derivePath(path); 19 | let node = account.derive(0).derive(0); 20 | 21 | let btcAddress = bitcoin.payments.p2pkh({ 22 | pubkey: node.publicKey, 23 | network: network 24 | }).address; 25 | 26 | console.log("Carteira gerada!"); 27 | console.log("Endereço: ", btcAddress); 28 | console.log("Chave Privada: ", node.toWIF()); 29 | console.log("Seed: ", mnemonic); -------------------------------------------------------------------------------- /wallet-btcjs/index.js: -------------------------------------------------------------------------------- 1 | //index.js 2 | const readline = require("readline"); 3 | const rl = readline.createInterface({ 4 | input: process.stdin, 5 | output: process.stdout 6 | }) 7 | 8 | const WalletService = require("./WalletService"); 9 | let myAddress = ""; 10 | 11 | function menu() { 12 | setTimeout(() => { 13 | console.clear(); 14 | 15 | if (myAddress) 16 | console.log(`You are logged as ${myAddress}`); 17 | else 18 | console.log(`You aren't logged.`); 19 | 20 | console.log("1 - Create Wallet"); 21 | console.log("2 - Recover Wallet"); 22 | console.log("3 - Balance"); 23 | console.log("4 - Send BTC"); 24 | console.log("5 - Search tx"); 25 | rl.question("Choose your option: ", (answer) => { 26 | switch (answer) { 27 | case "1": createWallet(); break; 28 | case "2": recoverWallet(); break; 29 | case "3": getBalance(); break; 30 | case "4": sendBtc(); break; 31 | case "5": searchTx(); break; 32 | default: { 33 | console.log('Wrong option!'); 34 | menu(); 35 | } 36 | } 37 | }) 38 | 39 | }, 1000) 40 | } 41 | 42 | menu(); 43 | 44 | function preMenu() { 45 | rl.question(`Press any key to continue...`, () => { 46 | menu(); 47 | }) 48 | } 49 | 50 | function createWallet() { 51 | console.clear(); 52 | 53 | const myWallet = WalletService.createWallet(); 54 | myAddress = myWallet.address; 55 | 56 | console.log(`Your new wallet:`); 57 | console.log(myAddress); 58 | 59 | preMenu(); 60 | } -------------------------------------------------------------------------------- /wallet-btcjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wallet-btcjs", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index.js" 8 | }, 9 | "keywords": [], 10 | "author": "LuizTools", 11 | "license": "MIT", 12 | "dependencies": { 13 | "bip32": "^4.0.0", 14 | "bip39": "^3.1.0", 15 | "bitcoinjs-lib": "^6.1.6", 16 | "tiny-secp256k1": "^2.2.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /wallet-ethers/.env.example: -------------------------------------------------------------------------------- 1 | # Your RPC Node URL. Ex with BSC Testnet: https://data-seed-prebsc-1-s1.binance.org:8545/ 2 | BLOCKCHAIN_NODE= 3 | 4 | # Your cryptcurrency symbol. Ex with BSC: BNB 5 | SYMBOL= -------------------------------------------------------------------------------- /wallet-ethers/README.md: -------------------------------------------------------------------------------- 1 | # Wallet Ethers 2 | Wallet example written in Node.js (console) with EthersJS. 3 | 4 | ## How to Run 5 | 6 | 1. git clone 7 | 2. cd wallet-ethers 8 | 3. npm install 9 | 4. copy .env.example as .env 10 | 5. fill .env variables 11 | 6. npm start 12 | 13 | ## More 14 | 15 | Full tutorial at (in portuguese): https://www.luiztools.com.br/post/como-criar-sua-propria-carteira-de-criptomoedas-com-js-ethers/ 16 | 17 | Follow me on social networks: https://about.me/luiztools 18 | 19 | Receive news on Telegram: https://t.me/luiznews -------------------------------------------------------------------------------- /wallet-ethers/WalletService.js: -------------------------------------------------------------------------------- 1 | const { ethers } = require("ethers"); 2 | 3 | const provider = new ethers.JsonRpcProvider(process.env.BLOCKCHAIN_NODE); 4 | 5 | let myWallet = null; 6 | 7 | function createWallet() { 8 | myWallet = ethers.Wallet.createRandom(provider); 9 | return myWallet; 10 | } 11 | 12 | function recoverWallet(pkOrMnemonic) { 13 | myWallet = pkOrMnemonic.indexOf(" ") !== -1 14 | ? ethers.Wallet.fromPhrase(pkOrMnemonic, provider) 15 | : new ethers.Wallet(pkOrMnemonic, provider); 16 | 17 | return myWallet; 18 | } 19 | 20 | async function getBalance(address) { 21 | const balance = await provider.getBalance(myWallet.address); 22 | return { 23 | balanceInWei: balance, 24 | balanceInEth: ethers.formatEther(balance) 25 | } 26 | } 27 | 28 | function addressIsValid(address) { 29 | return ethers.isAddress(address); 30 | } 31 | 32 | async function buildTransaction(toWallet, amountInEth) { 33 | const amount = ethers.parseEther(amountInEth); 34 | 35 | const tx = { 36 | to: toWallet, 37 | value: amount 38 | } 39 | 40 | const feeData = await provider.getFeeData(); 41 | const txFee = 21000n * feeData.gasPrice;//default gas limit para transferências 42 | 43 | const balance = await provider.getBalance(myWallet.address); 44 | if (balance < (amount + txFee)) { 45 | return false; 46 | } 47 | 48 | return tx; 49 | } 50 | 51 | function sendTransaction(tx){ 52 | return myWallet.sendTransaction(tx); 53 | } 54 | 55 | function getTransaction(hash){ 56 | return provider.getTransaction(hash); 57 | } 58 | 59 | module.exports = { 60 | createWallet, 61 | recoverWallet, 62 | getBalance, 63 | addressIsValid, 64 | buildTransaction, 65 | sendTransaction, 66 | getTransaction 67 | } -------------------------------------------------------------------------------- /wallet-ethers/index.js: -------------------------------------------------------------------------------- 1 | require("dotenv").config(); 2 | 3 | const SYMBOL = process.env.SYMBOL; 4 | 5 | const readline = require("readline"); 6 | const rl = readline.createInterface({ 7 | input: process.stdin, 8 | output: process.stdout 9 | }) 10 | 11 | const WalletService = require("./WalletService"); 12 | let myAddress = ""; 13 | 14 | function menu() { 15 | setTimeout(() => { 16 | console.clear(); 17 | 18 | if (myAddress) 19 | console.log(`You are logged as ${myAddress}`); 20 | else 21 | console.log(`You aren't logged.`); 22 | 23 | console.log("1 - Create Wallet"); 24 | console.log("2 - Recover Wallet"); 25 | console.log("3 - Balance"); 26 | console.log("4 - Send " + SYMBOL); 27 | console.log("5 - Search tx"); 28 | rl.question("Choose your option: ", (answer) => { 29 | switch (answer) { 30 | case "1": createWallet(); break; 31 | case "2": recoverWallet(); break; 32 | case "3": getBalance(); break; 33 | case "4": sendTx(); break; 34 | case "5": searchTx(); break; 35 | default: { 36 | console.log('Wrong option!'); 37 | menu(); 38 | } 39 | } 40 | }) 41 | 42 | }, 1000) 43 | } 44 | 45 | function preMenu() { 46 | rl.question(`Press any key to continue...`, () => { 47 | menu(); 48 | }) 49 | } 50 | 51 | function createWallet() { 52 | console.clear(); 53 | 54 | const myWallet = WalletService.createWallet(); 55 | myAddress = myWallet.address; 56 | 57 | console.log(`Your new wallet:`); 58 | console.log(myAddress); 59 | 60 | preMenu(); 61 | } 62 | 63 | function recoverWallet() { 64 | console.clear(); 65 | rl.question(`What is your private key or menmonic phrase? `, (pkOrMnemonic) => { 66 | const myWallet = WalletService.recoverWallet(pkOrMnemonic); 67 | myAddress = myWallet.address; 68 | 69 | console.log(`Your recovered wallet:`); 70 | console.log(myAddress); 71 | 72 | preMenu(); 73 | }) 74 | } 75 | 76 | async function getBalance() { 77 | console.clear(); 78 | 79 | if (!myAddress) { 80 | console.log(`You don't have a wallet yet.`); 81 | return preMenu(); 82 | } 83 | 84 | const { balanceInEth } = await WalletService.getBalance(myAddress); 85 | console.log(`${SYMBOL} ${balanceInEth}`); 86 | 87 | preMenu(); 88 | } 89 | 90 | function sendTx() { 91 | console.clear(); 92 | 93 | if (!myAddress) { 94 | console.log(`You don't have a wallet yet.`); 95 | return preMenu(); 96 | } 97 | 98 | console.log(`Your wallet is ${myAddress}`); 99 | rl.question(`To Wallet: `, (toWallet) => { 100 | if (!WalletService.addressIsValid(toWallet)) { 101 | console.log(`Invalid wallet.`); 102 | return preMenu(); 103 | } 104 | 105 | rl.question(`Amount (in ${SYMBOL}): `, async (amountInEth) => { 106 | if (!amountInEth) { 107 | console.log(`Invalid amount.`); 108 | return preMenu(); 109 | } 110 | 111 | const tx = await WalletService.buildTransaction(toWallet, amountInEth); 112 | 113 | if (!tx) { 114 | console.log(`Insufficient balance (amount + fee).`); 115 | return preMenu(); 116 | } 117 | 118 | try { 119 | const txReceipt = await WalletService.sendTransaction(tx); 120 | console.log("Transaction successful: "); 121 | console.log(txReceipt); 122 | } 123 | catch (err) { 124 | console.error(err); 125 | } 126 | 127 | return preMenu(); 128 | }) 129 | }) 130 | 131 | preMenu(); 132 | } 133 | 134 | function searchTx() { 135 | console.clear(); 136 | rl.question(`Your tx hash: `, async (hash) => { 137 | const txReceipt = await WalletService.getTransaction(hash); 138 | console.log("Transaction receipt: "); 139 | console.log(txReceipt); 140 | 141 | return preMenu(); 142 | }) 143 | } 144 | 145 | menu(); -------------------------------------------------------------------------------- /wallet-ethers/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wallet-ethers", 3 | "version": "1.0.0", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "wallet-ethers", 9 | "version": "1.0.0", 10 | "license": "ISC", 11 | "dependencies": { 12 | "dotenv": "^16.4.5", 13 | "ethers": "^6.13.1" 14 | } 15 | }, 16 | "node_modules/@adraffy/ens-normalize": { 17 | "version": "1.10.1", 18 | "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", 19 | "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==" 20 | }, 21 | "node_modules/@noble/curves": { 22 | "version": "1.2.0", 23 | "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", 24 | "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", 25 | "dependencies": { 26 | "@noble/hashes": "1.3.2" 27 | }, 28 | "funding": { 29 | "url": "https://paulmillr.com/funding/" 30 | } 31 | }, 32 | "node_modules/@noble/hashes": { 33 | "version": "1.3.2", 34 | "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", 35 | "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", 36 | "engines": { 37 | "node": ">= 16" 38 | }, 39 | "funding": { 40 | "url": "https://paulmillr.com/funding/" 41 | } 42 | }, 43 | "node_modules/@types/node": { 44 | "version": "18.15.13", 45 | "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", 46 | "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==" 47 | }, 48 | "node_modules/aes-js": { 49 | "version": "4.0.0-beta.5", 50 | "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", 51 | "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==" 52 | }, 53 | "node_modules/dotenv": { 54 | "version": "16.4.5", 55 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", 56 | "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", 57 | "engines": { 58 | "node": ">=12" 59 | }, 60 | "funding": { 61 | "url": "https://dotenvx.com" 62 | } 63 | }, 64 | "node_modules/ethers": { 65 | "version": "6.13.1", 66 | "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.1.tgz", 67 | "integrity": "sha512-hdJ2HOxg/xx97Lm9HdCWk949BfYqYWpyw4//78SiwOLgASyfrNszfMUNB2joKjvGUdwhHfaiMMFFwacVVoLR9A==", 68 | "funding": [ 69 | { 70 | "type": "individual", 71 | "url": "https://github.com/sponsors/ethers-io/" 72 | }, 73 | { 74 | "type": "individual", 75 | "url": "https://www.buymeacoffee.com/ricmoo" 76 | } 77 | ], 78 | "dependencies": { 79 | "@adraffy/ens-normalize": "1.10.1", 80 | "@noble/curves": "1.2.0", 81 | "@noble/hashes": "1.3.2", 82 | "@types/node": "18.15.13", 83 | "aes-js": "4.0.0-beta.5", 84 | "tslib": "2.4.0", 85 | "ws": "8.17.1" 86 | }, 87 | "engines": { 88 | "node": ">=14.0.0" 89 | } 90 | }, 91 | "node_modules/tslib": { 92 | "version": "2.4.0", 93 | "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", 94 | "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==" 95 | }, 96 | "node_modules/ws": { 97 | "version": "8.17.1", 98 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", 99 | "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", 100 | "engines": { 101 | "node": ">=10.0.0" 102 | }, 103 | "peerDependencies": { 104 | "bufferutil": "^4.0.1", 105 | "utf-8-validate": ">=5.0.2" 106 | }, 107 | "peerDependenciesMeta": { 108 | "bufferutil": { 109 | "optional": true 110 | }, 111 | "utf-8-validate": { 112 | "optional": true 113 | } 114 | } 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /wallet-ethers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wallet-ethers", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "node index" 8 | }, 9 | "keywords": [], 10 | "author": "LuizTools", 11 | "license": "MIT", 12 | "dependencies": { 13 | "dotenv": "^16.4.5", 14 | "ethers": "^6.13.1" 15 | } 16 | } 17 | --------------------------------------------------------------------------------