Transfer
Cross-chain payments & transfers
Send & receive payments across chains, create payment requests with shareable links, & generate professional invoices for your business.
Overview
Torque provides multiple ways to send & receive payments:
Direct transfers to wallet addresses or usernames
Request payments from others via shareable links
Create & send professional invoices
Key Features
- Multiple Transfer Types: Send, Request, Invoice
- Address Resolution: Wallet addresses, usernames (@username), contacts
- Same-Chain Transfers: Direct transfers on same network
- Cross-Chain Transfers: Bridge transfers via LI.FI
- Contact Management: Save & manage recipient contacts
- QR Code Support: Generate QR codes for addresses
Send Transfers
Same-Chain Transfers
Direct token transfers on the same blockchain network. No bridge is required.
Flow:
- User enters recipient (address, username, or contact)
- User enters amount & selects currency
- System resolves recipient address
- User confirms transaction
- Transaction sent on-chain
- Transfer completed
Supported:
- All ERC-20 tokens
- Native tokens (ETH, MATIC, etc.)
- All supported chains
// Send 100 USDC on Ethereum
const recipient = "0x..." // or "@username" or contact
const amount = "100"
const currency = "USDC"
const chainId = 1 // Ethereum
// Transaction created & sentCross-Chain Transfers
Transfer tokens between different blockchain networks using LI.FI bridge.
Flow:
- User enters recipient address
- User enters amount & selects currency
- User selects destination chain
- System finds bridge route via LI.FI
- User confirms bridge transaction
- Tokens bridged to destination chain
- Transfer completed on destination chain
Supported Routes:
import { getLifiRoute, executeLifiRoute } from '@/lib/lifi'
// Bridge 100 USDC from Ethereum to Base
const route = await getLifiRoute({
fromChain: 1, // Ethereum
toChain: 8453, // Base
fromToken: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', // USDC
toToken: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', // USDC on Base
amount: '1000000000', // 1000 USDC
fromAddress: userAddress,
})
// Execute bridge transfer
const receipt = await executeLifiRoute({
route,
wallet,
})Address Resolution
Wallet Address
Format: 0x followed by 40 hexadecimal characters
0x742d35Cc6634C0532925a3b844Bc9e7595f0bEbUsername Resolution
Format: @username or username
// User enters "@alice"
const username = "@alice"
const user = await getUserByUsername({ username: "alice" })
const recipientAddress = user.walletAddressContact Selection
Select from saved contacts with avatars, search, & quick selection.
// User selects contact
const contact = {
name: "Alice",
walletAddress: "0x...",
email: "alice@example.com"
}
const recipientAddress = contact.walletAddressRequest Payments
Torque's request payment feature allows users to create payment requests & share them with others. Recipients can pay the request directly through a simple interface, supporting multiple payment methods.
Key Features
- Shareable Links: Generate unique payment request links
- Username/Address Support: Request payments using username or wallet address
- Multiple Payment Methods: Support for various crypto tokens
- Expiration Handling: Requests can expire after a set time (default 7 days)
- Status Tracking: Track payment request status (pending, completed, expired, cancelled)
- Email Notifications: Recipients can be notified via email
How It Works
Request Creation Flow
- Navigate to Transfer page & select "Request Payment"
- Enter amount & currency
- System generates unique request link
- Link can be shared via email, messaging, etc.
Recipient Receives Request
- Recipient clicks request link
- Opens request page:
/request/[identifier]/[amount]?currency=CODE - Sees payment details & requester information
- Selects payment method
- Connects wallet & pays
Payment Processing
- Payment is processed on-chain
- Request status updated to "completed"
- Both parties receive confirmation
URL Format
Request Link Structure
/request/[identifier]/[amount]?currency=CODEParameters:
identifier- Username (with @) or wallet addressamount- Requested amount (e.g., 100.00)currency- Currency code (e.g., USDC, USDT, ETH)
Examples
/request/@alice/100.00?currency=USDC
/request/0x1234...5678/50.00?currency=USDT
/request/@bob/0.1?currency=ETHCreating Payment Requests
Via Transfer Interface
- Navigate to Transfer page
- Click "Request Payment" tab
- Enter amount & select currency
- Click "Generate Link"
- Copy & share the generated link
Programmatically
import { createPaymentRequest } from '@/convex/payments'
// Create payment request
const requestId = await createPaymentRequest({
paymentId: generateUniqueId(), // Unique payment ID
recipientId: username || walletAddress, // Recipient identifier
amount: "100.00", // Amount as string
currency: "USDC", // Currency code
description: "Payment for services", // Optional description
recipientAddress: walletAddress, // Optional wallet address
metadata: {
network: "ethereum",
chainId: 1,
timestamp: Date.now()
},
expiresAt: Date.now() + (7 * 24 * 60 * 60 * 1000) // 7 days
})
// Generate request link
const baseUrl = "https://app.torque.fi"
const identifier = username ? `@${username}` : walletAddress
const link = `${baseUrl}/request/${encodeURIComponent(identifier)}/${amount}?currency=${currency}`Payment Request Schema
interface PaymentRequest {
_id: string // Convex document ID
paymentId: string // Unique payment identifier
recipientId: string // Recipient identifier (username or address)
amount: string // Amount as string (e.g., "100.00")
currency: string // Currency code (e.g., "USDC")
description?: string // Optional description
status: "pending" | "completed" | "expired" | "cancelled"
recipientAddress?: string // Wallet address of recipient
metadata?: {
network?: string
chainId?: number
timestamp?: number
[key: string]: any
}
expiresAt?: number // Expiration timestamp
createdAt: number // Creation timestamp
updatedAt: number // Last update timestamp
}Status Values
- pending - Request is active & awaiting payment
- completed - Payment has been completed
- expired - Request has expired
- cancelled - Request was cancelled by creator
Payment Methods
Payment requests support multiple crypto payment methods based on requester's accepted currencies, chain support, & token availability.
Payment Method Selection
Recipients can choose from available payment methods:
- Crypto Tokens: USDC, USDT, DAI, ETH, etc.
- Chain-Specific: Only tokens available on current chain
// Payment flow
const handlePayment = async () => {
// 1. Validate inputs
if (!isConnected || !paymentMethod || !recipientAddress) {
return
}
// 2. Extract currency from payment method
const currency = paymentMethod.replace('crypto-', '').toUpperCase()
// 3. Get token details
const tokenAddress = getTokenAddress(currency, chainId)
const tokenDecimals = getTokenDecimals(currency, chainId)
// 4. Create transfer transaction
const amount = parseUnits(requestedAmount, tokenDecimals)
const data = encodeFunctionData({
abi: ERC20_ABI,
functionName: 'transfer',
args: [recipientAddress, amount]
})
// 5. Send transaction
await sendTransaction({
to: tokenAddress,
data,
value: BigInt(0)
})
// 6. Update payment request status
await updatePaymentRequestStatus({
paymentId: paymentRequest.paymentId,
status: "completed",
recipientAddress: address,
transactionHash: txHash
})
}Expiration
Default: 7 days from creation
Configurable: Can be set when creating request
No Expiration: Set expiresAt to undefined for no expiration
Expiration Behavior
- Before Expiry: Request is active, recipients can pay, creator can cancel
- After Expiry: Request status changes to "expired", recipients cannot pay
API Integration
Create Payment Request
const requestId = await createPaymentRequest({
paymentId: string, // Unique payment ID
recipientId: string, // Username or wallet address
amount: string, // Amount as string
currency: string, // Currency code
description?: string, // Optional
recipientAddress?: string, // Optional wallet address
metadata?: object, // Optional metadata
expiresAt?: number // Optional expiration timestamp
})Get Payment Request
// Get by paymentId & recipientId
const request = await getPaymentRequest({
paymentId: string,
recipientId: string
})
// Get by paymentId only
const request = await getPaymentRequestById({
paymentId: string
})
// Get by identifier, amount, & currency
const request = await getPaymentRequestByHumanId({
recipientId: string, // Username or address
amount: string, // Amount
currency: string // Currency code
})Update Payment Request Status
await updatePaymentRequestStatus({
paymentId: string,
status: "completed" | "expired" | "cancelled",
recipientAddress?: string, // For completed status
transactionHash?: string, // For completed status
metadata?: object // Additional metadata
})Complete Request Flow
// 1. Create payment request
const paymentId = generateUniqueId()
const requestId = await createPaymentRequest({
paymentId,
recipientId: "@alice",
amount: "100.00",
currency: "USDC",
description: "Payment for services rendered",
recipientAddress: "0x...", // Optional
expiresAt: Date.now() + (7 * 24 * 60 * 60 * 1000) // 7 days
})
// 2. Generate request link
const link = `https://app.torque.fi/request/@alice/100.00?currency=USDC`
// 3. Share link (email, messaging, etc.)
// 4. Recipient pays (on request page)
// Payment is processed automatically
// 5. Status updated to "completed"Invoices
Businesses can create professional invoices & send them to customers. Customers pay invoices through a public invoice page.
Invoice Creation Flow
- Navigate to Transfer → Invoice tab
- Add line items (name, description, quantity, unit price)
- Set due date
- Generate invoice
- Share invoice link
Link Format
/invoice/[public_id]Invoice Payment Flow
- Customer receives invoice link
- Customer reviews invoice details
- Customer selects payment method & connects wallet
- Customer confirms payment
- Invoice status updated to "paid"
Transfer Execution
// 1. Resolve recipient
let recipientAddress: string
if (recipient.startsWith('0x')) {
// Direct address
recipientAddress = recipient
} else if (recipient.startsWith('@')) {
// Username lookup
const username = recipient.slice(1)
const user = await getUserByUsername({ username })
recipientAddress = user.walletAddress
} else if (selectedContact) {
// Contact selection
recipientAddress = selectedContact.walletAddress
}
// 2. Get token details
const tokenAddress = getTokenAddress(currency, chainId)
const tokenDecimals = getTokenDecimals(currency, chainId)
// 3. Create transaction
const amountInWei = parseUnits(amount.toString(), tokenDecimals)
const data = encodeFunctionData({
abi: ERC20_ABI,
functionName: 'transfer',
args: [recipientAddress, amountInWei]
})
// 4. Send transaction
await sendTransaction({
to: tokenAddress,
data,
value: BigInt(0) // ERC20 transfers don't require ETH
})Contact Management
Adding Contacts
- Click "Add Contact" button
- Enter contact details (name, wallet address, email, notes)
- Save contact
Features
- Contact avatars
- Quick selection
- Search contacts
- Edit/delete contacts
Transaction Tracking
All transfers are tracked with the following information:
- Transaction hash
- Recipient address
- Amount & currency
- Chain ID
- Timestamp
- Status (pending, confirming, success, failed)
Transaction Status
Error Handling
"Connect wallet"
Wallet not connected. Solution: Connect wallet first
"Invalid address or username"
Address format invalid or username not found. Solution: Verify address format or username
"Insufficient balance"
Not enough tokens for transfer. Solution: Add funds to wallet
"Token not supported on this network"
Token not available on current chain. Solution: Switch to supported chain
"Bridge route not found"
No bridge route available for token pair. Solution: Try different token or route
Best Practices
- Verify Recipient: Always verify recipient address before sending
- Check Balance: Ensure sufficient balance before transfer
- Gas Settings: Use appropriate gas settings for network
- Network Selection: Ensure you're on correct network
- Token Approval: Ensure token approval for DEX trades (if needed)