Pular para o conteúdo principal

Interaja com outros contratos de Solidity

contratos Inteligentessolidityremiximplementaçãocomponibilidade
Avançado
jdourlens
EthereumDev(opens in a new tab)
5 de abril de 2020
4 minutos de leitura minute read
comp-tutorial-metadata-tip-author 0x19dE91Af973F404EDF5B4c093983a7c6E3EC8ccE

Nos tutoriais anteriores, aprendemos muito como publicar seu primeiro contrato inteligente e adicionar alguns recursos a ele, como controlar o acesso com modificadores(opens in a new tab) ou manipulação 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 inteligenteCounter, criando uma fábrica para ele. Seu nome será CounterFactory. De início, aqui está o código do nosso primeiro contrato inteligente Counter:

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, "You're not the owner of the contract");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "You need to use the factory");
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
Copiar

Note que modificamos ligeiramente o código do contrato para manter um controle do endereço da fábrica e do endereço do proprietário. Quando você chamar um código de contrato de outro contrato, o msg.sender irá consultar o endereço da nossa fábrica de contratos. Este é um ponto muito importante para entender como usar um contrato para interagir com outros contratos é uma prática comum. Você deve, portanto, cuidar de quem é o remetente em casos complexos.

Para isso também adicionamos um modificador de onlyFactory que certifica-se de que a função de mudança de estado só pode 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á o proprietário ao endereço de seu contrato:

1mapping(address => Counter) _counters;
Copiar

Na Ethereum, o mapeamento é equivalente a objetos em Javascript. Eles permitem mapear uma chave do tipo A para um valor do tipo B. Neste 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 }
Copiar

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

Para obter a contagem de um Counter específico, fica 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}
Copiar

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

A função increment é bastante 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 }
Copiar

Observe que, se for chamado várias vezes, nosso contador poderá ser vítima de um transbordamento ("overflow"). Você deve usar a biblioteca SafeMath(opens in a new tab) tanto quanto possível para se proteger deste possível caso.

Para implantar nosso contrato, você precisará fornecer tanto o código da CounterFactory quanto o Counter. Ao implantar, por exemplo, em Remix, você precisará selecionar a 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, "You're not the owner of the contract");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "You need to use the factory");
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
Copiar

Depois de compilar, na seção de implante de Remix, você selecionará a fábrica a ser implantada:

Selecionando a fábrica a ser implantada no Remix

Então você pode brincar com sua fábrica de contrato e verificar a mudança de valor. Se você prefere chamar o contrato inteligente a partir de um endereço diferente, altere o endereço na Conta selecionada do Remix.

Última edição: @nhsz(opens in a new tab), 15 de agosto de 2023

Este tutorial foi útil?