Улучшение: Paymaster postOp
Мы можем сделать небольшое улучшение, чтобы позволить paymaster сделать больше. В данный момент paymaster вызываются только на этапе проверки, до того, как операция будет выполнена.
Но в зависимости от результата операции, от paymaster может потребоваться сделать что-то другое.
Например, платежной системе, позволяющей пользователям оплачивать газ в стейблкоинах, необходимо знать, сколько газа было фактически использовано в ходе операции, чтобы знать, сколько USDC взимать.
Таким образом, мы добавим в paymaster новый метод postOp, который будет вызываться после завершения операции и передавать ей данные о количестве использованного газа.
Мы также хотим, чтобы paymaster мог "передавать информацию самому себе" и использовать данные, которые он вычислил во время валидации, на этапе postOp. Именно поэтому мы позволим после валидации возвращать произвольные "контекстные" данные, которые позже будут переданы в postOp.
Наша первая попытка создать postOp будет выглядеть следующим образом:
contract Paymaster {
function validatePaymasterOp(UserOperation op) returns (bytes context);
function postOp(bytes context, uint256 actualGasCost);
}Но для платежной системы, которая хочет в конце снять деньги в стейблкоинах, есть небольшие проблемы.
Допустим, перед тем как одобрить выполнение операции (в validatePaymasterOp), paymaster проверил, достаточно ли у пользователя, например, USDC для оплаты операции. Но вполне возможно, что во время выполнения операция отдаст все USDC кошелька, что будет означать, что платежная система не сможет извлечь оплату в конце.
P.S. Может ли paymaster взимать максимальную сумму USDC в начале, а затем возвращать неиспользованную часть в конце? Это вроде бы работает, но неудобно: требуется два вызова перевода вместо одного, что увеличивает стоимость газа и вызывает два разных события перевода.
Нам нужен способ, чтобы paymaster мог вызвать откат операции после ее выполнения, и если это произойдет, он должен иметь возможность получить платеж в любом случае, поскольку независимо от того, что произойдет, он уже согласился заплатить за газ во время validatePaymasterOp.
Способ реализации тут заключается в том, чтобы контракт потенциально вызывал postOp дважды.
Сначала он вызывает postOp в рамках того же выполнения, в котором он только что выполнил executeOp кошелька, и, таким образом, если postOp отменяется, это приводит к тому, что все эффекты executeOp также отменяются.
Если это происходит, то контракт снова вызывает postOp, но теперь мы оказываемся в ситуации, в которой были до выполнения executeOp, и поскольку в этой ситуации мы только что проверили validatePaymasterOp, paymaster должен быть в состоянии извлечь свою прибыль.
Чтобы придать postOp немного больше контекста, мы дадим ему еще один параметр: флаг, указывающий на то, что мы находимся во "втором запуске" после того, как он уже однажды отступил:
contract Paymaster {
function validatePaymasterOp(UserOperation op) returns (bytes context);
function postOp(bool hasAlreadyReverted, bytes context, uint256 actualGasCost);
}#accountabstraction