137 lines
4.4 KiB
TypeScript
137 lines
4.4 KiB
TypeScript
/**
|
||
* Uniswap: Universal Router with Permit2 integration
|
||
*
|
||
* This example demonstrates how to use Universal Router for complex multi-step transactions
|
||
* with Permit2 signature-based approvals.
|
||
*
|
||
* Universal Router supports:
|
||
* - Token swaps (Uniswap v2/v3)
|
||
* - NFT operations
|
||
* - Permit2 approvals
|
||
* - WETH wrapping/unwrapping
|
||
* - Multiple commands in one transaction
|
||
*/
|
||
|
||
import { createWalletRpcClient } from '../../src/utils/chain-config.js';
|
||
import { getUniswapUniversalRouter, getPermit2Address } from '../../src/utils/addresses.js';
|
||
import { getTokenMetadata, parseTokenAmount } from '../../src/utils/tokens.js';
|
||
import { waitForTransaction } from '../../src/utils/rpc.js';
|
||
import type { Address, Hex } from 'viem';
|
||
|
||
const CHAIN_ID = 1; // Mainnet (change to 8453 for Base, etc.)
|
||
const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`;
|
||
|
||
// Universal Router ABI
|
||
const UNIVERSAL_ROUTER_ABI = [
|
||
{
|
||
name: 'execute',
|
||
type: 'function',
|
||
stateMutability: 'payable',
|
||
inputs: [
|
||
{ name: 'commands', type: 'bytes' },
|
||
{ name: 'inputs', type: 'bytes[]' },
|
||
],
|
||
outputs: [],
|
||
},
|
||
{
|
||
name: 'execute',
|
||
type: 'function',
|
||
stateMutability: 'payable',
|
||
inputs: [
|
||
{ name: 'commands', type: 'bytes' },
|
||
{ name: 'inputs', type: 'bytes[]' },
|
||
{ name: 'deadline', type: 'uint256' },
|
||
],
|
||
outputs: [],
|
||
},
|
||
] as const;
|
||
|
||
/**
|
||
* Universal Router command types
|
||
* See: https://github.com/Uniswap/universal-router/blob/main/contracts/Commands.sol
|
||
*/
|
||
const COMMANDS = {
|
||
V3_SWAP_EXACT_IN: 0x00,
|
||
V3_SWAP_EXACT_OUT: 0x01,
|
||
PERMIT2_TRANSFER_FROM: 0x02,
|
||
PERMIT2_PERMIT_BATCH: 0x03,
|
||
SWEEP: 0x04,
|
||
TRANSFER: 0x05,
|
||
PAY_PORTION: 0x06,
|
||
V2_SWAP_EXACT_IN: 0x08,
|
||
V2_SWAP_EXACT_OUT: 0x09,
|
||
PERMIT2_PERMIT: 0x0a,
|
||
WRAP_ETH: 0x0b,
|
||
UNWRAP_WETH: 0x0c,
|
||
PERMIT2_TRANSFER_FROM_BATCH: 0x0d,
|
||
} as const;
|
||
|
||
/**
|
||
* Encode V3 swap exact input command
|
||
*
|
||
* This is a simplified example. In production, use the Universal Router SDK
|
||
* or carefully encode commands according to the Universal Router spec.
|
||
*/
|
||
function encodeV3SwapExactInput(params: {
|
||
recipient: Address;
|
||
amountIn: bigint;
|
||
amountOutMin: bigint;
|
||
path: Hex;
|
||
payerIsUser: boolean;
|
||
}): { command: number; input: Hex } {
|
||
// This is a conceptual example. Actual encoding is more complex.
|
||
// See: https://docs.uniswap.org/contracts/universal-router/technical-reference
|
||
throw new Error('Use Universal Router SDK for proper command encoding');
|
||
}
|
||
|
||
async function universalRouterSwap() {
|
||
const walletClient = createWalletRpcClient(CHAIN_ID, PRIVATE_KEY);
|
||
const publicClient = walletClient as any;
|
||
const account = walletClient.account?.address;
|
||
|
||
if (!account) {
|
||
throw new Error('No account available');
|
||
}
|
||
|
||
const routerAddress = getUniswapUniversalRouter(CHAIN_ID);
|
||
const tokenIn = getTokenMetadata(CHAIN_ID, 'USDC');
|
||
const tokenOut = getTokenMetadata(CHAIN_ID, 'WETH');
|
||
const amountIn = parseTokenAmount('1000', tokenIn.decimals);
|
||
const deadline = BigInt(Math.floor(Date.now() / 1000) + 600);
|
||
|
||
console.log(`Universal Router swap: ${amountIn} ${tokenIn.symbol} -> ${tokenOut.symbol}`);
|
||
console.log(`Router: ${routerAddress}`);
|
||
console.log(`Account: ${account}`);
|
||
|
||
// Note: Universal Router command encoding is complex.
|
||
// In production, use:
|
||
// 1. Universal Router SDK (when available)
|
||
// 2. Or carefully encode commands according to the spec
|
||
// 3. Or use Protocolink which handles Universal Router integration
|
||
|
||
console.log('\n⚠️ This is a conceptual example.');
|
||
console.log('In production, use:');
|
||
console.log(' 1. Universal Router SDK for command encoding');
|
||
console.log(' 2. Or use Protocolink which integrates Universal Router');
|
||
console.log(' 3. Or carefully follow the Universal Router spec:');
|
||
console.log(' https://docs.uniswap.org/contracts/universal-router/technical-reference');
|
||
|
||
// Example flow:
|
||
// 1. Create Permit2 signature (see uniswap-permit2.ts)
|
||
// 2. Encode Universal Router commands
|
||
// 3. Execute via Universal Router.execute()
|
||
// 4. Universal Router will:
|
||
// - Use Permit2 to transfer tokens
|
||
// - Execute swap
|
||
// - Send output to recipient
|
||
// - All in one transaction
|
||
}
|
||
|
||
// Run if executed directly
|
||
if (import.meta.url === `file://${process.argv[1]}`) {
|
||
universalRouterSwap().catch(console.error);
|
||
}
|
||
|
||
export { universalRouterSwap, COMMANDS };
|
||
|