Skip to content

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

@tolstenko

Description

@tolstenko

Introducao a SmartContracts com Solidity

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

Sumario

  • Criar projeto Solidity com hardhat
  • Testes automatizados de Solitidy com hardhat
  • Deploy em testnet mumbai
  • Integracao com frontent react

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions