InfiniBrains / Awesome-GameDev-Resources

GameDev Dojo Community resource materials
MIT License
21 stars 71 forks source link

[Coding Dojo] - Introducao a Smart contracts com Solidity #21

Open tolstenko opened 1 year ago

tolstenko commented 1 year ago

Introducao a SmartContracts com Solidity

Tema: aplicação frontend consumindo um smartcontract Pitch: Desenvolvimento de Aplicações Descentralizadas

Sumario

Atividade:

Seguir documentacao https://hardhat.org/tutorial/creating-a-new-hardhat-project

mkdir smartocntracts-tuto
cd smartocntracts-tuto
npm init # setup the way you prefer
npx hardhat # select create typescript project

Seguir documentacao https://www.npmjs.com/package/@typechain/hardhat

npm install --save-dev typechain @typechain/hardhat @typechain/ethers-v5

Adicione no arquivo hardhat.config.ts

import '@typechain/hardhat'
import '@nomiclabs/hardhat-ethers'

Delete Lock.sol na pasta contracts e tbm Lock.ts da pasta testes. Crie um contrato simples de greet.ts na pasta contracts:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.9;

contract Greeter {
    constructor() {}

    function greet() public pure returns (string memory) {
        return "Hello, World!";
    }
}

bash:

npx hardhat compile

Agora vamos integrar ao front! O melhor caminho eh criar testes automatizados via typescript. Depois de criar as funcionalidades ja testadas, basta copiar o fluxo testado pro front e acoplar em algum botao.

Crie um arquivo na pasta test como greeter.ts

import { expect } from "chai";
import { ethers } from "hardhat";
import {Greeter__factory} from "../typechain-types";
import {Greeter} from "../typechain-types";

describe("Greeter", function () {
    let greeter: Greeter;

    async function deployGreeter() {
        const Greeter: Greeter__factory = await ethers.getContractFactory("Greeter") as Greeter__factory;
        const [owner, otherAccount] = await ethers.getSigners();
        greeter = await Greeter.deploy();
        return ;
    }

    beforeEach(async function () {
        await deployGreeter();
    });

    describe("Hello Test", function () {
        it("Should print \"Hello World!\"", async function () {
            let response = await greeter.greet();
            await expect(response).equal( "Hello, World!");
        });
    });
});

Rode os testes:

hardhat test

Se tudo tiver certo, faca deploy do contrato, recomendo testnet polygon. Edite o orquivo deploy.ts da pasta scripts. Para efetivamente fazer deploy do contrato.

import { ethers } from "hardhat";
import { Greeter__factory } from "../typechain-types";
import { Greeter } from "../typechain-types";

async function main() {
  const Greeter: Greeter__factory = await ethers.getContractFactory("Greeter") as Greeter__factory;
  const [owner, otherAccount] = await ethers.getSigners();
  let greeter: Greeter = await Greeter.deploy();
  await greeter.deployed();

  console.log(`Greeter deployed to ${greeter.address}`);
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

Pra que o contrato seja deployado em alguma chain, digamos polygon testnet, vc precisara das credenciais da wallet.

Pra isso crie um arquivo .env coloque ele no .gitignore e adicione o seu mnemonic phrase la. Se voce nao tiver uma wallet, recomendo criar uma apenas para este fim agora e salvar no arquivo .env. Como vamos usar .env nao esqueca de instalar o pacote dotenv com npm install --save-dev dotenv.

Para usar o contrato no navegador a forma mais simple eh adicionar a extensao metamask. Apos isto, importe suas chaves na metamask(criado no passo anterior) ou crie uma por la.

Use esse hardhat.config.ts que ele ja esta configurado para usar testnet da polygon e outras chains.

import { HardhatUserConfig } from "hardhat/config";
import "@nomicfoundation/hardhat-toolbox";
import '@typechain/hardhat'
import '@nomiclabs/hardhat-ethers'
import * as dotenv from "dotenv";
import { resolve } from "path";
import { Wallet } from "ethers";
import * as fs from 'fs';

dotenv.config({ path: resolve(__dirname, "./.env") });

let mnemonic = process.env.MNEMONIC || "";
let wallet:Wallet;
if(mnemonic)
  wallet = Wallet.fromMnemonic(mnemonic);
else {
  mnemonic = Wallet.createRandom().mnemonic.phrase;
  wallet = Wallet.fromMnemonic(mnemonic);
  console.warn("RANDOM MNEMONIC used: " + mnemonic);
  console.warn("PRIVATE KEY used: " + wallet.privateKey);
  console.warn("WALLET ADDR used: " + wallet.address);
  fs.writeFileSync(resolve(__dirname, "./.env"), `MNEMONIC="${mnemonic}"\nPRIVATEKEY="${wallet.privateKey}"\nWALLETADDRESS="${wallet.address}"\n`);
}

const config: HardhatUserConfig = {
  solidity: {
    compilers: [
      {
        version: "0.8.18",
        settings: {
          metadata: {
            bytecodeHash: "none",
          },
          optimizer: {
            enabled: true,
            runs: 800,
          },
        },
      }
    ],
  },
  networks: {
    local: {
      url: "http://localhost:8545",
      accounts: { mnemonic },
    },
    bsctest: {
      url: "https://data-seed-prebsc-1-s1.binance.org:8545",
      chainId: 97,
      accounts: { mnemonic },
    },
    bsc: {
      url: "https://bsc-dataseed.binance.org/",
      chainId: 56,
      accounts: { mnemonic },
    },
    mumbai: {
      url: "https://polygon-mumbai.blockpi.network/v1/rpc/public",
      chainId: 80001,
      accounts: { mnemonic },
    },
    hardhat: {
      accounts: { mnemonic },
    },
  },
  gasReporter: {
    enabled: process.env.REPORT_GAS !== undefined,
    currency: "USD",
  },
  typechain: {
    outDir: "typechain-types",
    target: "ethers-v5",
  },
  paths: {
    artifacts: "./artifacts",
    cache: "./cache",
    sources: "./contracts",
    tests: "./test",
  },
};

export default config;

Adicione a mumbai a sua wallet aqui https://chainlist.org/chain/80001

Se voce nao tiver tokens na wallet que vc estiver usando, precisamos adicionar fundos, no caso da polygon mumbai, adicione fundos aqui. https://faucet.polygon.technology/

Para deployar para a polygon testnet basta rodar agora com tudo configurado:

hardhat run --network mumbai scripts/deploy.ts

Guarde o endereco do contrato pois vamos usar ele na integracao com o front. No meu caso o endereco do contrato gerado foi: "0xe720699f67778FBF021B3a5C6C5092ea4e5c3087"

Agora vamos integrar o contrato ao front.

Crie o projeto com a stack typescript que melhor lhe convir. Recomendo react. Copie toda a pasta typechain-types para o seu sources pois vamos usar ele.

Se voce estiver na pasta do smartcontracts-tuto acima, volte um nivel com cd ...

Para criar um esqueleto com react, vc pode usar:

npx create-react-app react-demo --template typescript

Agora copie o typechain-types da sua pasta smartcontracts-tuto para a nova pasta do react-demo recem criada dentro da subpasta src.

Na pasta do react-demo, instale @nomicfoundation/hardhat-toolbox via npm install --save @nomicfoundation/hardhat-toolbox.

Altere o App.tsx para chamar o greeter.

import React from 'react';
import logo from './logo.svg';
import './App.css';
import {Greeter, Greeter__factory} from './typechain-types';
import {ethers, Wallet} from 'ethers';

async function connect() {
  console.log('clicked');
  (window as any).ethereum.request({ method: 'eth_requestAccounts' });
}

async function callGreet(){
  const provider = new ethers.providers.Web3Provider((window as any).ethereum);
  const contract = Greeter__factory.connect('0xe720699f67778FBF021B3a5C6C5092ea4e5c3087', provider);
  let ret = await contract.greet();
  console.log(ret);
  alert(ret);
}

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.tsx</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Learn React
        </a>
        <button onClick={() => connect()}>Connect to Metamask</button>
        <button onClick={() => callGreet()}>Call Greet</button>
      </header>
    </div>
  );
}

export default App;

E pronto basta voce editar seu contrato e usar ele como achar melhor no seu frontend.

image image