Перейти к содержанию

ERC-721: передача токена и полномочия оператора

После выдачи первого LibraryPass текущим владельцем tokenId 1 является R. Администратор коллекции может продолжать выпуск других номеров, но перемещение этого абонемента определяется полномочиями ERC-721. Разберём обычного владельца, одобренный адрес для одного номера и оператора для всех токенов данного владельца.

Мы продолжаем полный LibraryPass из предыдущего урока без изменения исходника. Методы approve, setApprovalForAll, transferFrom и safeTransferFrom унаследованы из закреплённого ERC721. Здесь важно понимать предоставленное поведение, а не добавлять дублирующую функцию transferPass с собственными неполными проверками.

Передача владельцем

R может передать абонемент Q, указав фактического текущего владельца в from. Аргумент from не является способом выдать себя за другого человека: реализация проверяет непосредственный вызывающий адрес и полномочие на токен. Указание правильного R посторонним X ещё не даёт X права.

Следующие строки описывают вызовы к известному экземпляру. Это схема клиентских действий, а не самостоятельный Solidity-контракт и не выполненные операции.

ownerOf(1) → R
R: safeTransferFrom(R, Q, 1)
ownerOf(1) → Q
balanceOf(R) уменьшается на 1
balanceOf(Q) увеличивается на 1

Номер токена остаётся 1. Передача не выпускает второй абонемент, не расходует nextId и не меняет административный owner коллекции. Если приложение показывает новый номер после transfer, вероятно, оно смешивает переход владения с mint либо ошибочно использует номер события как tokenId.

Аргумент to не может быть нулевым для обычной передачи. Это не способ сжечь токен через transferFrom. В нашей коллекции внешний burn вообще отсутствует. Желание удалить абонемент требует отдельного явно разрешённого метода и решения, кто может им пользоваться.

Разрешение для одного номера

R может назначить S адресом, одобренным для tokenId 1, через approve(S,1). Такое действие не перемещает токен. До последующей передачи ownerOf(1) по-прежнему равен R. S получает возможность инициировать допустимый переход, включая передачу третьему адресату, поэтому это реальное полномочие распоряжения.

R: approve(S, 1)
getApproved(1) → S
S: safeTransferFrom(R, Q, 1)
ownerOf(1) → Q
getApproved(1) → нулевой адрес

Одобрение отдельного tokenId очищается при переходе владения в выбранной реализации. Новый владелец Q не наследует прежнее разрешение S на этот номер. Иначе адрес, которому доверял R, продолжал бы управлять собственностью Q без его решения.

Пока переход не произошёл, владелец может изменить одобрение или сбросить его нулевым адресом. Такое изменение относится к текущему состоянию. Если S уже передал токен раньше в порядке сети, поздний сброс не вернёт владение. Интерфейс должен читать receipt и актуальное состояние, а не трактовать отправленный запрос revoke как завершённую защиту.

Оператор всей коллекции владельца

setApprovalForAll(S,true) устанавливает более широкое право. В рамках этой коллекции S может управлять токенами R, включая будущие токены, которые R получит, пока настройка действует. Это не разрешение только для номера, показанного на текущем экране.

Такое право всё ещё не является глобальным одобрением всех NFT во всех контрактах. Оно хранится в конкретном экземпляре коллекции и относится к паре owner–operator. Совпадающие адреса R и S в другой сети либо другой коллекции не наследуют запись из нашего LibraryPass.

У текущего оператора есть полномочия, предусмотренные реализацией для управления токенами владельца, в том числе назначение одобрения отдельного номера. Поэтому приложение должно явно показывать широту разрешения перед подписью. Название «подключить сервис» скрывает существенный смысл операции и не годится для такого действия.

Когда токен перейдёт Q, операторское разрешение, выданное R, не даст S управления им от имени Q. Но запись R–S может продолжать действовать для других токенов R. Очистка одобрения одного tokenId и отключение isApprovedForAll — разные операции, которые нельзя заменить одним предположением об автоматическом полном отзыве.

Safe и обычная передача

transferFrom выполняет переход без обязательной проверки интерфейса контрактного получателя. safeTransferFrom дополнительно обращается к приёмному hook, когда целевой адрес имеет код. Если контракт Q не поддерживает приём должным образом, безопасная передача отклонится вместе с изменениями владения.

Слово safe здесь относится к предусмотренной проверке получателя. Оно не обещает, что Q умеет возвращать токен, что его администратор надёжен или что операция соответствует бизнес-смыслу библиотеки. Контракт может корректно принять ERC-721 и не иметь метода дальнейшего перемещения.

До внешнего callback реализация уже обновляет ownership и очищает одобрение отдельного номера. Получатель читает согласованное новое состояние токена. Тем не менее его код может выполнять другие разрешённые действия. Если приложение добавит платёж, начисление или учёт посещения вокруг передачи, этот собственный код нуждается в отдельном анализе взаимодействий.

Ошибка from и изменение состояния

Пусть R подготовил transferFrom(R,Q,1), но до исполнения номер 1 перешёл N через другой разрешённый маршрут. Старое значение from больше не соответствует владельцу. Транзакция не должна автоматически «найти нынешнего владельца» и переместить его токен. Она относится к конкретному намерению и состоянию.

В pinned ERC721 сначала выполняется внутреннее обновление с проверкой полномочий, затем проверяется соответствие previousOwner аргументу from. При несоответствии revert откатывает весь вызов. Порядок строк не означает, что неверный from оставляет частичный переход после ошибки.

Для самостоятельного разбора сравните три разрешения: S одобрен на номер 1; S оператор R; S только owner коллекции. Определите, что S может сделать с номерами 1 и 2, если оба принадлежат R. Затем передайте первый Q и повторите анализ. Так границы полномочий становятся видны по конкретным субъектам и состояниям, а не по общему слову «доступ».

Подтверждение результата в приложении

Сразу после отправки кошельком интерфейс может показать ожидающий переход, но не окончательное владение Q. Необходимо дождаться принятого приложением уровня подтверждения и связать receipt с сетью, адресом коллекции и номером. Для токенного доступа уровень подтверждения определяется отдельно от визуальной скорости анимации.

Если запрос RPC оборвался, это не доказательство отклонения transfer. Повторная подпись может создать новое намерение. Сначала приложение ищет известную транзакцию и читает состояние, затем предлагает дальнейшее действие. Так сетевой сбой не превращается в неверное объяснение полномочий стандарта.

Для самостоятельного уточнения: ERC721.sol (5.4.0), ERC721Utils.sol (5.4.0).

Оглавление курса. Листинги, расчёты и наблюдения здесь разобраны как ожидаемое поведение; компиляция, учебные программы и проверки при написании не запускались.

Оглавление серии