@gasfree-kit/evm-4337
v1.0.1
Published
ERC-4337 gasless transactions for EVM chains — powered by WDK
Maintainers
Readme
@gasfree-kit/evm-4337
⚠️ Deprecation notice (v2.0.0) — the positional methods
EvmTransfer.sendToken,EvmTransfer.sendBatchToken,EvmTransfer.checkTokenBalance, andEvmTransfer.getTransactionEstimateFeeare deprecated in favour of the options-object methodsEvmTransfer.send,EvmTransfer.sendBatch,EvmTransfer.getBalance, andEvmTransfer.estimateFee.The legacy positional methods will be removed three weeks after the v2.0.0 publish date in the next major release. The new methods infer
tokenAddressfrom the chain, throw on failure, and return a flat result object (no{ success, message, data }wrapper).
ERC-4337 USDT transfers for EVM chains using Safe smart accounts.
This package gives you:
- standard seed-phrase driven transfers
- sponsored mode, where a paymaster covers gas
- token-paid mode, where gas is charged in USDT
- optional passkey linking and passkey-signed transfers
How It Works
┌──────────────────────┐
│ Your app │
└──────────┬───────────┘
│
v
┌──────────────────────┐
│ @gasfree-kit/evm-4337│
└──────────┬───────────┘
│
v
┌──────────────────────┐ ┌──────────────────────┐
│ Wallet manager │──────>│ Safe smart account │
└──────────────────────┘ └──────────┬───────────┘
│
v
┌──────────────────────┐
┌──────────────────────┐ │ UserOperation │
│ Paymaster │·····> │ (ERC-4337) │
│ │ └──────────┬───────────┘
│ sponsors gas or │ │
│ charges in USDT │ v
└──────────────────────┘ ┌──────────────────────┐
│ Bundler │
└──────────┬───────────┘
│
v
┌──────────────────────┐
│ EVM chain │
└──────────────────────┘Supported Chains
| Chain | Chain ID | Typical fallback fee estimate |
| -------- | -------- | ----------------------------- |
| Ethereum | 1 | 1.40 USDT |
| Base | 8453 | 0.15 USDT |
| Arbitrum | 42161 | 0.10 USDT |
| Optimism | 10 | 0.10 USDT |
| Polygon | 137 | 0.10 USDT |
| Celo | 42220 | 0.05 USDT |
| Plasma | 9745 | 0.05 USDT |
Prerequisites
This package depends on @gasfree-kit/core for:
- Seed phrase generation —
generateSeedPhrase()creates the mnemonic used to set up wallets - Chain metadata —
EVM_CHAINSprovides chain IDs, USDT addresses, and explorer URLs - Address validation —
validateEvmAddress()catches malformed addresses before sending - Error classes —
GasfreeError,InsufficientBalanceError, etc. for consistent error handling
@gasfree-kit/core is installed automatically as a dependency.
Installation
npm install @gasfree-kit/evm-4337 @gasfree-kit/coreAfter installing, your package manager will prompt you to install the required peer dependencies listed in package.json. Some peer dependencies are marked optional and only needed if you enable passkey linking.
Choose Your Gas Mode
| Mode | When to use it | Required config |
| ---------- | --------------------------------- | ------------------------------------------------ |
| Sponsored | Your paymaster fully covers gas | isSponsored: true and sponsorshipPolicyId |
| Token-paid | Gas is deducted from USDT balance | isSponsored: false and a paymaster/token setup |
Quick Start
1. Create a config
Sponsored mode:
import type { EVM4337ClientConfig } from '@gasfree-kit/evm-4337';
const sponsoredConfig: EVM4337ClientConfig = {
chain: 'base',
rpcUrl: 'https://mainnet.base.org',
bundlerUrl: 'https://your-bundler.example.com',
paymasterUrl: 'https://your-paymaster.example.com',
isSponsored: true,
sponsorshipPolicyId: 'your-policy-id',
};Token-paid mode:
const tokenPaidConfig: EVM4337ClientConfig = {
chain: 'base',
rpcUrl: 'https://mainnet.base.org',
bundlerUrl: 'https://your-bundler.example.com',
paymasterUrl: 'https://your-paymaster.example.com',
isSponsored: false,
paymasterAddress: '0xYourPaymasterAddress',
// Optional:
// paymasterTokenAddress: '0xYourUSDTLikeToken'
};2. Generate a seed phrase
import { generateSeedPhrase } from '@gasfree-kit/core';
const seedPhrase = await generateSeedPhrase();3. Set up the Safe smart account
import { setupErc4337Wallet } from '@gasfree-kit/evm-4337';
const { wallet, account, address } = await setupErc4337Wallet(seedPhrase, sponsoredConfig);
console.log(address); // Safe address4. Check balance
import { EvmTransfer } from '@gasfree-kit/evm-4337';
const balance = await EvmTransfer.getBalance({ seedPhrase, config: sponsoredConfig });
console.log(balance.balance); // "75.00" — human-readable USDT
console.log(balance.balanceRaw); // 75000000n — base units
console.log(balance.address); // Safe addressThe token address is inferred from the chain (EVM_CHAINS[chain].usdtAddress).
Pass tokenAddress to override.
5. Estimate fees
const estimate = await EvmTransfer.estimateFee({
seedPhrase,
config: sponsoredConfig,
to: '0x1111111111111111111111111111111111111111',
});
console.log(estimate.fee); // "0.15"6. Send a transfer
const result = await EvmTransfer.send({
seedPhrase,
config: sponsoredConfig,
to: '0x1111111111111111111111111111111111111111',
amount: '50.00',
});
console.log(result.transactionHash);
console.log(result.from); // sender Safe address
console.log(result.to);7. Send a batch transfer
const batch = await EvmTransfer.sendBatch({
seedPhrase,
config: sponsoredConfig,
recipients: [
{ address: '0x1111111111111111111111111111111111111111', amount: '25.00' },
{ address: '0x2222222222222222222222222222222222222222', amount: '10.00' },
],
});The legacy positional methods (
sendToken,sendBatchToken,checkTokenBalance,getTransactionEstimateFee) remain available — they still return the previous{ success, message, data }envelope and are marked@deprecated.
Passkey Flow
Passkeys are optional. They let you add a WebAuthn signer to an existing Safe so transfers can be approved with biometrics instead of a seed phrase.
Important:
- the Safe must already be deployed on-chain before you link a passkey
storage: 'persist'is convenience storage only, not hardened secret storage
Passkey diagram
┌──────────────────────┐
│ Seed phrase owner │──────┐
└──────────────────────┘ │
v
┌──────────────────────┐
│ Safe smart account │
└──────────┬───────────┘
┌──────────────────────┐ │
│ Passkey (WebAuthn) │──────┘
└──────────────────────┘
│
v
┌──────────────────────┐
│ Signed UserOperation │
└──────────┬───────────┘
│
v
┌──────────────────────┐
│ Bundler + Paymaster │
└──────────┬───────────┘
│
v
┌──────────────────────┐
│ EVM chain │
└──────────────────────┘Link a passkey to an existing Safe
import { linkPasskeyToSafe } from '@gasfree-kit/evm-4337';
const passkey = await linkPasskeyToSafe(seedPhrase, sponsoredConfig, {
rpName: 'My App',
userName: '[email protected]',
storage: 'not_persist',
});
console.log(passkey.safeAddress);
console.log(passkey.credential.signerAddress);Check whether a passkey is linked
import { isPasskeyLinked } from '@gasfree-kit/evm-4337';
const status = await isPasskeyLinked(seedPhrase, sponsoredConfig);
if (status.linked) {
console.log(status.signerAddress);
}Send a transfer with a passkey
import { PasskeyTransfer } from '@gasfree-kit/evm-4337';
import { EVM_CHAINS } from '@gasfree-kit/core';
const tx = await PasskeyTransfer.sendToken(
passkey.credential,
sponsoredConfig,
EVM_CHAINS.base.usdtAddress,
'10.00',
'0x1111111111111111111111111111111111111111',
);Remove a passkey owner
import { unlinkPasskeyFromSafe } from '@gasfree-kit/evm-4337';
await unlinkPasskeyFromSafe(seedPhrase, sponsoredConfig, passkey.credential);Main Exports
| Export | What it does |
| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------- |
| setupErc4337Wallet | Creates an ERC-4337 wallet from a seed phrase and resolves the Safe address |
| EvmTransfer | Estimates, checks balance, sends single transfers, and sends batch transfers |
| PasskeyTransfer | Sends passkey-signed transfers after a passkey has been linked |
| linkPasskeyToSafe | Registers a passkey and adds its signer to the Safe owner set |
| isPasskeyLinked | Checks whether a stored passkey is still an owner on-chain |
| unlinkPasskeyFromSafe | Removes a passkey signer from the Safe |
| getErc4337ConfigForChain | Normalizes the public client config into runtime config |
| GAS_FEE_FALLBACKS and GAS_FEE_ESTIMATES | Exposes fallback fee heuristics per chain |
| toUsdtBaseUnitsEvm, formatTokenBalance, feeToUsdt, encodeErc20Transfer | Utility helpers for amounts and calldata |
Export Example
import {
setupErc4337Wallet,
EvmTransfer,
PasskeyTransfer,
linkPasskeyToSafe,
isPasskeyLinked,
unlinkPasskeyFromSafe,
getErc4337ConfigForChain,
GAS_FEE_FALLBACKS,
GAS_FEE_ESTIMATES,
} from '@gasfree-kit/evm-4337';Notes
sponsorshipPolicyIdis required whenisSponsoredistrue- self-transfers are blocked
- token-paid mode checks that the USDT balance can cover the transfer and gas reserve
- passkey linking verifies the signer deployment before adding it as an owner
License
MIT
