Взаимодействие с другими контрактами из Solidity
В предыдущих руководствах мы многое узнали о том, как развернуть свой первый умный контракт и добавить в него некоторые функции, такие как контроль доступа с помощью модификаторовopens in a new tab или обработка ошибок в Solidityopens in a new tab. В этом руководстве мы узнаем, как развернуть умный контракт из существующего контракта и взаимодействовать с ним.
Мы создадим контракт, который позволит любому желающему иметь свой собственный умный контракт Counter, создав для него фабрику. Ее название будет CounterFactory. Во-первых, вот код нашего первоначального умного контракта 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, "Вы не являетесь владельцем контракта");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _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}Показать всеОбратите внимание, что мы немного изменили код контракта, чтобы отслеживать адрес фабрики и адрес владельца контракта. Когда вы вызываете код контракта из другого контракта, msg.sender будет ссылаться на адрес нашей фабрики контрактов. Это действительно важный момент для понимания, поскольку использование одного контракта для взаимодействия с другими является обычной практикой. Поэтому в сложных случаях следует обращать внимание на то, кто является отправителем.
Для этого мы также добавили модификатор onlyFactory, который гарантирует, что функция изменения состояния может быть вызвана только фабрикой, которая передаст исходного вызывающего в качестве параметра.
Внутри нашего нового CounterFactory, который будет управлять всеми остальными Counters, мы добавим сопоставление, которое будет связывать владельца с адресом его контракта-счетчика:
1mapping(address => Counter) _counters;В Ethereum сопоставления эквивалентны объектам в javascript, они позволяют сопоставить ключ типа А со значением типа Б. В данном случае мы сопоставляем адрес владельца с экземпляром его Counter.
Создание нового экземпляра Counter для кого-либо будет выглядеть так:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }Сначала мы проверяем, владеет ли человек уже счетчиком. Если у него нет счетчика, мы создаем новый экземпляр счетчика, передавая его адрес в конструктор Counter, и присваиваем вновь созданный экземпляр сопоставлению.
Получение значения определенного Counter будет выглядеть так:
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}Первая функция проверяет, существует ли контракт Counter для данного адреса, а затем вызывает метод getCount из экземпляра. Вторая функция, getMyCount, — это просто сокращение для прямой передачи msg.sender в функцию getCount.
Функция increment очень похожа, но она передает исходного отправителя транзакции в контракт Counter:
1function increment() public {2 require (_counters[msg.sender] != Counter(0));3 Counter(_counters[msg.sender]).increment(msg.sender);4 }Обратите внимание, что при слишком большом количестве вызовов наш счетчик может стать жертвой переполнения. Вам следует как можно чаще использовать библиотеку SafeMathopens in a new tab, чтобы защититься от этого возможного случая.
Чтобы развернуть наш контракт, вам нужно будет предоставить код как CounterFactory, так и Counter. При развертывании, например в Remix, вам нужно будет выбрать CounterFactory.
Вот полный код:
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, "Вы не являетесь владельцем контракта");12 _;13 }1415 modifier onlyFactory() {16 require(msg.sender == _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}Показать всеПосле компиляции в разделе развертывания Remix вы выберете фабрику для развертывания:
Затем вы можете поэкспериментировать с вашей фабрикой контрактов и проверить изменение значения. Если вы хотите вызвать умный контракт с другого адреса, вам нужно будет изменить адрес в поле выбора аккаунта в Remix.
Последнее обновление страницы: 15 августа 2023 г.
