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 ou manipulação de erros no Solidity. 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 tudoNote 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;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  }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}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  }Observe que, se for chamado várias vezes, nosso contador poderá ser vítima de um transbordamento ("overflow"). Você deve usar a biblioteca SafeMath 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 tudoDepois 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.
