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:

Send
Send

Direct transfers to wallet addresses or usernames

Request
Request

Request payments from others via shareable links

Invoices
Invoices

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:

  1. User enters recipient (address, username, or contact)
  2. User enters amount & selects currency
  3. System resolves recipient address
  4. User confirms transaction
  5. Transaction sent on-chain
  6. Transfer completed

Supported:

  • All ERC-20 tokens
  • Native tokens (ETH, MATIC, etc.)
  • All supported chains
same-chain-transfer.ts
// Send 100 USDC on Ethereum
const recipient = "0x..." // or "@username" or contact
const amount = "100"
const currency = "USDC"
const chainId = 1 // Ethereum

// Transaction created & sent

Cross-Chain Transfers

Transfer tokens between different blockchain networks using LI.FI bridge.

Flow:

  1. User enters recipient address
  2. User enters amount & selects currency
  3. User selects destination chain
  4. System finds bridge route via LI.FI
  5. User confirms bridge transaction
  6. Tokens bridged to destination chain
  7. Transfer completed on destination chain

Supported Routes:

Ethereum ↔ Base~2 min
Ethereum ↔ Arbitrum~10 min
Base ↔ Polygon~5 min
Arbitrum ↔ Optimism~3 min
cross-chain-transfer.ts
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

0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb

Username Resolution

Format: @username or username

username-resolution.ts
// User enters "@alice"
const username = "@alice"
const user = await getUserByUsername({ username: "alice" })
const recipientAddress = user.walletAddress

Contact Selection

Select from saved contacts with avatars, search, & quick selection.

contact-selection.ts
// User selects contact
const contact = {
  name: "Alice",
  walletAddress: "0x...",
  email: "alice@example.com"
}

const recipientAddress = contact.walletAddress

Request 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

  1. Navigate to Transfer page & select "Request Payment"
  2. Enter amount & currency
  3. System generates unique request link
  4. Link can be shared via email, messaging, etc.

Recipient Receives Request

  1. Recipient clicks request link
  2. Opens request page: /request/[identifier]/[amount]?currency=CODE
  3. Sees payment details & requester information
  4. Selects payment method
  5. Connects wallet & pays

Payment Processing

  1. Payment is processed on-chain
  2. Request status updated to "completed"
  3. Both parties receive confirmation

URL Format

Request Link Structure

/request/[identifier]/[amount]?currency=CODE

Parameters:

  • identifier - Username (with @) or wallet address
  • amount - 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=ETH

Creating Payment Requests

Via Transfer Interface

  1. Navigate to Transfer page
  2. Click "Request Payment" tab
  3. Enter amount & select currency
  4. Click "Generate Link"
  5. Copy & share the generated link

Programmatically

create-payment-request.ts
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

payment-request-schema.ts
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-processing.ts
// 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

create-request.ts
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-request.ts
// 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

update-status.ts
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

complete-request-flow.ts
// 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

  1. Navigate to Transfer → Invoice tab
  2. Add line items (name, description, quantity, unit price)
  3. Set due date
  4. Generate invoice
  5. Share invoice link

Link Format

/invoice/[public_id]

Invoice Payment Flow

  1. Customer receives invoice link
  2. Customer reviews invoice details
  3. Customer selects payment method & connects wallet
  4. Customer confirms payment
  5. Invoice status updated to "paid"

Transfer Execution

transfer-execution.ts
// 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

  1. Click "Add Contact" button
  2. Enter contact details (name, wallet address, email, notes)
  3. 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

Pending:Transaction submitted, waiting for confirmation
Confirming:Transaction confirmed, waiting for finality
Success:Transaction completed successfully
Failed:Transaction failed or reverted

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)
Was this helpful?