-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbuildSwapCallData.ts
More file actions
132 lines (120 loc) · 3.49 KB
/
Copy pathbuildSwapCallData.ts
File metadata and controls
132 lines (120 loc) · 3.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import { calculateMinimumOutput } from "@/helpers/swap";
import type { BuildSwapCallDataParams } from "@/types";
import { COMMANDS } from "@/types";
import type { UniDevKitV4Instance } from "@/types/core";
import { getQuote } from "@/utils/getQuote";
import { ethers } from "ethers";
import type { Hex } from "viem";
/**
* Builds calldata for a Uniswap V4 swap
*
* This function creates the necessary calldata to execute a token swap through
* Uniswap V4's Universal Router. It handles pool discovery, parameter encoding,
* and deadline management.
*
* @param params - Swap configuration parameters
* @param instance - UniDevKitV4 instance for pool operations
* @returns Promise resolving to encoded calldata
*
* @throws Error if pool doesn't exist
*
* @example
* ```typescript
* const swapParams = {
* tokenIn: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", // USDC
* amountIn: parseUnits("100", 6), // 100 USDC
* pool: pool,
* slippageTolerance: 50, // 0.5%
* };
*
* const calldata = await buildSwapCallData(swapParams);
*
* // Send transaction
* const tx = await sendTransaction({
* to: universalRouterAddress,
* data: calldata,
* value: 0,
* });
* ```
*/
export async function buildSwapCallData(
params: BuildSwapCallDataParams,
instance: UniDevKitV4Instance,
): Promise<Hex> {
// Extract and set default parameters
const { tokenIn, amountIn, pool, slippageTolerance = 50 } = params;
const zeroForOne =
tokenIn.toLowerCase() === pool.poolKey.currency0.toLowerCase();
// Get quote and calculate minimum output amount
const quote = await getQuote(
{
pool,
amountIn,
zeroForOne,
},
instance,
);
// Calculate minimum output amount based on slippage
const amountOutMin = calculateMinimumOutput(
quote.amountOut,
slippageTolerance,
);
// Encode Universal Router commands
const commands = ethers.utils.solidityPack(
["uint8"],
[COMMANDS.V4_ROUTER_EXECUTE],
);
// Encode swap actions sequence
const actions = ethers.utils.solidityPack(
["uint8", "uint8", "uint8"],
[COMMANDS.SWAP_EXACT_IN_SINGLE, COMMANDS.SETTLE_ALL, COMMANDS.TAKE_ALL],
);
// Encode swap parameters
const exactInputSingleParams = ethers.utils.defaultAbiCoder.encode(
[
"tuple(tuple(address currency0, address currency1, uint24 fee, int24 tickSpacing, address hooks) poolKey, bool zeroForOne, uint128 amountIn, uint128 amountOutMinimum, bytes hookData)",
],
[
{
poolKey: pool.poolKey,
zeroForOne,
amountIn: ethers.BigNumber.from(amountIn.toString()),
amountOutMinimum: amountOutMin,
hookData: "0x",
},
],
);
// Encode token amounts for settlement
const swapParams = [
exactInputSingleParams,
ethers.utils.defaultAbiCoder.encode(
["address", "uint128"],
zeroForOne
? [pool.poolKey.currency0, amountIn]
: [pool.poolKey.currency1, amountIn],
),
ethers.utils.defaultAbiCoder.encode(
["address", "uint128"],
zeroForOne ? [pool.poolKey.currency1, 0] : [pool.poolKey.currency0, 0],
),
];
// Encode final inputs
const inputs = [
ethers.utils.defaultAbiCoder.encode(
["bytes", "bytes[]"],
[actions, swapParams],
),
];
// Set 5-minute deadline
const deadline = BigInt(Math.floor(Date.now() / 1000) + 60 * 5);
// Create Universal Router interface
const universalRouterInterface = new ethers.utils.Interface([
"function execute(bytes commands, bytes[] inputs, uint256 deadline)",
]);
// Encode final calldata
return universalRouterInterface.encodeFunctionData("execute", [
commands,
inputs,
deadline,
]) as Hex;
}