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

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

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

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

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

1pragma solidity 0.5.17;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "Ви не є власником контракту");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "Потрібно використовувати фабрику");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
Показати все

Зауважте, що ми трохи змінили код контракту, щоб відстежувати адресу фабрики та адресу власника контракту. Коли ви викликаєте код контракту з іншого контракту, 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}
5
6function 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;
2
3contract Counter {
4
5 uint256 private _count;
6 address private _owner;
7 address private _factory;
8
9
10 modifier onlyOwner(address caller) {
11 require(caller == _owner, "Ви не є власником контракту");
12 _;
13 }
14
15 modifier onlyFactory() {
16 require(msg.sender == _factory, "Потрібно використовувати фабрику");
17 _;
18 }
19
20 constructor(address owner) public {
21 _owner = owner;
22 _factory = msg.sender;
23 }
24
25 function getCount() public view returns (uint256) {
26 return _count;
27 }
28
29 function increment(address caller) public onlyFactory onlyOwner(caller) {
30 _count++;
31 }
32
33}
34
35contract CounterFactory {
36
37 mapping(address => Counter) _counters;
38
39 function createCounter() public {
40 require (_counters[msg.sender] == Counter(0));
41 _counters[msg.sender] = new Counter(msg.sender);
42 }
43
44 function increment() public {
45 require (_counters[msg.sender] != Counter(0));
46 Counter(_counters[msg.sender]).increment(msg.sender);
47 }
48
49 function getCount(address account) public view returns (uint256) {
50 require (_counters[account] != Counter(0));
51 return (_counters[account].getCount());
52 }
53
54 function getMyCount() public view returns (uint256) {
55 return (getCount(msg.sender));
56 }
57
58}
Показати все

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

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

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

Останні оновлення сторінки: 15 серпня 2023 р.

Чи була ця інструкція корисною?