Pular para o conteúdo principal

Interaja com outros contratos de Solidity

smart contracts
Solidity
Remix
implantação
componibilidade
Avançado
jdourlens
5 de abril de 2020
4 minutos de leitura

Nos tutoriais anteriores, aprendemos muito sobre como implantar seu primeiro contrato inteligente e adicionar alguns recursos a ele, como controle de acesso com modificadores (opens in a new tab) ou tratamento de erros no Solidity (opens in a new tab). Neste tutorial, aprenderemos como implantar um contrato inteligente a partir de um contrato existente e interagir com ele.

Faremos um contrato que permite a qualquer pessoa ter seu próprio contrato inteligente Counter, criando uma fábrica para ele. Seu nome será CounterFactory. Primeiro, aqui está o código do nosso contrato inteligente Counter inicial:

1pragma solidity 0.5.17;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "Você não é o proprietário do contrato");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "Você precisa usar a fábrica");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
Exibir tudo

Observe que modificamos ligeiramente o código do contrato para manter um registro do endereço da fábrica e do endereço do proprietário do contrato. Quando você chama o código de um contrato a partir de outro, o msg.sender se referirá ao endereço da nossa fábrica de contratos. Este é um ponto muito importante a ser entendido, pois usar um contrato para interagir com outros é uma prática comum. Portanto, você deve ter cuidado com quem é o remetente em casos complexos.

Para isso, também adicionamos um modificador onlyFactory que garante que a função que altera o estado só possa ser chamada pela fábrica, que passará o chamador original como um parâmetro.

Dentro de nossa nova CounterFactory, que gerenciará todos os outros Counters, adicionaremos um mapeamento que associará um proprietário ao endereço de seu contrato Counter:

1mapping(address => Counter) _counters;

No Ethereum, mapeamentos são o equivalente a objetos em JavaScript; eles permitem mapear uma chave do tipo A para um valor do tipo B. Nesse caso, mapeamos o endereço de um proprietário com a instância de seu Counter.

Instanciar um novo Counter para alguém ficará assim:

1 function createCounter() public {
2 require (_counters[msg.sender] == Counter(0));
3 _counters[msg.sender] = new Counter(msg.sender);
4 }

Primeiro, verificamos se a pessoa já possui um Counter. Se a pessoa não possuir um Counter, nós instanciamos um novo, passando o endereço dela para o construtor Counter, e atribuímos a instância recém-criada ao mapeamento.

Para obter a contagem de um Counter específico, o código será assim:

1function getCount(address account) public view returns (uint256) {
2 require (_counters[account] != Counter(0));
3 return (_counters[account].getCount());
4}
5
6function getMyCount() public view returns (uint256) {
7 return (getCount(msg.sender));
8}

A primeira função verifica se o contrato Counter existe para um determinado endereço e, em seguida, chama o método getCount da instância. A segunda função, getMyCount, é apenas um atalho para passar o msg.sender diretamente para a função getCount.

A função increment é bem parecida, mas passa o remetente da transação original para o contrato Counter:

1function increment() public {
2 require (_counters[msg.sender] != Counter(0));
3 Counter(_counters[msg.sender]).increment(msg.sender);
4 }

Observe que, se chamada muitas vezes, nosso contador poderia ser vítima de um estouro (overflow). Você deve usar a biblioteca SafeMath (opens in a new tab) o máximo possível para se proteger desse possível caso.

Para implantar nosso contrato, você precisará fornecer tanto o código da CounterFactory quanto o do Counter. Ao implantar, por exemplo, no Remix, você precisará selecionar CounterFactory.

Aqui está o código completo:

1pragma solidity 0.5.17;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "Você não é o proprietário do contrato");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "Você precisa usar a fábrica");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
34
35contract CounterFactory {
36
37 mapping(address => Counter) _counters;
38
39 function createCounter() public {
40 require (_counters[msg.sender] == Counter(0));
41 _counters[msg.sender] = new Counter(msg.sender);
42 }
43
44 function increment() public {
45 require (_counters[msg.sender] != Counter(0));
46 Counter(_counters[msg.sender]).increment(msg.sender);
47 }
48
49 function getCount(address account) public view returns (uint256) {
50 require (_counters[account] != Counter(0));
51 return (_counters[account].getCount());
52 }
53
54 function getMyCount() public view returns (uint256) {
55 return (getCount(msg.sender));
56 }
57
58}
Exibir tudo

Após a compilação, na seção de implantação do Remix, você selecionará a fábrica a ser implantada:

Selecionando a fábrica a ser implantada no Remix

Então você pode experimentar sua fábrica de contratos e verificar a alteração de valor. Se você quiser chamar o contrato inteligente de um endereço diferente, precisará alterar o endereço no seletor de Contas do Remix.

Última atualização da página: 15 de agosto de 2023

Este tutorial foi útil?