@supanovaapp/sdk
v0.2.10
Published
React SDK for Supa Backend + Privy.io integration with Canton Network and EVM Smart Wallets support
Maintainers
Readme
Supa SDK
Supa SDK allows dApps to connect to Canton Network with Privy.io authentication and Ed25519 signing via Stellar wallets.
Quick overview
For a quick overview of the code, check out the demo application in the /demo folder.
Key Features
- Privy.io Authentication - Email, wallet, and social login methods
- EVM Smart Wallets - Support for Privy Smart Wallets with gas sponsorship
- Built-in Confirmation Modals - User-friendly signing confirmations
- Theme Support - Light/dark mode with customizable appearance
- Automatic Polling - Transaction completion tracking
- TypeScript Support - Full type safety and IntelliSense
- React Hooks - Simple and intuitive API
Installation
From npm (when published)
npm install @supa/sdk
# or
yarn add @supa/sdk
# or
pnpm add @supa/sdkOptional: For Smart Wallets support
npm install permissionless viemFrom local repository
If the package is not yet published to npm, you can install it locally:
1. Clone the repository
git clone <repository-url>
cd supa-sdk2. Install dependencies and build
npm install
npm run build3. Link to your project
Option A: Using npm link (recommended)
In the SDK directory:
npm linkIn your project directory:
npm link @supa/sdkOption B: Using local path
In your project's package.json, add:
{
"dependencies": {
"@supa/sdk": "file:../path/to/supa-sdk"
}
}Then run:
npm installOption C: Using tarball
In the SDK directory:
npm packThis creates a .tgz file. In your project:
npm install ../path/to/supa-sdk/supa-sdk-0.1.0.tgzQuick Start
import { SupaProvider, useAuth, useCanton } from '@supa/sdk';
function App() {
return (
<SupaProvider config={{ privyAppId: 'your-app-id' }}>
<MyApp />
</SupaProvider>
);
}
function MyApp() {
const { login, authenticated } = useAuth();
const { registerCanton, isRegistered, sendTransaction } = useCanton();
if (!authenticated) {
return <button onClick={login}>Login</button>;
}
if (!isRegistered) {
return <button onClick={registerCanton}>Register Canton</button>;
}
return (
<button onClick={() => sendTransaction(command, contracts)}>
Send Transaction
</button>
);
}Usage guide
1. Initialize the SDK
Wrap your application with SupaProvider:
import { SupaProvider } from '@supa/sdk';
function App() {
return (
<SupaProvider
config={{
privyAppId: 'your-privy-app-id',
apiBaseUrl: 'https://stage_api.supa.fyi', // optional
nodeIdentifier: 'nodeId',
appearance: {
theme: 'light', // 'light' or 'dark'
accentColor: '#6366f1',
},
loginMethods: ['email', 'wallet', 'google'],
}}
>
<YourApp />
</SupaProvider>
);
}Configuration options:
privyAppId- Your Privy App ID (required)apiBaseUrl- Backend API URL (default:https://stage_api.supa.fyi)nodeIdentifier- Canton node identifierappearance- Theme and styling optionsloginMethods- Array of enabled authentication methods
2. Connect to the wallet
Use the useAuth hook to manage authentication:
import { useAuth } from '@supa/sdk';
function LoginButton() {
const { login, logout, authenticated, user } = useAuth();
if (!authenticated) {
return <button onClick={login}>Login with Privy</button>;
}
return (
<div>
<p>Welcome, {user?.email?.address}!</p>
<button onClick={logout}>Logout</button>
</div>
);
}After successful authentication, authenticated becomes true and user object contains user data.
3. Canton Network Operations
Register Canton Wallet
import { useCanton } from '@supa/sdk';
function CantonWallet() {
const { registerCanton, isRegistered, cantonUser, loading } = useCanton();
const handleRegister = async () => {
try {
await registerCanton();
console.log('Canton wallet registered!');
} catch (error) {
console.error('Registration failed:', error);
}
};
if (!isRegistered) {
return <button onClick={handleRegister} disabled={loading}>
Register Canton Wallet
</button>;
}
return (
<div>
<p>Party ID: {cantonUser?.partyId}</p>
<p>Email: {cantonUser?.email}</p>
</div>
);
}Get Active Contracts
const { getActiveContracts } = useCanton();
// Get all contracts
const allContracts = await getActiveContracts();
// Filter by template IDs
const filteredContracts = await getActiveContracts([
'template-id-1',
'template-id-2'
]);Get Canton Balances
const { getBalances, cantonBalances } = useCanton();
// Fetch balances
try {
const balances = await getBalances();
// Find Canton Coin token
const cantonCoin = balances.tokens.find(
token => token.instrumentId.id === 'Amulet'
);
if (cantonCoin) {
console.log('Unlocked balance:', cantonCoin.totalUnlockedBalance);
console.log('Locked balance:', cantonCoin.totalLockedBalance);
console.log('Total balance:', cantonCoin.totalBalance);
// Access locked UTXOs for details
cantonCoin.lockedUtxos.forEach(utxo => {
console.log('Locked amount:', utxo.amount);
console.log('Expires at:', utxo.lock.expiresAt);
console.log('Context:', utxo.lock.context);
});
}
} catch (error) {
console.error('Failed to load balances:', error);
}
// Or use the cached state
if (cantonBalances) {
console.log('Cached balances:', cantonBalances);
}Send Canton Coin
const { sendCantonCoin } = useCanton();
try {
const result = await sendCantonCoin(
'receiver-party::1220abc123...', // Receiver Party ID
'100.5', // Amount (max 10 decimal places)
'Payment for services', // Optional memo
{
timeout: 30000, // completion timeout (ms)
pollInterval: 1000, // polling interval (ms)
}
);
console.log('Canton Coin sent successfully:', result);
} catch (error) {
// Special handling for preapproval errors
if (error.message.includes('preapproval')) {
console.error('Receiver must have transfer preapproval enabled');
} else {
console.error('Transfer failed:', error);
}
}Note: The amount cannot have more than 10 decimal places. Transfers are only supported to wallets with preapproved transfers enabled.
Submit a Transaction
const { sendTransaction } = useCanton();
const commands = {
// Your Canton command(s)
};
try {
const result = await sendTransaction(commands, disclosedContracts, {
timeout: 30000, // completion timeout (ms)
pollInterval: 1000, // polling interval (ms)
});
console.log('Transaction successful:', result);
} catch (error) {
console.error('Transaction failed:', error);
}The SDK automatically:
- Prepares the transaction
- Shows confirmation modal
- Signs with user approval
- Submits and polls for completion
Sign a Message
const { signMessage } = useCanton();
try {
const signature = await signMessage('Hello, Canton!');
console.log('Signature:', signature);
} catch (error) {
console.error('Signing failed:', error);
}4. Devnet Operations
Request test tokens from the devnet faucet:
const { tapDevnet } = useCanton();
try {
const result = await tapDevnet('1000', {
timeout: 30000,
pollInterval: 1000,
});
console.log('Tokens received:', result);
} catch (error) {
console.error('Faucet request failed:', error);
}5. Advanced Features
Custom Modal Options
import { useSignMessage } from '@supa/sdk';
const { signMessage } = useSignMessage();
await signMessage('Hello', {
title: 'Sign Message',
description: 'Please review and sign.',
confirmText: 'Sign',
rejectText: 'Cancel',
onSuccess: (sig) => console.log('Signed:', sig),
onRejection: () => console.log('Rejected'),
});Custom Transaction Modals
import { useSendTransaction } from '@supa/sdk';
const { sendTransaction } = useSendTransaction();
await sendTransaction(command, contracts, {
modalTitle: 'Confirm Payment',
modalDescription: 'Send 100 tokens to Alice',
modalConfirmText: 'Pay Now',
submitOptions: { timeout: 30000 },
});Available Hooks
| Hook | Purpose | Key Methods |
|------|---------|-------------|
| useAuth | Authentication | login, logout, authenticated, user |
| useCanton | Canton Network | registerCanton, getBalances, sendCantonCoin, signMessage, sendTransaction, getActiveContracts, tapDevnet |
| useSignMessage | Enhanced message signing | signMessage with custom modals |
| useSendTransaction | Enhanced transactions | sendTransaction with custom modals |
| useConfirmModal | Generic modals | confirm, signMessageConfirm, signTransactionConfirm |
TypeScript Support
Full TypeScript support with generated types:
import type {
// Hook Return Types
UseAuthReturn,
UseCantonReturn,
UseSignMessageReturn,
UseSendTransactionReturn,
UseConfirmModalReturn,
// Canton Types
CantonMeResponseDto,
CantonActiveContractsResponseDto,
CantonQueryCompletionResponseDto,
CantonWalletBalancesResponseDto,
CantonTokenBalanceDto,
CantonInstrumentIdDto,
CantonLockedUtxoDto,
CantonUnlockedUtxoDto,
CantonPrepareAmuletTransferRequestDto,
// Option Types
SignMessageOptions,
SendTransactionOptions,
ConfirmModalOptions,
CantonSubmitPreparedOptions,
} from '@supa/sdk';How to run demo
Prerequisites
The demo uses the local version of the SDK (file:.. dependency), so you need to build the SDK first.
# 1. Install SDK dependencies
npm install
# 2. Create .env file in demo folder with your Privy credentials
# demo/.env:
# VITE_PRIVY_APP_ID=your_privy_app_id
# VITE_PRIVY_CLIENT_ID=your_privy_client_id
# VITE_API_BASE_URL=https://stage_api.supa.fyi
# VITE_CANTON_NODE_ID=nodeId
# 3. Build SDK, pack and run demo (one command)
npm run build && npm pack && cd demo && rm -rf node_modules/@supa node_modules/.vite package-lock.json && npm i && npm run devThis command builds the SDK, creates a tarball, cleans old dependencies/cache, reinstalls and starts the dev server.
Visit http://localhost:6969 to see the demo.
Note: If you make changes to the SDK source code, run the full command again to rebuild and restart.
The demo application includes:
- Complete authentication flow
- Canton wallet registration with automatic transfer preapproval
- Canton balance display with locked/unlocked UTXO details
- Canton Coin sending with validation
- Message signing with modals
- Transaction sending with modals
- Contract querying
- Devnet faucet integration
- Theme switching
- Error handling
Development Guide
This section is for active SDK development and contribution.
Setup for Development
# 1. Clone the repository
git clone <repository-url>
cd supa-sdk
# 2. Install dependencies
npm install
# 3. Build the SDK
npm run buildDevelopment Workflow
The demo application in /demo folder is already configured to use the local SDK version via "@supa/sdk": "file:.." dependency.
Recommended Workflow
# Build SDK, pack and run demo (from root directory)
npm run build && npm pack && cd demo && rm -rf node_modules/@supa node_modules/.vite package-lock.json && npm i && npm run devAfter making changes to SDK source, run the same command again. This ensures:
- Clean build of SDK
- Fresh tarball package
- Cleared cache and dependencies
- Proper dev server restart
Build Output
The npm run build command creates distribution files in dist/:
dist/index.js- CommonJS bundledist/index.esm.js- ES modules bundledist/index.d.ts- TypeScript definitions
Publish to NPM
npm run build
npm publishSupport
- Demo: Full working example in
/demofolder - Documentation:
- Issues: Report bugs on GitHub
- Examples: Check out the demo application for complete implementation examples
Advanced Features
EVM Smart Wallets
Supa SDK supports Privy Smart Wallets for EVM chains with gas sponsorship capabilities.
<SupaProvider
config={{
privyAppId: 'your-app-id',
nodeIdentifier: 'node',
smartWallets: {
enabled: true,
paymasterContext: {
mode: 'SPONSORED',
// ... paymaster configuration
}
}
}}
>
<YourApp />
</SupaProvider>See Smart Wallets documentation for detailed setup and usage.
Version: 0.1.0
License: MIT
React: 18+ / 19
TypeScript: 5+
Privy SDK: 3.3.0+
