@tuwaio/quasar-sdk
v0.1.3
Published
Layer 5 (L5) of the TUWA Ecosystem. The Quasar Cloud SDK providing server-side Node.js & Edge utilities for transaction indexing, billing, and backend API integration.
Maintainers
Readme
@tuwaio/quasar-sdk
The official Layer 5 (L5) server-side Node.js & Edge SDK for the TUWA Quasar Cloud.
🏛️ What is @tuwaio/quasar-sdk?
@tuwaio/quasar-sdk is Layer 5 (L5) of the TUWA ecosystem architecture — the official backend companion to the TUWA client libraries. It serves as the gateway to the Quasar Cloud Engine, allowing your server to securely push transaction logs, query paginated transaction histories, and sync infrastructure states.
It operates strictly on the server (Node.js, Next.js Server Actions, or Edge functions) and uses Secret Keys to communicate with Quasar's iron-dome guarded endpoints.
✨ Key Features
- ☁️ Cloud Sync: Automatically persist pending and terminal transaction states to the Quasar Database for cross-device history.
- 🔐 Headless SIWX (CAIP-122) Auth Ready: Seamlessly pairs with
@tuwaio/sdk/siwx/serverfor strict cryptographic verification of user sessions before allowing database writes. - ⚡ Edge Ready: Uses
ofetchand lightweight cryptography to run seamlessly in Cloudflare Workers and Vercel Edge. - 📦 InMemory Sync: Perfectly pairs with
@tuwaio/sdk/pulsar(createTxInMemoryStore) to fetch history and hydrate local React states.
💾 Installation
pnpm add @tuwaio/quasar-sdk ofetch @tuwaio/pulsar-core @tuwaio/siwx-core @tuwaio/siwx-server @tuwaio/siwx-reactNote: ofetch and @tuwaio/pulsar-core are required peer dependencies. The @tuwaio/siwx-* packages are required if you intend to use the headless SIWX (CAIP-122) authentication integrations.
🚀 Quick Start (Node.js / Edge)
This is a basic example of how to interact with the Quasar Cloud directly from your secure backend environments (like Next.js API Routes, Server Actions, or NestJS).
import { Quasar, type Transaction } from '@tuwaio/quasar-sdk';
import { verifySiwxPayload, type SiwxSession } from '@tuwaio/sdk/siwx/server';
// Initialize Quasar with your Secret Key from the Dashboard
const quasar = new Quasar({ secretKey: process.env.QUASAR_SDK_SK ?? '' });
/**
* Example Next.js Server Action to Sync a Transaction
*/
export async function syncTransaction(tx: Transaction, sessionData: SiwxSession) {
// 1. Verify the client's signature (prevent unauthorized spoofing)
const isValid = await verifySiwxPayload(sessionData);
if (!isValid) throw new Error('Invalid signature.');
// 2. Sync the transaction securely to the Quasar Cloud
await quasar.pulsar.syncCreate(tx, 'My Application');
return { success: true };
}
/**
* Example Next.js Server Action to Fetch History
*/
export async function getHistory(
params: { walletAddress: string; page: number; limit: number; appName: string },
sessionData: SiwxSession,
) {
const isValid = await verifySiwxPayload(sessionData);
if (!isValid) throw new Error('Invalid signature.');
// Return the paginated transaction history
return quasar.pulsar.getHistory(params);
}🔐 Frontend Authentication (SIWX)
The Quasar SDK relies on the standard SIWX (CAIP-122) protocol for authenticating requests.
You should use the headless useSatelliteSiwxAutoAuth hook provided by @tuwaio/sdk/siwx on your frontend. This hook automatically prompts users to sign a CAIP-122 message when they connect their wallet, and establishes a secure session with your backend.
For detailed frontend integration, see the SIWX Documentation.
🌍 The Full Flow (Production Architecture)
To see how incredibly powerful @tuwaio/quasar-sdk is when combined with @tuwaio/sdk, here is a complete architectural overview without any skipped steps. Notice how the Pulsar Store and Headless SIWX dance together effortlessly:
1. Define Your Transaction Types (types.ts)
First, define the strict union of all possible transactions your app supports.
// src/types.ts
import type { Transaction } from '@tuwaio/sdk/pulsar';
export enum AppTxType {
SWAP = 'SWAP',
}
export type SwapTx = Transaction & {
type: AppTxType.SWAP;
payload: { tokenIn: string; tokenOut: string; amount: number };
};
export type TransactionUnion = SwapTx;2. Secure Server Proxy (actions.ts)
Proxy calls to Quasar Cloud using your backend to protect secret keys.
// src/app/actions.ts
'use server';
import { Quasar } from '@tuwaio/quasar-sdk';
import { verifySiwxPayload, type SiwxSession } from '@tuwaio/sdk/siwx/server';
import { TransactionUnion } from '@/types';
const quasar = new Quasar({ secretKey: process.env.QUASAR_SDK_SK ?? '' });
export async function syncTransaction(tx: TransactionUnion, sessionData: SiwxSession) {
const isValid = await verifySiwxPayload(sessionData);
if (!isValid) throw new Error('Invalid signature.');
await quasar.pulsar.syncCreate(tx, 'My App');
return { success: true };
}
export async function getHistory(params: any, sessionData: SiwxSession) {
const isValid = await verifySiwxPayload(sessionData);
if (!isValid) throw new Error('Invalid signature.');
return quasar.pulsar.getHistory(params);
}// src/hooks/usePulsarStore.ts
'use client';
import { createPulsarStore, createTxInMemoryStore, createBoundedUseStore } from '@tuwaio/sdk/pulsar';
import { pulsarEvmAdapter } from '@tuwaio/evm-sdk/pulsar';
import { pulsarSolanaAdapter } from '@tuwaio/solana-sdk/pulsar';
import { useSiwxSessionStore } from '@tuwaio/sdk/siwx';
import { preFlightTxCheck } from '@tuwaio/quasar-sdk';
import { getHistory, syncTransaction } from '@/app/actions';
import { wagmiConfig, appEVMChains, solanaRPCUrls } from '@/configs/appConfig';
import { TransactionUnion } from '@/types';
const storageName = 'transactions-tracking-storage';
const initialStore = createPulsarStore<TransactionUnion>({
name: storageName,
adapter: [pulsarEvmAdapter(wagmiConfig, appEVMChains), pulsarSolanaAdapter({ rpcUrls: solanaRPCUrls })],
beforeTxProcess: async () => {
// Ensures we have a valid SIWX session and Quasar Cloud is reachable before executing blockchain logic
await preFlightTxCheck();
},
onRemoteCreate: async (tx) => {
try {
// Syncs the new transaction to Quasar via Next.js Server Actions
const session = useSiwxSessionStore.getState().session;
if (session) {
await syncTransaction(tx as TransactionUnion, session);
}
} catch (err) {
console.error('[PulsarHook] Remote sync failed:', err);
}
},
});
export const usePulsarStore = createBoundedUseStore(initialStore);
// Wrap with inMemoryStore to enable remote history fetching & pagination
const pulsarInMemoryStore = createTxInMemoryStore<TransactionUnion>({
localTransactionsPool: initialStore.getState().transactionsPool,
getHistory: async ({ page, walletAddress }) => {
try {
const session = useSiwxSessionStore.getState().session;
if (!session) return null;
const history = await getHistory({ walletAddress, page, limit: 10, appName: 'My App' }, session);
if (!history) return null;
return { ...history, docs: history.docs as TransactionUnion[] };
} catch (error) {
console.error('[PulsarHook] Failed to fetch history:', error);
throw error;
}
},
onHistoryFetched: async (remoteTxs) => {
await initialStore.getState().injectExternalPendingTxs(remoteTxs);
},
});
initialStore.subscribe((state) => pulsarInMemoryStore.getState().syncWithLocalPool(state.transactionsPool));
export const usePulsarInMemoryStore = createBoundedUseStore(pulsarInMemoryStore);4. Nova Transactions Provider (NovaTransactionsProvider.tsx)
// src/providers/NovaTransactionsProvider.tsx
'use client';
import { useSatelliteConnectStore } from '@tuwaio/sdk/satellite';
import { useInitializeTransactionsPool, type TxInMemoryPagination } from '@tuwaio/sdk/pulsar';
import { getAdapterFromConnectorType } from '@tuwaio/sdk/orbit';
import { NovaTransactionsProvider as NTP } from '@tuwaio/sdk/nova-transactions/providers';
import { usePulsarInMemoryStore, usePulsarStore } from '@/hooks/usePulsarStore';
export function NovaTransactionsProvider({ pagination }: { pagination: TxInMemoryPagination }) {
const initialTx = usePulsarStore((state) => state.initialTx);
const closeTxTrackedModal = usePulsarStore((state) => state.closeTxTrackedModal);
const executeTxAction = usePulsarStore((state) => state.executeTxAction);
const initializeTransactionsPool = usePulsarStore((state) => state.initializeTransactionsPool);
const activeConnection = useSatelliteConnectStore((state) => state.activeConnection);
const getAdapter = usePulsarStore((state) => state.getAdapter);
const transactionsPool = usePulsarInMemoryStore((state) => state.transactionsPool);
useInitializeTransactionsPool({ initializeTransactionsPool });
return (
<NTP
transactionsPool={transactionsPool}
initialTx={initialTx}
closeTxTrackedModal={closeTxTrackedModal}
executeTxAction={executeTxAction}
connectedWalletAddress={activeConnection?.isConnected ? activeConnection.address : undefined}
connectedAdapterType={getAdapterFromConnectorType(activeConnection?.connectorType ?? 'evm:')}
adapter={getAdapter()}
pagination={pagination}
/>
);
}5. The Seamless UI Integration (AppProviders.tsx)
// src/providers/AppProviders.tsx
'use client';
import { SatelliteConnectProvider, useSatelliteConnection } from '@tuwaio/sdk/satellite';
import { NovaConnectProvider } from '@tuwaio/sdk/nova-connect';
import { satelliteEVMAdapter } from '@tuwaio/evm-sdk/satellite';
import { EVMConnectorsWatcher } from '@tuwaio/evm-sdk/nova-connect';
import { satelliteSolanaAdapter } from '@tuwaio/solana-sdk/satellite';
import { SolanaConnectorsWatcher } from '@tuwaio/solana-sdk/nova-connect';
import { useSiwx, useSiwxSession } from '@tuwaio/sdk/siwx';
import { isSafeApp, getAdapterFromConnectorType, OrbitAdapter } from '@tuwaio/sdk/orbit';
import { appEVMChains, solanaRPCUrls, wagmiConfig } from '@/configs/appConfig';
import { usePulsarInMemoryStore, usePulsarStore } from '@/hooks/usePulsarStore';
import { NovaTransactionsProvider } from '@/providers/NovaTransactionsProvider';
export function AppProviders({ children }: { children: React.ReactNode }) {
const getAdapter = usePulsarStore((state) => state.getAdapter);
const transactionsPool = usePulsarInMemoryStore((state) => state.transactionsPool);
const isLoading = usePulsarInMemoryStore((state) => state.isLoading);
const isError = usePulsarInMemoryStore((state) => state.isError);
const currentPage = usePulsarInMemoryStore((state) => state.currentPage);
const hasMore = usePulsarInMemoryStore((state) => state.hasMore);
const fetchNextPage = usePulsarInMemoryStore((state) => state.fetchNextPage);
const fetchInitial = usePulsarInMemoryStore((state) => state.fetchInitial);
const pagination = { isLoading, isError, currentPage, hasMore, fetchNextPage };
// Watch SIWX session to keep connection state aligned
const siwxSession = useSiwxSession();
const { signIn } = useSiwx();
return (
<SatelliteConnectProvider
adapter={[satelliteEVMAdapter(wagmiConfig, appEVMChains), satelliteSolanaAdapter({ rpcUrls: solanaRPCUrls })]}
autoConnect={true}
callbackAfterConnected={async (connection) => {
const isEVM = getAdapterFromConnectorType(connection.connectorType) === OrbitAdapter.EVM;
if (isEVM && isSafeApp) return;
// Trigger SIWX flow
await signIn();
// Fetch history slightly after connection and sign-in
setTimeout(() => fetchInitial(connection.address), 2000);
}}
>
<EVMConnectorsWatcher wagmiConfig={wagmiConfig} siwx={siwxSession} />
<SolanaConnectorsWatcher siwx={siwxSession} />
<NovaTransactionsProvider pagination={pagination} />
<NovaConnectProvider
appChains={appEVMChains}
solanaRPCUrls={solanaRPCUrls}
transactionPool={transactionsPool}
pulsarAdapter={getAdapter()}
withImpersonated
withBalance
withChain
pagination={pagination}
>
{children}
</NovaConnectProvider>
</SatelliteConnectProvider>
);
}6. Creating a Transaction (Usage)
Now you can safely execute strictly-typed, cross-chain transactions anywhere in your app. The store automatically routes the transaction to the correct adapter, and Quasar syncs it to the cloud.
// src/components/SwapButton.tsx
'use client';
import { getAdapterFromConnectorType, OrbitAdapter } from '@tuwaio/sdk/orbit';
import { useSatelliteConnectStore } from '@tuwaio/sdk/satellite';
import { TxActionButton } from '@tuwaio/sdk/nova-transactions';
import { usePulsarStore, usePulsarInMemoryStore } from '@/hooks/usePulsarStore';
import { AppTxType } from '@/types';
export function SwapButton() {
const executeTxAction = usePulsarStore((s) => s.executeTxAction);
const getLastTxKey = usePulsarStore((s) => s.getLastTxKey);
const transactionsPool = usePulsarInMemoryStore((s) => s.transactionsPool);
const activeConnection = useSatelliteConnectStore((s) => s.activeConnection);
const handleSwapAction = async () => {
// Dynamically determine the adapter based on the currently connected wallet
const adapterType = getAdapterFromConnectorType(activeConnection?.connectorType ?? 'evm:');
await executeTxAction({
actionFunction: async () => {
/* your wagmi/solana contract call */
},
params: {
adapter: adapterType,
type: AppTxType.SWAP,
title: 'Token Swap',
desiredChainID: adapterType === OrbitAdapter.EVM ? 1 : undefined,
payload: { tokenIn: 'USDC', tokenOut: adapterType === OrbitAdapter.EVM ? 'ETH' : 'SOL', amount: 100 },
},
});
};
return (
<TxActionButton
action={handleSwapAction}
getLastTxKey={getLastTxKey}
transactionsPool={transactionsPool}
walletAddress={activeConnection?.address}
>
Cross-Chain Swap
</TxActionButton>
);
}📦 Available Namespaces
This package provides the following server-side utilities and React helpers:
Quasar— The main server-side client class instance used to accessquasar.pulsar.*methods.preFlightTxCheck— A client-side helper to ensure the user has a valid SIWX session and Quasar Cloud is reachable before prompting wallet signatures.
🤝 Contributing & Support
Contributions are welcome! Please read our main Contribution Guidelines.
If you find this library useful, please consider supporting its development. Every contribution helps!
📄 License
This project is licensed under the Apache-2.0 License - see the LICENSE file for details.
