Interaja com outros contratos de Solidity
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;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "You're not the owner of the contract");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "You need to use the factory");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}Exibir tudoCopiar
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}56function 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;23contract Counter {45 uint256 private _count;6 address private _owner;7 address private _factory;8910 modifier onlyOwner(address caller) {11 require(caller == _owner, "You're not the owner of the contract");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _factory, "You need to use the factory");17 _;18 }1920 constructor(address owner) public {21 _owner = owner;22 _factory = msg.sender;23 }2425 function getCount() public view returns (uint256) {26 return _count;27 }2829 function increment(address caller) public onlyFactory onlyOwner(caller) {30 _count++;31 }3233}3435contract CounterFactory {3637 mapping(address => Counter) _counters;3839 function createCounter() public {40 require (_counters[msg.sender] == Counter(0));41 _counters[msg.sender] = new Counter(msg.sender);42 }4344 function increment() public {45 require (_counters[msg.sender] != Counter(0));46 Counter(_counters[msg.sender]).increment(msg.sender);47 }4849 function getCount(address account) public view returns (uint256) {50 require (_counters[account] != Counter(0));51 return (_counters[account].getCount());52 }5354 function getMyCount() public view returns (uint256) {55 return (getCount(msg.sender));56 }5758}Exibir tudoCopiar
Depois de compilar, na seção de implante de Remix, você selecionará a fábrica a ser implantada:
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