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:

  1. Queries the EVM node for AGGFlowSwap event logs emitted by the Aggregator Entrypoint contract (0x5E19EB2A6BA30892CCe33f93D7Fb24D498512266).
  2. Aggregates and filters logs for your user's wallet address (user) and your project's custom token address.
  3. Fetches block timestamps in parallel for the most recent swaps so you can display transaction dates.
  4. Evaluates eligibility against the minSwaps count 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

ParameterTypeRequirementDescription
addressStringRequiredThe user's EVM wallet address to verify (case-insensitive).
tokenStringOptionalYour token's contract address on LitVM. Defaults to ZKUSDC if omitted.
minSwapsNumberOptionalSwap count threshold for the quest. Default is 1.
fromBlockNumberOptionalThe block number to start scanning logs. Recommended to set to your token's deployment block to prevent timeouts. Default is 290904 (DEX Deployment block).
toBlockNumber/StrOptionalEnd block range. Defaults to "latest".
counterTokenStringOptionalVerify 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>
  );
};

Need dedicated RPC endpoints or customized RPC rate limits? Contact the LitvmSwap core team.