EIP‑5792
EIP-5792 defines a JSON-RPC interface to enable wallets to expose:
✅ EIP-6963 wallet discovery
✅ Wallet connection & account display
✅ Capability detection using wallet_getCapabilities
✅ Batched ETH transfers with wallet_sendCalls
✅ Transaction status polling via wallet_getCallsStatus
This allows apps to submit grouped transactions in a single wallet popup, improving UX and enabling gas sponsorship.
You can also learn in detailed about each functionality here.
Installation
Clone this repository and install dependencies:
git clone https://github.com/gnosischain/gnosis-dapp-boilerplate
cd gnosis-dapp-boilerplate
npm install
▶️ Running the App
npm run dev
Visit http://localhost:3000 in your browser.
Navigate to the EIP-5792 page.
Practical Example Implementation
1. Capability Detection (wallet_getCapabilities
)
const loadCaps = async () => {
if (!hasProvider()) return;
try {
const params = [account!, [toHex(chainId!)]] as const;
const all: Record<string, any> =
typeof provider.getCapabilities === 'function'
? await provider.getCapabilities(account!, [toHex(chainId!)])
: await provider.request({ method: 'wallet_getCapabilities', params });
const c = all?.[toHex(chainId!)] ?? null;
setCaps(c);
const atomicSupported = Boolean(
c?.atomic && ['ready', 'supported'].includes(c.atomic.status)
);
if (atomicSupported) {
message.success('Atomic batch supported by your wallet.');
} else {
message.error('Atomic batch NOT supported by your wallet.');
}
} catch {
setCaps(null);
message.error('Failed to load capabilities.');
}
};
2. Batch Transaction Submission (wallet_sendCalls
)
const sendBatch = async (vals: any) => {
if (!hasProvider()) return;
setLoading(true);
setLastTxHash(null);
setPendingId(null);
try {
const calls = [
{
to: vals.call1.to,
value: `0x${parseEther(vals.call1.amount.toString()).toString(16)}`,
},
];
if (vals.call2?.to) calls.push({ to: vals.call2.to, value: `0x${parseEther(vals.call2.amount.toString()).toString(16)}` });
if (vals.call3?.to) calls.push({ to: vals.call3.to, value: `0x${parseEther(vals.call3.amount.toString()).toString(16)}` });
const payload = {
version: '2.0.0',
chainId: toHex(chainId!),
from: account!,
atomicRequired: true,
calls,
};
if (paymaster) {
payload.capabilities = { paymasterService: { url: undefined } };
}
const res: any =
typeof provider.sendCalls === 'function'
? await provider.sendCalls(payload)
: await provider.request({ method: 'wallet_sendCalls', params: [payload] });
const hash = typeof res === 'string' ? res : res?.transactionHash || res?.hash;
if (hash) {
setLastTxHash(hash);
message.success('Batch submitted – confirm in wallet.');
} else if (res?.id) {
setPendingId(res.id);
message.info('Batch submitted – waiting for on-chain tx…');
} else {
message.warning('wallet_sendCalls returned no hash or id.');
}
} catch (err: any) {
message.error(`wallet_sendCalls failed: ${err.message}`);
} finally {
setLoading(false);
}
};
3. Polling Transaction Status (wallet_getCallsStatus
)
useEffect(() => {
if (!pendingId || !provider) return;
const poll = setInterval(async () => {
try {
const status: any = await provider.request({
method: 'wallet_getCallsStatus',
params: [pendingId],
});
if (status?.status === 200 && status?.receipts?.length) {
const txHash = status.receipts[0].transactionHash || status.receipts[0].hash;
if (txHash) {
setLastTxHash(txHash);
setPendingId(null);
clearInterval(poll);
message.success('Batch executed on-chain.');
}
} else if (status?.status >= 400) {
message.error('Batch failed.');
setPendingId(null);
clearInterval(poll);
}
} catch (e) {
console.error(e);
}
}, POLL_INTERVAL);
return () => clearInterval(poll);
}, [pendingId, provider]);