Complete Example
This example demonstrates a complete Silent Swap flow using the Core SDK in a Node.js backend environment. Silent Swap enables private, non-custodial cross-chain swaps.
Setup
import {
createSilentSwapClient,
createViemSigner,
parseTransactionRequestForViem,
createSignInMessage,
createEip712DocForOrder,
createEip712DocForWalletGeneration,
createHdFacilitatorGroupFromEntropy,
quoteResponseToEip712Document,
caip19FungibleEvmToken,
queryDepositCount,
GATEWAY_ABI,
PublicKeyArgGroups,
DeliveryMethod,
FacilitatorKeyType,
ENVIRONMENT,
hexToBytes,
type SilentSwapClient,
type EvmSigner,
type AuthResponse,
} from '@silentswap/sdk';
import { createWalletClient, http, publicActions, erc20Abi } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { avalanche, mainnet } from 'viem/chains';
import BigNumber from 'bignumber.js';
// Create wallet client
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`);
const client = createWalletClient({
account,
chain: avalanche,
transport: http(),
}).extend(publicActions);
// Create EVM signer
const signer = createViemSigner(account, client);
// Create SilentSwap client
const silentswap = createSilentSwapClient({
environment: ENVIRONMENT.MAINNET,
baseUrl: 'https://api.silentswap.com',
});Complete Silent Swap Flow
async function executeSilentSwap(
recipientAddress: `0x${string}`,
tokenAddress: `0x${string}`,
tokenAmount: string,
tokenDecimals: number = 6
) {
try {
// Step 1: Authenticate and derive entropy
console.log('Step 1: Authenticating with SilentSwap...');
const entropy = await authenticateAndDeriveEntropy(silentswap, signer);
console.log('✓ Authentication successful');
// Step 2: Create facilitator group
console.log('\nStep 2: Creating facilitator group...');
const depositCount = await queryDepositCount(account.address);
const group = await createHdFacilitatorGroupFromEntropy(
hexToBytes(entropy),
depositCount
);
console.log(`✓ Facilitator group created (deposit count: ${depositCount})`);
// Step 3: Get quote
console.log('\nStep 3: Requesting quote...');
const quoteResponse = await getQuote(
silentswap,
signer,
group,
recipientAddress,
tokenAddress,
tokenAmount,
tokenDecimals
);
console.log(`✓ Quote received (Order ID: ${quoteResponse.quoteId})`);
// Step 4: Sign authorizations and create order
console.log('\nStep 4: Signing authorizations and creating order...');
const orderResponse = await createOrder(
silentswap,
signer,
group,
quoteResponse
);
console.log(`✓ Order created (Order ID: ${orderResponse.response.orderId})`);
// Step 5: Execute deposit
console.log('\nStep 5: Executing deposit transaction...');
const depositHash = await executeDeposit(client, orderResponse);
console.log(`✓ Deposit transaction sent: ${depositHash}`);
// Step 6: Watch for completion
console.log('\nStep 6: Watching for order completion...');
await watchForCompletion(
client,
tokenAddress,
recipientAddress,
group,
tokenDecimals
);
return {
orderId: orderResponse.response.orderId,
depositHash,
quote: quoteResponse,
};
} catch (err) {
console.error('Silent Swap error:', err);
throw err;
}
}
// Helper: Authenticate and derive entropy
async function authenticateAndDeriveEntropy(
silentswap: SilentSwapClient,
signer: EvmSigner
): Promise<`0x${string}`> {
// Get nonce
const [nonceError, nonceResponse] = await silentswap.nonce(signer.address);
if (!nonceResponse || nonceError) {
throw new Error(`Failed to get nonce: ${nonceError?.type}: ${nonceError?.error}`);
}
// Create sign-in message
const signInMessage = createSignInMessage(
signer.address,
nonceResponse.nonce,
'silentswap.com'
);
// Sign message
const siweSignature = await signer.signEip191Message(signInMessage.message);
// Authenticate
const [authError, authResponse] = await silentswap.authenticate({
siwe: {
message: signInMessage.message,
signature: siweSignature,
},
});
if (!authResponse || authError) {
throw new Error(`Failed to authenticate: ${authError?.type}: ${authError?.error}`);
}
// Derive entropy from auth token
const eip712Doc = createEip712DocForWalletGeneration(authResponse.secretToken);
const entropy = await signer.signEip712TypedData(eip712Doc);
return entropy;
}
// Helper: Get quote
async function getQuote(
silentswap: SilentSwapClient,
signer: EvmSigner,
group: Awaited<ReturnType<typeof createHdFacilitatorGroupFromEntropy>>,
recipientAddress: `0x${string}`,
tokenAddress: `0x${string}`,
tokenAmount: string,
tokenDecimals: number
) {
// Derive viewer account
const viewer = await group.viewer();
const { publicKeyBytes: pk65_viewer } = viewer.exportPublicKey(
'*',
FacilitatorKeyType.SECP256K1
);
// Export public keys for facilitator group
const groupPublicKeys = await group.exportPublicKeys(1, [
...PublicKeyArgGroups.GENERIC,
]);
// Request quote
const [quoteError, quoteResponse] = await silentswap.quote({
signer: signer.address,
viewer: pk65_viewer,
outputs: [
{
method: DeliveryMethod.SNIP,
recipient: recipientAddress,
asset: caip19FungibleEvmToken(1, tokenAddress),
value: BigNumber(tokenAmount).shiftedBy(tokenDecimals).toFixed(0) as `${bigint}`,
facilitatorPublicKeys: groupPublicKeys[0],
},
],
});
if (quoteError || !quoteResponse) {
throw new Error(`Failed to get quote: ${quoteError?.type}: ${quoteError?.error}`);
}
return quoteResponse;
}
// Helper: Create order
async function createOrder(
silentswap: SilentSwapClient,
signer: EvmSigner,
group: Awaited<ReturnType<typeof createHdFacilitatorGroupFromEntropy>>,
quoteResponse: Awaited<ReturnType<typeof getQuote>>
) {
// Sign authorizations
const signedAuths = await Promise.all(
quoteResponse.authorizations.map(async (g_auth) => ({
...g_auth,
signature: await (async () => {
if ('eip3009_deposit' === g_auth.type) {
return await signer.signEip712TypedData(g_auth.eip712);
}
throw Error(`Authorization instruction type not implemented: ${g_auth.type}`);
})(),
}))
);
// Sign the order's EIP-712
const orderDoc = quoteResponseToEip712Document(quoteResponse);
const signedQuote = await signer.signEip712TypedData(orderDoc);
// Approve proxy authorizations
const facilitatorReplies = await group.approveProxyAuthorizations(
quoteResponse.facilitators,
{
proxyPublicKey: silentswap.proxyPublicKey,
}
);
// Place the order
const [orderError, orderResponse] = await silentswap.order({
quote: quoteResponse.quote,
quoteId: quoteResponse.quoteId,
authorizations: signedAuths,
eip712Domain: orderDoc.domain,
signature: signedQuote,
facilitators: facilitatorReplies,
});
if (orderError || !orderResponse) {
throw new Error(`Failed to place order: ${orderError?.type}: ${orderError?.error}`);
}
return orderResponse;
}
// Helper: Execute deposit
async function executeDeposit(
client: ReturnType<typeof createWalletClient>,
orderResponse: Awaited<ReturnType<typeof createOrder>>
) {
// Parse transaction request
const txRequestParams = parseTransactionRequestForViem(orderResponse.transaction);
// Send transaction
const hash = await client.sendTransaction(txRequestParams);
// Wait for confirmation
const txReceipt = await client.waitForTransactionReceipt({ hash });
console.log(
`Deposit confirmed: ${BigNumber(orderResponse.response.order.deposit)
.shiftedBy(-6)
.toFixed()} USDC at ${txReceipt.transactionHash}`
);
return hash;
}
// Helper: Watch for completion
async function watchForCompletion(
client: ReturnType<typeof createWalletClient>,
tokenAddress: `0x${string}`,
recipientAddress: `0x${string}`,
group: Awaited<ReturnType<typeof createHdFacilitatorGroupFromEntropy>>,
tokenDecimals: number
) {
// Get facilitator account for coin type 60 (ETH) at output index 0
const facilitator0Eth = await group.account('60', 0);
const facilitator0EthEvm = await facilitator0Eth.evmSigner();
// Create client for destination chain (Mainnet)
const destinationClient = createWalletClient({
chain: mainnet,
transport: http(),
}).extend(publicActions);
// Watch for ERC-20 transfer event
return new Promise<void>((resolve) => {
destinationClient.watchContractEvent({
address: tokenAddress,
abi: erc20Abi,
eventName: 'Transfer',
args: {
to: recipientAddress,
from: facilitator0EthEvm.address,
},
onLogs: (logs) => {
for (const log of logs) {
const { to, value } = log.args;
console.log(
`✓ Recipient ${to} received ${BigNumber(value!)
.shiftedBy(-tokenDecimals)
.toFixed()} tokens`
);
}
resolve();
},
});
});
}Usage Example
// Execute a Silent Swap: Send 10 USDC from Avalanche to Ethereum
const result = await executeSilentSwap(
account.address, // Recipient address
'0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC token address
'10', // Amount (will be converted using decimals)
6 // USDC has 6 decimals
);
console.log('Silent Swap completed:', result);Error Handling Example
async function safeSilentSwap(...args: Parameters<typeof executeSilentSwap>) {
try {
return await executeSilentSwap(...args);
} catch (err) {
if (err instanceof Error) {
if (err.message.includes('Failed to get nonce')) {
console.error('Nonce retrieval failed. Check your connection.');
} else if (err.message.includes('Failed to authenticate')) {
console.error('Authentication failed. Check your signature.');
} else if (err.message.includes('Failed to get quote')) {
console.error('Quote request failed. Check your parameters.');
} else if (err.message.includes('Failed to place order')) {
console.error('Order creation failed. Check your authorizations.');
} else {
console.error('Unexpected error:', err.message);
}
}
throw err;
}
}Backend Service Example
import express from 'express';
import { executeSilentSwap } from './silent-swap';
const app = express();
app.use(express.json());
app.post('/api/silent-swap', async (req, res) => {
try {
const { recipientAddress, tokenAddress, amount, decimals } = req.body;
const result = await executeSilentSwap(
recipientAddress,
tokenAddress,
amount,
decimals
);
res.json({
success: true,
orderId: result.orderId,
depositHash: result.depositHash,
});
} catch (err) {
res.status(500).json({
success: false,
error: err instanceof Error ? err.message : 'Unknown error',
});
}
});
app.listen(3000, () => {
console.log('Silent Swap API server running on port 3000');
});Key Concepts
Authentication Flow
- Get Nonce: Request a nonce from the SilentSwap API
- Sign Message: Create and sign a SIWE (Sign-In with Ethereum) message
- Authenticate: Send the signed message to get an auth token
- Derive Entropy: Sign the auth token to derive entropy for wallet generation
Facilitator Group
The facilitator group is a hierarchical deterministic (HD) wallet system that generates:
- Viewer Account: Used to observe order execution
- Facilitator Accounts: Used to execute the swap on different chains
Order Flow
- Quote: Request a quote with your desired outputs
- Sign Authorizations: Sign all required authorizations (deposits, meta-txs, etc.)
- Sign Order: Sign the order's EIP-712 typed data
- Approve Proxies: Approve proxy authorizations from facilitator accounts
- Place Order: Submit the complete order to the API
- Deposit: Execute the deposit transaction on the source chain
- Watch: Monitor for completion on the destination chain
Best Practices
- Secure Entropy Storage: Store entropy securely for wallet recovery
- Error Handling: Always handle errors at each step
- Transaction Monitoring: Monitor deposit transactions and watch for completion
- Nonce Management: Use the deposit count as the nonce for facilitator groups
- Chain Switching: Ensure you're on the correct chain before executing transactions
Next Steps
- Review Account Setup for detailed setup instructions
- Learn about Creating an Order for order creation details
- See Deposit for deposit transaction handling