ON-CHAIN RPC API
Direct RPC Quest Checker
Verify if a user has completed swap quests for your token directly querying the blockchain nodes. Zero indexer dependency, zero delay, fully trustless on-chain logs checking.
How the RPC check works
Unlike the subgraph API which queries a cached database index, the RPC verification endpoint queries active blockchain nodes directly:
- Queries the EVM node for
AGGFlowSwapevent logs emitted by the Aggregator Entrypoint contract (0x5E19EB2A6BA30892CCe33f93D7Fb24D498512266). - Aggregates and filters logs for your user's wallet address (
user) and your project's custom token address. - Fetches block timestamps in parallel for the most recent swaps so you can display transaction dates.
- Evaluates eligibility against the
minSwapscount threshold and returns structured proofs.
API Endpoint Specs
Access this endpoint using standard GET or POST requests. CORS is enabled globally.
GET
Direct On-Chain Swap Check
https://LitvmSwap.com/api/rpc-verify-swaps?address=[wallet_address]&token=[your_token_address]&minSwaps=[count]&fromBlock=[start_block]Request Parameters
| Parameter | Type | Requirement | Description |
|---|---|---|---|
address | String | Required | The user's EVM wallet address to verify (case-insensitive). |
token | String | Optional | Your token's contract address on LitVM. Defaults to ZKUSDC if omitted. |
minSwaps | Number | Optional | Swap count threshold for the quest. Default is 1. |
fromBlock | Number | Optional | The block number to start scanning logs. Recommended to set to your token's deployment block to prevent timeouts. Default is 290904 (DEX Deployment block). |
toBlock | Number/Str | Optional | End block range. Defaults to "latest". |
counterToken | String | Optional | Verify swaps made specifically against a target counter asset. |
⚠️ Optimizing RPC Performance
CRITICAL PERFORMANCE NOTE: Scanning millions of blocks via RPC can cause requests to timeout. Always advise clients to specify the
fromBlock parameter, pointing to the exact block where their custom token was deployed. This reduces the node scan range and keeps response times under 200ms.JSON Response Structure
The response is designed to match the Subgraph verification format 100%, allowing seamless hot-swapping.
RPC API JSON Response
{
"success": true,
"address": "0x23d542dcefb00b1f4268e67a0ec1ef4de0a58fe2",
"token": {
"address": "0xdf69970b2fe416339187aa41d39882e864984ce9",
"symbol": "ZKUSDC",
"name": "ZKUSDC",
"decimals": 18
},
"counterToken": null,
"verification": {
"totalSwaps": 16,
"aggregatorSwaps": {
"total": 16,
"swappedIn": 2,
"swappedOut": 14
},
"directPoolSwaps": {
"total": 0,
"v2": 0,
"v3": 0
},
"totalVolumeToken": 2207418.024949277,
"totalVolumeUSD": 2207418.024949277,
"tokenPriceUSD": 1,
"minSwapsRequired": 3,
"isEligible": true,
"remainingSwapsNeeded": 0
},
"proofs": {
"swapCount": 16,
"swaps": [
{
"id": "0x1fb98ddc62ac59633834f217baae01e094fc4ffaa3cbccb7f416b02b5e020c71-14978000",
"txHash": "0x1fb98ddc62ac59633834f217baae01e094fc4ffaa3cbccb7f416b02b5e020c71",
"type": "sell",
"timestamp": 1778439431,
"dateTime": "2026-05-10T18:57:11.000Z",
"amountIn": "500000000000000000000000",
"amountInDecimal": 500000,
"amountOut": "571588317793374195800821",
"amountOutDecimal": 571588.317793
}
]
},
"timestamp": "2026-07-12T05:58:30.000Z"
}Node.js Integration Boilerplate
Node.js Integration Code
// Example: Querying RPC-based verify-swaps in Node.js
const axios = require('axios');
async function verifyUserSwapsViaRpc(userAddress, tokenAddress, minSwaps = 1) {
try {
const url = `${window.location.origin}/api/rpc-verify-swaps`;
const response = await axios.get(url, {
params: {
address: userAddress,
token: tokenAddress,
minSwaps: minSwaps,
fromBlock: 290904, // Default deployment block on LitVM to optimize search range
limit: 10
}
});
const { success, verification, token, proofs } = response.data;
if (success) {
console.log(`RPC Verification for token: ${token.symbol}`);
console.log(`Eligible: ${verification.isEligible ? 'YES' : 'NO'}`);
console.log(`Total swaps parsed: ${verification.totalSwaps}`);
console.log(`Swaps detail returned: ${proofs.swapCount}`);
return verification.isEligible;
}
return false;
} catch (error) {
console.error('RPC check error:', error.response?.data || error.message);
return false;
}
}React + TypeScript UI Example
React Component (RpcQuestStatus.tsx)
import React, { useState } from 'react';
// Interfaces mirroring the API response schema
interface RpcSwapProof {
id: string;
txHash: string;
type: 'buy' | 'sell';
timestamp: number | null;
dateTime: string | null;
amountIn: string;
amountInDecimal: number;
amountOut: string;
amountOutDecimal: number;
}
interface RpcVerifyResponse {
success: boolean;
address: string;
token: {
address: string;
symbol: string;
name: string;
decimals: number;
};
verification: {
totalSwaps: number;
totalVolumeToken: number;
totalVolumeUSD: number;
minSwapsRequired: number;
isEligible: boolean;
remainingSwapsNeeded: number;
};
proofs: {
swapCount: number;
swaps: RpcSwapProof[];
};
}
export const RpcQuestStatus: React.FC = () => {
const [userAddress, setUserAddress] = useState('');
const [tokenAddress, setTokenAddress] = useState('0xdf69970B2fE416339187aA41D39882e864984CE9');
const [loading, setLoading] = useState(false);
const [data, setData] = useState<RpcVerifyResponse | null>(null);
const [error, setError] = useState<string | null>(null);
const performRpcCheck = async () => {
setLoading(true);
setError(null);
setData(null);
try {
const response = await fetch(
`/api/rpc-verify-swaps?address=${userAddress}&token=${tokenAddress}&minSwaps=1&fromBlock=290904`
);
const json = await response.json();
if (json.success) {
setData(json);
} else {
setError(json.error || 'Failed to verify swaps.');
}
} catch (err: any) {
setError(err.message || 'Network error.');
} finally {
setLoading(false);
}
};
return (
<div style={{ background: '#0d1117', padding: 24, borderRadius: 12, color: 'white' }}>
<h3>On-Chain RPC Quest Checker</h3>
<input
value={userAddress}
onChange={e => setUserAddress(e.target.value)}
placeholder="User Address (0x...)"
style={{ width: '100%', padding: '10px', margin: '10px 0', background: '#161b22', color: '#fff', border: '1px solid #30363d', borderRadius: 6 }}
/>
<button onClick={performRpcCheck} disabled={loading} style={{ background: '#238636', padding: '10px 20px', border: 'none', borderRadius: 6, color: '#fff', cursor: 'pointer' }}>
{loading ? 'Verifying directly on RPC...' : 'Verify Swaps'}
</button>
{data && (
<div style={{ marginTop: 20 }}>
<p>Total Swaps: {data.verification.totalSwaps}</p>
<p>Eligible: {data.verification.isEligible ? '✅ Yes' : '❌ No'}</p>
</div>
)}
</div>
);
};