Deposit Widget
Minimal deposit-only widget using the lightweight shield module (~150KB).
Use Case
Perfect for:
- Payment links
- Donation buttons
- Onramp integration
- Embedded deposit forms
HTML + Vanilla JS
html
<!DOCTYPE html>
<html>
<head>
<title>Deposit Widget</title>
<script type="module">
import { shieldTo, DEPLOYMENTS } from 'https://esm.sh/@zkprivacy/sdk/shield';
import { createWalletClient, custom, parseUnits } from 'https://esm.sh/viem';
window.deposit = async function() {
const recipient = document.getElementById('recipient').value;
const amount = document.getElementById('amount').value;
const status = document.getElementById('status');
if (!window.ethereum) {
status.textContent = 'Please install MetaMask';
return;
}
try {
status.textContent = 'Connecting wallet...';
const [account] = await window.ethereum.request({
method: 'eth_requestAccounts'
});
const walletClient = createWalletClient({
transport: custom(window.ethereum),
});
status.textContent = 'Processing deposit...';
const result = await shieldTo(
{
rpcUrl: DEPLOYMENTS.remote.rpcUrl,
poolAddress: DEPLOYMENTS.remote.pool,
},
{
recipient,
amount,
tokenAddress: DEPLOYMENTS.remote.tokens.zkUSD,
walletClient,
}
);
status.textContent = `Success! TX: ${result.txHash.slice(0, 20)}...`;
} catch (error) {
status.textContent = `Error: ${error.message}`;
}
}
</script>
</head>
<body>
<h1>Deposit USDC</h1>
<input id="recipient" placeholder="Recipient (zks1...)" style="width: 100%">
<br><br>
<input id="amount" placeholder="Amount (e.g., 100)" type="number">
<br><br>
<button onclick="deposit()">Deposit</button>
<p id="status"></p>
</body>
</html>React Component
tsx
// components/DepositWidget.tsx
import { useState } from 'react';
import { shieldTo, DEPLOYMENTS } from '@zkprivacy/sdk/shield';
import { createWalletClient, custom } from 'viem';
interface DepositWidgetProps {
recipient: string;
token?: 'zkUSD' | 'zkEUR' | 'zkPLN';
onSuccess?: (txHash: string) => void;
onError?: (error: Error) => void;
}
export function DepositWidget({
recipient,
token = 'zkUSD',
onSuccess,
onError,
}: DepositWidgetProps) {
const [amount, setAmount] = useState('');
const [status, setStatus] = useState<'idle' | 'connecting' | 'depositing' | 'success' | 'error'>('idle');
const [txHash, setTxHash] = useState<string | null>(null);
const handleDeposit = async () => {
if (!amount || !window.ethereum) return;
try {
setStatus('connecting');
await window.ethereum.request({ method: 'eth_requestAccounts' });
const walletClient = createWalletClient({
transport: custom(window.ethereum),
});
setStatus('depositing');
const result = await shieldTo(
{
rpcUrl: DEPLOYMENTS.remote.rpcUrl,
poolAddress: DEPLOYMENTS.remote.pool,
},
{
recipient,
amount,
tokenAddress: DEPLOYMENTS.remote.tokens[token],
walletClient,
}
);
setTxHash(result.txHash);
setStatus('success');
onSuccess?.(result.txHash);
} catch (error) {
setStatus('error');
onError?.(error as Error);
}
};
return (
<div className="deposit-widget">
<h3>Deposit {token}</h3>
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Amount"
disabled={status === 'connecting' || status === 'depositing'}
/>
<button
onClick={handleDeposit}
disabled={!amount || status === 'connecting' || status === 'depositing'}
>
{status === 'connecting' && 'Connecting...'}
{status === 'depositing' && 'Depositing...'}
{status === 'idle' && 'Deposit'}
{status === 'success' && 'Done!'}
{status === 'error' && 'Try Again'}
</button>
{txHash && (
<a href={`https://etherscan.io/tx/${txHash}`} target="_blank">
View Transaction
</a>
)}
</div>
);
}Payment Link Generator
Generate payment links for recipients:
typescript
// utils/payment-link.ts
export function generatePaymentLink(
recipient: string,
amount?: string,
token?: string,
): string {
const baseUrl = 'https://pay.yourapp.com';
const params = new URLSearchParams();
params.set('to', recipient);
if (amount) params.set('amount', amount);
if (token) params.set('token', token);
return `${baseUrl}?${params.toString()}`;
}
// Usage
const link = generatePaymentLink('zks1abc...', '100', 'zkUSD');
// https://pay.yourapp.com?to=zks1abc...&amount=100&token=zkUSDPayment Page
tsx
// pages/pay.tsx
import { useSearchParams } from 'react-router-dom';
import { DepositWidget } from '../components/DepositWidget';
export function PaymentPage() {
const [params] = useSearchParams();
const recipient = params.get('to');
const amount = params.get('amount');
const token = params.get('token') as 'zkUSD' | 'zkEUR' | 'zkPLN';
if (!recipient) {
return <p>Invalid payment link</p>;
}
return (
<div>
<h1>Send Payment</h1>
<p>To: {recipient.slice(0, 20)}...</p>
{amount && <p>Amount: {amount} {token || 'zkUSD'}</p>}
<DepositWidget
recipient={recipient}
token={token}
onSuccess={(tx) => {
alert('Payment sent! TX: ' + tx);
}}
/>
</div>
);
}Bundle Size Comparison
| Import | Size |
|---|---|
@zkprivacy/sdk/shield | ~150KB |
@zkprivacy/sdk | ~2MB |
For deposit-only widgets, always use the shield submodule!