DeFi Integration
Integrate Veridex passkey wallets with DeFi protocols for gasless, user-friendly DeFi.
🏦 Try it yourself: Clone and run the full DeFi vault example:
git clone https://github.com/Veridex-Protocol/examples.git cd examples && npm install && npm run integration:defiView source on GitHub (opens in a new tab) · Browse all examples
Overview
Enable your users to:
- Swap tokens without gas
- Provide liquidity with one click
- Stake assets using FaceID/TouchID
- Manage DeFi positions seamlessly
Token Swaps
Uniswap V3 Integration
import { createSDK } from '@veridex/sdk';
import { ethers } from 'ethers';
const sdk = createSDK('base', {
network: 'mainnet',
relayerUrl: 'https://relayer.veridex.network',
});
const UNISWAP_ROUTER = '0x2626664c2603336E57B271c5C0b26F421741e481'; // Base
interface SwapParams {
tokenIn: string;
tokenOut: string;
amountIn: bigint;
slippagePercent: number;
}
async function swapTokens({ tokenIn, tokenOut, amountIn, slippagePercent }: SwapParams) {
// 1. Approve router
await sdk.approveViaRelayer({
token: tokenIn,
spender: UNISWAP_ROUTER,
amount: amountIn,
});
// 2. Get quote and calculate minimum out
const quote = await getQuote(tokenIn, tokenOut, amountIn);
const minAmountOut = quote * BigInt(100 - slippagePercent) / 100n;
// 3. Execute swap
const routerInterface = new ethers.Interface([
'function exactInputSingle((address tokenIn, address tokenOut, uint24 fee, address recipient, uint256 amountIn, uint256 amountOutMinimum, uint160 sqrtPriceLimitX96)) returns (uint256 amountOut)'
]);
const tx = await sdk.executeViaRelayer({
target: UNISWAP_ROUTER,
data: routerInterface.encodeFunctionData('exactInputSingle', [{
tokenIn,
tokenOut,
fee: 3000, // 0.3%
recipient: sdk.getVaultAddress(),
amountIn,
amountOutMinimum: minAmountOut,
sqrtPriceLimitX96: 0,
}]),
});
return tx;
}Swap Component
import { useState } from 'react';
import { parseUnits, formatUnits } from 'ethers';
const TOKENS = {
ETH: { address: '0x4200000000000000000000000000000000000006', decimals: 18 },
USDC: { address: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', decimals: 6 },
DAI: { address: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb', decimals: 18 },
};
export function SwapWidget() {
const [tokenIn, setTokenIn] = useState('USDC');
const [tokenOut, setTokenOut] = useState('ETH');
const [amountIn, setAmountIn] = useState('');
const [quote, setQuote] = useState<string | null>(null);
const [loading, setLoading] = useState(false);
const handleQuote = async () => {
if (!amountIn) return;
const amount = parseUnits(amountIn, TOKENS[tokenIn].decimals);
const quoteAmount = await getQuote(
TOKENS[tokenIn].address,
TOKENS[tokenOut].address,
amount
);
setQuote(formatUnits(quoteAmount, TOKENS[tokenOut].decimals));
};
const handleSwap = async () => {
setLoading(true);
try {
await swapTokens({
tokenIn: TOKENS[tokenIn].address,
tokenOut: TOKENS[tokenOut].address,
amountIn: parseUnits(amountIn, TOKENS[tokenIn].decimals),
slippagePercent: 1,
});
alert('Swap successful!');
} catch (error) {
console.error(error);
}
setLoading(false);
};
return (
<div className="p-6 border rounded-xl max-w-md">
<h3 className="font-bold mb-4">Swap Tokens</h3>
{/* From */}
<div className="mb-4">
<label className="text-sm text-gray-500">From</label>
<div className="flex gap-2">
<input
type="number"
value={amountIn}
onChange={(e) => setAmountIn(e.target.value)}
onBlur={handleQuote}
className="flex-1 p-2 border rounded"
placeholder="0.0"
/>
<select
value={tokenIn}
onChange={(e) => setTokenIn(e.target.value)}
className="p-2 border rounded"
>
{Object.keys(TOKENS).map(t => <option key={t}>{t}</option>)}
</select>
</div>
</div>
{/* To */}
<div className="mb-4">
<label className="text-sm text-gray-500">To (estimated)</label>
<div className="flex gap-2">
<input
type="text"
value={quote || ''}
readOnly
className="flex-1 p-2 border rounded bg-gray-50"
placeholder="0.0"
/>
<select
value={tokenOut}
onChange={(e) => setTokenOut(e.target.value)}
className="p-2 border rounded"
>
{Object.keys(TOKENS).map(t => <option key={t}>{t}</option>)}
</select>
</div>
</div>
<button
onClick={handleSwap}
disabled={loading || !quote}
className="w-full bg-blue-600 text-white py-3 rounded-lg"
>
{loading ? 'Swapping...' : 'Swap'}
</button>
</div>
);
}Liquidity Provision
Add Liquidity
const POSITION_MANAGER = '0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1'; // Base
async function addLiquidity(
token0: string,
token1: string,
amount0: bigint,
amount1: bigint,
tickLower: number,
tickUpper: number
) {
// 1. Approve tokens
await sdk.approveViaRelayer({ token: token0, spender: POSITION_MANAGER, amount: amount0 });
await sdk.approveViaRelayer({ token: token1, spender: POSITION_MANAGER, amount: amount1 });
// 2. Mint position
const nftManagerInterface = new ethers.Interface([
'function mint((address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, address recipient, uint256 deadline)) returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)'
]);
return await sdk.executeViaRelayer({
target: POSITION_MANAGER,
data: nftManagerInterface.encodeFunctionData('mint', [{
token0,
token1,
fee: 3000,
tickLower,
tickUpper,
amount0Desired: amount0,
amount1Desired: amount1,
amount0Min: 0,
amount1Min: 0,
recipient: sdk.getVaultAddress(),
deadline: Math.floor(Date.now() / 1000) + 3600,
}]),
});
}Staking
Stake Component
const STAKING_CONTRACT = '0xYourStakingContract';
export function StakingWidget({ token }: { token: string }) {
const [amount, setAmount] = useState('');
const [staked, setStaked] = useState(0n);
const [rewards, setRewards] = useState(0n);
const [loading, setLoading] = useState(false);
const handleStake = async () => {
setLoading(true);
try {
const amountWei = parseUnits(amount, 18);
// Approve
await sdk.approveViaRelayer({
token,
spender: STAKING_CONTRACT,
amount: amountWei,
});
// Stake
await sdk.executeViaRelayer({
target: STAKING_CONTRACT,
data: stakingIface.encodeFunctionData('stake', [amountWei]),
});
setAmount('');
await loadStakeInfo();
} catch (error) {
console.error(error);
}
setLoading(false);
};
const handleUnstake = async () => {
setLoading(true);
try {
await sdk.executeViaRelayer({
target: STAKING_CONTRACT,
data: stakingIface.encodeFunctionData('withdraw', [staked]),
});
await loadStakeInfo();
} catch (error) {
console.error(error);
}
setLoading(false);
};
const handleClaim = async () => {
setLoading(true);
try {
await sdk.executeViaRelayer({
target: STAKING_CONTRACT,
data: stakingIface.encodeFunctionData('getReward', []),
});
await loadStakeInfo();
} catch (error) {
console.error(error);
}
setLoading(false);
};
return (
<div className="p-6 border rounded-xl">
<h3 className="font-bold mb-4">Staking</h3>
<div className="grid grid-cols-2 gap-4 mb-4">
<div className="p-3 bg-gray-50 rounded">
<div className="text-sm text-gray-500">Staked</div>
<div className="font-mono">{formatUnits(staked, 18)}</div>
</div>
<div className="p-3 bg-gray-50 rounded">
<div className="text-sm text-gray-500">Rewards</div>
<div className="font-mono">{formatUnits(rewards, 18)}</div>
</div>
</div>
<div className="space-y-3">
<input
type="number"
value={amount}
onChange={(e) => setAmount(e.target.value)}
placeholder="Amount to stake"
className="w-full p-2 border rounded"
/>
<button onClick={handleStake} disabled={loading} className="w-full bg-green-600 text-white py-2 rounded">
{loading ? 'Staking...' : 'Stake'}
</button>
<div className="grid grid-cols-2 gap-2">
<button onClick={handleUnstake} disabled={loading} className="border py-2 rounded">
Unstake All
</button>
<button onClick={handleClaim} disabled={loading} className="border py-2 rounded">
Claim Rewards
</button>
</div>
</div>
</div>
);
}Lending Protocols
Aave Integration
const AAVE_POOL = '0xA238Dd80C259a72e81d7e4664a9801593F98d1c5'; // Base
// Supply tokens
async function supply(token: string, amount: bigint) {
// Approve
await sdk.approveViaRelayer({ token, spender: AAVE_POOL, amount });
// Supply
const poolInterface = new ethers.Interface([
'function supply(address asset, uint256 amount, address onBehalfOf, uint16 referralCode)'
]);
return await sdk.executeViaRelayer({
target: AAVE_POOL,
data: poolInterface.encodeFunctionData('supply', [
token,
amount,
sdk.getVaultAddress(),
0,
]),
});
}
// Borrow tokens
async function borrow(token: string, amount: bigint, interestRateMode: 1 | 2) {
const poolInterface = new ethers.Interface([
'function borrow(address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf)'
]);
return await sdk.executeViaRelayer({
target: AAVE_POOL,
data: poolInterface.encodeFunctionData('borrow', [
token,
amount,
interestRateMode, // 1 = stable, 2 = variable
0,
sdk.getVaultAddress(),
]),
});
}
// Repay loan
async function repay(token: string, amount: bigint, interestRateMode: 1 | 2) {
await sdk.approveViaRelayer({ token, spender: AAVE_POOL, amount });
const poolInterface = new ethers.Interface([
'function repay(address asset, uint256 amount, uint256 interestRateMode, address onBehalfOf) returns (uint256)'
]);
return await sdk.executeViaRelayer({
target: AAVE_POOL,
data: poolInterface.encodeFunctionData('repay', [
token,
amount,
interestRateMode,
sdk.getVaultAddress(),
]),
});
}Session Keys for DeFi
Enable automated DeFi operations:
// Create session for DeFi operations
const session = await sdk.sessions.create({
duration: 3600, // 1 hour
maxValue: parseUnits('1000', 6), // Max $1000
allowedTokens: [USDC, ETH],
allowedContracts: [UNISWAP_ROUTER, AAVE_POOL],
});
// Now operations within limits don't need biometric
await swapTokens({ ... }); // Auto-approved ✓
await supply(USDC, parseUnits('100', 6)); // Auto-approved ✓Best Practices
- Slippage protection - Always set reasonable slippage limits
- Deadline handling - Use deadlines for all DeFi transactions
- Quote freshness - Refresh quotes before execution
- Session limits - Set conservative session spending limits
- Error handling - Handle revert reasons gracefully
Security Considerations
- Verify contract addresses against official sources
- Use price oracles for sanity checks
- Implement circuit breakers for large transactions
- Monitor for frontrunning in quotes
- Validate user inputs thoroughly