Skip to content

Commit df36f55

Browse files
authored
Merge pull request #6 from BootNodeDev/feat/getPoolKeyFromPoolId
feat: getPoolKeyFromPoolId
2 parents 9fad3c8 + 034d8d1 commit df36f55

File tree

13 files changed

+386
-21
lines changed

13 files changed

+386
-21
lines changed

src/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
export { useGetPool } from "@/hooks/useGetPool";
2+
export { useGetPoolKeyFromPoolId } from "@/hooks/useGetPoolKeyFromPoolId";
23
export { useGetQuote } from "@/hooks/useGetQuote";

src/hooks/useGetPool.ts

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type { UseGetPoolOptions } from "@/types/hooks/useGetPool";
2-
import type { PoolParams } from "@/types/utils/getPool";
32
import { getPool } from "@/utils/getPool";
43
import { useQuery } from "@tanstack/react-query";
54
import type { Pool } from "@uniswap/v4-sdk";
@@ -30,22 +29,21 @@ import type { Pool } from "@uniswap/v4-sdk";
3029
* });
3130
* ```
3231
*/
33-
function serializeParams(params?: PoolParams) {
34-
if (!params) return undefined;
35-
return {
36-
...params,
37-
tokens: params.tokens.map((t) => t.toLowerCase()),
38-
};
39-
}
4032

4133
export function useGetPool({
4234
params,
4335
chainId,
4436
queryOptions = {},
45-
}: UseGetPoolOptions = {}) {
46-
if (!params) throw new Error("No params provided");
37+
}: UseGetPoolOptions) {
4738
return useQuery<Pool | undefined, Error, Pool | undefined, unknown[]>({
48-
queryKey: ["pool", serializeParams(params), chainId],
39+
queryKey: [
40+
"pool",
41+
params.fee,
42+
params.tokens,
43+
params.hooks,
44+
params.tickSpacing,
45+
chainId,
46+
],
4947
queryFn: () => getPool(params, chainId),
5048
...queryOptions,
5149
});
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import type { UseGetPoolKeyFromPoolIdOptions } from "@/types/hooks/useGetPoolKeyFromPoolId";
2+
import { getPoolKeyFromPoolId } from "@/utils/getPoolKeyFromPoolId";
3+
import { useQuery } from "@tanstack/react-query";
4+
5+
/**
6+
* React hook for fetching Uniswap V4 pool key information using React Query.
7+
* Handles caching, loading states, and error handling automatically.
8+
*
9+
* @param options - Configuration options for the hook
10+
* @returns Query result containing pool key data, loading state, error state, and refetch function
11+
*
12+
* @example
13+
* ```tsx
14+
* const { data, isLoading, error, refetch } = useGetPoolKeyFromPoolId({
15+
* poolId: "0x1234...",
16+
* chainId: 1,
17+
* queryOptions: {
18+
* enabled: true,
19+
* staleTime: 30000,
20+
* gcTime: 300000,
21+
* retry: 3,
22+
* onSuccess: (data) => console.log('Pool key data received:', data)
23+
* }
24+
* });
25+
* ```
26+
*/
27+
export function useGetPoolKeyFromPoolId({
28+
poolId,
29+
chainId,
30+
queryOptions = {},
31+
}: UseGetPoolKeyFromPoolIdOptions) {
32+
return useQuery({
33+
queryKey: ["poolKey", poolId, chainId],
34+
queryFn: () => getPoolKeyFromPoolId({ poolId, chainId }),
35+
...queryOptions,
36+
});
37+
}

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ export * from "@/core/uniDevKitV4Factory";
44

55
// Hooks
66
export * from "@/hooks/useGetPool";
7+
export * from "@/hooks/useGetPoolKeyFromPoolId";
78
export * from "@/hooks/useGetPosition";
89
export * from "@/hooks/useGetQuote";
910

1011
// Utils
12+
export * from "@/utils/getPoolKeyFromPoolId";
1113
export * from "@/utils/getQuote";
1214
export * from "@/utils/getTokens";
1315

src/test/hooks/useGetPool.test.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { useGetPool } from "@/hooks/useGetPool";
2-
import type { UseGetPoolOptions } from "@/types/hooks/useGetPool";
32
import { getPool } from "@/utils/getPool";
43
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
54
import { renderHook, waitFor } from "@testing-library/react";
@@ -101,14 +100,6 @@ describe("useGetPool", () => {
101100
expect(result.current.status).toBe("error");
102101
});
103102

104-
it("should throw error if no params provided", () => {
105-
expect(() => {
106-
renderHook(() => useGetPool(undefined as unknown as UseGetPoolOptions), {
107-
wrapper,
108-
});
109-
}).toThrow("No params provided");
110-
});
111-
112103
it("should handle custom query options", async () => {
113104
const mockPool = {
114105
token0: new Token(1, USDC, 6, "USDC", "USD Coin"),
Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
import { useGetPoolKeyFromPoolId } from "@/hooks/useGetPoolKeyFromPoolId";
2+
import { getPoolKeyFromPoolId } from "@/utils/getPoolKeyFromPoolId";
3+
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
4+
import { renderHook, waitFor } from "@testing-library/react";
5+
import { jsx as _jsx } from "react/jsx-runtime";
6+
import type { Mock } from "vitest";
7+
import { beforeEach, describe, expect, it, vi } from "vitest";
8+
9+
// Mock the getPoolKeyFromPoolId function
10+
vi.mock("@/utils/getPoolKeyFromPoolId", () => ({
11+
getPoolKeyFromPoolId: vi.fn(),
12+
}));
13+
14+
describe("useGetPoolKeyFromPoolId", () => {
15+
let queryClient: QueryClient;
16+
17+
beforeEach(() => {
18+
queryClient = new QueryClient({
19+
defaultOptions: {
20+
queries: {
21+
retry: false,
22+
},
23+
},
24+
});
25+
vi.clearAllMocks();
26+
});
27+
28+
const wrapper = ({ children }: { children: React.ReactNode }) =>
29+
_jsx(QueryClientProvider, { client: queryClient, children });
30+
31+
it("should fetch pool key data successfully", async () => {
32+
const mockPoolKey = {
33+
currency0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
34+
currency1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
35+
fee: 3000,
36+
tickSpacing: 60,
37+
hooks: "0x0000000000000000000000000000000000000000",
38+
};
39+
40+
(getPoolKeyFromPoolId as Mock).mockResolvedValue(mockPoolKey);
41+
42+
const { result } = renderHook(
43+
() =>
44+
useGetPoolKeyFromPoolId({
45+
poolId: "0x1234",
46+
chainId: 1,
47+
}),
48+
{ wrapper },
49+
);
50+
51+
await waitFor(() => {
52+
expect(result.current.isLoading).toBe(false);
53+
});
54+
55+
expect(result.current.data).toEqual(mockPoolKey);
56+
expect(result.current.error).toBeNull();
57+
expect(result.current.isLoading).toBe(false);
58+
expect(result.current.status).toBe("success");
59+
expect(getPoolKeyFromPoolId).toHaveBeenCalledWith({
60+
poolId: "0x1234",
61+
chainId: 1,
62+
});
63+
});
64+
65+
it("should handle errors", async () => {
66+
const error = new Error("Failed to fetch pool key");
67+
(getPoolKeyFromPoolId as Mock).mockRejectedValue(error);
68+
69+
const { result } = renderHook(
70+
() =>
71+
useGetPoolKeyFromPoolId({
72+
poolId: "0x1234",
73+
chainId: 1,
74+
}),
75+
{ wrapper },
76+
);
77+
78+
await waitFor(() => {
79+
expect(result.current.isLoading).toBe(false);
80+
});
81+
82+
expect(result.current.data).toBeUndefined();
83+
expect(result.current.error).toBe(error);
84+
expect(result.current.isLoading).toBe(false);
85+
expect(result.current.status).toBe("error");
86+
});
87+
88+
it("should handle custom query options", async () => {
89+
const mockPoolKey = {
90+
currency0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
91+
currency1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
92+
fee: 3000,
93+
tickSpacing: 60,
94+
hooks: "0x0000000000000000000000000000000000000000",
95+
};
96+
97+
(getPoolKeyFromPoolId as Mock).mockResolvedValue(mockPoolKey);
98+
99+
const { result } = renderHook(
100+
() =>
101+
useGetPoolKeyFromPoolId({
102+
poolId: "0x1234",
103+
chainId: 1,
104+
queryOptions: {
105+
enabled: true,
106+
staleTime: 5000,
107+
},
108+
}),
109+
{ wrapper },
110+
);
111+
112+
await waitFor(() => {
113+
expect(result.current.isLoading).toBe(false);
114+
});
115+
116+
expect(result.current.data).toEqual(mockPoolKey);
117+
expect(result.current.error).toBeNull();
118+
expect(result.current.isLoading).toBe(false);
119+
expect(result.current.status).toBe("success");
120+
});
121+
});
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import type { UniDevKitV4 } from "@/core/uniDevKitV4";
2+
import { getInstance } from "@/core/uniDevKitV4Factory";
3+
import { getPoolKeyFromPoolId } from "@/utils/getPoolKeyFromPoolId";
4+
import { describe, expect, it, vi } from "vitest";
5+
6+
// Mock the SDK instance
7+
vi.mock("@/core/uniDevKitV4Factory", () => ({
8+
getInstance: vi.fn(),
9+
}));
10+
11+
describe("getPoolKeyFromPoolId", () => {
12+
const mockPoolId =
13+
"0x1234567890123456789012345678901234567890123456789012345678901234";
14+
const mockChainId = 1;
15+
const expectedPoolId25Bytes =
16+
"0x12345678901234567890123456789012345678901234567890";
17+
18+
it("should throw error if SDK instance not found", async () => {
19+
vi.mocked(getInstance).mockReturnValue(undefined as unknown as UniDevKitV4);
20+
21+
await expect(
22+
getPoolKeyFromPoolId({ poolId: mockPoolId, chainId: mockChainId }),
23+
).rejects.toThrow("SDK not initialized");
24+
});
25+
26+
it("should return pool key when SDK instance exists", async () => {
27+
const mockPoolKey = [
28+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
29+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
30+
3000,
31+
60,
32+
"0x0000000000000000000000000000000000000000",
33+
];
34+
35+
const mockClient = {
36+
readContract: vi.fn().mockResolvedValue(mockPoolKey),
37+
};
38+
39+
const mockSdk = {
40+
getClient: vi.fn().mockReturnValue(mockClient),
41+
getContractAddress: vi.fn().mockReturnValue("0xPositionManager"),
42+
instance: {},
43+
createInstance: vi.fn(),
44+
createClient: vi.fn(),
45+
getChainId: vi.fn(),
46+
getContract: vi.fn(),
47+
} as unknown as UniDevKitV4;
48+
49+
vi.mocked(getInstance).mockReturnValue(mockSdk);
50+
51+
const result = await getPoolKeyFromPoolId({
52+
poolId: mockPoolId,
53+
chainId: mockChainId,
54+
});
55+
56+
expect(result).toEqual({
57+
currency0: mockPoolKey[0],
58+
currency1: mockPoolKey[1],
59+
fee: mockPoolKey[2],
60+
tickSpacing: mockPoolKey[3],
61+
hooks: mockPoolKey[4],
62+
});
63+
expect(mockClient.readContract).toHaveBeenCalledWith({
64+
address: "0xPositionManager",
65+
abi: expect.any(Object),
66+
functionName: "poolKeys",
67+
args: [expectedPoolId25Bytes],
68+
});
69+
});
70+
71+
it("should handle contract read errors", async () => {
72+
const mockClient = {
73+
readContract: vi
74+
.fn()
75+
.mockRejectedValue(new Error("Contract read failed")),
76+
};
77+
78+
const mockSdk = {
79+
getClient: vi.fn().mockReturnValue(mockClient),
80+
getContractAddress: vi.fn().mockReturnValue("0xPositionManager"),
81+
instance: {},
82+
createInstance: vi.fn(),
83+
createClient: vi.fn(),
84+
getChainId: vi.fn(),
85+
getContract: vi.fn(),
86+
} as unknown as UniDevKitV4;
87+
88+
vi.mocked(getInstance).mockReturnValue(mockSdk);
89+
90+
await expect(
91+
getPoolKeyFromPoolId({ poolId: mockPoolId, chainId: mockChainId }),
92+
).rejects.toThrow("Contract read failed");
93+
});
94+
95+
it("should correctly convert poolId from 32 bytes to 25 bytes", async () => {
96+
const mockPoolKey = [
97+
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
98+
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
99+
3000,
100+
60,
101+
"0x0000000000000000000000000000000000000000",
102+
];
103+
104+
const mockClient = {
105+
readContract: vi.fn().mockResolvedValue(mockPoolKey),
106+
};
107+
108+
const mockSdk = {
109+
getClient: vi.fn().mockReturnValue(mockClient),
110+
getContractAddress: vi.fn().mockReturnValue("0xPositionManager"),
111+
instance: {},
112+
createInstance: vi.fn(),
113+
createClient: vi.fn(),
114+
getChainId: vi.fn(),
115+
getContract: vi.fn(),
116+
} as unknown as UniDevKitV4;
117+
118+
vi.mocked(getInstance).mockReturnValue(mockSdk);
119+
120+
await getPoolKeyFromPoolId({ poolId: mockPoolId, chainId: mockChainId });
121+
122+
// Verify that the poolId was correctly converted
123+
expect(mockClient.readContract).toHaveBeenCalledWith(
124+
expect.objectContaining({
125+
args: [expectedPoolId25Bytes],
126+
}),
127+
);
128+
});
129+
});

src/types/hooks/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export * from "@/types/hooks/useGetPool";
2+
export * from "@/types/hooks/useGetPoolKeyFromPoolId";
23
export * from "@/types/hooks/useGetPosition";
34
export * from "@/types/hooks/useGetQuote";

src/types/hooks/useGetPool.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import type { Pool } from "@uniswap/v4-sdk";
77
*/
88
export type UseGetPoolOptions = {
99
/** Initial pool parameters */
10-
params?: PoolParams;
10+
params: PoolParams;
1111
/** Chain ID */
1212
chainId?: number;
1313
/** React Query options */
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { PoolKey } from "@/types/utils/getPoolKeyFromPoolId";
2+
import type { UseQueryOptions } from "@tanstack/react-query";
3+
4+
/**
5+
* Configuration options for the useGetPoolKeyFromPoolId hook.
6+
*/
7+
export type UseGetPoolKeyFromPoolIdOptions = {
8+
/** The 32-byte pool ID in hex format (0x...) */
9+
poolId: `0x${string}`;
10+
/** Chain ID */
11+
chainId?: number;
12+
/** React Query options */
13+
queryOptions?: Omit<
14+
UseQueryOptions<PoolKey | undefined, Error, PoolKey | undefined, unknown[]>,
15+
"queryKey"
16+
>;
17+
};

0 commit comments

Comments
 (0)