From 82ca11d1a87a97c8a13b33d77e27cf9f40169551 Mon Sep 17 00:00:00 2001
From: Iwueseiter <156322726+Iwueseiter@users.noreply.github.com>
Date: Fri, 6 Sep 2024 11:33:14 +0000
Subject: [PATCH 01/11] feat: harvest time ui
---
src/components/HarvestTime.tsx | 150 +++++++++++++++++++++++++++++++++
src/components/Strategies.tsx | 5 ++
2 files changed, 155 insertions(+)
create mode 100644 src/components/HarvestTime.tsx
diff --git a/src/components/HarvestTime.tsx b/src/components/HarvestTime.tsx
new file mode 100644
index 00000000..fd622f64
--- /dev/null
+++ b/src/components/HarvestTime.tsx
@@ -0,0 +1,150 @@
+import { Box, Text } from '@chakra-ui/react';
+import React from 'react';
+
+export default function HarvestTime() {
+ return (
+
+
+
+
+ APY
+
+
+
+ 23.94%
+
+
+
+ 🔥 1.96x boosted
+
+
+
+
+
+
+
+ Next Harvest in:
+
+
+
+
+ Days
+
+
+ 18
+
+
+
+
+
+ Hrs
+
+
+ 15
+
+
+
+
+
+ Mins
+
+
+ 05
+
+
+
+
+
+ Sec
+
+
+ 01
+
+
+
+
+
+
+
+ Total rewards harvested:
+
+ $500 | Total number of times harvested: 48
+
+
+
+
+ How does it work?
+
+
+ Deposit your USDC to automatically loop your funds between zkLend and
+ Nostra to create a delta neutral position. This strategy is designed to
+ maximize your yield on USDC. Your position is automatically adjusted
+ periodically to maintain a healthy health factor. You receive a NFT as
+ representation for your stake on STRKFarm. You can withdraw anytime by
+ redeeming your NFT for USDC.
+
+
+ );
+}
diff --git a/src/components/Strategies.tsx b/src/components/Strategies.tsx
index b215f901..c8df4807 100755
--- a/src/components/Strategies.tsx
+++ b/src/components/Strategies.tsx
@@ -40,6 +40,7 @@ import { userStatsAtom } from '@/store/utils.atoms';
import { IStrategyProps, StrategyLiveStatus } from '@/strategies/IStrategy';
import { allPoolsAtomUnSorted } from '@/store/protocols';
import { FaWallet } from 'react-icons/fa';
+import HarvestTime from './HarvestTime';
export default function Strategies() {
const allPools = useAtomValue(allPoolsAtomUnSorted);
@@ -389,6 +390,9 @@ export default function Strategies() {
return (
+
+
+
+
From f02c748459a52fb945b7b7a5121949c803e88c2f Mon Sep 17 00:00:00 2001
From: Iwueseiter <156322726+Iwueseiter@users.noreply.github.com>
Date: Mon, 16 Sep 2024 05:06:25 +0000
Subject: [PATCH 02/11] feat: display API data
---
.../[strategyId]/_components/Strategy.tsx | 112 +------
src/components/HarvestTime.tsx | 308 +++++++++++++-----
src/store/harvest.atom.ts | 25 +-
src/utils.ts | 11 +
4 files changed, 261 insertions(+), 195 deletions(-)
diff --git a/src/app/strategy/[strategyId]/_components/Strategy.tsx b/src/app/strategy/[strategyId]/_components/Strategy.tsx
index 61f5609e..2a51cf68 100755
--- a/src/app/strategy/[strategyId]/_components/Strategy.tsx
+++ b/src/app/strategy/[strategyId]/_components/Strategy.tsx
@@ -31,7 +31,6 @@ import { useAccount } from '@starknet-react/core';
import { atom, useAtomValue, useSetAtom } from 'jotai';
import mixpanel from 'mixpanel-browser';
import { useEffect, useMemo, useState } from 'react';
-import { isMobile } from 'react-device-detect';
import Deposit from '@/components/Deposit';
import CONSTANTS from '@/constants';
@@ -109,112 +108,20 @@ const Strategy = ({ params }: StrategyParams) => {
{strategy ? strategy.name : 'Strategy Not found'}
+
+
{strategy && (
+
+
-
-
-
-
- How does it work?
-
-
- {strategy.description}
-
-
- {getUniqueById(
- strategy.actions.map((p) => ({
- id: p.pool.protocol.name,
- logo: p.pool.protocol.logo,
- })),
- ).map((p) => (
-
-
-
- {p.id}
-
-
- ))}
-
-
-
-
-
- APY
-
-
- {(strategy.netYield * 100).toFixed(2)}%
-
-
- {strategy.leverage.toFixed(2)}x boosted
-
-
-
-
-
- {!balData.isLoading &&
- !balData.isError &&
- !balData.isPending &&
- balData.data &&
- balData.data.tokenInfo && (
-
- Your Holdings:
- {address
- ? `${balData.data.amount.toEtherToFixedDecimals(4)} ${balData.data.tokenInfo?.name}`
- : isMobile
- ? CONSTANTS.MOBILE_MSG
- : 'Connect wallet'}
-
- )}
- {(balData.isLoading ||
- balData.isPending ||
- !balData.data?.tokenInfo) && (
-
- Your Holdings:
- {address ? (
-
- ) : isMobile ? (
- CONSTANTS.MOBILE_MSG
- ) : (
- 'Connect wallet'
- )}
-
- )}
- {balData.isError && (
-
- Your Holdings: Error
-
- )}
-
-
+
+
+
+
+
@@ -276,7 +183,6 @@ const Strategy = ({ params }: StrategyParams) => {
-
diff --git a/src/components/HarvestTime.tsx b/src/components/HarvestTime.tsx
index 0c149e9c..02d3f761 100644
--- a/src/components/HarvestTime.tsx
+++ b/src/components/HarvestTime.tsx
@@ -1,18 +1,34 @@
import React, { useMemo } from 'react';
-import { Box, Text } from '@chakra-ui/react';
+import {
+ Avatar,
+ Box,
+ Card,
+ Center,
+ Spinner,
+ Stat,
+ StatHelpText,
+ StatLabel,
+ StatNumber,
+ Text,
+ Wrap,
+ WrapItem,
+} from '@chakra-ui/react';
import { useAccount } from '@starknet-react/core';
-import { StrategyInfo } from '@/store/strategies.atoms'; // Adjust import path if necessary
+import { StrategyInfo } from '@/store/strategies.atoms';
import { HarvestTimeAtom } from '@/store/harvest.atoms';
import { useAtomValue } from 'jotai';
+import { formatTimestamp, getUniqueById } from '@/utils';
+import CONSTANTS from '@/constants';
+import { isMobile } from 'react-device-detect';
interface HarvestTimeProps {
strategy: StrategyInfo;
+ balData: any;
}
-const HarvestTime: React.FC = ({ strategy }) => {
+const HarvestTime: React.FC = ({ strategy, balData }) => {
const { address } = useAccount();
-
- const contractAddress = strategy.holdingTokens[0].address ?? "";
+ const contractAddress = strategy.holdingTokens[0].address ?? '';
const harvestTimeAtom = useMemo(
() => HarvestTimeAtom(contractAddress),
@@ -21,94 +37,224 @@ const HarvestTime: React.FC = ({ strategy }) => {
const harvestTime = useAtomValue(harvestTimeAtom);
+ const data = harvestTime.data?.findManyHarvests[0];
+
+ const harvestTimestamp = useMemo(() => {
+ if (!data?.timestamp) return null;
+ return formatTimestamp(data.timestamp);
+ }, [data?.timestamp]);
+
return (
-
-
-
- APY
-
-
-
- 23.94%
-
-
-
- 🔥 1.96x boosted
-
-
+
+
+
+
+ APY
+
+
+ {(strategy.netYield * 100).toFixed(2)}%
+
+
+ {strategy.leverage.toFixed(2)}x boosted
+
+
+
-
-
-
- Next Harvest in:
-
-
- {/* Repeated Box components for days, hours, mins, secs */}
+
+
+ Next Harvest in:
+
-
- Days
-
-
- 18
-
+
+
+ Mon
+
+
+ {harvestTimestamp?.month}
+
+
+
+
+
+ Days
+
+
+ {harvestTimestamp?.day}
+
+
+
+
+
+ Hour
+
+
+ {harvestTimestamp?.hour}
+
+
+
+
+
+ Mins
+
+
+ {harvestTimestamp?.minute}
+
+
+
+ {/*
+
+ Secs
+
+
+ {harvestTimestamp?.second}
+
+ */}
- {/* Other time boxes omitted for brevity */}
-
-
-
- Total rewards harvested:
-
- $500 | Total number of times harvested: 48
+
+
+
+ Total rewards harvested:
+
+ {(BigInt(data?.amount.toString() ?? '') / BigInt(10 ** 18)).toString()} |
+ Total number of times harvested:{' '}
+ {harvestTime?.data?.totalHarvests}
+
-
-
-
- How does it work?
-
-
- Deposit your USDC to automatically loop your funds between zkLend and
- Nostra to create a delta neutral position. This strategy is designed to
- maximize your yield on USDC. Your position is automatically adjusted
- periodically to maintain a healthy health factor. You receive a NFT as
- representation for your stake on STRKFarm. You can withdraw anytime by
- redeeming your NFT for USDC.
-
+
+
+
+
+
+ How does it work?
+
+
+ {strategy.description}
+
+
+ {getUniqueById(
+ strategy.actions.map((p) => ({
+ id: p.pool.protocol.name,
+ logo: p.pool.protocol.logo,
+ })),
+ ).map((p) => (
+
+
+
+ {p.id}
+
+
+ ))}
+
+
+
+
+
+ {!balData.isLoading &&
+ !balData.isError &&
+ !balData.isPending &&
+ balData.data &&
+ balData.data.tokenInfo && (
+
+ Your Holdings:
+ {address
+ ? `${balData.data.amount.toEtherToFixedDecimals(4)} ${balData.data.tokenInfo?.name}`
+ : isMobile
+ ? CONSTANTS.MOBILE_MSG
+ : 'Connect wallet'}
+
+ )}
+ {(balData.isLoading ||
+ balData.isPending ||
+ !balData.data?.tokenInfo) && (
+
+ Your Holdings:
+ {address ? (
+
+ ) : isMobile ? (
+ CONSTANTS.MOBILE_MSG
+ ) : (
+ 'Connect wallet'
+ )}
+
+ )}
+ {balData.isError && (
+
+ Your Holdings: Error
+
+ )}
+
+
);
};
diff --git a/src/store/harvest.atom.ts b/src/store/harvest.atom.ts
index 8f41ea65..ad2d20c7 100644
--- a/src/store/harvest.atom.ts
+++ b/src/store/harvest.atom.ts
@@ -19,26 +19,24 @@ const GET_HARVESTS_QUERY = gql`
query Query(
$where: HarvestsWhereInput,
$take: Int,
- $orderBy: [HarvestsOrderByWithRelationInput!],
- $contract: String!,
- $totalStrkHarvestedContract2: String!
+ $orderBy: [HarvestsOrderByWithRelationInput!]
) {
findManyHarvests(where: $where, take: $take, orderBy: $orderBy) {
contract
amount
timestamp
}
- totalHarvests(contract: $contract)
- totalStrkHarvested(contract: $totalStrkHarvestedContract2)
+ totalHarvests
+
}
`;
+
// Function to execute the query
async function getHarvestData(
contract: string,
- totalStrkHarvestedContract2?: string,
- take: number = 10,
- orderBy: any = [{ timestamp: 'desc' }] // adjust the sort order as needed
+ take: number = 1,
+ orderBy: any = [{ timestamp: 'desc' }]
): Promise {
try {
const { data } = await apolloClient.query({
@@ -51,8 +49,6 @@ async function getHarvestData(
},
take,
orderBy,
- contract,
- totalStrkHarvestedContract2,
},
});
@@ -69,7 +65,14 @@ export const HarvestTimeAtom = (contract: string) =>
atomWithQuery((get) => ({
queryKey: ['harvest_data', contract],
queryFn: async () => {
- const result = await getHarvestData(contract);
+
+ //Mock Harvest time
+ const testContractAddress = "0x20d5fc4c9df4f943ebb36078e703369c04176ed00accf290e8295b659d2cea6"
+ const result = await getHarvestData(testContractAddress);
return result;
+
+ // const testContractAddress = "0x20d5fc4c9df4f943ebb36078e703369c04176ed00accf290e8295b659d2cea6"
+ // const result = await getHarvestData(contract);
+ // return result;
},
}));
diff --git a/src/utils.ts b/src/utils.ts
index dfc2c871..6f121851 100755
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -101,3 +101,14 @@ export function getDisplayCurrencyAmount(
) {
return Number(Number(amount).toFixed(decimals)).toLocaleString();
}
+
+export function formatTimestamp(timestamp: string) {
+ const date = new Date(parseInt(timestamp, 10) * 1000);
+ return {
+ day: date.getUTCDate(),
+ month: date.getUTCMonth() + 1,
+ hour: date.getUTCHours(),
+ minute: date.getUTCMinutes(),
+ second: date.getUTCSeconds(),
+ };
+}
From 56a7b75a4092493d16c8050bfd710a3bef35084f Mon Sep 17 00:00:00 2001
From: Iwueseiter <156322726+Iwueseiter@users.noreply.github.com>
Date: Mon, 16 Sep 2024 11:24:41 +0000
Subject: [PATCH 03/11] fix: imports
---
src/components/HarvestTime.tsx | 21 +++++++++++++++------
1 file changed, 15 insertions(+), 6 deletions(-)
diff --git a/src/components/HarvestTime.tsx b/src/components/HarvestTime.tsx
index 02d3f761..530a9c77 100644
--- a/src/components/HarvestTime.tsx
+++ b/src/components/HarvestTime.tsx
@@ -14,8 +14,8 @@ import {
WrapItem,
} from '@chakra-ui/react';
import { useAccount } from '@starknet-react/core';
-import { StrategyInfo } from '@/store/strategies.atoms';
-import { HarvestTimeAtom } from '@/store/harvest.atoms';
+import { StrategyInfo } from '@/store/strategies.atoms';
+import { HarvestTimeAtom } from '@/store/harvest.atom';
import { useAtomValue } from 'jotai';
import { formatTimestamp, getUniqueById } from '@/utils';
import CONSTANTS from '@/constants';
@@ -51,12 +51,17 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
APY
-
+
{(strategy.netYield * 100).toFixed(2)}%
- {strategy.leverage.toFixed(2)}x boosted
+ {strategy.leverage.toFixed(2)}x boosted
@@ -175,8 +180,12 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
Total rewards harvested:
- {(BigInt(data?.amount.toString() ?? '') / BigInt(10 ** 18)).toString()} |
- Total number of times harvested:{' '}
+
+ {(
+ BigInt(data?.amount.toString() ?? '') / BigInt(10 ** 18)
+ ).toString()}
+ {' '}
+ | Total number of times harvested:{' '}
{harvestTime?.data?.totalHarvests}
From 808a4f50a5768900f0a24787f27ff3c82dd625b5 Mon Sep 17 00:00:00 2001
From: Iwueseiter <156322726+Iwueseiter@users.noreply.github.com>
Date: Mon, 16 Sep 2024 18:19:06 +0000
Subject: [PATCH 04/11] fix: lint
---
.../[strategyId]/_components/Strategy.tsx | 20 ++-----------------
src/components/Strategies.tsx | 1 -
src/store/harvest.atom.ts | 17 +++++++---------
src/utils/apolloClient.ts | 2 +-
4 files changed, 10 insertions(+), 30 deletions(-)
diff --git a/src/app/strategy/[strategyId]/_components/Strategy.tsx b/src/app/strategy/[strategyId]/_components/Strategy.tsx
index 2a51cf68..f40c2ed1 100755
--- a/src/app/strategy/[strategyId]/_components/Strategy.tsx
+++ b/src/app/strategy/[strategyId]/_components/Strategy.tsx
@@ -4,18 +4,12 @@ import {
Avatar,
Box,
Card,
- Center,
Flex,
Grid,
GridItem,
Link,
ListItem,
OrderedList,
- Spinner,
- Stat,
- StatHelpText,
- StatLabel,
- StatNumber,
Tab,
TabIndicator,
TabList,
@@ -24,8 +18,6 @@ import {
Tabs,
Text,
VStack,
- Wrap,
- WrapItem,
} from '@chakra-ui/react';
import { useAccount } from '@starknet-react/core';
import { atom, useAtomValue, useSetAtom } from 'jotai';
@@ -33,14 +25,13 @@ import mixpanel from 'mixpanel-browser';
import { useEffect, useMemo, useState } from 'react';
import Deposit from '@/components/Deposit';
-import CONSTANTS from '@/constants';
import { DUMMY_BAL_ATOM } from '@/store/balance.atoms';
import { StrategyInfo, strategiesAtom } from '@/store/strategies.atoms';
import {
StrategyTxPropsToMessageWithStrategies,
transactionsAtom,
} from '@/store/transactions.atom';
-import { getUniqueById, shortAddress } from '@/utils';
+import { shortAddress } from '@/utils';
import { StrategyParams } from '../page';
import HarvestTime from '@/components/HarvestTime';
@@ -109,19 +100,13 @@ const Strategy = ({ params }: StrategyParams) => {
-
{strategy && (
-
-
-
-
+
-
-
@@ -182,7 +167,6 @@ const Strategy = ({ params }: StrategyParams) => {
-
diff --git a/src/components/Strategies.tsx b/src/components/Strategies.tsx
index a089c014..235a91ac 100755
--- a/src/components/Strategies.tsx
+++ b/src/components/Strategies.tsx
@@ -40,7 +40,6 @@ export default function Strategies() {
return (
-
What are strategies?
diff --git a/src/store/harvest.atom.ts b/src/store/harvest.atom.ts
index ad2d20c7..1932d2ec 100644
--- a/src/store/harvest.atom.ts
+++ b/src/store/harvest.atom.ts
@@ -17,8 +17,8 @@ interface QueryResponse {
// GraphQL query
const GET_HARVESTS_QUERY = gql`
query Query(
- $where: HarvestsWhereInput,
- $take: Int,
+ $where: HarvestsWhereInput
+ $take: Int
$orderBy: [HarvestsOrderByWithRelationInput!]
) {
findManyHarvests(where: $where, take: $take, orderBy: $orderBy) {
@@ -27,16 +27,14 @@ const GET_HARVESTS_QUERY = gql`
timestamp
}
totalHarvests
-
}
`;
-
// Function to execute the query
async function getHarvestData(
contract: string,
take: number = 1,
- orderBy: any = [{ timestamp: 'desc' }]
+ orderBy: any = [{ timestamp: 'desc' }],
): Promise {
try {
const { data } = await apolloClient.query({
@@ -60,19 +58,18 @@ async function getHarvestData(
}
}
-
export const HarvestTimeAtom = (contract: string) =>
atomWithQuery((get) => ({
queryKey: ['harvest_data', contract],
queryFn: async () => {
-
//Mock Harvest time
- const testContractAddress = "0x20d5fc4c9df4f943ebb36078e703369c04176ed00accf290e8295b659d2cea6"
+ const testContractAddress =
+ '0x20d5fc4c9df4f943ebb36078e703369c04176ed00accf290e8295b659d2cea6';
const result = await getHarvestData(testContractAddress);
- return result;
+ return result;
// const testContractAddress = "0x20d5fc4c9df4f943ebb36078e703369c04176ed00accf290e8295b659d2cea6"
// const result = await getHarvestData(contract);
- // return result;
+ // return result;
},
}));
diff --git a/src/utils/apolloClient.ts b/src/utils/apolloClient.ts
index 419a28bf..0c05c718 100644
--- a/src/utils/apolloClient.ts
+++ b/src/utils/apolloClient.ts
@@ -5,4 +5,4 @@ const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
});
-export default apolloClient;
\ No newline at end of file
+export default apolloClient;
From b607e865350bc3894a029467da9238e0799f7567 Mon Sep 17 00:00:00 2001
From: Iwueseiter <156322726+Iwueseiter@users.noreply.github.com>
Date: Tue, 17 Sep 2024 05:58:49 +0000
Subject: [PATCH 05/11] fix: remove mock contract
---
src/components/HarvestTime.tsx | 29 ++++++++++-------------------
src/store/harvest.atom.ts | 20 ++++++++------------
2 files changed, 18 insertions(+), 31 deletions(-)
diff --git a/src/components/HarvestTime.tsx b/src/components/HarvestTime.tsx
index 530a9c77..b98df005 100644
--- a/src/components/HarvestTime.tsx
+++ b/src/components/HarvestTime.tsx
@@ -1,4 +1,4 @@
-import React, { useMemo } from 'react';
+import React, { useEffect, useMemo } from 'react';
import {
Avatar,
Box,
@@ -14,7 +14,7 @@ import {
WrapItem,
} from '@chakra-ui/react';
import { useAccount } from '@starknet-react/core';
-import { StrategyInfo } from '@/store/strategies.atoms';
+import { StrategyInfo } from '@/store/strategies.atoms';
import { HarvestTimeAtom } from '@/store/harvest.atom';
import { useAtomValue } from 'jotai';
import { formatTimestamp, getUniqueById } from '@/utils';
@@ -51,17 +51,12 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
APY
-
+
{(strategy.netYield * 100).toFixed(2)}%
- {strategy.leverage.toFixed(2)}x boosted
+ {strategy.leverage.toFixed(2)}x boosted
@@ -93,7 +88,7 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
Mon
- {harvestTimestamp?.month}
+ {harvestTimestamp?.month ?? 0}
@@ -112,7 +107,7 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
Days
- {harvestTimestamp?.day}
+ {harvestTimestamp?.day ?? 0}
@@ -131,7 +126,7 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
Hour
- {harvestTimestamp?.hour}
+ {harvestTimestamp?.hour ?? 0}
@@ -150,7 +145,7 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
Mins
- {harvestTimestamp?.minute}
+ {harvestTimestamp?.minute ?? 0}
@@ -180,12 +175,8 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
Total rewards harvested:
-
- {(
- BigInt(data?.amount.toString() ?? '') / BigInt(10 ** 18)
- ).toString()}
- {' '}
- | Total number of times harvested:{' '}
+ {(BigInt(data?.amount.toString() ?? '') / BigInt(10 ** 18)).toString()} |
+ Total number of times harvested:{' '}
{harvestTime?.data?.totalHarvests}
diff --git a/src/store/harvest.atom.ts b/src/store/harvest.atom.ts
index 1932d2ec..654c565a 100644
--- a/src/store/harvest.atom.ts
+++ b/src/store/harvest.atom.ts
@@ -17,8 +17,8 @@ interface QueryResponse {
// GraphQL query
const GET_HARVESTS_QUERY = gql`
query Query(
- $where: HarvestsWhereInput
- $take: Int
+ $where: HarvestsWhereInput,
+ $take: Int,
$orderBy: [HarvestsOrderByWithRelationInput!]
) {
findManyHarvests(where: $where, take: $take, orderBy: $orderBy) {
@@ -27,14 +27,16 @@ const GET_HARVESTS_QUERY = gql`
timestamp
}
totalHarvests
+
}
`;
+
// Function to execute the query
async function getHarvestData(
contract: string,
take: number = 1,
- orderBy: any = [{ timestamp: 'desc' }],
+ orderBy: any = [{ timestamp: 'desc' }]
): Promise {
try {
const { data } = await apolloClient.query({
@@ -58,18 +60,12 @@ async function getHarvestData(
}
}
+
export const HarvestTimeAtom = (contract: string) =>
atomWithQuery((get) => ({
queryKey: ['harvest_data', contract],
queryFn: async () => {
- //Mock Harvest time
- const testContractAddress =
- '0x20d5fc4c9df4f943ebb36078e703369c04176ed00accf290e8295b659d2cea6';
- const result = await getHarvestData(testContractAddress);
- return result;
-
- // const testContractAddress = "0x20d5fc4c9df4f943ebb36078e703369c04176ed00accf290e8295b659d2cea6"
- // const result = await getHarvestData(contract);
- // return result;
+ const result = await getHarvestData(contract);
+ return result;
},
}));
From 9c1bb355c2a54fb59ea79268ca5b42641f88d9ce Mon Sep 17 00:00:00 2001
From: Iwueseiter <156322726+Iwueseiter@users.noreply.github.com>
Date: Tue, 17 Sep 2024 05:59:28 +0000
Subject: [PATCH 06/11] fix: remove mock contract
---
src/components/HarvestTime.tsx | 21 +++++++++++++++------
src/store/harvest.atom.ts | 11 ++++-------
2 files changed, 19 insertions(+), 13 deletions(-)
diff --git a/src/components/HarvestTime.tsx b/src/components/HarvestTime.tsx
index b98df005..d17d23db 100644
--- a/src/components/HarvestTime.tsx
+++ b/src/components/HarvestTime.tsx
@@ -1,4 +1,4 @@
-import React, { useEffect, useMemo } from 'react';
+import React, { useMemo } from 'react';
import {
Avatar,
Box,
@@ -14,7 +14,7 @@ import {
WrapItem,
} from '@chakra-ui/react';
import { useAccount } from '@starknet-react/core';
-import { StrategyInfo } from '@/store/strategies.atoms';
+import { StrategyInfo } from '@/store/strategies.atoms';
import { HarvestTimeAtom } from '@/store/harvest.atom';
import { useAtomValue } from 'jotai';
import { formatTimestamp, getUniqueById } from '@/utils';
@@ -51,12 +51,17 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
APY
-
+
{(strategy.netYield * 100).toFixed(2)}%
- {strategy.leverage.toFixed(2)}x boosted
+ {strategy.leverage.toFixed(2)}x boosted
@@ -175,8 +180,12 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
Total rewards harvested:
- {(BigInt(data?.amount.toString() ?? '') / BigInt(10 ** 18)).toString()} |
- Total number of times harvested:{' '}
+
+ {(
+ BigInt(data?.amount.toString() ?? '') / BigInt(10 ** 18)
+ ).toString()}
+ {' '}
+ | Total number of times harvested:{' '}
{harvestTime?.data?.totalHarvests}
diff --git a/src/store/harvest.atom.ts b/src/store/harvest.atom.ts
index 654c565a..5f351766 100644
--- a/src/store/harvest.atom.ts
+++ b/src/store/harvest.atom.ts
@@ -17,8 +17,8 @@ interface QueryResponse {
// GraphQL query
const GET_HARVESTS_QUERY = gql`
query Query(
- $where: HarvestsWhereInput,
- $take: Int,
+ $where: HarvestsWhereInput
+ $take: Int
$orderBy: [HarvestsOrderByWithRelationInput!]
) {
findManyHarvests(where: $where, take: $take, orderBy: $orderBy) {
@@ -27,16 +27,14 @@ const GET_HARVESTS_QUERY = gql`
timestamp
}
totalHarvests
-
}
`;
-
// Function to execute the query
async function getHarvestData(
contract: string,
take: number = 1,
- orderBy: any = [{ timestamp: 'desc' }]
+ orderBy: any = [{ timestamp: 'desc' }],
): Promise {
try {
const { data } = await apolloClient.query({
@@ -60,12 +58,11 @@ async function getHarvestData(
}
}
-
export const HarvestTimeAtom = (contract: string) =>
atomWithQuery((get) => ({
queryKey: ['harvest_data', contract],
queryFn: async () => {
const result = await getHarvestData(contract);
- return result;
+ return result;
},
}));
From c41bba76f3339f0ca880a65e0861e79ff2220c4e Mon Sep 17 00:00:00 2001
From: Iwueseiter <156322726+Iwueseiter@users.noreply.github.com>
Date: Tue, 24 Sep 2024 13:49:09 +0000
Subject: [PATCH 07/11] fix: change query
---
.../[strategyId]/_components/Strategy.tsx | 311 ++++++++++++------
src/components/HarvestTime.tsx | 31 +-
src/store/harvest.atom.ts | 17 +-
src/store/transactions.atom.ts | 161 ++++++++-
src/utils.ts | 69 ++++
yarn.lock | 125 ++++++-
6 files changed, 577 insertions(+), 137 deletions(-)
diff --git a/src/app/strategy/[strategyId]/_components/Strategy.tsx b/src/app/strategy/[strategyId]/_components/Strategy.tsx
index f40c2ed1..2d162211 100755
--- a/src/app/strategy/[strategyId]/_components/Strategy.tsx
+++ b/src/app/strategy/[strategyId]/_components/Strategy.tsx
@@ -22,18 +22,22 @@ import {
import { useAccount } from '@starknet-react/core';
import { atom, useAtomValue, useSetAtom } from 'jotai';
import mixpanel from 'mixpanel-browser';
-import { useEffect, useMemo, useState } from 'react';
+import { useCallback, useEffect, useMemo, useState } from 'react';
import Deposit from '@/components/Deposit';
import { DUMMY_BAL_ATOM } from '@/store/balance.atoms';
import { StrategyInfo, strategiesAtom } from '@/store/strategies.atoms';
-import {
- StrategyTxPropsToMessageWithStrategies,
- transactionsAtom,
-} from '@/store/transactions.atom';
-import { shortAddress } from '@/utils';
+import { transactionsAtom, TxHistoryAtom } from '@/store/transactions.atom';
import { StrategyParams } from '../page';
import HarvestTime from '@/components/HarvestTime';
+import {
+ capitalize,
+ getTokenInfoFromAddr,
+ shortAddress,
+ timeAgo,
+} from '@/utils';
+import MyNumber from '@/utils/MyNumber';
+import { ExternalLinkIcon } from '@chakra-ui/icons';
const Strategy = ({ params }: StrategyParams) => {
const { address } = useAccount();
@@ -41,10 +45,6 @@ const Strategy = ({ params }: StrategyParams) => {
const transactions = useAtomValue(transactionsAtom);
const [isMounted, setIsMounted] = useState(false);
- useEffect(() => {
- console.log('txs', transactions);
- }, [transactions]);
-
const strategy: StrategyInfo | undefined = useMemo(() => {
const id = params.strategyId;
@@ -53,25 +53,92 @@ const Strategy = ({ params }: StrategyParams) => {
return strategies.find((s) => s.id === id);
}, [params.strategyId, strategies]);
+ console.log('strategy', strategy);
+
+ const strategyAddress = useMemo(() => {
+ const holdingTokens = strategy?.holdingTokens;
+ if (holdingTokens && holdingTokens.length) {
+ const holdingTokenInfo: any = holdingTokens[0];
+ return (holdingTokenInfo.address || holdingTokenInfo.token) as string;
+ }
+ return '';
+ }, [strategy]);
+
const setBalQueryEnable = useSetAtom(strategy?.balEnabled || atom(false));
useEffect(() => {
setBalQueryEnable(true);
}, []);
- // const balAtom = getBalanceAtom(strategy?.holdingTokens[0]);
const balData = useAtomValue(strategy?.balanceAtom || DUMMY_BAL_ATOM);
- // cons{ balance, underlyingTokenInfo, isLoading, isError }
- useEffect(() => {
+
+ // fetch tx history
+ const txHistoryAtom = useMemo(
+ () =>
+ TxHistoryAtom(
+ strategyAddress,
+ address!,
+ strategy?.balanceAtom || DUMMY_BAL_ATOM,
+ ),
+ [address, strategyAddress, balData],
+ );
+ const txHistoryResult = useAtomValue(txHistoryAtom);
+ const txHistory = useMemo(() => {
+ if (txHistoryResult.data) {
+ return {
+ findManyInvestment_flows: [
+ ...txHistoryResult.data.findManyInvestment_flows,
+ ].sort((a, b) => {
+ return b.timestamp - a.timestamp;
+ }),
+ };
+ }
console.log(
- 'balData',
- balData.isError,
- balData.isLoading,
- balData.isPending,
- balData.data,
- balData.error,
+ 'TxHistoryAtom',
+ txHistoryResult.error,
+ txHistoryResult.isError,
+ txHistoryResult.isLoading,
);
- }, [balData]);
+ return txHistoryResult.data || { findManyInvestment_flows: [] };
+ }, [txHistoryResult.data]);
+
+ // compute profit
+ // profit doesnt change quickly in real time, but total deposit amount can change
+ // and it can impact the profit calc as txHistory may not be updated at the same time as balData
+ // So, we compute profit once only
+ const [profit, setProfit] = useState(0);
+ const computeProfit = useCallback(() => {
+ if (!txHistory.findManyInvestment_flows.length) return 0;
+ const tokenInfo = getTokenInfoFromAddr(
+ txHistory.findManyInvestment_flows[0].asset,
+ );
+ if (!tokenInfo) return 0;
+ const netDeposits = txHistory.findManyInvestment_flows.reduce((acc, tx) => {
+ const sign = tx.type === 'deposit' ? 1 : -1;
+ return (
+ acc +
+ sign *
+ Number(
+ new MyNumber(tx.amount, tokenInfo.decimals).toEtherToFixedDecimals(
+ 4,
+ ),
+ )
+ );
+ }, 0);
+ const currentValue = Number(
+ balData.data?.amount.toEtherToFixedDecimals(4) || '0',
+ );
+ if (currentValue === 0) return 0;
+
+ if (netDeposits === 0) return 0;
+ setProfit(currentValue - netDeposits);
+ }, [txHistory, balData]);
+
+ useEffect(() => {
+ if (profit == 0) {
+ computeProfit();
+ }
+ }, [txHistory, balData]);
useEffect(() => {
mixpanel.track('Strategy page open', { name: params.strategyId });
@@ -258,94 +325,136 @@ const Strategy = ({ params }: StrategyParams) => {
))}
-
- {/* Risks card */}
-
-
- Risks
-
-
- {strategy.risks.map((r) => (
-
- {r}
-
- ))}
-
-
-
- {/* Transaction history card */}
-
-
- Transaction history
-
-
- {/* If more than 1 filtered tx */}
- {transactions.filter((tx) => tx.info.strategyId == strategy.id)
- .length > 0 && (
- <>
+
+
+ {/* Risks card */}
+
- Note: This feature saves and shows transactions made on this
- device since it was added. Clearing your browser cache will
- remove this data.
+ Risks
+
+
+ {strategy.risks.map((r) => (
+
+ {r}
+
+ ))}
+
+
+
+
+ {/* Transaction history card */}
+
+
+ Transaction history
+
+
+ There may be delays fetching data. If your transaction{' '}
+ {`isn't`} found, try again later.
- {transactions
- .filter((tx) => tx.info.strategyId == strategy.id)
- .map((tx, index) => {
- return (
-
-
- {/* The default msg contains strategy name, since this for a specific strategy, replace it */}
- {index + 1}){' '}
- {StrategyTxPropsToMessageWithStrategies(
- tx.info,
- strategies,
- ).replace(` in ${strategy.name}`, '')}
-
-
- {/* The default msg contains strategy name, since this for a specific strategy, replace it */}
- Transacted on {tx.createdAt.toLocaleDateString()} [
-
+ {txHistory.findManyInvestment_flows.map((tx, index) => {
+ const token = getTokenInfoFromAddr(tx.asset);
+ const decimals = token?.decimals;
+
+ return (
+
+
- {shortAddress(tx.txHash)}
-
- ]
-
-
- );
- })}
- >
- )}
+ {index + 1}.
+
+
+ {Number(
+ new MyNumber(
+ tx.amount,
+ decimals!,
+ ).toEtherToFixedDecimals(
+ token.displayDecimals,
+ ),
+ ).toLocaleString()}{' '}
+ {token?.name}
+
+
+ {capitalize(tx.type)}
+
+
- {/* If no filtered tx */}
- {transactions.filter((tx) => tx.info.strategyId == strategy.id)
- .length == 0 && (
-
- No transactions recorded since this feature was added. We use
- your {"browser's"} storage to save your transaction history.
- Make a deposit or withdrawal to see your transactions here.
- Clearning browser cache will remove this data.
-
- )}
-
+
+
+
+ {shortAddress(tx.txHash)}
+
+
+
+ {/* The default msg contains strategy name, since this for a specific strategy, replace it */}
+ {timeAgo(new Date(tx.timestamp * 1000))}
+
+
+
+
+ );
+ })}
+ >
+ )}
+
+ {/* If no filtered tx */}
+ {txHistory.findManyInvestment_flows.length === 0 && (
+
+ No transactions found
+
+ )}
+
+
+
)}
>
diff --git a/src/components/HarvestTime.tsx b/src/components/HarvestTime.tsx
index d17d23db..e7358600 100644
--- a/src/components/HarvestTime.tsx
+++ b/src/components/HarvestTime.tsx
@@ -153,40 +153,17 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
{harvestTimestamp?.minute ?? 0}
-
- {/*
-
- Secs
-
-
- {harvestTimestamp?.second}
-
- */}
- Total rewards harvested:
+ Total rewards harvested:{' '}
+ {harvestTime?.data?.totalStrkHarvestedByContract.STRKAmount}
-
- {(
- BigInt(data?.amount.toString() ?? '') / BigInt(10 ** 18)
- ).toString()}
- {' '}
- | Total number of times harvested:{' '}
- {harvestTime?.data?.totalHarvests}
+ {} | Total number of times harvested:{' '}
+ {harvestTime?.data?.totalHarvestsByContract}
diff --git a/src/store/harvest.atom.ts b/src/store/harvest.atom.ts
index 5f351766..60b89c7d 100644
--- a/src/store/harvest.atom.ts
+++ b/src/store/harvest.atom.ts
@@ -10,8 +10,12 @@ interface HarvestTime {
interface QueryResponse {
findManyHarvests: HarvestTime[];
- totalHarvests: number;
- totalStrkHarvested: number;
+ totalHarvestsByContract: number;
+ totalStrkHarvestedByContract: {
+ STRKAmount: number;
+ USDValue: number;
+ rawSTRKAmount: string;
+ };
}
// GraphQL query
@@ -20,13 +24,19 @@ const GET_HARVESTS_QUERY = gql`
$where: HarvestsWhereInput
$take: Int
$orderBy: [HarvestsOrderByWithRelationInput!]
+ $contract: String!
) {
findManyHarvests(where: $where, take: $take, orderBy: $orderBy) {
contract
amount
timestamp
}
- totalHarvests
+ totalHarvestsByContract(contract: $contract)
+ totalStrkHarvestedByContract(contract: $contract) {
+ STRKAmount
+ USDValue
+ rawSTRKAmount
+ }
}
`;
@@ -47,6 +57,7 @@ async function getHarvestData(
},
take,
orderBy,
+ contract,
},
});
diff --git a/src/store/transactions.atom.ts b/src/store/transactions.atom.ts
index b13fda53..78a11a63 100755
--- a/src/store/transactions.atom.ts
+++ b/src/store/transactions.atom.ts
@@ -4,11 +4,15 @@
import { TOKENS } from '@/constants';
import { capitalize, standariseAddress } from '@/utils';
import MyNumber from '@/utils/MyNumber';
-import { Getter, Setter, atom } from 'jotai';
+import { Atom, Getter, Setter, atom } from 'jotai';
import toast from 'react-hot-toast';
import { RpcProvider, TransactionExecutionStatus } from 'starknet';
import { StrategyInfo, strategiesAtom } from './strategies.atoms';
import { createAtomWithStorage } from './utils.atoms';
+import { atomWithQuery, AtomWithQueryResult } from 'jotai-tanstack-query';
+import { gql } from '@apollo/client';
+import apolloClient from '@/utils/apolloClient';
+import { BalanceResult } from './balance.atoms';
export interface StrategyTxProps {
strategyId: string;
@@ -25,6 +29,105 @@ export interface TransactionInfo {
createdAt: Date;
}
+export interface TxHistory {
+ findManyInvestment_flows: {
+ amount: string;
+ timestamp: number;
+ type: string;
+ txHash: string;
+ asset: string;
+ __typename: 'Investment_flows';
+ }[];
+}
+
+async function getTxHistory(
+ contract: string,
+ owner: string,
+): Promise {
+ try {
+ const contractAddrFormatted = standariseAddress(contract);
+ const ownerAddrFormatted = standariseAddress(owner);
+ const { data } = await apolloClient.query({
+ query: gql`
+ query Query($where: Investment_flowsWhereInput) {
+ findManyInvestment_flows(where: $where) {
+ amount
+ timestamp
+ type
+ txHash
+ asset
+ }
+ }
+ `,
+ variables: {
+ where: {
+ contract: {
+ equals: contractAddrFormatted,
+ },
+ owner: {
+ equals: ownerAddrFormatted,
+ },
+ },
+ },
+ // fetchPolicy: 'network-only'
+ });
+
+ return data;
+ } catch (error) {
+ console.error('GraphQL Error:', error);
+ throw error;
+ }
+}
+
+export const newTxsAtom = atom([]);
+
+export const TxHistoryAtom = (
+ contract: string,
+ owner: string,
+ balData: Atom>,
+) =>
+ atomWithQuery((get) => ({
+ // balData just to trigger a refetch
+ queryKey: ['tx_history', { contract, owner }, get(balData)],
+ queryFn: async ({ queryKey }: any): Promise => {
+ const [, { contract, owner }] = queryKey;
+ const res = await getTxHistory(contract, owner);
+
+ console.log('TxHistoryAtom res', res);
+ // add new txs from local cache
+ const newTxs = get(newTxsAtom);
+ console.log('TxHistoryAtom newTxs', newTxs);
+ const allTxs = res.findManyInvestment_flows.concat(
+ newTxs.map((tx) => {
+ return {
+ amount: tx.info.amount.toString(),
+ timestamp: Math.round(tx.createdAt.getTime() / 1000),
+ type: tx.info.actionType,
+ txHash: tx.txHash,
+ asset: tx.info.tokenAddr,
+ __typename: 'Investment_flows',
+ };
+ }),
+ );
+
+ console.log('TxHistoryAtom', allTxs);
+ // remove any duplicate txs by txHash
+ const txMap: any = {}; // txHash: boolean
+ const txHashes = allTxs.filter((txInfo) => {
+ if (txMap[txInfo.txHash]) {
+ return false;
+ }
+ txMap[txInfo.txHash] = true;
+ return true;
+ });
+
+ console.log('TxHistoryAtom txHashes', txHashes);
+ return {
+ findManyInvestment_flows: txHashes,
+ };
+ },
+ }));
+
// in local storage, objects like Date, MyNumber are stored as strings
// this function deserialises them back to their original types
// declare let localStorage: any;
@@ -76,14 +179,64 @@ async function waitForTransaction(
nodeUrl: process.env.NEXT_PUBLIC_RPC_URL,
});
console.log('waitForTransaction', tx);
- await provider.waitForTransaction(tx.txHash, {
- successStates: [TransactionExecutionStatus.SUCCEEDED],
- });
+ await isTxAccepted(tx.txHash);
console.log('waitForTransaction done', tx);
const txs = await get(transactionsAtom);
tx.status = 'success';
txs.push(tx);
set(transactionsAtom, txs);
+
+ let newTxs = get(newTxsAtom);
+ const txExists = newTxs.find(
+ (t) => t.txHash.toLowerCase() === tx.txHash.toLowerCase(),
+ );
+ if (!txExists) {
+ newTxs = [...newTxs, tx];
+ set(newTxsAtom, newTxs);
+ }
+}
+
+// Somehow waitForTransaction is giving delayed confirmation
+// even with 5s retry interval. So, using this function instead
+async function isTxAccepted(txHash: string) {
+ const provider = new RpcProvider({
+ nodeUrl: process.env.NEXT_PUBLIC_RPC_URL,
+ });
+ let keepChecking = true;
+ const maxRetries = 30;
+ let retry = 0;
+ while (keepChecking) {
+ let txInfo: any;
+ try {
+ txInfo = await provider.getTransactionStatus(txHash);
+ } catch (error) {
+ console.error('isTxAccepted error', error);
+ retry++;
+ if (retry > maxRetries) {
+ throw new Error('Transaction status unknown');
+ }
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ continue;
+ }
+
+ console.debug('isTxAccepted', txInfo);
+ if (!txInfo.finality_status || txInfo.finality_status == 'RECEIVED') {
+ // do nothing
+ await new Promise((resolve) => setTimeout(resolve, 2000));
+ continue;
+ }
+ if (txInfo.finality_status == 'ACCEPTED_ON_L2') {
+ if (txInfo.execution_status === TransactionExecutionStatus.SUCCEEDED) {
+ keepChecking = false;
+ return true;
+ }
+ throw new Error('Transaction reverted');
+ } else if (txInfo.finality_status == 'REJECTED') {
+ throw new Error('Transaction rejected');
+ } else {
+ throw new Error('Transaction status unknown');
+ }
+ }
}
async function initToast(tx: TransactionInfo, get: Getter, set: Setter) {
diff --git a/src/utils.ts b/src/utils.ts
index 6f121851..85c71029 100755
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -1,6 +1,9 @@
import { MenuItemProps, MenuListProps } from '@chakra-ui/react';
import { num } from 'starknet';
import { TOKENS } from './constants';
+import toast from 'react-hot-toast';
+import { TokenInfo } from './strategies/IStrategy';
+import axios from 'axios';
export function getUniqueStrings(arr: Array) {
const _arr: string[] = [];
@@ -92,6 +95,9 @@ export function generateReferralCode() {
}
export function getReferralUrl(referralCode: string) {
+ if (window.location.origin.includes('app.strkfarm.xyz')) {
+ return `https://strkfarm.xyz/r/${referralCode}`;
+ }
return `${window.location.origin}/r/${referralCode}`;
}
@@ -112,3 +118,66 @@ export function formatTimestamp(timestamp: string) {
second: date.getUTCSeconds(),
};
}
+
+export function copyReferralLink(refCode: string) {
+ navigator.clipboard.writeText(getReferralUrl(refCode));
+
+ toast.success('Referral link copied to clipboard', {
+ position: 'bottom-right',
+ });
+}
+
+export async function getPrice(tokenInfo: TokenInfo) {
+ try {
+ return await getPriceFromMyAPI(tokenInfo);
+ } catch (e) {
+ console.error('getPriceFromMyAPI error', e);
+ }
+ console.log('getPrice coinbase', tokenInfo.name);
+ const priceInfo = await axios.get(
+ `https://api.coinbase.com/v2/prices/${tokenInfo.name}-USDT/spot`,
+ );
+ const price = Number(priceInfo.data.data.amount);
+ return price;
+}
+
+export async function getPriceFromMyAPI(tokenInfo: TokenInfo) {
+ console.log('getPrice from redis', tokenInfo.name);
+
+ const endpoint =
+ typeof window === 'undefined'
+ ? process.env.HOSTNAME
+ : window.location.origin;
+ const priceInfo = await axios.get(`${endpoint}/api/price/${tokenInfo.name}`);
+ const now = new Date();
+ const priceTime = new Date(priceInfo.data.timestamp);
+ if (now.getTime() - priceTime.getTime() > 900000) {
+ // 15 mins
+ throw new Error('Price is stale');
+ }
+ const price = Number(priceInfo.data.price);
+ return price;
+}
+
+export function timeAgo(date: Date): string {
+ const now = new Date();
+ const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);
+ const minutes = Math.floor(seconds / 60);
+ const hours = Math.floor(minutes / 60);
+ const days = Math.floor(hours / 24);
+ const weeks = Math.floor(days / 7);
+ const months = Math.floor(days / 30);
+
+ if (hours < 1) return `${minutes}min ago`;
+ if (hours < 24) return `${hours}h ago`;
+ if (days < 7) return `${days}d ago`;
+ if (weeks < 4) return `${weeks}w ago`;
+ if (months < 3) return `${months}mon ago`;
+
+ // If more than 3 months, return in DD MMM, YY format
+ return date.toLocaleDateString('en-US', {
+ day: '2-digit',
+ month: 'short',
+ year: '2-digit',
+ });
+}
diff --git a/yarn.lock b/yarn.lock
index 9470188d..472108b8 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15,6 +15,26 @@
framer-motion "^6.3.11"
lodash.union "^4.6.0"
+"@apollo/client@^3.11.8":
+ version "3.11.8"
+ resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.11.8.tgz#f6bacdc7e1b243807c1387113e1d445a53471a9c"
+ integrity sha512-CgG1wbtMjsV2pRGe/eYITmV5B8lXUCYljB2gB/6jWTFQcrvirUVvKg7qtFdjYkQSFbIffU1IDyxgeaN81eTjbA==
+ dependencies:
+ "@graphql-typed-document-node/core" "^3.1.1"
+ "@wry/caches" "^1.0.0"
+ "@wry/equality" "^0.5.6"
+ "@wry/trie" "^0.5.0"
+ graphql-tag "^2.12.6"
+ hoist-non-react-statics "^3.3.2"
+ optimism "^0.18.0"
+ prop-types "^15.7.2"
+ rehackt "^0.1.0"
+ response-iterator "^0.2.6"
+ symbol-observable "^4.0.0"
+ ts-invariant "^0.10.3"
+ tslib "^2.3.0"
+ zen-observable-ts "^1.2.5"
+
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.21.4", "@babel/code-frame@^7.24.7":
version "7.24.7"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.24.7.tgz#882fd9e09e8ee324e496bd040401c6f046ef4465"
@@ -1128,6 +1148,11 @@
resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.7.tgz#d0ece53ce99ab5a8e37ebdfe5e32452a2bfc073e"
integrity sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==
+"@graphql-typed-document-node/core@^3.1.1":
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.2.0.tgz#5f3d96ec6b2354ad6d8a28bf216a1d97b5426861"
+ integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
+
"@humanwhocodes/config-array@^0.11.14":
version "0.11.14"
resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.14.tgz#d78e481a039f7566ecc9660b4ea7fe6b1fec442b"
@@ -2263,6 +2288,41 @@
"@walletconnect/window-getters" "^1.0.1"
tslib "1.14.1"
+"@wry/caches@^1.0.0":
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/@wry/caches/-/caches-1.0.1.tgz#8641fd3b6e09230b86ce8b93558d44cf1ece7e52"
+ integrity sha512-bXuaUNLVVkD20wcGBWRyo7j9N3TxePEWFZj2Y+r9OoUzfqmavM84+mFykRicNsBqatba5JLay1t48wxaXaWnlA==
+ dependencies:
+ tslib "^2.3.0"
+
+"@wry/context@^0.7.0":
+ version "0.7.4"
+ resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.7.4.tgz#e32d750fa075955c4ab2cfb8c48095e1d42d5990"
+ integrity sha512-jmT7Sb4ZQWI5iyu3lobQxICu2nC/vbUhP0vIdd6tHC9PTfenmRmuIFqktc6GH9cgi+ZHnsLWPvfSvc4DrYmKiQ==
+ dependencies:
+ tslib "^2.3.0"
+
+"@wry/equality@^0.5.6":
+ version "0.5.7"
+ resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.7.tgz#72ec1a73760943d439d56b7b1e9985aec5d497bb"
+ integrity sha512-BRFORjsTuQv5gxcXsuDXx6oGRhuVsEGwZy6LOzRRfgu+eSfxbhUQ9L9YtSEIuIjY/o7g3iWFjrc5eSY1GXP2Dw==
+ dependencies:
+ tslib "^2.3.0"
+
+"@wry/trie@^0.4.3":
+ version "0.4.3"
+ resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.4.3.tgz#077d52c22365871bf3ffcbab8e95cb8bc5689af4"
+ integrity sha512-I6bHwH0fSf6RqQcnnXLJKhkSXG45MFral3GxPaY4uAl0LYDZM+YDVDAiU9bYwjTuysy1S0IeecWtmq1SZA3M1w==
+ dependencies:
+ tslib "^2.3.0"
+
+"@wry/trie@^0.5.0":
+ version "0.5.0"
+ resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.5.0.tgz#11e783f3a53f6e4cd1d42d2d1323f5bc3fa99c94"
+ integrity sha512-FNoYzHawTMk/6KMQoEG5O4PuioX19UbwdQKF44yw0nLfOypfQdjtfZzo/UIJWAJ23sNIFbD1Ug9lbaDGMwbqQA==
+ dependencies:
+ tslib "^2.3.0"
+
"@zag-js/dom-query@0.16.0":
version "0.16.0"
resolved "https://registry.yarnpkg.com/@zag-js/dom-query/-/dom-query-0.16.0.tgz#bca46bcd78f78c900064478646d95f9781ed098e"
@@ -4089,6 +4149,18 @@ graphemer@^1.4.0:
resolved "https://registry.yarnpkg.com/graphemer/-/graphemer-1.4.0.tgz#fb2f1d55e0e3a1849aeffc90c4fa0dd53a0e66c6"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
+graphql-tag@^2.12.6:
+ version "2.12.6"
+ resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1"
+ integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==
+ dependencies:
+ tslib "^2.1.0"
+
+graphql@^16.9.0:
+ version "16.9.0"
+ resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f"
+ integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==
+
h3@^1.10.2, h3@^1.11.1:
version "1.12.0"
resolved "https://registry.yarnpkg.com/h3/-/h3-1.12.0.tgz#9d7f05f08a997d263e484b02436cb027df3026d8"
@@ -4185,7 +4257,7 @@ hmac-drbg@^1.0.1:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.1"
-hoist-non-react-statics@^3.3.1:
+hoist-non-react-statics@^3.3.1, hoist-non-react-statics@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
@@ -5152,6 +5224,16 @@ onetime@^6.0.0:
dependencies:
mimic-fn "^4.0.0"
+optimism@^0.18.0:
+ version "0.18.0"
+ resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.18.0.tgz#e7bb38b24715f3fdad8a9a7fc18e999144bbfa63"
+ integrity sha512-tGn8+REwLRNFnb9WmcY5IfpOqeX2kpaYJ1s6Ae3mn12AeydLkR3j+jSCmVQFoXqU8D41PAJ1RG1rCRNWmNZVmQ==
+ dependencies:
+ "@wry/caches" "^1.0.0"
+ "@wry/context" "^0.7.0"
+ "@wry/trie" "^0.4.3"
+ tslib "^2.3.0"
+
optionator@^0.9.3:
version "0.9.4"
resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734"
@@ -5436,7 +5518,7 @@ process-warning@^1.0.0:
resolved "https://registry.yarnpkg.com/process-warning/-/process-warning-1.0.0.tgz#980a0b25dc38cd6034181be4b7726d89066b4616"
integrity sha512-du4wfLyj4yCZq1VupnVSZmRsPJsNuxoDQFdCFHLaYiEbFBD7QE0a+I4D7hOxrVnh78QE/YipFAj9lXHiXocV+Q==
-prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1:
+prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
@@ -5732,6 +5814,11 @@ regexp.prototype.flags@^1.5.1, regexp.prototype.flags@^1.5.2:
es-errors "^1.3.0"
set-function-name "^2.0.1"
+rehackt@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/rehackt/-/rehackt-0.1.0.tgz#a7c5e289c87345f70da8728a7eb878e5d03c696b"
+ integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==
+
require-directory@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
@@ -5770,6 +5857,11 @@ resolve@^2.0.0-next.5:
path-parse "^1.0.7"
supports-preserve-symlinks-flag "^1.0.0"
+response-iterator@^0.2.6:
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/response-iterator/-/response-iterator-0.2.6.tgz#249005fb14d2e4eeb478a3f735a28fd8b4c9f3da"
+ integrity sha512-pVzEEzrsg23Sh053rmDUvLSkGXluZio0qu8VT6ukrYuvtjVfCbDZH9d6PGXb8HZfzdNZt8feXv/jvUzlhRgLnw==
+
reusify@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
@@ -6252,6 +6344,11 @@ swr@2.2.5:
client-only "^0.0.1"
use-sync-external-store "^1.2.0"
+symbol-observable@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
+ integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==
+
system-architecture@^0.1.0:
version "0.1.0"
resolved "https://registry.yarnpkg.com/system-architecture/-/system-architecture-0.1.0.tgz#71012b3ac141427d97c67c56bc7921af6bff122d"
@@ -6385,6 +6482,13 @@ ts-interface-checker@^0.1.9:
resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
+ts-invariant@^0.10.3:
+ version "0.10.3"
+ resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.10.3.tgz#3e048ff96e91459ffca01304dbc7f61c1f642f6c"
+ integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==
+ dependencies:
+ tslib "^2.1.0"
+
ts-mixer@^6.0.3:
version "6.0.4"
resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.4.tgz#1da39ceabc09d947a82140d9f09db0f84919ca28"
@@ -6415,6 +6519,11 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.1, tslib@^2.4.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.3.tgz#0438f810ad7a9edcde7a241c3d80db693c8cbfe0"
integrity sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==
+tslib@^2.3.0:
+ version "2.7.0"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
+ integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
+
type-check@^0.4.0, type-check@~0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
@@ -6822,6 +6931,18 @@ yocto-queue@^1.0.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.1.1.tgz#fef65ce3ac9f8a32ceac5a634f74e17e5b232110"
integrity sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==
+zen-observable-ts@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58"
+ integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==
+ dependencies:
+ zen-observable "0.8.15"
+
+zen-observable@0.8.15:
+ version "0.8.15"
+ resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
+ integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
+
zod@^3.22.2:
version "3.23.8"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.23.8.tgz#e37b957b5d52079769fb8097099b592f0ef4067d"
From c0fd32898b22a06a23dc31ae910199a8c0203b1b Mon Sep 17 00:00:00 2001
From: Akira <156126180+akiraonstarknet@users.noreply.github.com>
Date: Tue, 24 Sep 2024 11:31:01 +0530
Subject: [PATCH 08/11] Add tx history (#78)
* Add GraphQL tx history (#77)
* feat: use STRKFarm Graphql API to show tx history
* add console.log statements for debugging
* modify graphql query
* fix: fix apollo client
* refac: modify graphgl query
* feat: implement function to get tx history using graphql
* feat: show tx history in Strategy page
* fix: fix linting issue
* feat: implement function to get strategy address
---------
Co-authored-by: Mystic <149405096+Gift-Naomi@users.noreply.github.com>
Co-authored-by: Gift-Naomi
* fix tx toast update delay, add earnings info, cleaned tx component
---------
Co-authored-by: Mystic <149405096+Gift-Naomi@users.noreply.github.com>
Co-authored-by: Gift-Naomi
---
.../[strategyId]/_components/Strategy.tsx | 1 +
yarn.lock | 17 +++++++++--------
2 files changed, 10 insertions(+), 8 deletions(-)
diff --git a/src/app/strategy/[strategyId]/_components/Strategy.tsx b/src/app/strategy/[strategyId]/_components/Strategy.tsx
index 7de0ce10..b58b7b18 100755
--- a/src/app/strategy/[strategyId]/_components/Strategy.tsx
+++ b/src/app/strategy/[strategyId]/_components/Strategy.tsx
@@ -17,6 +17,7 @@ import {
TabPanels,
Tabs,
Text,
+ Tooltip,
VStack,
} from '@chakra-ui/react';
import { useAccount } from '@starknet-react/core';
diff --git a/yarn.lock b/yarn.lock
index e9085882..8d96df68 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -15,11 +15,7 @@
framer-motion "^6.3.11"
lodash.union "^4.6.0"
-<<<<<<< HEAD
-"@apollo/client@^3.11.8":
-=======
"@apollo/client@3.11.8":
->>>>>>> e9f234a0c9a1a6d378aa3ac9ba7310f3486707ef
version "3.11.8"
resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.11.8.tgz#f6bacdc7e1b243807c1387113e1d445a53471a9c"
integrity sha512-CgG1wbtMjsV2pRGe/eYITmV5B8lXUCYljB2gB/6jWTFQcrvirUVvKg7qtFdjYkQSFbIffU1IDyxgeaN81eTjbA==
@@ -4656,11 +4652,7 @@ graphql-tag@^2.12.6:
dependencies:
tslib "^2.1.0"
-<<<<<<< HEAD
-graphql@^16.9.0:
-=======
graphql@16.9.0:
->>>>>>> e9f234a0c9a1a6d378aa3ac9ba7310f3486707ef
version "16.9.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.9.0.tgz#1c310e63f16a49ce1fbb230bd0a000e99f6f115f"
integrity sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==
@@ -6503,7 +6495,10 @@ rehackt@^0.1.0:
integrity sha512-7kRDOuLHB87D/JESKxQoRwv4DzbIdwkAGQ7p6QKGdVlY1IZheUnVhlk/4UZlNUVxdAXpyxikE3URsG067ybVzw==
<<<<<<< HEAD
+<<<<<<< HEAD
+=======
=======
+>>>>>>> fb93dff (Add tx history (#78))
request-promise-core@1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.3.tgz#e9a3c081b51380dfea677336061fea879a829ee9"
@@ -7332,8 +7327,11 @@ tslib@^2.3.0:
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.7.0.tgz#d9b40c5c40ab59e8738f297df3087bf1a2690c01"
integrity sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==
+<<<<<<< HEAD
<<<<<<< HEAD
=======
+=======
+>>>>>>> fb93dff (Add tx history (#78))
tunnel-agent@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd"
@@ -7820,7 +7818,10 @@ yoctocolors-cjs@^2.1.2:
resolved "https://registry.yarnpkg.com/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz#f4b905a840a37506813a7acaa28febe97767a242"
integrity sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==
+<<<<<<< HEAD
>>>>>>> e9f234a0c9a1a6d378aa3ac9ba7310f3486707ef
+=======
+>>>>>>> fb93dff (Add tx history (#78))
zen-observable-ts@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz#6c6d9ea3d3a842812c6e9519209365a122ba8b58"
From 75355dfdf35fd4b5d8c2523632b55b8f73c755cf Mon Sep 17 00:00:00 2001
From: akiraonstarknet
Date: Thu, 26 Sep 2024 13:36:28 +0530
Subject: [PATCH 09/11] add cache to /stats
---
src/app/api/stats/route.ts | 10 ++++++++--
src/app/strategy/[strategyId]/_components/Strategy.tsx | 1 -
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/src/app/api/stats/route.ts b/src/app/api/stats/route.ts
index ee639367..ee17e3da 100755
--- a/src/app/api/stats/route.ts
+++ b/src/app/api/stats/route.ts
@@ -1,7 +1,7 @@
import { getStrategies } from '@/store/strategies.atoms';
import { NextResponse } from 'next/server';
-export const revalidate = 60;
+export const revalidate = 1800;
export async function GET(_req: Request) {
const strategies = getStrategies();
@@ -28,7 +28,13 @@ export async function GET(_req: Request) {
const result = await Promise.all(values);
- return NextResponse.json({
+ const response = NextResponse.json({
tvl: result.reduce((a, b) => a + b, 0),
});
+
+ response.headers.set(
+ 'Cache-Control',
+ 's-maxage=1800, stale-while-revalidate=120',
+ );
+ return response;
}
diff --git a/src/app/strategy/[strategyId]/_components/Strategy.tsx b/src/app/strategy/[strategyId]/_components/Strategy.tsx
index b58b7b18..7de0ce10 100755
--- a/src/app/strategy/[strategyId]/_components/Strategy.tsx
+++ b/src/app/strategy/[strategyId]/_components/Strategy.tsx
@@ -17,7 +17,6 @@ import {
TabPanels,
Tabs,
Text,
- Tooltip,
VStack,
} from '@chakra-ui/react';
import { useAccount } from '@starknet-react/core';
From 43ee68bea723efb792653d7fcbef57f914ebab0c Mon Sep 17 00:00:00 2001
From: Akira <156126180+akiraonstarknet@users.noreply.github.com>
Date: Fri, 27 Sep 2024 22:45:24 +0530
Subject: [PATCH 10/11] Harvest timer (#147)
* feat: harvest time ui
* feat: display API data
* fix: imports
* fix: lint
* fix: remove mock contract
* fix: remove mock contract
* fix: change query
* fix harvest timer: standarise address
* fix harvest timer bugs
* fix harvest timer bugs [2]
* fix harvest timer bugs [3]
---------
Co-authored-by: Iwueseiter <156322726+Iwueseiter@users.noreply.github.com>
---
.../[strategyId]/_components/Strategy.tsx | 37 +--
src/components/HarvestTime.tsx | 224 ++++++++++++++++++
src/store/harvest.atom.ts | 80 +++++++
src/utils.ts | 33 ++-
4 files changed, 344 insertions(+), 30 deletions(-)
create mode 100644 src/components/HarvestTime.tsx
create mode 100644 src/store/harvest.atom.ts
diff --git a/src/app/strategy/[strategyId]/_components/Strategy.tsx b/src/app/strategy/[strategyId]/_components/Strategy.tsx
index 521ef738..d9866875 100755
--- a/src/app/strategy/[strategyId]/_components/Strategy.tsx
+++ b/src/app/strategy/[strategyId]/_components/Strategy.tsx
@@ -12,10 +12,6 @@ import {
ListItem,
OrderedList,
Spinner,
- Stat,
- StatHelpText,
- StatLabel,
- StatNumber,
Tab,
TabIndicator,
TabList,
@@ -32,13 +28,12 @@ import { useAccount } from '@starknet-react/core';
import { atom, useAtomValue, useSetAtom } from 'jotai';
import mixpanel from 'mixpanel-browser';
import { useCallback, useEffect, useMemo, useState } from 'react';
-import { isMobile } from 'react-device-detect';
import Deposit from '@/components/Deposit';
-import CONSTANTS from '@/constants';
import { DUMMY_BAL_ATOM } from '@/store/balance.atoms';
import { StrategyInfo, strategiesAtom } from '@/store/strategies.atoms';
import { transactionsAtom, TxHistoryAtom } from '@/store/transactions.atom';
+import HarvestTime from '@/components/HarvestTime';
import {
capitalize,
getTokenInfoFromAddr,
@@ -49,6 +44,8 @@ import {
import { StrategyParams } from '../page';
import MyNumber from '@/utils/MyNumber';
import { ExternalLinkIcon } from '@chakra-ui/icons';
+import { isMobile } from 'react-device-detect';
+import CONSTANTS from '@/constants';
const Strategy = ({ params }: StrategyParams) => {
const { address } = useAccount();
@@ -177,13 +174,15 @@ const Strategy = ({ params }: StrategyParams) => {
{strategy ? strategy.name : 'Strategy Not found'}
+
{strategy && (
-
-
+
+
+
{
))}
-
-
-
- APY
-
-
- {(strategy.netYield * 100).toFixed(2)}%
-
-
- {strategy.leverage.toFixed(2)}x boosted
-
-
-
{
+
@@ -370,6 +350,7 @@ const Strategy = ({ params }: StrategyParams) => {
+
Behind the scenes
diff --git a/src/components/HarvestTime.tsx b/src/components/HarvestTime.tsx
new file mode 100644
index 00000000..63adf6c3
--- /dev/null
+++ b/src/components/HarvestTime.tsx
@@ -0,0 +1,224 @@
+import React, { useMemo } from 'react';
+import {
+ Box,
+ Flex,
+ Stat,
+ StatLabel,
+ StatNumber,
+ Tag,
+ Text,
+ Tooltip,
+} from '@chakra-ui/react';
+import { useAccount } from '@starknet-react/core';
+import { StrategyInfo } from '@/store/strategies.atoms';
+import { HarvestTimeAtom } from '@/store/harvest.atom';
+import { useAtomValue } from 'jotai';
+import { formatTimediff, getDisplayCurrencyAmount, timeAgo } from '@/utils';
+import { isMobile } from 'react-device-detect';
+
+interface HarvestTimeProps {
+ strategy: StrategyInfo;
+ balData: any;
+}
+
+const HarvestTime: React.FC = ({ strategy, balData }) => {
+ const { address } = useAccount();
+ const holdingToken: any = strategy.holdingTokens[0];
+ const contractAddress = holdingToken.address || holdingToken.token || '';
+
+ const harvestTimeAtom = useMemo(
+ () => HarvestTimeAtom(contractAddress),
+ [address],
+ );
+
+ const harvestTime = useAtomValue(harvestTimeAtom);
+
+ const data = harvestTime.data?.findManyHarvests[0];
+
+ const lastHarvest = useMemo(() => {
+ if (!data || !data.timestamp) return null;
+ return new Date(Number(data.timestamp) * 1000);
+ }, [data?.timestamp]);
+
+ const harvestTimestamp = useMemo(() => {
+ const DAYMS = 86400 * 1000;
+ // Base date is last harvest time + 2 days or now (for no harvest strats)
+ const baseDate = lastHarvest
+ ? new Date(lastHarvest.getTime() + 2 * DAYMS)
+ : new Date();
+
+ // With base date, get next sunday 12am UTC
+ // set date to coming sunday in UTC
+ const nextHarvest = baseDate;
+ nextHarvest.setUTCDate(
+ nextHarvest.getUTCDate() + (7 - nextHarvest.getUTCDay()),
+ );
+ nextHarvest.setUTCHours(0);
+ nextHarvest.setUTCMinutes(0);
+ nextHarvest.setUTCSeconds(0);
+
+ // if nextHarvest is within 24hrs of last harvest,
+ // increase it by 7 days
+ // This is needed as harvest can happen anytime near deadline
+ if (
+ lastHarvest &&
+ nextHarvest.getTime() - lastHarvest.getTime() < 86400 * 1000
+ ) {
+ nextHarvest.setUTCDate(nextHarvest.getUTCDate() + 7);
+ }
+
+ return formatTimediff(nextHarvest);
+ }, [data?.timestamp]);
+
+ return (
+
+
+
+
+ APY
+
+ {(strategy.netYield * 100).toFixed(2)}%
+
+
+
+
+
+ 🔥{strategy.leverage.toFixed(2)}x boosted
+
+
+
+
+
+ {!isMobile && (
+
+
+
+ Next Harvest in:{' '}
+ {harvestTimestamp.isZero && (
+
+ Anytime now
+
+ )}
+
+
+
+
+ Days
+
+
+ {harvestTimestamp.days ?? 0}
+
+
+
+
+
+ Hour
+
+
+ {harvestTimestamp.hours ?? 0}
+
+
+
+
+
+ Mins
+
+
+ {harvestTimestamp.minutes ?? 0}
+
+
+
+
+
+ )}
+
+
+
+
+ Harvested{' '}
+
+ {getDisplayCurrencyAmount(
+ harvestTime?.data?.totalStrkHarvestedByContract.STRKAmount || 0,
+ 2,
+ )}{' '}
+ STRK
+ {' '}
+ over {harvestTime?.data?.totalHarvestsByContract} claims.{' '}
+ {lastHarvest && (
+
+ Last harvested {timeAgo(lastHarvest)}.
+
+ )}
+
+
+
+ );
+};
+
+export default HarvestTime;
diff --git a/src/store/harvest.atom.ts b/src/store/harvest.atom.ts
new file mode 100644
index 00000000..73413d07
--- /dev/null
+++ b/src/store/harvest.atom.ts
@@ -0,0 +1,80 @@
+import { gql } from '@apollo/client';
+import apolloClient from '@/utils/apolloClient';
+import { atomWithQuery } from 'jotai-tanstack-query';
+import { standariseAddress } from '@/utils';
+
+interface HarvestTime {
+ contract: string;
+ amount: string;
+ timestamp: string;
+}
+
+interface QueryResponse {
+ findManyHarvests: HarvestTime[];
+ totalHarvestsByContract: number;
+ totalStrkHarvestedByContract: {
+ STRKAmount: number;
+ USDValue: number;
+ rawSTRKAmount: string;
+ };
+}
+
+// GraphQL query
+const GET_HARVESTS_QUERY = gql`
+ query Query(
+ $where: HarvestsWhereInput
+ $take: Int
+ $orderBy: [HarvestsOrderByWithRelationInput!]
+ $contract: String!
+ ) {
+ findManyHarvests(where: $where, take: $take, orderBy: $orderBy) {
+ contract
+ amount
+ timestamp
+ }
+ totalHarvestsByContract(contract: $contract)
+ totalStrkHarvestedByContract(contract: $contract) {
+ STRKAmount
+ USDValue
+ rawSTRKAmount
+ }
+ }
+`;
+
+// Function to execute the query
+async function getHarvestData(
+ contract: string,
+ take: number = 1,
+ orderBy: any = [{ timestamp: 'desc' }],
+): Promise {
+ try {
+ const { data } = await apolloClient.query({
+ query: GET_HARVESTS_QUERY,
+ variables: {
+ where: {
+ contract: {
+ equals: standariseAddress(contract),
+ },
+ },
+ take,
+ orderBy,
+ contract: standariseAddress(contract),
+ },
+ });
+
+ // Return the data
+ return data;
+ } catch (error) {
+ console.error('Error fetching harvest data:', error);
+ return null;
+ }
+}
+
+export const HarvestTimeAtom = (contract: string) =>
+ atomWithQuery((get) => ({
+ queryKey: ['harvest_data', contract],
+ queryFn: async () => {
+ const result = await getHarvestData(contract);
+ return result;
+ },
+ }));
diff --git a/src/utils.ts b/src/utils.ts
index c4211f2b..3f50a652 100755
--- a/src/utils.ts
+++ b/src/utils.ts
@@ -108,6 +108,35 @@ export function getDisplayCurrencyAmount(
return Number(Number(amount).toFixed(decimals)).toLocaleString();
}
+// returns time to endtime in days, hours, minutes
+export function formatTimediff(endTime: Date) {
+ const now = new Date();
+ if (now.getTime() >= endTime.getTime()) {
+ return {
+ days: 0,
+ hours: 0,
+ minutes: 0,
+ isZero: true,
+ };
+ }
+
+ // else return number of days, months, weeks, hours, minutrs, seconds to endtime
+ const diff = endTime.getTime() - now.getTime();
+ // get days floor
+ const days = Math.floor(diff / (1000 * 60 * 60 * 24));
+ // after accounting days, get remaining hours
+ const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
+ // after accounting days and hours, get remaining minutes
+ const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60));
+
+ return {
+ days,
+ hours,
+ minutes,
+ isZero: false,
+ };
+}
+
export function copyReferralLink(refCode: string) {
navigator.clipboard.writeText(getReferralUrl(refCode));
@@ -134,9 +163,9 @@ export async function getPriceFromMyAPI(tokenInfo: TokenInfo) {
console.log('getPrice from redis', tokenInfo.name);
const endpoint =
- typeof window === 'undefined'
+ (typeof window === 'undefined'
? process.env.HOSTNAME
- : window.location.origin;
+ : window.location.origin) || 'https://app.strkfarm.xyz';
const priceInfo = await axios.get(`${endpoint}/api/price/${tokenInfo.name}`);
const now = new Date();
const priceTime = new Date(priceInfo.data.timestamp);
From 3fb92053a800b0f3f4b7a8160d3dc03834aea189 Mon Sep 17 00:00:00 2001
From: Akira <156126180+akiraonstarknet@users.noreply.github.com>
Date: Sat, 28 Sep 2024 18:27:09 +0530
Subject: [PATCH 11/11] bug: harvest timer lastHarvest state fix
---
src/components/HarvestTime.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/HarvestTime.tsx b/src/components/HarvestTime.tsx
index 63adf6c3..7fa6a5b5 100644
--- a/src/components/HarvestTime.tsx
+++ b/src/components/HarvestTime.tsx
@@ -68,7 +68,7 @@ const HarvestTime: React.FC = ({ strategy, balData }) => {
}
return formatTimediff(nextHarvest);
- }, [data?.timestamp]);
+ }, [data?.timestamp, lastHarvest]);
return (