Skip to Content
Quasar Cloud SDKTransaction Syncing & History

Transaction Syncing & History

The @tuwaio/quasar-sdk package enables your backend (Next.js Server Actions or Node.js API routes) to securely persist transaction states to the cloud (syncCreate) and query cross-device history logs (getHistory) for connected wallet addresses.


🔐 The SIWX Security Model

When calling Quasar Cloud APIs from your server, your QUASAR_SECRET_KEY grants administrative access to the Quasar Engine. However, to prevent unauthorized clients from reading or spoofing another user’s transaction history, every request sent to your backend should include a SIWX Session payload (SiwxSession).

Why SIWX Auth?

  1. Cryptographic Proof of Ownership: Proves that the connected user actually owns the wallet address (walletAddress) using the standard CAIP-122 sign-in.
  2. Replay Attack Protection: Every signature embeds an ISO timestamp and a secure nonce. The verifySiwxPayload method from @tuwaio/sdk/siwx/server strictly validates the signature and its expiration.
  3. Stateless Multi-Chain Verification: Supports both EVM (viem) and Solana (@solana/kit / SubtleCrypto) verification out of the box with zero database lookups on your server.

☁️ 1. Syncing Transactions (syncCreate)

Use quasar.pulsar.syncCreate in your backend server action or API route to persist a transaction state in the Quasar Engine database.

// src/app/actions/syncTransaction.ts 'use server'; import { cookies } from 'next/headers'; import { Quasar, QuasarSDKError, type Transaction } from '@tuwaio/quasar-sdk'; import { isSessionMatchingTarget } from '@tuwaio/sdk/siwx'; import { getSiwxServerSession } from '@tuwaio/sdk/siwx/server'; import { sessionStore } from '@/lib/authStores'; // Initialize Quasar with your server-only Secret Key const quasar = new Quasar({ secretKey: process.env.QUASAR_SECRET_KEY ?? '', }); export async function syncTransactionServerAction(tx: Transaction, appName: string = 'My dApp') { // Step 1: Read and verify session server-side const session = await getSiwxServerSession({ cookieSource: await cookies(), sessionStore, }); if (!session) { throw new Error('Unauthorized: No active session found.'); } try { // Step 2: Ensure the transaction address matches the verified signer (EVM case-insensitive / Solana case-sensitive) if (tx.from && !isSessionMatchingTarget(session, tx.from, tx.chainId)) { throw new Error('Forbidden: Wallet address mismatch.'); } // Step 3: Persist the transaction to Quasar Cloud const result = await quasar.pulsar.syncCreate(tx, appName); return { success: true, txKey: result.txKey }; } catch (error) { if (error instanceof QuasarSDKError) { console.error(`[Quasar Engine Error ${error.status}]: ${error.message}`); throw new Error(`Cloud Sync Failed: ${error.message}`); } throw error; } }

📜 2. Querying Transaction History (getHistory)

Query paginated transaction histories for a verified user across all their devices.

// src/app/actions/getHistory.ts 'use server'; import { cookies } from 'next/headers'; import { Quasar, QuasarSDKError } from '@tuwaio/quasar-sdk'; import { isSessionMatchingTarget } from '@tuwaio/sdk/siwx'; import { getSiwxServerSession } from '@tuwaio/sdk/siwx/server'; import { sessionStore } from '@/lib/authStores'; const quasar = new Quasar({ secretKey: process.env.QUASAR_SECRET_KEY ?? '', }); export interface GetHistoryParams { walletAddress: string; page?: number; limit?: number; chainId?: string; appName?: string; } export async function getHistoryServerAction(params: GetHistoryParams) { const session = await getSiwxServerSession({ cookieSource: await cookies(), sessionStore, }); if (!session || !isSessionMatchingTarget(session, params.walletAddress, params.chainId)) { throw new Error('Unauthorized: Session mismatch or unauthenticated.'); } try { // Query paginated history from Quasar Cloud Engine const history = await quasar.pulsar.getHistory({ walletAddress: params.walletAddress, page: params.page ?? 1, limit: params.limit ?? 10, appName: params.appName ?? 'My dApp', }); return history; } catch (error) { if (error instanceof QuasarSDKError) { console.error(`[Quasar History Error ${error.status}]: ${error.message}`); throw new Error(`Failed to fetch history: ${error.message}`); } throw error; } }

🛡️ 3. Production Error Handling (QuasarSDKError)

All calls to quasar.pulsar.* throw typed QuasarSDKError instances when an API request fails (e.g. rate limits, invalid payload, or authentication issues).

import { QuasarSDKError } from '@tuwaio/quasar-sdk'; try { // @ts-expect-error - Assuming txData is a valid Transaction await quasar.pulsar.syncCreate(txData, 'My App'); } catch (error) { if (error instanceof QuasarSDKError) { // Inspected properties: error.status (HTTP status code), error.message console.error(`Quasar Error Code: ${error.status}`); console.error(`Error Message: ${error.message}`); } else { console.error('Unexpected System Error:', error); } }

⚡ 4. Full Client Integration with Pulsar Engine

Now that your server actions are ready, connect them to your client-side Pulsar transaction store. The server action automatically resolves the authenticated user session from HTTP-Only cookies:

// src/hooks/usePulsarStore.ts 'use client'; import { createPulsarStore } from '@tuwaio/sdk/pulsar'; import { preFlightTxCheck } from '@tuwaio/quasar-sdk'; import { syncTransactionServerAction } from '@/app/actions'; import { TransactionUnion } from '@/types'; export const pulsarStore = createPulsarStore<TransactionUnion>({ name: 'app-transaction-storage', // 1. Ensure a valid session exists before initiating transaction tracking // and ping Quasar Cloud to ensure the server is alive. beforeTxProcess: async () => { await preFlightTxCheck(); }, // 2. Automatically sync transaction states to Quasar Cloud Engine onRemoteCreate: async (tx) => { try { await syncTransactionServerAction(tx as TransactionUnion); } catch (err) { console.error('Remote transaction sync failed:', err); throw err; // Rethrow to inform pulsar-core that sync failed } }, });
Last updated on