Re-structure inputs + better tooltips
This commit is contained in:
parent
f17679e651
commit
9c4de97ae3
36
src/App.tsx
36
src/App.tsx
|
|
@ -9,7 +9,8 @@ import Grid from "@material-ui/core/Grid";
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button";
|
||||||
// Components
|
// Components
|
||||||
import Header from "./Header";
|
import Header from "./Header";
|
||||||
import InputParams from "./InputParams";
|
import CurveDesignInputParams from "./CurveDesignInputParams";
|
||||||
|
import SimulationInputParams from "./SimulationInputParams";
|
||||||
import SupplyVsDemandChart from "./SupplyVsDemandChart";
|
import SupplyVsDemandChart from "./SupplyVsDemandChart";
|
||||||
import ResultParams from "./ResultParams";
|
import ResultParams from "./ResultParams";
|
||||||
import PriceSimulationChart from "./PriceSimulationChart";
|
import PriceSimulationChart from "./PriceSimulationChart";
|
||||||
|
|
@ -53,8 +54,7 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||||
backgroundColor: "#293640"
|
backgroundColor: "#293640"
|
||||||
},
|
},
|
||||||
box: {
|
box: {
|
||||||
padding: theme.spacing(3, 3),
|
padding: theme.spacing(3, 3)
|
||||||
minHeight: 310
|
|
||||||
},
|
},
|
||||||
boxButton: {
|
boxButton: {
|
||||||
padding: theme.spacing(3, 3)
|
padding: theme.spacing(3, 3)
|
||||||
|
|
@ -66,6 +66,12 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
borderBottom: "1px solid #313d47"
|
borderBottom: "1px solid #313d47"
|
||||||
},
|
},
|
||||||
|
boxBorderBottom: {
|
||||||
|
borderBottom: "1px solid #313d47"
|
||||||
|
},
|
||||||
|
initialRaise: {
|
||||||
|
justifyContent: "space-between"
|
||||||
|
},
|
||||||
boxChart: {
|
boxChart: {
|
||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%",
|
height: "100%",
|
||||||
|
|
@ -93,7 +99,8 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||||
marginBottom: -theme.spacing(headerOffset)
|
marginBottom: -theme.spacing(headerOffset)
|
||||||
},
|
},
|
||||||
button: {
|
button: {
|
||||||
background: "linear-gradient(290deg, #2ad179, #4ab47c)",
|
// background: "linear-gradient(290deg, #2ad179, #4ab47c)", // Green gradient
|
||||||
|
background: "linear-gradient(290deg, #1880e0, #3873d8)", // blue gradient
|
||||||
color: "white"
|
color: "white"
|
||||||
},
|
},
|
||||||
// Descriptions
|
// Descriptions
|
||||||
|
|
@ -170,11 +177,11 @@ const resultParameterDescriptions = [
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const [curveParams, setCurveParams] = useState({
|
const [curveParams, setCurveParams] = useState({
|
||||||
d0: 1e6, // Initial raise, d0 (DAI)
|
|
||||||
theta: 0.35, // fraction allocated to reserve (.)
|
theta: 0.35, // fraction allocated to reserve (.)
|
||||||
p0: 0.1, // Hatch sale price p0 (DAI / token)
|
p0: 0.1, // Hatch sale price p0 (DAI / token)
|
||||||
p1: 0.3, // Return factor (.)
|
p1: 0.3, // Return factor (.)
|
||||||
wFee: 0.05 // friction coefficient (.)
|
wFee: 0.05, // friction coefficient (.)
|
||||||
|
d0: 3e6 // Initial raise, d0 (DAI)
|
||||||
});
|
});
|
||||||
|
|
||||||
const { d0, theta, p0, p1, wFee } = curveParams;
|
const { d0, theta, p0, p1, wFee } = curveParams;
|
||||||
|
|
@ -321,6 +328,7 @@ export default function App() {
|
||||||
];
|
];
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<header className={classes.header}>
|
<header className={classes.header}>
|
||||||
|
|
@ -364,8 +372,22 @@ export default function App() {
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
|
<Box className={`${classes.box} ${classes.boxBorderBottom}`}>
|
||||||
|
<CurveDesignInputParams
|
||||||
|
curveParams={curveParams}
|
||||||
|
setCurveParams={setCurveParamsThrottle}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<Box className={`${classes.boxHeader} ${classes.initialRaise}`}>
|
||||||
|
<Typography variant="h6">Run parameters</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Box className={classes.box}>
|
<Box className={classes.box}>
|
||||||
<InputParams setCurveParams={setCurveParamsThrottle} />
|
<SimulationInputParams
|
||||||
|
curveParams={curveParams}
|
||||||
|
setCurveParams={setCurveParamsThrottle}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { InputFieldInterface, CurveParamsInterface } from "./types";
|
||||||
|
import InputParams from "./InputParams";
|
||||||
|
|
||||||
|
export default function CurveDesignInputParams({
|
||||||
|
curveParams,
|
||||||
|
setCurveParams
|
||||||
|
}: {
|
||||||
|
curveParams: CurveParamsInterface;
|
||||||
|
setCurveParams(newCurveParams: any): void;
|
||||||
|
}) {
|
||||||
|
const [theta, setTheta] = useState(0.35); // fraction allocated to reserve (.)
|
||||||
|
const [p0, setP0] = useState(0.1); // Hatch sale Price p0 (DAI / token)
|
||||||
|
const [p1, setP1] = useState(0.3); // Return factor (.)
|
||||||
|
const [wFee, setWFee] = useState(0.05); // friction coefficient (.)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTheta(curveParams.theta);
|
||||||
|
setP0(curveParams.p0);
|
||||||
|
setP1(curveParams.p1);
|
||||||
|
setWFee(curveParams.wFee);
|
||||||
|
}, [curveParams]);
|
||||||
|
|
||||||
|
function _setP0(newP0: number) {
|
||||||
|
setP0(newP0);
|
||||||
|
if (p1 < newP0) setP1(newP0);
|
||||||
|
else if (p1 > newP0 * maxReturnRate) setP1(newP0 * maxReturnRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setParentCurveParams() {
|
||||||
|
setCurveParams((params: CurveParamsInterface) => ({
|
||||||
|
...params,
|
||||||
|
theta,
|
||||||
|
p0,
|
||||||
|
p1,
|
||||||
|
wFee
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const maxReturnRate = 10;
|
||||||
|
|
||||||
|
const inputFields: InputFieldInterface[] = [
|
||||||
|
{
|
||||||
|
label: "Allocation to funding pool",
|
||||||
|
value: theta,
|
||||||
|
setter: setTheta,
|
||||||
|
min: 0,
|
||||||
|
max: 0.9,
|
||||||
|
step: 0.01,
|
||||||
|
unit: "%",
|
||||||
|
suffix: "%",
|
||||||
|
format: (n: number) => `${Math.round(100 * n)}%`,
|
||||||
|
toText: (n: number) => String(+(n * 1e2).toFixed(0)),
|
||||||
|
toNum: (n: string) => parseFloat(n) * 1e-2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Hatch price",
|
||||||
|
value: p0,
|
||||||
|
setter: _setP0,
|
||||||
|
min: 0.01,
|
||||||
|
max: 1,
|
||||||
|
step: 0.01,
|
||||||
|
unit: "$",
|
||||||
|
prefix: "$",
|
||||||
|
toText: (n: number) => String(+n.toFixed(2)),
|
||||||
|
toNum: (n: string) => parseFloat(n),
|
||||||
|
format: (n: number) => `$${n}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Post-hatch price",
|
||||||
|
value: p1,
|
||||||
|
setter: setP1,
|
||||||
|
min: p0 || 0.1,
|
||||||
|
max: Number((maxReturnRate * p0).toFixed(2)),
|
||||||
|
step: 0.01,
|
||||||
|
unit: "$",
|
||||||
|
prefix: "$",
|
||||||
|
toText: (n: number) => String(+n.toFixed(2)),
|
||||||
|
toNum: (n: string) => parseFloat(n),
|
||||||
|
format: (n: number) => `$${n}`
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Exit tribute",
|
||||||
|
value: wFee,
|
||||||
|
setter: setWFee,
|
||||||
|
min: 0,
|
||||||
|
max: 0.1,
|
||||||
|
step: 0.001,
|
||||||
|
unit: "%",
|
||||||
|
suffix: "%",
|
||||||
|
format: (n: number) => `${+(100 * n).toFixed(1)}%`,
|
||||||
|
toText: (n: number) => String(+(n * 1e2).toFixed(1)),
|
||||||
|
toNum: (n: string) => parseFloat(n) * 1e-2
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InputParams
|
||||||
|
inputFields={inputFields}
|
||||||
|
onChangeCommited={setParentCurveParams}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,47 +1,11 @@
|
||||||
import React, { useState } from "react";
|
import React from "react";
|
||||||
import {
|
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||||
createStyles,
|
|
||||||
makeStyles,
|
|
||||||
withStyles,
|
|
||||||
Theme
|
|
||||||
} from "@material-ui/core/styles";
|
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import Slider from "@material-ui/core/Slider";
|
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField";
|
||||||
import NumberFormat from "react-number-format";
|
import NumberFormat from "react-number-format";
|
||||||
|
import { InputFieldInterface } from "./types";
|
||||||
const PrettoSlider = withStyles({
|
import PrettoSlider from "./PrettoSlider";
|
||||||
root: {
|
|
||||||
height: 8
|
|
||||||
},
|
|
||||||
thumb: {
|
|
||||||
height: 24,
|
|
||||||
width: 24,
|
|
||||||
backgroundColor: "#fff",
|
|
||||||
border: "2px solid currentColor",
|
|
||||||
marginTop: -8,
|
|
||||||
marginLeft: -12,
|
|
||||||
"&:focus,&:hover,&$active": {
|
|
||||||
boxShadow: "inherit"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
active: {},
|
|
||||||
valueLabel: {
|
|
||||||
left: "calc(-50% + 4px)"
|
|
||||||
},
|
|
||||||
track: {
|
|
||||||
height: 8,
|
|
||||||
borderRadius: 4
|
|
||||||
},
|
|
||||||
rail: {
|
|
||||||
height: 8,
|
|
||||||
borderRadius: 4
|
|
||||||
},
|
|
||||||
markLabel: {
|
|
||||||
top: 30
|
|
||||||
}
|
|
||||||
})(Slider);
|
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
|
@ -79,6 +43,9 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||||
},
|
},
|
||||||
slider: {
|
slider: {
|
||||||
color: theme.palette.primary.main
|
color: theme.palette.primary.main
|
||||||
|
},
|
||||||
|
secondaryColor: {
|
||||||
|
color: theme.palette.secondary.light
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
@ -101,118 +68,12 @@ function NumberFormatCustom(props: any) {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function InputParams({
|
export default function InputParams({
|
||||||
curveParams,
|
inputFields,
|
||||||
setCurveParams
|
onChangeCommited
|
||||||
}: {
|
}: {
|
||||||
curveParams?: {
|
inputFields: InputFieldInterface[];
|
||||||
d0: number;
|
onChangeCommited(): void;
|
||||||
theta: number;
|
|
||||||
p0: number;
|
|
||||||
p1: number;
|
|
||||||
wFee: number;
|
|
||||||
};
|
|
||||||
setCurveParams(newCurveParams: any): void;
|
|
||||||
}) {
|
}) {
|
||||||
const [d0, setD0] = useState(1e6); // Initial raise, d0 (DAI)
|
|
||||||
const [theta, setTheta] = useState(0.35); // fraction allocated to reserve (.)
|
|
||||||
const [p0, setP0] = useState(0.1); // Hatch sale Price p0 (DAI / token)
|
|
||||||
const [p1, setP1] = useState(0.3); // Return factor (.)
|
|
||||||
const [wFee, setWFee] = useState(0.05); // friction coefficient (.)
|
|
||||||
|
|
||||||
function _setP0(newP0: number) {
|
|
||||||
setP0(newP0);
|
|
||||||
if (p1 < newP0) setP1(newP0);
|
|
||||||
else if (p1 > newP0 * maxReturnRate) setP1(newP0 * maxReturnRate);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setParentCurveParams() {
|
|
||||||
setCurveParams({ d0, theta, p0, p1, wFee });
|
|
||||||
}
|
|
||||||
|
|
||||||
const maxReturnRate = 10;
|
|
||||||
|
|
||||||
const inputFields: {
|
|
||||||
label: string;
|
|
||||||
value: number;
|
|
||||||
setter(newValue: any): void;
|
|
||||||
min: number;
|
|
||||||
max: number;
|
|
||||||
step: number;
|
|
||||||
unit?: string;
|
|
||||||
prefix?: string;
|
|
||||||
suffix?: string;
|
|
||||||
toText?(value: number): string;
|
|
||||||
toNum?(value: string): number;
|
|
||||||
format(value: number): string;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
label: "Initial raise",
|
|
||||||
value: d0,
|
|
||||||
setter: setD0,
|
|
||||||
min: 0.1e6,
|
|
||||||
max: 10e6,
|
|
||||||
step: 0.1e6,
|
|
||||||
unit: "$M",
|
|
||||||
prefix: "$",
|
|
||||||
suffix: "M",
|
|
||||||
format: (n: number) => `$${+(n * 1e-6).toFixed(1)}M`,
|
|
||||||
toText: (n: number) => String(+(n * 1e-6).toFixed(1)),
|
|
||||||
toNum: (n: string) => Math.floor(parseFloat(n) * 1e6)
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Allocation to funding pool",
|
|
||||||
value: theta,
|
|
||||||
setter: setTheta,
|
|
||||||
min: 0,
|
|
||||||
max: 0.9,
|
|
||||||
step: 0.01,
|
|
||||||
unit: "%",
|
|
||||||
suffix: "%",
|
|
||||||
format: (n: number) => `${Math.round(100 * n)}%`,
|
|
||||||
toText: (n: number) => String(+(n * 1e2).toFixed(0)),
|
|
||||||
toNum: (n: string) => parseFloat(n) * 1e-2
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Hatch price",
|
|
||||||
value: p0,
|
|
||||||
setter: _setP0,
|
|
||||||
min: 0.01,
|
|
||||||
max: 1,
|
|
||||||
step: 0.01,
|
|
||||||
unit: "$",
|
|
||||||
prefix: "$",
|
|
||||||
toText: (n: number) => String(+n.toFixed(2)),
|
|
||||||
toNum: (n: string) => parseFloat(n),
|
|
||||||
format: (n: number) => `$${n}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Post-hatch price",
|
|
||||||
value: p1,
|
|
||||||
setter: setP1,
|
|
||||||
min: p0 || 0.1,
|
|
||||||
max: Number((maxReturnRate * p0).toFixed(2)),
|
|
||||||
step: 0.01,
|
|
||||||
unit: "$",
|
|
||||||
prefix: "$",
|
|
||||||
toText: (n: number) => String(+n.toFixed(2)),
|
|
||||||
toNum: (n: string) => parseFloat(n),
|
|
||||||
format: (n: number) => `$${n}`
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "Exit tribute",
|
|
||||||
value: wFee,
|
|
||||||
setter: setWFee,
|
|
||||||
min: 0,
|
|
||||||
max: 0.1,
|
|
||||||
step: 0.001,
|
|
||||||
unit: "%",
|
|
||||||
suffix: "%",
|
|
||||||
format: (n: number) => `${+(100 * n).toFixed(1)}%`,
|
|
||||||
toText: (n: number) => String(+(n * 1e2).toFixed(1)),
|
|
||||||
toNum: (n: string) => parseFloat(n) * 1e-2
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -227,6 +88,7 @@ export default function InputParams({
|
||||||
step,
|
step,
|
||||||
prefix,
|
prefix,
|
||||||
suffix,
|
suffix,
|
||||||
|
secondaryColor,
|
||||||
format,
|
format,
|
||||||
toText,
|
toText,
|
||||||
toNum
|
toNum
|
||||||
|
|
@ -253,7 +115,7 @@ export default function InputParams({
|
||||||
sanitizeInput(
|
sanitizeInput(
|
||||||
toNum ? toNum(e.target.value) : parseFloat(e.target.value)
|
toNum ? toNum(e.target.value) : parseFloat(e.target.value)
|
||||||
);
|
);
|
||||||
setParentCurveParams();
|
onChangeCommited();
|
||||||
}}
|
}}
|
||||||
InputProps={{
|
InputProps={{
|
||||||
inputComponent: NumberFormatCustom,
|
inputComponent: NumberFormatCustom,
|
||||||
|
|
@ -269,12 +131,14 @@ export default function InputParams({
|
||||||
|
|
||||||
<Grid item xs={4}>
|
<Grid item xs={4}>
|
||||||
<PrettoSlider
|
<PrettoSlider
|
||||||
className={classes.slider}
|
className={`${classes.slider} ${
|
||||||
|
secondaryColor ? classes.secondaryColor : ""
|
||||||
|
}`}
|
||||||
valueLabelDisplay="auto"
|
valueLabelDisplay="auto"
|
||||||
aria-label={label}
|
aria-label={label}
|
||||||
defaultValue={value}
|
defaultValue={value}
|
||||||
onChange={(_, newValue) => sanitizeInput(Number(newValue))}
|
onChange={(_, newValue) => sanitizeInput(Number(newValue))}
|
||||||
onChangeCommitted={setParentCurveParams}
|
onChangeCommitted={onChangeCommited}
|
||||||
value={value}
|
value={value}
|
||||||
min={min}
|
min={min}
|
||||||
max={max}
|
max={max}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { withStyles } from "@material-ui/core/styles";
|
||||||
|
import Slider from "@material-ui/core/Slider";
|
||||||
|
|
||||||
|
export default withStyles({
|
||||||
|
root: {
|
||||||
|
height: 8
|
||||||
|
},
|
||||||
|
thumb: {
|
||||||
|
height: 24,
|
||||||
|
width: 24,
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
border: "2px solid currentColor",
|
||||||
|
marginTop: -8,
|
||||||
|
marginLeft: -12,
|
||||||
|
"&:focus,&:hover,&$active": {
|
||||||
|
boxShadow: "inherit"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
active: {},
|
||||||
|
valueLabel: {
|
||||||
|
left: "calc(-50% + 4px)"
|
||||||
|
},
|
||||||
|
track: {
|
||||||
|
height: 8,
|
||||||
|
borderRadius: 4
|
||||||
|
},
|
||||||
|
rail: {
|
||||||
|
height: 8,
|
||||||
|
borderRadius: 4
|
||||||
|
},
|
||||||
|
markLabel: {
|
||||||
|
top: 30
|
||||||
|
}
|
||||||
|
})(Slider);
|
||||||
|
|
@ -10,9 +10,25 @@ import {
|
||||||
ResponsiveContainer,
|
ResponsiveContainer,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "recharts";
|
} from "recharts";
|
||||||
|
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||||
import { useTheme } from "@material-ui/styles";
|
import { useTheme } from "@material-ui/styles";
|
||||||
import { linspace } from "./utils";
|
import { linspace } from "./utils";
|
||||||
|
|
||||||
|
const keyHorizontal = "x";
|
||||||
|
const keyVerticalLeft = "Price (DAI/token)";
|
||||||
|
const keyVerticalRight = "Total exit tributes (DAI)";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
tooltip: {
|
||||||
|
border: "1px solid #313d47",
|
||||||
|
backgroundColor: "#384b59",
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
color: "#c7ccd2"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
function PriceSimulationChart({
|
function PriceSimulationChart({
|
||||||
priceTimeseries,
|
priceTimeseries,
|
||||||
withdrawFeeTimeseries,
|
withdrawFeeTimeseries,
|
||||||
|
|
@ -30,10 +46,6 @@ function PriceSimulationChart({
|
||||||
// returnF - Return factor (.)
|
// returnF - Return factor (.)
|
||||||
// wFee - friction coefficient (.)
|
// wFee - friction coefficient (.)
|
||||||
|
|
||||||
const keyHorizontal = "x";
|
|
||||||
const keyVerticalLeft = "Price (DAI / token)";
|
|
||||||
const keyVerticalRight = "Total exit tributes (DAI)";
|
|
||||||
|
|
||||||
const data = [];
|
const data = [];
|
||||||
for (let t = 0; t < priceTimeseries.length; t++) {
|
for (let t = 0; t < priceTimeseries.length; t++) {
|
||||||
data.push({
|
data.push({
|
||||||
|
|
@ -46,6 +58,7 @@ function PriceSimulationChart({
|
||||||
// Chart components
|
// Chart components
|
||||||
|
|
||||||
const theme: any = useTheme();
|
const theme: any = useTheme();
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
function renderColorfulLegendText(value: string) {
|
function renderColorfulLegendText(value: string) {
|
||||||
return <span style={{ color: theme.palette.text.secondary }}>{value}</span>;
|
return <span style={{ color: theme.palette.text.secondary }}>{value}</span>;
|
||||||
|
|
@ -67,6 +80,35 @@ function PriceSimulationChart({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function CustomTooltip({ active, payload, label }: any) {
|
||||||
|
if (active) {
|
||||||
|
const price = payload[0].value;
|
||||||
|
const exit = payload[1].value;
|
||||||
|
const weekNum = label;
|
||||||
|
const toolTipData: string[][] = [
|
||||||
|
["Price", price.toFixed(2), "DAI/tk"],
|
||||||
|
["Exit t.", formatter(exit), "DAI"],
|
||||||
|
["Week", weekNum, ""]
|
||||||
|
];
|
||||||
|
|
||||||
|
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 (
|
return (
|
||||||
<ResponsiveContainer debounce={1}>
|
<ResponsiveContainer debounce={1}>
|
||||||
<AreaChart
|
<AreaChart
|
||||||
|
|
@ -115,7 +157,7 @@ function PriceSimulationChart({
|
||||||
stroke={theme.palette.text.secondary}
|
stroke={theme.palette.text.secondary}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tooltip formatter={value => Number(value)} />
|
<Tooltip content={<CustomTooltip />} />
|
||||||
|
|
||||||
<Area
|
<Area
|
||||||
isAnimationActive={false}
|
isAnimationActive={false}
|
||||||
|
|
@ -146,8 +188,9 @@ function PriceSimulationChart({
|
||||||
yAxisId="right"
|
yAxisId="right"
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey={keyVerticalRight}
|
dataKey={keyVerticalRight}
|
||||||
stroke={theme.palette.secondary.main}
|
stroke={theme.palette.secondary.dark}
|
||||||
fill={theme.palette.secondary.main}
|
fill={theme.palette.secondary.dark}
|
||||||
|
fillOpacity="0.8"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* <ReferenceLine
|
{/* <ReferenceLine
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
import { InputFieldInterface, CurveParamsInterface } from "./types";
|
||||||
|
import InputParams from "./InputParams";
|
||||||
|
|
||||||
|
export default function CurveDesignInputParams({
|
||||||
|
curveParams,
|
||||||
|
setCurveParams
|
||||||
|
}: {
|
||||||
|
curveParams: CurveParamsInterface;
|
||||||
|
setCurveParams(newCurveParams: any): void;
|
||||||
|
}) {
|
||||||
|
const [d0, setD0] = useState(3e6); // Initial raise, d0 (DAI)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setD0(curveParams.d0);
|
||||||
|
}, [curveParams]);
|
||||||
|
|
||||||
|
function setParentCurveParams() {
|
||||||
|
setCurveParams((params: CurveParamsInterface) => ({
|
||||||
|
...params,
|
||||||
|
d0
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
const inputFields: InputFieldInterface[] = [
|
||||||
|
{
|
||||||
|
label: "Initial raise",
|
||||||
|
value: d0,
|
||||||
|
setter: setD0,
|
||||||
|
min: 0.1e6,
|
||||||
|
max: 10e6,
|
||||||
|
step: 0.1e6,
|
||||||
|
unit: "$M",
|
||||||
|
prefix: "$",
|
||||||
|
suffix: "M",
|
||||||
|
format: (n: number) => `$${+(n * 1e-6).toFixed(1)}M`,
|
||||||
|
toText: (n: number) => String(+(n * 1e-6).toFixed(1)),
|
||||||
|
toNum: (n: string) => Math.floor(parseFloat(n) * 1e6),
|
||||||
|
secondaryColor: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<InputParams
|
||||||
|
inputFields={inputFields}
|
||||||
|
onChangeCommited={setParentCurveParams}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -10,10 +10,25 @@ import {
|
||||||
ResponsiveContainer,
|
ResponsiveContainer,
|
||||||
Tooltip
|
Tooltip
|
||||||
} from "recharts";
|
} from "recharts";
|
||||||
|
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||||
import { getLinspaceTicks } from "./utils";
|
import { getLinspaceTicks } from "./utils";
|
||||||
import { getInitialParams } from "./math";
|
import { getInitialParams, getPriceR } from "./math";
|
||||||
import { useTheme } from "@material-ui/styles";
|
import { useTheme } from "@material-ui/styles";
|
||||||
|
|
||||||
|
const keyHorizontal = "x";
|
||||||
|
const keyVertical = "Supply (tokens) / Reserve (DAI)";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
|
createStyles({
|
||||||
|
tooltip: {
|
||||||
|
border: "1px solid #313d47",
|
||||||
|
backgroundColor: "#384b59",
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
color: "#c7ccd2"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
function SupplyVsDemandChart({
|
function SupplyVsDemandChart({
|
||||||
theta,
|
theta,
|
||||||
d0,
|
d0,
|
||||||
|
|
@ -35,21 +50,23 @@ function SupplyVsDemandChart({
|
||||||
const {
|
const {
|
||||||
k, // Invariant power kappa (.)
|
k, // Invariant power kappa (.)
|
||||||
R0, // Initial reserve (DAI)
|
R0, // Initial reserve (DAI)
|
||||||
S0 // initial supply of tokens (token)
|
S0, // initial supply of tokens (token)
|
||||||
|
V0 // invariant coef
|
||||||
} = getInitialParams({
|
} = getInitialParams({
|
||||||
d0,
|
d0,
|
||||||
theta,
|
theta,
|
||||||
p0,
|
p0,
|
||||||
p1
|
p1
|
||||||
});
|
});
|
||||||
const S_of_R = (R: number) => S0 * (R / R0) ** (1 / k);
|
const R0_round = Math.round(R0);
|
||||||
|
const S_of_R = (R: number) => S0 * (R / R0_round) ** (1 / k);
|
||||||
|
|
||||||
// Function setup
|
// Function setup
|
||||||
const f = S_of_R;
|
const f = S_of_R;
|
||||||
const from = 0;
|
const from = 0;
|
||||||
const to = 4 * R0;
|
const to = 4 * R0_round;
|
||||||
const steps = 100 + 1; // Add 1 for the ticks to match
|
const steps = 100 + 1; // Add 1 for the ticks to match
|
||||||
const step = (to - from) / steps;
|
const step = Math.round((to - from) / (steps - 1));
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prettify the result converting 1000000 to 1M
|
* Prettify the result converting 1000000 to 1M
|
||||||
|
|
@ -68,12 +85,9 @@ function SupplyVsDemandChart({
|
||||||
: // No scale
|
: // No scale
|
||||||
[1, ""];
|
[1, ""];
|
||||||
|
|
||||||
const keyHorizontal = "x";
|
|
||||||
const keyVertical = "Supply (tokens) / Reserve (DAI)";
|
|
||||||
|
|
||||||
const data = [];
|
const data = [];
|
||||||
for (let i = 0; i < steps; i++) {
|
for (let i = 0; i < steps; i++) {
|
||||||
const x = from + step * i;
|
const x = Math.round(from + step * i);
|
||||||
data.push({
|
data.push({
|
||||||
[keyHorizontal]: x,
|
[keyHorizontal]: x,
|
||||||
[keyVertical]: f(x)
|
[keyVertical]: f(x)
|
||||||
|
|
@ -83,6 +97,7 @@ function SupplyVsDemandChart({
|
||||||
// Chart components
|
// Chart components
|
||||||
|
|
||||||
const theme: any = useTheme();
|
const theme: any = useTheme();
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
const formatter = (n: number) =>
|
const formatter = (n: number) =>
|
||||||
(+(n / scaling).toPrecision(2)).toLocaleString();
|
(+(n / scaling).toPrecision(2)).toLocaleString();
|
||||||
|
|
@ -105,6 +120,34 @@ function SupplyVsDemandChart({
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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"],
|
||||||
|
["Reserve", 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 (
|
return (
|
||||||
<ResponsiveContainer debounce={1}>
|
<ResponsiveContainer debounce={1}>
|
||||||
<AreaChart
|
<AreaChart
|
||||||
|
|
@ -136,7 +179,7 @@ function SupplyVsDemandChart({
|
||||||
domain={[0, f(to)]}
|
domain={[0, f(to)]}
|
||||||
stroke={theme.palette.text.secondary}
|
stroke={theme.palette.text.secondary}
|
||||||
/>
|
/>
|
||||||
<Tooltip formatter={value => formatter(Number(value))} />
|
<Tooltip content={<CustomTooltip />} />
|
||||||
<Area
|
<Area
|
||||||
isAnimationActive={false}
|
isAnimationActive={false}
|
||||||
type="monotone"
|
type="monotone"
|
||||||
|
|
@ -145,7 +188,7 @@ function SupplyVsDemandChart({
|
||||||
fill={theme.palette.primary.main}
|
fill={theme.palette.primary.main}
|
||||||
/>
|
/>
|
||||||
<ReferenceLine
|
<ReferenceLine
|
||||||
x={R0}
|
x={R0_round}
|
||||||
stroke={theme.palette.primary.main}
|
stroke={theme.palette.primary.main}
|
||||||
strokeDasharray="9 0"
|
strokeDasharray="9 0"
|
||||||
label={<ReferenceLabel />}
|
label={<ReferenceLabel />}
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,9 @@ const theme = createMuiTheme({
|
||||||
main: "#2ecd79"
|
main: "#2ecd79"
|
||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
main: "#116be0"
|
main: "#116be0",
|
||||||
|
light: "#0f8bff",
|
||||||
|
dark: "#116be0"
|
||||||
},
|
},
|
||||||
error: {
|
error: {
|
||||||
main: red.A400
|
main: red.A400
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
export interface InputFieldInterface {
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
setter(newValue: any): void;
|
||||||
|
min: number;
|
||||||
|
max: number;
|
||||||
|
step: number;
|
||||||
|
unit?: string;
|
||||||
|
prefix?: string;
|
||||||
|
suffix?: string;
|
||||||
|
secondaryColor?: boolean;
|
||||||
|
toText?(value: number): string;
|
||||||
|
toNum?(value: string): number;
|
||||||
|
format(value: number): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CurveParamsInterface {
|
||||||
|
d0: number;
|
||||||
|
theta: number;
|
||||||
|
p0: number;
|
||||||
|
p1: number;
|
||||||
|
wFee: number;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue