Сегодня поговорим про такой полезный инструмент как AccessControl от OpenZeppelin, данная библиотека позволит вам регулировать доступ к разного рода функционалу на ваших умных контрактах и не только. Вы сможете объявлять роли, присваивать эти роли другим пользователям и даже назначать роли для адресов, которые будут назначать другие роли. Это невероятный, гибкий и простой инструмент, с которым должен быть знаком каждый разработчик умных контрактов. А теперь к делу! Для лучшего понимания материала рекомендую открыть код библиотеки в соседней вкладке. Первое что мы видим в контракте AccessControl это структура RoleData - она содержит в себе всю необходимую информацию, которая относится к роли: mapping members - показывает какие адреса обладают ролью, возвращая true/false; adminRole - здесь хранится хэш роли администратора, который может назначать новые адреса на роль, то есть если у меня есть adminRole, то я могу изменять состояние mapping members, если не назначить администратора, то в слоте adminRole по умолчанию будет хранится нулевой хэш(0x0000000000000000000000000000000000000000000000000000000000000000), то есть администратором будет обладатель роли DEFAULT_ADMIN_ROLE. Почему именно DEFAULT_ADMIN_ROLE? Потому что этой роли по умолчанию присвоен нулевой хэш на строке 57. В свою очередь структуры RoleData хранятся в mapping _roles, в качестве входного аргумента mapping _roles принимает хэш роли, информацию по которой мы хотим получить. То есть: передаем в _roles хэш интересующей нас роли, а в ответ получаем объект, который содержит адреса всех обладателей этой роли и adminRole - хэш админской роли, её обладатель может назначать и удалять members. Давайте получим хэш для импровизированной роли, которая будет называться ADMIN_ROLE, для этого достаточно передать в хэш-функцию keccak256 строку "ADMIN_ROLE": Обязательно ли объявлять роли в контракте? Нет, но такая практика упростит вам жизнь, потому что, во-первых, мы будем обращаться к ним, чтобы уточнить хэш и не получать его каждый раз в ручную при помощи своих сил, а во-вторых, сам контракт будет обращаться к этим переменным(об этом ещё будет сказано). В первую очередь будут описаны функции с модификатором видимости public, к внутренним(internal) функциям и их назначению мы ещё вернемся. модификатор onlyRole(bytes32 role) - используется для того, чтобы указать обладатель какой роли может вызывать функцию. supportsInterface(bytes4 interfaceId) - эта функция не имеет прямого отношения к теме текущего материала, если хотите узнать о её назначении, рекомендую ознакомится со стандартом EIP-165. hasRole(bytes32 role, address account) - возвращает true/false в зависимости от того, есть ли у адреса(аргумент account) эта роль(аргумент role), можно использовать аналогично модификатору onlyRole, либо каким-то иным образом внутри функции, чтобы убедиться что адрес обладает ролью. Также можно вызывать hasRole снаружи напрямую, чтобы узнать есть ли у адреса какая-то роль. getRoleAdmin(bytes32 role) - возвращает хэш админской роли для роли, которая была передана как аргумент(role) если вызвать getRoleAdmin(0x0000000000000000000000000000000000000000000000000000000000000000), то есть, если передать хэш роли DEFAULT_ADMIN_ROLE, то в ответ получим 0x0000000000000000000000000000000000000000000000000000000000000000, потому что по умолчанию DEFAULT_ADMIN_ROLE является администратором для всех ролей, для которых не был назначен администратор, для удобства в следующих примерах вместо хэша будут указаны переменные в которых этот хэш хранится getRoleAdmin(ADMIN_ROLE) -> DEFAULT_ADMIN_ROLE, получим нулевой хэш, так как для этой роли мы не назначали администратора getRoleAdmin(MINTER_ROLE) -> DEFAULT_ADMIN_ROLE, снова получим нулевой хэш в ответ, так как ситуация аналогична примеру выше(для ADMIN_ROLE) getRoleAdmin(BURNER_ROLE) -> ADMIN_ROLE, здесь нам функция getRoleAdmin сообщает, что вот, мол, для BURNER_ROLE админ это обладатель роли ADMIN_ROLE, почему не DEFAULT_ADMIN_ROLE? потому что в конструкторе мы вызвали _setRoleAdmin(BURNER_ROLE, ADMIN_ROLE).  grantRole(bytes32 role, address account) - функция дающая роль(role) адресу(account), чтобы это сработало, вызывающий должен обладать ролью, которая является администратором по отношению к role, пример: например, мы хотим дать адресу address1 роль ADMIN_ROLE, для этого обладатель DEFAULT_ADMIN_ROLE должен вызвать grantRole(address1, ADMIN_ROLE), после успешного завершения транзакции address1 пополнит обладателей роли ADMIN_ROLE теперь, address1 хочет дать роль BURNER_ROLE адресу address2, он может это сделать, так как в пункте выше обладатель DEFAULT_ADMIN_ROLE дал роль ADMIN_ROLE адресу address1. Адрес address1 вызывает grantRole(address2, BURNER_ROLE), теперь адрес address2 стал обладателем роли BURNER_ROLE.  revokeRole(bytes32 role, address account) - функция обратная grantRole, если grantRole кому-то дает роль, то revokeRole, наоборот, забирает. renounceRole(bytes32 role, address account) - функция отказа от роли, отказаться от роли может только её обладатель, то есть в качестве аргумента account должен быть передан адрес вызывающего транзакцию, в противном случае функция откатится и вы получите сообщение "AccessControl: can only renounce roles for self", представим ситуацию где это может использоваться: Все функции описанные выше имели модификатор видимости public, это значит что их можно вызвать откуда угодно. Функции же описанные в этом разделе все без исключения имеют модификатор видимости internal, это значит, что их можно вызвать либо в конструкторе, либо в внутри функции которая объявлена в контракте, наследуемый от AccessControl. Давайте рассмотрим устройство каждой функции изнутри, заодно поймем, как изменяется storage AccessControl _checkRole(bytes32 role) - проверить обладает ли отправитель ролью role _checkRole(bytes32 role, address account) - проверит обладает ли account ролью role: _setupRole(bytes32 role, address account) - тот же смысл, что и grantRole, но эту функцию рекомендуются вызывать только в конструкторе: _setRoleAdmin(bytes32 role, bytes32 adminRole) - установить администратора(adminRole) для роли(role): _grantRole(bytes32 role, address account) - дать адресу account роль role: _revokeRole(bytes32 role, address account) - забрать у адреса account роль role: Остались вопросы? С чем-то не согласны? Пишите комментарии Поддержать автора криптовалютой: 0x021Db128ceab47C66419990ad95b3b180dF3f91F  Developer