Skip to main content

Usage

const { syncTransfers, syncTransfersAsync, data, isLoading, isError, error, isSuccess, reset } =
  useSyncOnChainTransfers();

Parameters

syncTransfers / syncTransfersAsync accept an object with:
ParameterTypeRequiredDescription
walletAddressstringYesWallet public key to sync transfers for
bearerTokenstringYesBearer token for authentication

Return Value

PropertyTypeDescription
syncTransfers(input) => voidTrigger sync (fire-and-forget)
syncTransfersAsync(input) => Promise<SyncOnChainTransfersResponse>Promise-based sync
dataSyncOnChainTransfersResponse | undefinedSync result
isLoadingbooleanWhether sync is in progress
isErrorbooleanWhether an error occurred
errorError | nullError from last sync attempt
isSuccessbooleanWhether last sync succeeded
reset() => voidReset mutation state

SyncOnChainTransfersResponse

FieldTypeDescription
syncednumberNew transactions discovered and saved to DB
totalnumberTotal transfers found on-chain for this wallet

Example Implementation

import { useState } from "react";
import { useAuth } from "@clerk/nextjs";
import {
  useSyncOnChainTransfers,
  useGetTransactionList,
} from "@chipi-stack/nextjs";

export function TransactionHistory({ wallet }: { wallet: { publicKey: string } }) {
  const { getToken } = useAuth();
  const { syncTransfersAsync, isLoading: isSyncing } = useSyncOnChainTransfers();
  const { data: txList, refetch } = useGetTransactionList({
    query: { walletAddress: wallet.publicKey },
    getBearerToken: getToken,
  });

  const handleSync = async () => {
    const token = await getToken();
    if (!token) return;

    // Discover external transfers (e.g., USDC received from Argent X)
    const result = await syncTransfersAsync({
      walletAddress: wallet.publicKey,
      bearerToken: token,
    });

    console.log(`Found ${result.synced} new transfers`);
    // Transaction list cache is automatically invalidated — refetch happens
  };

  return (
    <div>
      <button onClick={handleSync} disabled={isSyncing}>
        {isSyncing ? "Syncing..." : "Sync On-Chain Transfers"}
      </button>

      {txList?.data.map((tx) => (
        <div key={tx.id}>
          <span>{tx.subType === "receive" ? "Received" : "Sent"}</span>
          <span>{tx.amount} {tx.token}</span>
          <span>{tx.transactionHash.slice(0, 12)}...</span>
        </div>
      ))}
    </div>
  );
}

How It Works

  1. Calls POST /transactions/sync-on-chain on the backend
  2. Backend queries Voyager API for all token transfers for the wallet
  3. Compares with existing DB records by transaction hash
  4. Saves new transfers to the Transaction table (read-through cache)
  5. Returns { synced, total }
  6. Invalidates useGetTransactionList cache so new transfers appear immediately
After sync, useGetTransactionList returns both SDK-initiated transfers AND external receives.

When to Use

  • On wallet load: Sync once when user opens their wallet to discover any external receives
  • Pull-to-refresh: Let users manually trigger sync
  • After receiving funds: If user expects incoming funds from an external wallet
Synced transfers are cached in the database. Subsequent useGetTransactionList calls return them without re-querying Voyager.

Limitations

  • Only discovers token transfers (ERC-20). Contract interactions without transfers are not synced.
  • Voyager API rate limits apply. Avoid calling sync on every render.
  • First sync for a wallet with many transfers may take a few seconds (paginated, max 500 transfers per sync).