Solidity | Интерфейсы
Всем привет сегодня пойдет речь об интерфейсах и их применение.
Зачем они вообще нужны. В основном для того, чтоб мы понимали какая функция за что отвечает и какие аргументы она принимает. Если пока не понятно, то поймете на примере. Интерфейсы облегчают нам жизнь если вкратце.
Рассмотрим пример:
Напишем контракт, который будет отвечать за все наши функции. Я буду писать в ремиксе.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; contract Gamer{ mapping (address => uint) gamers; function joinGame(address player, uint amount) public { require(amount > 2000, "Not anougth to join game"); gamers[player] = 1; } function leaveGame(address player) public { require(gamers[player] == 1, "you are not in game"); gamers[player] = 0; } function getEntry(address player) public view returns(uint){ return gamers[player]; } }
Логика простая у нас есть мапппинг, который будет сохранять информацию об игроках, которые вошли в игру.
Чтоб войти в игру joinGame, нужно заплатить больше 2000 wei.
leaveGame отвечает за выход из игры.
И getEntry отвечает за вывод информации, о там в игре вы или нет.
В целом простой контракт без особенностей.
Теперь мы можем импортировать этот контракт и его исходный код в другой контракт и использовать его методы.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./Gamer.sol"; contract myGame{ Gamer game; constructor(address _game){ game = Gamer(_game); } }
Gamer game - это грубо говоря наш адрес первого смарт контракта. Gamer должен совпадать с название контракта, который вы импортировали. Это замена адреса. Позже увидим как это работает.
Строчка в конструкторе: game = Gamer(_game) это такая конструкция, которая делает нашу переменную game первым контрактом со всеми его методами и их можно вызывать.
Обращаясь к game.joinGame(), мы можем вызвать эту функцию и не переопределять ее как в наследовании. Довольно удобно.
Но теперь представим, что код первого контракта большой и в нем куча функций и в каждой огромное количество строк, или у нас нет доступа к информации о исходном коде контракта, потому что его создатель решил сделать приватным весь код. То что делать? На помощь приходят интерфейсы.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IGamer{ function joinGame(address player, uint amount)payable external; function leaveGame(address player) external; function getEntry(address player) external view returns(uint); }
Все названия интерфейсов принято писать через I(название).
Как видно мы тут просто определяем каждую функцию из первого контракта и говорим что она в себя принимает и что возвращает.
Важно! Все функции не могут быть public или еще какие-то, только external
Теперь осталось импортировать его в наш первый контракт, чтоб было понятно какая функция за что отвечает.
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IGamer.sol"; contract Gamer{ mapping (address => uint) gamers; function joinGame(address player, uint amount) public { require(amount > 2000, "Not anougth to join game"); gamers[player] = 1; } function leaveGame(address player) public { require(gamers[player] == 1, "you are not in game"); gamers[player] = 0; } function getEntry(address player) public view returns(uint){ return gamers[player]; } }
Вот и все теперь у нас есть интерфейс нашего контракта.
Напишем смарт-контракт, которые будет вызывать эти методы у себя через интерфейс!
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "./IGamer.sol"; contract myGame{ IGamer game; constructor(address _game){ game = IGamer(_game); } function joinGame()payable public { game.joinGame(msg.sender, msg.value); } function leave() public { game.leaveGame(msg.sender); } function get() public view returns(uint){ return game.getEntry(msg.sender); } }
Вот мы импортируем интерфейс уже в наш новый смарт контракт myGame.
IGamer game; constructor(address _game){ game = IGamer(_game); }
Тут мы подключаемся к интерфейсу также, как я показывал в начале c контрактом.
И дальше идут функции у нашего второго контракта, где мы вызываем через интерфейс функции первого контракта и соответственно с аргументами, которые они должны принимать. Ух надеюсь понятно объяснил.
Важно! Если не передавать аргументы в первый контракт, то работать не будет
То есть обязательно в первом контракте функция joinGame должна принимать адрес и количество денег на вход, она не должна быть payable в следствии этого, а если написать msg.sender и msg.value в первом контракте, то работать не будет. Только через аргументы!
Давайте посмотрим как это работает.
Открываем первый смарт контракт, компилируем его и нажимаем deploy.
Дальше под цифрой 1 мы копируем адрес контракта который мы только что за деплоили
Под цифрой 2 мы вставляем его и нажимаем deploy.
Должно быть 2 смарт-контракта. И теперь открываем второй контракт myGame и тестим функции.
Передаем больше чем 2000 wei и вызываем joinGame. Баланс смарт-контракта должен быть больше нуля.
Вызовем get и нам вернет 1 так как мы вошли в игру.
Если вызвать функцию leave и потом снова get, то она вернет 0, так как мы вышли из игры.
Все работает! Мы лишь подключились к нашему интерфейсу, в котором нет реализации ни одной функции, но у нас они работают корректно, как в первом контракте.
На этом все. Рассказал для чего нужны нам интерфейсы в solidity и рассмотрели наглядный пример. Дальше больше. Как видите мы все ближе подбираемся к теме erc20 и erc 721, а там есть что рассказать. Удачи.
tg: мой телеграмчик)