Нещодавно я заново вивчав solidity, консолідував деталі та писав “Solidity Minimalist Primer” для початківців (хлопці з програмування можуть знайти інший підручник), який оновлюється 1-3 рази на тиждень.
Весь код та навчальні посібники мають відкритий вихідний код на GitHub: github.com/AmazingAng/WTFSolidity
У лекції 17: Надсилання ETH, ми говорили про використання call для надсилання ETH, і в цій лекції ми покажемо вам, як використовувати його для виклику контрактів.
call – це низькорівнева функція-член типу address, яка використовується для взаємодії з іншими контрактами. Він повертає (булеве значення, дані), що відповідає успішності виклику та значенню, що повертається цільової функції відповідно.
call є офіційно рекомендованим способом надсилання ETH шляхом запуску резервних функцій або функцій отримання за допомогою solidity. Не рекомендується дзвонити в інший договір дзвінком, тому що коли ви викликаєте функцію небезпечного контракту, ви віддаєте йому ініціативу. Рекомендованим методом, як і раніше, є виклик функції після оголошення змінної контракту, дивись Лекція 19: Виклик інших контрактів. Якщо ми не знаємо вихідного коду або ABI контракту іншої сторони, ми не можемо генерувати змінні контракту, але ми все одно можемо викликати функції контракту іншої сторони через виклик.
Правила використання call наступні:

Двійкове кодування використовує структуровану функцію кодування abi.encodeWithSignature для отримання:

Сигнатурою функції є «Назва функції (тип параметра, розділений комами)». Наприклад, abi.encodeWithSignature(“f(uint256,address)”, _x, _addr).
Крім того, при виклику контракту можна вказати кількість ETH і газу, які будуть відправлені транзакцією:

Це здається трохи складним, тому давайте розглянемо приклад програми для дзвінків.
Почнемо з написання простого цільового контракту, OtherContract, і розгортання його, код в основному такий же, як і в Лекції 19, за винятком додавання резервної функції.

Цей контракт містить змінну стану x, журнал подій, який спрацьовує при отриманні ETH, і три функції:
getBalance(): повертає ETH баланс контракту. setX(): зовнішня функція до оплати, яка може встановити значення x і відправити ETH на контракт. getX(): зчитує значення x.
Напишемо контракт Call для виклику функції цільового контракту. Перш за все, напишіть визначення події Response і виведіть успіх і дані, повернуті викликом, щоб ми могли спостерігати значення, що повертається.

Ми визначаємо функцію callSetX для виклику setX() цільового контракту, передаємо суму ETH в msg.value і відпускаємо подію Response, щоб вивести успіх і дані:

Далі ми викликаємо callSetX для зміни змінної стану _x на 5, а параметрами є адреса OtherContract та 5, оскільки цільова функція setX() не повертає значення, тому вихід даних події Response дорівнює 0x, який є порожнім.
Викличемо функцію getX(), яка поверне значення цільового контракту _x, типу uint256. Ми можемо використовувати abi.decode для розшифровки значення, що повертається викликом, даних і зчитування значення.

З виводу події Response ми можемо побачити, що дані 0x0000000000000000000000000000000000000000000000000000000000000005. Після abi.decode остаточне значення, що повертається, дорівнює 5.
Якщо функція, яку ми вводимо в виклик, не існує в цільовому контракті, то спрацьовує резервна функція цільового контракту.

У наведеному вище прикладі ми викликали неіснуючу функцію foo. Виклик все ще може бути успішно виконаний і повернути успішний, але насправді він є резервною функцією цільового контракту, що викликається.
У цій доповіді ми показали, як використовувати низькорівневий виклик функції для виклику інших контрактів. Call не є рекомендованим методом виклику контракту, оскільки він не є безпечним. Але корисно дозволити нам викликати цільовий контракт, не знаючи вихідного коду та ABI.