Evento, Construtor e Herança

porMatheusem18/05/2022

Nesse artigo iremos abordar sobre como emitir eventos, declarar construtores e definir heranças no seu contrato inteligente. Falaremos sobre emit, constructor e herança.

Event (Evento)

Os eventos permitem realizar registros na blockchain Ethereum.

Alguns casos de uso para eventos são:

  • Ouvindo eventos e atualizando a interface do usuário
  • Uma forma mais barata para o armazenamento de dados
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract Event {
// Declaração do evento
// Até 3 parâmetros podem ser indexados
// Os parâmetros indexados ajudam a filtrar os logs pelo parâmetro indexado
event Log(address indexed sender, string message);
event AnotherLog();
function test() public {
emit Log(msg.sender, "Olá Mundo!");
emit Log(msg.sender, "Olá comunidade Solidity!");
emit AnotherLog();
}
}

Constructor (Construtor)

Um constructor é uma função opcional que é executada na criação do contrato.

Aqui estão alguns exemplos de como passar argumentos para os construtores

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
// Contrato X
contract X {
string public name;
constructor(string memory _name) {
name = _name;
}
}
// Contrato Y
contract Y {
string public text;
constructor(string memory _text) {
text = _text;
}
}
// Existem 2 maneiras de inicializar o contrato pai com parâmetros.
// Passe os parâmetros aqui na lista de herança.
contract B is X("Input to X"), Y("Input to Y") {
}
contract C is X, Y {
// Passe os parâmetros aqui no construtor,
// semelhante aos modificadores de função.
constructor(string memory _name, string memory _text) X(_name) Y(_text) {}
}
// Os construtores pai são sempre chamados na ordem de herança,
// independentemente da ordem dos contratos pai listados
// no construtor do contrato filho.
// Ordem de chamada dos construtores:
// 1. X
// 2. Y
// 3. D
contract D is X, Y {
constructor() X("X foi chamada") Y("Y foi chamada") {}
}
// Ordem de chamada dos construtores:
// 1. X
// 2. Y
// 3. E
contract E is X, Y {
constructor() Y("Y foi chamada") X("X foi chamada") {}
}

Inheritance (Herança)

No Solidity podemos definir múltiplas heranças. Os contratos podem herdar outro contrato usando a palavra-chave is.

A função que será substituída por um contrato filho deve ser declarada como virtual.

A função que irá substituir uma função pai deve usar a palavra-chave override.

A ordem de herança é importante.

Você deve listar os contratos-pai na ordem de “mais básicos” para “mais derivados”.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/* Gráfico de herança
A
/ \
B C
/ \ /
F D,E
*/
contract A {
function foo() public pure virtual returns (string memory) {
return "A";
}
}
// Os contratos herdam outros contratos usando a palavra-chave 'is'.
contract B is A {
// Sobrepor A.foo()
function foo() public pure virtual override returns (string memory) {
return "B";
}
}
contract C is A {
// Sobrepor A.foo()
function foo() public pure virtual override returns (string memory) {
return "C";
}
}
// Os contratos podem herdar de vários contratos pai.
// Quando é chamada uma função que é definida várias
// vezes em contratos diferentes, os contratos pai são
// pesquisados da direita para a esquerda e em profundidade.
contract D is B, C {
// D.foo() retorna "C"
// já que C é o contrato pai mais à direita com função foo()
function foo() public pure override(B, C) returns (string memory) {
return super.foo();
}
}
contract E is C, B {
// E.foo() retorna "B"
// já que B é o contrato pai mais à direita com função foo()
function foo() public pure override(C, B) returns (string memory) {
return super.foo();
}
}
// A herança deve ser ordenada de “mais básica” para “mais derivada”.
// Trocar a ordem de A e B gerará um erro de compilação.
contract F is A, B {
function foo() public pure override(A, B) returns (string memory) {
return super.foo();
}
}

Chamando um Contrato Pai Herdado

Os contratos pai podem ser chamados diretamente ou usando a palavra-chave super.

Vamos aprender como substituir corretamente as variáveis de estado herdadas.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
/* Árvore de Herança
A
/ \
B C
\ /
D
*/
contract A {
// Isso é chamado de evento. Você pode emitir eventos
// de sua função e eles serão registrados no log de transações.
// No nosso caso, isso será útil para rastrear chamadas de função.
event Log(string message);
function foo() public virtual {
emit Log("A.foo chamada");
}
function bar() public virtual {
emit Log("A.bar chamada");
}
}
contract B is A {
function foo() public virtual override {
emit Log("B.foo chamada");
A.foo();
}
function bar() public virtual override {
emit Log("B.bar chamada");
super.bar();
}
}
contract C is A {
function foo() public virtual override {
emit Log("C.foo chamada");
A.foo();
}
function bar() public virtual override {
emit Log("C.bar chamada");
super.bar();
}
}
contract D is B, C {
// Tente:
// - Chame D.foo e verifique os logs de transações.
// Embora D herde A, B e C, só chamou C e depois A.
// - Chame D.bar e verifique os logs de transações
// D chamou C, depois B e finalmente A.
// Embora 'super' tenha sido chamado duas vezes
// (por B e C), ele chamou A apenas uma vez.
function foo() public override(B, C) {
super.foo();
}
function bar() public override(B, C) {
super.bar();
}
}

Sombreando variáveis de estado herdadas

Ao contrário das funções, as variáveis de estado não podem ser substituídas pela declaração novamente no contrato filho.

Vamos aprender como substituir corretamente as variáveis de estado herdadas.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
contract A {
string public name = "Contract A";
function getName() public view returns (string memory) {
return name;
}
}
// O sombreamento não é permitido em Solidity 0.6
// Isso não vai compilar
// contract B is A {
// string public name = "Contract B";
// }
contract C is A {
// Essa é a maneira correta de substituir variáveis de estado herdadas.
constructor() {
name = "Contract C";
}
// C.getName retorna "Contract C"
}

Testar no Remix