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

ERC-20 allowance: расходование от имени владельца

Иногда пользователь разрешает приложению расходовать часть своих токенов. Для этого ERC-20 предусматривает approve, allowance и transferFrom. Это другой договор, чем прямой transfer: владелец остаётся источником баланса, а spender получает конкретное право расходования.

Рассмотрим читателя R с десятью LCR и отдельный учебный приёмник C. R разрешает C три LCR. Никакая издательская роль от этого не появляется. Мы по-прежнему используем собственный фиксированный LibraryCredit без комиссий и callback-расширений.

Два адреса и два действия

approve(spender,value) записывает разрешение от текущего владельца. Это не перевод: balanceOf не уменьшается только от назначения лимита. Позднее spender вызывает transferFrom(from,to,value) и использует разрешение по паре owner/spender.

R → approve(C, 3_000_000)
allowance(R,C)=3_000_000
balanceOf(R) по-прежнему 10_000_000

C → transferFrom(R,C,2_000_000)
balanceOf(R)=8_000_000
balanceOf(C)=2_000_000
allowance(R,C)=1_000_000 для обычного конечного лимита

Разрешение двум разным spender хранится отдельно. Назначив C три кредита, R не разрешает произвольному D расходовать их. Однако сам C может иметь широкую логику, и её нужно изучить до назначения: токен проверяет адрес и количество, а не намерение кнопки сайта.

Учебный приёмник

// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract CreditInbox {
    error InvalidToken();
    error ZeroAmount();
    error TransferFailed();
    IERC20 public immutable credit;
    event Received(address indexed reader, uint256 amount);

    constructor(address credit_) {
        if (credit_ == address(0)) revert InvalidToken();
        credit = IERC20(credit_);
    }
    function deposit(uint256 amount) external {
        if (amount == 0) revert ZeroAmount();
        bool ok = credit.transferFrom(msg.sender, address(this), amount);
        if (!ok) revert TransferFailed();
        emit Received(msg.sender, amount);
    }
}

Это отдельный приёмник для нашего известного токена, без вывода и без пользовательской бухгалтерии. Он не является хранилищем реальных средств. Для выполнения deposit R заранее назначает allowance именно адресу inbox. Внутри вызова transferFrom токен видит spender равным inbox, а from передан как R.

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

Сумма баланса и сумма разрешения

Allowance может быть больше текущего баланса. Это не создаёт новые единицы. Когда spender пытается перевести количество, проверяются и разрешение, и доступный баланс по правилам реализации. Пополнение баланса позже может сделать прежнее сохранённое разрешение снова значимым.

Следовательно, лимит не стоит воспринимать как привязанный только к одной текущей покупке. Если осталось разрешение, C может попытаться использовать его позднее, пока договор его поддерживает. В обычном approve нет автоматического срока. Для проекта со сроком требуется отдельный механизм, не простое изменение названия кнопки.

В закреплённом OpenZeppelin ERC20 максимальное uint256 рассматривается как специальное практически неограниченное разрешение и не уменьшается обычным расходованием. Это отличается от конечного лимита таблицы. Пользователь должен ясно видеть, когда интерфейс предлагает такое назначение, и зачем оно нужно.

Замена и отзыв лимита

Повторный approve устанавливает новое value вместо текущего allowance. Он не добавляет сумму. Если раньше осталось один миллион, approve(C,500000) делает лимит полмиллиона по выбранной реализации. Для увеличения приложение должно осознанно подготовить новое итоговое значение.

При изменении ненулевого разрешения есть проблема порядка: spender способен использовать старое до новой записи. Процедура обнуления и последующего назначения помогает интерфейсу управлять переходом, но не возвращает уже потраченные единицы. Перед следующим шагом полезно прочитать актуальное разрешение и историю.

Действие Баланс владельца Разрешение
approve(C,3 млн) Само не меняется Установлен лимит
transferFrom на 2 млн Уменьшается Конечный лимит уменьшается
approve(C,0) Само не меняется Дальнейший обычный расход запрещён

Доверие к получателю

Inbox умеет только принять собственный известный токен. Если ему передать адрес нестандартного ERC-20, возможны другой ответ, комиссия или дополнительная логика. Один bool не доказывает точное количество полученных единиц. Следующий урок расширит разбор формы ответа через SafeERC20, сохранив границу экономического поведения.

Разрешение также не может ограничить C только одной человеческой целью, если сам C имеет другие методы transferFrom. Перед выдачей полезно читать его правила. Токен знает разрешённый spender, from и сумму, но не проверяет текст сайта «это только для курса 17».

Для упражнения назначьте allowance C, а затем вызовите deposit другого экземпляра D. Разрешение не проходит по паре R/D. Наличие одинакового исходника приёмника не объединяет их адреса. Эта ошибка конфигурации объясняется состоянием токена, а не необходимостью выдавать безграничный лимит всем.

Мы получили отдельный маршрут расходования, который требует двух осмысленных действий и точного выбора spender. ERC-20 и реализация allowance в 5.4.0 фиксируют этот договор.

Разрешение после завершения упражнения

Предположим, R разрешил три миллиона, а inbox использовал два. Оставшийся миллион не исчезает только потому, что страница показала «готово». Если следующее действие не требуется, владелец может намеренно обнулить allowance. Это отдельная транзакция с собственным результатом, а не локальное удаление записи формы.

Срок permit в будущем уроке ограничит подачу подписанного разрешения, но не обязательно срок уже установленного allowance. Поэтому нельзя ждать, что истечение подписи автоматически уберёт полученный лимит. Нужно читать конкретный договор токена и приложения, различая эти моменты.

У нашего inbox нет возврата. Он выбран для анализа пары owner/spender и принимает только лабораторные единицы. Если проекту требуется полноценный платёж с отменой, нужен другой контракт с учётом обязательств и отдельными маршрутами выдачи. Дописать одну кнопку сервера недостаточно: баланс внутри адреса приёмника не станет расходуемым им без предусмотренного кода.

Для проверки понимания отдайте C разрешение на пять миллионов, затем R уменьшит баланс обычным transfer другому читателю. Allowance остаётся, но C не сможет получить больше доступного остатка по правилам фиксированного токена. После будущего пополнения прежнее разрешение опять может быть использовано. Именно поэтому баланс сейчас не заменяет оценку сохранённого права.

Если пользователь видит запрос approve, интерфейс должен назвать spender и итоговый лимит. Надпись «подключить кошелёк» скрывает настоящий эффект и мешает осмысленному решению. Подключение аккаунта, назначение разрешения и исполнение deposit остаются разными действиями.

Для самостоятельного уточнения: eip-20, ERC20.sol (5.4.0).

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

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