levr-sdk 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (108) hide show
  1. package/README.md +109 -13
  2. package/dist/esm/abis/StateView.js +149 -0
  3. package/dist/esm/abis/V3QuoterV2.js +26 -0
  4. package/dist/esm/abis/index.js +4 -0
  5. package/dist/esm/balance.js +30 -4
  6. package/dist/esm/balance.js.map +1 -1
  7. package/dist/esm/client/hook/use-balance.js +2 -1
  8. package/dist/esm/client/hook/use-balance.js.map +1 -1
  9. package/dist/esm/client/hook/use-fee-receivers.js +0 -1
  10. package/dist/esm/client/hook/use-fee-receivers.js.map +1 -1
  11. package/dist/esm/client/hook/use-governance.js +13 -32
  12. package/dist/esm/client/hook/use-governance.js.map +1 -1
  13. package/dist/esm/client/hook/use-project.js +7 -10
  14. package/dist/esm/client/hook/use-project.js.map +1 -1
  15. package/dist/esm/client/hook/use-proposals.js +2 -4
  16. package/dist/esm/client/hook/use-proposals.js.map +1 -1
  17. package/dist/esm/client/hook/use-stake.js +3 -23
  18. package/dist/esm/client/hook/use-stake.js.map +1 -1
  19. package/dist/esm/client/hook/use-swap.js +14 -6
  20. package/dist/esm/client/hook/use-swap.js.map +1 -1
  21. package/dist/esm/client/levr-provider.js +9 -2
  22. package/dist/esm/client/levr-provider.js.map +1 -1
  23. package/dist/esm/client/query-keys.js +1 -1
  24. package/dist/esm/client/query-keys.js.map +1 -1
  25. package/dist/esm/constants.js +41 -0
  26. package/dist/esm/constants.js.map +1 -1
  27. package/dist/esm/fee-receivers.js +5 -2
  28. package/dist/esm/fee-receivers.js.map +1 -1
  29. package/dist/esm/governance.js +20 -5
  30. package/dist/esm/governance.js.map +1 -1
  31. package/dist/esm/index.js +3 -1
  32. package/dist/esm/index.js.map +1 -1
  33. package/dist/esm/pool-key.js +152 -0
  34. package/dist/esm/pool-key.js.map +1 -0
  35. package/dist/esm/project.js +48 -5
  36. package/dist/esm/project.js.map +1 -1
  37. package/dist/esm/projects.js +2 -30
  38. package/dist/esm/projects.js.map +1 -1
  39. package/dist/esm/quote/index.js +98 -0
  40. package/dist/esm/quote/index.js.map +1 -0
  41. package/dist/esm/quote/v3.js +62 -0
  42. package/dist/esm/quote/v3.js.map +1 -0
  43. package/dist/esm/quote/v4.js +228 -0
  44. package/dist/esm/quote/v4.js.map +1 -0
  45. package/dist/esm/stake.js +102 -40
  46. package/dist/esm/stake.js.map +1 -1
  47. package/dist/esm/usd-price.js +149 -0
  48. package/dist/esm/usd-price.js.map +1 -0
  49. package/dist/esm/util.js +45 -1
  50. package/dist/esm/util.js.map +1 -1
  51. package/dist/types/abis/StateView.d.ts +278 -0
  52. package/dist/types/abis/V3QuoterV2.d.ts +39 -0
  53. package/dist/types/abis/index.d.ts +2 -0
  54. package/dist/types/balance.d.ts +4 -6
  55. package/dist/types/balance.d.ts.map +1 -1
  56. package/dist/types/client/hook/index.d.ts +1 -1
  57. package/dist/types/client/hook/index.d.ts.map +1 -1
  58. package/dist/types/client/hook/use-balance.d.ts +3 -1
  59. package/dist/types/client/hook/use-balance.d.ts.map +1 -1
  60. package/dist/types/client/hook/use-fee-receivers.d.ts +1 -1
  61. package/dist/types/client/hook/use-fee-receivers.d.ts.map +1 -1
  62. package/dist/types/client/hook/use-governance.d.ts +20 -15
  63. package/dist/types/client/hook/use-governance.d.ts.map +1 -1
  64. package/dist/types/client/hook/use-project.d.ts +3 -1
  65. package/dist/types/client/hook/use-project.d.ts.map +1 -1
  66. package/dist/types/client/hook/use-proposals.d.ts +1 -1
  67. package/dist/types/client/hook/use-proposals.d.ts.map +1 -1
  68. package/dist/types/client/hook/use-stake.d.ts +37 -132
  69. package/dist/types/client/hook/use-stake.d.ts.map +1 -1
  70. package/dist/types/client/hook/use-swap.d.ts +2 -1
  71. package/dist/types/client/hook/use-swap.d.ts.map +1 -1
  72. package/dist/types/client/levr-provider.d.ts +31 -38
  73. package/dist/types/client/levr-provider.d.ts.map +1 -1
  74. package/dist/types/client/query-keys.d.ts +1 -1
  75. package/dist/types/client/query-keys.d.ts.map +1 -1
  76. package/dist/types/constants.d.ts +18 -0
  77. package/dist/types/constants.d.ts.map +1 -1
  78. package/dist/types/fee-receivers.d.ts +1 -2
  79. package/dist/types/fee-receivers.d.ts.map +1 -1
  80. package/dist/types/governance.d.ts +8 -21
  81. package/dist/types/governance.d.ts.map +1 -1
  82. package/dist/types/index.d.ts +3 -1
  83. package/dist/types/index.d.ts.map +1 -1
  84. package/dist/types/pool-key.d.ts +121 -0
  85. package/dist/types/pool-key.d.ts.map +1 -0
  86. package/dist/types/project.d.ts +7 -12
  87. package/dist/types/project.d.ts.map +1 -1
  88. package/dist/types/projects.d.ts +1 -1
  89. package/dist/types/projects.d.ts.map +1 -1
  90. package/dist/types/quote/index.d.ts +97 -0
  91. package/dist/types/quote/index.d.ts.map +1 -0
  92. package/dist/types/quote/v3.d.ts +78 -0
  93. package/dist/types/quote/v3.d.ts.map +1 -0
  94. package/dist/types/quote/v4.d.ts +95 -0
  95. package/dist/types/quote/v4.d.ts.map +1 -0
  96. package/dist/types/stake.d.ts +38 -59
  97. package/dist/types/stake.d.ts.map +1 -1
  98. package/dist/types/types.d.ts +15 -0
  99. package/dist/types/types.d.ts.map +1 -1
  100. package/dist/types/usd-price.d.ts +141 -0
  101. package/dist/types/usd-price.d.ts.map +1 -0
  102. package/dist/types/util.d.ts +17 -0
  103. package/dist/types/util.d.ts.map +1 -1
  104. package/package.json +1 -1
  105. package/dist/esm/quote-v4.js +0 -169
  106. package/dist/esm/quote-v4.js.map +0 -1
  107. package/dist/types/quote-v4.d.ts +0 -54
  108. package/dist/types/quote-v4.d.ts.map +0 -1
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @description Unified quote API for Uniswap V3 and V4
3
+ *
4
+ * @remarks
5
+ * This module provides quote functionality for both Uniswap V3 and V4 pools.
6
+ * Each version offers two methods:
7
+ * - `read`: Performs an async call to get the quote immediately
8
+ * - `bytecode`: Returns encoded call data for use in multicalls or custom execution
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // V3 Read (immediate result)
13
+ * const v3Quote = await quote.v3.read({
14
+ * publicClient,
15
+ * quoterAddress: '0x...',
16
+ * tokenIn: '0x123...',
17
+ * tokenOut: '0x456...',
18
+ * amountIn: parseUnits('1', 18),
19
+ * fee: 3000,
20
+ * })
21
+ * console.log(`Output: ${formatUnits(v3Quote.amountOut, 6)}`)
22
+ *
23
+ * // V3 Bytecode (for multicall)
24
+ * const v3Bytecode = quote.v3.bytecode({
25
+ * quoterAddress: '0x...',
26
+ * tokenIn: '0x123...',
27
+ * tokenOut: '0x456...',
28
+ * amountIn: parseUnits('1', 18),
29
+ * fee: 3000,
30
+ * })
31
+ * // Use v3Bytecode.address and v3Bytecode.data in multicall
32
+ *
33
+ * // V4 Read (immediate result with hook fees and price impact)
34
+ * const v4Quote = await quote.v4.read({
35
+ * publicClient,
36
+ * poolKey,
37
+ * zeroForOne: true,
38
+ * amountIn: parseEther('1'),
39
+ * pricing,
40
+ * tokenAddress: '0x...',
41
+ * })
42
+ * console.log(`Output: ${formatEther(v4Quote.amountOut)}`)
43
+ * console.log(`Price Impact: ${v4Quote.priceImpactBps}%`)
44
+ * console.log(`Hook Fees:`, v4Quote.hookFees)
45
+ *
46
+ * // V4 Bytecode (for multicall)
47
+ * const v4Bytecode = quote.v4.bytecode({
48
+ * publicClient, // Needed for chain ID
49
+ * poolKey,
50
+ * zeroForOne: true,
51
+ * amountIn: parseEther('1'),
52
+ * })
53
+ * // Use v4Bytecode.address and v4Bytecode.data in multicall
54
+ * ```
55
+ */
56
+ // Import implementations
57
+ import { quoteV3Read, quoteV3Bytecode } from "./v3.js";
58
+ import { quoteV4Read, quoteV4Bytecode } from "./v4.js";
59
+ /**
60
+ * @description Unified quote API for Uniswap V3 and V4
61
+ */
62
+ export const quote = {
63
+ /**
64
+ * Uniswap V3 quote methods
65
+ */
66
+ v3: {
67
+ /**
68
+ * Get a V3 quote by reading from the quoter contract
69
+ * @param params Quote parameters
70
+ * @returns Quote result with output amount
71
+ */
72
+ read: quoteV3Read,
73
+ /**
74
+ * Get encoded bytecode for a V3 quote (for multicalls)
75
+ * @param params Quote parameters
76
+ * @returns Contract address and encoded call data
77
+ */
78
+ bytecode: quoteV3Bytecode,
79
+ },
80
+ /**
81
+ * Uniswap V4 quote methods
82
+ */
83
+ v4: {
84
+ /**
85
+ * Get a V4 quote by reading from the quoter contract
86
+ * @param params Quote parameters
87
+ * @returns Quote result with output amount, gas estimate, price impact, and hook fees
88
+ */
89
+ read: quoteV4Read,
90
+ /**
91
+ * Get encoded bytecode for a V4 quote (for multicalls)
92
+ * @param params Quote parameters
93
+ * @returns Contract address and encoded call data
94
+ */
95
+ bytecode: quoteV4Bytecode,
96
+ },
97
+ };
98
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/quote/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsDG;AAeH,yBAAyB;AACzB,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,MAAM,CAAA;AAEnD;;GAEG;AACH,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB;;OAEG;IACH,EAAE,EAAE;QACF;;;;WAIG;QACH,IAAI,EAAE,WAAW;QACjB;;;;WAIG;QACH,QAAQ,EAAE,eAAe;KAC1B;IACD;;OAEG;IACH,EAAE,EAAE;QACF;;;;WAIG;QACH,IAAI,EAAE,WAAW;QACjB;;;;WAIG;QACH,QAAQ,EAAE,eAAe;KAC1B;CACF,CAAA"}
@@ -0,0 +1,62 @@
1
+ import { encodeFunctionData } from "viem";
2
+ import { V3QuoterV2 } from "../abis/index.js";
3
+ // ============================================================================
4
+ // V3 Implementation
5
+ // ============================================================================
6
+ /**
7
+ * @description Quote a swap on Uniswap V3 by reading from the quoter contract
8
+ * @param params Parameters for V3 quote
9
+ * @returns Output amount and fee tier
10
+ */
11
+ export const quoteV3Read = async (params) => {
12
+ if (!params.publicClient) {
13
+ throw new Error("publicClient is required for read method");
14
+ }
15
+ const { publicClient, quoterAddress, tokenIn, tokenOut, amountIn, fee, sqrtPriceLimitX96 = 0n, } = params;
16
+ const result = await publicClient.simulateContract({
17
+ address: quoterAddress,
18
+ abi: V3QuoterV2,
19
+ functionName: "quoteExactInputSingle",
20
+ args: [
21
+ {
22
+ tokenIn,
23
+ tokenOut,
24
+ amountIn,
25
+ fee,
26
+ sqrtPriceLimitX96,
27
+ },
28
+ ],
29
+ });
30
+ const [amountOut] = result.result;
31
+ return {
32
+ amountOut,
33
+ fee,
34
+ };
35
+ };
36
+ /**
37
+ * @description Get bytecode for a V3 quote that can be used in multicalls
38
+ * @param params Parameters for V3 quote
39
+ * @returns Contract address, encoded call data, and ABI
40
+ */
41
+ export const quoteV3Bytecode = (params) => {
42
+ const { quoterAddress, tokenIn, tokenOut, amountIn, fee, sqrtPriceLimitX96 = 0n } = params;
43
+ const data = encodeFunctionData({
44
+ abi: V3QuoterV2,
45
+ functionName: "quoteExactInputSingle",
46
+ args: [
47
+ {
48
+ tokenIn,
49
+ tokenOut,
50
+ amountIn,
51
+ fee,
52
+ sqrtPriceLimitX96,
53
+ },
54
+ ],
55
+ });
56
+ return {
57
+ address: quoterAddress,
58
+ data,
59
+ abi: V3QuoterV2,
60
+ };
61
+ };
62
+ //# sourceMappingURL=v3.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v3.js","sourceRoot":"","sources":["../../../src/quote/v3.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,MAAM,CAAA;AAEzC,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAwEpC,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,MAAqB,EAAkC,EAAE;IACzF,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;IAC7D,CAAC;IAED,MAAM,EACJ,YAAY,EACZ,aAAa,EACb,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,GAAG,EACH,iBAAiB,GAAG,EAAE,GACvB,GAAG,MAAM,CAAA;IAEV,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,gBAAgB,CAAC;QACjD,OAAO,EAAE,aAAa;QACtB,GAAG,EAAE,UAAU;QACf,YAAY,EAAE,uBAAuB;QACrC,IAAI,EAAE;YACJ;gBACE,OAAO;gBACP,QAAQ;gBACR,QAAQ;gBACR,GAAG;gBACH,iBAAiB;aAClB;SACF;KACF,CAAC,CAAA;IAEF,MAAM,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,CAAA;IAEjC,OAAO;QACL,SAAS;QACT,GAAG;KACJ,CAAA;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAqB,EAA6B,EAAE;IAClF,MAAM,EAAE,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAG,EAAE,iBAAiB,GAAG,EAAE,EAAE,GAAG,MAAM,CAAA;IAE1F,MAAM,IAAI,GAAG,kBAAkB,CAAC;QAC9B,GAAG,EAAE,UAAU;QACf,YAAY,EAAE,uBAAuB;QACrC,IAAI,EAAE;YACJ;gBACE,OAAO;gBACP,QAAQ;gBACR,QAAQ;gBACR,GAAG;gBACH,iBAAiB;aAClB;SACF;KACF,CAAC,CAAA;IAEF,OAAO;QACL,OAAO,EAAE,aAAa;QACtB,IAAI;QACJ,GAAG,EAAE,UAAU;KAChB,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,228 @@
1
+ import { encodeAbiParameters, encodeFunctionData, formatUnits, keccak256 } from "viem";
2
+ import { IClankerHookDynamicFee, IClankerHookStaticFee, V4Quoter } from "../abis/index.js";
3
+ import { UNISWAP_V4_QUOTER } from "../constants.js";
4
+ // ============================================================================
5
+ // V4 Helper Functions
6
+ // ============================================================================
7
+ /**
8
+ * @description Try to get static fees from a Clanker hook using multicall
9
+ */
10
+ const tryGetStaticFees = async (publicClient, hookAddress, poolId) => {
11
+ try {
12
+ const results = await publicClient.multicall({
13
+ contracts: [
14
+ {
15
+ address: hookAddress,
16
+ abi: IClankerHookStaticFee,
17
+ functionName: "clankerFee",
18
+ args: [poolId],
19
+ },
20
+ {
21
+ address: hookAddress,
22
+ abi: IClankerHookStaticFee,
23
+ functionName: "pairedFee",
24
+ args: [poolId],
25
+ },
26
+ ],
27
+ });
28
+ if (results[0].status === "success" && results[1].status === "success") {
29
+ return {
30
+ clankerFee: Number(results[0].result),
31
+ pairedFee: Number(results[1].result),
32
+ };
33
+ }
34
+ return undefined;
35
+ }
36
+ catch {
37
+ return undefined;
38
+ }
39
+ };
40
+ /**
41
+ * @description Try to get dynamic fee configuration from a Clanker hook
42
+ */
43
+ const tryGetDynamicFees = async (publicClient, hookAddress, poolId) => {
44
+ try {
45
+ const config = await publicClient.readContract({
46
+ address: hookAddress,
47
+ abi: IClankerHookDynamicFee,
48
+ functionName: "poolConfigVars",
49
+ args: [poolId],
50
+ });
51
+ return {
52
+ baseFee: Number(config.baseFee),
53
+ maxLpFee: Number(config.maxLpFee),
54
+ };
55
+ }
56
+ catch {
57
+ return undefined;
58
+ }
59
+ };
60
+ /**
61
+ * @description Get hook fee information from Clanker hooks
62
+ */
63
+ const getHookFees = async (publicClient, poolKey) => {
64
+ // Generate pool ID for hook queries (keccak256 of abi.encode(PoolKey))
65
+ const poolId = keccak256(encodeAbiParameters([
66
+ {
67
+ type: "tuple",
68
+ components: [
69
+ { name: "currency0", type: "address" },
70
+ { name: "currency1", type: "address" },
71
+ { name: "fee", type: "uint24" },
72
+ { name: "tickSpacing", type: "int24" },
73
+ { name: "hooks", type: "address" },
74
+ ],
75
+ },
76
+ ], [poolKey]));
77
+ // Try static fees first
78
+ const staticFees = await tryGetStaticFees(publicClient, poolKey.hooks, poolId);
79
+ if (staticFees) {
80
+ return {
81
+ type: "static",
82
+ clankerFee: staticFees.clankerFee,
83
+ pairedFee: staticFees.pairedFee,
84
+ };
85
+ }
86
+ // Try dynamic fees
87
+ const dynamicFees = await tryGetDynamicFees(publicClient, poolKey.hooks, poolId);
88
+ if (dynamicFees) {
89
+ return {
90
+ type: "dynamic",
91
+ baseFee: dynamicFees.baseFee,
92
+ maxLpFee: dynamicFees.maxLpFee,
93
+ };
94
+ }
95
+ return undefined;
96
+ };
97
+ /**
98
+ * @description Calculate price impact using USD pricing
99
+ * Compares the execution price to the market spot price
100
+ */
101
+ const calculatePriceImpact = (amountIn, amountOut, currency0, currency1, currency0Decimals, currency1Decimals, tokenAddress, pricing, zeroForOne) => {
102
+ try {
103
+ // Determine which currency is the token and which is WETH
104
+ const currency0IsToken = currency0.toLowerCase() === tokenAddress.toLowerCase();
105
+ // Format amounts to decimal strings
106
+ const amountInFormatted = parseFloat(formatUnits(amountIn, zeroForOne ? currency0Decimals : currency1Decimals));
107
+ const amountOutFormatted = parseFloat(formatUnits(amountOut, zeroForOne ? currency1Decimals : currency0Decimals));
108
+ if (amountInFormatted === 0 || amountOutFormatted === 0)
109
+ return undefined;
110
+ // Get market spot prices
111
+ const wethPrice = parseFloat(pricing.wethUsd);
112
+ const tokenPrice = parseFloat(pricing.tokenUsd);
113
+ if (tokenPrice === 0 || wethPrice === 0)
114
+ return undefined;
115
+ // Calculate execution rate (how many output per input)
116
+ const executionRate = amountOutFormatted / amountInFormatted;
117
+ // Determine swap direction and calculate market rate
118
+ const inputIsToken = zeroForOne ? currency0IsToken : !currency0IsToken;
119
+ // Calculate market spot rate (output per input at market prices)
120
+ const marketRate = inputIsToken
121
+ ? tokenPrice / wethPrice // Token → WETH
122
+ : wethPrice / tokenPrice; // WETH → Token
123
+ // Getting better or equal rate than market - minimal impact
124
+ if (executionRate >= marketRate) {
125
+ return 0.1;
126
+ }
127
+ // Getting worse rate than market - calculate actual slippage
128
+ const impact = (1 - executionRate / marketRate) * 100;
129
+ return impact;
130
+ }
131
+ catch (error) {
132
+ console.warn("Price impact calculation failed:", error);
133
+ return undefined;
134
+ }
135
+ };
136
+ // ============================================================================
137
+ // V4 Implementation
138
+ // ============================================================================
139
+ /**
140
+ * @description Quote a swap on Uniswap V4 by reading from the quoter contract
141
+ * @param params Quote parameters including pool key and amount
142
+ * @returns Quote result with output amount, gas estimate, price impact, and hook fees
143
+ */
144
+ export const quoteV4Read = async (params) => {
145
+ if (!params.publicClient) {
146
+ throw new Error("publicClient is required for read method");
147
+ }
148
+ const { publicClient, poolKey, zeroForOne, amountIn, hookData = "0x", pricing, currency0Decimals = 18, currency1Decimals = 18, tokenAddress, } = params;
149
+ const chainId = publicClient.chain?.id;
150
+ if (!chainId)
151
+ throw new Error("Chain ID not found on public client");
152
+ const quoterAddress = UNISWAP_V4_QUOTER(chainId);
153
+ if (!quoterAddress)
154
+ throw new Error(`V4 Quoter address not found for chain ID ${chainId}`);
155
+ // Fetch hook fees and quote in parallel
156
+ const [{ result }, hookFees] = await Promise.all([
157
+ publicClient.simulateContract({
158
+ address: quoterAddress,
159
+ abi: V4Quoter,
160
+ functionName: "quoteExactInputSingle",
161
+ args: [
162
+ {
163
+ poolKey: {
164
+ currency0: poolKey.currency0,
165
+ currency1: poolKey.currency1,
166
+ fee: poolKey.fee,
167
+ tickSpacing: poolKey.tickSpacing,
168
+ hooks: poolKey.hooks,
169
+ },
170
+ zeroForOne,
171
+ exactAmount: amountIn,
172
+ hookData,
173
+ },
174
+ ],
175
+ }),
176
+ getHookFees(publicClient, poolKey),
177
+ ]);
178
+ // The quoter returns [amountOut: bigint, gasEstimate: bigint]
179
+ const [amountOut, gasEstimate] = result;
180
+ // Calculate price impact if pricing and token address are available
181
+ const priceImpactBps = pricing && tokenAddress
182
+ ? calculatePriceImpact(amountIn, amountOut, poolKey.currency0, poolKey.currency1, currency0Decimals, currency1Decimals, tokenAddress, pricing, zeroForOne)
183
+ : undefined;
184
+ return {
185
+ amountOut,
186
+ gasEstimate,
187
+ priceImpactBps,
188
+ hookFees,
189
+ };
190
+ };
191
+ /**
192
+ * @description Get bytecode for a V4 quote that can be used in multicalls
193
+ * @param params Quote parameters including pool key and amount
194
+ * @returns Contract address, encoded call data, and ABI
195
+ */
196
+ export const quoteV4Bytecode = (params) => {
197
+ const { poolKey, zeroForOne, amountIn, hookData = "0x", publicClient } = params;
198
+ const chainId = publicClient?.chain?.id;
199
+ if (!chainId)
200
+ throw new Error("Chain ID required for bytecode generation");
201
+ const quoterAddress = UNISWAP_V4_QUOTER(chainId);
202
+ if (!quoterAddress)
203
+ throw new Error(`V4 Quoter address not found for chain ID ${chainId}`);
204
+ const data = encodeFunctionData({
205
+ abi: V4Quoter,
206
+ functionName: "quoteExactInputSingle",
207
+ args: [
208
+ {
209
+ poolKey: {
210
+ currency0: poolKey.currency0,
211
+ currency1: poolKey.currency1,
212
+ fee: poolKey.fee,
213
+ tickSpacing: poolKey.tickSpacing,
214
+ hooks: poolKey.hooks,
215
+ },
216
+ zeroForOne,
217
+ exactAmount: amountIn,
218
+ hookData,
219
+ },
220
+ ],
221
+ });
222
+ return {
223
+ address: quoterAddress,
224
+ data,
225
+ abi: V4Quoter,
226
+ };
227
+ };
228
+ //# sourceMappingURL=v4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"v4.js","sourceRoot":"","sources":["../../../src/quote/v4.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,MAAM,CAAA;AAEtF,OAAO,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAyFhD,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,gBAAgB,GAAG,KAAK,EAC5B,YAA0B,EAC1B,WAA0B,EAC1B,MAAqB,EAC2C,EAAE;IAClE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,YAAY,CAAC,SAAS,CAAC;YAC3C,SAAS,EAAE;gBACT;oBACE,OAAO,EAAE,WAAW;oBACpB,GAAG,EAAE,qBAAqB;oBAC1B,YAAY,EAAE,YAAY;oBAC1B,IAAI,EAAE,CAAC,MAAM,CAAC;iBACf;gBACD;oBACE,OAAO,EAAE,WAAW;oBACpB,GAAG,EAAE,qBAAqB;oBAC1B,YAAY,EAAE,WAAW;oBACzB,IAAI,EAAE,CAAC,MAAM,CAAC;iBACf;aACF;SACF,CAAC,CAAA;QAEF,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YACvE,OAAO;gBACL,UAAU,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;gBACrC,SAAS,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;aACrC,CAAA;QACH,CAAC;QACD,OAAO,SAAS,CAAA;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,iBAAiB,GAAG,KAAK,EAC7B,YAA0B,EAC1B,WAA0B,EAC1B,MAAqB,EACuC,EAAE;IAC9D,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,YAAY,CAAC;YAC7C,OAAO,EAAE,WAAW;YACpB,GAAG,EAAE,sBAAsB;YAC3B,YAAY,EAAE,gBAAgB;YAC9B,IAAI,EAAE,CAAC,MAAM,CAAC;SACf,CAAC,CAAA;QACF,OAAO;YACL,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC;YAC/B,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC;SAClC,CAAA;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAA;AAED;;GAEG;AACH,MAAM,WAAW,GAAG,KAAK,EACvB,YAA0B,EAC1B,OAAgB,EAC4B,EAAE;IAC9C,uEAAuE;IACvE,MAAM,MAAM,GAAG,SAAS,CACtB,mBAAmB,CACjB;QACE;YACE,IAAI,EAAE,OAAO;YACb,UAAU,EAAE;gBACV,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE;gBACtC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,SAAS,EAAE;gBACtC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE;gBAC/B,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,OAAO,EAAE;gBACtC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;aACnC;SACF;KACF,EACD,CAAC,OAAO,CAAC,CACV,CACF,CAAA;IAED,wBAAwB;IACxB,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,YAAY,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAC9E,IAAI,UAAU,EAAE,CAAC;QACf,OAAO;YACL,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE,UAAU,CAAC,UAAU;YACjC,SAAS,EAAE,UAAU,CAAC,SAAS;SAChC,CAAA;IACH,CAAC;IAED,mBAAmB;IACnB,MAAM,WAAW,GAAG,MAAM,iBAAiB,CAAC,YAAY,EAAE,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;IAChF,IAAI,WAAW,EAAE,CAAC;QAChB,OAAO;YACL,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,WAAW,CAAC,OAAO;YAC5B,QAAQ,EAAE,WAAW,CAAC,QAAQ;SAC/B,CAAA;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,oBAAoB,GAAG,CAC3B,QAAgB,EAChB,SAAiB,EACjB,SAAwB,EACxB,SAAwB,EACxB,iBAAyB,EACzB,iBAAyB,EACzB,YAA2B,EAC3B,OAAsB,EACtB,UAAmB,EACC,EAAE;IACtB,IAAI,CAAC;QACH,0DAA0D;QAC1D,MAAM,gBAAgB,GAAG,SAAS,CAAC,WAAW,EAAE,KAAK,YAAY,CAAC,WAAW,EAAE,CAAA;QAE/E,oCAAoC;QACpC,MAAM,iBAAiB,GAAG,UAAU,CAClC,WAAW,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAC1E,CAAA;QACD,MAAM,kBAAkB,GAAG,UAAU,CACnC,WAAW,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAC3E,CAAA;QAED,IAAI,iBAAiB,KAAK,CAAC,IAAI,kBAAkB,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QAEzE,yBAAyB;QACzB,MAAM,SAAS,GAAG,UAAU,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QAC7C,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAA;QAE/C,IAAI,UAAU,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC;YAAE,OAAO,SAAS,CAAA;QAEzD,uDAAuD;QACvD,MAAM,aAAa,GAAG,kBAAkB,GAAG,iBAAiB,CAAA;QAE5D,qDAAqD;QACrD,MAAM,YAAY,GAAG,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAA;QAEtE,iEAAiE;QACjE,MAAM,UAAU,GAAG,YAAY;YAC7B,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC,eAAe;YACxC,CAAC,CAAC,SAAS,GAAG,UAAU,CAAA,CAAC,eAAe;QAE1C,4DAA4D;QAC5D,IAAI,aAAa,IAAI,UAAU,EAAE,CAAC;YAChC,OAAO,GAAG,CAAA;QACZ,CAAC;QAED,6DAA6D;QAC7D,MAAM,MAAM,GAAG,CAAC,CAAC,GAAG,aAAa,GAAG,UAAU,CAAC,GAAG,GAAG,CAAA;QAErD,OAAO,MAAM,CAAA;IACf,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,kCAAkC,EAAE,KAAK,CAAC,CAAA;QACvD,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAA;AAED,+EAA+E;AAC/E,oBAAoB;AACpB,+EAA+E;AAE/E;;;;GAIG;AACH,MAAM,CAAC,MAAM,WAAW,GAAG,KAAK,EAAE,MAAqB,EAAkC,EAAE;IACzF,IAAI,CAAC,MAAM,CAAC,YAAY,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAA;IAC7D,CAAC;IAED,MAAM,EACJ,YAAY,EACZ,OAAO,EACP,UAAU,EACV,QAAQ,EACR,QAAQ,GAAG,IAAI,EACf,OAAO,EACP,iBAAiB,GAAG,EAAE,EACtB,iBAAiB,GAAG,EAAE,EACtB,YAAY,GACb,GAAG,MAAM,CAAA;IAEV,MAAM,OAAO,GAAG,YAAY,CAAC,KAAK,EAAE,EAAE,CAAA;IACtC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAA;IAEpE,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAChD,IAAI,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,OAAO,EAAE,CAAC,CAAA;IAE1F,wCAAwC;IACxC,MAAM,CAAC,EAAE,MAAM,EAAE,EAAE,QAAQ,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC/C,YAAY,CAAC,gBAAgB,CAAC;YAC5B,OAAO,EAAE,aAAa;YACtB,GAAG,EAAE,QAAQ;YACb,YAAY,EAAE,uBAAuB;YACrC,IAAI,EAAE;gBACJ;oBACE,OAAO,EAAE;wBACP,SAAS,EAAE,OAAO,CAAC,SAAS;wBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;wBAC5B,GAAG,EAAE,OAAO,CAAC,GAAG;wBAChB,WAAW,EAAE,OAAO,CAAC,WAAW;wBAChC,KAAK,EAAE,OAAO,CAAC,KAAK;qBACrB;oBACD,UAAU;oBACV,WAAW,EAAE,QAAQ;oBACrB,QAAQ;iBACT;aACF;SACF,CAAC;QACF,WAAW,CAAC,YAAY,EAAE,OAAO,CAAC;KACnC,CAAC,CAAA;IAEF,8DAA8D;IAC9D,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,MAAM,CAAA;IAEvC,oEAAoE;IACpE,MAAM,cAAc,GAClB,OAAO,IAAI,YAAY;QACrB,CAAC,CAAC,oBAAoB,CAClB,QAAQ,EACR,SAAS,EACT,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,SAAS,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,OAAO,EACP,UAAU,CACX;QACH,CAAC,CAAC,SAAS,CAAA;IAEf,OAAO;QACL,SAAS;QACT,WAAW;QACX,cAAc;QACd,QAAQ;KACT,CAAA;AACH,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAqB,EAA6B,EAAE;IAClF,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,EAAE,YAAY,EAAE,GAAG,MAAM,CAAA;IAE/E,MAAM,OAAO,GAAG,YAAY,EAAE,KAAK,EAAE,EAAE,CAAA;IACvC,IAAI,CAAC,OAAO;QAAE,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAA;IAE1E,MAAM,aAAa,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAA;IAChD,IAAI,CAAC,aAAa;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,OAAO,EAAE,CAAC,CAAA;IAE1F,MAAM,IAAI,GAAG,kBAAkB,CAAC;QAC9B,GAAG,EAAE,QAAQ;QACb,YAAY,EAAE,uBAAuB;QACrC,IAAI,EAAE;YACJ;gBACE,OAAO,EAAE;oBACP,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,SAAS,EAAE,OAAO,CAAC,SAAS;oBAC5B,GAAG,EAAE,OAAO,CAAC,GAAG;oBAChB,WAAW,EAAE,OAAO,CAAC,WAAW;oBAChC,KAAK,EAAE,OAAO,CAAC,KAAK;iBACrB;gBACD,UAAU;gBACV,WAAW,EAAE,QAAQ;gBACrB,QAAQ;aACT;SACF;KACF,CAAC,CAAA;IAEF,OAAO;QACL,OAAO,EAAE,aAAa;QACtB,IAAI;QACJ,GAAG,EAAE,QAAQ;KACd,CAAA;AACH,CAAC,CAAA"}
package/dist/esm/stake.js CHANGED
@@ -1,7 +1,6 @@
1
1
  import { encodeFunctionData, erc20Abi, formatUnits, parseUnits } from "viem";
2
2
  import { LevrForwarder_v1, LevrStaking_v1 } from "./abis/index.js";
3
3
  import { WETH } from "./constants.js";
4
- import { quoteV4 } from "./quote-v4.js";
5
4
  export class Stake {
6
5
  wallet;
7
6
  publicClient;
@@ -11,6 +10,7 @@ export class Stake {
11
10
  chainId;
12
11
  userAddress;
13
12
  trustedForwarder;
13
+ pricing;
14
14
  constructor(config) {
15
15
  if (Object.values(config).some((value) => !value))
16
16
  throw new Error("Invalid config");
@@ -22,6 +22,7 @@ export class Stake {
22
22
  this.chainId = config.publicClient.chain?.id ?? 1; // Get chainId from publicClient
23
23
  this.userAddress = config.wallet.account.address;
24
24
  this.trustedForwarder = config.trustedForwarder;
25
+ this.pricing = config.pricing;
25
26
  }
26
27
  /**
27
28
  * Approve ERC20 tokens for spending by the staking contract
@@ -159,14 +160,21 @@ export class Stake {
159
160
  ],
160
161
  });
161
162
  const [totalStaked, escrowBalance, windowSeconds, streamStart, streamEnd, rewardRate] = results.map((r) => r.result);
163
+ const totalStakedFormatted = formatUnits(totalStaked, this.tokenDecimals);
164
+ const escrowBalanceFormatted = formatUnits(escrowBalance, this.tokenDecimals);
165
+ const rewardRateFormatted = formatUnits(rewardRate, this.tokenDecimals);
166
+ // Calculate USD values if pricing is available
167
+ const tokenPrice = this.pricing ? parseFloat(this.pricing.tokenUsd) : null;
162
168
  return {
163
169
  totalStaked: {
164
170
  raw: totalStaked,
165
- formatted: formatUnits(totalStaked, this.tokenDecimals),
171
+ formatted: totalStakedFormatted,
172
+ usd: tokenPrice ? (parseFloat(totalStakedFormatted) * tokenPrice).toString() : undefined,
166
173
  },
167
174
  escrowBalance: {
168
175
  raw: escrowBalance,
169
- formatted: formatUnits(escrowBalance, this.tokenDecimals),
176
+ formatted: escrowBalanceFormatted,
177
+ usd: tokenPrice ? (parseFloat(escrowBalanceFormatted) * tokenPrice).toString() : undefined,
170
178
  },
171
179
  streamParams: {
172
180
  windowSeconds: windowSeconds,
@@ -176,7 +184,8 @@ export class Stake {
176
184
  },
177
185
  rewardRatePerSecond: {
178
186
  raw: rewardRate,
179
- formatted: formatUnits(rewardRate, this.tokenDecimals),
187
+ formatted: rewardRateFormatted,
188
+ usd: tokenPrice ? (parseFloat(rewardRateFormatted) * tokenPrice).toString() : undefined,
180
189
  },
181
190
  };
182
191
  }
@@ -202,10 +211,13 @@ export class Stake {
202
211
  });
203
212
  const stakedBalance = results[0].result;
204
213
  const aprBps = results[1].result;
214
+ const stakedBalanceFormatted = formatUnits(stakedBalance, this.tokenDecimals);
215
+ const tokenPrice = this.pricing ? parseFloat(this.pricing.tokenUsd) : null;
205
216
  return {
206
217
  stakedBalance: {
207
218
  raw: stakedBalance,
208
- formatted: formatUnits(stakedBalance, this.tokenDecimals),
219
+ formatted: stakedBalanceFormatted,
220
+ usd: tokenPrice ? (parseFloat(stakedBalanceFormatted) * tokenPrice).toString() : undefined,
209
221
  },
210
222
  aprBps: {
211
223
  raw: aprBps,
@@ -225,14 +237,36 @@ export class Stake {
225
237
  functionName: "outstandingRewards",
226
238
  args: [token],
227
239
  });
240
+ const availableFormatted = formatUnits(result[0], decimals);
241
+ const pendingFormatted = formatUnits(result[1], decimals);
242
+ // Calculate USD values if pricing is available
243
+ let availableUsd;
244
+ let pendingUsd;
245
+ if (this.pricing) {
246
+ const isTokenReward = token === this.tokenAddress;
247
+ const wethAddress = WETH(this.chainId)?.address;
248
+ const isWethReward = wethAddress && token.toLowerCase() === wethAddress.toLowerCase();
249
+ if (isTokenReward) {
250
+ const price = parseFloat(this.pricing.tokenUsd);
251
+ availableUsd = (parseFloat(availableFormatted) * price).toString();
252
+ pendingUsd = (parseFloat(pendingFormatted) * price).toString();
253
+ }
254
+ else if (isWethReward) {
255
+ const price = parseFloat(this.pricing.wethUsd);
256
+ availableUsd = (parseFloat(availableFormatted) * price).toString();
257
+ pendingUsd = (parseFloat(pendingFormatted) * price).toString();
258
+ }
259
+ }
228
260
  return {
229
261
  available: {
230
262
  raw: result[0],
231
- formatted: formatUnits(result[0], decimals),
263
+ formatted: availableFormatted,
264
+ usd: availableUsd,
232
265
  },
233
266
  pending: {
234
267
  raw: result[1],
235
- formatted: formatUnits(result[1], decimals),
268
+ formatted: pendingFormatted,
269
+ usd: pendingUsd,
236
270
  },
237
271
  };
238
272
  }
@@ -248,10 +282,27 @@ export class Stake {
248
282
  functionName: "claimableRewards",
249
283
  args: [this.userAddress, token],
250
284
  });
285
+ const formatted = formatUnits(result, decimals);
286
+ // Calculate USD value if pricing is available
287
+ let usd;
288
+ if (this.pricing) {
289
+ const isTokenReward = token === this.tokenAddress;
290
+ const wethAddress = WETH(this.chainId)?.address;
291
+ const isWethReward = wethAddress && token.toLowerCase() === wethAddress.toLowerCase();
292
+ if (isTokenReward) {
293
+ const price = parseFloat(this.pricing.tokenUsd);
294
+ usd = (parseFloat(formatted) * price).toString();
295
+ }
296
+ else if (isWethReward) {
297
+ const price = parseFloat(this.pricing.wethUsd);
298
+ usd = (parseFloat(formatted) * price).toString();
299
+ }
300
+ }
251
301
  return {
252
302
  claimable: {
253
303
  raw: result,
254
- formatted: formatUnits(result, decimals),
304
+ formatted,
305
+ usd,
255
306
  },
256
307
  };
257
308
  }
@@ -267,9 +318,25 @@ export class Stake {
267
318
  functionName: "rewardRatePerSecond",
268
319
  args: [token],
269
320
  });
321
+ const formatted = formatUnits(result, decimals);
322
+ // Calculate USD value if pricing is available
323
+ let usd;
324
+ if (this.pricing) {
325
+ const isTokenReward = token.toLowerCase() === this.tokenAddress.toLowerCase();
326
+ const isWethReward = !isTokenReward;
327
+ if (isTokenReward) {
328
+ const price = parseFloat(this.pricing.tokenUsd);
329
+ usd = (parseFloat(formatted) * price).toString();
330
+ }
331
+ else if (isWethReward) {
332
+ const price = parseFloat(this.pricing.wethUsd);
333
+ usd = (parseFloat(formatted) * price).toString();
334
+ }
335
+ }
270
336
  return {
271
337
  raw: result,
272
- formatted: formatUnits(result, decimals),
338
+ formatted,
339
+ usd,
273
340
  };
274
341
  }
275
342
  /**
@@ -321,17 +388,21 @@ export class Stake {
321
388
  return receipt;
322
389
  }
323
390
  /**
324
- * Calculate WETH APR using pool price and reward rates
325
- * Formula: (wethRewardRatePerSecond * secondsPerYear * wethPriceInUnderlying / totalStaked) * 10000
391
+ * Calculate WETH APR using USD pricing data
392
+ * No on-chain quotes needed - uses existing pricing data
393
+ * Formula: (wethRewardRatePerSecond * secondsPerYear * wethPriceInTokens / totalStaked) * 10000
326
394
  *
327
- * @param poolKey - The Uniswap V4 pool key for price discovery
328
395
  * @returns WETH APR in basis points and percentage
329
396
  */
330
- async calculateWethApr(poolKey) {
397
+ async calculateWethApr() {
331
398
  const wethAddress = WETH(this.chainId)?.address;
332
399
  if (!wethAddress) {
333
400
  throw new Error("WETH address not found for this chain");
334
401
  }
402
+ // Require pricing data to calculate APR
403
+ if (!this.pricing) {
404
+ return { raw: 0n, percentage: 0 };
405
+ }
335
406
  // Get pool data and WETH reward rate
336
407
  const [poolData, wethRewardRate] = await Promise.all([
337
408
  this.getPoolData(),
@@ -342,39 +413,30 @@ export class Stake {
342
413
  if (totalStaked === 0n || wethRewardRate.raw === 0n) {
343
414
  return { raw: 0n, percentage: 0 };
344
415
  }
345
- // Quote 1 WETH to determine price in underlying tokens
346
- // Determine swap direction: WETH -> underlying
347
- const wethIsCurrency0 = wethAddress.toLowerCase() < this.tokenAddress.toLowerCase();
348
- const zeroForOne = wethIsCurrency0; // Swap WETH for underlying
349
- // Quote 1 WETH (18 decimals)
350
- const oneWeth = parseUnits("1", 18);
351
- let wethPriceInUnderlying;
352
- try {
353
- const quote = await quoteV4({
354
- publicClient: this.publicClient,
355
- chainId: this.chainId,
356
- poolKey,
357
- zeroForOne,
358
- amountIn: oneWeth,
359
- });
360
- // amountOut is how many underlying tokens we get for 1 WETH
361
- wethPriceInUnderlying = quote.amountOut;
362
- }
363
- catch (error) {
364
- // If quote fails, return 0 APR
365
- console.error("Failed to quote WETH price:", error);
416
+ // Calculate WETH price in underlying tokens using USD pricing
417
+ // wethPriceInTokens = wethUsd / tokenUsd
418
+ const wethUsd = parseFloat(this.pricing.wethUsd);
419
+ const tokenUsd = parseFloat(this.pricing.tokenUsd);
420
+ if (tokenUsd === 0) {
366
421
  return { raw: 0n, percentage: 0 };
367
422
  }
423
+ const wethPriceInTokens = wethUsd / tokenUsd;
368
424
  // Calculate annual WETH rewards (rewards per second * seconds per year)
369
425
  const secondsPerYear = BigInt(365 * 24 * 60 * 60);
370
426
  const annualWethRewards = wethRewardRate.raw * secondsPerYear;
371
- // Convert WETH rewards to underlying token equivalent
372
- // wethPriceInUnderlying is already in underlying token decimals from the quote
373
- // annualWethRewards is in WETH (18 decimals)
374
- // Both need to be normalized to underlying token decimals
375
- const annualRewardsInUnderlying = (annualWethRewards * wethPriceInUnderlying) / BigInt(1000000000000000000);
427
+ // Convert WETH rewards to underlying token equivalent using bigint math
428
+ // annualWethRewards is in WETH wei (18 decimals)
429
+ // wethPriceInTokens is tokens per WETH (e.g., 3.75 billion for $3751 WETH / $0.000001 token)
430
+ // Scale the price ratio to bigint with high precision (use 1e18 for precision)
431
+ const priceScaleFactor = BigInt(1000000000000000000);
432
+ const wethPriceScaled = BigInt(Math.floor(wethPriceInTokens * Number(priceScaleFactor)));
433
+ // Formula: (annualWethRewards_wei * wethPrice_tokens/WETH) / 1e18
434
+ // Since both are already in wei and we have token decimals:
435
+ // Result = (annualWethRewards * wethPriceScaled) / priceScaleFactor
436
+ // This gives us token wei (same decimals as token)
437
+ const annualRewardsInUnderlying = (annualWethRewards * wethPriceScaled) / priceScaleFactor;
376
438
  // Calculate APR: (annualRewardsInUnderlying / totalStaked) * 10000
377
- const aprBps = (annualRewardsInUnderlying * 10000n) / totalStaked;
439
+ const aprBps = totalStaked > 0n ? (annualRewardsInUnderlying * 10000n) / totalStaked : 0n;
378
440
  return {
379
441
  raw: aprBps,
380
442
  percentage: Number(aprBps) / 100, // Convert bps to percentage