китаев.tech

Paymaster: как оплатить газ за пользователя

Paymaster позволяет спонсировать газ за пользователя в Account Abstraction. Показываем на коде, как подключить gasless-транзакции.

Gasless без ETH на кошельке пользователя

Paymaster — контракт в ERC-4337, который берёт на себя оплату газа за UserOperation. Пользователь по-прежнему подписывает намерение; ETH на smart-аккаунте для комиссии не обязателен.

Это главный продуктовый кейс Account Abstraction: убрать «сначала купи ETH на газ» из онбординга. Без smart-аккаунта и EntryPoint Paymaster не работает — обычный EOA gasless не умеет.


Verifying Paymaster: как устроена спонсорская схема

Два частых типа:

ТипИдея
Verifying PaymasterOff-chain сервис решает «спонсировать или нет», подписывает разрешение; on-chain контракт проверяет подпись
ERC-20 PaymasterПользователь платит газ стейблкоином / токеном приложения

Pimlico Verifying Paymaster — готовый сервис: клиент запрашивает спонсорство (pm_sponsorUserOperation), получает поля paymaster / paymasterData (и gas-лимиты paymaster), вшивает их в UserOp и шлёт bundler'у.

text
Клиент собирает UserOp (без оплаты газа пользователем)

Paymaster RPC: «готовы спонсировать?» → paymaster + paymasterData

Подпись smart-аккаунта

Bundler → EntryPoint → газ списывается с Paymaster / его депозита

Подключаем verifying paymaster через Pimlico

Нужны пакеты permissionless и viem, API-ключ Pimlico. Пример — Sepolia: Safe smart-аккаунт + gasless-вызов. Ключ и (опционально) PRIVATE_KEY — из окружения.

typescript
import { writeFileSync } from "node:fs";
import { createSmartAccountClient } from "permissionless";
import { toSafeSmartAccount } from "permissionless/accounts";
import { createPimlicoClient } from "permissionless/clients/pimlico";
import { entryPoint07Address } from "viem/account-abstraction";
import {
  type Hex,
  createPublicClient,
  http,
} from "viem";
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
import { sepolia } from "viem/chains";

const apiKey = process.env.PIMLICO_API_KEY;
if (!apiKey) {
  throw new Error("Задайте PIMLICO_API_KEY из dashboard.pimlico.io");
}

// Владелец аккаунта: свой ключ или одноразовый для теста
const privateKey =
  (process.env.PRIVATE_KEY as Hex | undefined) ??
  (() => {
    const pk = generatePrivateKey();
    writeFileSync(".env", `PRIVATE_KEY=${pk}\n`, { flag: "a" });
    return pk;
  })();

const publicClient = createPublicClient({
  chain: sepolia,
  transport: http("https://ethereum-sepolia-rpc.publicnode.com"),
});

const pimlicoUrl = `https://api.pimlico.io/v2/${sepolia.id}/rpc?apikey=${apiKey}`;

// Bundler + verifying paymaster на одном endpoint у Pimlico
const pimlicoClient = createPimlicoClient({
  transport: http(pimlicoUrl),
  entryPoint: {
    address: entryPoint07Address,
    version: "0.7",
  },
});

const account = await toSafeSmartAccount({
  client: publicClient,
  owners: [privateKeyToAccount(privateKey)],
  entryPoint: {
    address: entryPoint07Address,
    version: "0.7",
  },
  version: "1.4.1",
});

console.log("Smart account:", account.address);
// До первого UserOp контракта на адресе может не быть — деплой counterfactual

const smartAccountClient = createSmartAccountClient({
  account,
  chain: sepolia,
  bundlerTransport: http(pimlicoUrl),
  // Главное: paymaster заполняет paymaster* поля UserOp
  paymaster: pimlicoClient,
  userOperation: {
    estimateFeesPerGas: async () => {
      return (await pimlicoClient.getUserOperationGasPrice()).fast;
    },
  },
});

// Пользователь не платит Sepolia ETH за газ — спонсор Pimlico (в рамках квоты ключа)
const txHash = await smartAccountClient.sendTransaction({
  to: "0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045",
  value: 0n,
  data: "0x1234",
});

console.log("Bundled tx:", `https://sepolia.etherscan.io/tx/${txHash}`);

Под капотом клиент: собирает UserOp → просит verifying paymaster спонсорство → подписывает → eth_sendUserOperation. В receipt бандла газ оплачен не балансом пользователя на аккаунте (для комиссии), а политикой Paymaster.


Как это работает

paymaster: pimlicoClient — middleware спонсорства. Без него UserOp должен оплачиваться депозитом smart-аккаунта в EntryPoint (нужен ETH на аккаунте / депозите).

pm_sponsorUserOperation (внутри SDK) — off-chain решение paymaster'а. В UserOp v0.7 появляются paymaster, paymasterData, paymasterVerificationGasLimit, paymasterPostOpGasLimit.

toSafeSmartAccount — counterfactual адрес: байткод появится при первом успешном UserOp (factory + factoryData). Paymaster обычно спонсирует и деплой, и вызов — иначе «пустой» аккаунт не стартует.

getUserOperationGasPrice().fast — комиссии bundler'а Pimlico; для их сети fees лучше брать с bundler RPC, а не с публичного eth_gasPrice.


Политика спонсорства (то, что решает продукт)

Verifying paymaster — не «бесплатный газ навсегда», а ваш бюджет + правила:

  • кого спонсировать (только ваши sender, только определённый callData);
  • лимиты на UserOp / день / пользователя;
  • testnet vs mainnet (на mainnet без пополненного баланса paymaster'а спонсорства не будет).

На Pimlico это настраивается в dashboard и через sponsorship policies. В коде выше — дефолтная спонсорская квота API-ключа на Sepolia.

Альтернатива verifying — ERC-20 paymaster: пользователь платит USDC, UX всё ещё без «ETH на газ», но не полностью free.


Частые ошибки

Ждать gasless от обычного MetaMask EOA → Paymaster работает только в потоке UserOperation / EntryPoint. Без smart-аккаунта спонсировать нечего.

Забыть paymaster в createSmartAccountClient → bundler примет UserOp только если у аккаунта есть депозит ETH в EntryPoint. Ошибка будет про insufficient funds / prepaid gas, а не про «paymaster не подключён».

Собрать UserOp вручную и не вызвать sponsor → пустые paymaster* = пользователь платит сам. SDK с paymaster: pimlicoClient делает sponsor до подписи автоматически.

Крутить mainnet с testnet-ключом / нулевым балансом paymaster → Sepolia-квота не переносится. На mainnet сначала политика и депозит спонсора.


Что дальше

Материалы китаev.tech публикуются в образовательных целях и не являются инвестиционной рекомендацией. Примеры кода и описания протоколов — для обучения; использование в продакшне на ваш собственный риск. Дисклеймер и политика конфиденциальности.