What is EIP‑2612? on Solana?
EIP‑2612 is the ERC‑20 Permit Extension that lets a token holder grant spending allowance to a smart contract or another account with an off‑chain signature instead of an on‑chain approve transaction. By embedding the signed data in a single permit() call, users save gas, and integrators can bundle approval + action into one atomic transaction.
Key Characteristics
- Gasless, single‑call approvals: A holder signs an off‑chain EIP‑712 message and one permit() call instantly sets the allowance while verifying the signature, removing the need for a separate on‑chain approve and streamlining UX.
- Security & backward compatibility: Each permit consumes a unique, ever‑increasing nonce and the signature is bound to the token’s EIP‑712 domain (name, chain ID, contract address). The extension adds < 200 bytes to a standard ERC‑20, leaving balances and transfers untouched.
- Seamless DeFi & wallet integration: DEXs, lending protocols, routers, and meta‑TX relayers can batch “permit → action” in one atomic transaction, while wallets like MetaMask and Ledger present it as a familiar “Sign Message” flow instead of a gas‑priced approval.
Why EIP‑2612 Is Unnecessary on Solana
Since Solana lets you combine approval and action in one atomic transaction and allows someone else to cover the fees by default, the two pain points that EIP‑2612 solves on Ethereum (duplicated approvals and user‑paid gas) are already removed at the protocol level. There is no need for an additional permit function, typed‑data domain, or contract upgrade; the standard SPL‑Token program satisfies those requirements out of the box.
Token Approval Model
On Ethereum, transferring ERC20 tokens between two addresses often requires approve + transferfrom . if you’re doing it on behalf of a user. On Solana, each user already has an ATA(Associated token accounts) recognized by the Token Program. A single Solana transaction can atomically call multiple instructions, so no separate approval step is required. You can directly transfer from one user’s token account to another in one go.
Fee‑Payer (Gas Delegation) Model
Every Solana transaction explicitly names both its signers and a fee‑payer. A user can sign the transaction while designating a third party (a wallet service, dApp backend, or relayer) to pay the fees. The protocol therefore delivers the same gasless user experience that EIP‑2612 targets, but without introducing a special off‑chain signature scheme such as EIP‑712.
How to do EIP-2612 on Solana
Below are two minimal, copy‑paste‑ready examples that map the two most common “permit → action” scenarios to Solana’s native primitives. Both use @solana/web3.js and @solana/spl‑token.
1. Owner Pays the Fee (Self‑Sponsored Transfer)
The owner both signs and pays the fee. All we do is bundle Approve → Transfer into a single atomic transaction, no extra permit function required.
import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
import {
createApproveInstruction,
createTransferInstruction,
getAssociatedTokenAddressSync
} from "@solana/spl-token";
const connection = new Connection("https://api.devnet.solana.com");
const owner = Keypair.generate();
const delegate = owner.publicKey;
const recipient = new PublicKey("DESTINATION_WALLET");
const mint = new PublicKey("TOKEN_MINT");
const amount = 1_000_000;
const ownerATA = getAssociatedTokenAddressSync(mint, owner.publicKey);
const recipientATA = getAssociatedTokenAddressSync(mint, recipient);
const ixApprove = createApproveInstruction(ownerATA, delegate, owner.publicKey, amount);
const ixTransfer = createTransferInstruction(ownerATA, recipientATA, owner.publicKey, amount);
const tx = new Transaction().add(ixApprove, ixTransfer);
tx.feePayer = owner.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.sign(owner);
const sig = await connection.sendRawTransaction(tx.serialize());
console.log("Sent (self‑sponsored):", sig);
2. Third Party Pays the Fee (Relayed Transfer)
Here the holder signs the instruction, but a relayer (or dApp back‑end) covers the SOL fee. Solana supports this natively via the feePayer field plus partial signatures.
import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
import { createTransferInstruction, getAssociatedTokenAddressSync } from "@solana/spl-token";
const connection = new Connection("https://api.devnet.solana.com");
const owner = Keypair.generate(); // replace with real keypair
const feePayer = Keypair.generate(); // replace with real keypair
const recipient = new PublicKey("DESTINATION_WALLET");
const mint = new PublicKey("TOKEN_MINT");
const amount = 500_000;
const ownerATA = getAssociatedTokenAddressSync(mint, owner.publicKey);
const recipientATA = getAssociatedTokenAddressSync(mint, recipient);
const ix = createTransferInstruction(ownerATA, recipientATA, owner.publicKey, amount);
const tx = new Transaction().add(ix);
tx.feePayer = feePayer.publicKey;
tx.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
tx.partialSign(owner);
tx.sign(feePayer);
const sig = await connection.sendRawTransaction(tx.serialize());
console.log("Sent (relayer‑sponsored):", sig);
Start building on Solana
Node storing all data and participating in consensus
- Ethereum: Archive Node
- Solana: [n/a]
Node storing some data and participating in consensus
- Ethereum: Full Node
- Solana: Consensus Node
Node storing some data and not participating in consensus
- Ethereum: Light Node
- Solana: RPC Node
Node storing all data and participating in consensus
- Ethereum: Archive Node
- Solana: [n/a]
Node storing some data and participating in consensus
- Ethereum: Full Node
- Solana: Consensus Node
Node storing some data and not participating in consensus
- Ethereum: Light Node
- Solana: RPC Node