diff --git a/.changeset/polite-horses-accept.md b/.changeset/polite-horses-accept.md new file mode 100644 index 000000000..7ec561dd9 --- /dev/null +++ b/.changeset/polite-horses-accept.md @@ -0,0 +1,5 @@ +--- +"@turnkey/gas-station": minor +--- + +Add USDC swap example with approveThenExecute and reimbursable gas station support diff --git a/examples/tk-gas-station/package.json b/examples/tk-gas-station/package.json index 55b7d1f65..b32507fa3 100644 --- a/examples/tk-gas-station/package.json +++ b/examples/tk-gas-station/package.json @@ -11,6 +11,7 @@ "eth-transfer": "tsx src/transferETHDelegated.ts", "usdc-transfer": "tsx src/transferUSDCDelegated.ts", "approve-then-execute": "tsx src/approveThenExecuteDemo.ts", + "swap-usdc-eth": "tsx src/swapUSDCForETH.ts", "test:policies": "jest policyEnforcement", "clean": "rimraf ./dist ./.cache", "typecheck": "tsc --noEmit" @@ -21,6 +22,9 @@ "@turnkey/gas-station": "workspace:*", "@turnkey/sdk-server": "workspace:*", "@turnkey/viem": "workspace:*", + "@uniswap/sdk-core": "^6.0.0", + "@uniswap/universal-router-sdk": "^3.0.0", + "@uniswap/v3-sdk": "^3.9.0", "dotenv": "^16.0.3", "ethers": "^6.10.0", "prompts": "^2.4.2", diff --git a/examples/tk-gas-station/src/swapUSDCForETH.ts b/examples/tk-gas-station/src/swapUSDCForETH.ts new file mode 100644 index 000000000..e96e94b7a --- /dev/null +++ b/examples/tk-gas-station/src/swapUSDCForETH.ts @@ -0,0 +1,408 @@ +/** + * USDC β†’ WETH Swap Example using Turnkey Gas Station + * + * This example demonstrates a gasless token swap using the Gas Station pattern + * with EIP-7702 authorization. It's ideal for users who hold USDC but no ETH. + * + * Flow (2 transactions total): + * 1. approveThenExecute: Atomically approves USDC to Permit2 AND sets up + * Permit2 allowance for Universal Router (combines 2 approvals into 1 tx) + * 2. execute: Performs the actual swap via Uniswap Universal Router + * + * The paymaster pays for all gas - the user only signs intents. + * + * Prerequisites: + * - EOA wallet with USDC balance on Base mainnet + * - Paymaster wallet with ETH for gas + * - Both wallets accessible via Turnkey + * + * Usage: + * pnpm tsx src/swapUSDCForETH.ts [--amount ] + * + * Environment variables required (in .env.local): + * - BASE_URL: Turnkey API base URL + * - API_PRIVATE_KEY: Turnkey API private key + * - API_PUBLIC_KEY: Turnkey API public key + * - ORGANIZATION_ID: Turnkey organization ID + * - EOA_ADDRESS: User wallet address (holds USDC) + * - PAYMASTER_ADDRESS: Paymaster wallet address (pays gas) + * - BASE_RPC_URL: Base mainnet RPC endpoint + */ + +import { resolve } from "path"; +import * as dotenv from "dotenv"; +import { z } from "zod"; +import { parseArgs } from "node:util"; +import { + parseUnits, + formatUnits, + createWalletClient, + http, + encodeAbiParameters, + encodePacked, + type Hex, + concat, + toHex, +} from "viem"; +import { Turnkey as TurnkeyServerSDK } from "@turnkey/sdk-server"; +import { createAccount } from "@turnkey/viem"; +import { GasStationClient, CHAIN_PRESETS } from "@turnkey/gas-station"; +import { Token } from "@uniswap/sdk-core"; +import { FeeAmount } from "@uniswap/v3-sdk"; +import { print } from "./utils"; + +// ============================================================================ +// Configuration +// ============================================================================ + +dotenv.config({ path: resolve(process.cwd(), ".env.local") }); + +// Parse CLI arguments +const { values: args } = parseArgs({ + args: process.argv.slice(2), + options: { + amount: { + type: "string", + short: "a", + default: "0.10", + }, + }, +}); + +const SWAP_AMOUNT_USDC = args.amount!; + +// Environment validation +const envSchema = z.object({ + BASE_URL: z.string().url("BASE_URL must be a valid Turnkey API URL"), + API_PRIVATE_KEY: z.string().min(1, "API_PRIVATE_KEY is required"), + API_PUBLIC_KEY: z.string().min(1, "API_PUBLIC_KEY is required"), + ORGANIZATION_ID: z.string().min(1, "ORGANIZATION_ID is required"), + EOA_ADDRESS: z + .string() + .regex( + /^0x[a-fA-F0-9]{40}$/, + "EOA_ADDRESS must be a valid Ethereum address", + ), + PAYMASTER_ADDRESS: z + .string() + .regex( + /^0x[a-fA-F0-9]{40}$/, + "PAYMASTER_ADDRESS must be a valid Ethereum address", + ), + BASE_RPC_URL: z.string().url("BASE_RPC_URL must be a valid URL"), +}); + +const env = envSchema.parse(process.env); + +// ============================================================================ +// Contract Addresses (Base Mainnet) +// ============================================================================ + +const BASE_CHAIN_ID = 8453; +const EXPLORER_URL = "https://basescan.org"; + +const USDC_ADDRESS: Hex = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913"; +const WETH_ADDRESS: Hex = "0x4200000000000000000000000000000000000006"; +const PERMIT2_ADDRESS: Hex = "0x000000000022D473030F116dDEE9F6B43aC78BA3"; +const UNIVERSAL_ROUTER_ADDRESS: Hex = + "0x3fC91A3afd70395Cd496C647d5a6CC9D4B2b7FAD"; + +// Token definitions for amount parsing +const USDC_TOKEN = new Token( + BASE_CHAIN_ID, + USDC_ADDRESS, + 6, + "USDC", + "USD Coin", +); + +// ============================================================================ +// Uniswap Encoding Helpers +// ============================================================================ + +// Universal Router command for V3 exact input swap +const V3_SWAP_EXACT_IN = 0x00; + +// Function selectors +const UNIVERSAL_ROUTER_EXECUTE_SELECTOR: Hex = "0x3593564c"; +const PERMIT2_APPROVE_SELECTOR: Hex = "0x87517c45"; + +/** + * Encodes the swap path for Uniswap V3 (tokenIn -> fee -> tokenOut) + */ +function encodeV3SwapPath(tokenIn: Hex, fee: FeeAmount, tokenOut: Hex): Hex { + return encodePacked( + ["address", "uint24", "address"], + [tokenIn, fee, tokenOut], + ); +} + +/** + * Encodes parameters for V3_SWAP_EXACT_IN command + */ +function encodeV3SwapExactInParams( + recipient: Hex, + amountIn: bigint, + amountOutMin: bigint, + path: Hex, + payerIsUser: boolean, +): Hex { + return encodeAbiParameters( + [ + { type: "address" }, + { type: "uint256" }, + { type: "uint256" }, + { type: "bytes" }, + { type: "bool" }, + ], + [recipient, amountIn, amountOutMin, path, payerIsUser], + ); +} + +/** + * Builds complete calldata for Universal Router execute + */ +function buildUniversalRouterExecuteCalldata( + commands: Hex, + inputs: Hex[], + deadline: bigint, +): Hex { + const params = encodeAbiParameters( + [{ type: "bytes" }, { type: "bytes[]" }, { type: "uint256" }], + [commands, inputs, deadline], + ); + return concat([UNIVERSAL_ROUTER_EXECUTE_SELECTOR, params]); +} + +/** + * Builds calldata for Permit2.approve(token, spender, amount, expiration) + */ +function buildPermit2ApproveCalldata( + token: Hex, + spender: Hex, + amount: bigint, + expiration: number, +): Hex { + const params = encodeAbiParameters( + [ + { type: "address" }, + { type: "address" }, + { type: "uint160" }, + { type: "uint48" }, + ], + [token, spender, amount, expiration], + ); + return concat([PERMIT2_APPROVE_SELECTOR, params]); +} + +// ============================================================================ +// Main Execution +// ============================================================================ + +async function main(): Promise { + print("πŸ”„ Turnkey Gas Station: USDC β†’ WETH Swap", ""); + print("Network", "Base Mainnet"); + print("Swap Amount", `${SWAP_AMOUNT_USDC} USDC`); + print("User Wallet", env.EOA_ADDRESS); + print("Paymaster", env.PAYMASTER_ADDRESS); + print("", ""); + + // Initialize Turnkey SDK + const turnkeyClient = new TurnkeyServerSDK({ + apiBaseUrl: env.BASE_URL, + apiPrivateKey: env.API_PRIVATE_KEY, + apiPublicKey: env.API_PUBLIC_KEY, + defaultOrganizationId: env.ORGANIZATION_ID, + }); + + // Create wallet clients + const [userAccount, paymasterAccount] = await Promise.all([ + createAccount({ + client: turnkeyClient.apiClient(), + organizationId: env.ORGANIZATION_ID, + signWith: env.EOA_ADDRESS as Hex, + }), + createAccount({ + client: turnkeyClient.apiClient(), + organizationId: env.ORGANIZATION_ID, + signWith: env.PAYMASTER_ADDRESS as Hex, + }), + ]); + + const preset = CHAIN_PRESETS.BASE_MAINNET; + const rpcUrl = env.BASE_RPC_URL; + + const userWalletClient = createWalletClient({ + account: userAccount, + chain: preset.chain, + transport: http(rpcUrl), + }); + + const paymasterWalletClient = createWalletClient({ + account: paymasterAccount, + chain: preset.chain, + transport: http(rpcUrl), + }); + + // Initialize Gas Station clients + const userClient = new GasStationClient({ walletClient: userWalletClient }); + const paymasterClient = new GasStationClient({ + walletClient: paymasterWalletClient, + }); + + // ────────────────────────────────────────────────────────────────────────── + // Step 1: EIP-7702 Authorization (if needed) + // ────────────────────────────────────────────────────────────────────────── + + print("Step 1: EIP-7702 Authorization", ""); + + const isAuthorized = await userClient.isAuthorized(); + + if (!isAuthorized) { + print("Status", "EOA not authorized, signing authorization..."); + + const authorization = await userClient.signAuthorization(); + const authResult = await paymasterClient.submitAuthorizations([ + authorization, + ]); + + print("βœ… Authorized", `${EXPLORER_URL}/tx/${authResult.txHash}`); + await delay(2000); + } else { + print("βœ… Already authorized", "Skipping"); + } + + print("", ""); + + // ────────────────────────────────────────────────────────────────────────── + // Step 2: Combined Approval (approveThenExecute) + // ────────────────────────────────────────────────────────────────────────── + + print("Step 2: Combined Approval", ""); + print( + "Action", + "ERC20 approve USDCβ†’Permit2 + Permit2 approve for Universal Router", + ); + + const swapAmount = parseUnits(SWAP_AMOUNT_USDC, USDC_TOKEN.decimals); + const approveNonce = await userClient.getNonce(); + + // Permit2 allowance: 1000x swap amount, expires in 30 days + const permit2AllowanceAmount = swapAmount * 1000n; + const permit2Expiration = Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 30; + + // Build Permit2.approve calldata (the "execute" part of approveThenExecute) + const permit2ApproveCalldata = buildPermit2ApproveCalldata( + USDC_ADDRESS, + UNIVERSAL_ROUTER_ADDRESS, + permit2AllowanceAmount, + permit2Expiration, + ); + + // Max ERC20 approval for USDCβ†’Permit2 (the "approve" part of approveThenExecute) + const maxErc20Approval = BigInt( + "0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", + ); + + // Sign combined approval intent + const approvalIntent = await userClient + .createIntent() + .setTarget(PERMIT2_ADDRESS) + .withValue(0n) + .withCallData(permit2ApproveCalldata) + .signApprovalExecution( + approveNonce, + USDC_ADDRESS, + PERMIT2_ADDRESS, + maxErc20Approval, + ); + + // Paymaster executes both approvals atomically + const approvalResult = + await paymasterClient.approveThenExecute(approvalIntent); + + print("βœ… Approved", `${EXPLORER_URL}/tx/${approvalResult.txHash}`); + print("Gas Used", approvalResult.gasUsed.toString()); + await delay(2000); + + print("", ""); + + // ────────────────────────────────────────────────────────────────────────── + // Step 3: Execute Swap + // ────────────────────────────────────────────────────────────────────────── + + print("Step 3: Execute Swap", ""); + + const swapNonce = await userClient.getNonce(); + const deadline = BigInt(Math.floor(Date.now() / 1000) + 60 * 20); // 20 min + + // Build swap path: USDC β†’ (0.05% fee) β†’ WETH + const swapPath = encodeV3SwapPath(USDC_ADDRESS, FeeAmount.LOW, WETH_ADDRESS); + + // Encode swap parameters (recipient receives WETH, user pays with USDC) + const swapParams = encodeV3SwapExactInParams( + env.EOA_ADDRESS as Hex, + swapAmount, + 0n, // Accept any amount out (production should use a quote service) + swapPath, + true, + ); + + // Build Universal Router execute calldata + const commands = toHex(new Uint8Array([V3_SWAP_EXACT_IN])); + const swapCalldata = buildUniversalRouterExecuteCalldata( + commands, + [swapParams], + deadline, + ); + + // Sign and execute swap intent + const swapIntent = await userClient + .createIntent() + .setTarget(UNIVERSAL_ROUTER_ADDRESS) + .withValue(0n) + .withCallData(swapCalldata) + .sign(swapNonce); + + const swapResult = await paymasterClient.execute(swapIntent); + + print("βœ… Swapped", `${EXPLORER_URL}/tx/${swapResult.txHash}`); + print("Gas Used", swapResult.gasUsed.toString()); + + print("", ""); + + // ────────────────────────────────────────────────────────────────────────── + // Summary + // ────────────────────────────────────────────────────────────────────────── + + const totalGas = approvalResult.gasUsed + swapResult.gasUsed; + + print("═══════════════════════════════════════════", ""); + print("βœ… Swap Complete!", ""); + print("Amount", `${SWAP_AMOUNT_USDC} USDC β†’ WETH`); + print( + "Total Gas", + `${totalGas} (Approval: ${approvalResult.gasUsed} + Swap: ${swapResult.gasUsed})`, + ); + print( + "Transactions", + "2 (combined approvals into 1 tx using approveThenExecute)", + ); + print("Swap TX", `${EXPLORER_URL}/tx/${swapResult.txHash}`); + print("═══════════════════════════════════════════", ""); + print("", ""); + print( + "Note", + "You received WETH. To get native ETH, an additional unwrap step is needed.", + ); +} + +function delay(ms: number): Promise { + return new Promise((resolve) => setTimeout(resolve, ms)); +} + +// Run with error handling +main().catch((error) => { + console.error("\n❌ Error:", error.message || error); + process.exit(1); +}); diff --git a/examples/tk-gas-station/src/transferUSDCReimbursable.ts b/examples/tk-gas-station/src/transferUSDCReimbursable.ts new file mode 100644 index 000000000..02dfaa648 --- /dev/null +++ b/examples/tk-gas-station/src/transferUSDCReimbursable.ts @@ -0,0 +1,262 @@ +import { resolve } from "path"; +import * as dotenv from "dotenv"; +import { z } from "zod"; +import { parseArgs } from "node:util"; +import { parseUnits, createWalletClient, http, type Hex } from "viem"; +import { Turnkey as TurnkeyServerSDK } from "@turnkey/sdk-server"; +import { createAccount } from "@turnkey/viem"; +import { + GasStationClient, + buildTokenTransfer, + CHAIN_PRESETS, + type ChainPreset, + type ReimbursableExecutionIntent, +} from "@turnkey/gas-station"; +import { print } from "./utils"; + +dotenv.config({ path: resolve(process.cwd(), ".env.local") }); + +// Transfer amount configuration +const USDC_AMOUNT = "0.01"; // 1 penny in USDC +const INITIAL_DEPOSIT_USDC = "1"; // 1 USDC initial deposit for gas (excess refunded) +const TRANSACTION_GAS_LIMIT = 200000n; // Gas limit in wei for the inner transaction + +// Parse command line arguments +const { values } = parseArgs({ + args: process.argv.slice(2), + options: { + chain: { + type: "string", + short: "c", + default: "base", + }, + }, +}); + +// Validate chain argument +const validChains = ["base", "mainnet"] as const; +type ValidChain = (typeof validChains)[number]; + +if (!validChains.includes(values.chain as ValidChain)) { + console.error( + `Invalid chain: ${values.chain}. Valid options: ${validChains.join(", ")}`, + ); + process.exit(1); +} + +const selectedChain = values.chain as ValidChain; + +// Map chain selection to chain presets +const chainPresetMap: Record = { + base: CHAIN_PRESETS.BASE_MAINNET, + mainnet: CHAIN_PRESETS.ETHEREUM_MAINNET, +}; + +const preset = chainPresetMap[selectedChain]; + +const envSchema = z.object({ + BASE_URL: z.string().url(), + API_PRIVATE_KEY: z.string().min(1), + API_PUBLIC_KEY: z.string().min(1), + ORGANIZATION_ID: z.string().min(1), + EOA_ADDRESS: z.string().min(1), + PAYMASTER_ADDRESS: z.string().min(1), + ETH_RPC_URL: z.string().url(), + BASE_RPC_URL: z.string().url(), +}); + +const env = envSchema.parse(process.env); + +print( + `🌐 Using ${selectedChain.toUpperCase()} network`, + `USDC: ${preset.tokens?.USDC}`, +); + +const turnkeyClient = new TurnkeyServerSDK({ + apiBaseUrl: env.BASE_URL, + apiPrivateKey: env.API_PRIVATE_KEY, + apiPublicKey: env.API_PUBLIC_KEY, + defaultOrganizationId: env.ORGANIZATION_ID, +}); + +/** + * Demonstrates USDC transfer using the Reimbursable Gas Station pattern. + * In this flow, the EOA pays for gas in USDC instead of the paymaster paying. + * + * Key differences from regular gas station: + * 1. EOA signs TWO signatures: + * - Execution signature (same as regular flow) - authorizes the transaction + * - Session signature (new!) - authorizes USDC transfers for gas payment + * 2. Contract pulls USDC from EOA, executes transaction, and refunds excess + * 3. Paymaster submits the transaction but EOA pays for gas in USDC + * 4. Session signature can be cached and reused across multiple transactions + */ +const main = async () => { + const rpcUrl = selectedChain === "base" ? env.BASE_RPC_URL : env.ETH_RPC_URL; + + // Create viem wallet clients with Turnkey accounts + const userAccount = await createAccount({ + client: turnkeyClient.apiClient(), + organizationId: env.ORGANIZATION_ID, + signWith: env.EOA_ADDRESS as Hex, + }); + + const paymasterAccount = await createAccount({ + client: turnkeyClient.apiClient(), + organizationId: env.ORGANIZATION_ID, + signWith: env.PAYMASTER_ADDRESS as Hex, + }); + + const userWalletClient = createWalletClient({ + account: userAccount, + chain: preset.chain, + transport: http(rpcUrl), + }); + + const paymasterWalletClient = createWalletClient({ + account: paymasterAccount, + chain: preset.chain, + transport: http(rpcUrl), + }); + + // Create Gas Station clients with the viem wallet clients + const userClient = new GasStationClient({ + walletClient: userWalletClient, + }); + + const paymasterClient = new GasStationClient({ + walletClient: paymasterWalletClient, + }); + + // Explorer URL for displaying transaction links + const explorerUrl = + selectedChain === "base" ? "https://basescan.org" : "https://etherscan.io"; + + // Step 1: Check if EOA is already authorized, authorize if needed + const isAuthorized = await userClient.isAuthorized(); + + if (!isAuthorized) { + print("===== Starting EIP-7702 Authorization =====", ""); + print("EOA not yet authorized", "Starting authorization..."); + + // Step 1: User signs the authorization + print("User signing authorization...", ""); + const authorization = await userClient.signAuthorization(); + + // Step 2: Paymaster submits the authorization transaction + const authResult = await paymasterClient.submitAuthorizations([ + authorization, + ]); + + print("Authorization transaction sent", authResult.txHash); + print("Waiting for confirmation...", ""); + print("βœ… Authorization SUCCEEDED", ""); + print( + "βœ… Authorization complete", + `${explorerUrl}/tx/${authResult.txHash}`, + ); + + // This delay helps ensure proper sequencing of on-chain state after authorization. + await new Promise((resolve) => setTimeout(resolve, 2000)); + } else { + print("βœ“ EOA already authorized", "Skipping authorization"); + } + + // Step 2: Sign session signature for USDC transfer authorization + print("===== Creating Session Signature =====", ""); + + const usdcAddress = preset.tokens?.USDC; + if (!usdcAddress) { + throw new Error(`USDC address not configured for ${selectedChain}`); + } + + const nonce = await userClient.getNonce(); + print(`Current nonce: ${nonce}`, ""); + + // Get the reimbursable contract address from the client + const reimbursableContract = (userClient as any).reimbursableContract as Hex; + + const initialDepositUSDC = parseUnits(INITIAL_DEPOSIT_USDC, 6); // 6 decimals for USDC + + // Sign session signature to authorize USDC transfer for gas payment + // Note: The session signature does NOT commit to a specific amount + // It's a general authorization for the reimbursable contract to interact with USDC + const sessionSignature = await userClient + .createIntent() + .signSessionForUSDCTransfer(nonce, usdcAddress, reimbursableContract); + + print( + "βœ“ Session signature created", + `Authorizes USDC transfers for gas payment`, + ); + + // Step 3: Create execution parameters for USDC transfer + print("===== Starting USDC Transfer with Reimbursement =====", ""); + + const transferAmount = parseUnits(USDC_AMOUNT, 6); // 6 decimals for USDC + + // Build the execution parameters using the helper + const executionParams = buildTokenTransfer( + usdcAddress, + env.PAYMASTER_ADDRESS as Hex, + transferAmount, + ); + + print( + `Executing USDC transfer`, + `${transferAmount} units (${USDC_AMOUNT} USDC) to ${env.PAYMASTER_ADDRESS}`, + ); + + // Step 3b: User creates and signs the execution intent + const executionIntent = await userClient + .createIntent() + .setTarget(executionParams.outputContract) + .withValue(executionParams.value ?? 0n) + .withCallData(executionParams.callData) + .sign(nonce); + + print("βœ“ Execution intent signed by user", ""); + + // Create the reimbursable execution intent (includes both signatures!) + const reimbursableIntent: ReimbursableExecutionIntent = { + ...executionIntent, + initialDepositUSDC, + transactionGasLimitWei: TRANSACTION_GAS_LIMIT, + sessionSignature, + }; + + print( + "βœ“ Reimbursable intent created", + "Includes execution + session signatures", + ); + + // Step 4: Paymaster executes with reimbursement (EOA pays for gas in USDC) + print("Executing intent via reimbursable gas station...", ""); + const result = + await paymasterClient.executeWithReimbursement(reimbursableIntent); + + print("Execution transaction sent", result.txHash); + print("Waiting for confirmation...", ""); + print("βœ… Execution SUCCEEDED", ""); + print("Confirmed", `Block: ${result.blockNumber}, Gas: ${result.gasUsed}`); + + print("===== USDC Transfer Complete =====", ""); + print( + "βœ… Successfully transferred 0.01 USDC from EOA to paymaster", + `TX: ${explorerUrl}/tx/${result.txHash}`, + ); + print( + "Gas payment", + `EOA deposited ${INITIAL_DEPOSIT_USDC} USDC for gas (excess refunded)`, + ); + print("Gas usage", `${result.gasUsed} gas units`); + + // Optional: Demonstrate cached session signature usage + print("\n===== Demonstrating Cached Session Signature =====", ""); + print( + "Note", + "On subsequent calls, you can pass '0x' as sessionSignature to use cached signature", + ); +}; + +main(); diff --git a/packages/gas-station/src/abi/reimbursable-gas-station.ts b/packages/gas-station/src/abi/reimbursable-gas-station.ts new file mode 100644 index 000000000..57929427a --- /dev/null +++ b/packages/gas-station/src/abi/reimbursable-gas-station.ts @@ -0,0 +1,620 @@ +export const reimbursableGasStationAbi = [ + { + inputs: [ + { internalType: "address", name: "_priceFeed", type: "address" }, + { internalType: "address", name: "_tkGasDelegate", type: "address" }, + { + internalType: "address", + name: "_reimbursementAddress", + type: "address", + }, + { internalType: "address", name: "_reimbursementErc20", type: "address" }, + { internalType: "uint16", name: "_gasFeeBasisPoints", type: "uint16" }, + { internalType: "uint256", name: "_minimumGasFee", type: "uint256" }, + { internalType: "uint256", name: "_maxDepositLimit", type: "uint256" }, + { + internalType: "uint256", + name: "_minimumTransactionGasLimitWei", + type: "uint256", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { inputs: [], name: "GasLimitExceeded", type: "error" }, + { inputs: [], name: "InsufficientBalance", type: "error" }, + { inputs: [], name: "InvalidPrice", type: "error" }, + { inputs: [], name: "InvalidSessionSignature", type: "error" }, + { inputs: [], name: "NotDelegated", type: "error" }, + { inputs: [], name: "TransactionGasLimitTooLow", type: "error" }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "_target", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "_to", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_ethAmount", + type: "uint256", + }, + { + indexed: false, + internalType: "address", + name: "_erc20", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "_spender", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_approveAmount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "_data", + type: "bytes", + }, + ], + name: "ApproveThenExecuteFailed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "_target", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_callsLength", + type: "uint256", + }, + ], + name: "BatchExecutionFailed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "_target", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "_to", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_ethAmount", + type: "uint256", + }, + { + indexed: false, + internalType: "bytes", + name: "_data", + type: "bytes", + }, + ], + name: "ExecutionFailed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "_target", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_gasChange", + type: "uint256", + }, + ], + name: "GasChangeReturned", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "_from", + type: "address", + }, + { + indexed: false, + internalType: "address", + name: "_destination", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_gasUsed", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "_reimbursementAmount", + type: "uint256", + }, + ], + name: "GasReimbursed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "_from", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_amount", + type: "uint256", + }, + ], + name: "GasUnpaid", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "_from", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_initialDepositERC20", + type: "uint256", + }, + ], + name: "InitialDeposit", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "_to", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_amount", + type: "uint256", + }, + ], + name: "TransferFailed", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "_to", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "_amount", + type: "uint256", + }, + ], + name: "TransferFailedUnclaimedStored", + type: "event", + }, + { + inputs: [], + name: "BASE_GAS_FEE_ERC20", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "ERC20_TRANSFER_SUCCEEDED_RETURN_DATA_CHECK", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "GAS_FEE_BASIS_POINTS", + outputs: [{ internalType: "uint16", name: "", type: "uint16" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "MAX_DEPOSIT_LIMIT_ERC20", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "MINIMUM_TRANSACTION_GAS_LIMIT_WEI", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "PRICE_FEED_DECIMALS", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "REIMBURSEMENT_ADDRESS", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "REIMBURSEMENT_ERC20", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "REIMBURSEMENT_ERC20_TOKEN", + outputs: [{ internalType: "contract IERC20", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "REIMBURSEMENT_TOKEN_DECIMALS", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "TEN_TO_18_PLUS_PRICE_FEED_DECIMALS", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "TEN_TO_REIMBURSEMENT_TOKEN_DECIMALS", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "TK_GAS_DELEGATE", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_initialDepositERC20", + type: "uint256", + }, + { + internalType: "uint256", + name: "_transactionGasLimitWei", + type: "uint256", + }, + { + internalType: "bytes", + name: "_packedSessionSignatureData", + type: "bytes", + }, + { internalType: "address", name: "_target", type: "address" }, + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_ethAmount", type: "uint256" }, + { internalType: "address", name: "_erc20", type: "address" }, + { internalType: "address", name: "_spender", type: "address" }, + { internalType: "uint256", name: "_approveAmount", type: "uint256" }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "approveThenExecute", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_initialDepositERC20", + type: "uint256", + }, + { + internalType: "uint256", + name: "_transactionGasLimitWei", + type: "uint256", + }, + { + internalType: "bytes", + name: "_packedSessionSignatureData", + type: "bytes", + }, + { internalType: "address", name: "_target", type: "address" }, + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_ethAmount", type: "uint256" }, + { internalType: "address", name: "_erc20", type: "address" }, + { internalType: "address", name: "_spender", type: "address" }, + { internalType: "uint256", name: "_approveAmount", type: "uint256" }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "approveThenExecuteReturns", + outputs: [{ internalType: "bytes", name: "result", type: "bytes" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_initialDepositERC20", + type: "uint256", + }, + { + internalType: "uint256", + name: "_transactionGasLimitWei", + type: "uint256", + }, + { + internalType: "bytes", + name: "_packedSessionSignatureData", + type: "bytes", + }, + { internalType: "address", name: "_targetEoA", type: "address" }, + { internalType: "bytes", name: "_signature", type: "bytes" }, + { internalType: "uint128", name: "_counter", type: "uint128" }, + ], + name: "burnCounter", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_initialDepositERC20", + type: "uint256", + }, + { + internalType: "uint256", + name: "_transactionGasLimitWei", + type: "uint256", + }, + { + internalType: "bytes", + name: "_packedSessionSignatureData", + type: "bytes", + }, + { internalType: "address", name: "_targetEoA", type: "address" }, + { internalType: "bytes", name: "_signature", type: "bytes" }, + { internalType: "uint128", name: "_nonce", type: "uint128" }, + ], + name: "burnNonce", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { internalType: "address", name: "_targetEoA", type: "address" }, + { internalType: "uint128", name: "_counter", type: "uint128" }, + ], + name: "checkSessionCounterExpired", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "claimUnclaimedGasReimbursements", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_initialDepositERC20", + type: "uint256", + }, + { + internalType: "uint256", + name: "_transactionGasLimitWei", + type: "uint256", + }, + { + internalType: "bytes", + name: "_packedSessionSignatureData", + type: "bytes", + }, + { internalType: "address", name: "_target", type: "address" }, + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_ethAmount", type: "uint256" }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "execute", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_initialDepositERC20", + type: "uint256", + }, + { + internalType: "uint256", + name: "_transactionGasLimitWei", + type: "uint256", + }, + { + internalType: "bytes", + name: "_packedSessionSignatureData", + type: "bytes", + }, + { internalType: "address", name: "_target", type: "address" }, + { + components: [ + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + internalType: "struct IBatchExecution.Call[]", + name: "_calls", + type: "tuple[]", + }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "executeBatch", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_initialDepositERC20", + type: "uint256", + }, + { + internalType: "uint256", + name: "_transactionGasLimitWei", + type: "uint256", + }, + { + internalType: "bytes", + name: "_packedSessionSignatureData", + type: "bytes", + }, + { internalType: "address", name: "_target", type: "address" }, + { + components: [ + { internalType: "address", name: "to", type: "address" }, + { internalType: "uint256", name: "value", type: "uint256" }, + { internalType: "bytes", name: "data", type: "bytes" }, + ], + internalType: "struct IBatchExecution.Call[]", + name: "_calls", + type: "tuple[]", + }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "executeBatchReturns", + outputs: [{ internalType: "bytes[]", name: "result", type: "bytes[]" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "uint256", + name: "_initialDepositERC20", + type: "uint256", + }, + { + internalType: "uint256", + name: "_transactionGasLimitWei", + type: "uint256", + }, + { + internalType: "bytes", + name: "_packedSessionSignatureData", + type: "bytes", + }, + { internalType: "address", name: "_target", type: "address" }, + { internalType: "address", name: "_to", type: "address" }, + { internalType: "uint256", name: "_ethAmount", type: "uint256" }, + { internalType: "bytes", name: "_data", type: "bytes" }, + ], + name: "executeReturns", + outputs: [{ internalType: "bytes", name: "result", type: "bytes" }], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_targetEoA", type: "address" }], + name: "getNonce", + outputs: [{ internalType: "uint128", name: "", type: "uint128" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "_targetEoA", type: "address" }], + name: "isDelegated", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function", + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "unclaimedGasReimbursements", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function", + }, +] as const; diff --git a/packages/gas-station/src/config.ts b/packages/gas-station/src/config.ts index b470a383b..40ffa5e9e 100644 --- a/packages/gas-station/src/config.ts +++ b/packages/gas-station/src/config.ts @@ -7,12 +7,15 @@ export const DEFAULT_DELEGATE_CONTRACT: Hex = "0x000066a00056CD44008768E2aF00696e19A30084"; export const DEFAULT_EXECUTION_CONTRACT: Hex = "0x00000000008c57a1CE37836a5e9d36759D070d8c"; +export const DEFAULT_REIMBURSABLE_USDC_CONTRACT: Hex = + "0x4c0a2998B4Dc7BAF418109b80E5dde7395703dcb"; // Type definitions export interface GasStationConfig { walletClient: WalletClient; delegateContract?: Hex; executionContract?: Hex; + reimbursableContract?: Hex; defaultGasLimit?: bigint; } @@ -51,6 +54,12 @@ export interface ApprovalExecutionIntent extends ExecutionIntent { approveAmount: bigint; // The amount to approve } +export interface ReimbursableExecutionIntent extends ExecutionIntent { + initialDepositUSDC: bigint; // Initial USDC deposit for gas (6 decimals), excess refunded + transactionGasLimitWei: bigint; // Gas limit in wei for the inner transaction + sessionSignature: Hex; // 85 bytes session signature +} + // Chain preset configurations export const CHAIN_PRESETS: Record = { BASE_MAINNET: { diff --git a/packages/gas-station/src/gasStationClient.ts b/packages/gas-station/src/gasStationClient.ts index f79f9943f..3a2b73741 100644 --- a/packages/gas-station/src/gasStationClient.ts +++ b/packages/gas-station/src/gasStationClient.ts @@ -9,14 +9,17 @@ import { type Hex, } from "viem"; import { gasStationAbi } from "./abi/gas-station"; +import { reimbursableGasStationAbi } from "./abi/reimbursable-gas-station"; import type { GasStationConfig, ExecutionIntent, ApprovalExecutionIntent, + ReimbursableExecutionIntent, } from "./config"; import { DEFAULT_DELEGATE_CONTRACT, DEFAULT_EXECUTION_CONTRACT, + DEFAULT_REIMBURSABLE_USDC_CONTRACT, } from "./config"; import { IntentBuilder } from "./intentBuilder"; import { @@ -29,6 +32,7 @@ export class GasStationClient { private publicClient: PublicClient; private delegateContract: Hex; private executionContract: Hex; + private reimbursableContract: Hex; constructor(config: GasStationConfig) { this.walletClient = config.walletClient; @@ -40,6 +44,8 @@ export class GasStationClient { config.delegateContract ?? DEFAULT_DELEGATE_CONTRACT; this.executionContract = config.executionContract ?? DEFAULT_EXECUTION_CONTRACT; + this.reimbursableContract = + config.reimbursableContract ?? DEFAULT_REIMBURSABLE_USDC_CONTRACT; } /** @@ -404,4 +410,105 @@ export class GasStationClient { gasUsed: receipt.gasUsed, }; } + + /** + * Sign a reimbursable execution transaction (paymaster signs, doesn't send). + * This is useful for testing policies - the paymaster attempts to sign the execution + * but doesn't actually broadcast it to the network. + * Call this with a paymaster client to test if the paymaster can sign the execution. + */ + async signReimbursableExecution( + intent: ReimbursableExecutionIntent, + ): Promise { + // Pack the execution data (signature, nonce, deadline, args) + const packedExecutionData = packExecutionData({ + signature: intent.signature, + nonce: intent.nonce, + deadline: intent.deadline, + args: intent.callData, + }); + + const callData = encodeFunctionData({ + abi: reimbursableGasStationAbi, + functionName: "executeReturns", + args: [ + intent.initialDepositUSDC, + intent.transactionGasLimitWei, + intent.sessionSignature, + intent.eoaAddress, + intent.outputContract, + intent.ethAmount, + packedExecutionData, + ], + }); + + const signedTx = await this.walletClient.signTransaction({ + to: this.reimbursableContract, + data: callData, + gas: BigInt(300000), + type: "eip1559", + account: this.walletClient.account, + chain: this.walletClient.chain, + }); + + return signedTx; + } + + /** + * Execute with reimbursement through the reimbursable gas station contract. + * The EOA pays for gas in USDC rather than the paymaster covering the cost. + * The contract pulls initialDepositUSDC from the EOA, executes the transaction + * with a gas limit of transactionGasLimitWei, and refunds any unused USDC. + * Call this with a paymaster client to submit the transaction (paymaster only pays initial gas). + */ + async executeWithReimbursement( + intent: ReimbursableExecutionIntent, + ): Promise<{ txHash: Hex; blockNumber: bigint; gasUsed: bigint }> { + // Pack the execution data (signature, nonce, deadline, args) + const packedExecutionData = packExecutionData({ + signature: intent.signature, + nonce: intent.nonce, + deadline: intent.deadline, + args: intent.callData, + }); + + const txHash = await this.walletClient.sendTransaction({ + to: this.reimbursableContract, + data: encodeFunctionData({ + abi: reimbursableGasStationAbi, + functionName: "executeReturns", + args: [ + intent.initialDepositUSDC, + intent.transactionGasLimitWei, + intent.sessionSignature, + intent.eoaAddress, + intent.outputContract, + intent.ethAmount, + packedExecutionData, + ], + }), + gas: BigInt(300000), + account: this.walletClient.account, + }); + + const receipt = await this.publicClient.waitForTransactionReceipt({ + hash: txHash, + }); + + if (receipt.status !== "success") { + // Try to get the revert reason if available + const revertReason = await this.getRevertReason(txHash); + throw new Error( + `Reimbursable execution failed: ${revertReason || "Transaction reverted"}. ` + + `Gas used: ${receipt.gasUsed}/${receipt.cumulativeGasUsed}. ` + + `Transaction hash: ${txHash}`, + ); + } + + return { + txHash, + blockNumber: receipt.blockNumber, + gasUsed: receipt.gasUsed, + }; + } } diff --git a/packages/gas-station/src/gasStationUtils.ts b/packages/gas-station/src/gasStationUtils.ts index 303bf9864..763f81b65 100644 --- a/packages/gas-station/src/gasStationUtils.ts +++ b/packages/gas-station/src/gasStationUtils.ts @@ -175,3 +175,29 @@ export function packExecutionData({ args, // variable length ]); } + +/** + * Packs session signature data for the reimbursable gas station. + * Used to authorize USDC transfers for gas payment. + * + * Packed data format: + * Layout: [signature(65)][nonce(16)][deadline(4)] + * - signature: bytes 0-64 (65 bytes) + * - nonce: bytes 65-80 (16 bytes, uint128) + * - deadline: bytes 81-84 (4 bytes, uint32) + */ +export function packSessionSignature({ + signature, + nonce, + deadline, +}: { + signature: Hex; + nonce: bigint; + deadline: number; +}): Hex { + return concat([ + signature, // 65 bytes + pad(toHex(nonce), { size: 16 }), // 16 bytes (uint128) + pad(toHex(deadline), { size: 4 }), // 4 bytes (uint32) + ]); +} diff --git a/packages/gas-station/src/index.ts b/packages/gas-station/src/index.ts index b32ce6759..58f3cfffa 100644 --- a/packages/gas-station/src/index.ts +++ b/packages/gas-station/src/index.ts @@ -10,6 +10,7 @@ export { buildETHTransferFromEther, buildContractCall, packExecutionData, + packSessionSignature, ERC20_ABI, } from "./gasStationUtils"; @@ -23,6 +24,7 @@ export { createCustomPreset, DEFAULT_EXECUTION_CONTRACT, DEFAULT_DELEGATE_CONTRACT, + DEFAULT_REIMBURSABLE_USDC_CONTRACT, } from "./config"; // Policy utilities @@ -41,4 +43,9 @@ export type { ContractCallParams, ExecutionIntent, ApprovalExecutionIntent, + ReimbursableExecutionIntent, } from "./config"; + +// ABI exports +export { gasStationAbi } from "./abi/gas-station"; +export { reimbursableGasStationAbi } from "./abi/reimbursable-gas-station"; diff --git a/packages/gas-station/src/intentBuilder.ts b/packages/gas-station/src/intentBuilder.ts index e5bc6fb33..d2273dc7f 100644 --- a/packages/gas-station/src/intentBuilder.ts +++ b/packages/gas-station/src/intentBuilder.ts @@ -11,7 +11,7 @@ import type { ExecutionIntent, ApprovalExecutionIntent, } from "./config"; -import { ERC20_ABI } from "./gasStationUtils"; +import { ERC20_ABI, packSessionSignature } from "./gasStationUtils"; interface IntentBuilderConfig { eoaWalletClient: WalletClient; @@ -260,6 +260,60 @@ export class IntentBuilder { }; } + /** + * Signs a session signature for USDC transfer authorization in the reimbursable gas station. + * This authorizes the reimbursable contract to interact with the USDC contract on behalf of the EOA. + * The session signature does NOT commit to a specific amount - amounts are specified at execution time. + * This allows the same session signature to be cached and reused for multiple transactions. + * Returns an 85-byte packed signature that can be passed to executeWithReimbursement(). + */ + async signSessionForUSDCTransfer( + currentNonce: bigint, + usdcAddress: Hex, + reimbursableContract: Hex, + sessionDeadline?: number, + ): Promise { + const nonce = this.nonce ?? currentNonce; + // Default deadline: 1 hour from now + const deadline = sessionDeadline ?? Math.floor(Date.now() / 1000) + 60 * 60; + + // EIP-712 domain and types for session execution + const domain = { + name: "TKGasDelegate", + version: "1", + chainId: this.config.chainId, + verifyingContract: this.config.eoaAddress, + }; + + // Based on hashSessionExecution from the delegate contract + // keccak256("SessionExecution(uint128 counter,uint32 deadline,address sender,address to)") + const types = { + SessionExecution: [ + { name: "counter", type: "uint128" }, + { name: "deadline", type: "uint32" }, + { name: "sender", type: "address" }, + { name: "to", type: "address" }, + ], + }; + + const message = { + counter: nonce, + deadline, + sender: reimbursableContract, + to: usdcAddress, + }; + + const signature = await this.config.eoaWalletClient.signTypedData({ + account: this.config.eoaWalletClient.account, + domain, + types, + primaryType: "SessionExecution", + message, + }); + + return packSessionSignature({ signature, nonce, deadline }); + } + // Static factory method for quick intent creation static create(config: IntentBuilderConfig): IntentBuilder { return new IntentBuilder(config); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0cfc8dcca..313a54e0c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -947,6 +947,15 @@ importers: '@turnkey/viem': specifier: workspace:* version: link:../../packages/viem + '@uniswap/sdk-core': + specifier: ^6.0.0 + version: 6.1.1 + '@uniswap/universal-router-sdk': + specifier: ^3.0.0 + version: 3.4.0(bufferutil@4.0.9)(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10) + '@uniswap/v3-sdk': + specifier: ^3.9.0 + version: 3.25.2(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10)) dotenv: specifier: ^16.0.3 version: 16.0.3 @@ -6547,6 +6556,9 @@ packages: '@openzeppelin/contracts@3.4.2-solc-0.7': resolution: {integrity: sha512-W6QmqgkADuFcTLzHL8vVoNBtkwjvQRpYIAom7KiUNoLKghyx3FgH0GBjt8NRvigV1ZmMOBllvE1By1C+bi8WpA==} + '@openzeppelin/contracts@4.7.0': + resolution: {integrity: sha512-52Qb+A1DdOss8QvJrijYYPSf32GUg2pGaG/yCxtaA3cu4jduouTdg4XZSMLW9op54m1jH7J8hoajhHKOPsoJFw==} + '@openzeppelin/contracts@5.0.2': resolution: {integrity: sha512-ytPc6eLGcHHnapAZ9S+5qsdomhjo6QBHTDRRBFfTxXIpsicMhVPouPgmUPebZZZGX7vt9USA+Z+0M0dSVtSUEA==} @@ -9131,24 +9143,54 @@ packages: resolution: {integrity: sha512-f6UIliwBbRsgVLxIaBANF6w09tYqc6Y/qXdsrbEmXHyFA7ILiKrIwRFXe1yOg8M3cksgVsO9N7yuL2DdCGQKBA==} engines: {node: '>=10'} + '@uniswap/permit2-sdk@1.4.0': + resolution: {integrity: sha512-l/aGhfhB93M76vXs4eB8QNwhELE6bs66kh7F1cyobaPtINaVpMmlJv+j3KmHeHwAZIsh7QXyYzhDxs07u0Pe4Q==} + + '@uniswap/router-sdk@1.23.0': + resolution: {integrity: sha512-KkHoMauTZh2N44sOU0ZuYseNNn9nAvaU57HwyCWjtwZdA7HaXtACfIRJbQvnkNNuALJfzHNkuv2aFyPSjNNmMw==} + '@uniswap/sdk-core@3.2.6': resolution: {integrity: sha512-MvH/3G0W0sM2g7XjaUy9qU7IabxL/KQp/ucU0AQGpVxiTaAhmVRtsjkkv9UDyzpIXVrmevl4kRgV7KKE29UuXA==} engines: {node: '>=10'} deprecated: breaking change required major version bump + '@uniswap/sdk-core@5.9.0': + resolution: {integrity: sha512-OME7WR6+5QwQs45A2079r+/FS0zU944+JCQwUX9GyIriCxqw2pGu4F9IEqmlwD+zSIMml0+MJnJJ47pFgSyWDw==} + engines: {node: '>=10'} + + '@uniswap/sdk-core@6.1.1': + resolution: {integrity: sha512-S9D5NTn7vV+wYwXbKOmYVjJidgmKY6zUsG5KGlQO4fNvcIde1TtVgtMXJl06qv1JeJKbGnzkIAZG4R82lSVZCg==} + engines: {node: '>=10'} + '@uniswap/sdk-core@7.7.2': resolution: {integrity: sha512-0KqXw+y0opBo6eoPAEoLHEkNpOu0NG9gEk7GAYIGok+SHX89WlykWsRYeJKTg9tOwhLpcG9oHg8xZgQ390iOrA==} engines: {node: '>=10'} + '@uniswap/sdk-core@7.9.0': + resolution: {integrity: sha512-HHUFNK3LMi4KMQCAiHkdUyL62g/nrZLvNT44CY8RN4p8kWO6XYWzqdQt6OcjCsIbhMZ/Ifhe6Py5oOoccg/jUQ==} + engines: {node: '>=10'} + '@uniswap/swap-router-contracts@1.3.1': resolution: {integrity: sha512-mh/YNbwKb7Mut96VuEtL+Z5bRe0xVIbjjiryn+iMMrK2sFKhR4duk/86mEz0UO5gSx4pQIw9G5276P5heY/7Rg==} engines: {node: '>=10'} deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. + '@uniswap/universal-router-sdk@3.4.0': + resolution: {integrity: sha512-EB/NLIkuT2BCdKnh2wcXT0cmINjRoiskjibFclpheALHL49XSrB08H4k7KV3BP6+JNKLeTHekvTDdsMd9rs5TA==} + engines: {node: '>=14'} + + '@uniswap/universal-router@2.0.0-beta.1': + resolution: {integrity: sha512-DdaMHaoDyJoCwpH+BiRKw/w2vjZtZS+ekpyrhmIeOBK1L2QEVFj977BNo6t24WzriZ9mSuIKF69RjHdXDUgHsQ==} + engines: {node: '>=14'} + '@uniswap/v2-core@1.0.1': resolution: {integrity: sha512-MtybtkUPSyysqLY2U210NBDeCHX+ltHt3oADGdjqoThZaFRDKwM6k1Nb3F0A3hk5hwuQvytFWhrWHOEq6nVJ8Q==} engines: {node: '>=10'} + '@uniswap/v2-sdk@4.16.0': + resolution: {integrity: sha512-USMm2qz1xhEX8R0dhd0mHzf6pz5aCLjbtud1ZyUBk+gshhUCFp6NW9UovH0L5hqrH03rTvmqQdfhHMW5m+Sosg==} + engines: {node: '>=10'} + '@uniswap/v3-core@1.0.0': resolution: {integrity: sha512-kSC4djMGKMHj7sLMYVnn61k9nu+lHjMIxgg9CDQT+s2QYLoA56GbSK9Oxr+qJXzzygbkrmuY6cwgP6cW2JXPFA==} engines: {node: '>=10'} @@ -9165,11 +9207,19 @@ packages: resolution: {integrity: sha512-0oiyJNGjUVbc958uZmAr+m4XBCjV7PfMs/OUeBv+XDl33MEYF/eH86oBhvqGDM8S/cYaK55tCXzoWkmRUByrHg==} engines: {node: '>=10'} + '@uniswap/v3-sdk@3.26.0': + resolution: {integrity: sha512-bcoWNE7ntNNTHMOnDPscIqtIN67fUyrbBKr6eswI2gD2wm5b0YYFBDeh+Qc5Q3117o9i8S7QdftqrU8YSMQUfQ==} + engines: {node: '>=10'} + '@uniswap/v3-staker@1.0.0': resolution: {integrity: sha512-JV0Qc46Px5alvg6YWd+UIaGH9lDuYG/Js7ngxPit1SPaIP30AlVer1UYB7BRYeUVVxE+byUyIeN5jeQ7LLDjIw==} engines: {node: '>=10'} deprecated: Please upgrade to 1.0.1 + '@uniswap/v4-sdk@1.23.0': + resolution: {integrity: sha512-WpnkNacNTe/qL4kj3DVC2nHaivUeuzYsWIvon+olAWYZyy+Frsnzfon/ZlznDifMPoV+im+MqYFsNQke4Vz3LA==} + engines: {node: '>=14'} + '@unrs/resolver-binding-android-arm-eabi@1.11.1': resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} cpu: [arm] @@ -18821,15 +18871,15 @@ snapshots: '@ethersproject/abi@5.7.0': dependencies: - '@ethersproject/address': 5.7.0 + '@ethersproject/address': 5.8.0 '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/constants': 5.7.0 '@ethersproject/hash': 5.7.0 - '@ethersproject/keccak256': 5.7.0 + '@ethersproject/keccak256': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 + '@ethersproject/strings': 5.8.0 '@ethersproject/abi@5.8.0': dependencies: @@ -18846,7 +18896,7 @@ snapshots: '@ethersproject/abstract-provider@5.7.0': dependencies: '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/networks': 5.7.1 '@ethersproject/properties': 5.7.0 @@ -18867,7 +18917,7 @@ snapshots: dependencies: '@ethersproject/abstract-provider': 5.7.0 '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 @@ -18882,8 +18932,8 @@ snapshots: '@ethersproject/address@5.7.0': dependencies: '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/keccak256': 5.7.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/keccak256': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/rlp': 5.7.0 @@ -18897,7 +18947,7 @@ snapshots: '@ethersproject/base64@5.7.0': dependencies: - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/base64@5.8.0': dependencies: @@ -18905,12 +18955,12 @@ snapshots: '@ethersproject/basex@5.7.0': dependencies: - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/properties': 5.7.0 '@ethersproject/bignumber@5.7.0': dependencies: - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/logger': 5.7.0 bn.js: 5.2.2 @@ -18938,12 +18988,12 @@ snapshots: '@ethersproject/contracts@5.7.0': dependencies: - '@ethersproject/abi': 5.7.0 + '@ethersproject/abi': 5.8.0 '@ethersproject/abstract-provider': 5.7.0 '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 + '@ethersproject/address': 5.8.0 '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/constants': 5.7.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 @@ -18965,14 +19015,14 @@ snapshots: '@ethersproject/hash@5.7.0': dependencies: '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 + '@ethersproject/address': 5.8.0 '@ethersproject/base64': 5.7.0 '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/keccak256': 5.7.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/keccak256': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 + '@ethersproject/strings': 5.8.0 '@ethersproject/hash@5.8.0': dependencies: @@ -18991,28 +19041,28 @@ snapshots: '@ethersproject/abstract-signer': 5.7.0 '@ethersproject/basex': 5.7.0 '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/pbkdf2': 5.7.0 '@ethersproject/properties': 5.7.0 '@ethersproject/sha2': 5.7.0 '@ethersproject/signing-key': 5.7.0 - '@ethersproject/strings': 5.7.0 + '@ethersproject/strings': 5.8.0 '@ethersproject/transactions': 5.7.0 '@ethersproject/wordlists': 5.7.0 '@ethersproject/json-wallets@5.7.0': dependencies: '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/address': 5.8.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/hdnode': 5.7.0 - '@ethersproject/keccak256': 5.7.0 + '@ethersproject/keccak256': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/pbkdf2': 5.7.0 '@ethersproject/properties': 5.7.0 '@ethersproject/random': 5.7.0 - '@ethersproject/strings': 5.7.0 + '@ethersproject/strings': 5.8.0 '@ethersproject/transactions': 5.7.0 aes-js: 3.0.0 scrypt-js: 3.0.1 @@ -19041,7 +19091,7 @@ snapshots: '@ethersproject/pbkdf2@5.7.0': dependencies: - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/sha2': 5.7.0 '@ethersproject/properties@5.7.0': @@ -19056,11 +19106,11 @@ snapshots: dependencies: '@ethersproject/abstract-provider': 5.7.0 '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 + '@ethersproject/address': 5.8.0 '@ethersproject/base64': 5.7.0 '@ethersproject/basex': 5.7.0 '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/constants': 5.7.0 '@ethersproject/hash': 5.7.0 '@ethersproject/logger': 5.7.0 @@ -19069,7 +19119,7 @@ snapshots: '@ethersproject/random': 5.7.0 '@ethersproject/rlp': 5.7.0 '@ethersproject/sha2': 5.7.0 - '@ethersproject/strings': 5.7.0 + '@ethersproject/strings': 5.8.0 '@ethersproject/transactions': 5.7.0 '@ethersproject/web': 5.7.1 bech32: 1.1.4 @@ -19080,12 +19130,12 @@ snapshots: '@ethersproject/random@5.7.0': dependencies: - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/rlp@5.7.0': dependencies: - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/rlp@5.8.0': @@ -19095,7 +19145,7 @@ snapshots: '@ethersproject/sha2@5.7.0': dependencies: - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/logger': 5.7.0 hash.js: 1.1.7 @@ -19107,7 +19157,7 @@ snapshots: '@ethersproject/signing-key@5.7.0': dependencies: - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 bn.js: 5.2.2 @@ -19126,11 +19176,11 @@ snapshots: '@ethersproject/solidity@5.7.0': dependencies: '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 - '@ethersproject/keccak256': 5.7.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/keccak256': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/sha2': 5.7.0 - '@ethersproject/strings': 5.7.0 + '@ethersproject/strings': 5.8.0 '@ethersproject/solidity@5.8.0': dependencies: @@ -19155,11 +19205,11 @@ snapshots: '@ethersproject/transactions@5.7.0': dependencies: - '@ethersproject/address': 5.7.0 + '@ethersproject/address': 5.8.0 '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/constants': 5.7.0 - '@ethersproject/keccak256': 5.7.0 + '@ethersproject/keccak256': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 '@ethersproject/rlp': 5.7.0 @@ -19187,13 +19237,13 @@ snapshots: dependencies: '@ethersproject/abstract-provider': 5.7.0 '@ethersproject/abstract-signer': 5.7.0 - '@ethersproject/address': 5.7.0 + '@ethersproject/address': 5.8.0 '@ethersproject/bignumber': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/hash': 5.7.0 '@ethersproject/hdnode': 5.7.0 '@ethersproject/json-wallets': 5.7.0 - '@ethersproject/keccak256': 5.7.0 + '@ethersproject/keccak256': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 '@ethersproject/random': 5.7.0 @@ -19204,10 +19254,10 @@ snapshots: '@ethersproject/web@5.7.1': dependencies: '@ethersproject/base64': 5.7.0 - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 + '@ethersproject/strings': 5.8.0 '@ethersproject/web@5.8.0': dependencies: @@ -19219,11 +19269,11 @@ snapshots: '@ethersproject/wordlists@5.7.0': dependencies: - '@ethersproject/bytes': 5.7.0 + '@ethersproject/bytes': 5.8.0 '@ethersproject/hash': 5.7.0 '@ethersproject/logger': 5.7.0 '@ethersproject/properties': 5.7.0 - '@ethersproject/strings': 5.7.0 + '@ethersproject/strings': 5.8.0 '@farcaster/frame-core@0.0.26(typescript@5.4.3)': dependencies: @@ -20766,6 +20816,8 @@ snapshots: '@openzeppelin/contracts@3.4.2-solc-0.7': {} + '@openzeppelin/contracts@4.7.0': {} + '@openzeppelin/contracts@5.0.2': {} '@particle-network/analytics@1.0.2': @@ -25860,6 +25912,25 @@ snapshots: '@uniswap/lib@4.0.1-alpha': {} + '@uniswap/permit2-sdk@1.4.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)': + dependencies: + ethers: 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + tiny-invariant: 1.3.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + '@uniswap/router-sdk@1.23.0(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10))': + dependencies: + '@ethersproject/abi': 5.8.0 + '@uniswap/sdk-core': 7.7.2 + '@uniswap/swap-router-contracts': 1.3.1(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10)) + '@uniswap/v2-sdk': 4.16.0 + '@uniswap/v3-sdk': 3.25.2(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10)) + '@uniswap/v4-sdk': 1.23.0(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10)) + transitivePeerDependencies: + - hardhat + '@uniswap/sdk-core@3.2.6': dependencies: '@ethersproject/address': 5.8.0 @@ -25869,6 +25940,30 @@ snapshots: tiny-invariant: 1.3.3 toformat: 2.0.0 + '@uniswap/sdk-core@5.9.0': + dependencies: + '@ethersproject/address': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/strings': 5.7.0 + big.js: 5.2.2 + decimal.js-light: 2.5.1 + jsbi: 3.2.5 + tiny-invariant: 1.3.3 + toformat: 2.0.0 + + '@uniswap/sdk-core@6.1.1': + dependencies: + '@ethersproject/address': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/strings': 5.7.0 + big.js: 5.2.2 + decimal.js-light: 2.5.1 + jsbi: 3.2.5 + tiny-invariant: 1.3.3 + toformat: 2.0.0 + '@uniswap/sdk-core@7.7.2': dependencies: '@ethersproject/address': 5.8.0 @@ -25881,6 +25976,18 @@ snapshots: tiny-invariant: 1.3.3 toformat: 2.0.0 + '@uniswap/sdk-core@7.9.0': + dependencies: + '@ethersproject/address': 5.8.0 + '@ethersproject/bytes': 5.8.0 + '@ethersproject/keccak256': 5.7.0 + '@ethersproject/strings': 5.7.0 + big.js: 5.2.2 + decimal.js-light: 2.5.1 + jsbi: 3.2.5 + tiny-invariant: 1.3.3 + toformat: 2.0.0 + '@uniswap/swap-router-contracts@1.3.1(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10))': dependencies: '@openzeppelin/contracts': 3.4.2-solc-0.7 @@ -25892,8 +25999,41 @@ snapshots: transitivePeerDependencies: - hardhat + '@uniswap/universal-router-sdk@3.4.0(bufferutil@4.0.9)(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10))(utf-8-validate@5.0.10)': + dependencies: + '@openzeppelin/contracts': 4.7.0 + '@uniswap/permit2-sdk': 1.4.0(bufferutil@4.0.9)(utf-8-validate@5.0.10) + '@uniswap/router-sdk': 1.23.0(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10)) + '@uniswap/sdk-core': 5.9.0 + '@uniswap/universal-router': 2.0.0-beta.1 + '@uniswap/v2-core': 1.0.1 + '@uniswap/v2-sdk': 4.16.0 + '@uniswap/v3-core': 1.0.0 + '@uniswap/v3-sdk': 3.25.2(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10)) + '@uniswap/v4-sdk': 1.23.0(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10)) + bignumber.js: 9.3.1 + ethers: 5.7.2(bufferutil@4.0.9)(utf-8-validate@5.0.10) + transitivePeerDependencies: + - bufferutil + - hardhat + - utf-8-validate + + '@uniswap/universal-router@2.0.0-beta.1': + dependencies: + '@openzeppelin/contracts': 5.0.2 + '@uniswap/v2-core': 1.0.1 + '@uniswap/v3-core': 1.0.0 + '@uniswap/v2-core@1.0.1': {} + '@uniswap/v2-sdk@4.16.0': + dependencies: + '@ethersproject/address': 5.8.0 + '@ethersproject/solidity': 5.8.0 + '@uniswap/sdk-core': 7.9.0 + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + '@uniswap/v3-core@1.0.0': {} '@uniswap/v3-core@1.0.1': {} @@ -25919,12 +26059,35 @@ snapshots: transitivePeerDependencies: - hardhat + '@uniswap/v3-sdk@3.26.0(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10))': + dependencies: + '@ethersproject/abi': 5.8.0 + '@ethersproject/solidity': 5.8.0 + '@uniswap/sdk-core': 7.9.0 + '@uniswap/swap-router-contracts': 1.3.1(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10)) + '@uniswap/v3-periphery': 1.4.4 + '@uniswap/v3-staker': 1.0.0 + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + transitivePeerDependencies: + - hardhat + '@uniswap/v3-staker@1.0.0': dependencies: '@openzeppelin/contracts': 3.4.1-solc-0.7-2 '@uniswap/v3-core': 1.0.0 '@uniswap/v3-periphery': 1.4.4 + '@uniswap/v4-sdk@1.23.0(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10))': + dependencies: + '@ethersproject/solidity': 5.8.0 + '@uniswap/sdk-core': 7.9.0 + '@uniswap/v3-sdk': 3.26.0(hardhat@2.26.3(bufferutil@4.0.9)(ts-node@10.9.2(@types/node@24.7.1)(typescript@5.4.3))(typescript@5.4.3)(utf-8-validate@5.0.10)) + tiny-invariant: 1.3.3 + tiny-warning: 1.0.3 + transitivePeerDependencies: + - hardhat + '@unrs/resolver-binding-android-arm-eabi@1.11.1': optional: true