Skip to content

Commit

Permalink
Lend and Borrow (#2891)
Browse files Browse the repository at this point in the history
* lending page

* sort by borrow amount

* lend and borrow improvements

* add back midas
  • Loading branch information
brightiron authored Jul 24, 2023
1 parent 08ecc85 commit 0ab8999
Show file tree
Hide file tree
Showing 8 changed files with 746 additions and 21 deletions.
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ import { girth as gTheme } from "src/themes/girth.js";
import { light as lightTheme } from "src/themes/light.js";
import { BondModalContainer } from "src/views/Bond/components/BondModal/BondModal";
import { BondModalContainerV3 } from "src/views/Bond/components/BondModal/BondModalContainerV3";
import { Lending } from "src/views/Lending";
import { LendingMarkets } from "src/views/Lending/LendingMarkets";
import { Liquidity } from "src/views/Liquidity";
import { ExternalStakePools } from "src/views/Liquidity/ExternalStakePools/ExternalStakePools";
import { Vault } from "src/views/Liquidity/Vault";
Expand Down Expand Up @@ -246,6 +248,8 @@ function App() {
<Route path="/bridge" element={<Bridge />} />
<Route path="/dashboard/*" element={<TreasuryDashboard />} />
<Route path="/range/*" element={<Range />} />
<Route path="/lending" element={<Lending />} />
<Route path="/lending/markets" element={<LendingMarkets />} />
<Route path="/liquidity" element={<Liquidity />} />
<Route path="/liquidity/pools" element={<ExternalStakePools />} />
<Route path="/liquidity/vaults" element={<Vaults />} />
Expand Down
20 changes: 20 additions & 0 deletions src/assets/icons/lendAndBorrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 16 additions & 2 deletions src/components/Sidebar/NavContent.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { Box, Divider, Link, Paper, SvgIcon, Typography, useTheme } from "@mui/material";
import { styled } from "@mui/material/styles";
import { Icon, NavItem } from "@olympusdao/component-library";
import { Icon } from "@olympusdao/component-library";
import React from "react";
import { NavLink } from "react-router-dom";
import { ReactComponent as lendAndBorrowIcon } from "src/assets/icons/lendAndBorrow.svg";
import { ReactComponent as OlympusIcon } from "src/assets/icons/olympus-nav-header.svg";
import NavItem from "src/components/library/NavItem";
import { DecimalBigNumber } from "src/helpers/DecimalBigNumber/DecimalBigNumber";
import { useTestableNetworks } from "src/hooks/useTestableNetworks";
import { BondDiscount } from "src/views/Bond/components/BondDiscount";
Expand Down Expand Up @@ -54,6 +56,11 @@ const NavContent: React.VFC = () => {
<Divider sx={{ borderColor: theme.colors.gray[600] }} />
</Box>
<NavItem to="/stake" icon="stake" label={`Stake`} />
<NavItem
customIcon={<SvgIcon component={lendAndBorrowIcon} />}
label={`Lend & Borrow`}
to="/lending"
/>
<NavItem icon="settings" label={`Provide Liquidity`} to="/liquidity" />
<NavItem to="/range" icon="range" label={`Range`} defaultExpanded={false}>
<RangePrice bidOrAsk="ask" />
Expand All @@ -62,7 +69,14 @@ const NavContent: React.VFC = () => {
<NavItem href="https://vote.olympusdao.finance/" icon="voting" label={`Governance`} />
</>
) : (
<NavItem icon="settings" label={`Provide Liquidity`} to="/liquidity" />
<>
<NavItem
customIcon={<SvgIcon component={lendAndBorrowIcon} />}
label={`Lend & Borrow`}
to="/lending"
/>
<NavItem icon="settings" label={`Provide Liquidity`} to="/liquidity" />
</>
)}

<Box className="menu-divider">
Expand Down
173 changes: 173 additions & 0 deletions src/components/library/NavItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
import { Box, Link, LinkProps } from "@mui/material";
import { styled } from "@mui/material/styles";
import { Accordion, Chip, Icon, OHMChipProps } from "@olympusdao/component-library";
import { IconName } from "@olympusdao/component-library/lib/components/Icon";
import { FC, ReactNode } from "react";
import { NavLink, useLocation } from "react-router-dom";

const PREFIX = "NavItem";

const classes = {
root: `${PREFIX}-root`,
title: `${PREFIX}-title`,
};

const Root = styled("div", { shouldForwardProp: prop => prop !== "match" })<MatchProps>(({ theme, match }) => ({
[`&.${classes.root}`]: {
alignItems: "center",
marginBottom: "12px",
"& .link-container": {
paddingRight: "12px",
},
"& a.active": {
"& .link-container": {
backgroundColor: theme.colors.gray[600],
},
textDecoration: "none",
},
"& .MuiAccordion-root": {
background: "transparent",
"& .MuiAccordionDetails-root a.active .activePill": {
marginLeft: "-35px",
marginRight: "35px",
},
"& .MuiAccordionSummary-expandIconWrapper": {
padding: "0px 18px",
},
},
"& .MuiAccordion-root &:last-child": {
paddingBottom: "0px",
},
"& .MuiAccordionSummary-root": {
"&.Mui-expanded": {
marginBottom: "6px",
},
"& a.active .link-container": {
backgroundColor: theme.colors.gray[600],
marginRight: "-48px",
},
},

"& .MuiAccordionDetails-root": {
"& .link-container .title": {
fontSize: "13px",
lineHeight: 1,
},
"& a.active .link-container": {
backgroundColor: theme.colors.gray[600],
},
paddingLeft: "20px",
display: "block",
"& .nav-item-container": {
paddingTop: "3px",
paddingBottom: "3px",
paddingRight: "0px",
},
},

"& svg": {
marginRight: "12px",
},
"& svg.accordion-arrow": {
marginRight: "0px",
},
"& .external-site-link": {
"& .external-site-link-icon": {
opacity: "0",
},
"&:hover .external-site-link-icon": {
marginLeft: "5px",
opacity: "1",
},
},
},

[`& .${classes.title}`]: {
lineHeight: "33px",
paddingLeft: "12px",
paddingTop: "3px",
paddingBottom: "3px",
fontSize: "15px",
},
}));

interface MatchProps {
match: boolean;
}

export interface OHMNavItemProps extends LinkProps {
label: string;
customIcon?: ReactNode;
icon?: IconName;
chip?: string | ReactNode;
className?: string;
to?: any;
/**Will Override to prop. Used for External Links */
href?: string;
children?: ReactNode;
defaultExpanded?: boolean;
chipColor?: OHMChipProps["template"];
}

/**
* Primary NavItem Component for UI.
*/
const NavItem: FC<OHMNavItemProps> = ({
chip,
className = "",
customIcon,
icon,
label,
to,
children,
defaultExpanded = true,
chipColor,
...props
}) => {
const currentLocation = useLocation();
const match = currentLocation.pathname === to || currentLocation.pathname === `/${to}`;

const linkProps = props.href
? {
href: props.href,
target: "_blank",
className: `external-site-link ${className}`,
}
: {
component: NavLink,
to: to,
className: `button-dapp-menu ${className}`,
};
const LinkItem = () => (
<Link {...linkProps} {...props} underline="hover">
<Box
display="flex"
flexDirection="row"
alignItems="center"
alignContent="center"
justifyContent="space-around"
className="link-container"
>
<Box display="flex" width={"100%"} alignItems="center" className={`${classes.title} title`}>
{customIcon ? customIcon : icon && <Icon name={icon} style={{ fontSize: "21px" }} />}
{label}
{props.href && <Icon className="external-site-link-icon" name="arrow-up" />}
</Box>
{chip && <Chip size="small" label={chip} template={chipColor} />}
</Box>
</Link>
);
return (
<Root className={`${classes.root} nav-item-container`} match={match}>
{children ? (
<Accordion defaultExpanded={defaultExpanded} arrowOnlyCollapse summary={<LinkItem />}>
{children}
</Accordion>
) : (
<LinkItem />
)}
</Root>
);
};

export default NavItem;
57 changes: 38 additions & 19 deletions src/hooks/useGetLPStats.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
import { useQuery } from "@tanstack/react-query";
import axios from "axios";

export const useGetLPStats = () => {
const defillamaAPI = "https://yields.llama.fi/pools";
const { data, isFetched, isLoading } = useQuery(["GetLPStats"], async () => {
return await axios.get<defillamaAPI>(defillamaAPI).then(res => {
return res.data.data
.filter(
pool =>
pool.symbol.split("-")[0] === "OHM" ||
pool.symbol.split("-")[0] === "GOHM" ||
pool.symbol.split("-")[1] === "GOHM" ||
pool.symbol.split("-")[1] === "OHM" ||
pool.symbol.split("-")[0] === "OHMFRAXBP",
)
.filter(pool => pool.apy !== 0 && pool.exposure !== "single")
.map(pool => {
return { ...pool, ...mapProjectToName(pool.project), id: pool.pool };
});
});
export const useGetLPStats = (exposure?: string) => {
const { data, isFetched, isLoading } = useQuery(["GetLPStats", exposure], async () => {
const ohmPools = await getOhmPools();

const ohmPoolsFilters = ohmPools.filter(pool => pool.exposure === exposure || "multi");
return ohmPoolsFilters;
});

return { data, isFetched, isLoading };
};

const mapProjectToName = (project: string) => {
export const getOhmPools = async () => {
const defillamaAPI = "https://yields.llama.fi/pools";
return await axios.get<defillamaAPI>(defillamaAPI).then(res => {
return res.data.data
.filter(
pool =>
pool.symbol.split("-")[0] === "OHM" ||
pool.symbol.split("-")[0] === "GOHM" ||
pool.symbol.split("-")[1] === "GOHM" ||
pool.symbol.split("-")[1] === "OHM" ||
pool.symbol.split("-")[0] === "OHMFRAXBP",
)
.map(pool => {
return { ...pool, ...mapProjectToName(pool.project), id: pool.pool };
});
});
};

export const mapProjectToName = (project: string) => {
switch (project) {
case "balancer-v2":
return { projectName: "Balancer", projectLink: "https://app.balancer.fi/" };
Expand Down Expand Up @@ -67,6 +73,19 @@ const mapProjectToName = (project: string) => {
return { projectName: "Ramses", projectLink: "https://app.ramses.exchange/liquidity" };
case "chronos":
return { projectName: "Chronos", projectLink: "https://app.chronos.exchange/liquidity" };
case "sentiment":
return { projectName: "Sentiment", projectLink: "https://arbitrum.sentiment.xyz/borrow/OHM?symbol=USDC.e" };
case "midas-capital":
return { projectName: "Midas Capital", projectLink: "https://app.midascapital.xyz/42161/pool/1" };
case "silo-finance":
return { projectName: "Silo Finance", projectLink: "https://app.silo.finance/" };
case "inverse-finance-firm":
return { projectName: "Inverse Finance", projectLink: "https://www.inverse.finance/firm" };
case "fraxlend":
return {
projectName: "Frax",
projectLink: "https://app.frax.finance/fraxlend/pair?address=0x66bf36dBa79d4606039f04b32946A260BCd3FF52",
};
default:
return { projectName: project, projectLink: "" };
}
Expand Down
41 changes: 41 additions & 0 deletions src/hooks/useGetLendBorrowStats.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useQuery } from "@tanstack/react-query";
import axios from "axios";
import { DefiLlamaPool, getOhmPools } from "src/hooks/useGetLPStats";

type LendAndBorrow = {
apyBaseBorrow: number;
apyRewardBorrow: number;
borrowFactor: number | null;
debtCeilingUsd: number | null;
ltv: number;
mintedCoin: string | null;
pool: string;
rewardTokens: string[];
totalBorrowUsd: number;
totalSupplyUsd: number;
underlyingTokens: string[];
};
export const useGetLendAndBorrowStats = () => {
const defillamaAPI = "https://yields.llama.fi/lendBorrow";
const { data, isFetched, isLoading } = useQuery(["GetLendBorrowStats"], async () => {
const ohmPools = await getOhmPools();
console.log(ohmPools, "ohmPools");
const lendAndBorrowPools = await axios.get<LendAndBorrow[]>(defillamaAPI).then(res => {
return res.data;
});
const ohmLendAndBorrowPools = ohmPools.filter(pool =>
lendAndBorrowPools.some(lendAndBorrowPool => lendAndBorrowPool.pool === pool.id),
);
const poolsAndLendBorrowStats = ohmLendAndBorrowPools.map(pool => {
const lendBorrowPool = lendAndBorrowPools.find(lendAndBorrowPool => lendAndBorrowPool.pool === pool.id);
return { ...pool, lendAndBorrow: lendBorrowPool };
});
return poolsAndLendBorrowStats;
});

return { data, isFetched, isLoading };
};

export interface LendAndBorrowPool extends DefiLlamaPool {
lendAndBorrow: LendAndBorrow;
}
Loading

0 comments on commit 0ab8999

Please sign in to comment.