Tutorial de solidez

Solidity é uma linguagem orientada a objetos de alto nível para o desenvolvimento de dApps (aplicações descentralizadas), no blockchain Ethereum.

Um blockchain é uma rede ponto a ponto de computadores, chamados de nós, que compartilham todos os dados e o código na rede.

Então, se você é um dispositivo conectado ao blockchain, você é um nó na rede e conversa com todos os outros nós de computador na rede (falaremos como configurar o nó Ethereum em sua máquina local em tutoriais posteriores).

Agora você tem uma cópia de todos os dados e do código no blockchain. Não há mais necessidade de servidores centrais.

O que é Ethereum?

Em sua forma mais simples, Ethereum é uma plataforma de software aberta baseada na tecnologia blockchain que permite aos desenvolvedores construir e implantar aplicativos descentralizados.

Enquanto o blockchain Bitcoin é usado para rastrear a propriedade de moeda digital (bitcoins), o blockchain Ethereum se concentra em executar o código de aplicativos descentralizados.

Na blockchain Ethereum, em vez de minerar bitcoin, os mineiros trabalham para ganhar Ether, um tipo de token criptográfico que abastece a rede. Além de uma criptomoeda negociável, Ether também é usado por desenvolvedores de aplicativos para pagar taxas de transação e serviços na rede Ethereum.

Há um segundo tipo de token que é usado para pagar as taxas dos mineiros por incluir transações em seu bloco, é chamado de gás, e cada execução de contrato inteligente requer que uma certa quantidade de gás seja enviada junto com ele para atrair os mineiros a colocá-lo o blockchain.

Começando com o básico

O código da Solidity está encapsulado em contratos.

O blockchain Ethereum nos permite executar código com a Máquina Virtual Ethereum (EVM) no blockchain com algo chamado de contrato inteligente.

Os contratos inteligentes são onde reside toda a lógica de negócios de nosso aplicativo – todas as variáveis ​​e funções pertencem a um contrato, e este será o ponto de partida de todos os seus projetos.

Os contatos inteligentes são escritos em uma linguagem de programação chamada Solidity, que parece uma mistura de Javascript e C.

Remix IDE

Remix é uma ferramenta online que permite que você escreva contratos inteligentes do Solidity, depois os implante e execute.

Apenas vá para https://remix.ethereum.org do seu navegador e podemos começar a codificar.

Como você pode ver, você pode escolher entre Solidity e Vyper. Ambas são linguagens para escrever contratos inteligentes, Vyper é semelhante a python e Solidity é semelhante a javascript.

Ambos podem compilar para o bytecode EVM, tipo Javascript e Typescript. Estamos escolhendo Solidity.

No lado esquerdo está o explorador de arquivos. Por padrão, existem dois arquivos .sol, apenas para demonstrar a sintaxe básica (ballot.sol é um contrato inteligente, ballot_test.sol é um script para testar esse contrato inteligente).

Você só precisa clicar no botão de adição e podemos começar a codificar nosso primeiro contrato inteligente.

Todo o código-fonte do Solidity deve começar com um “pragma de versão” – uma declaração da versão do compilador Solidity que este código deve usar. Isso evita problemas com futuras versões do compilador, potencialmente introduzindo alterações que quebrariam seu código.

Se parece com isso:

solidez do pragma ^ 0,4,25;

(para a versão Solidity acima de 0.4.25)

ou

solidez de pragma >= 0,5,0 < 0,6.0;

(para a versão Solidity entre 0.5.0 e 0.6.0)

Então você cria seu contrato digitando a palavra reservada contrato e o nome do seu arquivo .sol (é importante que o nome do contrato corresponda ao nome do arquivo, discutiremos o motivo mais tarde). No nosso caso,

contrato MyFirstContract {

}

Vamos compilá-lo. Você só precisa navegar até a guia de compilação à esquerda e clicar no botão grande de compilação. Se algo estiver errado com o código, você verá erros e avisos aqui (seja compassivo com o Solidity, ainda é uma “linguagem jovem”).

Com nosso contrato atual está tudo bem porque realmente não fizemos nada.

Agora vou gerar um erro de propósito apenas para mostrar uma coisa a vocês. Você pode selecionar manualmente o compilador nesse menu suspenso.

Vamos escolher, por exemplo, a versão 0.4.26. Agora, compile novamente. Agora você verá um erro “Compilador ainda não carregado”.

Isso ocorre porque especificamos com pragma para trabalhar com versões do compilador acima de 0.5.0. Basta alterar a versão do compilador novamente e o erro desaparecerá.

Ok, vamos codificar agora!

Começaremos com um código simples de ‘Hello world’ e obteremos e definiremos as funções, apenas para nos familiarizarmos com a sintaxe.

Um contrato no sentido de Solidity é uma coleção de código (suas funções) e dados (seu estado) que reside em um endereço específico na blockchain Ethereum.

Primeiro, vamos definir a variável de estado chamada mensagem, por exemplo, e seu tipo será string.

Nossa função get retornará o valor de nossa mensagem variável e a função set atribuirá um novo valor à nossa mensagem variável.

Como digitar funções?

Primeira palavra reservada função então o nome da função e parâmetros específicos e depois disso .

function myFunction () retorna (bool) {

return true;

}

As funções podem ser público ou privado. Se uma função for pública, ela pode ser chamada fora do contrato. Se uma função for privada, ela tem um escopo limitado e pode ser chamada apenas a partir de seu contrato atual (de alguma outra função, por exemplo).

Aqui está a lista de todos os especificadores de visibilidade de função:

  • público: visível externamente e internamente (cria uma função getter para variáveis ​​de armazenamento / estado)
  • privado: apenas visível no contrato atual
  • externo: visível apenas externamente (apenas para funções) – ou seja, só pode ser chamado de mensagem (por meio de this.func)
  • interno: apenas visível internamente

As funções podem ser puro, visualizar, ou a pagar. Se uma função não grava nenhum dado no blockchain, é muito recomendado ser view, porque as funções view não custam nenhum gás.

Aqui está a lista de todos os modificadores de função (também há modificadores para variáveis ​​de estado, eventos e argumentos de eventos, mas falarei sobre eles mais tarde):

  • puro: Proíbe modificação ou acesso de estado.
  • visualizar: Não permite modificação de estado.
  • a pagar: Permite que eles recebam Ether junto com uma chamada.

Se Function retornar algum valor você precisa especificar isso com a palavra reservada retorna e, em seguida, entre colchetes regulares para especificar qual tipo a função retorna. No nosso caso, será string (porque retornamos nossa mensagem variável que é string)

Se a função não retornar nenhum valor, não há necessidade de retorna demonstração.

Para acessar uma variável de estado, você não precisa do prefixo esta. como é comum em outras línguas.

Por causa disso, uma prática comum é escrever argumentos de função com sintaxe de sublinhado (_mensagem). Essa convenção veio do Javascript, onde métodos e variáveis ​​privados começam com _.

Para ser claro, seu código funcionará bem e sem sublinhados, mas é mais limpo com eles.

Você notará a palavra reservada memória em nosso código. Se você escrever nosso código sem memória e definir o pragma para alguma versão abaixo de 0,5. * Funcionará bem, mas quando você alterar seu compilador para acima de 0,5. * EVM gera erro de compilação.

Por que isso acontece?

Bem, a Máquina Virtual Ethereum tem três áreas onde pode armazenar itens.

  • O primeiro é armazenar, onde residem todas as variáveis ​​do estado do contrato. Cada contrato tem seu próprio armazenamento e é persistente entre as chamadas de função e muito caro para usar.
  • O segundo é memória, isso é usado para manter valores temporários. É apagado entre chamadas de função (externas) e é mais barato de usar.
  • O terceiro é o pilha, que é usado para conter pequenas variáveis ​​locais. É quase gratuito para usar, mas só pode conter uma quantidade limitada de valores.

Para quase todos os tipos, você não pode especificar onde eles devem ser armazenados, porque eles são copiados sempre que são usados.

Mas quando você trabalha com arrays ou structs, e a partir de versões mais recentes também com strings, o compilador o forçará a especificar a área de armazenamento.

Então, nosso código agora se parece com este:

solidez do pragma ^ 0,5,0;

contrato MyFirstContract {

mensagem de string;

function get () retornos de exibição pública (memória de string) {

mensagem de retorno;

}

conjunto de funções (string memory _message) public {

mensagem = _mensagem;

}

}

Observe que alguns desenvolvedores do Solidity quebram esses especificadores de visibilidade em linhas separadas para tornar o código mais limpo. Portanto, nossa função get pode ser escrita assim:

função get ()

público

visualizar

retorna (string)

{

mensagem de retorno;

}

É realmente com você como você escolhe escrever suas funções.

Vamos compilar nosso contrato agora e testá-lo.

Para compilá-lo basta repetir os passos abaixo (Compile .sol botão ou cmd / ctrl + S do teclado e ele irá recompilar automaticamente)

Para realmente ver como funciona (se a compilação não gerar erros), você precisa implantar seu contrato.

Para fazer isso, navegue até a guia Implementação da esquerda para o ambiente, selecione JavaScriptVM e clique no botão Implementar.

Após a implantação, agora podemos ver os métodos de nosso contrato. Vamos nos concentrar apenas nessa parte da tela agora.

Você pode ver que há dois botões (obter & set) para nossas duas funções públicas. Se algum desses fosse privado, não o veríamos aqui.

Se clicarmos no botão get, o EVM executará nossa função get.

Vamos ver como funcionou.

Temos uma string vazia. Não é ótimo, não é terrível. Mas por que? Bem, porque não inicializamos nossa variável de mensagem em primeiro lugar.

Apenas uma pausa rápida. Quero que você apresente o Remix Terminal. Está no editor de código e aqui você pode rastrear todas as suas transações, para ver se elas foram executadas com sucesso ou não, para depurá-las, ver detalhes (hash de transação etc.) e muito mais.

Por enquanto, temos duas transações bem-sucedidas. Um é o contrato de implantação e nos custa éter (mas não se preocupe, estamos no editor agora tudo é virtual) e o segundo é o Call of our visualizar função.

Ok, vamos voltar agora. O que acontecerá se chamarmos a função set agora?

Precisamos passar um argumento _message (“Hello World” por exemplo) e clicar no botão de transação para executar a função. Você pode acompanhar o sucesso da transação no Terminal.

Agora vamos chamar a função get novamente. Agora retorna nossa mensagem.

Vamos fazer algumas melhorias em nosso código. Não inicializamos nossa mensagem variável. Vamos fazer isso.

contrato MyFirstContract {

string mensagem = "Olá Mundo!";

function get () public view return (string memory) {

mensagem de retorno;

}

conjunto de funções (string memory _message) public {

mensagem = _mensagem;

}

}

Observe que a mensagem agora é “Olá, mundo!”, E quando chamarmos a função get pela primeira vez, ela não retornará uma string vazia.

Para testar isso, precisamos compilar nosso contrato (cmd / ctrl + S).

Então, para implantá-lo novamente. Precisamos criar uma nova instância de contrato (por causa das mudanças que fizemos) e publicá-la no blockchain.

Basta excluir a versão anterior do editor (não de nosso blockchain virtual, é claro) e clicar no botão Deploy novamente. Vamos chamar nossa função get agora.

Agradável! Vamos chamar a função set agora.

E pegue de novo.

Legal.

Vamos agora tornar nossa mensagem um constante.

Nosso código agora:

solidez do pragma ^ 0,5,0;

contrato MyFirstContract {

string constante mensagem = "Olá Mundo!";

function get () retornos de exibição pública (memória de string) {

mensagem de retorno;

}

conjunto de funções (string memory _message) public {

mensagem = _mensagem;

}

}

Quando tentamos compilá-lo, obtemos um erro em nossa função set. Isso porque não se pode alterar o valor de uma constante.

Vamos apenas nos livrar dessa constante agora.

Inicializar variáveis ​​como essa não é um erro, mas é muito melhor se fizermos isso no construtor. Você pode escrever construtor no Solidity com:

construtor () public {

// faça alguma coisa…

}

Construtor é apenas outra função que está sendo chamada durante a implantação do contrato inteligente. Nosso código parece um pouco diferente, mas funciona da mesma forma.

solidez do pragma ^ 0,5,0;

contrato MyFirstContract {

mensagem de string;

construtor () public {

mensagem = "Olá Mundo!";

}

function get () retornos de exibição pública (memória de string) {

mensagem de retorno;

}

conjunto de funções (string memory _message) public {

mensagem = _mensagem;

}

}

Você pode compilá-lo novamente e testá-lo se quiser.

Finalmente, pode-se alterar a visibilidade das variáveis ​​de estado. Se você tornar suas variáveis ​​de estado público isso significa que pode-se reivindicar seus valores fora do contrato.

Solidity fará para cada variável de estado público um método com o mesmo nome que pode ser chamado como uma função regular (como uma função getter).

Isso significa que podemos nos livrar de nossa função get, basta declarar a mensagem variável como público, e nosso código funcionará da mesma forma, será muito mais limpo e nos custará menos implantá-lo um dia na Rede Principal.

Quanto maior o código, mais gás é necessário para executá-lo e o custo de execução de nosso dApp aumenta.

Quando desenvolvemos contratos inteligentes, precisamos ser:

  • eficiente – a taxa de gás consumida deve ser baixa
  • preciso – uma vez que você implanta o contrato inteligente, ele não pode ser alterado e é público 24 horas por dia, 7 dias por semana, a cada linha de código (imagine um hacker que encontra um bug e pode explorar seu dApp)

Nosso código final para hoje se parece com este:

solidez do pragma ^ 0,5,0;

contrato MyFirstContract {

string public message;

construtor () public {

mensagem = "Olá Mundo!";

}

conjunto de funções (string memory _message) public {

mensagem = _mensagem;

}

}

Vamos implantá-lo e testá-lo.

Você pode ver aquele botão de mensagem. Ele está sendo criado porque nossa mensagem de variável de estado é pública.

Se chamarmos isso, ele deve nos retornar um valor que está sendo inicializado por meio do construtor (que é “Olá, mundo!”).

Legal. Vamos testar a função definida agora.

Como aprender Solidity?

Solidity em si é uma linguagem muito simples, mas para ser um bom desenvolvedor de Solidity é preciso entender como tudo funciona no Ethereum.

  • Solidity é uma linguagem de programação de alto nível com sintaxe semelhante a ECMAScript (javascript).
  • Ele compila para o bytecode EVM, algo que apenas o EVM pode entender.
  • O compilador é chamado Solc.

Vejamos este contrato simples como exemplo:

solidez do pragma ^ 0,5,0;

contrato Exemplo {

uint a = 10 + 5;

}

Simples assim. Agora vamos compilá-lo. Se formos para Detalhes do Contrato no Terminal, podemos ver muitas informações.

Nesse caso, o código compilado é:

0x6080604052600f600055348015601457600080fd5b5060358060226000396000f3fe6080604052600080fdfea165627a7a72305820bf75c57b7d8745a79baee513ead21a9eb8b07589d3fe6080604052600080fdfea165627a7a72305820bf75c57b7d8745a79baee513ead21a9eb8a

Esses valores longos são representações hexadecimais do contrato final, também conhecido como bytecode. EVM só entende bytecode.

Mas, se algo der errado, ficamos presos com algum erro, por exemplo, não se pode depurar bytecode.

Opcodes

O idioma acima do bytecode é opcode. Opcode é uma linguagem de programação de baixo nível. Solidity e Opcode são como C e Assembly Language, por exemplo.

Então, quando precisamos depurar alguma transação com falha, depuramos opcode.

Uma coisa que você deve saber sobre Solidity e depuração – é muito difícil. Mas não é impossível, então vamos mergulhar nisso.

Este é o opcode de nosso contrato de exemplo:

0 PUSH1 60

02 PUSH1 40

04 MSTORE

05 PUSH1 0f

07 PUSH1 00

09 SSTORE

10 CALLVALUE

11 DUP1

12 ISZERO

13 PUSH1 14

15 JUMPI

16 PUSH1 00

18 DUP1

19 REVERT

20 JUMPDEST

21 POP

22 PUSH1 35

24 DUP1

25 PUSH1 22

27 PUSH1 00

29 CODECOPY

30 PUSH1 00

32 RETORNO

33 INVÁLIDO

34 PUSH1 80

36 PUSH1 40

38 MSTORE

39 PUSH1 00

41 DUP1

42 REVERT

43 INVÁLIDO

44 LOG1

45 PUSH6 627a7a723058

52 SHA3

53 INVÁLIDO

54 PUSH22 c57b7d8745a79baee513ead21a9eb8b075896f8e4c59

77 INVÁLIDO

78 DUP10

79 E

80 JUMPI

81 INVÁLIDO

82 SALDO

83 PUSH29 750029

Opcodes são as instruções legíveis por humanos de baixo nível do programa. Todos os opcodes têm suas contrapartes hexadecimais, por exemplo MSTORE é 0x52.

O EVM é Stack Machine. É baseado na estrutura LIFO (Last In First Out). Para simplificar, imagine empilhar fatias de pão no micro-ondas, a ÚLTIMA fatia que você coloca é a PRIMEIRA que você tira.

Na aritmética normal, escrevemos nossa equação desta forma:

10 + 2 * 2

e a resposta é 14, porque fazemos multiplicação antes da adição.

Em uma máquina de pilha, funciona no princípio LIFO:

2 2 * 10 +

Isso significa que coloque 2 na pilha primeiro, seguido por outro 2, seguido pela ação de multiplicação. O resultado é 4 no topo da pilha. Agora adicione um número 10 em cima de 4 e, eventualmente, some os 2 números. O valor final da pilha torna-se 14.

O ato de colocar dados na pilha é chamado de instrução PUSH e o ato de remover dados da pilha é chamado de instrução POP. É óbvio que o opcode mais comum que vemos em nosso exemplo acima é PUSH1, o que significa colocar 1 byte de dados na pilha.

Então, esta instrução:

PUSH1 0x60

significa colocar um valor de 1 byte de “0x60” na pilha. Coincidentemente, o valor hexadecimal para PUSH1 também é “0x60”. Removendo o não obrigatório “0x”, poderíamos escrever esta lógica em bytecode como “6060”.

Vamos um pouco mais longe.

PUSH1 0x60 PUSH1 0x40 MSTORE

O MSTORE (0x52) recebe 2 entradas e não produz nenhuma saída. Os opcodes acima significam:

PUSH1 (0x60): coloque 0x60 na pilha.

PUSH1 (0x40): colocar 0x40 na pilha.

MSTORE (0x52): alocar 0x60 de espaço de memória e mover para a posição 0x40.

O bytecode resultante é:

6060604052

Na verdade, sempre vemos este número mágico “6060604052” no início de qualquer bytecode de solidez, porque é como o bootstrap de contrato inteligente.

Para complicar ainda mais a questão, 0x40 ou 0x60 não podem ser interpretados como o número real 40 ou 60. Como eles são hexadecimais, 40 na verdade é igual a 64 (16¹ x 4) e 60 é igual a 96 (16¹ x 6) em decimal.

Resumindo, o que “PUSH1 0x60 PUSH1 0x40 MSTORE” está fazendo é alocar 96 bytes de memória e mover o ponteiro para o início do 64º byte. Agora temos 64 bytes para espaço temporário e 32 bytes para armazenamento de memória temporário.

No EVM, existem 3 locais para armazenar dados. Em primeiro lugar, na pilha. Acabamos de usar o opcode PUSH para armazenar dados lá de acordo com o exemplo acima.

Em segundo lugar, na memória (RAM), onde usamos o opcode MSTORE e, por último, no armazenamento em disco, onde usamos SSTORE para armazenar os dados. O gás necessário para armazenar dados para armazenamento é o mais caro e armazenar dados para empilhar é o mais barato.

Agora é uma boa hora para voltar ao nosso código Solidity a partir deste tutorial e recapitular o que aprendemos sobre a palavra reservada memória e como o compilador nos força a especificar como armazenamos strings, por exemplo.

Nós cobrimos apenas o básico do bytecode e alguns opcodes.

Não precisamos saber opcodes para começar a escrever um contrato inteligente!

Por outro lado, o tratamento de erros do EVM ainda é muito primitivo e é útil olhar para opcodes quando as coisas dão errado.

Conclusão

Esta primeira lição tem um pouco mais de teoria do que codificação real, mas é muito importante para iniciantes saberem como as coisas funcionam no Ethereum. Nos próximos tutoriais, iremos escrever um código mais interessante e aprender como implantar nosso próprio token na blockchain Ethereum.

Até então &# 128075;

Mike Owergreen Administrator
Sorry! The Author has not filled his profile.
follow me