augmented-tbc-design/src/SupplyVsDemandChart.tsx

215 lines
5.3 KiB
TypeScript

import React from "react";
import {
AreaChart,
Area,
XAxis,
YAxis,
CartesianGrid,
Legend,
ReferenceLine,
ReferenceDot,
ReferenceArea,
ResponsiveContainer,
Tooltip
} from "recharts";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import { getLinspaceTicks, getUnits } from "./utils";
import { getInitialParams, getPriceR } from "./math";
import { useTheme } from "@material-ui/styles";
const isAnimationActive = false;
const keyHorizontal = "x";
const keyVertical = "Supply (tokens) / Collateral (DAI)";
// Do to transparency and color merging issues
// these colors are handpicked to look the closest to the theme colors
const referenceLineColor = "#b7c1cb";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
tooltip: {
border: "1px solid #313d47",
backgroundColor: "#384b59",
padding: theme.spacing(1),
color: "#c7ccd2"
}
})
);
function SupplyVsDemandChart({
theta,
d0,
p0,
p1
}: {
theta: number;
d0: number;
p0: number;
p1: number;
}) {
// d0 - Initial raise, d0 (DAI)
// theta - fraction allocated to reserve (.)
// p0 - Hatch sale Price p0 (DAI / token)
// returnF - Return factor (.)
// wFee - friction coefficient (.)
// Hatch parameters
const {
k, // Invariant power kappa (.)
R0, // Initial reserve (DAI)
S0, // initial supply of tokens (token)
V0 // invariant coef
} = getInitialParams({
d0,
theta,
p0,
p1
});
const R0_round = Math.round(R0);
const S_of_R = (R: number) => S0 * (R / R0_round) ** (1 / k);
// Function setup
const f = S_of_R;
const from = 0;
const to = 4 * R0_round;
const steps = 100 + 1; // Add 1 for the ticks to match
const step = Math.round((to - from) / (steps - 1));
/**
* Prettify the result converting 1000000 to 1M
*/
const biggest = Math.max(to, f(to));
const { scaling, unit } = getUnits(biggest);
const data = [];
for (let i = 0; i < steps; i++) {
const x = Math.round(from + step * i);
data.push({
[keyHorizontal]: x,
[keyVertical]: f(x)
});
}
// Chart components
const theme: any = useTheme();
const classes = useStyles();
const formatter = (n: number) =>
(+(n / scaling).toPrecision(2)).toLocaleString();
function renderColorfulLegendText(value: string) {
return <span style={{ color: theme.palette.text.secondary }}>{value}</span>;
}
function ReferenceLabel(props: any) {
const { textAnchor, viewBox } = props;
return (
<text
x={viewBox.x + viewBox.width / 4 + 10}
y={viewBox.y + 20}
fill={referenceLineColor}
textAnchor={textAnchor}
>
Initial Token Supply
</text>
);
}
function CustomTooltip({ active, payload, label }: any) {
if (active) {
const supply = payload[0].value;
const reserve = label;
const price = getPriceR({ R: reserve, V0, k });
const toolTipData: string[][] = [
["Supply", formatter(supply) + unit, "tokens"],
["Collateral", formatter(reserve) + unit, "DAI"],
["Price", price.toFixed(2), "DAI/token"]
];
return (
<div className={classes.tooltip}>
<table>
<tbody>
{toolTipData.map(([name, value, _unit]) => (
<tr key={name}>
<td>{name}</td>
<td>{value}</td>
<td>{_unit}</td>
</tr>
))}
</tbody>
</table>
</div>
);
} else return null;
}
return (
<ResponsiveContainer debounce={1}>
<AreaChart
width={0}
height={400}
data={data}
margin={{
top: 10,
right: 30,
left: 0,
bottom: 0
}}
>
<CartesianGrid
vertical={false}
stroke={theme.palette.text.secondary}
strokeOpacity={0.13}
/>
<XAxis
interval={24}
dataKey={keyHorizontal}
tickFormatter={formatter}
unit={unit}
tick={{ fill: theme.palette.text.secondary }}
stroke={theme.palette.text.secondary}
/>
<YAxis
interval={"preserveStartEnd"}
ticks={getLinspaceTicks(data.map(d => d[keyVertical]), 3)}
tickFormatter={formatter}
unit={unit}
tick={{ fill: theme.palette.text.secondary }}
domain={[0, f(to)]}
stroke={theme.palette.text.secondary}
/>
<Tooltip content={<CustomTooltip />} />
<Area
isAnimationActive={isAnimationActive}
type="monotone"
dataKey={keyVertical}
stroke={theme.palette.primary.main}
fill={theme.palette.primary.main}
fillOpacity={0.3}
strokeWidth={2}
/>
{/* Necessary because ReferenceDot types do not allow "label" k */}
<ReferenceLine
x={R0_round}
y={f(R0_round)}
stroke={"transparent"}
strokeDasharray="9 0"
label={<ReferenceLabel />}
/>
<ReferenceDot
x={R0_round}
y={f(R0_round)}
r={6}
fill={theme.palette.primary.main}
stroke={2}
/>
<Legend formatter={renderColorfulLegendText} />
</AreaChart>
</ResponsiveContainer>
);
}
export default SupplyVsDemandChart;