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
2 changes: 1 addition & 1 deletion src/hooks/useGetPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function useGetPosition({
unknown[]
>({
queryKey: ["position", tokenId, chainId],
queryFn: () => getPosition(tokenId, chainId),
queryFn: () => getPosition({ tokenId }, chainId),
...queryOptions,
});
}
34 changes: 21 additions & 13 deletions src/test/hooks/useGetPosition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,18 @@ describe("useGetPosition", () => {
"WETH",
"Wrapped Ether",
),
amounts: {
amount0: "1000000",
amount1: "1000000000000000000",
position: {
amounts: {
amount0: "1000000",
amount1: "1000000000000000000",
},
tickLower: -100,
tickUpper: 100,
liquidity: BigInt("1000000000000000000"),
},
tickLower: -100,
tickUpper: 100,
liquidity: BigInt("1000000000000000000"),
pool: {},
poolId: "0x1234567890123456789012345678901234567890",
tokenId: "123",
};

(getPosition as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(
Expand All @@ -75,7 +79,7 @@ describe("useGetPosition", () => {
});

expect(result.current.data).toEqual(mockPosition);
expect(getPosition).toHaveBeenCalledWith("123", 1);
expect(getPosition).toHaveBeenCalledWith({ tokenId: "123" }, 1);
});

it("should handle errors", async () => {
Expand Down Expand Up @@ -122,14 +126,18 @@ describe("useGetPosition", () => {
"WETH",
"Wrapped Ether",
),
amounts: {
amount0: "1000000",
amount1: "1000000000000000000",
position: {
amounts: {
amount0: "1000000",
amount1: "1000000000000000000",
},
tickLower: -100,
tickUpper: 100,
liquidity: BigInt("1000000000000000000"),
},
tickLower: -100,
tickUpper: 100,
liquidity: BigInt("1000000000000000000"),
pool: {},
poolId: "0x1234567890123456789012345678901234567890",
tokenId: "123",
};

(getPosition as unknown as ReturnType<typeof vi.fn>).mockResolvedValueOnce(
Expand Down
27 changes: 13 additions & 14 deletions src/test/utils/getPosition.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ describe("getPosition", () => {

it("should throw error if SDK instance not found", async () => {
mockGetInstance.mockReturnValueOnce(undefined);
await expect(getPosition(mockTokenId, mockChainId)).rejects.toThrow(
"SDK not initialized",
);
await expect(
getPosition({ tokenId: mockTokenId }, mockChainId),
).rejects.toThrow("SDK not found. Please create an instance first.");
});

it("should throw error if tokens not found", async () => {
Expand All @@ -59,9 +59,9 @@ describe("getPosition", () => {
});
mockGetTokens.mockResolvedValueOnce(null);

await expect(getPosition(mockTokenId, mockChainId)).rejects.toThrow(
"Tokens not found",
);
await expect(
getPosition({ tokenId: mockTokenId }, mockChainId),
).rejects.toThrow("Tokens not found");
});

it("should throw error if liquidity is 0", async () => {
Expand Down Expand Up @@ -91,9 +91,9 @@ describe("getPosition", () => {
new Token(1, mockTokens[1], 18, "WETH", "Wrapped Ether"),
]);

await expect(getPosition(mockTokenId, mockChainId)).rejects.toThrow(
"Liquidity is 0",
);
await expect(
getPosition({ tokenId: mockTokenId }, mockChainId),
).rejects.toThrow("Liquidity is 0");
});

it("should return position data when position exists", async () => {
Expand Down Expand Up @@ -137,15 +137,14 @@ describe("getPosition", () => {

mockGetTokens.mockResolvedValueOnce(mockTokenInstances);

const result = await getPosition(mockTokenId, mockChainId);
const result = await getPosition({ tokenId: mockTokenId }, mockChainId);

expect(result).toBeDefined();
expect(result?.token0).toEqual(mockTokenInstances[0]);
expect(result?.token1).toEqual(mockTokenInstances[1]);
expect(result?.liquidity).toBe(BigInt("1000000000000000000"));
expect(result?.amounts).toBeDefined();
expect(result?.tickLower).toBeDefined();
expect(result?.tickUpper).toBeDefined();
expect(result?.position).toBeDefined();
expect(result?.pool).toBeDefined();
expect(result?.poolId).toBeDefined();
expect(result?.tokenId).toBe(mockTokenId);
});
});
20 changes: 7 additions & 13 deletions src/types/hooks/useGetPosition.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,23 @@
import type { UseQueryOptions } from "@tanstack/react-query";
import type { Token } from "@uniswap/sdk-core";
import type { Pool, Position } from "@uniswap/v4-sdk";

/**
* Result type for position data
*/
export interface PositionResult {
/** The position instance */
position: Position;
/** The pool instance associated with the position */
pool: Pool;
/** First token in the pair */
token0: Token;
/** Second token in the pair */
token1: Token;
/** Token amounts in the position */
amounts: {
/** Amount of token0 */
amount0: string;
/** Amount of token1 */
amount1: string;
};
/** Lower tick boundary of the position */
tickLower: number;
/** Upper tick boundary of the position */
tickUpper: number;
/** Total liquidity in the position */
liquidity: bigint;
/** Unique identifier for the pool */
poolId: `0x${string}`;
/** The unique identifier of the position */
tokenId: string;
}

/**
Expand Down
28 changes: 28 additions & 0 deletions src/types/utils/getPosition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { Token } from "@uniswap/sdk-core";
import type { Pool, Position } from "@uniswap/v4-sdk";

/**
* Parameters required for retrieving a Uniswap V4 position instance.
*/
export interface GetPositionParams {
/** The unique identifier of the position */
tokenId: string;
}

/**
* Response structure for retrieving a Uniswap V4 position instance.
*/
export interface GetPositionResponse {
/** The position instance */
position: Position;
/** The pool instance associated with the position */
pool: Pool;
/** The first token in the pool pair */
token0: Token;
/** The second token in the pool pair */
token1: Token;
/** The unique identifier of the pool */
poolId: `0x${string}`;
/** The unique identifier of the position */
tokenId: string;
}
52 changes: 22 additions & 30 deletions src/utils/getPosition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,55 @@ import { V4PositionManagerAbi } from "@/constants/abis/V4PositionMananger";
import { V4StateViewAbi } from "@/constants/abis/V4StateView";
import { getInstance } from "@/core/uniDevKitV4Factory";
import { decodePositionInfo } from "@/helpers/positions";
import type {
GetPositionParams,
GetPositionResponse,
} from "@/types/utils/getPosition";
import { getTokens } from "@/utils/getTokens";
import type { Token } from "@uniswap/sdk-core";
import { Pool, Position as V4Position } from "@uniswap/v4-sdk";

interface PositionResult {
token0: Token;
token1: Token;
amounts: {
amount0: string;
amount1: string;
};
tickLower: number;
tickUpper: number;
liquidity: bigint;
poolId: `0x${string}`;
}

/**
* Retrieves a Uniswap V4 position instance for a given token ID.
* @param params Position parameters including token ID
* @param chainId Optional chain ID where the position exists
* @returns Promise resolving to position data
* @throws Error if SDK instance is not found or if position data is invalid
*/
export async function getPosition(
tokenId: string,
params: GetPositionParams,
chainId?: number,
): Promise<PositionResult | undefined> {
): Promise<GetPositionResponse> {
const sdk = getInstance(chainId);
if (!sdk) throw new Error("SDK not initialized");
if (!sdk) {
throw new Error("SDK not found. Please create an instance first.");
}

const client = sdk.getClient();
const positionManager = sdk.getContractAddress("positionManager");
const stateView = sdk.getContractAddress("stateView");

// Fetch poolKey and raw position info
const [poolAndPositionInfo, liquidity] = await client.multicall({
allowFailure: false,
contracts: [
{
address: positionManager,
abi: V4PositionManagerAbi,
functionName: "getPoolAndPositionInfo",
args: [BigInt(tokenId)],
args: [BigInt(params.tokenId)],
},
{
address: positionManager,
abi: V4PositionManagerAbi,
functionName: "getPositionLiquidity",
args: [BigInt(tokenId)],
args: [BigInt(params.tokenId)],
},
],
});

const [poolKey, rawInfo] = poolAndPositionInfo;

const { currency0, currency1, fee, tickSpacing, hooks } = poolKey;

console.log("poolKey", poolKey);
console.log("rawInfo", rawInfo);

if (liquidity === 0n) {
throw new Error("Liquidity is 0");
}
Expand Down Expand Up @@ -119,15 +115,11 @@ export async function getPosition(
});

return {
position,
pool,
token0,
token1,
amounts: {
amount0: position.amount0.toSignificant(6),
amount1: position.amount1.toSignificant(6),
},
tickLower,
tickUpper,
liquidity,
poolId,
tokenId: params.tokenId,
};
}