Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { useGetPool } from "@/hooks/useGetPool";
export { useGetPoolKeyFromPoolId } from "@/hooks/useGetPoolKeyFromPoolId";
export { useGetQuote } from "@/hooks/useGetQuote";
20 changes: 9 additions & 11 deletions src/hooks/useGetPool.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import type { UseGetPoolOptions } from "@/types/hooks/useGetPool";
import type { PoolParams } from "@/types/utils/getPool";
import { getPool } from "@/utils/getPool";
import { useQuery } from "@tanstack/react-query";
import type { Pool } from "@uniswap/v4-sdk";
Expand Down Expand Up @@ -30,22 +29,21 @@ import type { Pool } from "@uniswap/v4-sdk";
* });
* ```
*/
function serializeParams(params?: PoolParams) {
if (!params) return undefined;
return {
...params,
tokens: params.tokens.map((t) => t.toLowerCase()),
};
}

export function useGetPool({
params,
chainId,
queryOptions = {},
}: UseGetPoolOptions = {}) {
if (!params) throw new Error("No params provided");
}: UseGetPoolOptions) {
return useQuery<Pool | undefined, Error, Pool | undefined, unknown[]>({
queryKey: ["pool", serializeParams(params), chainId],
queryKey: [
"pool",
params.fee,
params.tokens,
params.hooks,
params.tickSpacing,
chainId,
],
queryFn: () => getPool(params, chainId),
...queryOptions,
});
Expand Down
37 changes: 37 additions & 0 deletions src/hooks/useGetPoolKeyFromPoolId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import type { UseGetPoolKeyFromPoolIdOptions } from "@/types/hooks/useGetPoolKeyFromPoolId";
import { getPoolKeyFromPoolId } from "@/utils/getPoolKeyFromPoolId";
import { useQuery } from "@tanstack/react-query";

/**
* React hook for fetching Uniswap V4 pool key information using React Query.
* Handles caching, loading states, and error handling automatically.
*
* @param options - Configuration options for the hook
* @returns Query result containing pool key data, loading state, error state, and refetch function
*
* @example
* ```tsx
* const { data, isLoading, error, refetch } = useGetPoolKeyFromPoolId({
* poolId: "0x1234...",
* chainId: 1,
* queryOptions: {
* enabled: true,
* staleTime: 30000,
* gcTime: 300000,
* retry: 3,
* onSuccess: (data) => console.log('Pool key data received:', data)
* }
* });
* ```
*/
export function useGetPoolKeyFromPoolId({
poolId,
chainId,
queryOptions = {},
}: UseGetPoolKeyFromPoolIdOptions) {
return useQuery({
queryKey: ["poolKey", poolId, chainId],
queryFn: () => getPoolKeyFromPoolId({ poolId, chainId }),
...queryOptions,
});
}
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ export * from "@/core/uniDevKitV4Factory";

// Hooks
export * from "@/hooks/useGetPool";
export * from "@/hooks/useGetPoolKeyFromPoolId";
export * from "@/hooks/useGetPosition";
export * from "@/hooks/useGetQuote";

// Utils
export * from "@/utils/getPoolKeyFromPoolId";
export * from "@/utils/getQuote";
export * from "@/utils/getTokens";

Expand Down
9 changes: 0 additions & 9 deletions src/test/hooks/useGetPool.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useGetPool } from "@/hooks/useGetPool";
import type { UseGetPoolOptions } from "@/types/hooks/useGetPool";
import { getPool } from "@/utils/getPool";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { renderHook, waitFor } from "@testing-library/react";
Expand Down Expand Up @@ -101,14 +100,6 @@ describe("useGetPool", () => {
expect(result.current.status).toBe("error");
});

it("should throw error if no params provided", () => {
expect(() => {
renderHook(() => useGetPool(undefined as unknown as UseGetPoolOptions), {
wrapper,
});
}).toThrow("No params provided");
});

it("should handle custom query options", async () => {
const mockPool = {
token0: new Token(1, USDC, 6, "USDC", "USD Coin"),
Expand Down
121 changes: 121 additions & 0 deletions src/test/hooks/useGetPoolKeyFromPoolId.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
import { useGetPoolKeyFromPoolId } from "@/hooks/useGetPoolKeyFromPoolId";
import { getPoolKeyFromPoolId } from "@/utils/getPoolKeyFromPoolId";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { renderHook, waitFor } from "@testing-library/react";
import { jsx as _jsx } from "react/jsx-runtime";
import type { Mock } from "vitest";
import { beforeEach, describe, expect, it, vi } from "vitest";

// Mock the getPoolKeyFromPoolId function
vi.mock("@/utils/getPoolKeyFromPoolId", () => ({
getPoolKeyFromPoolId: vi.fn(),
}));

describe("useGetPoolKeyFromPoolId", () => {
let queryClient: QueryClient;

beforeEach(() => {
queryClient = new QueryClient({
defaultOptions: {
queries: {
retry: false,
},
},
});
vi.clearAllMocks();
});

const wrapper = ({ children }: { children: React.ReactNode }) =>
_jsx(QueryClientProvider, { client: queryClient, children });

it("should fetch pool key data successfully", async () => {
const mockPoolKey = {
currency0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
currency1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
fee: 3000,
tickSpacing: 60,
hooks: "0x0000000000000000000000000000000000000000",
};

(getPoolKeyFromPoolId as Mock).mockResolvedValue(mockPoolKey);

const { result } = renderHook(
() =>
useGetPoolKeyFromPoolId({
poolId: "0x1234",
chainId: 1,
}),
{ wrapper },
);

await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});

expect(result.current.data).toEqual(mockPoolKey);
expect(result.current.error).toBeNull();
expect(result.current.isLoading).toBe(false);
expect(result.current.status).toBe("success");
expect(getPoolKeyFromPoolId).toHaveBeenCalledWith({
poolId: "0x1234",
chainId: 1,
});
});

it("should handle errors", async () => {
const error = new Error("Failed to fetch pool key");
(getPoolKeyFromPoolId as Mock).mockRejectedValue(error);

const { result } = renderHook(
() =>
useGetPoolKeyFromPoolId({
poolId: "0x1234",
chainId: 1,
}),
{ wrapper },
);

await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});

expect(result.current.data).toBeUndefined();
expect(result.current.error).toBe(error);
expect(result.current.isLoading).toBe(false);
expect(result.current.status).toBe("error");
});

it("should handle custom query options", async () => {
const mockPoolKey = {
currency0: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
currency1: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
fee: 3000,
tickSpacing: 60,
hooks: "0x0000000000000000000000000000000000000000",
};

(getPoolKeyFromPoolId as Mock).mockResolvedValue(mockPoolKey);

const { result } = renderHook(
() =>
useGetPoolKeyFromPoolId({
poolId: "0x1234",
chainId: 1,
queryOptions: {
enabled: true,
staleTime: 5000,
},
}),
{ wrapper },
);

await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});

expect(result.current.data).toEqual(mockPoolKey);
expect(result.current.error).toBeNull();
expect(result.current.isLoading).toBe(false);
expect(result.current.status).toBe("success");
});
});
129 changes: 129 additions & 0 deletions src/test/utils/getPoolKeyFromPoolId.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import type { UniDevKitV4 } from "@/core/uniDevKitV4";
import { getInstance } from "@/core/uniDevKitV4Factory";
import { getPoolKeyFromPoolId } from "@/utils/getPoolKeyFromPoolId";
import { describe, expect, it, vi } from "vitest";

// Mock the SDK instance
vi.mock("@/core/uniDevKitV4Factory", () => ({
getInstance: vi.fn(),
}));

describe("getPoolKeyFromPoolId", () => {
const mockPoolId =
"0x1234567890123456789012345678901234567890123456789012345678901234";
const mockChainId = 1;
const expectedPoolId25Bytes =
"0x12345678901234567890123456789012345678901234567890";

it("should throw error if SDK instance not found", async () => {
vi.mocked(getInstance).mockReturnValue(undefined as unknown as UniDevKitV4);

await expect(
getPoolKeyFromPoolId({ poolId: mockPoolId, chainId: mockChainId }),
).rejects.toThrow("SDK not initialized");
});

it("should return pool key when SDK instance exists", async () => {
const mockPoolKey = [
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
3000,
60,
"0x0000000000000000000000000000000000000000",
];

const mockClient = {
readContract: vi.fn().mockResolvedValue(mockPoolKey),
};

const mockSdk = {
getClient: vi.fn().mockReturnValue(mockClient),
getContractAddress: vi.fn().mockReturnValue("0xPositionManager"),
instance: {},
createInstance: vi.fn(),
createClient: vi.fn(),
getChainId: vi.fn(),
getContract: vi.fn(),
} as unknown as UniDevKitV4;

vi.mocked(getInstance).mockReturnValue(mockSdk);

const result = await getPoolKeyFromPoolId({
poolId: mockPoolId,
chainId: mockChainId,
});

expect(result).toEqual({
currency0: mockPoolKey[0],
currency1: mockPoolKey[1],
fee: mockPoolKey[2],
tickSpacing: mockPoolKey[3],
hooks: mockPoolKey[4],
});
expect(mockClient.readContract).toHaveBeenCalledWith({
address: "0xPositionManager",
abi: expect.any(Object),
functionName: "poolKeys",
args: [expectedPoolId25Bytes],
});
});

it("should handle contract read errors", async () => {
const mockClient = {
readContract: vi
.fn()
.mockRejectedValue(new Error("Contract read failed")),
};

const mockSdk = {
getClient: vi.fn().mockReturnValue(mockClient),
getContractAddress: vi.fn().mockReturnValue("0xPositionManager"),
instance: {},
createInstance: vi.fn(),
createClient: vi.fn(),
getChainId: vi.fn(),
getContract: vi.fn(),
} as unknown as UniDevKitV4;

vi.mocked(getInstance).mockReturnValue(mockSdk);

await expect(
getPoolKeyFromPoolId({ poolId: mockPoolId, chainId: mockChainId }),
).rejects.toThrow("Contract read failed");
});

it("should correctly convert poolId from 32 bytes to 25 bytes", async () => {
const mockPoolKey = [
"0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",
3000,
60,
"0x0000000000000000000000000000000000000000",
];

const mockClient = {
readContract: vi.fn().mockResolvedValue(mockPoolKey),
};

const mockSdk = {
getClient: vi.fn().mockReturnValue(mockClient),
getContractAddress: vi.fn().mockReturnValue("0xPositionManager"),
instance: {},
createInstance: vi.fn(),
createClient: vi.fn(),
getChainId: vi.fn(),
getContract: vi.fn(),
} as unknown as UniDevKitV4;

vi.mocked(getInstance).mockReturnValue(mockSdk);

await getPoolKeyFromPoolId({ poolId: mockPoolId, chainId: mockChainId });

// Verify that the poolId was correctly converted
expect(mockClient.readContract).toHaveBeenCalledWith(
expect.objectContaining({
args: [expectedPoolId25Bytes],
}),
);
});
});
1 change: 1 addition & 0 deletions src/types/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "@/types/hooks/useGetPool";
export * from "@/types/hooks/useGetPoolKeyFromPoolId";
export * from "@/types/hooks/useGetPosition";
export * from "@/types/hooks/useGetQuote";
2 changes: 1 addition & 1 deletion src/types/hooks/useGetPool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Pool } from "@uniswap/v4-sdk";
*/
export type UseGetPoolOptions = {
/** Initial pool parameters */
params?: PoolParams;
params: PoolParams;
/** Chain ID */
chainId?: number;
/** React Query options */
Expand Down
17 changes: 17 additions & 0 deletions src/types/hooks/useGetPoolKeyFromPoolId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { PoolKey } from "@/types/utils/getPoolKeyFromPoolId";
import type { UseQueryOptions } from "@tanstack/react-query";

/**
* Configuration options for the useGetPoolKeyFromPoolId hook.
*/
export type UseGetPoolKeyFromPoolIdOptions = {
/** The 32-byte pool ID in hex format (0x...) */
poolId: `0x${string}`;
/** Chain ID */
chainId?: number;
/** React Query options */
queryOptions?: Omit<
UseQueryOptions<PoolKey | undefined, Error, PoolKey | undefined, unknown[]>,
"queryKey"
>;
};
Loading