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

(WIP) Initial work on growth visualization #62

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
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
15 changes: 8 additions & 7 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"arrowParens": "avoid",
"bracketSpacing": true,
"printWidth": 180,
"singleQuote": false,
"tabWidth": 4,
"trailingComma": "all"
}
"arrowParens": "avoid",
"bracketSpacing": true,
"printWidth": 180,
"singleQuote": false,
"tabWidth": 4,
"trailingComma": "all",
"endOfLine": "auto"
}
24,016 changes: 0 additions & 24,016 deletions package-lock.json

This file was deleted.

4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
]
},
"dependencies": {
"@apollo/client": "^3.5.6",
"@ethersproject/providers": "^5.0.12",
"@material-ui/core": "^4.11.4",
"@material-ui/icons": "^4.11.2",
Expand All @@ -24,6 +25,7 @@
"classnames": "^2.3.1",
"ethers": "^5.4.0",
"from-exponential": "^1.1.1",
"graphql": "^16.2.0",
"intl": "^1.2.5",
"lodash": "^4.17.21",
"node-sass": "^6.0.1",
Expand All @@ -32,9 +34,11 @@
"react": "^16.14.0",
"react-copy-to-clipboard": "^5.0.4",
"react-dom": "^16.14.0",
"react-query": "^3.34.6",
"react-redux": "^7.2.4",
"react-router-dom": "^5.2.0",
"react-scripts": "^4.0.3",
"recharts": "^2.1.8",
"redux": "^4.1.0",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0",
Expand Down
5 changes: 5 additions & 0 deletions src/Root/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import ViewBase from "../components/ViewBase";
import { Stake, ChooseBond, Bond, Dashboard, NotFound, Calculator } from "../views";
import "./style.scss";
import useTokens from "../hooks/tokens";
import { Growth } from "src/views/Growth";

function App() {
const dispatch = useDispatch();
Expand Down Expand Up @@ -119,6 +120,10 @@ function App() {
<Stake />
</Route>

<Route path="/growth">
<Growth />
</Route>

<Route path="/mints">
{bonds.map(bond => {
return (
Expand Down
17 changes: 17 additions & 0 deletions src/components/Drawer/drawer-content/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ function NavContent() {
if (currentPath.indexOf("dashboard") >= 0 && page === "dashboard") {
return true;
}
if (currentPath.indexOf("growth") >= 0 && page === "growth") {
return true;
}
if (currentPath.indexOf("stake") >= 0 && page === "stake") {
return true;
}
Expand Down Expand Up @@ -69,6 +72,20 @@ function NavContent() {
</div>
</Link>

<Link
component={NavLink}
to="/growth"
isActive={(match: any, location: any) => {
return checkPage(location, "growth");
}}
className={classnames("button-dapp-menu", { active: isActive })}
>
<div className="dapp-menu-item">
<img alt="" src={DashboardIcon} />
<p>Growth</p>
</div>
</Link>

<Link
component={NavLink}
to="/stake"
Expand Down
30 changes: 30 additions & 0 deletions src/components/Text/Text.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { CSSProperties, forwardRef, ReactNode } from "react";

export enum FontWeight {
Default = "Montserrat",
Medium = "Montserrat Medium",
Semibold = "Montserrat Semibold",
Bold = "Montserrat Bold",
}

export interface TextProps {
size?: string;
weight?: FontWeight;
children?: ReactNode;
dimmed?: boolean;
style?: CSSProperties; // for all kinds of overrides
}

export const Text = forwardRef<HTMLSpanElement, TextProps>(({ size = "14px", weight = FontWeight.Default, dimmed = false, style = {}, children }: TextProps, ref) => {
const textStyle: CSSProperties = {
fontSize: size,
fontFamily: weight,
color: dimmed ? "rgba(255, 255, 255, 0.6)" : "#fff",
...style,
};
return (
<span ref={ref} style={textStyle}>
{children}
</span>
);
});
1 change: 1 addition & 0 deletions src/constants/graph.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const SUBGRAPH_URL = "https://api.thegraph.com/subgraphs/name/olofens/doing-some-stuff";
38 changes: 38 additions & 0 deletions src/helpers/apollo/apollo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { SUBGRAPH_URL } from "./../../constants/graph";
import { ApolloClient, InMemoryCache, gql } from "@apollo/client";

const client = () =>
new ApolloClient({
uri: SUBGRAPH_URL,
cache: new InMemoryCache(),
});

const apollo = async <T>(queryString: string) => {
try {
const data = client().query<T>({
query: gql(queryString),
});
return data;
} catch (err) {
console.error("graph ql error: ", err);
}
};

const extClient = (uri: string) =>
new ApolloClient({
uri: uri,
cache: new InMemoryCache(),
});

export const apolloExt = async (queryString: string, uri: string) => {
try {
const data = await extClient(uri).query({
query: gql(queryString),
});
return data;
} catch (err) {
console.error("external graph ql api error: ", err);
}
};

export default apollo;
2 changes: 2 additions & 0 deletions src/store/slices/app-slice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ export const loadAppDetails = createAsyncThunk(
const marketCap = totalSupply * marketPrice;

const tokenBalPromises = allBonds.map(bond => bond.getTreasuryBalance(networkID, provider));
console.log(allBonds);
const tokenBalances = await Promise.all(tokenBalPromises);
console.log(tokenBalances);
const treasuryBalance = tokenBalances.reduce((tokenBalance0, tokenBalance1) => tokenBalance0 + tokenBalance1, 0);

const tokenAmountsPromises = allBonds.map(bond => bond.getTokenAmount(networkID, provider));
Expand Down
20 changes: 20 additions & 0 deletions src/views/Growth/ChartCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { MutableRefObject, ReactNode, RefObject } from "react";
import { FontWeight, Text } from "src/components/Text/Text";
import "./growth.scss";

export const ChartCard = ({ children, title, valueRef, initValue }: { children: ReactNode; title: string; valueRef: RefObject<HTMLSpanElement>; initValue: string }) => {
return (
<div className="chart-card">
<div className="text-wrapper">
<Text weight={FontWeight.Medium} dimmed size="20px">
{title}
</Text>
<Text ref={valueRef} weight={FontWeight.Bold} size="24px">
{initValue}
</Text>
</div>

{children}
</div>
);
};
35 changes: 35 additions & 0 deletions src/views/Growth/Charts.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import React from "react";
import { useGrowthData } from "./useGrowthData";
import "./growth.scss";
import { GrowthChart } from "./GrowthChart";
import { GrowthDataPoint } from "./types";

const SOURCES_1: (keyof GrowthDataPoint)[] = ["treasuryMIMMarketValue"];
const SOURCES_2: (keyof GrowthDataPoint)[] = [
"treasuryMIMMarketValue",
"treasuryMIMFromTIMEMIMJLP",
"treasuryMIMFromWETHMIMJLP",
"treasuryWAVAXMarketValue",
"treasuryWETHMarketValue",
"treasuryWETHValueFromWETHMIMJLP",
];

export const Charts = () => {
const data = useGrowthData();

return (
<div className="container">
<h3>Growth</h3>
{data && (
<div className="charts-container">
<div className="flex-member">
<GrowthChart data={data} sources={SOURCES_1} title="Treasury Risk-Free Value" />
</div>
<div className="flex-member">
<GrowthChart data={data} sources={SOURCES_2} title="Total treasury assets" />
</div>
</div>
)}
</div>
);
};
83 changes: 83 additions & 0 deletions src/views/Growth/GrowthChart.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import React, { useCallback, useMemo, useRef, useState } from "react";
import { Area, AreaChart, ResponsiveContainer, XAxis, YAxis, Tooltip } from "recharts";
import { ChartCard } from "./ChartCard";
import { formatCash, getCoinColors } from "./helpers";
import { GrowthDataPoint } from "./types";
import "./growth.scss";
import { Text } from "src/components/Text/Text";

export interface GrowthChartProps {
sources: (keyof GrowthDataPoint)[]; // just get the datapoints
data: GrowthDataPoint[];
title: string;
}

const yAxisTickFormatter = (value: number) => {
return `$${formatCash(value)}`;
};

const xAxisTickFormatter = (date: Date): string => {
return date.toLocaleDateString("en-US", {
month: "short",
day: "numeric",
});
};

export const GrowthChart = ({ sources, data, title }: GrowthChartProps) => {
const calculateTotal = useCallback((index: number) => {
const datapoint = data[index];
const valuesToSum = sources.map(key => datapoint[key]).filter(value => value != null);
// @ts-ignore
const sum: number = valuesToSum.reduce((prev, next) => prev + next, 0);
return "$" + Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(sum);
}, []);

const initValue = useRef(calculateTotal(data.length - 1));
const valueRef = useRef<HTMLSpanElement>(null);

const handleMouseMove = useCallback((moveData: any) => {
if (!valueRef.current) return;
if (moveData.isTooltipActive) {
// we're hovering something, lets calculate new totals
valueRef.current.innerText = calculateTotal(moveData.activeTooltipIndex);
} else {
// we stopped hovering, reset to latest index
valueRef.current.innerText = calculateTotal(data.length - 1);
}
}, []);

return (
<ChartCard title={title} valueRef={valueRef} initValue={initValue.current}>
<ResponsiveContainer height="90%" width="100%">
<AreaChart
data={data}
margin={{
top: 10,
right: 10,
left: -5,
bottom: 24,
}}
onMouseMove={handleMouseMove}
>
<defs>
{sources.map(key => {
const [color1, color2] = getCoinColors(key);
return (
<linearGradient id={key} x1="0" y1="0" x2="0" y2="1">
<stop offset="5%" stopColor={color1} />
<stop offset="80%" stopColor={color2} />
</linearGradient>
);
})}
</defs>
<XAxis dataKey="timestamp" tickFormatter={xAxisTickFormatter} minTickGap={25} fontSize="13px" padding={{ right: 20 }} axisLine={false} tickLine={false} />
<YAxis tickFormatter={yAxisTickFormatter} axisLine={false} tickLine={false} fontSize="13px" />
<Tooltip />
{sources.map(key => {
return <Area type="monotone" dataKey={key} stackId="1" stroke="#000" strokeWidth={0} fill={`url(#${key})`} />;
})}
</AreaChart>
</ResponsiveContainer>
</ChartCard>
);
};
14 changes: 14 additions & 0 deletions src/views/Growth/TreasuryQueries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const treasuryQuery = `
query {
protocolMetrics(first: 1000, orderBy: timestamp, orderDirection: desc) {
id
timestamp
treasuryMIMMarketValue
treasuryWAVAXMarketValue
treasuryWETHMarketValue
treasuryMIMFromWETHMIMJLP
treasuryMIMFromTIMEMIMJLP
treasuryWETHValueFromWETHMIMJLP
}
}
`;
6 changes: 6 additions & 0 deletions src/views/Growth/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const WETH_COLOR_1 = "#474748";
export const WETH_COLOR_2 = "#3c3c3d";
export const AVAX_COLOR_1 = "#EB5C5C";
export const AVAX_COLOR_2 = "#e84142";
export const MIM_COLOR_1 = "#8D8BF8";
export const MIM_COLOR_2 = "#7b79f7";
34 changes: 34 additions & 0 deletions src/views/Growth/growth.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.charts-container {
width: 80%;
min-height: 600px;
margin: auto;
display: flex;
align-content: center;
justify-content: center;
gap: 24px;
}

.flex-member {
flex: 1;
max-width: 600px;
}

.container {
font-family: Montserrat;
}

.chart-card {
height: 500px;
background: rgba(0, 0, 0, 0.2);
-webkit-backdrop-filter: blur(100px);
backdrop-filter: blur(100px);
padding: 24px;
border-radius: 10px;
}

.text-wrapper {
display: flex;
flex-direction: column;
gap: 6px;
margin-bottom: 16px;
}
Loading