215 lines
5.3 KiB
TypeScript
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;
|