@turtleclub/opportunities 0.1.0-beta.20 → 0.1.0-beta.22

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.
@@ -1,13 +1,5 @@
1
- import { useState, useEffect, useCallback } from "react";
2
- import { NEXUS_COVER_NFT_ADDRESS, USER_NFTS_API_URL } from "../constants";
3
-
4
- interface UserNft {
5
- user: string;
6
- chain: number;
7
- token: string;
8
- token_id: string;
9
- last_transfer: string;
10
- }
1
+ import { useUserNfts, type UserNft } from "@turtleclub/hooks";
2
+ import { NEXUS_COVER_NFT_ADDRESS } from "../constants";
11
3
 
12
4
  export type CoverNft = UserNft;
13
5
 
@@ -18,66 +10,17 @@ interface UseUserCoverNftsReturn {
18
10
  refetch: () => void;
19
11
  }
20
12
 
21
- async function fetchUserNfts(userAddress: string): Promise<UserNft[]> {
22
- const response = await fetch(USER_NFTS_API_URL, {
23
- method: "POST",
24
- headers: {
25
- "Content-Type": "application/json",
26
- },
27
- body: JSON.stringify({
28
- user: userAddress,
29
- chain: 1,
30
- }),
31
- });
32
-
33
-
34
-
35
- if (!response.ok) {
36
- throw new Error("Failed to fetch user NFTs");
37
- }
38
-
39
- return response.json();
40
- }
41
-
42
13
  export function useUserCoverNfts(userAddress: string | undefined): UseUserCoverNftsReturn {
43
- const [coverNfts, setCoverNfts] = useState<CoverNft[]>([]);
44
- const [isLoading, setIsLoading] = useState(false);
45
- const [error, setError] = useState<string | null>(null);
46
-
47
- const fetchCoverNfts = useCallback(async () => {
48
- if (!userAddress) {
49
- setCoverNfts([]);
50
- return;
51
- }
52
-
53
- setIsLoading(true);
54
- setError(null);
55
-
56
- try {
57
- const nfts = await fetchUserNfts(userAddress);
58
- const filtered = nfts.filter(
59
- (nft) => nft.token.toLowerCase() === NEXUS_COVER_NFT_ADDRESS.toLowerCase()
60
- );
61
- setCoverNfts(filtered);
62
- } catch (err: unknown) {
63
- const errorMessage = err instanceof Error ? err.message : "Failed to fetch cover NFTs";
64
- console.error("Cover NFTs fetch error:", err);
65
- setError(errorMessage);
66
- setCoverNfts([]);
67
- } finally {
68
- setIsLoading(false);
69
- }
70
- }, [userAddress]);
71
-
72
- useEffect(() => {
73
- fetchCoverNfts();
74
- }, [fetchCoverNfts]);
14
+ const { data, isLoading, error, refetch } = useUserNfts({
15
+ userAddress,
16
+ chain: 1,
17
+ tokenAddress: NEXUS_COVER_NFT_ADDRESS,
18
+ });
75
19
 
76
20
  return {
77
- coverNfts,
21
+ coverNfts: data ?? [],
78
22
  isLoading,
79
- error,
80
- refetch: fetchCoverNfts,
23
+ error: error?.message ?? null,
24
+ refetch,
81
25
  };
82
26
  }
83
-
@@ -1,4 +1,4 @@
1
1
  export { NexusCoverSection } from "./components/NexusCoverSection";
2
- export type { NexusCoverSectionProps, NexusQuoteResult } from "./types";
2
+ export type { NexusCoverSectionProps } from "./types";
3
3
 
4
4
  export { useNexusProduct, getNexusProductLookup, getProtocolForInsurance } from "./hooks/useNexusProduct";
@@ -1,45 +1,29 @@
1
+ import { CoverAsset } from "@nexusmutual/sdk";
1
2
  import type { Opportunity } from "@turtleclub/hooks";
3
+ import type { Asset } from "@turtleclub/ui";
2
4
 
3
- /** Result from Nexus SDK quote API */
4
- export interface NexusQuoteResult {
5
- buyCoverInput: {
6
- buyCoverParams: unknown;
7
- poolAllocationRequests: unknown;
8
- };
9
- displayInfo: {
10
- premiumInAsset: string;
11
- yearlyCostPerc: number;
12
- };
13
- }
5
+ // The Nexus SDK doesn't export the NexusProduct type, so we need to define it ourselves.
6
+ // The product has more attributes than just id, name, and coverAssets, but this are the only ones we need for now.
14
7
  export type NexusProduct = {
15
8
  id: number;
16
9
  name: string;
17
- coverAssets: number[];
10
+ coverAssets: string[];
18
11
  };
19
12
 
20
13
  export interface CoverOfferCardProps {
21
14
  productId: number;
22
15
  opportunity: Opportunity;
23
- amountToCover: string;
24
16
  buyerAddress: string;
25
17
  protocolName: string;
26
18
  coverProductName: string;
19
+ coverAvailableAssets: string[];
27
20
  onSuccess?: (message: string) => void;
28
21
  onError?: (message: string) => void;
29
22
  startExpanded?: boolean;
30
23
  }
31
24
 
32
- export interface CoverQuoteState {
33
- isLoading: boolean;
34
- error: string | null;
35
- quoteResult: NexusQuoteResult | null;
36
- coverPeriod: number;
37
- premiumEth: string | null;
38
- yearlyCostPerc: number | null;
39
- }
40
25
  export interface NexusCoverSectionProps {
41
26
  opportunity: Opportunity;
42
- amountToCover: string;
43
27
  buyerAddress: string;
44
28
  onSuccess?: (message: string) => void;
45
29
  onError?: (message: string) => void;
@@ -47,16 +31,8 @@ export interface NexusCoverSectionProps {
47
31
  startExpanded?: boolean;
48
32
  }
49
33
 
50
- export interface CoverRequestData {
51
- protocolName: string;
52
- coverageAmount: number;
53
- periodDays: number;
54
- desiredApySacrifice: number;
55
- estimatedPremium: number;
56
- }
57
34
  export interface CoverRequestFormProps {
58
35
  protocolName: string;
59
- depositedAmount?: string;
60
36
  baseApyLabel?: string;
61
37
  onDismiss?: () => void;
62
38
  onSuccess?: (message: string) => void;
@@ -1,3 +1,4 @@
1
+ import { CoverAsset } from "@nexusmutual/sdk";
1
2
  import type { Opportunity } from "@turtleclub/hooks";
2
3
  import { BaseError, UserRejectedRequestError } from "viem";
3
4
 
@@ -79,4 +80,11 @@ export function parseCoverTiming(startSec: number, periodSec: number, gracePerio
79
80
  expiresSec,
80
81
  graceEndsSec,
81
82
  };
82
- }
83
+ }
84
+
85
+ export const TOKEN_SYMBOL_TO_COVER_ASSET: Record<string, CoverAsset> = {
86
+ ETH: CoverAsset.ETH,
87
+ DAI: CoverAsset.DAI,
88
+ USDC: CoverAsset.USDC,
89
+ cbBTC: CoverAsset.cbBTC,
90
+ };
@@ -1,77 +0,0 @@
1
- "use client";
2
-
3
- import type { ComponentType } from "react";
4
- import { Button } from "@turtleclub/ui";
5
-
6
- interface MembershipRequestCardProps {
7
- isExpanded: boolean;
8
- onToggle: () => void;
9
- membershipUrl: string;
10
- ShieldIcon: ComponentType<any>;
11
- ExternalLinkIcon: ComponentType<any>;
12
- ChevronUpIcon: ComponentType<any>;
13
- ChevronDownIcon: ComponentType<any>;
14
- }
15
-
16
- export function MembershipRequestCard({
17
- isExpanded,
18
- onToggle,
19
- membershipUrl,
20
- ShieldIcon,
21
- ExternalLinkIcon,
22
- ChevronUpIcon,
23
- ChevronDownIcon,
24
- }: MembershipRequestCardProps) {
25
- return (
26
- <div className="rounded-xl bg-gradient-to-br from-primary/10 to-card border border-primary/20 overflow-hidden">
27
- <button
28
- onClick={onToggle}
29
- className="w-full p-4 flex items-center justify-between hover:bg-primary/5 transition-colors"
30
- >
31
- <div className="flex items-center gap-3">
32
- <div className="p-2 rounded-lg bg-primary/10 border border-primary/20">
33
- <ShieldIcon className="w-5 h-5 text-primary" />
34
- </div>
35
- <div className="text-left">
36
- <div className="flex items-center gap-2">
37
- <span className="text-sm font-semibold text-foreground">Protect Your Deposit</span>
38
- <span className="px-2 py-0.5 text-[10px] font-medium text-primary bg-primary/10 rounded-full border border-primary/20">
39
- NEW
40
- </span>
41
- </div>
42
- <p className="text-xs text-muted-foreground">Nexus Mutual Single Protocol Cover</p>
43
- </div>
44
- </div>
45
- {isExpanded ? (
46
- <ChevronUpIcon className="w-5 h-5 text-muted-foreground" />
47
- ) : (
48
- <ChevronDownIcon className="w-5 h-5 text-muted-foreground" />
49
- )}
50
- </button>
51
-
52
- {isExpanded && (
53
- <div className="px-4 pb-4 space-y-4">
54
- <div className="p-3 rounded-lg bg-background/50 border border-border">
55
- <p className="text-xs text-muted-foreground leading-relaxed">
56
- <span className="text-primary font-medium">Membership required.</span> Join Nexus
57
- Mutual to protect your deposit against smart contract exploits, oracle failures, and
58
- protocol-specific risks.
59
- </p>
60
- </div>
61
-
62
- <Button asChild className="w-full" variant="default">
63
- <a
64
- href={membershipUrl}
65
- target="_blank"
66
- rel="noopener noreferrer"
67
- className="flex items-center justify-center gap-2"
68
- >
69
- Become a Member
70
- <ExternalLinkIcon className="w-4 h-4" />
71
- </a>
72
- </Button>
73
- </div>
74
- )}
75
- </div>
76
- );
77
- }