Desenvolva, compile e implante um contrato inteligente na rede Elrond
Pré-requisitos para seu sistema (você pode seguir os links se ainda não instalou essas ferramentas):
Vamos começar criando um novo contrato na linha de comando em uma pasta de sua escolha:
erdpy contract new crowdfunding --template empty
Vamos passar por todas as partes desse primeiro comando.
Aqui está a explicação oficial do propósito da CLI erdpy:
Elrond Python Command Line Tools e SDK para interagir com a Elrond Network (em geral) e Contratos Inteligentes (em particular).
O grupo de comando contract é para compilar, implantar, atualizar e interagir com contratos inteligentes.
Com new, criamos um novo contrato inteligente com base em um modelo.
Depois disso, especificamos o nome do nosso novo contrato inteligente como *crowdfunding *(financiamento coletivo).
Este último é um argumento opcional template, com o qual especificamos o modelo a ser usado. Usamos empty (vazio) para criar um Contrato Inteligente com apenas a estrutura base necessária.
Por favor, abra o projeto recém-criado no VS Code. Você deve ver uma pasta em seu explorador, crowdfunding, que contém tudo o que precisamos para criar nosso contrato.
Faremos algumas tarefas de limpeza primeiro. Alteraremos o valor de path (caminho) no arquivo Cargo.toml, de “src/empty.rs” para “src/crowdfunding.rs”, para declarar que esse arquivo conterá o código para nosso Contrato Inteligente. Agora precisamos renomear o arquivo na pasta src de empty.rs para crowdfunding.rs e substituir o conteúdo do arquivo pelo seguinte:
#![no_std]
elrond_wasm::imports!();
#[elrond_wasm::contract]
pub trait Crowdfunding {
#[init]
fn init(&self) {}
}
Vamos examinar essas linhas.
Rust não foi escrito especialmente para o desenvolvimento de contratos inteligentes como o Solidity. Na primeira linha, dizemos que não queremos carregar as bibliotecas padrão para tornar nosso Contrato Inteligente mais leve.
A próxima linha importa o Framework da Elrond para simplificar seu Contrato Inteligente.
Com a próxima linha, dizemos que o seguinte é um Contrato Inteligente. Após essa linha, nosso Contrato Inteligente começa com pub trait Crowdfunding
.
O que significa a palavra-chave trait em Rust?
Traço (Trait) define a funcionalidade que um determinado tipo possui e pode compartilhar com outros tipos. Podemos usar traços para definir o comportamento compartilhado de forma abstrata. Podemos usar _limites de traço para especificar que um tipo genérico pode ser qualquer tipo que tenha determinado comportamento._
Nota: Os traços são semelhantes a um recurso frequentemente chamado interfaces em outros idiomas, embora com algumas diferenças.
As próximas duas linhas definem o método construtor, que será executado apenas uma vez quando você implantar o Contrato Inteligente na rede. Deve ser anotado com #[init]
O que são anotações do Contrato Inteligentes?
Anotações (também conhecidas como “atributos” do Rust) são o pão com manteiga do framework elrond-wasm
de desenvolvimento de contrato inteligente.
Você pode mergulhar mais fundo nas anotações que a Elrond tem a oferecer aqui.
Vamos construir nosso novo contrato.
cd crowdfunding
erdpy contract build
Isso pode demorar um pouco. Se o comando foi bem-sucedido, você verá uma nova subpasta na pasta crowdfunding > output.
Sumário
1 . Armazenamento
2 . Funções
3 . Implantação
Armazenamento
Definimos valores que devem ser armazenados na cadeia. Podemos conseguir isso por anotações. Todos os métodos definidos para manipulação de armazenamento não devem ter uma implementação.
A primeira anotação view define um ponto de extremidade (endpoint)_ público para acessar o valor de armazenamento. Com storage_mapper definimos um _setter e getter de armazenamento e SingleValueMapper é o mapeador de armazenamento mais simples que gerencia apenas uma chave de armazenamento.
Precisamos de três chaves de armazenamento on-chain primeiro: target - um valor-alvo desejado configurado, deadline - um prazo expresso como um carimbo de data/hora de bloco após o qual o contrato não pode ser financiado e deposit - o depósito, que são os fundos recebidos e há um argumento extra para o endereço do doador.
Os valores para o valor-alvo e depósito são uma soma de EGLD e serão armazenados como 1 EGLD = 10¹⁸ EGLD-wei, todos os pagamentos são expressos em wei.
#[view(getTarget)]
#[storage_mapper("target")]
fn target(&self) -> SingleValueMapper<BigUint>;
#[view(getDeadline)]
#[storage_mapper("deadline")]
fn deadline(&self) -> SingleValueMapper<u64>;
#[view(getDeposit)]
#[storage_mapper("deposit")]
fn deposit(&self, donor: &ManagedAddress) -> SingleValueMapper<BigUint>;
Funções
Em seguida, adicionaremos mais dois argumentos ao construtor de dois de nossos campos de armazenamento e duas verificações de que o valor-alvo precisa ser maior que zero e o prazo final deve ser no futuro.
#[init]
fn init(&self, target: BigUint, deadline: u64) {
require!(target > 0, "Alvo deve ser mais do que 0");
self.target().set(target);
require!(
deadline > self.get_current_time(),
"O prazo não pode estar no passado"
);
self.deadline().set(deadline);
}
fn get_current_time(&self) -> u64 {
self.blockchain().get_block_timestamp()
}
Em seguida, definimos um ponto de extremidade publicamente acessível para acompanhar quem doou quanto, é uma função pagável que receberá EGLD.
#[endpoint]
#[payable("EGLD")]
fn fund(&self) {
let payment = self.call_value().egld_value();
let caller = self.blockchain().get_caller();
self.deposit(&caller).update(|deposit| *deposit += payment);
}
Adicionamos mais duas linhas à nossa função de financiamento, as novas linhas estão marcadas em negrito. Para evitar o financiamento após o prazo, rejeitamos todos os pagamentos que chegam após o prazo definido.
#[endpoint]
#[payable("EGLD")]
fn fund(&self) {
let payment = self.call_value().egld_value();
let current_time = self.blockchain().get_block_timstamp();
require!(current_time < self.deadline().get(), "não é possível financiar após o prazo");
let caller = self.blockchain().get_caller();
self.deposit(&caller).update(|deposit| *deposit += payment);
}
Precisamos definir um ponto de extremidade para ler o status real do financiamento. Essas linhas devem ser adicionadas fora do contrato.
A palavra-chave #[derive]
em Rust permite que você implemente automaticamente certos traços para o seu tipo.
#[derive(TopEncode, TopDecode, TypeAbi, PartialEq, Clone, Copy)]
pub enum Status {
FundingPeriod,
Successful,
Failed,
}
Precisamos adicionar outra importação ao nosso arquivo abaixo da primeira importação:
elrond_wasm::derive_imports!();
Adicionamos mais dois métodos aos nossos traços de contrato para receber o status real e o financiamento atual com getCurrentFunds.
#[view]
fn status(&self) -> Status {
if self.blockchain().get_block_timestamp() <= self.deadline().get() {
Status::FundingPeriod
} else if self.get_current_funds() >= self.target().get() {
Status::sucessful
} else {
Status::Failed
}
}
#[view(getCurrentFunds)]
fn get_current_funds(&self) -> BigUint {
self.blockchain().get_sc_balance(&TokenIdentifier::egld(), 0)
}
Finalmente, precisamos de uma função para reivindicar o financiamento. Nesta função, evitamos a reivindicação antes que o prazo seja atingido. Se o financiamento foi bem sucedido, o proprietário recebe o saldo atual. Se o financiamento falhar, apenas os doadores podem reivindicar os valores investidos.
#[endpoint]
fn claim(&self) {
match self.status() {
Status::FundingPeriod => sc_panic!("não pode reivindicar antes do prazo"),
Status::sucessfull => {
let caller = self.blockchain(). get_caller();
require!(
caller == self.blockchain().get_owner_address(),
"somente o proprietário pode reivindicar financiamento com sucesso"
);
let sc_balance = self.get_current_funds();
self.send().direct_egld(&caller, &sc_balance);
},
Status::Failed => {
let caller = self.blockchain().get_caller();
let deposit = self.deposit(&caller).get();
if deposit > 0u32 {
self.deposit(&caller).clear();
self.send().direct_egld(&caller, &deposit);
}
},
}
}
Você pode ver o código final do contrato aqui.
Implantação
Primeiro, precisamos criar uma carteira de proprietário na Devnet aqui. Clique em “Create Wallet” e anote a frase de segurança (24 palavras) que pode nos ajudar a recuperar a carteira e a senha do JSON Keystore.
Guarde a frase de segurança (24 palavras) num local seguro.
Faça login na sua carteira Devnet e clique em Faucet para obter alguns fundos. Para enviar transações, precisamos de alguns xEGLD para as taxas de gás.
Geramos um arquivo PEM de chave privada para que não precisemos digitar nossa senha toda vez para confirmar as transações.
cd crowdfunding
erdpy --verbose wallet deriva ./wallet-owner.pem --mnemonic
Você precisa digitar a frase de segurança recém-recebida de 24 palavras separadas por espaço em branco.
Crie um arquivo erdpy.json na pasta crowdfunding e coloque o seguinte conteúdo nele:
{
"configurations": {
"default": {
"proxy": "https://devnet-api.elrond.com",
"chainID" : "D"
}
},
"contract": {
"deploy": {
"verbose": true,
"bytecode": "./output/crowdfunding.wasm",
"recall-nonce": true,
"pem": " ./wallet-owner.pem",
"gas-limit": 60000000,
"arguments": [100000000000000000000, 130000046930],
"send": true,
"outfile": "deploy-testnet.interaction.json"
}
}
}
Construa & Implante o contrato com os seguintes comandos:
erdpy contract build
erdpy contract deploy
Em algum lugar na saída da linha de comando, você verá uma linha como esta:
INFO:cli.contracts:Contract address: erd1qqqqqqqqqqqqqpgqfrxgju4fvxjrygagw52xamtmzmptex54rfyq40ns5r
Copie e cole o endereço do contrato na pesquisa no Devnet Explorer e veja os detalhes do seu contrato etc.
Em um próximo artigo, construiremos o frontend para este Contrato Inteligente. Você pode ver uma prévia abaixo.
Este artigo foi escrito por Patric e traduzido por Diogo Jorge. O artigo original pode ser encontrado aqui.
Oldest comments (0)