/** * Protocolink: Protocolink with Permit2 signatures * * This example demonstrates how to use Protocolink with Permit2 * for gasless approvals via signatures. */ import * as api from '@protocolink/api'; import * as common from '@protocolink/common'; import { createWalletRpcClient } from '../../src/utils/chain-config.js'; import { waitForTransaction } from '../../src/utils/rpc.js'; const CHAIN_ID = common.ChainId.mainnet; const PRIVATE_KEY = process.env.PRIVATE_KEY as `0x${string}`; async function protocolinkWithPermit2() { 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 USDC: common.Token = { chainId: CHAIN_ID, address: '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48', decimals: 6, symbol: 'USDC', name: 'USD Coin', }; const WBTC: common.Token = { chainId: CHAIN_ID, address: '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599', decimals: 8, symbol: 'WBTC', name: 'Wrapped Bitcoin', }; const amountIn = '1000'; // 1000 USDC console.log(`Using Protocolink with Permit2 for gasless approvals`); console.log(`Swapping ${amountIn} USDC → WBTC, then supplying to Aave`); console.log(`Account: ${account}`); try { // Step 1: Get swap quotation const swapQuotation = await api.protocols.uniswapv3.getSwapTokenQuotation(CHAIN_ID, { input: { token: USDC, amount: amountIn }, tokenOut: WBTC, slippage: 100, }); // Step 2: Build swap logic const swapLogic = api.protocols.uniswapv3.newSwapTokenLogic(swapQuotation); // Step 3: Get supply quotation const supplyQuotation = await api.protocols.aavev3.getSupplyQuotation(CHAIN_ID, { input: swapQuotation.output, }); // Step 4: Build supply logic const supplyLogic = api.protocols.aavev3.newSupplyLogic(supplyQuotation); const routerLogics = [swapLogic, supplyLogic]; // Step 5: Get permit2 data (if token supports it) // Protocolink will automatically use Permit2 when available console.log('\n1. Building router transaction with Permit2...'); const routerData = await api.router.getRouterData(CHAIN_ID, { account, logics: routerLogics, // Permit2 will be used automatically if: // 1. Token supports Permit2 // 2. User has sufficient balance // 3. No existing approval }); console.log(`Router: ${routerData.router}`); console.log(`Using Permit2: ${routerData.permit2Data ? 'Yes' : 'No'}`); console.log(`Estimated gas: ${routerData.estimation.gas}`); // Step 6: If Permit2 data is provided, sign it if (routerData.permit2Data) { console.log('\n2. Signing Permit2 permit...'); // Protocolink SDK handles Permit2 signing internally // You may need to sign the permit data before executing // See Protocolink docs for exact flow } // Step 7: Execute transaction console.log('\n3. Executing transaction...'); const tx = await walletClient.sendTransaction({ to: routerData.router, data: routerData.data, value: BigInt(routerData.estimation.value || '0'), gas: BigInt(routerData.estimation.gas), }); await waitForTransaction(publicClient, tx); console.log(`Transaction executed: ${tx}`); console.log('\nāœ… Transaction with Permit2 completed successfully!'); } catch (error) { console.error('Error executing transaction:', error); throw error; } } // Run if executed directly if (import.meta.url === `file://${process.argv[1]}`) { protocolinkWithPermit2().catch(console.error); } export { protocolinkWithPermit2 };