WEB3DEV

Cover image for Contrato inteligente de armazenamento simples na Blockchain Avalanche com Solidity, TypeScript, EVM, HardHat e EthersJS
Fatima Lima
Fatima Lima

Posted on

Contrato inteligente de armazenamento simples na Blockchain Avalanche com Solidity, TypeScript, EVM, HardHat e EthersJS

Introdução

A Avalanche é uma plataforma de blockchain relativamente nova que visa resolver alguns dos problemas de escalabilidade e eficiência enfrentados por outras redes de blockchain, como Bitcoin e Ethereum. Ela foi criada por uma equipe de cientistas da computação liderada por Emin Gün Sirer, uma figura bem conhecida na comunidade das blockchains.

A Avalanche usa um novo mecanismo de consenso chamado consenso Avalanche, que permite alto rendimento, baixa latência e alta finalização de transações. Diferentemente dos algoritmos tradicionais de consenso Proof-of-Work (Prova de Trabalho) ou Proof-of-Stake (Prova de Participação), a Avalanche se baseia em uma amostragem aleatória dos participantes da rede para chegar rapidamente a um consenso sobre o estado da rede.

Um dos principais recursos da Avalanche é sua capacidade de suportar várias sub-redes ou "blockchains dentro de uma blockchain". Isso significa que diferentes grupos de usuários podem criar suas próprias sub-redes com suas próprias regras e estruturas de governança e, ao mesmo tempo, interagir com a rede Avalanche mais ampla.

Outro recurso notável da Avalanche é seu suporte à criação de contratos inteligentes usando a linguagem de programação Solidity, que também é usada pela Ethereum. Isso permite que os desenvolvedores criem aplicativos descentralizados (dApps) na plataforma Avalanche com ferramentas e linguagens conhecidas.

De modo geral, a Avalanche é uma plataforma de blockchain promissora que oferece melhorias significativas em termos de escalabilidade, velocidade e flexibilidade em relação a outras redes de blockchain existentes. Seu mecanismo de consenso exclusivo e o suporte a várias sub-redes a tornam uma opção interessante para desenvolvedores e usuários que desejam criar aplicativos descentralizados ou participar de um ecossistema de blockchain rápido e eficiente.

Passos

Pré-requisitos

Antes de começar, verifique se você tem as seguintes ferramentas instaladas:

  1. Node.js (v14+)
  2. npm (v7+)
  3. HardHat (v2+)
  4. Solidity (v0.8+)

Passo 1: Configurando o projeto

Crie um novo diretório para o projeto e navegue até ele:


mkdir simple-storage

cd simple-storage

Enter fullscreen mode Exit fullscreen mode

Inicialize um novo projeto Node.js e instale as dependências necessárias:


npm init -y

npm install --save-dev hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers typescript ts-node dotenv @types/dotenv @types/mocha @types/chai avalanche

Enter fullscreen mode Exit fullscreen mode

Inicialize o HardHat:


npx hardhat

Enter fullscreen mode Exit fullscreen mode

Escolha "Create an empty hardhat.config.js" (Criar um hardhat.config.js vazio) quando solicitado.

Passo 2: Configure o TypeScript e o HardHat

Crie um novo arquivo tsconfig.json na raiz do projeto com o seguinte conteúdo:


{

  "compilerOptions": {

    "target": "es2017",

    "module": "commonjs",

    "strict": true,

    "esModuleInterop": true,

    "skipLibCheck": true,

    "forceConsistentCasingInFileNames": true,

    "resolveJsonModule": true,

    "outDir": "dist"

  },

  "include": ["./scripts", "./test"],

  "files": ["./hardhat.config.ts"]

}

Enter fullscreen mode Exit fullscreen mode

Renomeie o hardhat.config.js para hardhat.config.ts e atualize seu conteúdo:


import { HardhatUserConfig } from 'hardhat/config';

import '@nomiclabs/hardhat-waffle';

import '@nomiclabs/hardhat-ethers';

import 'dotenv/config';

const config: HardhatUserConfig = {

  solidity: '0.8.0',

  networks: {

    hardhat: {

      chainId: 31337,

    },

  },

  mocha: {

    timeout: 20000,

  },

};

export default config;

Enter fullscreen mode Exit fullscreen mode

Passo 3: Criando um Contrato Simple Storage (Armazenamento Simples)

Crie um novo diretório contracts e um novo arquivo Solidity SimpleStorage.sol dentro dele:


mkdir contracts

touch contracts/SimpleStorage.sol

Enter fullscreen mode Exit fullscreen mode

Adicione o seguinte código ao SimpleStorage.sol:


// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract SimpleStorage {

  uint256 private _storedData;

  function set(uint256 value) public {

    _storedData = value;

  }

  function get() public view returns (uint256) {

    return _storedData;

  }

}

Enter fullscreen mode Exit fullscreen mode
Visão Geral

O contrato Simple Storage foi projetado para demonstrar a funcionalidade básica de um contrato inteligente, permitindo que os usuários armazenem e recuperem dados na blockchain da Ethereum. O contrato consiste em uma única variável de estado que contém um número inteiro sem sinal e duas funções, para definir e obter o valor armazenado.

Variáveis de Estado

O contrato possui uma variável de estado:

  • storedData: Esse número inteiro sem sinal (uint256) contém os dados armazenados. É o principal armazenamento de dados para o contrato e seu valor pode ser alterado com a função set() e recuperado com a função get().
Funções

O contrato possui duas funções principais:

set()

Esta função permite que os usuários atualizem o valor da variável de estado storedData. Ela recebe um único parâmetro de input, um valor uint256, que representa os novos dados a serem armazenados.


function set(uint256 x) public {

    storedData = x;

}

Enter fullscreen mode Exit fullscreen mode

Quando chamada, essa função atualiza a variável de estado storedData com o valor de input fornecido.

get()

Essa função permite que os usuários recuperem o valor atual da variável de estado storedData. Ele não recebe nenhum parâmetro de entrada e retorna o valor uint256 armazenado no storedData.


function get() public view returns (uint256) {

    return storedData;

}

Enter fullscreen mode Exit fullscreen mode

Quando chamada, essa função retorna o valor atual da variável de estado storedData, sem alterá-la.

Fluxo de Interação

Os passos a seguir descrevem o fluxo de interação típico para usuários que interagem com o contrato do Simple Storage:

  1. Implantar o contrato: O usuário implanta o contrato Simple Storage na blockchain Ethereum. Após a implantação, a variável de estado storedData é inicializada com o valor 0.
  2. Configurar os dados de armazenamento: O usuário chama a função set(), fornecendo um valor uint256 como input. A função atualiza a variável de estado storedData com o valor fornecido.
  3. Obter os dados armazenados: o usuário chama a função get() para recuperar o valor atual da variável de estado storedData. A função retorna o valor uint256, sem alterá-lo.
Conclusão

O contrato inteligente Simple Storage demonstra a funcionalidade básica de um contrato inteligente na blockchain Ethereum. Ele permite que os usuários armazenem e recuperem um valor inteiro sem sinal por meio de duas funções simples, set() e get(). Esse contrato serve como ponto de partida para entender os fundamentos do desenvolvimento de contratos inteligentes e pode ser expandido para implementar recursos e casos de uso mais complexos.

Passo 4: Escrevendo testes para o contrato

Crie um novo diretório test e um novo arquivo TypeScript SimpleStorage.test.ts dentro dele:


mkdir test

touch test/SimpleStorage.test.ts

Enter fullscreen mode Exit fullscreen mode

Adicione o código a seguir ao SimpleStorage.test.ts:


// @ts-ignore

import { ethers, waffle } from "hardhat";

import { Contract } from "ethers";

import { expect } from "chai";

describe("SimpleStorage", () => {

    let simpleStorage: Contract;

    beforeEach(async () => {

        const SimpleStorage = await ethers.getContractFactory("SimpleStorage");

        simpleStorage = await SimpleStorage.deploy();

        await simpleStorage.deployed();

    });

    it("Initial stored data should be 0", async () => {

        const storedData = await simpleStorage.get();

        expect(storedData.toString()).to.equal("0");

    });

    it("Should set stored data to a new value", async () => {

        await simpleStorage.set(42);

        const storedData = await simpleStorage.get();

        expect(storedData.toString()).to.equal("42");

    });

});

Enter fullscreen mode Exit fullscreen mode

Passo 5: Compile o contrato e execute os testes

Compile o contrato com o HardHat:


npx hardhat compile

Enter fullscreen mode Exit fullscreen mode

Execute os testes:


npx hardhat test

Enter fullscreen mode Exit fullscreen mode

Se tudo estiver configurado corretamente, você verá que os testes foram bem-sucedidos.

Passo 6: Implante o contrato numa rede local

Crie um novo diretório scripts e um novo arquivo TypeScript deploy.ts dentro dele:


mkdir scripts

touch scripts/deploy.ts

Enter fullscreen mode Exit fullscreen mode

Adicione o seguinte código ao deploy.ts:


// @ts-ignore

import { ethers } from "hardhat";

async function main() {

    const SimpleStorageFactory = await ethers.getContractFactory("SimpleStorage");

    const simpleStorage = await SimpleStorageFactory.deploy();

    await simpleStorage.deployed();

    console.log("SimpleStorage deployed to:", simpleStorage.address);

}

main()

    .then(() => process.exit(0))

    .catch((error) => {

        console.error(error);

        process.exit(1);

    });

Enter fullscreen mode Exit fullscreen mode

Execute o script de implantação:


npx hardhat run scripts/deploy.ts

Enter fullscreen mode Exit fullscreen mode

Você deverá ver o contrato implantado em um endereço local.

Passo 7: Implante o contrato na Testnet Avalanche usando o Ethers.js

Para implantar o contrato na Testnet Avalanche, você precisa de uma conta com AVAX de teste. Você pode obtê-la na seção Avalanche Faucet.

Adicione a seguinte configuração ao seu hardhat.config.ts:


import { HardhatUserConfig } from 'hardhat/config';

import '@nomiclabs/hardhat-waffle';

import '@nomiclabs/hardhat-ethers'; // Add this line

import 'dotenv/config';

const config: HardhatUserConfig = {

  solidity: '0.8.0',

  networks: {

    hardhat: {

      chainId: 31337,

    },

    fuji: {

      url: "https://api.avax-test.network/ext/bc/C/rpc",

      chainId: 43113,

      gasPrice: 225000000000,

      accounts: [process.env.PRIVATE_KEY || ""],

    },

  },

  mocha: {

    timeout: 20000,

  },

};

export default config;

Enter fullscreen mode Exit fullscreen mode

Certifique-se de substituir a PRIVATE_KEY no array accounts por sua chave privada.

Execute os script de implantação na Testnet Avalanche:


npx hardhat run scripts/deploy.ts --network fuji

Enter fullscreen mode Exit fullscreen mode

Você deverá ver o contrato implantado em um endereço de Testnet Avalanche.

Agora você criou com sucesso um contrato de armazenamento simples no Solidity com TypeScript, EVM e AvalancheJS, além de testes e implementações locais do HardHat.

Passo 8: Interagindo com o contrato usando o AvalancheJS

Para interagir com o contrato implantado, crie um novo arquivo TypeScript interact.ts dentro do diretório scripts:


touch scripts/interact.ts

Enter fullscreen mode Exit fullscreen mode

Adicione o seguinte código ao interact.ts:


import {ethers} from 'ethers';

import * as dotenv from 'dotenv';

dotenv.config();

const CONTRACT_ADDRESS = process.env.CONTRACT_ADDRESS as string|| '';

if (!CONTRACT_ADDRESS) {

    throw new Error('Please set your contract address in the interact.ts script');

}

// Sua ABI Aqui

const ABI = [

    {

        "inputs": [],

        "name": "get",

        "outputs": [

            {

                "internalType": "uint256",

                "name": "",

                "type": "uint256"

            }

        ],

        "stateMutability": "view",

        "type": "function"

    },

    {

        "inputs": [

            {

                "internalType": "uint256",

                "name": "value",

                "type": "uint256"

            }

        ],

        "name": "set",

        "outputs": [],

        "stateMutability": "nonpayable",

        "type": "function"

    }

];

const FUJI_RPC_URL = "https://api.avax-test.network/ext/bc/C/rpc";

const PRIVATE_KEY = process.env.PRIVATE_KEY as string || '';

async function interact() {

    dotenv.config();

    if (!CONTRACT_ADDRESS) {

        throw new Error("Please set your contract address in the interact.ts script");

    }

    const provider = new ethers.providers.JsonRpcProvider(FUJI_RPC_URL);

    const wallet = new ethers.Wallet(PRIVATE_KEY, provider);

    console.log("Wallet address:", wallet.address);

    const simpleStorage = new ethers.Contract(CONTRACT_ADDRESS, ABI, wallet);

    console.log("Contract address:", CONTRACT_ADDRESS);

    console.log("Contract instance:", simpleStorage);

    // Ler dados armazenados

    const storedData = await simpleStorage.get();

    console.log("Stored data:", storedData.toString());

    // Configurar dados novos

    const tx = await simpleStorage.set(100);

    await tx.wait();

    // Ler os dados armazenados atualizados

    const updatedStoredData = await simpleStorage.get();

    console.log("Updated stored data:", updatedStoredData.toString());

}

interact().then(r => console.log("Complete"));

Enter fullscreen mode Exit fullscreen mode

Substitua YOUR_CONTRACT_ADDRESS pelo endereço do seu contrato implantado e adicione a ABI do seu contrato.

Execute o script de interação:


npx ts-node scripts/interact.ts

Enter fullscreen mode Exit fullscreen mode

Você deverá ver os dados armazenados, o recibo da transação e os dados armazenados atualizados na saída do console.


╰─ npx ts-node scripts/interact.ts

Wallet address: 0x2E3bE6ddaC75f8dA97d14009A540E917d881ea92

Contract address: 0x0697aBc8Dc960d53911f4A8BB8989826b78CaF61

Contract instance: Contract {

  interface: Interface {

    fragments: [ [FunctionFragment], [FunctionFragment] ],

    _abiCoder: AbiCoder { coerceFunc: null },

    functions: { 'get()': [FunctionFragment], 'set(uint256)': [FunctionFragment] },

    errors: {},

    events: {},

    structs: {},

    deploy: ConstructorFragment {

      name: null,

      type: 'constructor',

      inputs: [],

      payable: false,

      stateMutability: 'nonpayable',

      gas: null,

      _isFragment: true

    },

    _isInterface: true

  },

  provider: JsonRpcProvider {

    _isProvider: true,

    _events: [],

    _emitted: { block: -2 },

    disableCcipRead: false,

    formatter: Formatter { formats: [Object] },

    anyNetwork: false,

    _networkPromise: Promise { <pending> },

    _maxInternalBlockNumber: -1024,

    _lastBlockNumber: -2,

    _maxFilterBlockRange: 10,

    _pollingInterval: 4000,

    _fastQueryDate: 0,

    connection: { url: 'https://api.avax-test.network/ext/bc/C/rpc' },

    _nextId: 42

  },

  signer: Wallet {

    _isSigner: true,

    _signingKey: [Function (anonymous)],

    _mnemonic: [Function (anonymous)],

    address: '0x2E3bE6ddaC75f8dA97d14009A540E917d881ea92',

    provider: JsonRpcProvider {

      _isProvider: true,

      _events: [],

      _emitted: [Object],

      disableCcipRead: false,

      formatter: [Formatter],

      anyNetwork: false,

      _networkPromise: [Promise],

      _maxInternalBlockNumber: -1024,

      _lastBlockNumber: -2,

      _maxFilterBlockRange: 10,

      _pollingInterval: 4000,

      _fastQueryDate: 0,

      connection: [Object],

      _nextId: 42

    }

  },

  callStatic: {

    'get()': [Function (anonymous)],

    'set(uint256)': [Function (anonymous)],

    get: [Function (anonymous)],

    set: [Function (anonymous)]

  },

  estimateGas: {

    'get()': [Function (anonymous)],

    'set(uint256)': [Function (anonymous)],

    get: [Function (anonymous)],

    set: [Function (anonymous)]

  },

  functions: {

    'get()': [Function (anonymous)],

    'set(uint256)': [Function (anonymous)],

    get: [Function (anonymous)],

    set: [Function (anonymous)]

  },

  populateTransaction: {

    'get()': [Function (anonymous)],

    'set(uint256)': [Function (anonymous)],

    get: [Function (anonymous)],

    set: [Function (anonymous)]

  },

  filters: {},

  _runningEvents: {},

  _wrappedEmits: {},

  address: '0x0697aBc8Dc960d53911f4A8BB8989826b78CaF61',

  resolvedAddress: Promise { <pending> },

  'get()': [Function (anonymous)],

  'set(uint256)': [Function (anonymous)],

  get: [Function (anonymous)],

  set: [Function (anonymous)]

}

Stored data: 0

Updated stored data: 100

Complete

Enter fullscreen mode Exit fullscreen mode

É isso aí! Agora você desenvolveu com sucesso um contrato de armazenamento simples no Solidity com TypeScript, EVM, HardHat e EthersJS, incluindo testes locais, implantações na Testnet Avalanche e interações com o contrato implantado.

Código Fonte

https://github.com/nawodyaishan/avalanche-simple-storage

Esse artigo foi escrito por Nawodya Ishan e traduzido por Fátima Lima. O original pode ser lido aqui.

Oldest comments (0)