Перейти до основного вмісту

Взаємодія з іншими контрактами за допомогою Solidity

Смарт-контракти
мова програмування
remix
розгортання
композиційність
Для досвідчених користувачів
jdourlens
5 квітня 2020 р.
4 читається за хвилину

У попередніх посібниках ми багато чого дізналися, як розгорнути свій перший смарт-контракт, а також додати до нього деякі функції, як-от керування доступом за допомогою модифікаторів (opens in a new tab) або обробка помилок у Solidity (opens in a new tab). У цьому посібнику ми дізнаємося, як розгорнути смарт-контракт з існуючого контракту та взаємодіяти з ним.

Ми створимо контракт, який дозволить будь-кому мати власний смарт-контракт Counter, створивши для нього фабрику під назвою CounterFactory. Спочатку ось код нашого початкового смарт-контракту Counter:

Зауважте, що ми трохи змінили код контракту, щоб відстежувати адресу фабрики та адресу власника контракту. Коли ви викликаєте код контракту з іншого контракту, msg.sender посилатиметься на адресу нашої фабрики контрактів. Це дуже важливий момент для розуміння, оскільки використання одного контракту для взаємодії з іншими є поширеною практикою. Тому у складних випадках слід звертати увагу на те, хто є відправником (sender).

Для цього ми також додали модифікатор onlyFactory, який гарантує, що функція, яка змінює стан, може бути викликана лише фабрикою, яка передасть початкового викликача (caller) як параметр.

Усередині нашої нової фабрики CounterFactory, яка керуватиме всіма іншими лічильниками (Counter), ми додамо відображення (mapping), яке пов'язуватиме власника з адресою його контракту-лічильника:

mapping(address => Counter) _counters;

В Ethereum відображення (mapping) є еквівалентом об'єктів у JavaScript; вони дають змогу зіставити ключ типу A зі значенням типу B. У цьому випадку ми зіставляємо адресу власника з екземпляром його лічильника (Counter).

Створення екземпляра нового лічильника (Counter) для когось виглядатиме так:

  function createCounter() public {
      require (_counters[msg.sender] == Counter(0));
      _counters[msg.sender] = new Counter(msg.sender);
  }

Спочатку ми перевіряємо, чи вже має особа лічильник. Якщо в особи немає лічильника, ми створюємо екземпляр нового лічильника, передаючи її адресу в конструктор Counter, і присвоюємо новостворений екземпляр відображенню (mapping).

Щоб отримати значення конкретного лічильника (Counter), код виглядатиме так:

function getCount(address account) public view returns (uint256) {
    require (_counters[account] != Counter(0));
    return (_counters[account].getCount());
}

function getMyCount() public view returns (uint256) {
    return (getCount(msg.sender));
}

Перша функція перевіряє, чи існує контракт Counter для заданої адреси, а потім викликає метод getCount з екземпляра. Друга функція, getMyCount, — це просто скорочення для прямої передачі msg.sender у функцію getCount.

Функція increment дуже схожа, але передає початкового відправника транзакції до контракту Counter:

function increment() public {
      require (_counters[msg.sender] != Counter(0));
      Counter(_counters[msg.sender]).increment(msg.sender);
  }

Зауважте, що якщо викликати її забагато разів, наш лічильник може стати жертвою переповнення. Вам слід якомога частіше використовувати бібліотеку SafeMath (opens in a new tab), щоб захиститися від цього можливого випадку.

Щоб розгорнути наш контракт, вам потрібно буде надати код як CounterFactory, так і Counter. Наприклад, під час розгортання в Remix вам потрібно буде вибрати CounterFactory.

Ось повний код:

Після компіляції в розділі розгортання Remix виберіть фабрику для розгортання:

Вибір фабрики для розгортання в Remix

Потім ви можете поекспериментувати з вашою фабрикою контрактів і перевірити, як змінюється значення. Якщо ви хочете викликати смарт-контракт з іншої адреси, вам потрібно буде змінити адресу у випадаючому списку Account в Remix.

Останнє оновлення сторінки: 3 березня 2026 р.

Цей посібник був корисним?