Documentation Index
Fetch the complete documentation index at: https://docs.dynamic.xyz/docs/llms.txt
Use this file to discover all available pages before exploring further.
You can request USDC from someone using any of the following methods:
Subdomain
If you are on EVM, you can allow your users to generate their own unique subdomain for receiving funds. Set it up using the Global Identity guide.
Dynamic UI
Once a user has created their subdomain, they can find it in their user profile section of the Dynamic Widget and copy it to share with others, who can then send USDC to them.
Custom UI
Access the users subdomain by finding the VCs which have a name service, and then checking the nameService.name property:
const walletVCsWithNameService = user.verifiedCredentials.filter(
vc => vc.nameService?.name
);
const nameService = walletVCsWithNameService[0].nameService.name;
If you have multiple wallet VCs with a name service, you can filter by the primary wallet:
const { primaryWallet } = useDynamicContext();
const primaryWalletVC = walletVCsWithNameService.find(vc => vc.address === primaryWallet.address);
const nameService = primaryWalletVC?.nameService.name;
QR Code
Dynamic UI
In the “Deposit” section of the Dynamic Widget UI, you’ll see a “Receive by QR button”, which will reveal a QR code that anyone can scan. It will provide the wallet address for the sender to send USDC to.
Custom UI
Simply access the users wallet address using the primaryWallet.address property, and use any QR code generator to create a QR code for said address.
Wallet Address
You can also allow the user to copy and paste their wallet address directly rather than via providing a QR code.
Dynamic UI
In the same “Receive by QR” section, you’ll see a partial wallet address along with a “Copy” button.
Custom UI
Simply access the users wallet address using the primaryWallet.address property and provide it to the user.
Payment Links
You can build custom payment links that allow others to send you USDC with a preset amount. While Dynamic doesn’t provide a native payment link generator, you can create this functionality using the wallet’s direct transfer capabilities and URL parameters.
Building Payment Links
Create a component that generates shareable URLs with preset payment parameters:
import { useDynamicContext } from "@dynamic-labs/sdk-react-core";
import { useState } from "react";
const PaymentLinkGenerator = () => {
const [amount, setAmount] = useState("10");
const [paymentLink, setPaymentLink] = useState("");
const { primaryWallet } = useDynamicContext();
const generatePaymentLink = async () => {
if (!primaryWallet?.address) return;
// Get current network
const currentChainId = await getNetwork(primaryWallet.connector);
// Create a payment link with preset parameters
const baseUrl = window.location.origin;
const params = new URLSearchParams({
recipient: primaryWallet.address,
amount: amount,
token: "USDC",
network: currentChainId?.toString() || "1", // Use current network
});
const link = `${baseUrl}/pay?${params.toString()}`;
setPaymentLink(link);
};
const copyToClipboard = () => {
navigator.clipboard.writeText(paymentLink);
};
return (
<div>
<h3>Generate Payment Link</h3>
<div>
<label>Amount (USDC):</label>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
min="0.01"
step="0.01"
/>
</div>
<button onClick={generatePaymentLink}>Generate Link</button>
{paymentLink && (
<div>
<p>Payment Link:</p>
<input
type="text"
value={paymentLink}
readOnly
style={{ width: "100%", marginBottom: "10px" }}
/>
<button onClick={copyToClipboard}>Copy Link</button>
</div>
)}
</div>
);
};
Processing Payment Links
Create a payment page that handles incoming payment links and uses the wallet’s direct transfer capabilities:
import { useDynamicContext, getNetwork } from "@dynamic-labs/sdk-react-core";
import { isEthereumWallet } from "@dynamic-labs/ethereum";
import { parseUnits, erc20Abi } from 'viem';
import { useEffect, useState } from "react";
import { useSearchParams } from "react-router-dom";
const PaymentPage = () => {
const [searchParams] = useSearchParams();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState(false);
const { primaryWallet, user } = useDynamicContext();
// Extract parameters from URL
const recipientAddress = searchParams.get("recipient");
const amount = searchParams.get("amount");
const token = searchParams.get("token") || "USDC";
const network = searchParams.get("network") || "1";
const handlePayment = async () => {
if (!recipientAddress || !amount) {
setError("Invalid payment link parameters");
return;
}
// Check if user is authenticated
if (!user) {
setError("Please log in to complete this payment");
return;
}
if (!primaryWallet || !isEthereumWallet(primaryWallet)) {
setError("Wallet not connected or not EVM compatible");
return;
}
// Check if wallet is on the correct network for USDC
const expectedChainId = parseInt(network); // From URL params
const currentChainId = await getNetwork(primaryWallet.connector);
if (currentChainId !== expectedChainId) {
// Try to switch to the correct network
if (primaryWallet.connector.supportsNetworkSwitching()) {
try {
setError(`Switching to ${getNetworkName(expectedChainId)}...`);
await primaryWallet.switchNetwork(expectedChainId);
// Verify the switch was successful
const newChainId = await getNetwork(primaryWallet.connector);
if (newChainId !== expectedChainId) {
setError(`Failed to switch to ${getNetworkName(expectedChainId)}. Please switch manually.`);
return;
}
} catch (switchError: any) {
setError(`Failed to switch network: ${switchError.message}. Please switch to ${getNetworkName(expectedChainId)} manually.`);
return;
}
} else {
setError(`Please switch to ${getNetworkName(expectedChainId)}. Your wallet doesn't support automatic network switching.`);
return;
}
}
setIsLoading(true);
setError(null);
try {
const walletClient = await primaryWallet.getWalletClient();
// Get USDC contract address for the current network
const usdcAddress = getUSDCAddress(currentChainId);
// Convert amount to USDC units (6 decimals)
const amountInUnits = parseUnits(amount, 6);
// Send USDC transfer
const hash = await walletClient.writeContract({
address: usdcAddress as `0x${string}`,
abi: erc20Abi,
functionName: 'transfer',
args: [recipientAddress as `0x${string}`, amountInUnits],
});
setSuccess(true);
console.log("USDC transfer successful:", hash);
} catch (err: any) {
setError(err.message || "Payment failed");
} finally {
setIsLoading(false);
}
};
// Helper function to get USDC contract address for different networks
const getUSDCAddress = (chainId: number): string => {
const usdcAddresses: Record<number, string> = {
1: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // Ethereum mainnet
137: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", // Polygon
42161: "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8", // Arbitrum One
10: "0x7F5c764cBc14f9669B88837ca1490cCa17c31607", // Optimism
8453: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // Base
11155111: "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", // Sepolia testnet
};
const address = usdcAddresses[chainId];
if (!address) {
throw new Error(`USDC not supported on network ${chainId}`);
}
return address;
};
// Helper function to get network name
const getNetworkName = (chainId: number): string => {
const networks: Record<number, string> = {
1: "Ethereum Mainnet",
137: "Polygon",
42161: "Arbitrum One",
10: "Optimism",
8453: "Base",
11155111: "Sepolia Testnet",
};
return networks[chainId] || `Network ${chainId}`;
};
if (!recipientAddress || !amount) {
return <div>Invalid payment link</div>;
}
// Show login prompt if user is not authenticated
if (!user) {
return (
<div style={{ padding: '20px', border: '1px solid #ffc107', borderRadius: '8px', backgroundColor: '#fff3cd' }}>
<h2>Authentication Required</h2>
<p>You need to log in to complete this payment.</p>
<p><strong>Amount:</strong> {amount} {token}</p>
<p><strong>Recipient:</strong> {recipientAddress.substring(0, 8)}...{recipientAddress.substring(recipientAddress.length - 6)}</p>
<div style={{ marginTop: '20px' }}>
<p>Please connect your wallet to continue with the payment.</p>
<p style={{ fontSize: '14px', color: '#666' }}>
Once you log in, you'll be able to complete the payment securely.
</p>
</div>
</div>
);
}
if (success) {
return (
<div>
<h2>Payment Successful!</h2>
<p>You have successfully sent {amount} {token} to {recipientAddress.substring(0, 8)}...</p>
</div>
);
}
return (
<div>
<h2>Complete Payment</h2>
<p>Amount: {amount} {token}</p>
<p>Recipient: {recipientAddress.substring(0, 8)}...{recipientAddress.substring(recipientAddress.length - 6)}</p>
{isLoading && <p>Processing payment...</p>}
{error && <p style={{ color: "red" }}>Error: {error}</p>}
<button onClick={handlePayment} disabled={isLoading}>
{isLoading ? "Processing..." : "Pay Now"}
</button>
</div>
);
};
For more sophisticated payment links, you can include additional metadata:
const generateAdvancedPaymentLink = (amount: string, description?: string, reference?: string) => {
if (!primaryWallet?.address) return;
const baseUrl = window.location.origin;
const params = new URLSearchParams({
recipient: primaryWallet.address,
amount: amount,
token: "USDC",
network: "1",
...(description && { description }),
...(reference && { reference }),
timestamp: Date.now().toString(),
});
return `${baseUrl}/pay?${params.toString()}`;
};
// Example usage:
const link = generateAdvancedPaymentLink("25.50", "Coffee payment", "coffee-2024-01");
// Result: https://yourapp.com/pay?recipient=0x...&amount=25.50&token=USDC&network=1&description=Coffee%20payment&reference=coffee-2024-01×tamp=1704067200000
Security Considerations
When implementing payment links:
- Validate Parameters: Always validate the recipient address and amount on the server side
- Rate Limiting: Implement rate limiting to prevent abuse
- Expiration: Consider adding expiration timestamps to payment links
- HTTPS Only: Ensure payment links are only served over HTTPS
- Amount Limits: Set reasonable minimum and maximum amounts for payments