import { ethers } from 'ethers';
import { LitNodeClient } from '@lit-protocol/lit-node-client';
import { getSignedUniswapQuote, bundledVincentAbility as uniswapBundledAbility } from '@lit-protocol/vincent-ability-uniswap-swap';
import { bundledVincentAbility as erc20BundledAbility } from '@lit-protocol/vincent-ability-erc20-approval';
import { getVincentAbilityClient } from '@lit-protocol/vincent-app-sdk/abilityClient';
const DELEGATEE_PRIVATE_KEY = "YOUR-DELEGATEE-PRIVATE-KEY-HERE";
const delegatorPkpEthAddress = "YOUR-DELEGATOR-WALLET-ADDRESS-HERE";
const RPC_URL = "https://base.llamarpc.com";
const CHAIN_ID = 8453;
const TOKEN_IN = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'; // USDC
const TOKEN_OUT = '0x4200000000000000000000000000000000000006'; // WETH
const TOKEN_IN_DECIMALS = 6;
const SWAP_AMOUNT = 0.1;
const yellowstoneProvider = new ethers.providers.JsonRpcProvider("https://yellowstone-rpc.litprotocol.com/");
const delegateeSigner = new ethers.Wallet(DELEGATEE_PRIVATE_KEY, yellowstoneProvider);
console.log('Delegatee address:', delegateeSigner.address);
console.log('Initializing Lit Node Client...');
const litNodeClient = new LitNodeClient({
litNetwork: 'datil',
debug: true,
});
await litNodeClient.connect();
console.log('Connected to Lit Network');
console.log('Generating signed Uniswap quote...');
const signedUniswapQuote = await getSignedUniswapQuote({
quoteParams: {
rpcUrl: RPC_URL,
tokenInAddress: TOKEN_IN,
tokenInAmount: SWAP_AMOUNT.toString(),
tokenOutAddress: TOKEN_OUT,
recipient: delegatorPkpEthAddress,
},
ethersSigner: delegateeSigner,
litNodeClient,
});
console.log('Signed quote generated:', JSON.stringify(signedUniswapQuote, null, 2));
// Step 1: Check and approve ERC20 if needed
console.log('\n=== Step 1: ERC20 Approval ===');
const uniswapRouterAddress = signedUniswapQuote.quote.to;
console.log('Uniswap router address:', uniswapRouterAddress);
const erc20ApprovalAbilityClient = getVincentAbilityClient({
bundledVincentAbility: erc20BundledAbility,
ethersSigner: delegateeSigner,
});
// Precheck if approval is needed
console.log('Checking if ERC20 approval is needed...');
const approvalPrecheckResult = await erc20ApprovalAbilityClient.precheck(
{
rpcUrl: RPC_URL,
chainId: CHAIN_ID,
spenderAddress: uniswapRouterAddress,
tokenAddress: TOKEN_IN,
tokenAmount: ethers.utils.parseUnits(SWAP_AMOUNT.toString(), TOKEN_IN_DECIMALS).toString(),
alchemyGasSponsor: false,
},
{
delegatorPkpEthAddress,
}
);
console.log('Approval precheck result:', JSON.stringify(approvalPrecheckResult, null, 2));
if (!approvalPrecheckResult.success) {
throw new Error(`Approval precheck failed: ${approvalPrecheckResult.runtimeError}`);
}
if ('noNativeTokenBalance' in approvalPrecheckResult.result) {
throw new Error('PKP has no native token balance for gas');
}
if (!approvalPrecheckResult.result.alreadyApproved) {
console.log('Executing ERC20 approval...');
const approvalExecutionResult = await erc20ApprovalAbilityClient.execute(
{
rpcUrl: RPC_URL,
chainId: CHAIN_ID,
spenderAddress: uniswapRouterAddress,
tokenAddress: TOKEN_IN,
tokenAmount: ethers.utils.parseUnits(SWAP_AMOUNT.toString(), TOKEN_IN_DECIMALS).toString(),
alchemyGasSponsor: false,
},
{
delegatorPkpEthAddress,
}
);
console.log('Approval execution result:', JSON.stringify(approvalExecutionResult, null, 2));
if (!approvalExecutionResult.success) {
throw new Error(`Approval execution failed: ${approvalExecutionResult.runtimeError}`);
}
console.log('ERC20 approval successful! Tx hash:', approvalExecutionResult.result.approvalTxHash);
} else {
console.log('Sufficient allowance already exists, skipping approval');
}
// Step 2: Execute the swap
console.log('\n=== Step 2: Execute Uniswap Swap ===');
const uniswapSwapAbilityClient = getVincentAbilityClient({
bundledVincentAbility: uniswapBundledAbility,
ethersSigner: delegateeSigner,
});
console.log('Executing Uniswap swap...');
const swapExecutionResult = await uniswapSwapAbilityClient.execute(
{
rpcUrlForUniswap: RPC_URL,
signedUniswapQuote: {
quote: signedUniswapQuote.quote,
signature: signedUniswapQuote.signature,
},
},
{
delegatorPkpEthAddress,
}
);
console.log('Swap execution result:', JSON.stringify(swapExecutionResult, null, 2));
if (!swapExecutionResult.success) {
throw new Error(`Swap execution failed: ${swapExecutionResult.runtimeError}`);
}
console.log('\n✅ Swap successful! Tx hash:', swapExecutionResult.result.swapTxHash);
console.log('View on BaseScan:', `https://basescan.org/tx/${swapExecutionResult.result.swapTxHash}`);
litNodeClient.disconnect();
console.log('\nDisconnected from Lit Network');