Skip to main content

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]);