@turtleclub/hooks 0.5.0-beta.12 → 0.5.0-beta.14
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.
- package/README.md +202 -33
- package/dist/index.cjs +275 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +233 -44
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/v2/covers/api.ts +23 -0
- package/src/v2/covers/hooks.ts +20 -0
- package/src/v2/covers/index.ts +13 -0
- package/src/v2/covers/schema.ts +20 -0
- package/src/v2/earn-interactions/api.ts +70 -0
- package/src/v2/earn-interactions/hooks.ts +57 -0
- package/src/v2/earn-interactions/index.ts +4 -0
- package/src/v2/earn-interactions/queries.ts +8 -0
- package/src/v2/earn-interactions/schema.ts +60 -0
- package/src/v2/index.ts +8 -1
- package/src/v2/nfts/api.ts +25 -0
- package/src/v2/nfts/hooks.ts +24 -0
- package/src/v2/nfts/index.ts +13 -0
- package/src/v2/nfts/queries.ts +10 -0
- package/src/v2/nfts/schema.ts +12 -0
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@turtleclub/hooks",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.5.0-beta.
|
|
4
|
+
"version": "0.5.0-beta.14",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"exports": {
|
|
7
7
|
".": {
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
"publishConfig": {
|
|
55
55
|
"access": "public"
|
|
56
56
|
},
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "1c079d74e9530d05b212ee03ee221e668ae3ce96"
|
|
58
58
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { apiClient } from "../lib/api-client";
|
|
2
|
+
import type { CoverRequestData, SubmitCoverRequestResponse } from "./schema";
|
|
3
|
+
import { submitCoverRequestResponseSchema } from "./schema";
|
|
4
|
+
|
|
5
|
+
// POST /turtle/nexus-cover-requests
|
|
6
|
+
export async function submitCoverRequest(
|
|
7
|
+
data: CoverRequestData
|
|
8
|
+
): Promise<SubmitCoverRequestResponse> {
|
|
9
|
+
const response = await apiClient.fetch("/turtle/nexus-cover-requests", {
|
|
10
|
+
method: "POST",
|
|
11
|
+
body: data,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const result = submitCoverRequestResponseSchema.safeParse(response);
|
|
15
|
+
|
|
16
|
+
if (!result.success) {
|
|
17
|
+
console.log("[ZOD ERROR]", result.error);
|
|
18
|
+
throw new Error(`Failed to submit cover request due to an invalid server response.`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return result.data;
|
|
22
|
+
}
|
|
23
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { useMutation } from "@tanstack/react-query";
|
|
2
|
+
import { submitCoverRequest } from "./api";
|
|
3
|
+
|
|
4
|
+
export interface UseSubmitCoverRequestOptions {
|
|
5
|
+
onSuccess?: (message: string) => void;
|
|
6
|
+
onError?: (message: string) => void;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function useSubmitCoverRequest(options?: UseSubmitCoverRequestOptions) {
|
|
10
|
+
return useMutation({
|
|
11
|
+
mutationFn: submitCoverRequest,
|
|
12
|
+
onSuccess: () => {
|
|
13
|
+
options?.onSuccess?.("Cover request submitted successfully!");
|
|
14
|
+
},
|
|
15
|
+
onError: (error) => {
|
|
16
|
+
console.error("[useSubmitCoverRequest]", error);
|
|
17
|
+
options?.onError?.("Failed to submit Cover Request");
|
|
18
|
+
},
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Hooks
|
|
2
|
+
export { useSubmitCoverRequest } from "./hooks";
|
|
3
|
+
export type { UseSubmitCoverRequestOptions } from "./hooks";
|
|
4
|
+
|
|
5
|
+
// API functions
|
|
6
|
+
export { submitCoverRequest } from "./api";
|
|
7
|
+
|
|
8
|
+
// Schemas
|
|
9
|
+
export { coverRequestDataSchema, submitCoverRequestResponseSchema } from "./schema";
|
|
10
|
+
|
|
11
|
+
// Types
|
|
12
|
+
export type { CoverRequestData, SubmitCoverRequestResponse } from "./schema";
|
|
13
|
+
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
export const coverRequestDataSchema = z.object({
|
|
4
|
+
protocolName: z.string(),
|
|
5
|
+
coverageAmount: z.string(),
|
|
6
|
+
periodDays: z.number(),
|
|
7
|
+
desiredApySacrifice: z.string(),
|
|
8
|
+
calculatedEstimatedPremium: z.string(),
|
|
9
|
+
tokenSymbol: z.string(),
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
export const submitCoverRequestResponseSchema = z.object({
|
|
14
|
+
success: z.boolean(),
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
export type CoverRequestData = z.infer<typeof coverRequestDataSchema>;
|
|
19
|
+
export type SubmitCoverRequestResponse = z.infer<typeof submitCoverRequestResponseSchema>;
|
|
20
|
+
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { apiClient } from "../lib/api-client";
|
|
2
|
+
import {
|
|
3
|
+
interactionResponseSchema,
|
|
4
|
+
type InteractionResponse,
|
|
5
|
+
type InteractionParams,
|
|
6
|
+
} from "./schema";
|
|
7
|
+
|
|
8
|
+
// Supported interaction types
|
|
9
|
+
type InteractionType = "deposit" | "withdraw" | "claimWithdraw";
|
|
10
|
+
|
|
11
|
+
// Helper to build query string from interaction params
|
|
12
|
+
function buildInteractionQuery(
|
|
13
|
+
params: Omit<InteractionParams, "opportunityId">
|
|
14
|
+
): string {
|
|
15
|
+
const searchParams = new URLSearchParams({
|
|
16
|
+
userAddress: params.userAddress,
|
|
17
|
+
tokenIn: params.tokenIn,
|
|
18
|
+
amount: params.amount,
|
|
19
|
+
distributorId: params.distributorId,
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
if (params.referralCode) {
|
|
23
|
+
searchParams.set("referralCode", params.referralCode);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (params.slippageBps !== undefined) {
|
|
27
|
+
searchParams.set("slippageBps", params.slippageBps.toString());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return searchParams.toString();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Generic interaction creator - DRY helper
|
|
34
|
+
async function createInteraction(
|
|
35
|
+
interactionType: InteractionType,
|
|
36
|
+
params: InteractionParams
|
|
37
|
+
): Promise<InteractionResponse> {
|
|
38
|
+
const { opportunityId, ...queryParams } = params;
|
|
39
|
+
const query = buildInteractionQuery(queryParams);
|
|
40
|
+
|
|
41
|
+
const data = await apiClient.fetch(
|
|
42
|
+
`/v1/interactions/${interactionType}/${opportunityId}?${query}`,
|
|
43
|
+
{
|
|
44
|
+
method: "POST",
|
|
45
|
+
domain: "earn",
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const result = interactionResponseSchema.safeParse(data);
|
|
50
|
+
|
|
51
|
+
if (result.success === false) {
|
|
52
|
+
console.log("[ZOD ERROR]", result.error);
|
|
53
|
+
throw new Error(
|
|
54
|
+
`Failed to parse ${interactionType} interaction: ${result.error.message}`
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
return result.data;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// POST /v1/interactions/deposit/{opportunityId}
|
|
61
|
+
export const createDepositInteraction = (params: InteractionParams) =>
|
|
62
|
+
createInteraction("deposit", params);
|
|
63
|
+
|
|
64
|
+
// POST /v1/interactions/withdraw/{opportunityId}
|
|
65
|
+
export const createWithdrawInteraction = (params: InteractionParams) =>
|
|
66
|
+
createInteraction("withdraw", params);
|
|
67
|
+
|
|
68
|
+
// POST /v1/interactions/claimWithdraw/{opportunityId}
|
|
69
|
+
export const createClaimWithdrawInteraction = (params: InteractionParams) =>
|
|
70
|
+
createInteraction("claimWithdraw", params);
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { useMutation, type UseMutationOptions } from "@tanstack/react-query";
|
|
2
|
+
import {
|
|
3
|
+
createDepositInteraction,
|
|
4
|
+
createWithdrawInteraction,
|
|
5
|
+
createClaimWithdrawInteraction,
|
|
6
|
+
} from "./api";
|
|
7
|
+
import type { InteractionResponse, InteractionParams } from "./schema";
|
|
8
|
+
|
|
9
|
+
// Re-export param types for convenience
|
|
10
|
+
export type {
|
|
11
|
+
InteractionParams,
|
|
12
|
+
CreateDepositInteractionParams,
|
|
13
|
+
CreateWithdrawInteractionParams,
|
|
14
|
+
CreateClaimWithdrawInteractionParams,
|
|
15
|
+
} from "./schema";
|
|
16
|
+
|
|
17
|
+
// Hook options type alias for cleaner signatures
|
|
18
|
+
type InteractionMutationOptions = Omit<
|
|
19
|
+
UseMutationOptions<InteractionResponse, Error, InteractionParams>,
|
|
20
|
+
"mutationFn"
|
|
21
|
+
>;
|
|
22
|
+
|
|
23
|
+
// Higher-order function to create interaction hooks - DRY helper
|
|
24
|
+
function createInteractionHook(
|
|
25
|
+
mutationFn: (params: InteractionParams) => Promise<InteractionResponse>
|
|
26
|
+
) {
|
|
27
|
+
return function useInteraction(options?: InteractionMutationOptions) {
|
|
28
|
+
return useMutation({
|
|
29
|
+
mutationFn,
|
|
30
|
+
...options,
|
|
31
|
+
});
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Hook for creating a deposit interaction
|
|
37
|
+
* POST /v1/interactions/deposit/{opportunityId}
|
|
38
|
+
*/
|
|
39
|
+
export const useCreateDepositInteraction = createInteractionHook(
|
|
40
|
+
createDepositInteraction
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Hook for creating a withdraw interaction
|
|
45
|
+
* POST /v1/interactions/withdraw/{opportunityId}
|
|
46
|
+
*/
|
|
47
|
+
export const useCreateWithdrawInteraction = createInteractionHook(
|
|
48
|
+
createWithdrawInteraction
|
|
49
|
+
);
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Hook for creating a claim withdraw interaction
|
|
53
|
+
* POST /v1/interactions/claimWithdraw/{opportunityId}
|
|
54
|
+
*/
|
|
55
|
+
export const useCreateClaimWithdrawInteraction = createInteractionHook(
|
|
56
|
+
createClaimWithdrawInteraction
|
|
57
|
+
);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { createQueryKeys } from "@lukemorales/query-key-factory";
|
|
2
|
+
// Note: Interactions are mutations (POST), so no query keys are needed for fetching.
|
|
3
|
+
// This file exists for consistency and potential future GET endpoints.
|
|
4
|
+
|
|
5
|
+
export const earnInteractionsQueries = createQueryKeys("earnInteractions", {
|
|
6
|
+
// Placeholder for potential future GET endpoints
|
|
7
|
+
// e.g., getting interaction history
|
|
8
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Response Schemas
|
|
5
|
+
// ============================================================================
|
|
6
|
+
|
|
7
|
+
// Transaction schema - matches V1InteractionResponse.transactions[].transaction
|
|
8
|
+
export const transactionSchema = z.object({
|
|
9
|
+
chainId: z.number().int(),
|
|
10
|
+
data: z.string(),
|
|
11
|
+
to: z.string(),
|
|
12
|
+
value: z.string(),
|
|
13
|
+
gasLimit: z.string().optional(),
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
// Transaction response item - matches TypesTxResponseItem
|
|
17
|
+
export const txResponseItemSchema = z.object({
|
|
18
|
+
type: z.string().optional(),
|
|
19
|
+
description: z.string().optional(),
|
|
20
|
+
transaction: transactionSchema,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
// Interaction response - matches V1InteractionResponse
|
|
24
|
+
export const interactionResponseSchema = z.object({
|
|
25
|
+
transactions: z.array(txResponseItemSchema).nullable(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// ============================================================================
|
|
29
|
+
// Request Params (shared base - all interactions use same params)
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
export interface InteractionParams {
|
|
33
|
+
/** Opportunity unique identifier (path param) */
|
|
34
|
+
opportunityId: string;
|
|
35
|
+
/** User wallet address */
|
|
36
|
+
userAddress: string;
|
|
37
|
+
/** Input token address */
|
|
38
|
+
tokenIn: string;
|
|
39
|
+
/** Amount in smallest unit (wei) */
|
|
40
|
+
amount: string;
|
|
41
|
+
/** Distributor ID */
|
|
42
|
+
distributorId: string;
|
|
43
|
+
/** Optional referral code */
|
|
44
|
+
referralCode?: string;
|
|
45
|
+
/** Slippage tolerance in basis points (default: 50 = 0.5%) */
|
|
46
|
+
slippageBps?: number;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Type aliases for semantic clarity
|
|
50
|
+
export type CreateDepositInteractionParams = InteractionParams;
|
|
51
|
+
export type CreateWithdrawInteractionParams = InteractionParams;
|
|
52
|
+
export type CreateClaimWithdrawInteractionParams = InteractionParams;
|
|
53
|
+
|
|
54
|
+
// ============================================================================
|
|
55
|
+
// Type Exports
|
|
56
|
+
// ============================================================================
|
|
57
|
+
|
|
58
|
+
export type Transaction = z.infer<typeof transactionSchema>;
|
|
59
|
+
export type TxResponseItem = z.infer<typeof txResponseItemSchema>;
|
|
60
|
+
export type InteractionResponse = z.infer<typeof interactionResponseSchema>;
|
package/src/v2/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { earnOpportunitiesQueries } from "./earn-opportunities/queries";
|
|
|
4
4
|
import { earnRouteQueries } from "./earn-route/queries";
|
|
5
5
|
import { earnMembershipQueries } from "./earn-membership/queries";
|
|
6
6
|
import { earnDepositsQueries } from "./earn-deposits/queries";
|
|
7
|
+
import { earnInteractionsQueries } from "./earn-interactions/queries";
|
|
7
8
|
import { productsQueries } from "./products/queries";
|
|
8
9
|
import { ensoBalancesQueries } from "./enso-balances/queries";
|
|
9
10
|
import { widgetQueries } from "./widget/queries";
|
|
@@ -11,6 +12,7 @@ import { streamsQueries } from "./streams/queries";
|
|
|
11
12
|
import { supportedChainsQueries } from "./supported-chains/queries";
|
|
12
13
|
import { supportedTokensQueries } from "./supported-tokens/queries";
|
|
13
14
|
import { usersQueries } from "./users/queries";
|
|
15
|
+
import { nftsQueries } from "./nfts/queries";
|
|
14
16
|
|
|
15
17
|
// Merged query keys for cache invalidation
|
|
16
18
|
export const queries = mergeQueryKeys(
|
|
@@ -19,13 +21,15 @@ export const queries = mergeQueryKeys(
|
|
|
19
21
|
earnRouteQueries,
|
|
20
22
|
earnMembershipQueries,
|
|
21
23
|
earnDepositsQueries,
|
|
24
|
+
earnInteractionsQueries,
|
|
22
25
|
productsQueries,
|
|
23
26
|
ensoBalancesQueries,
|
|
24
27
|
widgetQueries,
|
|
25
28
|
streamsQueries,
|
|
26
29
|
supportedChainsQueries,
|
|
27
30
|
supportedTokensQueries,
|
|
28
|
-
usersQueries
|
|
31
|
+
usersQueries,
|
|
32
|
+
nftsQueries
|
|
29
33
|
);
|
|
30
34
|
|
|
31
35
|
// Features - Earn API
|
|
@@ -33,6 +37,7 @@ export * from "./earn-opportunities";
|
|
|
33
37
|
export * from "./earn-route";
|
|
34
38
|
export * from "./earn-membership";
|
|
35
39
|
export * from "./earn-deposits";
|
|
40
|
+
export * from "./earn-interactions";
|
|
36
41
|
export * from "./enso-balances";
|
|
37
42
|
|
|
38
43
|
// Features
|
|
@@ -46,6 +51,8 @@ export * from "./geocheck";
|
|
|
46
51
|
export * from "./streams";
|
|
47
52
|
export * from "./swap";
|
|
48
53
|
export * from "./users";
|
|
54
|
+
export * from "./nfts";
|
|
55
|
+
export * from "./covers";
|
|
49
56
|
|
|
50
57
|
// Shared schemas
|
|
51
58
|
export * from "./schemas/shared";
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { UserNft } from "./schema";
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const LUMON_API_BASE_URL = "https://lumon.turtle.xyz";
|
|
5
|
+
|
|
6
|
+
export async function fetchUserNfts(userAddress: string, chain: number): Promise<UserNft[]> {
|
|
7
|
+
const endpoint = `${LUMON_API_BASE_URL}/query/token/erc721_portfolio`;
|
|
8
|
+
const response = await fetch(endpoint, {
|
|
9
|
+
method: "POST",
|
|
10
|
+
headers: {
|
|
11
|
+
"Content-Type": "application/json",
|
|
12
|
+
},
|
|
13
|
+
body: JSON.stringify({
|
|
14
|
+
user: userAddress,
|
|
15
|
+
chain,
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
throw new Error("Failed to fetch user NFTs");
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return response.json();
|
|
24
|
+
}
|
|
25
|
+
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { skipToken, useQuery } from "@tanstack/react-query";
|
|
2
|
+
import { createQueryOptions } from "../lib/query-config";
|
|
3
|
+
import { nftsQueries } from "./queries";
|
|
4
|
+
import type { UserNft } from "./schema";
|
|
5
|
+
|
|
6
|
+
export interface UseUserNftsOptions {
|
|
7
|
+
userAddress: string | undefined;
|
|
8
|
+
chain: number;
|
|
9
|
+
tokenAddress?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function useUserNfts({ userAddress, chain, tokenAddress }: UseUserNftsOptions) {
|
|
13
|
+
return useQuery(
|
|
14
|
+
userAddress
|
|
15
|
+
? createQueryOptions(nftsQueries.byUser(userAddress, chain), {
|
|
16
|
+
select: (data: UserNft[]) =>
|
|
17
|
+
tokenAddress
|
|
18
|
+
? data.filter((nft) => nft.token.toLowerCase() === tokenAddress.toLowerCase())
|
|
19
|
+
: data,
|
|
20
|
+
})
|
|
21
|
+
: { queryKey: ["nfts", "byUser", "skip"], queryFn: skipToken }
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// Hooks
|
|
2
|
+
export { useUserNfts } from "./hooks";
|
|
3
|
+
export type { UseUserNftsOptions } from "./hooks";
|
|
4
|
+
|
|
5
|
+
// Query keys
|
|
6
|
+
export { nftsQueries } from "./queries";
|
|
7
|
+
|
|
8
|
+
// API functions
|
|
9
|
+
export { fetchUserNfts } from "./api";
|
|
10
|
+
|
|
11
|
+
// Types
|
|
12
|
+
export type { UserNft, UserNftsFilters } from "./schema";
|
|
13
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { createQueryKeys } from "@lukemorales/query-key-factory";
|
|
2
|
+
import { fetchUserNfts } from "./api";
|
|
3
|
+
|
|
4
|
+
export const nftsQueries = createQueryKeys("nfts", {
|
|
5
|
+
byUser: (userAddress: string, chain: number) => ({
|
|
6
|
+
queryKey: [userAddress, chain],
|
|
7
|
+
queryFn: () => fetchUserNfts(userAddress, chain),
|
|
8
|
+
}),
|
|
9
|
+
});
|
|
10
|
+
|