Documentation>Examples

Examples

Real-world code examples for common use cases

Wallet Adapter — Provider Setup
One-time setup: wrap your app with the gasless submitter. After this, every signAndSubmitTransaction call is automatically routed through SmoothSend.
providers.tsx
TypeScript
1import { SmoothSendTransactionSubmitter } from '@smoothsend/sdk';
2import { AptosWalletAdapterProvider } from '@aptos-labs/wallet-adapter-react';
3import { Network } from '@aptos-labs/ts-sdk';
4 
5const submitter = new SmoothSendTransactionSubmitter({
6 apiKey: process.env.NEXT_PUBLIC_SMOOTHSEND_API_KEY!,
7 network: 'mainnet', // or 'testnet'
8});
9 
10export function Providers({ children }: { children: React.ReactNode }) {
11 return (
12 <AptosWalletAdapterProvider
13 dappConfig={{
14 network: Network.MAINNET,
15 transactionSubmitter: submitter,
16 }}
17 >
18 {children}
19 </AptosWalletAdapterProvider>
20 );
21}
Wallet Adapter — Sending Any Transaction
After the provider is set up, your existing wallet code works unchanged. The user pays no gas.
TransferButton.tsx
TypeScript
1import { useWallet } from '@aptos-labs/wallet-adapter-react';
2 
3function TransferAPT() {
4 const { signAndSubmitTransaction, account } = useWallet();
5 
6 const handleTransfer = async () => {
7 // Works for APT transfers, contract calls, NFT mints — anything
8 const result = await signAndSubmitTransaction({
9 data: {
10 function: '0x1::coin::transfer',
11 typeArguments: ['0x1::aptos_coin::AptosCoin'],
12 functionArguments: [
13 '0xRecipientAddress',
14 100_000_000, // 1 APT (8 decimals)
15 ],
16 },
17 });
18 console.log('Tx hash:', result.hash);
19 };
20 
21 return <button onClick={handleTransfer}>Send 1 APT (gasless)</button>;
22}
useSmoothSend — Per-Function Gasless Routing
Some functions sponsored, some not — the hook routes automatically based on your Sponsorship Rules. No need to put transactionSubmitter in the wallet provider.
TodoList.tsx
TypeScript
1import { useSmoothSend, SmoothSendTransactionSubmitter } from '@smoothsend/sdk';
2import { useWallet } from '@aptos-labs/wallet-adapter-react';
3 
4const MODULE = '0xYourModuleAddress';
5const SMOOTHSEND_KEY = process.env.NEXT_PUBLIC_SMOOTHSEND_API_KEY!;
6 
7// Create once outside the component — avoids re-creating on every render
8const submitter = new SmoothSendTransactionSubmitter({
9 apiKey: SMOOTHSEND_KEY,
10 network: 'mainnet',
11});
12 
13function TodoList({ todos }: { todos: Array<{ id: number; content: string }> }) {
14 const { account } = useWallet();
15 // Drop-in for useWallet().signAndSubmitTransaction
16 const { signAndSubmitTransaction } = useSmoothSend(submitter);
17 
18 // 'create_todo' is NOT in Sponsorship Rules → user pays gas (~0.001 APT)
19 const handleCreate = async (content: string) => {
20 const result = await signAndSubmitTransaction({
21 data: {
22 function: `${MODULE}::todolist::create_todo`,
23 functionArguments: [content],
24 },
25 });
26 console.log('Created:', result.hash);
27 };
28 
29 // 'delete_todo' IS in Sponsorship Rules → gasless, user pays 0 APT
30 const handleDelete = async (id: number) => {
31 const result = await signAndSubmitTransaction({
32 data: {
33 function: `${MODULE}::todolist::delete_todo`,
34 functionArguments: [id],
35 },
36 });
37 console.log('Deleted:', result.hash);
38 };
39 
40 return (
41 <div>
42 <button onClick={() => handleCreate('New task')}>Create (pays gas)</button>
43 {todos.map((t) => (
44 <button key={t.id} onClick={() => handleDelete(t.id)}>
45 Delete "{t.content}" (gasless)
46 </button>
47 ))}
48 </div>
49 );
50}
Script Composer — USDC Transfer (Fee-in-Token)
Transfer USDC on mainnet — the relayer fee (~$0.01) is deducted from the USDC being sent. No APT or credits needed.
USDCTransfer.tsx
TypeScript
1import { ScriptComposerClient } from '@smoothsend/sdk';
2import { Deserializer, SimpleTransaction } from '@aptos-labs/ts-sdk';
3import { useWallet } from '@aptos-labs/wallet-adapter-react';
4 
5const client = new ScriptComposerClient({
6 apiKey: process.env.NEXT_PUBLIC_SMOOTHSEND_API_KEY!,
7 network: 'mainnet',
8});
9 
10// USDC mainnet asset address on Aptos
11const USDC = '0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b';
12 
13function USDCTransfer() {
14 const { account, signTransaction } = useWallet();
15 
16 const transfer = async (toAddress: string, amountUsdc: number) => {
17 // Step 1 — build (fee shown before signing)
18 const build = await client.buildTransfer({
19 sender: account!.address,
20 recipient: toAddress,
21 amount: String(amountUsdc * 1_000_000), // USDC has 6 decimals
22 assetType: USDC,
23 decimals: 6,
24 symbol: 'USDC',
25 });
26 console.log('Fee:', build.feeBreakdown.formatted.fee); // e.g. "0.010000 USDC"
27 
28 // Step 2 — sign with the connected wallet
29 const txBytes = new Uint8Array(build.transactionBytes);
30 const tx = SimpleTransaction.deserialize(new Deserializer(txBytes));
31 const signed = await signTransaction({ transactionOrPayload: tx });
32 
33 // Step 3 — submit
34 const { txHash } = await client.submitSignedTransaction({
35 transactionBytes: Array.from(txBytes),
36 authenticatorBytes: Array.from(signed.authenticator.bcsToBytes()),
37 });
38 console.log('Tx hash:', txHash);
39 };
40 
41 return (
42 <button onClick={() => transfer('0xRecipientAddress', 10)}>
43 Send 10 USDC (fee-in-token)
44 </button>
45 );
46}
Script Composer — Backend / Server-Side (Node.js)
For backend products (bots, WhatsApp flows, Keyless). Your server builds the transaction, your signing service produces an authenticator, then the server submits it. Use a sk_nogas_* key on the backend.
script-composer-backend.ts
TypeScript
1import { ScriptComposerClient } from '@smoothsend/sdk';
2 
3const client = new ScriptComposerClient({
4 apiKey: process.env.SMOOTHSEND_API_KEY!, // use sk_nogas_* on backend
5 network: 'mainnet',
6});
7 
8// USDC mainnet asset address on Aptos
9const USDC = '0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b';
10 
11async function sendUSDCViaBackend(sender: string, recipient: string, amountUsdc: number) {
12 // Step 1 — build on the server
13 const build = await client.buildTransfer({
14 sender,
15 recipient,
16 amount: String(amountUsdc * 1_000_000), // USDC has 6 decimals
17 assetType: USDC,
18 decimals: 6,
19 symbol: 'USDC',
20 });
21 
22 // Step 2 — sign using your own signer (Aptos Keyless / social login / custodial key store)
23 // This MUST return authenticatorBytes compatible with the built transactionBytes.
24 const signed = await yourSigningService.sign({
25 transactionBytes: build.transactionBytes,
26 sender,
27 });
28 
29 // Step 3 — submit from the server
30 const { txHash } = await client.submitSignedTransaction({
31 transactionBytes: build.transactionBytes,
32 authenticatorBytes: signed.authenticatorBytes,
33 });
34 
35 return txHash;
36}
Script Composer — Show Fee Before Transfer
Estimate the relayer fee and display it to users before they sign.
FeePreview.tsx
TypeScript
1import { ScriptComposerClient } from '@smoothsend/sdk';
2import { useState } from 'react';
3 
4async function getUSDCFee(client: ScriptComposerClient, amount: string) {
5 const estimate = await client.estimateFee({
6 amount,
7 assetType: '0xbae207659db88bea0cbead6da0ed00aac12edcdda169e591cd41c94180b46f3b',
8 decimals: 6,
9 symbol: 'USDC',
10 });
11 return estimate.formatted.fee; // e.g. "0.010000 USDC"
12}
13 
14function TransferForm() {
15 const [fee, setFee] = useState<string | null>(null);
16 
17 const handleAmountChange = async (amount: string) => {
18 if (!amount) return;
19 const feeDisplay = await getUSDCFee(client, String(Number(amount) * 1_000_000));
20 setFee(feeDisplay);
21 };
22 
23 return (
24 <div>
25 <input
26 type="number"
27 placeholder="Amount USDC"
28 onChange={(e) => handleAmountChange(e.target.value)}
29 />
30 {fee && <p>Relayer fee: {fee}</p>}
31 </div>
32 );
33}
Error Handling
Handle SmoothSend-specific errors and show meaningful messages to users.
handleTx.ts
TypeScript
1import { SmoothSendError } from '@smoothsend/sdk';
2 
3async function handleTransaction(tx: () => Promise<{ hash: string }>) {
4 try {
5 const result = await tx();
6 toast.success(`Submitted: ${result.hash}`);
7 } catch (error) {
8 if (error instanceof SmoothSendError) {
9 const messages: Record<number, string> = {
10 401: 'Invalid API key — check your configuration.',
11 402: 'Insufficient credits — top up your dashboard.',
12 429: 'Rate limit exceeded — please wait a moment.',
13 };
14 toast.error(messages[error.statusCode] ?? `Error: ${error.message}`);
15 } else {
16 toast.error('Wallet or network error. Please try again.');
17 }
18 }
19}