Взаємодія з іншими контрактами за допомогою Solidity
У попередніх посібниках ми багато чого дізналися, як розгорнути свій перший смарт-контракт, а також додати до нього деякі функції, як-от керування доступом за допомогою модифікаторів (opens in a new tab) або обробка помилок у Solidity (opens 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 посилатиметься на адресу нашої фабрики контрактів. Це дуже важливий момент для розуміння, оскільки використання одного контракту для взаємодії з іншими є поширеною практикою. Тому у складних випадках слід звертати увагу на те, хто є відправником (sender).
Для цього ми також додали модифікатор onlyFactory, який гарантує, що функція, яка змінює стан, може бути викликана лише фабрикою, яка передасть початкового викликача (caller) як параметр.
Усередині нашої нової фабрики CounterFactory, яка керуватиме всіма іншими лічильниками (Counter), ми додамо відображення (mapping), яке пов'язуватиме власника з адресою його контракту-лічильника:
1mapping(address => Counter) _counters;В Ethereum відображення (mapping) є еквівалентом об'єктів у JavaScript; вони дають змогу зіставити ключ типу A зі значенням типу B. У цьому випадку ми зіставляємо адресу власника з екземпляром його лічильника (Counter).
Створення екземпляра нового лічильника (Counter) для когось виглядатиме так:
1 function createCounter() public {2 require (_counters[msg.sender] == Counter(0));3 _counters[msg.sender] = new Counter(msg.sender);4 }Спочатку ми перевіряємо, чи вже має особа лічильник. Якщо в особи немає лічильника, ми створюємо екземпляр нового лічильника, передаючи її адресу в конструктор Counter, і присвоюємо новостворений екземпляр відображенню (mapping).
Щоб отримати значення конкретного лічильника (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 }Зауважте, що якщо викликати її забагато разів, наш лічильник може стати жертвою переповнення. Вам слід якомога частіше використовувати бібліотеку SafeMath (opens 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 виберіть фабрику для розгортання:
Потім ви можете поекспериментувати з вашою фабрикою контрактів і перевірити, як змінюється значення. Якщо ви хочете викликати смарт-контракт з іншої адреси, вам потрібно буде змінити адресу у випадаючому списку Account в Remix.
Останні оновлення сторінки: 15 серпня 2023 р.
