Code Samples & Starter Repos
Production-ready examples and boilerplate to accelerate your DUAL integration.
Quick Start Snippets
Five essential TypeScript snippets using @dual/sdk to get you started:
Authentication
import { DualClient } from '@dual/sdk';
const client = new DualClient({
apiKey: process.env.DUAL_API_KEY,
orgId: process.env.DUAL_ORG_ID,
});
const wallet = await client.wallets.login({
email: 'user@example.com',
password: 'secure-password',
});
console.log('Logged in:', wallet.id);
Create Template
const template = await client.templates.create({
name: 'io.example.product::v1',
description: 'A product token template',
private: {
serial_number: '',
manufacture_date: '',
price_usd: 0,
},
});
console.log('Template created:', template.id);
Mint Object
const object = await client.objects.create({
template_id: template.id,
owner_wallet_id: wallet.id,
properties: {
serial_number: 'SN-12345',
manufacture_date: '2026-03-14',
price_usd: 99.99,
},
});
console.log('Object minted:', object.id);
Transfer Object
const action = await client.actions.execute({
type: 'transfer',
object_id: object.id,
parameters: {
recipient_wallet_id: 'wallet-recipient-id',
notes: 'Product transferred',
},
});
console.log('Action executed:', action.id);
Query Objects
const objects = await client.objects.search({
template_id: template.id,
filters: {
owner_wallet_id: wallet.id,
status: 'active',
},
limit: 50,
});
console.log('Found objects:', objects.length);
Express.js API Backend
A sample Express route file that wraps DUAL operations:
import express from 'express';
import { DualClient } from '@dual/sdk';
const router = express.Router();
const dual = new DualClient({
apiKey: process.env.DUAL_API_KEY,
orgId: process.env.DUAL_ORG_ID,
});
// Create asset endpoint
router.post('/assets', async (req, res) => {
try {
const { template_id, properties, owner_id } = req.body;
const object = await dual.objects.create({
template_id,
properties,
owner_wallet_id: owner_id,
});
res.json({ success: true, asset_id: object.id });
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// List assets endpoint
router.get('/assets', async (req, res) => {
try {
const { owner_id, template_id } = req.query;
const objects = await dual.objects.search({
owner_wallet_id: owner_id,
template_id,
limit: 50,
});
res.json({ assets: objects });
} catch (error) {
res.status(400).json({ error: error.message });
}
});
// Transfer asset endpoint
router.post('/assets/:id/transfer', async (req, res) => {
try {
const { id } = req.params;
const { recipient_id } = req.body;
const action = await dual.actions.execute({
type: 'transfer',
object_id: id,
parameters: { recipient_wallet_id: recipient_id },
});
res.json({ success: true, action_id: action.id });
} catch (error) {
res.status(400).json({ error: error.message });
}
});
export default router;
Next.js Full-Stack App
Architecture overview: Pages for listing objects, viewing details, and an admin dashboard. Key API route and React component:
API Route: pages/api/assets/[id].ts
import { DualClient } from '@dual/sdk';
import { NextApiRequest, NextApiResponse } from 'next';
const dual = new DualClient({
apiKey: process.env.DUAL_API_KEY,
orgId: process.env.DUAL_ORG_ID,
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const { id } = req.query;
if (req.method === 'GET') {
try {
const object = await dual.objects.get(id as string);
const activity = await dual.objects.getActivity(id as string);
res.status(200).json({ object, activity });
} catch (error) {
res.status(404).json({ error: 'Asset not found' });
}
}
}
React Component: components/AssetDetail.tsx
import { useEffect, useState } from 'react';
export default function AssetDetail({ assetId }: { assetId: string }) {
const [asset, setAsset] = useState(null);
const [activity, setActivity] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch("/api/assets/" + assetId)
.then(res => res.json())
.then(data => {
setAsset(data.object);
setActivity(data.activity);
setLoading(false);
});
}, [assetId]);
if (loading) return Loading...;
return (
{asset.template_id}
Owner: {asset.owner_wallet_id}
Created: {new Date(asset.created_at).toLocaleDateString()}
Activity Log
{activity.map(act => (
- {act.type} - {act.timestamp}
))}
);
}
Webhook Handler
Express middleware that validates and processes DUAL webhook payloads with signature verification:
import crypto from 'crypto';
import express from 'express';
const WEBHOOK_SECRET = process.env.DUAL_WEBHOOK_SECRET;
export function verifyWebhookSignature(
payload: string,
signature: string
): boolean {
const expected = crypto
.createHmac('sha256', WEBHOOK_SECRET!)
.update(payload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(signature),
Buffer.from(expected)
);
}
export function webhookHandler(
req: express.Request,
res: express.Response
) {
const signature = req.headers['x-dual-signature'] as string;
const payload = JSON.stringify(req.body);
if (!verifyWebhookSignature(payload, signature)) {
return res.status(401).json({ error: 'Invalid signature' });
}
const { event_type, data } = req.body;
switch (event_type) {
case 'object.created':
console.log('Object created:', data.object_id);
// Handle creation
break;
case 'action.executed':
console.log('Action executed:', data.action_id);
// Handle action
break;
case 'transfer.completed':
console.log('Transfer complete:', data.object_id);
// Update your database
break;
}
res.status(200).json({ received: true });
}
CLI Scripting
Bash script for bulk minting 100 objects from a CSV file using the DUAL CLI:
#!/bin/bash
set -e
API_KEY="${DUAL_API_KEY}"
ORG_ID="${DUAL_ORG_ID}"
TEMPLATE_ID="io.example.product::v1"
CSV_FILE="products.csv"
echo "Starting bulk mint..."
while IFS=',' read -r name sku price; do
if [ "$name" != "name" ]; then
echo "Minting: $name"
dual objects create --template-id "$TEMPLATE_ID" --property name="$name" --property sku="$sku" --property price_usd="$price" --api-key "$API_KEY" --org-id "$ORG_ID"
# Rate limit: 10 per second
sleep 0.1
fi
done < "$CSV_FILE"
echo "Bulk mint complete!"
GitHub Starter Repos
Jump-start your development with these production-ready templates:
- dual-starter-express — Full Express.js backend with DUAL integration, authentication, and webhook handling. View repo
- dual-starter-nextjs — Next.js full-stack app with API routes, React components, and server-side rendering. View repo
- dual-starter-python — Python Flask backend for non-Node environments. View repo
- dual-sdk-examples — 20+ standalone examples covering all SDK features. View repo