Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fixed: eslint errors #14

Merged
merged 1 commit into from
Apr 20, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 0 additions & 120 deletions .eslintrc

This file was deleted.

138 changes: 137 additions & 1 deletion .eslintrc.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,139 @@
{
"extends": "next/core-web-vitals"
"parser": "@typescript-eslint/parser",
"env": {
"browser": true,
"node": true,
"es6": true,
"jest": true,
"mocha": true
},
"plugins": ["@typescript-eslint", "unused-imports"],
"extends": [
"next/core-web-vitals",
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"ecmaVersion": 2018
},
"rules": {
"no-unused-vars": "off",
"unused-imports/no-unused-imports": "error",
"unused-imports/no-unused-vars": [
"warn",
{
"vars": "all",
"varsIgnorePattern": "^_",
"args": "after-used",
"argsIgnorePattern": "^_"
}
],
"strict": 0,
"no-underscore-dangle": 0,
"no-mixed-requires": 0,
"no-process-exit": 0,
"no-warning-comments": 0,
"no-use-before-define": 0,
"curly": 0,
"no-multi-spaces": 0,
"no-alert": 0,
"consistent-return": 0,
"consistent-this": [0, "self"],
"func-style": 0,
"max-nested-callbacks": 0,
"camelcase": 0,
"no-dupe-class-members": 0,

// Warnings
"no-debugger": 1,
"no-empty": 1,
"no-invalid-regexp": 1,
"no-unused-expressions": 1,
"no-native-reassign": 1,
"no-fallthrough": 1,

// Errors
"eqeqeq": 2,
"no-undef": 2,
"no-dupe-keys": 2,
"no-empty-character-class": 2,
"no-self-compare": 2,
"valid-typeof": 2,
"handle-callback-err": 2,
"no-shadow-restricted-names": 2,
"no-new-require": 2,
"no-mixed-spaces-and-tabs": 2,
"block-scoped-var": 2,
"no-else-return": 2,
"no-throw-literal": 2,
"no-void": 2,
"radix": 2,
"wrap-iife": [2, "outside"],
"no-shadow": 0,
"no-path-concat": 2,
"valid-jsdoc": [0, { "requireReturn": false, "requireParamDescription": false, "requireReturnDescription": false }],

// stylistic errors
"no-spaced-func": 2,
"semi-spacing": 2,
"key-spacing": [2, { "beforeColon": false, "afterColon": true }],
"no-lonely-if": 2,
"no-floating-decimal": 2,
"brace-style": [2, "1tbs", { "allowSingleLine": true }],
// "comma": [2, "last"],
"no-multiple-empty-lines": [2, { "max": 1 }],
// "no-nested-ternary": 2,
"operator-assignment": [2, "always"],
"padded-blocks": [2, "never"],
"quote-props": [2, "as-needed"],
"keyword-spacing": [2, { "before": true, "after": true, "overrides": {} }],
"space-before-blocks": [2, "always"],
"array-bracket-spacing": [2, "never"],
"computed-property-spacing": [2, "never"],
"space-in-parens": [2, "never"],
"space-unary-ops": [2, { "words": true, "nonwords": false }],
"wrap-regex": 2,
"linebreak-style": 0,
"semi": [2, "always"],
"arrow-spacing": [2, { "before": true, "after": true }],
"no-class-assign": 2,
"no-const-assign": 2,
"no-this-before-super": 2,
"no-var": 2,
"object-shorthand": [2, "always"],
"prefer-arrow-callback": 2,
"prefer-const": 2,
"prefer-spread": 2,
"prefer-template": 2,

// typescript
"@typescript-eslint/no-empty-interface": "off",
"@typescript-eslint/no-use-before-define": ["off"],
"@typescript-eslint/no-empty-function": "off",
"@typescript-eslint/ban-ts-comment": "off",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-this-alias": "off",
"@typescript-eslint/no-unnecessary-type-constraint": "off",
"@typescript-eslint/ban-types": "off"
},
"overrides": [
{
"files": [
"test/**",
"*.spec.ts",
"*.test.ts",
"*.jsx",
"*.tsx",
"*.ts",
"*.js"
],
"rules": {
"prefer-arrow-callback": 0,
"@typescript-eslint/no-non-null-assertion": 0,
"@typescript-eslint/no-unused-vars": 0
}
}
]
}
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -7,8 +7,8 @@
"build": "next build",
"start": "next start",
"lint": "next lint",
"lint:check": "eslint --max-warnings 0 --config .eslintrc .",
"lint:fix": "eslint --max-warnings 0 --config .eslintrc . --fix",
"lint:check": "eslint --config .eslintrc.json .",
"lint:fix": "eslint --config .eslintrc.json . --fix",
"format:check": "prettier --check \"**/*.{ts,tsx,json}\"",
"format:fix": "prettier --write \"**/*.{ts,tsx,json}\""
},
@@ -59,6 +59,7 @@
"autoprefixer": "^10.0.1",
"eslint": "^8",
"eslint-config-next": "14.1.0",
"eslint-plugin-unused-imports": "^3.1.0",
"postcss": "^8",
"prettier": "^3.2.5",
"tailwindcss": "^3.3.0",
51 changes: 1 addition & 50 deletions src/app/claims/page.tsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,19 @@
"use client";

import Navbar from "@/components/Navbar";
import EkuboAtoms from "@/store/ekobu.store";
import Ekubo from "@/store/ekobu.store";
import Jediswap from "@/store/jedi.store";
import {
PoolInfo,
StrkDexIncentivesAtom,
allPoolsAtomUnSorted,
filteredPools,
sortPoolsAtom,
} from "@/store/pools";
import {
Avatar,
AvatarGroup,
Box,
Button,
Card,
CardBody,
CardHeader,
Center,
Container,
Flex,
Grid,
GridItem,
HStack,
Heading,
Image,
Link,
LinkBox,
LinkOverlay,
Skeleton,
Spinner,
Stack,
Stat,
StatLabel,
StatNumber,
Tab,
TabIndicator,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
Tooltip,
} from "@chakra-ui/react";
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
import { useEffect, useMemo, useState } from "react";
import useSWR from "swr";
import {
Pagination,
PaginationContainer,
usePagination,
PaginationNext,
PaginationPrevious,
PaginationPage,
PaginationPageGroup,
} from "@ajna/pagination";

import CONSTANTS from "@/constants";
import Filters from "@/components/Filters";
import tg from "@/assets/tg.svg";
import Pools from "@/components/Pools";
import Strategies from "@/components/Strategies";
import mixpanel from "mixpanel-browser";

export default function Claim() {
return (
13 changes: 7 additions & 6 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,18 @@
import type { Metadata } from "next";
import { Analytics } from "@vercel/analytics/react";
import { Analytics } from '@vercel/analytics/react';
import type { Metadata } from 'next';
// import { Inter, Courier_Prime } from "next/font/google";
import "./globals.css";
import { GoogleAnalytics } from "@next/third-parties/google";
import { GoogleAnalytics } from '@next/third-parties/google';
import React from 'react';
import './globals.css';

// const courier = Courier_Prime({
// weight: '400',
// subsets: ['latin']
// });

export const metadata: Metadata = {
title: "STRKFarm | Earn $STRK Tokens",
description: "Farm on the best pools of Starknet",
title: 'STRKFarm | Earn $STRK Tokens',
description: 'Farm on the best pools of Starknet',
};

export default function RootLayout({
112 changes: 38 additions & 74 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
@@ -1,101 +1,65 @@
"use client";
'use client';

import tg from '@/assets/tg.svg';
import Pools from '@/components/Pools';
import Strategies from '@/components/Strategies';
import CONSTANTS from '@/constants';

import Navbar from "@/components/Navbar";
import EkuboAtoms from "@/store/ekobu.store";
import Ekubo from "@/store/ekobu.store";
import Jediswap from "@/store/jedi.store";
import {
PoolInfo,
StrkDexIncentivesAtom,
allPoolsAtomUnSorted,
filteredPools,
sortPoolsAtom,
} from "@/store/pools";
import {
Avatar,
AvatarGroup,
Box,
Card,
CardBody,
CardHeader,
Center,
Container,
Flex,
HStack,
Heading,
Image,
Link,
LinkBox,
LinkOverlay,
Skeleton,
Spinner,
Stack,
Tab,
TabIndicator,
TabList,
TabPanel,
TabPanels,
Tabs,
Text,
Tooltip,
} from "@chakra-ui/react";
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
import { useEffect, useMemo, useState } from "react";
import useSWR from "swr";
import {
Pagination,
PaginationContainer,
usePagination,
PaginationNext,
PaginationPrevious,
PaginationPage,
PaginationPageGroup,
} from "@ajna/pagination";
import CONSTANTS from "@/constants";
import Filters from "@/components/Filters";
import tg from "@/assets/tg.svg";
import Pools from "@/components/Pools";
import Strategies from "@/components/Strategies";
import mixpanel from "mixpanel-browser";
} from '@chakra-ui/react';
import mixpanel from 'mixpanel-browser';
import { useEffect } from 'react';

export default function Home() {
useEffect(() => {
mixpanel.track("Page open");
mixpanel.track('Page open');
}, []);

return (
<Container maxWidth={"1000px"} margin={"0 auto"}>
<Box padding={"15px 30px"} borderRadius="10px" margin="20px 0px">
<Text fontSize={"35px"} color={"cyan"} textAlign={"center"}>
<Container maxWidth={'1000px'} margin={'0 auto'}>
<Box padding={'15px 30px'} borderRadius="10px" margin="20px 0px">
<Text fontSize={'35px'} color={'cyan'} textAlign={'center'}>
<b>🚀Starknet DeFi Spring</b>
</Text>
<Text
color="color2"
textAlign={"center"}
fontSize={"18px"}
marginBottom={"0px"}
textAlign={'center'}
fontSize={'18px'}
marginBottom={'0px'}
>
Identify the best $STRK rewarding pools and maximize your rewards
</Text>
{/* <Text color='cyan' textAlign={'center'} fontSize={'18px'} marginBottom={'20px'}>
Pools: {_filteredPools.length}, pages: {pagesCount}</Text> */}
</Box>
<Tabs position="relative" variant="unstyled" width={"100%"}>
<Tabs position="relative" variant="unstyled" width={'100%'}>
<TabList>
<Tab
color="light_grey"
_selected={{ color: "color2" }}
_selected={{ color: 'color2' }}
onClick={() => {
mixpanel.track("All pools clicked");
mixpanel.track('All pools clicked');
}}
>
All Pools
</Tab>
<Tab
color="light_grey"
_selected={{ color: "color2" }}
_selected={{ color: 'color2' }}
onClick={() => {
mixpanel.track("Strategies opened");
mixpanel.track('Strategies opened');
}}
>
Strategies✨
@@ -109,43 +73,43 @@ export default function Home() {
borderRadius="1px"
/>
<TabPanels>
<TabPanel bg="highlight" float={"left"} width={"100%"}>
<TabPanel bg="highlight" float={'left'} width={'100%'}>
<Pools />
</TabPanel>
<TabPanel bg="highlight" width={"100%"} float={"left"}>
<TabPanel bg="highlight" width={'100%'} float={'left'}>
<Strategies></Strategies>
</TabPanel>
</TabPanels>
</Tabs>
<hr
style={{
width: "100%",
borderColor: "#5f5f5f",
float: "left",
margin: "20px 0",
width: '100%',
borderColor: '#5f5f5f',
float: 'left',
margin: '20px 0',
}}
/>
<Text color="light_grey" textAlign={"center"} width={"100%"}>
<Text color="light_grey" textAlign={'center'} width={'100%'}>
More features coming soon. Join our Telegram channel to discuss
strategies, features and contribute.
</Text>
<Center padding="10px 0" width={"100%"} float={"left"}>
<Center padding="10px 0" width={'100%'} float={'left'}>
<Link href={CONSTANTS.COMMUNITY_TG} isExternal>
<Image src={tg.src} width="50" margin="0 auto" />
<Image src={tg.src} width="50" margin="0 auto" alt="telegram" />
</Link>
</Center>
<Center width={"100%"} float="left">
<Center width={'100%'} float="left">
<Box
width="300px"
maxWidth={"100%"}
marginTop={"20px"}
borderTop={"1px solid var(--chakra-colors-highlight)"}
textAlign={"center"}
textColor={"color2"}
maxWidth={'100%'}
marginTop={'20px'}
borderTop={'1px solid var(--chakra-colors-highlight)'}
textAlign={'center'}
textColor={'color2'}
padding="10px 0"
fontSize={"13px"}
fontSize={'13px'}
>
Made with ❤️ on Starknet by{" "}
Made with ❤️ on Starknet by{' '}
<Link href="https://t.me/akiraonstarknet" target="_blank">
@akiraonstarknet
</Link>
254 changes: 104 additions & 150 deletions src/app/strategy/page.tsx

Large diffs are not rendered by default.

16 changes: 1 addition & 15 deletions src/app/template.tsx
Original file line number Diff line number Diff line change
@@ -5,36 +5,22 @@
import { sepolia } from "@starknet-react/chains";
import {
StarknetConfig,
publicProvider,
argent,
braavos,
useInjectedConnectors,
useNetwork,
useAccount,
useConnect,
nethermindProvider,
jsonRpcProvider,
} from "@starknet-react/core";
import {
ChakraBaseProvider,
extendBaseTheme,
theme as chakraTheme,
extendTheme,
Flex,
Icon,
HStack,
Center,
Box,
Container,
} from "@chakra-ui/react";
import { Provider as JotaiProvider } from "jotai";
import { Sidebar, Menu, MenuItem, SubMenu } from "react-pro-sidebar";
import * as LogoSvg from "@public/logo.svg";
import * as HomeSvg from "@public/home.svg";
import * as PlaySvg from "@public/play.svg";
import Image from "next/image";
import Navbar from "@/components/Navbar";
import { RpcProvider, RpcProviderOptions, constants } from "starknet";
import { RpcProviderOptions, constants } from "starknet";
import mixpanel from "mixpanel-browser";

// ! make page view more dynamic
@@ -88,7 +74,7 @@
export default function Template({ children }: { children: React.ReactNode }) {
const chains = [sepolia];
const provider = jsonRpcProvider({
rpc: (chain) => {

Check warning on line 77 in src/app/template.tsx

GitHub Actions / Performs linting, formatting on the application

'chain' is defined but never used. Allowed unused args must match /^_/u
const args: RpcProviderOptions = {
nodeUrl:
"https://rpc.nethermind.io/mainnet-juno?apikey=t1HPjhplOyEQpxqVMhpwLGuwmOlbXN0XivWUiPAxIBs0kHVK",
@@ -106,7 +92,7 @@
order: "alphabetical",
});

function getIconNode(icon: typeof import("*.svg"), alt: string) {

Check warning on line 95 in src/app/template.tsx

GitHub Actions / Performs linting, formatting on the application

'getIconNode' is defined but never used. Allowed unused vars must match /^_/u
return (
<Center className="my-menu-button" width="100%" marginLeft={"-20px"}>
<Image src={icon} alt={alt} />
127 changes: 62 additions & 65 deletions src/components/Deposit.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChevronDownIcon } from "@chakra-ui/icons";
import { ChevronDownIcon } from '@chakra-ui/icons';
import {
Box,
Button,
@@ -17,21 +17,18 @@
NumberInputStepper,
Text,
Tooltip,
usePrevious,
} from "@chakra-ui/react";
import LoadingWrap from "./LoadingWrap";
import { IStrategyActionHook, TokenInfo } from "@/strategies/IStrategy";
import { useERC20Balance } from "@/hooks/useERC20Balance";
import { StrategyInfo } from "@/store/strategies.atoms";
import { useEffect, useMemo, useState, createRef } from "react";
import MyNumber from "@/utils/MyNumber";
import TxButton from "./TxButton";
import { MyMenuItemProps, MyMenuListProps } from "@/utils";
import { PrefixPathnameNormalizer } from "next/dist/server/future/normalizers/request/prefix";
import { useAccount, useProvider } from "@starknet-react/core";
import { ProviderInterface } from "starknet";
import { setMaxIdleHTTPParsers } from "http";
import mixpanel from "mixpanel-browser";
} from '@chakra-ui/react';
import LoadingWrap from './LoadingWrap';
import { IStrategyActionHook, TokenInfo } from '@/strategies/IStrategy';
import { useERC20Balance } from '@/hooks/useERC20Balance';
import { StrategyInfo } from '@/store/strategies.atoms';
import { useMemo, useState } from 'react';
import MyNumber from '@/utils/MyNumber';
import TxButton from './TxButton';
import { MyMenuItemProps, MyMenuListProps } from '@/utils';
import { useAccount, useProvider } from '@starknet-react/core';
import { ProviderInterface } from 'starknet';
import mixpanel from 'mixpanel-browser';

interface DepositProps {
strategy: StrategyInfo;
@@ -49,20 +46,20 @@
const [dirty, setDirty] = useState(false);

const [selectedMarket, setSelectedMarket] = useState(
props.callsInfo(MyNumber.fromZero(), address || "0x0", provider)[0]
props.callsInfo(MyNumber.fromZero(), address || '0x0', provider)[0]
.tokenInfo,
);
const [amount, setAmount] = useState(
MyNumber.fromEther("0", selectedMarket.decimals),
MyNumber.fromEther('0', selectedMarket.decimals),
);
const [rawAmount, setRawAmount] = useState("");
const [rawAmount, setRawAmount] = useState('');

const { calls, actions } = useMemo(() => {
const actions = props.callsInfo(amount, address || "0x0", provider);
const hook = actions.find((a) => a.tokenInfo.name == selectedMarket.name);
const actions = props.callsInfo(amount, address || '0x0', provider);
const hook = actions.find((a) => a.tokenInfo.name === selectedMarket.name);
if (!hook) return { calls: [], actions };
return { calls: hook.calls, actions };
}, [selectedMarket, amount, address, provider]);

Check warning on line 62 in src/components/Deposit.tsx

GitHub Actions / Performs linting, formatting on the application

React Hook useMemo has a missing dependency: 'props'. Either include it or remove the dependency array. However, 'props' will change when *any* prop changes, so the preferred fix is to destructure the 'props' object outside of the useMemo call and refer to those specific props inside useMemo

const { balance, isLoading, isError } = useERC20Balance(selectedMarket);

@@ -76,47 +73,47 @@
buttonText: string;
}) {
return (
<Box color={"light_grey"} textAlign={"right"}>
<Box color={'light_grey'} textAlign={'right'}>
<Text>Available balance </Text>
<LoadingWrap
isLoading={isLoading}
isError={isError}
skeletonProps={{
height: "10px",
width: "50px",
float: "right",
marginTop: "8px",
marginLeft: "5px",
height: '10px',
width: '50px',
float: 'right',
marginTop: '8px',
marginLeft: '5px',
}}
iconProps={{
marginLeft: "5px",
boxSize: "15px",
marginLeft: '5px',
boxSize: '15px',
}}
>
<Tooltip label={balance.toEtherStr()}>
<b style={{ marginLeft: "5px" }}>
<b style={{ marginLeft: '5px' }}>
{balance.toEtherToFixedDecimals(4)}
</b>
</Tooltip>
<Button
size={"sm"}
marginLeft={"5px"}
size={'sm'}
marginLeft={'5px'}
color="purple"
bg="highlight"
padding="0"
maxHeight={"25px"}
maxHeight={'25px'}
_hover={{
bg: "highlight",
color: "color_50p",
bg: 'highlight',
color: 'color_50p',
}}
_active={{
bg: "highlight",
color: "color_50p",
bg: 'highlight',
color: 'color_50p',
}}
onClick={() => {
setAmount(maxAmount);
setRawAmount(maxAmount.toEtherStr());
mixpanel.track("Chose max amount", {
mixpanel.track('Chose max amount', {
strategy: props.strategy.name,
buttonText: props.buttonText,
amount: amount.toEtherStr(),
@@ -140,20 +137,20 @@
<Menu>
<MenuButton
as={Button}
height={"100%"}
height={'100%'}
rightIcon={<ChevronDownIcon />}
bgColor={"highlight"}
borderColor={"bg"}
borderWidth={"1px"}
bgColor={'highlight'}
borderColor={'bg'}
borderWidth={'1px'}
color="purple"
>
<Center>
<ImageC
src={selectedMarket.logo.src}
alt=""
width={"20px"}
width={'20px'}
marginRight="5px"
/>{" "}
/>{' '}
{selectedMarket.name}
</Center>
</MenuButton>
@@ -163,21 +160,21 @@
key={dep.tokenInfo.name}
{...MyMenuItemProps}
onClick={() => {
if (selectedMarket.name != dep.tokenInfo.name) {
if (selectedMarket.name !== dep.tokenInfo.name) {
setSelectedMarket(dep.tokenInfo);
setAmount(new MyNumber("0", dep.tokenInfo.decimals));
setAmount(new MyNumber('0', dep.tokenInfo.decimals));
setDirty(false);
setRawAmount("");
setRawAmount('');
}
}}
>
<Center>
<ImageC
src={dep.tokenInfo.logo.src}
alt=""
width={"20px"}
width={'20px'}
marginRight="5px"
/>{" "}
/>{' '}
{dep.tokenInfo.name}
</Center>
</MenuItem>
@@ -199,18 +196,18 @@
min={0}
max={parseFloat(maxAmount.toEtherStr())}
step={parseFloat(selectedMarket.stepAmount.toEtherStr())}
color={"white"}
bg={"bg"}
borderRadius={"10px"}
color={'white'}
bg={'bg'}
borderRadius={'10px'}
onChange={(value) => {
if (value && Number(value) > 0)
setAmount(MyNumber.fromEther(value, selectedMarket.decimals));
else {
setAmount(new MyNumber("0", selectedMarket.decimals));
setAmount(new MyNumber('0', selectedMarket.decimals));
}
setRawAmount(value);
setDirty(true);
mixpanel.track("Enter amount", {
mixpanel.track('Enter amount', {
strategy: props.strategy.name,
buttonText: props.buttonText,
amount: amount.toEtherStr(),
@@ -219,39 +216,39 @@
address,
});
}}
marginTop={"10px"}
marginTop={'10px'}
keepWithinRange={false}
clampValueOnBlur={false}
value={rawAmount}
>
<NumberInputField
border={"0px"}
borderRadius={"10px"}
border={'0px'}
borderRadius={'10px'}
placeholder="Amount"
/>
<NumberInputStepper>
<NumberIncrementStepper color={"white"} border={"0px"} />
<NumberDecrementStepper color={"white"} border={"0px"} />
<NumberIncrementStepper color={'white'} border={'0px'} />
<NumberDecrementStepper color={'white'} border={'0px'} />
</NumberInputStepper>
</NumberInput>
{amount.isZero() && dirty && (
<Text marginTop="2px" marginLeft={"7px"} color="red" fontSize={"13px"}>
Require amount {">"} 0
<Text marginTop="2px" marginLeft={'7px'} color="red" fontSize={'13px'}>
Require amount {'>'} 0
</Text>
)}
{amount.compare(maxAmount.toEtherStr(), "gt") && (
<Text marginTop="2px" marginLeft={"7px"} color="red" fontSize={"13px"}>
{amount.compare(maxAmount.toEtherStr(), 'gt') && (
<Text marginTop="2px" marginLeft={'7px'} color="red" fontSize={'13px'}>
Amount to be less than {maxAmount.toEtherToFixedDecimals(2)}
</Text>
)}

<Center marginTop={"10px"}>
<Center marginTop={'10px'}>
<TxButton
text={`${props.buttonText}: ${amount.toEtherToFixedDecimals(2)} ${selectedMarket.name}`}
calls={calls}
buttonProps={{
isDisabled:
amount.isZero() || amount.compare(maxAmount.toEtherStr(), "gt"),
amount.isZero() || amount.compare(maxAmount.toEtherStr(), 'gt'),
}}
/>
</Center>
98 changes: 48 additions & 50 deletions src/components/Filters.tsx
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
import React from "react";
import Select, { StylesConfig } from "react-select";
import React from 'react';
import Select, { StylesConfig } from 'react-select';
import {
ALL_FILTER,
filterAtoms,
filters,
updateFiltersAtom,
} from "@/store/pools";
import * as chroma from "chroma.ts";
import { useAtom, useSetAtom } from "jotai";
} from '@/store/pools';
import * as chroma from 'chroma.ts';
import { useSetAtom } from 'jotai';
import {
Accordion,
AccordionButton,
AccordionItem,
AccordionPanel,
Box,
Container,
Stack,
Text,
} from "@chakra-ui/react";
import { HamburgerIcon } from "@chakra-ui/icons";
} from '@chakra-ui/react';
import { HamburgerIcon } from '@chakra-ui/icons';

export interface Option {
readonly value: string;
@@ -31,26 +29,26 @@
const colourStyles: StylesConfig<Option, true> = {
control: (styles) => ({
...styles,
padding: "10px 0",
backgroundColor: "var(--chakra-colors-bg)",
borderColor: "var(--chakra-colors-bg)",
padding: '10px 0',
backgroundColor: 'var(--chakra-colors-bg)',
borderColor: 'var(--chakra-colors-bg)',
}),
option: (styles, { data, isDisabled, isFocused, isSelected }) => {

Check warning on line 36 in src/components/Filters.tsx

GitHub Actions / Performs linting, formatting on the application

'isFocused' is defined but never used. Allowed unused args must match /^_/u
const color = chroma.color(data.color);
return {
...styles,
backgroundColor: "var(--chakra-colors-bg)",
backgroundColor: 'var(--chakra-colors-bg)',
color: isDisabled
? "#ccc"
? '#ccc'
: isSelected
? chroma.contrast(color, "white") > 2
? "white"
: "black"
? chroma.contrast(color, 'white') > 2
? 'white'
: 'black'
: data.color,
cursor: isDisabled ? "not-allowed" : "default",
cursor: isDisabled ? 'not-allowed' : 'default',

":active": {
...styles[":active"],
':active': {
...styles[':active'],
backgroundColor: !isDisabled
? isSelected
? data.color
@@ -59,45 +57,45 @@
},
};
},
container: (styles, {}) => {
container: (styles) => {
return {
...styles,
width: "100%",
width: '100%',
};
},
dropdownIndicator: (styles, {}) => {
dropdownIndicator: (styles) => {
return {
...styles,
color: "var(--chakra-colors-color2)",
color: 'var(--chakra-colors-color2)',
};
},
clearIndicator: (styles, {}) => {
clearIndicator: (styles) => {
return {
...styles,
color: "var(--chakra-colors-color2)",
color: 'var(--chakra-colors-color2)',
};
},
indicatorSeparator: (styles, {}) => {
indicatorSeparator: (styles) => {
return {
...styles,
backgroundColor: "var(--chakra-colors-color2)",
backgroundColor: 'var(--chakra-colors-color2)',
};
},
menu: (styles, {}) => {
menu: (styles) => {
return {
...styles,
backgroundColor: "var(--chakra-colors-highlight)",
backgroundColor: 'var(--chakra-colors-highlight)',
};
},
multiValue: (styles, { data }) => {
const color = chroma.color(data.color);
return {
...styles,
fontWeight: "bold",
backgroundColor: "none",
fontWeight: 'bold',
backgroundColor: 'none',
borderColor: color.alpha(0.7).css(),
borderWidth: "1px",
borderRadius: "5px",
borderWidth: '1px',
borderRadius: '5px',
};
},
multiValueLabel: (styles, { data }) => ({
@@ -107,9 +105,9 @@
multiValueRemove: (styles, { data }) => ({
...styles,
color: data.color,
":hover": {
':hover': {
backgroundColor: data.color,
color: "white",
color: 'white',
},
}),
};
@@ -118,7 +116,7 @@
// const colors: readonly string[] = ['#00B8D9', '#407cd5', '#7967e5', '#FF5630',
// '#FF8B00','#FFC400', '#36B37E', '#00875A', '#253858', '#666666']

const colors = ["rgba(86, 118, 254, 1)", "rgb(127 73 229)"];
const colors = ['rgba(86, 118, 254, 1)', 'rgb(127 73 229)'];
const updateFilters = useSetAtom(updateFiltersAtom);

const protocolOptions: readonly Option[] = filters.protocols.map(
@@ -136,7 +134,7 @@
});
return (
<Accordion allowToggle>
<AccordionItem borderTop="0px" borderBottom="1px" borderColor={"bg"}>
<AccordionItem borderTop="0px" borderBottom="1px" borderColor={'bg'}>
<h2>
<AccordionButton>
<Box
@@ -151,8 +149,8 @@
<HamburgerIcon color="color2" />
</AccordionButton>
</h2>
<AccordionPanel pb={4} bg="highlight" overflow={"visible"}>
<Text color="light_grey" fontSize={"14px"} width="100%">
<AccordionPanel pb={4} bg="highlight" overflow={'visible'}>
<Text color="light_grey" fontSize={'14px'} width="100%">
Protocols:
</Text>
<Select
@@ -164,20 +162,20 @@
styles={colourStyles}
onChange={(x, b) => {
console.log(x, Array.isArray(x), b);
if (filters.protocols.length == x.length) {
updateFilters("protocols", [ALL_FILTER]);
if (filters.protocols.length === x.length) {
updateFilters('protocols', [ALL_FILTER]);
} else {
updateFilters(
"protocols",
'protocols',
x.map((p) => p.value),
);
}
}}
/>

<Stack marginTop={"5px"} direction={{ base: "column", md: "row" }}>
<Box width={{ base: "100%", md: "50%" }}>
<Text color="light_grey" fontSize={"14px"} width="100%">
<Stack marginTop={'5px'} direction={{ base: 'column', md: 'row' }}>
<Box width={{ base: '100%', md: '50%' }}>
<Text color="light_grey" fontSize={'14px'} width="100%">
Categories:
</Text>
<Select
@@ -190,14 +188,14 @@
onChange={(x, b) => {
console.log(x, Array.isArray(x), b);
updateFilters(
"categories",
'categories',
x.map((p) => p.value),
);
}}
/>
</Box>
<Box width={{ base: "100%", md: "50%" }}>
<Text color="light_grey" fontSize={"14px"} width="100%">
<Box width={{ base: '100%', md: '50%' }}>
<Text color="light_grey" fontSize={'14px'} width="100%">
Protocol types:
</Text>
<Select
@@ -207,9 +205,9 @@
placeholder="Select Pool types"
options={poolTypes}
styles={colourStyles}
onChange={(x, b) => {

Check warning on line 208 in src/components/Filters.tsx

GitHub Actions / Performs linting, formatting on the application

'b' is defined but never used. Allowed unused args must match /^_/u
updateFilters(
"poolTypes",
'poolTypes',
x.map((p) => p.value),
);
}}
140 changes: 64 additions & 76 deletions src/components/Navbar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
Avatar,
Badge,
Box,
Button,
Center,
@@ -12,67 +11,56 @@
MenuButton,
MenuItem,
MenuList,
Popover,
PopoverArrow,
PopoverBody,
PopoverCloseButton,
PopoverContent,
PopoverFooter,
PopoverHeader,
PopoverTrigger,
Portal,
Select,
Spinner,
Text,
} from "@chakra-ui/react";
import { useAccount, useConnect, useDisconnect } from "@starknet-react/core";
import { ChevronDownIcon } from "@chakra-ui/icons";
import { num } from "starknet";
import tg from "@/assets/tg.svg";
import CONSTANTS from "@/constants";
import { useEffect } from "react";
import { useAtom, useAtomValue, useSetAtom } from "jotai";
import { addressAtom } from "@/store/claims.atoms";
} from '@chakra-ui/react';
import { useAccount, useConnect, useDisconnect } from '@starknet-react/core';
import { ChevronDownIcon } from '@chakra-ui/icons';
import tg from '@/assets/tg.svg';
import CONSTANTS from '@/constants';
import { useEffect } from 'react';
import { useAtom, useSetAtom } from 'jotai';
import { addressAtom } from '@/store/claims.atoms';
import {
capitalize,
shortAddress,
MyMenuListProps,
MyMenuItemProps,
} from "@/utils";
import { getStarknet } from "get-starknet-core";
import { WalletName, lastWalletAtom } from "@/store/utils.atoms";
} from '@/utils';
import { getStarknet } from 'get-starknet-core';
import { WalletName, lastWalletAtom } from '@/store/utils.atoms';

export default function Navbar() {
const { address, chainId, status, connector } = useAccount();

Check warning on line 34 in src/components/Navbar.tsx

GitHub Actions / Performs linting, formatting on the application

'chainId' is assigned a value but never used. Allowed unused vars must match /^_/u
const { connect, connectors } = useConnect();
const { disconnect, disconnectAsync } = useDisconnect();

Check warning on line 36 in src/components/Navbar.tsx

GitHub Actions / Performs linting, formatting on the application

'disconnect' is assigned a value but never used. Allowed unused vars must match /^_/u
const setAddress = useSetAtom(addressAtom);

Check warning on line 37 in src/components/Navbar.tsx

GitHub Actions / Performs linting, formatting on the application

'setAddress' is assigned a value but never used. Allowed unused vars must match /^_/u
const [lastWallet, setLastWallet] = useAtom(lastWalletAtom);
const getStarknetResult = getStarknet();

Check warning on line 39 in src/components/Navbar.tsx

GitHub Actions / Performs linting, formatting on the application

'getStarknetResult' is assigned a value but never used. Allowed unused vars must match /^_/u

useEffect(() => {
console.log("lastWallet", lastWallet);
console.log('lastWallet', lastWallet);
if (!address && lastWallet) {
const lastConnector = connectors.find((c) => c.name == lastWallet);
console.log("lastWallet connected", lastConnector);
const lastConnector = connectors.find((c) => c.name === lastWallet);
console.log('lastWallet connected', lastConnector);
if (!lastConnector)
console.error("last connector name found, but no connector");
console.error('last connector name found, but no connector');
else {
connect({ connector: lastConnector });
}
}
}, [lastWallet]);

useEffect(() => {
console.log("lastWallet connector", connector?.name);
console.log('lastWallet connector', connector?.name);
if (connector) {
const name: WalletName = connector.name as WalletName;
setLastWallet(name);
}
}, [connector]);

useEffect(() => {
console.log("lastWallet stats", {
console.log('lastWallet stats', {
address,
status,
connector,
@@ -81,45 +69,45 @@

return (
<Container
width={"100%"}
padding={"0"}
borderBottom={"1px solid var(--chakra-colors-color2)"}
position={"fixed"}
width={'100%'}
padding={'0'}
borderBottom={'1px solid var(--chakra-colors-color2)'}
position={'fixed'}
bg="bg"
zIndex={10000}
top="0"
>
<Center bg="highlight" color="orange" padding={0}>
<Text fontSize="12px" textAlign={"center"} padding="0px 5px">
{""}
<Text fontSize="12px" textAlign={'center'} padding="0px 5px">
{''}
<b>Alpha version, report bugs in our Telegram group.</b>
{""}
{''}
</Text>
</Center>
<Box
width={"100%"}
width={'100%'}
maxWidth="1400px"
margin={"0px auto"}
padding={"20px 20px 10px"}
margin={'0px auto'}
padding={'20px 20px 10px'}
>
<Flex width={"100%"}>
<Link href="/" margin="0 auto 0 0" textAlign={"left"}>
<Flex width={'100%'}>
<Link href="/" margin="0 auto 0 0" textAlign={'left'}>
<Text
fontSize={{ base: "20px", sm: "25px", md: "30px" }}
color={"color2"}
letterSpacing={"10px"}
marginTop={{ base: "7px", sm: "3px", md: "0" }}
fontSize={{ base: '20px', sm: '25px', md: '30px' }}
color={'color2'}
letterSpacing={'10px'}
marginTop={{ base: '7px', sm: '3px', md: '0' }}
>
<b>STRKFarm</b>
</Text>
</Link>
<Link href={"/claims"} isExternal>
<Link href={'/claims'} isExternal>
<Button
margin="0 0 0 auto"
borderColor="color2"
color="color2"
variant="ghost"
marginRight={"30px"}
marginRight={'30px'}
leftIcon={
<Avatar
size="sm"
@@ -130,9 +118,9 @@
/>
}
_hover={{
bg: "color2_50p",
bg: 'color2_50p',
}}
display={{ base: "none !important", md: "flex !important" }}
display={{ base: 'none !important', md: 'flex !important' }}
>
Claims
</Button>
@@ -153,18 +141,18 @@
/>
}
_hover={{
bg: "color2_50p",
bg: 'color2_50p',
}}
display={{ base: "none !important", md: "flex !important" }}
display={{ base: 'none !important', md: 'flex !important' }}
className="glow-button"
>
Join Telegram
</Button>
<IconButton
aria-label="tg"
variant={"ghost"}
borderColor={"color2"}
display={{ base: "block", md: "none" }}
variant={'ghost'}
borderColor={'color2'}
display={{ base: 'block', md: 'none' }}
icon={
<Avatar
size="sm"
@@ -174,7 +162,7 @@
color="color2"
src={tg.src}
_hover={{
bg: "color2_50p",
bg: 'color2_50p',
}}
/>
}
@@ -184,37 +172,37 @@
<MenuButton
as={Button}
rightIcon={<ChevronDownIcon />}
bgColor={"purple"}
bgColor={'purple'}
color="white"
borderColor={"purple"}
borderColor={'purple'}
_hover={{
bg: "bg",
borderColor: "purple",
borderWidth: "1px",
color: "purple",
bg: 'bg',
borderColor: 'purple',
borderWidth: '1px',
color: 'purple',
}}
_active={{
bg: "bg",
borderColor: "purple",
borderWidth: "1px",
color: "purple",
bg: 'bg',
borderColor: 'purple',
borderWidth: '1px',
color: 'purple',
}}
marginLeft={"10px"}
display={{ base: "none", sm: "flex" }}
marginLeft={'10px'}
display={{ base: 'none', sm: 'flex' }}
>
<Center>
{status == "connecting" || status == "reconnecting" ? (
<Spinner size={"sm"} marginRight={"5px"} />
{status === 'connecting' || status === 'reconnecting' ? (
<Spinner size={'sm'} marginRight={'5px'} />
) : (
<></>
)}
{status == "connected" && address ? shortAddress(address) : ""}
{status == "disconnected" ? "Connect" : ""}
{status === 'connected' && address ? shortAddress(address) : ''}
{status === 'disconnected' ? 'Connect' : ''}
</Center>
</MenuButton>
<MenuList {...MyMenuListProps}>
{/* connectors */}
{status != "connected" &&
{status !== 'connected' &&
connectors.map((conn) => (
<MenuItem
{...MyMenuItemProps}
@@ -225,21 +213,21 @@
>
<Avatar
src={conn.icon.light}
size={"2xs"}
marginRight={"5px"}
size={'2xs'}
marginRight={'5px'}
/>
{capitalize(conn.name)}
</MenuItem>
))}

{/* disconnect buttons */}
{status == "connected" && address && (
{status === 'connected' && address && (
<MenuItem
key="disconnect"
{...MyMenuItemProps}
onClick={() => {
disconnectAsync().then((data) => {
console.log("wallet disconnected");
console.log('wallet disconnected');
setLastWallet(null);
});
}}
136 changes: 63 additions & 73 deletions src/components/Pools.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,31 @@
"use client";
'use client';

import Navbar from "@/components/Navbar";
import EkuboAtoms from "@/store/ekobu.store";
import Ekubo from "@/store/ekobu.store";
import Jediswap from "@/store/jedi.store";
import {
PoolInfo,
StrkDexIncentivesAtom,
allPoolsAtomUnSorted,
filteredPools,
sortPoolsAtom,
} from "@/store/pools";
} from '@/store/pools';
import {
Avatar,
AvatarGroup,
Box,
Card,
CardBody,
CardHeader,
Center,
Container,
Flex,
HStack,
Heading,
Image,
Link,
LinkBox,
LinkOverlay,
Skeleton,
Spinner,
Stack,
Text,
Tooltip,
} from "@chakra-ui/react";
import { atom, useAtom, useAtomValue, useSetAtom } from "jotai";
import { useEffect, useMemo, useState } from "react";
import useSWR from "swr";
} from '@chakra-ui/react';
import { useAtomValue } from 'jotai';
import { useEffect, useMemo } from 'react';
import {
Pagination,
PaginationContainer,
@@ -44,10 +34,9 @@ import {
PaginationPrevious,
PaginationPage,
PaginationPageGroup,
} from "@ajna/pagination";
import CONSTANTS from "@/constants";
import Filters from "@/components/Filters";
import tg from "@/assets/tg.svg";
} from '@ajna/pagination';
import CONSTANTS from '@/constants';
import Filters from '@/components/Filters';

export default function Pools() {
const allPools = useAtomValue(allPoolsAtomUnSorted);
@@ -66,30 +55,31 @@ export default function Pools() {
const sortedPools = useAtomValue(sortPoolsAtom);

useEffect(() => {
console.log("sortedPools", sortedPools);
console.log('sortedPools', sortedPools);
}, [sortedPools]);
useEffect(() => {
console.log("pages", currentPage, setCurrentPage, pagesCount, pages);
console.log('pages', currentPage, setCurrentPage, pagesCount, pages);
}, [currentPage, setCurrentPage, pagesCount, pages]);

function getAPRWithToolTip(pool: PoolInfo) {
const tip = (
<Box width={"300px"}>
<Box width={'300px'}>
{pool.aprSplits.map((split) => {
if (split.apr == 0) {
if (split.apr === 0) {
return (
<Text key={split.title}>
{split.title}: {split.description}
</Text>
);
}
return (
<Flex width={"100%"} key={split.title}>
<Text key="1" width={"70%"}>
<Flex width={'100%'} key={split.title}>
<Text key="1" width={'70%'}>
{split.title}:
</Text>
<Text width={"30%"} textAlign={"right"} key="2">
{split.apr == "Err" ? split.apr : (split.apr * 100).toFixed(2)}%
<Text width={'30%'} textAlign={'right'} key="2">
{split.apr === 'Err' ? split.apr : (split.apr * 100).toFixed(2)}
%
</Text>
</Flex>
);
@@ -99,25 +89,25 @@ export default function Pools() {
return (
<Tooltip hasArrow label={tip} bg="gray.300" color="black">
<Center
width={{ base: "auto", md: "50%" }}
marginRight={"0px"}
marginLeft={{ base: "auto", md: "0px" }}
display={"flex"}
width={{ base: 'auto', md: '50%' }}
marginRight={'0px'}
marginLeft={{ base: 'auto', md: '0px' }}
display={'flex'}
>
{pool.isLoading && <Spinner />}
{!pool.isLoading && (
<>
<Avatar
size="xs"
bg={"black"}
bg={'black'}
src={CONSTANTS.LOGOS.STRK}
marginRight={"2px"}
alignContent={"right"}
marginRight={'2px'}
alignContent={'right'}
/>
<Text
textAlign={{ base: "right", md: "center" }}
textAlign={{ base: 'right', md: 'center' }}
color="cyan"
fontSize={"20px"}
fontSize={'20px'}
>
<b>{(pool.apr * 100).toFixed(2)}%</b>
</Text>
@@ -129,10 +119,10 @@ export default function Pools() {
}

return (
<Box float="left" width={"100%"}>
<Box float="left" width={'100%'}>
<Filters />
{
<Container width={"100%"} float={"left"} padding="0px">
<Container width={'100%'} float={'left'} padding="0px">
<Pagination
pagesCount={pagesCount}
currentPage={currentPage}
@@ -141,58 +131,58 @@ export default function Pools() {
setCurrentPage(page);
}}
>
<PaginationContainer align="right" float={"right"} p={4}>
<PaginationContainer align="right" float={'right'} p={4}>
<PaginationPrevious marginRight="4px" bg="highlight" color="cyan">
<Text>{"<"}</Text>
<Text>{'<'}</Text>
</PaginationPrevious>
<PaginationPageGroup>
{pages.map((page: number) => (
<PaginationPage
key={`pagination_page_${page}`}
page={page}
padding={"2px 10px"}
isDisabled={page == currentPage}
padding={'2px 10px'}
isDisabled={page === currentPage}
bg="highlight"
color="cyan"
/>
))}
</PaginationPageGroup>
<PaginationNext marginLeft="4px" bg="highlight" color="cyan">
<Text>{">"}</Text>
<Text>{'>'}</Text>
</PaginationNext>
</PaginationContainer>
</Pagination>
</Container>
}
{
<Box padding="0px 0" width={"100%"} float={"left"}>
<Text color="light_grey" textAlign={"right"} fontSize={"12px"}>
<Box padding="0px 0" width={'100%'} float={'left'}>
<Text color="light_grey" textAlign={'right'} fontSize={'12px'}>
Max {ITEMS_PER_PAGE} per page, total {_filteredPools.length}
</Text>
</Box>
}
<Container width="100%" float={"left"} padding={"0px"} marginTop={"10px"}>
<Card variant={"filled"} bg="opacity_50p" color={"purple"}>
<CardBody paddingTop={"5px"} paddingBottom={"5px"}>
<HStack width={"100%"}>
<Heading width={{ base: "50%", md: "33%" }} size="md">
<Container width="100%" float={'left'} padding={'0px'} marginTop={'10px'}>
<Card variant={'filled'} bg="opacity_50p" color={'purple'}>
<CardBody paddingTop={'5px'} paddingBottom={'5px'}>
<HStack width={'100%'}>
<Heading width={{ base: '50%', md: '33%' }} size="md">
Pool
</Heading>
<Stack
direction={{ base: "column", md: "row" }}
width={{ base: "50%", md: "66%" }}
direction={{ base: 'column', md: 'row' }}
width={{ base: '50%', md: '66%' }}
>
<Heading
width={{ base: "100%", md: "50%" }}
width={{ base: '100%', md: '50%' }}
size="md"
textAlign={{ base: "right", md: "center" }}
textAlign={{ base: 'right', md: 'center' }}
>
STRK APY(%)
</Heading>
<Heading
width={{ base: "100%", md: "50%" }}
width={{ base: '100%', md: '50%' }}
size="sm"
textAlign={"right"}
textAlign={'right'}
>
TVL($)
</Heading>
@@ -205,21 +195,21 @@ export default function Pools() {
{pools.map((pool, index) => (
<Card
key={`${pool.pool.name}_${pool.protocol.name}`}
variant={"filled"}
bg={index % 2 == 0 ? "color1_50p" : "color2_50p"}
variant={'filled'}
bg={index % 2 === 0 ? 'color1_50p' : 'color2_50p'}
color="white"
>
<Link
href={pool.protocol.link}
width={"100%"}
borderWidth={"0px"}
width={'100%'}
borderWidth={'0px'}
target="_blank"
>
<CardBody>
<HStack width={"100%"}>
<Box width={{ base: "50%", md: "33%" }}>
<HStack width={'100%'}>
<Box width={{ base: '50%', md: '33%' }}>
<Flex>
<AvatarGroup size="xs" max={2} marginRight={"5px"}>
<AvatarGroup size="xs" max={2} marginRight={'5px'}>
{pool.pool.logos.map((logo) => (
<Avatar key={logo} src={logo} />
))}
@@ -229,24 +219,24 @@ export default function Pools() {
<Heading size="xs">
<Avatar
size="2xs"
bg={"black"}
bg={'black'}
name="Dan Abrahmov"
src={pool.protocol.logo}
marginRight={"2px"}
marginRight={'2px'}
/>
{pool.protocol.name}
</Heading>
</Box>
</Flex>
</Box>
<Stack
direction={{ base: "column", md: "row" }}
width={{ base: "50%", md: "66%" }}
direction={{ base: 'column', md: 'row' }}
width={{ base: '50%', md: '66%' }}
>
{getAPRWithToolTip(pool)}
<Text
width={{ base: "100%", md: "50%" }}
textAlign={"right"}
width={{ base: '100%', md: '50%' }}
textAlign={'right'}
>
${Math.round(pool.tvl).toLocaleString()}
</Text>
@@ -258,14 +248,14 @@ export default function Pools() {
))}
</Stack>
)}
{allPools.length > 0 && pools.length == 0 && (
<Box padding="10px 0" width={"100%"} float={"left"}>
<Text color="light_grey" textAlign={"center"}>
{allPools.length > 0 && pools.length === 0 && (
<Box padding="10px 0" width={'100%'} float={'left'}>
<Text color="light_grey" textAlign={'center'}>
No pools. Check filters.
</Text>
</Box>
)}
{allPools.length == 0 && (
{allPools.length === 0 && (
<Stack>
<Skeleton height="70px" />
<Skeleton height="70px" />
188 changes: 78 additions & 110 deletions src/components/Strategies.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@
import CONSTANTS, { TokenName } from "@/constants";
import { allPoolsAtomUnSorted } from "@/store/pools";
import { StrategyInfo, strategiesAtom } from "@/store/strategies.atoms";
import { StrategyAction } from "@/strategies/simple.stable.strat";
import { getUnique, getUniqueById } from "@/utils";
import { AddIcon } from "@chakra-ui/icons";
import { allPoolsAtomUnSorted } from '@/store/pools';
import { StrategyInfo, strategiesAtom } from '@/store/strategies.atoms';
import { getUniqueById } from '@/utils';
import { AddIcon } from '@chakra-ui/icons';
import {
Accordion,
AccordionButton,
AccordionIcon,
AccordionItem,
AccordionPanel,
AlertDialog,
AlertDialogBody,
AlertDialogContent,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogOverlay,
Avatar,
AvatarGroup,
Box,
@@ -24,19 +11,10 @@ import {
CardBody,
Center,
Container,
Flex,
HStack,
Heading,
Link,
LinkBox,
LinkOverlay,
Modal,
ModalBody,
ModalCloseButton,
ModalContent,
ModalFooter,
ModalHeader,
ModalOverlay,
Popover,
PopoverArrow,
PopoverBody,
@@ -45,25 +23,15 @@ import {
PopoverTrigger,
Skeleton,
Stack,
Table,
TableCaption,
TableContainer,
Tbody,
Td,
Text,
Th,
Thead,
Tooltip,
Tr,
VisuallyHidden,
Wrap,
WrapItem,
useDisclosure,
} from "@chakra-ui/react";
import { useAtomValue } from "jotai";
import React, { useEffect } from "react";
import mixpanel from "mixpanel-browser";
import { useRouter } from "next/navigation";
} from '@chakra-ui/react';
import { useAtomValue } from 'jotai';
import React from 'react';
import mixpanel from 'mixpanel-browser';
import { useRouter } from 'next/navigation';

export default function Strategies() {
const allPools = useAtomValue(allPoolsAtomUnSorted);
@@ -77,10 +45,10 @@ export default function Strategies() {
<AvatarGroup
size="xs"
max={2}
marginRight={"5px"}
float={"left"}
visibility={"hidden"}
display={{ base: "none", md: "flex" }}
marginRight={'5px'}
float={'left'}
visibility={'hidden'}
display={{ base: 'none', md: 'flex' }}
>
{getUniqueById(
strat.actions.map((p) => ({
@@ -92,46 +60,46 @@ export default function Strategies() {
))}
</AvatarGroup>
<Box
float={"left"}
marginTop={{ base: "7px", md: "15px" }}
width={{ base: "calc(100% - 40px)", md: "calc(100% - 80px)" }}
opacity={"0.5"}
fontSize={"15px"}
fontFamily={"arial"}
float={'left'}
marginTop={{ base: '7px', md: '15px' }}
width={{ base: 'calc(100% - 40px)', md: 'calc(100% - 80px)' }}
opacity={'0.5'}
fontSize={'15px'}
fontFamily={'arial'}
>
{strat.description}
</Box>
<Popover>
<PopoverTrigger>
<Button
variant={"solid"}
size={"sm"}
variant={'solid'}
size={'sm'}
bg="highlight"
color="cyan"
float={"right"}
marginTop={"10px"}
float={'right'}
marginTop={'10px'}
_hover={{
backgroundColor: "bg",
backgroundColor: 'bg',
}}
onClick={() => {
mixpanel.track("Click one click deposit", { name: strat.name });
mixpanel.track('Click one click deposit', { name: strat.name });
}}
>
<AddIcon />
</Button>
</PopoverTrigger>
<PopoverContent bg="highlight" borderColor={"highlight"}>
<PopoverContent bg="highlight" borderColor={'highlight'}>
<PopoverArrow />
<PopoverCloseButton />
<PopoverBody marginTop={"20px"}>
<Text fontSize={"14px"}>
Thanks for showing interest in{" "}
<PopoverBody marginTop={'20px'}>
<Text fontSize={'14px'}>
Thanks for showing interest in{' '}
<b>`One Click Deposit` feature</b>. We are developing this as
you read this message and will be available soon. The button is
to let you know that we will be supporting this soon. 😎
</Text>

<Text fontSize={"14px"} color="light_grey" marginTop={"10px"}>
<Text fontSize={'14px'} color="light_grey" marginTop={'10px'}>
<b>
Join our Telegram group to get instant updates. Link on the
top.
@@ -146,16 +114,16 @@ export default function Strategies() {

function getStratCard(strat: StrategyInfo) {
return (
<Stack direction={{ base: "column", md: "row" }} width={"100%"}>
<Stack direction={{ base: 'column', md: 'row' }} width={'100%'}>
<LinkBox
width={{ base: "100%", md: "70%" }}
display={{ base: "flex", md: "flex" }}
width={{ base: '100%', md: '70%' }}
display={{ base: 'flex', md: 'flex' }}
onClick={() => {
mixpanel.track("Strategy expanded", { name: strat.name });
mixpanel.track('Strategy expanded', { name: strat.name });
}}
>
<Box width={"100%"}>
<AvatarGroup size="xs" max={2} marginRight={"5px"}>
<Box width={'100%'}>
<AvatarGroup size="xs" max={2} marginRight={'5px'}>
{getUniqueById(
strat.actions.map((p) => ({
id: p.pool.pool.name,
@@ -167,17 +135,17 @@ export default function Strategies() {
</AvatarGroup>
<Box>
<Heading
size={{ base: "sm", md: "md" }}
textAlign={"left"}
marginBottom={"5px"}
fontWeight={"bold"}
size={{ base: 'sm', md: 'md' }}
textAlign={'left'}
marginBottom={'5px'}
fontWeight={'bold'}
>
<LinkOverlay href={`/strategy?name=${strat.name}`}>
{strat.name}
</LinkOverlay>
</Heading>
<Heading
fontSize={{ base: "12px", md: "14px" }}
fontSize={{ base: '12px', md: '14px' }}
color="color1_light"
>
<Wrap>
@@ -187,15 +155,15 @@ export default function Strategies() {
logo: p.pool.protocol.logo,
})),
).map((p) => (
<WrapItem marginRight={"10px"} key={p.id}>
<WrapItem marginRight={'10px'} key={p.id}>
<Center>
<Avatar
size="2xs"
bg={"black"}
bg={'black'}
src={p.logo}
marginRight={"2px"}
marginRight={'2px'}
/>
<Text marginTop={"2px"}>{p.id}</Text>
<Text marginTop={'2px'}>{p.id}</Text>
</Center>
</WrapItem>
))}
@@ -205,26 +173,26 @@ export default function Strategies() {
</Box>
</LinkBox>
<Box
width={{ base: "100%", md: "30%" }}
marginTop={{ base: "10px", md: "0px" }}
width={{ base: '100%', md: '30%' }}
marginTop={{ base: '10px', md: '0px' }}
>
<Box width={"100%"} float="left" marginBottom={"5px"}>
<Box width={'100%'} float="left" marginBottom={'5px'}>
<Tooltip label="Includes fees & rewards earn from tokens shown. Click to know the investment proceduce.">
<Text
textAlign={"right"}
textAlign={'right'}
color="cyan"
fontWeight={"bold"}
float={{ base: "left", md: "right" }}
fontWeight={'bold'}
float={{ base: 'left', md: 'right' }}
>
{(strat.netYield * 100).toFixed(2)}%
</Text>
</Tooltip>
<AvatarGroup
size={{ base: "2xs", md: "xs" }}
size={{ base: '2xs', md: 'xs' }}
max={4}
spacing={"-6px"}
margin={{ base: "3px 5px 0", md: "0 5px" }}
float={{ base: "left", md: "right" }}
spacing={'-6px'}
margin={{ base: '3px 5px 0', md: '0 5px' }}
float={{ base: 'left', md: 'right' }}
>
{strat.rewardTokens.map((token) => (
<Avatar key={token.logo} src={token.logo} />
@@ -233,9 +201,9 @@ export default function Strategies() {
</Box>
<Tooltip label="Multiplier showing the additional reward earned compared to simple deposit">
<Text
textAlign={{ base: "left", md: "right" }}
textAlign={{ base: 'left', md: 'right' }}
width="100%"
float={"left"}
float={'left'}
color="color1_light"
>
{strat.leverage.toFixed(1)}x higher returns
@@ -250,30 +218,30 @@ export default function Strategies() {
);
}
return (
<Container width="100%" float={"left"} padding={"0px"} marginTop={"10px"}>
<Container width="100%" float={'left'} padding={'0px'} marginTop={'10px'}>
<Text
color="light_grey"
fontSize={"13px"}
marginBottom={"10px"}
fontFamily={"arial"}
fontSize={'13px'}
marginBottom={'10px'}
fontFamily={'arial'}
>
Strategies are combination of deposit & borrow actions that combine
various pools and risk combinations to maximize yield. We currently have
one High yield low risk strategy, and adding more as you read this.
</Text>
<Card
variant={"filled"}
variant={'filled'}
bg="opacity_50p"
color={"purple"}
display={{ base: "none", md: "visible" }}
color={'purple'}
display={{ base: 'none', md: 'visible' }}
>
<CardBody paddingTop={"5px"} paddingBottom={"5px"}>
<HStack width={"100%"}>
<Heading width={{ base: "70%" }} size="md">
<CardBody paddingTop={'5px'} paddingBottom={'5px'}>
<HStack width={'100%'}>
<Heading width={{ base: '70%' }} size="md">
Strategy
</Heading>
<Stack direction={{ base: "column" }} width={{ base: "30%" }}>
<Heading width={{ base: "100%" }} size="sm" textAlign={"right"}>
<Stack direction={{ base: 'column' }} width={{ base: '30%' }}>
<Heading width={{ base: '100%' }} size="sm" textAlign={'right'}>
APR(%)/Leverage
</Heading>
</Stack>
@@ -285,15 +253,15 @@ export default function Strategies() {
{strategies.map((strat, index) => (
<Card
key={`${strat.name}`}
variant={"filled"}
bg={index % 2 == 0 ? "color1_50p" : "color2_50p"}
variant={'filled'}
bg={index % 2 === 0 ? 'color1_50p' : 'color2_50p'}
color="white"
_hover={{
bg: index % 2 == 0 ? "color1_65p" : "color2_65p",
bg: index % 2 === 0 ? 'color1_65p' : 'color2_65p',
}}
>
<CardBody padding={{ base: "15px", md: "20px" }}>
<Box width={"100%"} padding={"0px 10px 0 0"}>
<CardBody padding={{ base: '15px', md: '20px' }}>
<Box width={'100%'} padding={'0px 10px 0 0'}>
{getStratCard(strat)}
</Box>
{/* {DepositButton(strat)} */}
@@ -302,14 +270,14 @@ export default function Strategies() {
))}
</Stack>
)}
{allPools.length > 0 && strategies.length == 0 && (
<Box padding="10px 0" width={"100%"} float={"left"}>
<Text color="light_grey" textAlign={"center"}>
{allPools.length > 0 && strategies.length === 0 && (
<Box padding="10px 0" width={'100%'} float={'left'}>
<Text color="light_grey" textAlign={'center'}>
No strategies. Check back soon.
</Text>
</Box>
)}
{allPools.length == 0 && (
{allPools.length === 0 && (
<Stack>
<Skeleton height="70px" />
</Stack>
1 change: 0 additions & 1 deletion src/components/TxButton.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import CONSTANTS from "@/constants";
import { Box, Button, ButtonProps, Spinner } from "@chakra-ui/react";
import {
UseContractWriteResult,
useAccount,
useContractWrite,
} from "@starknet-react/core";
33 changes: 16 additions & 17 deletions src/hooks/useERC4626Value.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import ERC20Abi from "@/abi/erc20.abi.json";
import ERC4626Abi from "@/abi/erc4626.abi.json";
import { TokenInfo } from "@/strategies/IStrategy";
import MyNumber from "@/utils/MyNumber";
import { useAccount, useContractRead } from "@starknet-react/core";
import { useEffect, useMemo } from "react";
import { useERC20Balance } from "./useERC20Balance";
import { TOKENS } from "@/constants";
import { standariseAddress } from "@/utils";
import { BlockTag, uint256 } from "starknet";
import ERC4626Abi from '@/abi/erc4626.abi.json';
import { TokenInfo } from '@/strategies/IStrategy';
import MyNumber from '@/utils/MyNumber';
import { useAccount, useContractRead } from '@starknet-react/core';
import { useEffect, useMemo } from 'react';
import { useERC20Balance } from './useERC20Balance';
import { TOKENS } from '@/constants';
import { standariseAddress } from '@/utils';
import { BlockTag, uint256 } from 'starknet';

export function useERC4626Value(token: TokenInfo | undefined) {
const { address } = useAccount();
@@ -23,10 +22,10 @@ export function useERC4626Value(token: TokenInfo | undefined) {
isLoading: isLoadingConvert,
error: errorBal,
} = useContractRead({
functionName: "convert_to_assets",
functionName: 'convert_to_assets',
args: [uint256.bnToUint256(balance.toString())],
abi: ERC4626Abi,
address: token?.token || "0x0",
address: token?.token || '0x0',
watch: true,
blockIdentifier: BlockTag.pending,
});
@@ -37,24 +36,24 @@ export function useERC4626Value(token: TokenInfo | undefined) {
isLoading: isLoadingAsset,
error: errorAsset,
} = useContractRead({
functionName: "asset",
functionName: 'asset',
args: [],
abi: ERC4626Abi,
address: token?.token || "0x0",
address: token?.token || '0x0',
watch: false,
});

const underlyingTokenInfo = useMemo(() => {
if (!underlyingAsset) return undefined;
return TOKENS.find(
(t) =>
standariseAddress(t.token) ==
standariseAddress(t.token) ===
standariseAddress(<bigint>underlyingAsset),
);
}, [underlyingAsset]);

const convertedBal = useMemo(() => {
console.log("4626balance", {
console.log('4626balance', {
address,
balance: balance.toString(),
underlyingBal,
@@ -97,7 +96,7 @@ export function useERC4626Value(token: TokenInfo | undefined) {
]);

useEffect(() => {
console.log("4626balance2", {
console.log('4626balance2', {
finalBal: convertedBal.balance.toEtherStr(),
});
}, [convertedBal]);
48 changes: 24 additions & 24 deletions src/store/IDapp.store.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import { AtomWithQueryResult } from "jotai-tanstack-query";
import { APRSplit, PoolInfo, PoolMetadata } from "./pools";
import { TokenName } from "@/constants";
import { StrategyAction } from "@/strategies/simple.stable.strat";
import { AtomWithQueryResult } from 'jotai-tanstack-query';
import { APRSplit, PoolInfo, PoolMetadata } from './pools';
import { TokenName } from '@/constants';
import { StrategyAction } from '@/strategies/simple.stable.strat';

export interface APRInfo {
asset: TokenName;
apr: number;
}

export class IDapp<BaseAPYT> {
name: string = "";
link: string = "";
logo: string = "";
name: string = '';
link: string = '';
logo: string = '';

incentiveDataKey: string = "";
incentiveDataKey: string = '';
_computePoolsInfo(data: any): PoolInfo[] {
throw new Error("not implemented: _computePoolsInfo");
throw new Error('not implemented: _computePoolsInfo');
}

addBaseAPYs<BaseAPYT>(
@@ -24,7 +24,7 @@ export class IDapp<BaseAPYT> {
): PoolInfo[] {
console.log(`lending: ${this.name}`, data);
if (data.isError) {
console.error("Error fetching lending base", data.error);
console.error('Error fetching lending base', data.error);
}
return pools.map((p) => {
const { baseAPY, splitApr, metadata } = this.getBaseAPY(p, <any>data);
@@ -34,7 +34,7 @@ export class IDapp<BaseAPYT> {
...p,
isLoading: data.isLoading,
aprSplits,
apr: baseAPY != "Err" ? p.apr + baseAPY : p.apr,
apr: baseAPY !== 'Err' ? p.apr + baseAPY : p.apr,
...metadata,
};
});
@@ -44,33 +44,33 @@ export class IDapp<BaseAPYT> {
p: PoolInfo,
data: AtomWithQueryResult<BaseAPYT, Error>,
): {
baseAPY: number | "Err";
baseAPY: number | 'Err';
splitApr: APRSplit | null;
metadata: PoolMetadata | null;
} {
throw new Error("not implemented: getBaseAPY");
throw new Error('not implemented: getBaseAPY');
}

getHF(positions: StrategyAction[]): { hf: number; isLiquidable: boolean } {
throw new Error("not implemented: getHF");
throw new Error('not implemented: getHF');
}

getMaxFactoredOut(positions: StrategyAction[], minHf: number): number {
throw new Error("not implemented: getMaxFactoredOut");
throw new Error('not implemented: getMaxFactoredOut');
}

commonVaultFilter(poolName: string) {
const supportedPools = [
"ETH/USDC",
"STRK/USDC",
"STRK/ETH",
"USDC/USDT",
"USDC",
"USDT",
"ETH",
"STRK",
'ETH/USDC',
'STRK/USDC',
'STRK/ETH',
'USDC/USDT',
'USDC',
'USDT',
'ETH',
'STRK',
];
console.log("filter", poolName, supportedPools.includes(poolName));
console.log('filter', poolName, supportedPools.includes(poolName));
// return !poolName.includes('DAI') && !poolName.includes('WSTETH') && !poolName.includes('BTC');
return supportedPools.includes(poolName);
}
55 changes: 26 additions & 29 deletions src/store/ekobu.store.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,24 @@
"use client";
'use client';

import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import CONSTANTS, { TokenName } from '@/constants';
import {
Category,
PoolInfo,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
} from "./pools";
import { PrimitiveAtom, atom } from "jotai";
import useSWR from "swr";
import { atomWithQuery } from "jotai-tanstack-query";
} from './pools';
import { atom } from 'jotai';
const fetcher = (...args: any[]) => {
return fetch(args[0], args[1]).then((res) => res.json());
};

export class Ekubo {
name = "Ekubo";
link = "https://app.ekubo.org/positions";
logo = "https://app.ekubo.org/logo.svg";
name = 'Ekubo';
link = 'https://app.ekubo.org/positions';
logo = 'https://app.ekubo.org/logo.svg';

incentiveDataKey = "Ekubo";
incentiveDataKey = 'Ekubo';
_computePoolsInfo(data: any) {
try {
const myData = data[this.incentiveDataKey];
@@ -32,13 +29,13 @@ export class Ekubo {
.forEach((poolName) => {
const arr = myData[poolName];
let category = Category.Others;
if (poolName == "USDC/USDT") {
if (poolName === 'USDC/USDT') {
category = Category.Stable;
} else if (poolName.includes("STRK")) {
} else if (poolName.includes('STRK')) {
category = Category.STRK;
}

const tokens: TokenName[] = <TokenName[]>poolName.split("/");
const tokens: TokenName[] = <TokenName[]>poolName.split('/');
const logo1 = CONSTANTS.LOGOS[tokens[0]];
const logo2 = CONSTANTS.LOGOS[tokens[1]];

@@ -57,13 +54,13 @@ export class Ekubo {
aprSplits: [
{
apr: 0,
title: "Base APR",
description: "Subject to position range",
title: 'Base APR',
description: 'Subject to position range',
},
{
apr: arr[arr.length - 1].apr,
title: "STRK rewards",
description: "Starknet DeFi Spring incentives",
title: 'STRK rewards',
description: 'Starknet DeFi Spring incentives',
},
],
category,
@@ -81,23 +78,23 @@ export class Ekubo {

return pools;
} catch (err) {
console.error("Error fetching pools", err);
console.error('Error fetching pools', err);
throw err;
}
}

commonVaultFilter(poolName: string) {
const supportedPools = [
"ETH/USDC",
"STRK/USDC",
"STRK/ETH",
"USDC/USDT",
"USDC",
"USDT",
"ETH",
"STRK",
'ETH/USDC',
'STRK/USDC',
'STRK/ETH',
'USDC/USDT',
'USDC',
'USDT',
'ETH',
'STRK',
];
console.log("filter2", poolName, supportedPools.includes(poolName));
console.log('filter2', poolName, supportedPools.includes(poolName));
// return !poolName.includes('DAI') && !poolName.includes('WSTETH') && !poolName.includes('BTC');
return supportedPools.includes(poolName);
}
@@ -108,7 +105,7 @@ const EkuboAtoms: ProtocolAtoms = {
pools: atom((get) => {
const poolsInfo = get(StrkDexIncentivesAtom);
const empty: PoolInfo[] = [];
console.log("ekubo", poolsInfo);
console.log('ekubo', poolsInfo);
if (poolsInfo.data) return ekubo._computePoolsInfo(poolsInfo.data);
return empty;
}),
7 changes: 1 addition & 6 deletions src/store/haiko.store.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
"use client";

import CONSTANTS from "@/constants";
import axios from "axios";
import {
Category,
PoolInfo,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
} from "./pools";
import { PrimitiveAtom, atom } from "jotai";
import useSWR from "swr";
import { atom } from "jotai";
import { Ekubo } from "./ekobu.store";

export class Haiko extends Ekubo {
34 changes: 15 additions & 19 deletions src/store/hashstack.store.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import CONSTANTS, { TokenName } from '@/constants';
import {
Category,
PoolInfo,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
StrkLendingIncentivesAtom,
} from "./pools";
import { Ekubo } from "./ekobu.store";
import { atom } from "jotai";
import { Jediswap } from "./jedi.store";
import { ZkLend } from "./zklend.store";
} from './pools';
import { atom } from 'jotai';
import { ZkLend } from './zklend.store';

export class Hashstack extends ZkLend {
name = "Hashstack";
link = "https://app.hashstack.finance/";
logo = "https://app.hashstack.finance/favicon-32x32.png";
name = 'Hashstack';
link = 'https://app.hashstack.finance/';
logo = 'https://app.hashstack.finance/favicon-32x32.png';

incentiveDataKey = "Hashstack";
incentiveDataKey = 'Hashstack';
SUPPLY_FACTOR = 0.7;
_computePoolsInfo(data: any) {
const myData = data[this.incentiveDataKey];
@@ -28,12 +24,12 @@ export class Hashstack extends ZkLend {
.filter(this.commonVaultFilter)
.forEach((poolName) => {
const arr = myData[poolName];
if (arr.length == 0) return;
if (arr.length === 0) return;

let category = Category.Others;
if (["USDC", "USDT"].includes(poolName)) {
if (['USDC', 'USDT'].includes(poolName)) {
category = Category.Stable;
} else if (poolName.includes("STRK")) {
} else if (poolName.includes('STRK')) {
category = Category.STRK;
}

@@ -54,13 +50,13 @@ export class Hashstack extends ZkLend {
aprSplits: [
{
apr: 0,
title: "Base APR",
description: "Shown soon",
title: 'Base APR',
description: 'Shown soon',
},
{
apr: arr[arr.length - 1].strk_grant_apr_nrs * this.SUPPLY_FACTOR,
title: "STRK rewards",
description: "Starknet DeFi Spring incentives",
title: 'STRK rewards',
description: 'Starknet DeFi Spring incentives',
},
],
category,
89 changes: 43 additions & 46 deletions src/store/jedi.store.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import CONSTANTS, { TokenName } from '@/constants';
import {
APRSplit,
Category,
@@ -8,35 +7,33 @@ import {
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
} from "./pools";
import { Ekubo } from "./ekobu.store";
import { atom } from "jotai";
import { IDapp } from "./IDapp.store";
import { AtomWithQueryResult, atomWithQuery } from "jotai-tanstack-query";
import { BlockInfo, getBlock } from "./utils.atoms";
import { stringify } from "querystring";
} from './pools';
import { atom } from 'jotai';
import { IDapp } from './IDapp.store';
import { AtomWithQueryResult, atomWithQuery } from 'jotai-tanstack-query';
import { BlockInfo, getBlock } from './utils.atoms';

interface MyBaseAprDoc {
id: string;
apr: number;
}

const PairInfo: any = {
"USDC/USDT":
"0x5801bdad32f343035fb242e98d1e9371ae85bc1543962fedea16c59b35bd19b",
"STRK/ETH":
"0x2ed66297d146ecd91595c3174da61c1397e8b7fcecf25d423b1ba6717b0ece9",
"ETH/USDC":
"0x4d0390b777b424e43839cd1e744799f3de6c176c7e32c1812a41dbd9c19db6a",
"STRK/USDC":
"0x5726725e9507c3586cc0516449e2c74d9b201ab2747752bb0251aaa263c9a26",
'USDC/USDT':
'0x5801bdad32f343035fb242e98d1e9371ae85bc1543962fedea16c59b35bd19b',
'STRK/ETH':
'0x2ed66297d146ecd91595c3174da61c1397e8b7fcecf25d423b1ba6717b0ece9',
'ETH/USDC':
'0x4d0390b777b424e43839cd1e744799f3de6c176c7e32c1812a41dbd9c19db6a',
'STRK/USDC':
'0x5726725e9507c3586cc0516449e2c74d9b201ab2747752bb0251aaa263c9a26',
};

export class Jediswap extends IDapp<string> {
name = "Jediswap (v1)";
link = "https://app.jediswap.xyz/#/pool";
logo = "https://app.jediswap.xyz/favicon/favicon-32x32.png";
incentiveDataKey = "Jediswap_v1";
name = 'Jediswap (v1)';
link = 'https://app.jediswap.xyz/#/pool';
logo = 'https://app.jediswap.xyz/favicon/favicon-32x32.png';
incentiveDataKey = 'Jediswap_v1';
_computePoolsInfo(data: any) {
try {
const myData = data[this.incentiveDataKey];
@@ -48,13 +45,13 @@ export class Jediswap extends IDapp<string> {
const arr = myData[poolName];
let category = Category.Others;

if (poolName == "USDC/USDT") {
if (poolName === 'USDC/USDT') {
category = Category.Stable;
} else if (poolName.includes("STRK")) {
} else if (poolName.includes('STRK')) {
category = Category.STRK;
}

const tokens: TokenName[] = <TokenName[]>poolName.split("/");
const tokens: TokenName[] = <TokenName[]>poolName.split('/');
const logo1 = CONSTANTS.LOGOS[tokens[0]];
const logo2 = CONSTANTS.LOGOS[tokens[1]];
const poolInfo: PoolInfo = {
@@ -72,8 +69,8 @@ export class Jediswap extends IDapp<string> {
aprSplits: [
{
apr: arr[arr.length - 1].apr,
title: "STRK rewards",
description: "Starknet DeFi Spring incentives",
title: 'STRK rewards',
description: 'Starknet DeFi Spring incentives',
},
],
category,
@@ -88,28 +85,28 @@ export class Jediswap extends IDapp<string> {
};
pools.push(poolInfo);
});
console.log("processed pools jedi", pools);
console.log('processed pools jedi', pools);
return pools;
} catch (err) {
console.error("Err fetching poools [2]", err);
console.error('Err fetching poools [2]', err);
throw err;
}
}

getBaseAPY(p: PoolInfo, data: AtomWithQueryResult<any, Error>) {
const aprData: MyBaseAprDoc[] = data.data;
let baseAPY: number | "Err" = "Err";
let baseAPY: number | 'Err' = 'Err';
let splitApr: APRSplit | null = null;
const metadata: PoolMetadata | null = null;
if (data.isSuccess) {
const pairId = PairInfo[p.pool.name];
const item = aprData.find((doc) => doc.id == pairId);
const item = aprData.find((doc) => doc.id === pairId);
if (item) {
baseAPY = item.apr;
splitApr = {
apr: baseAPY,
title: "Base APY",
description: "",
title: 'Base APY',
description: '',
};
}
}
@@ -122,7 +119,7 @@ export class Jediswap extends IDapp<string> {
}

async function getVolumes(block: number) {
let supportedPairs = "";
let supportedPairs = '';
Object.keys(PairInfo).forEach((pair: any) => {
supportedPairs += `"${PairInfo[pair]}",`;
});
@@ -142,10 +139,10 @@ async function getVolumes(block: number) {
});

const res = await fetch(CONSTANTS.JEDI.BASE_API, {
method: "POST",
method: 'POST',
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: data,
});
@@ -155,9 +152,9 @@ async function getVolumes(block: number) {
export const jedi = new Jediswap();
const JediAtoms: ProtocolAtoms = {
baseAPRs: atomWithQuery((get) => ({
queryKey: ["jedi_base_aprs"],
queryFn: async ({ queryKey: [] }) => {
console.log("jedi base");
queryKey: ['jedi_base_aprs'],
queryFn: async ({ queryKey }) => {
console.log('jedi base');
const nowSeconds = Math.round(new Date().getTime() / 1000) - 300; // giving enough time to have data
const NowMinus1DSeconds = nowSeconds - 86400;
const promsies: Promise<BlockInfo>[] = [
@@ -166,12 +163,12 @@ const JediAtoms: ProtocolAtoms = {
];
const [blockInfoNow, blockInfoMinus1D] = await Promise.all(promsies);

console.log("jedi base", { blockInfoNow, blockInfoMinus1D });
console.log('jedi base', { blockInfoNow, blockInfoMinus1D });
if (blockInfoNow && blockInfoMinus1D) {
const blockNow = blockInfoNow.data.blocks[0]?.number;
const blockMinus1D = blockInfoMinus1D.data.blocks[0]?.number;
if (blockNow && blockMinus1D) {
console.log("jedi base", "blocks", blockNow, blockMinus1D);
console.log('jedi base', 'blocks', blockNow, blockMinus1D);
const volumeNow = await getVolumes(blockNow);
const volumeMinus1D = await getVolumes(blockMinus1D);

@@ -180,8 +177,8 @@ const JediAtoms: ProtocolAtoms = {
volumeNow,
volumeMinus1D,
},
"jedi base",
"volumes",
'jedi base',
'volumes',
);

const aprData: { id: string; apr: number }[] =
@@ -194,11 +191,11 @@ const JediAtoms: ProtocolAtoms = {
// volumeUSD: "285564373.9797390817264734159690583"

const prevVolume = volumeMinus1D.data.pairs.find(
(p: any) => p.id == pair.id,
(p: any) => p.id === pair.id,
);
if (!prevVolume) {
console.error("prev vol not found", pair, volumeMinus1D);
throw new Error("prev vol not found");
console.error('prev vol not found', pair, volumeMinus1D);
throw new Error('prev vol not found');
}
const twnty4hrVol =
Number(pair.volumeUSD) - Number(prevVolume.volumeUSD);
7 changes: 1 addition & 6 deletions src/store/myswap.store.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
"use client";

import CONSTANTS from "@/constants";
import axios from "axios";
import {
Category,
PoolInfo,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
} from "./pools";
import { PrimitiveAtom, atom } from "jotai";
import useSWR from "swr";
import { atom } from "jotai";
import { Ekubo } from "./ekobu.store";
const fetcher = (...args: any[]) => {
return fetch(args[0], args[1]).then((res) => res.json());
7 changes: 0 additions & 7 deletions src/store/nimbora.store.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,9 @@
import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import {
Category,
PoolInfo,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
StrkLendingIncentivesAtom,
} from "./pools";
import { Ekubo } from "./ekobu.store";
import { atom } from "jotai";
import { Jediswap } from "./jedi.store";
import { ZkLend } from "./zklend.store";

export class Nimbora extends ZkLend {
5 changes: 0 additions & 5 deletions src/store/nostradex.store.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import {
Category,
PoolInfo,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
} from "./pools";
import { Ekubo } from "./ekobu.store";
import { atom } from "jotai";
import { Jediswap } from "./jedi.store";

66 changes: 29 additions & 37 deletions src/store/nostralending.store.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,14 @@
import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import CONSTANTS, { TokenName } from '@/constants';
import {
APRSplit,
Category,
PoolInfo,
PoolMetadata,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
StrkLendingIncentivesAtom,
} from "./pools";
import { Ekubo } from "./ekobu.store";
import { atom } from "jotai";
import { Jediswap } from "./jedi.store";
import { ZkLend } from "./zklend.store";
import { AtomWithQueryResult, atomWithQuery } from "jotai-tanstack-query";
import { IDapp } from "./IDapp.store";
import { number } from "starknet";
} from './pools';
import { atom } from 'jotai';
import { ZkLend } from './zklend.store';
import { AtomWithQueryResult, atomWithQuery } from 'jotai-tanstack-query';

interface MyBaseAprDoc {
_id: string;
@@ -40,57 +32,57 @@ interface NostraPoolFactor {
// ! to remove hard coding later
const PoolAddresses: { [token: string]: NostraPoolFactor } = {
USDC: {
asset: "0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8",
asset: '0x53c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8',
dToken:
"0x024e9b0d6bc79e111e6872bb1ada2a874c25712cf08dfc5bcf0de008a7cca55f",
'0x024e9b0d6bc79e111e6872bb1ada2a874c25712cf08dfc5bcf0de008a7cca55f',
borrowFactor: 0.95,
collateralFactor: 0.8,
},
STRK: {
asset: "0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d",
asset: '0x4718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',
dToken:
"0x001258eae3eae5002125bebf062d611a772e8aea3a1879b64a19f363ebd00947",
'0x001258eae3eae5002125bebf062d611a772e8aea3a1879b64a19f363ebd00947',
borrowFactor: 0.8,
collateralFactor: 0.6,
},
ETH: {
asset: "0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7",
asset: '0x49d36570d4e46f48e99674bd3fcc84644ddd6b96f7c741b1562b82f9e004dc7',
dToken:
"0x00ba3037d968790ac486f70acaa9a1cab10cf5843bb85c986624b4d0e5a82e74",
'0x00ba3037d968790ac486f70acaa9a1cab10cf5843bb85c986624b4d0e5a82e74',
borrowFactor: 0.9,
collateralFactor: 0.8,
},
USDT: {
asset: "0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8",
asset: '0x68f5c6a61780768455de69077e07e89787839bf8166decfbf92b645209c0fb8',
dToken:
"0x024e9b0d6bc79e111e6872bb1ada2a874c25712cf08dfc5bcf0de008a7cca55f",
'0x024e9b0d6bc79e111e6872bb1ada2a874c25712cf08dfc5bcf0de008a7cca55f',
borrowFactor: 0.95,
collateralFactor: 0.8,
},
};

export class NostraLending extends ZkLend {
name = "Nostra MM";
link = "https://app.nostra.finance/";
logo = "https://app.nostra.finance/favicon.svg";
name = 'Nostra MM';
link = 'https://app.nostra.finance/';
logo = 'https://app.nostra.finance/favicon.svg';

incentiveDataKey = "Nostra";
incentiveDataKey = 'Nostra';

getBaseAPY(p: PoolInfo, data: AtomWithQueryResult<any, Error>) {
let baseAPY: number | "Err" = "Err";
let baseAPY: number | 'Err' = 'Err';
let splitApr: APRSplit | null = null;
let metadata: PoolMetadata | null = null;
if (data.isSuccess) {
const items: {
documents: MyBaseAprDoc[];
} = data.data;
const item = items.documents.find((doc) => doc.asset == p.pool.name);
const item = items.documents.find((doc) => doc.asset === p.pool.name);
if (item) {
baseAPY = Number(item.lendingApy) / 10 ** 18;
splitApr = {
apr: baseAPY,
title: "Base APY",
description: "",
title: 'Base APY',
description: '',
};
metadata = {
borrow: {
@@ -114,18 +106,18 @@ export class NostraLending extends ZkLend {
export const nostraLending = new NostraLending();
const NostraLendingAtoms: ProtocolAtoms = {
baseAPRs: atomWithQuery((get) => ({
queryKey: ["nostra_lending_base_aprs"],
queryFn: async ({ queryKey: [] }) => {
queryKey: ['nostra_lending_base_aprs'],
queryFn: async ({ queryKey }) => {
const res = await fetch(CONSTANTS.NOSTRA.LENDING_GRAPH_URL, {
method: "POST",
method: 'POST',
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
dataSource: "nostra-production",
database: "prod-a-nostra-db",
collection: "apyStats",
dataSource: 'nostra-production',
database: 'prod-a-nostra-db',
collection: 'apyStats',
filter: { timestamp: { $gte: 1697500800 } },
sort: { timestamp: -1 },
}),
74 changes: 36 additions & 38 deletions src/store/pools.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,35 @@
import CONSTANTS from "@/constants";
import BigNumber from "bignumber.js";
import { Atom, PrimitiveAtom, atom } from "jotai";
import { AtomWithQueryResult, atomWithQuery } from "jotai-tanstack-query";
import EkuboAtoms, { ekubo } from "./ekobu.store";
import JediAtoms, { jedi } from "./jedi.store";
import MySwapAtoms, { mySwap } from "./myswap.store";
import TenkSwapAtoms, { TenkSwap, tenkswap } from "./tenkswap.store";
import HaikoAtoms, { haiko } from "./haiko.store";
import StarkDefiAtoms, { starkDefi } from "./starkdefi.store";
import NostraDexAtoms, { nostraDex } from "./nostradex.store";
import SithswapAtoms, { sithswap } from "./sithswap.store";
import ZkLendAtoms, { zkLend } from "./zklend.store";
import CONSTANTS from '@/constants';
import { Atom, atom } from 'jotai';
import { AtomWithQueryResult, atomWithQuery } from 'jotai-tanstack-query';
import EkuboAtoms, { ekubo } from './ekobu.store';
import HaikoAtoms, { haiko } from './haiko.store';
import HashstackAtoms, { hashstack } from './hashstack.store';
import JediAtoms, { jedi } from './jedi.store';
import MySwapAtoms, { mySwap } from './myswap.store';
import NimboraAtoms, { nimbora } from './nimbora.store';
import NostraDexAtoms, { nostraDex } from './nostradex.store';
import NostraLendingAtoms, {
NostraLending,
nostraLending,
} from "./nostralending.store";
import HashstackAtoms, { hashstack } from "./hashstack.store";
import NimboraAtoms, { nimbora } from "./nimbora.store";
} from './nostralending.store';
import SithswapAtoms, { sithswap } from './sithswap.store';
import StarkDefiAtoms, { starkDefi } from './starkdefi.store';
import TenkSwapAtoms, { tenkswap } from './tenkswap.store';
import ZkLendAtoms, { zkLend } from './zklend.store';

export enum Category {
Stable = "Stable Pools",
STRK = "STRK Pools",
Others = "Others",
Stable = 'Stable Pools',
STRK = 'STRK Pools',
Others = 'Others',
}

export enum PoolType {
DEXV2 = "V2 LP DEX",
DEXV3 = "Concentrated LP DEX",
Lending = "Lending",
DEXV2 = 'V2 LP DEX',
DEXV3 = 'Concentrated LP DEX',
Lending = 'Lending',
}

export interface APRSplit {
apr: number | "Err";
apr: number | 'Err';
title: string;
description: string;
}
@@ -133,26 +131,26 @@ export const PROTOCOLS = [
];

export const StrkDexIncentivesAtom = atomWithQuery((get) => ({
queryKey: ["strk_dex_incentives"],
queryFn: async ({ queryKey: [] }) => {
queryKey: ['strk_dex_incentives'],
queryFn: async ({ queryKey }) => {
const res = await fetch(CONSTANTS.DEX_INCENTIVE_URL); // common for all
let data = await res.text();
data = data.replaceAll("NaN", "0");
data = data.replaceAll('NaN', '0');
return JSON.parse(data);
},
}));

export const StrkLendingIncentivesAtom = atomWithQuery((get) => ({
queryKey: ["strk_lending_incentives"],
queryFn: async ({ queryKey: [] }) => {
queryKey: ['strk_lending_incentives'],
queryFn: async ({ queryKey }) => {
const res = await fetch(CONSTANTS.LENDING_INCENTIVES_URL); // common for all
let data = await res.text();
data = data.replaceAll("NaN", "0");
data = data.replaceAll('NaN', '0');
return JSON.parse(data);
},
}));

export const ALL_FILTER = "All";
export const ALL_FILTER = 'All';
export const filters = {
categories: [...Object.values(Category)],
types: [...Object.values(PoolType)],
@@ -170,14 +168,14 @@ export const updateFiltersAtom = atom(
(
get,
set,
type: "categories" | "poolTypes" | "protocols",
type: 'categories' | 'poolTypes' | 'protocols',
newOptions: string[],
) => {
if (type == "categories") {
if (type === 'categories') {
set(filterAtoms.categoriesAtom, newOptions);
} else if (type == "poolTypes") {
} else if (type === 'poolTypes') {
set(filterAtoms.typesAtom, newOptions);
} else if (type == "protocols") {
} else if (type === 'protocols') {
set(filterAtoms.protocolsAtom, newOptions);
}
},
@@ -193,16 +191,16 @@ export const allPoolsAtomUnSorted = atom((get) => {

// const allPoolsAtom = atom<PoolInfo[]>([]);

const SORT_OPTIONS = ["APR", "TVL"];
const SORT_OPTIONS = ['APR', 'TVL'];

export const sortAtom = atom(SORT_OPTIONS[0]);

export const sortPoolsAtom = atom((get) => {
const pools = get(allPoolsAtomUnSorted);
console.log("pre sort", pools);
console.log('pre sort', pools);
const sortOption = get(sortAtom);
pools.sort((a, b) => {
if (sortOption == SORT_OPTIONS[1]) {
if (sortOption === SORT_OPTIONS[1]) {
return b.tvl - a.tvl;
}
return b.apr - a.apr;
5 changes: 0 additions & 5 deletions src/store/sithswap.store.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import {
Category,
PoolInfo,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
} from "./pools";
import { Ekubo } from "./ekobu.store";
import { atom } from "jotai";
import { Jediswap } from "./jedi.store";

5 changes: 0 additions & 5 deletions src/store/starkdefi.store.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import {
Category,
PoolInfo,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
} from "./pools";
import { Ekubo } from "./ekobu.store";
import { atom } from "jotai";
import { Jediswap } from "./jedi.store";

35 changes: 16 additions & 19 deletions src/store/strategies.atoms.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
import {
SimpleStableStrategy,
StrategyAction,
} from "@/strategies/simple.stable.strat";
import { atom } from "jotai";
import { allPoolsAtomUnSorted } from "./pools";
import { AutoTokenStrategy } from "@/strategies/auto_strk.strat";
} from '@/strategies/simple.stable.strat';
import { atom } from 'jotai';
import { allPoolsAtomUnSorted } from './pools';
import { AutoTokenStrategy } from '@/strategies/auto_strk.strat';
import {
IStrategy,
IStrategyProps,
StrategyStatus,
} from "@/strategies/IStrategy";
import CONSTANTS from "@/constants";
} from '@/strategies/IStrategy';
import CONSTANTS from '@/constants';

export interface StrategyInfo extends IStrategyProps {
name: string;
@@ -19,34 +16,34 @@ export interface StrategyInfo extends IStrategyProps {
export const strategiesAtom = atom<StrategyInfo[]>((get) => {
const simpleStableStrat = new SimpleStableStrategy();
const autoStrkStrategy = new AutoTokenStrategy(
"STRK",
'STRK',
"Stake your STRK or zkLend's zSTRK token to receive biweekly DeFi Spring $STRK rewards. The strategy auto-collects your rewards and re-invests them in the zkLend STRK pool, giving you higher return through compounding. You receive frmzSTRK LP token as representation for your stake on STRKFarm. You can withdraw anytime by redeeming your frmzSTRK for zSTRK",
"zSTRK",
'zSTRK',
CONSTANTS.CONTRACTS.AutoStrkFarm,
);
const autoUSDCStrategy = new AutoTokenStrategy(
"USDC",
'USDC',
"Stake your USDC or zkLend's zUSDC token to receive biweekly DeFi Spring $STRK rewards. The strategy auto-collects your $STRK rewards, swaps them to USDC and re-invests them in the zkLend USDC pool, giving you higher return through compounding. You receive frmzUSDC LP token as representation for your stake on STRKFarm. You can withdraw anytime by redeeming your frmzUSDC for zUSDC",
"zUSDC",
'zUSDC',
CONSTANTS.CONTRACTS.AutoUsdcFarm,
);

const allPools = get(allPoolsAtomUnSorted);
const filteredPools = allPools.filter(
(p) => p.protocol.name == "zkLend" || p.protocol.name == "Nostra MM",
(p) => p.protocol.name === 'zkLend' || p.protocol.name === 'Nostra MM',
);

const strategies: StrategyInfo[] = [];

simpleStableStrat.solve(filteredPools, "1000");
autoStrkStrategy.solve(filteredPools, "1000");
autoUSDCStrategy.solve(filteredPools, "1000");
simpleStableStrat.solve(filteredPools, '1000');
autoStrkStrategy.solve(filteredPools, '1000');
autoUSDCStrategy.solve(filteredPools, '1000');
strategies.push({
name: "Auto Compounding STRK",
name: 'Auto Compounding STRK',
...autoStrkStrategy,
});
strategies.push({
name: "Auto Compounding USDC",
name: 'Auto Compounding USDC',
...autoUSDCStrategy,
});
// strategies.push({
5 changes: 0 additions & 5 deletions src/store/tenkswap.store.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import {
Category,
PoolInfo,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
} from "./pools";
import { Ekubo } from "./ekobu.store";
import { atom } from "jotai";
import { Jediswap } from "./jedi.store";

51 changes: 25 additions & 26 deletions src/store/utils.atoms.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import CONSTANTS from "@/constants";
import { atomWithQuery } from "jotai-tanstack-query";
import { unstable_renderSubtreeIntoContainer } from "react-dom";
import { atomWithStorage, createJSONStorage } from "jotai/utils";
import CONSTANTS from '@/constants';
import { atomWithQuery } from 'jotai-tanstack-query';
import { atomWithStorage, createJSONStorage } from 'jotai/utils';

export interface BlockInfo {
data: {
blocks: {
id: string;
number: number;
timestamp: string; // "2024-03-15T08:54:05",
__typename: "Block";
__typename: 'Block';
}[];
};
}
@@ -30,20 +29,20 @@ export async function getBlock(
}`,
variables: {},
});
console.log("jedi base", "data", data);
console.log('jedi base', 'data', data);
const res = await fetch(CONSTANTS.JEDI.BASE_API, {
method: "POST",
method: 'POST',
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: data,
});
const blockInfo = await res.json();
console.log("jedi base data", blockInfo, tSeconds);
console.log('jedi base data', blockInfo, tSeconds);
return blockInfo;
} catch (err) {
console.log("err", err);
console.log('err', err);
if (retry < 3) {
await new Promise((res) => setTimeout(res, 2000));
return await getBlock(tSeconds, retry + 1);
@@ -53,20 +52,20 @@ export async function getBlock(
}

export const blockInfoNowAtom = atomWithQuery((get) => ({
queryKey: ["block_now"],
queryFn: async ({ queryKey: [] }): Promise<BlockInfo> => {
console.log("jedi base", "block now");
queryKey: ['block_now'],
queryFn: async ({ queryKey }): Promise<BlockInfo> => {
console.log('jedi base', 'block now');
const nowSeconds = Math.round(new Date().getTime() / 1000);
const res = await getBlock(nowSeconds);
console.log("jedi base", "data2", res);
console.log('jedi base', 'data2', res);
return res;
},
}));

export const blockInfoMinus1DAtom = atomWithQuery((get) => ({
queryKey: ["block_minus_1d"],
queryFn: async ({ queryKey: [] }) => {
console.log("jedi base", "block_minus_1d");
queryKey: ['block_minus_1d'],
queryFn: async ({ queryKey }) => {
console.log('jedi base', 'block_minus_1d');
const nowSeconds = Math.round(new Date().getTime() / 1000);
const NowMinus1DSeconds = nowSeconds - 86400;
const data = JSON.stringify({
@@ -80,26 +79,26 @@ export const blockInfoMinus1DAtom = atomWithQuery((get) => ({
}`,
variables: {},
});
console.log("jedi base", "data", data);
console.log('jedi base', 'data', data);
const res = await fetch(CONSTANTS.JEDI.BASE_API, {
method: "POST",
method: 'POST',
headers: {
Accept: "application/json",
"Content-Type": "application/json",
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: data,
});
console.log("jedi base", "data2", res.json());
console.log('jedi base', 'data2', res.json());
return res.json();
},
}));

const ISSERVER = typeof window === "undefined";
const ISSERVER = typeof window === 'undefined';
declare let localStorage: any;

export type WalletName = "Braavos" | "Argent X";
export type WalletName = 'Braavos' | 'Argent X';
export const lastWalletAtom = atomWithStorage<null | WalletName>(
"lastWallet",
'lastWallet',
null,
{
...createJSONStorage(() => {
53 changes: 25 additions & 28 deletions src/store/zklend.store.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
"use client";
'use client';

import CONSTANTS, { TokenName } from "@/constants";
import axios from "axios";
import CONSTANTS, { TokenName } from '@/constants';
import {
APRSplit,
Category,
PoolInfo,
PoolMetadata,
PoolType,
ProtocolAtoms,
StrkDexIncentivesAtom,
StrkLendingIncentivesAtom,
} from "./pools";
import { PrimitiveAtom, atom } from "jotai";
import useSWR from "swr";
import { AtomWithQueryResult, atomWithQuery } from "jotai-tanstack-query";
import { IDapp } from "./IDapp.store";
import { StrategyAction } from "@/strategies/simple.stable.strat";
} from './pools';
import { atom } from 'jotai';
import { AtomWithQueryResult, atomWithQuery } from 'jotai-tanstack-query';
import { IDapp } from './IDapp.store';
import { StrategyAction } from '@/strategies/simple.stable.strat';
const fetcher = (...args: any[]) => {
return fetch(args[0], args[1]).then((res) => res.json());
};
@@ -54,11 +51,11 @@ interface MyBaseAprDoc {
}

export class ZkLend extends IDapp<MyBaseAprDoc[]> {
name = "zkLend";
link = "https://app.zklend.com/markets";
logo = "https://app.zklend.com/favicon.ico";
name = 'zkLend';
link = 'https://app.zklend.com/markets';
logo = 'https://app.zklend.com/favicon.ico';

incentiveDataKey = "zkLend";
incentiveDataKey = 'zkLend';
LIQUIDATION_THRESHOLD = 1;
_computePoolsInfo(data: any) {
const myData = data[this.incentiveDataKey];
@@ -68,12 +65,12 @@ export class ZkLend extends IDapp<MyBaseAprDoc[]> {
.filter(this.commonVaultFilter)
.forEach((poolName) => {
const arr = myData[poolName];
if (arr.length == 0) return;
if (arr.length === 0) return;

let category = Category.Others;
if (["USDC", "USDT"].includes(poolName)) {
if (['USDC', 'USDT'].includes(poolName)) {
category = Category.Stable;
} else if (poolName.includes("STRK")) {
} else if (poolName.includes('STRK')) {
category = Category.STRK;
}

@@ -94,8 +91,8 @@ export class ZkLend extends IDapp<MyBaseAprDoc[]> {
aprSplits: [
{
apr: arr[arr.length - 1].strk_grant_apr_nrs,
title: "STRK rewards",
description: "Starknet DeFi Spring incentives",
title: 'STRK rewards',
description: 'Starknet DeFi Spring incentives',
},
],
category,
@@ -115,28 +112,28 @@ export class ZkLend extends IDapp<MyBaseAprDoc[]> {
}

getBaseAPY(p: PoolInfo, data: AtomWithQueryResult<MyBaseAprDoc[], Error>) {
let baseAPY: number | "Err" = "Err";
let baseAPY: number | 'Err' = 'Err';
let splitApr: APRSplit | null = null;
let metadata: PoolMetadata | null = null;
if (data.isSuccess) {
const item = data.data.find((doc) => doc.token.symbol == p.pool.name);
const item = data.data.find((doc) => doc.token.symbol === p.pool.name);
if (item) {
baseAPY = item.lending_apy.raw_apy;
splitApr = {
apr: baseAPY,
title: "Base APY",
description: "",
title: 'Base APY',
description: '',
};
metadata = {
borrow: {
apr: item.borrowing_apy.net_apy,
borrowFactor:
parseInt(item.borrow_factor.value) /
parseInt(item.borrow_factor.value, 10) /
10 ** item.borrow_factor.decimals,
},
lending: {
collateralFactor:
parseInt(item.collateral_factor.value) /
parseInt(item.collateral_factor.value, 10) /
10 ** item.collateral_factor.decimals,
},
};
@@ -165,7 +162,7 @@ export class ZkLend extends IDapp<MyBaseAprDoc[]> {
});

let hf = Number.MAX_SAFE_INTEGER; // if not debt, i.e. denominator 0;
if (denominator != 0) {
if (denominator !== 0) {
hf = numerator / denominator;
}

@@ -197,8 +194,8 @@ export class ZkLend extends IDapp<MyBaseAprDoc[]> {
export const zkLend = new ZkLend();
const ZkLendAtoms: ProtocolAtoms = {
baseAPRs: atomWithQuery((get) => ({
queryKey: ["zklend_lending_base_aprs"],
queryFn: async ({ queryKey: [] }) => {
queryKey: ['zklend_lending_base_aprs'],
queryFn: async ({ queryKey }) => {
const res = await fetch(CONSTANTS.ZKLEND.BASE_APR_API);
return res.json();
},
Loading