Solidity | Экземпляры контрактов
Всем примет. Сегодня говорим об экземплярах контрактов и их особенностях. Покажу как работать с ними и их подводные камни.
Для чего вообще нам нужны экземпляры контрактов?
Самое очевидное, это для взаимодействия с контрактом.
Например: у нас есть контракт А, который реализует методы, и мы хотим их использовать у себя в контракте.
Иногда наш код удобно разделить на части, но как это было у меня в прошлом проекте, наследование использовать не получиться, потому что код слишком большой и в следствии смарт-контракт не может быть развернут. В таком случае нам приходит на помощь экземпляры контрактов.
Также удобно создавать ролевую систему, тоже было показано в прошлом проекте, где вызов функций смарт-контракта A мог только смарт-контракт B
В общем это очень сильный инструмент для разработки, без которого нельзя. Важно понимать, что мы можем брать экземпляр только развернутого контракта. Поэтому мы передаем адрес контракта для его инициализации.
Пример использования:
Самый очевидный и популярный пример:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract A{ IERC20 token; constructor(address _token){ token = IERC20(_token); } }
В данном примере я объявляю переменную типа интерфейс IERC20
token = IERC20(_token);
- эта строка кода создает переменную token
и присваивает ей значение, являющееся экземпляром смарт-контракта, реализующего интерфейс IERC20
address _token
- это адрес любого токена ERC20
.
Таким образом мы получаем доступ ко всем методам токена стандарта ERC20
, адрес которого мы передали.
// SPDX-License-Identifier: MITpragma solidity ^0.8.9; contract FirstContract{ struct human{ string name; uint age; } address public owner; mapping(address=>human) public humans; function setName(string calldata _name) public{ humans[msg.sender].name = _name; } function setAge(uint _age) public{ humans[msg.sender].age = _age; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.9; import "./test.sol"; contract B{ FirstContract public firstContract; constructor(address _A){ firstContract = FirstContract(_A); } function check() public view returns(string memory, uint){ (string memory _name, uint _age) = firstContract.humans(msg.sender); return (_name, _age); } function checkOwner() public view returns(address){ return firstContract.owner(); } }
Если у нас есть структура, маппинг или переменная, то обращаться мы можем к ней только под видом функции.
firstContract.owner()
- для того, чтоб узнать содержимое переменной нам нужно обратиться к ней, как к функции.
firstContract.owner
- так это выглядело, если бы у нас firstContract
вызывался через наследование.
Для того, чтоб вытащить поля из структуры, которая находиться в маппинге, нужно создавать кортеж переменных и в данном случае мы обращаемся к маппингу как к функции, таким образом доставая нужные нам поля.
Если мы хотим достать только определенное поле структуры, то можно сделать так: (string memory _name, ) = firstContract.humans(msg.sender);
Тут я достаю только имя из нашей структуры.
Мы не можем изменять переменные у экземпляра контракта в своем контракте, если нет функций, разрешающих это сделать.
Как видно у меня в примере, в FirstContract
я создал 2 функции устанавливающие значения полям структуры. И теперь вызвав эти функции у себя в смарт-контракте, мы можем изменить эти поля.
function setName_(string memory _name) public{ firstContract.humans(msg.sender).name = _name; }
function setName_(string memory _name) public{ firstContract.setName(_name); }
Нужно понимать, что сам контракт B
не видит эти функции, как при наследовании, поэтому нельзя взять экземпляр и таким образом использовать все его функции. Нужно явно объявлять их у себя.
Еще нельзя забывать, что мы должны импортировать интерфейс или сам смарт-контракт, чтоб наш контракт понимал с чем взаимодействовать.
В общем это основные особенности, при использовании экземпляра контракта. Мы должны рассматривать каждую переменную как функцию, а все остальное такое же как и в наследовании.
Это была небольшая статься, потому что я для себя подметил ряд необычных механик, которые думаю можно было рассмотреть. Всем удачи.