@turtleclub/opportunities 0.1.0-beta.5 → 0.1.0-beta.51

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.
@@ -0,0 +1,41 @@
1
+ import { CoverAsset } from "@nexusmutual/sdk";
2
+ import type { Opportunity } from "@turtleclub/hooks";
3
+ import type { Asset } from "@turtleclub/ui";
4
+
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.
7
+ export type NexusProduct = {
8
+ id: number;
9
+ name: string;
10
+ coverAssets: string[];
11
+ };
12
+
13
+ export interface CoverOfferCardProps {
14
+ productId: number;
15
+ opportunity: Opportunity;
16
+ buyerAddress: string;
17
+ protocolName: string;
18
+ coverProductName: string;
19
+ coverAvailableAssets: string[];
20
+ onSuccess?: (message: string) => void;
21
+ onError?: (message: string) => void;
22
+ startExpanded?: boolean;
23
+ }
24
+
25
+ export interface NexusCoverSectionProps {
26
+ opportunity: Opportunity;
27
+ buyerAddress: string;
28
+ onSuccess?: (message: string) => void;
29
+ onError?: (message: string) => void;
30
+ className?: string;
31
+ startExpanded?: boolean;
32
+ }
33
+
34
+ export interface CoverRequestFormProps {
35
+ protocolName: string;
36
+ baseApyLabel?: string;
37
+ onDismiss?: () => void;
38
+ onSuccess?: (message: string) => void;
39
+ onError?: (message: string) => void;
40
+ startExpanded?: boolean;
41
+ }
@@ -0,0 +1,90 @@
1
+ import { CoverAsset } from "@nexusmutual/sdk";
2
+ import type { Opportunity } from "@turtleclub/hooks";
3
+ import { BaseError, UserRejectedRequestError } from "viem";
4
+
5
+ export function getOpportunityAPY(opportunity: Opportunity): number {
6
+ if (!opportunity.incentives || opportunity.incentives.length === 0) {
7
+ return 0;
8
+ }
9
+
10
+ return opportunity.incentives.reduce((total, incentive) => {
11
+ return total + (incentive.yield ?? 0);
12
+ }, 0);
13
+ }
14
+
15
+ /**
16
+ * Calculate the adjusted APY after subtracting the cover cost
17
+ * @param opportunityAPY - The original APY as a percentage (e.g., 5 for 5%)
18
+ * @param yearlyCostPerc - The yearly cost as a decimal (e.g., 0.025 for 2.5%)
19
+ * @returns The adjusted APY as a percentage
20
+ */
21
+ export function calculateAdjustedAPY(
22
+ opportunityAPY: number,
23
+ yearlyCostPerc: number
24
+ ): number {
25
+ const coverCostPercentage = yearlyCostPerc * 100;
26
+ return Math.max(0, opportunityAPY - coverCostPercentage);
27
+ }
28
+
29
+
30
+ export function formatAPY(apy: number): string {
31
+ return `${apy.toFixed(2)}%`;
32
+ }
33
+
34
+ export function formatEthAmount(amount: string, decimals: number = 6): string {
35
+ const num = parseFloat(amount);
36
+ if (isNaN(num)) return "0";
37
+ return num.toFixed(decimals);
38
+ }
39
+
40
+
41
+ export const formatPurchaseError = (err: unknown): string => {
42
+ console.error("Purchase error:", err, typeof err);
43
+ if (err instanceof UserRejectedRequestError) {
44
+ return "Transaction cancelled";
45
+ }
46
+
47
+ if (err instanceof BaseError) {
48
+ const userRejected =
49
+ typeof err.walk === "function"
50
+ ? err.walk((error) => error instanceof UserRejectedRequestError)
51
+ : false;
52
+ if (userRejected) {
53
+ return "Transaction cancelled";
54
+ }
55
+
56
+ return err.shortMessage || err.message;
57
+ }
58
+
59
+ if (err instanceof Error) {
60
+ return err.message;
61
+ }
62
+
63
+ return "Failed to purchase cover";
64
+ };
65
+
66
+
67
+
68
+ const SECS_IN_DAY = 24 * 60 * 60;
69
+
70
+ export function parseCoverTiming(startSec: number, periodSec: number, gracePeriodSec: number = 0) {
71
+ const expiresSec = startSec + periodSec;
72
+ const graceEndsSec = expiresSec + gracePeriodSec;
73
+
74
+ return {
75
+ startDate: new Date(startSec * 1000),
76
+ expiresDate: new Date(expiresSec * 1000),
77
+ graceEndsDate: new Date(graceEndsSec * 1000),
78
+ periodDays: periodSec / SECS_IN_DAY,
79
+ graceDays: gracePeriodSec / SECS_IN_DAY,
80
+ expiresSec,
81
+ graceEndsSec,
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
+ };
package/src/index.ts CHANGED
@@ -7,6 +7,9 @@ export * from "./deposit";
7
7
  // Transaction Status
8
8
  export * from "./transaction-status";
9
9
 
10
+ // Cover Offer
11
+ export * from "./cover-offer";
12
+
10
13
  // Opportunity Table
11
14
  export * from "./opportunity-table/components";
12
15