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:
- Node.js (v14+)
- npm (v7+)
- HardHat (v2+)
- 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
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
Inicialize o HardHat:
npx hardhat
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"]
}
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;
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
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;
}
}
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;
}
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;
}
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:
- 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.
- 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.
- 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
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");
});
});
Passo 5: Compile o contrato e execute os testes
Compile o contrato com o HardHat:
npx hardhat compile
Execute os testes:
npx hardhat test
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
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);
});
Execute o script de implantação:
npx hardhat run scripts/deploy.ts
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;
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
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
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"));
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
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
É 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.
Top comments (0)