From 5ce3706232f53152040a2943cb9eeaf5fc102544 Mon Sep 17 00:00:00 2001 From: dapplion Date: Sun, 11 Aug 2019 18:12:07 +0200 Subject: [PATCH] Add vesting life slider + make initial raise slider bigger --- src/App.tsx | 167 ++++++++++----------------------- src/CurveDesignInputParams.tsx | 18 +++- src/HelpText.tsx | 78 ++++++++++++++- src/InputParamBig.tsx | 162 ++++++++++++++++++++++++++++++++ src/InputParams.tsx | 9 +- src/ResultParams.tsx | 18 +++- src/SimulationInputParams.tsx | 11 +-- src/TextWithPopover.tsx | 3 +- src/parametersDescriptions.ts | 5 + src/types.ts | 2 +- 10 files changed, 331 insertions(+), 142 deletions(-) create mode 100644 src/InputParamBig.tsx diff --git a/src/App.tsx b/src/App.tsx index ca18452..781f69a 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -115,7 +115,8 @@ const useStyles = makeStyles((theme: Theme) => }, button: { // background: "linear-gradient(290deg, #2ad179, #4ab47c)", // Green gradient - background: "linear-gradient(290deg, #1880e0, #3873d8)", // blue gradient + background: "linear-gradient(290deg, #1aa059, #3d9567)", // Darker Green gradient + // background: "linear-gradient(290deg, #1880e0, #3873d8)", // blue gradient color: "white" }, // Descriptions @@ -138,6 +139,23 @@ const useStyles = makeStyles((theme: Theme) => }, descriptionPadding: { padding: theme.spacing(0.5) + }, + d0Container: { + "& > div": { + padding: "0 12px 0 0 !important", + display: "flex", + alignItems: "center" + } + }, + d0Number: { + padding: "0 !important", + display: "flex", + alignItems: "center" + }, + d0Slidder: { + padding: "0 12px 0 0 !important", + display: "flex", + alignItems: "center" } }) ); @@ -148,10 +166,11 @@ export default function App() { p0: 0.1, // Hatch sale price p0 (DAI / token) p1: 0.3, // Return factor (.) wFee: 0.05, // friction coefficient (.) + vHalflife: 52, // Vesting half life (weeks) d0: 3e6 // Initial raise, d0 (DAI) }); - const { d0, theta, p0, p1, wFee } = curveParams; + const { d0, theta, p0, p1, wFee, vHalflife } = curveParams; /** * Throttle the curve update to prevent the expensive chart @@ -233,7 +252,7 @@ export default function App() { const tx_spread = 10; // vesting(should this be exposed in the app ?) const cliff = 8; // weeks before vesting starts ~2 months - const halflife = 52; // 26 weeks, half life is ~6 months + // const halflife = 52; // 26 weeks, half life is ~6 months // percentage of the hatch tokens which vest per week(since that is our timescale in the sim) // numSteps = 52 take 8ms to run @@ -275,7 +294,7 @@ export default function App() { // txsWithdraw.reduce((t, c) => t + c, 0); // Vest - const delta_H = vest_tokens({ week: t, H, halflife, cliff }); + const delta_H = vest_tokens({ week: t, H, halflife: vHalflife, cliff }); const H_next = H - delta_H; // find floor price @@ -346,14 +365,14 @@ export default function App() { description: resultParameterDescriptions.exitTributes.text, value: (+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() + - ` DAI (${withdrawCount} txs)` + " DAI", + valueFooter: `From ${withdrawCount} exit txs` }, { label: resultParameterDescriptions.slippage.name, description: resultParameterDescriptions.slippage.text, - value: - +(100 * avgSlippage).toFixed(3) + - ` % (avg tx size ${Math.round(avgTxSize).toLocaleString()} DAI)` + value: +(100 * avgSlippage).toFixed(3) + " %", + valueFooter: `Avg tx size ${Math.round(avgTxSize).toLocaleString()} DAI` } ]; @@ -374,37 +393,14 @@ export default function App() { Curve Design -
- - Parameters description: - -
- - - {[ - parameterDescriptions.theta, - parameterDescriptions.p0, - parameterDescriptions.p1, - parameterDescriptions.wFee, - parameterDescriptions.d0 - ].map(({ name, text }) => ( - - - - - ))} - -
- {name} - - - {text} - -
- - } + title={"Parameters description"} + table={[ + parameterDescriptions.theta, + parameterDescriptions.p0, + parameterDescriptions.p1, + parameterDescriptions.wFee, + parameterDescriptions.vHalflife + ]} />
@@ -414,17 +410,6 @@ export default function App() { setCurveParams={setCurveParamsThrottle} /> - - - Run parameters - - - - - @@ -432,15 +417,7 @@ export default function App() { Preview - - - {supplyVsDemandChartDescription} - - - } - /> + @@ -451,8 +428,15 @@ export default function App() { - + + + + + Simulation -
- - {simulationChartDescription} - -
- - - - {Object.values( - simulationParameterDescriptions - ).map(({ name, text }) => ( - - - - - ))} - -
- {name} - - - {text} - -
- - } + body={simulationChartDescription} + table={Object.values(simulationParameterDescriptions)} />
@@ -533,35 +489,8 @@ export default function App() { Results -
- - Result parameters description: - -
- - - {Object.values(resultParameterDescriptions).map( - ({ name, text }) => ( - - - - - ) - )} - -
- {name} - - - {text} - -
- - } + title={"Result parameters description"} + table={Object.values(resultParameterDescriptions)} />
diff --git a/src/CurveDesignInputParams.tsx b/src/CurveDesignInputParams.tsx index 1c6f0f1..7eee175 100644 --- a/src/CurveDesignInputParams.tsx +++ b/src/CurveDesignInputParams.tsx @@ -14,12 +14,14 @@ export default function CurveDesignInputParams({ 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 (.) + const [vHalflife, setVHalflife] = useState(52); // friction coefficient (.) useEffect(() => { setTheta(curveParams.theta); setP0(curveParams.p0); setP1(curveParams.p1); setWFee(curveParams.wFee); + setVHalflife(curveParams.vHalflife); }, [curveParams]); function _setP0(newP0: number) { @@ -34,7 +36,8 @@ export default function CurveDesignInputParams({ theta, p0, p1, - wFee + wFee, + vHalflife })); } @@ -90,6 +93,19 @@ export default function CurveDesignInputParams({ format: (n: number) => `${+(100 * n).toFixed(1)}%`, toText: (n: number) => String(+(n * 1e2).toFixed(1)), toNum: (n: string) => parseFloat(n) * 1e-2 + }, + { + label: `${parameterDescriptions.vHalflife.name} (weeks)`, + description: parameterDescriptions.vHalflife.text, + value: vHalflife, + setter: setVHalflife, + min: 52 / 2, + max: 52 * 2, + step: 1, + suffix: "", + format: (n: number) => String(Math.round(n)), + toText: (n: number) => String(Math.round(n)), + toNum: (n: string) => Math.round(parseInt(n)) } ]; diff --git a/src/HelpText.tsx b/src/HelpText.tsx index f5ca96a..51484c8 100644 --- a/src/HelpText.tsx +++ b/src/HelpText.tsx @@ -1,6 +1,7 @@ import React from "react"; import { makeStyles } from "@material-ui/core/styles"; import Popover from "@material-ui/core/Popover"; +import Typography from "@material-ui/core/Typography"; import Box from "@material-ui/core/Box"; import HelpIcon from "@material-ui/icons/HelpOutline"; @@ -25,10 +26,42 @@ const useStyles = makeStyles(theme => ({ [`@media screen and (max-width: ${theme.breakpoints.values.md}px)`]: { maxWidth: "90vw" } + }, + // Descriptions + descriptionContainer: { + "& > div:not(:last-child)": { + paddingBottom: theme.spacing(1), + marginBottom: theme.spacing(1), + borderBottom: "1px solid #3f5463" + }, + "& td": { + verticalAlign: "top", + padding: theme.spacing(0.5) + } + }, + descriptionTitle: { + padding: theme.spacing(0.5) + }, + descriptionBody: { + color: "#dbdfe4", + padding: theme.spacing(0.5) + }, + descriptionPadding: { + padding: theme.spacing(0.5) } })); -export default function SimplePopover({ text }: { text: any }) { +export default function SimplePopover({ + text, + title, + table, + body +}: { + text?: any; + title?: string; + table?: { name: string; text: string }[]; + body?: string; +}) { const classes = useStyles(); const [anchorEl, setAnchorEl] = React.useState(null); @@ -65,7 +98,48 @@ export default function SimplePopover({ text }: { text: any }) { horizontal: "center" }} > - {text} + +
+ {title && ( +
+ + {title} + +
+ )} + + {body && ( +
+ + {body} + +
+ )} + + {table && ( +
+ + + {table.map(({ name, text }) => ( + + + + + ))} + +
+ {name} + + + {text} + +
+
+ )} + + {text} +
+
); diff --git a/src/InputParamBig.tsx b/src/InputParamBig.tsx new file mode 100644 index 0000000..fb19cd6 --- /dev/null +++ b/src/InputParamBig.tsx @@ -0,0 +1,162 @@ +import React from "react"; +import { createStyles, makeStyles, Theme } from "@material-ui/core/styles"; +import Grid from "@material-ui/core/Grid"; +import TextField from "@material-ui/core/TextField"; +import Typography from "@material-ui/core/Typography"; +import NumberFormat from "react-number-format"; +import { InputFieldInterface } from "./types"; +import PrettoSlider from "./PrettoSlider"; +import HelpText from "./HelpText"; + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + root: { + margin: theme.spacing(6, 0, 3) + }, + lightBulb: { + verticalAlign: "middle", + marginRight: theme.spacing(1) + }, + leftContainer: { + // color: theme.palette.text.secondary + }, + centerContainer: { + // color: blackColor + }, + listBoxContainer: { + "& > div:not(:last-child)": { + paddingBottom: "12px", + marginBottom: "12px", + borderBottom: "1px solid #313d47" + } + }, + listBox: { + "& > div": { + display: "flex", + alignItems: "center", + "& p": { + marginBottom: 0 + } + }, + "& > div:not(:last-child)": { + paddingRight: "12px" + } + }, + slider: { + color: theme.palette.primary.main + } + }) +); + +function NumberFormatCustom(props: any) { + const { inputRef, onChange, prefix, suffix, ...other } = props; + + return ( + { + onChange({ target: { value: values.value } }); + }} + thousandSeparator + prefix={prefix} + suffix={suffix} + /> + ); +} + +export default function InputParamBig({ + inputFields, + onChangeCommited +}: { + inputFields: InputFieldInterface[]; + onChangeCommited(): void; +}) { + const classes = useStyles(); + + return ( +
+ {inputFields.map( + ({ + label, + description, + value, + setter, + min, + max, + step, + prefix, + suffix, + format, + toText, + toNum + }) => { + function sanitizeInput(num: number = 0) { + if (isNaN(num)) num = 0; + if (num > max) num = max; + else if (num < min) num = min; + setter(num); + } + + return ( + + + {label} + + + + + { + sanitizeInput( + toNum ? toNum(e.target.value) : parseFloat(e.target.value) + ); + onChangeCommited(); + }} + InputProps={{ + inputComponent: NumberFormatCustom, + disableUnderline: true, + inputProps: { + prefix, + suffix + } + }} + value={toText ? toText(value) : value} + /> + + + + sanitizeInput(Number(newValue))} + onChangeCommitted={onChangeCommited} + value={value} + min={min} + max={max} + step={step} + valueLabelFormat={value => format(value).replace("$", "")} + /> + + + ); + } + )} +
+ ); +} diff --git a/src/InputParams.tsx b/src/InputParams.tsx index 3d06aaa..9f0f1af 100644 --- a/src/InputParams.tsx +++ b/src/InputParams.tsx @@ -43,9 +43,6 @@ const useStyles = makeStyles((theme: Theme) => }, slider: { color: theme.palette.primary.main - }, - secondaryColor: { - color: theme.palette.secondary.light } }) ); @@ -89,7 +86,6 @@ export default function InputParams({ step, prefix, suffix, - secondaryColor, format, toText, toNum @@ -108,7 +104,6 @@ export default function InputParams({
- {/* {display(value)} */} { sanitizeInput( @@ -130,9 +125,7 @@ export default function InputParams({ "& > div:not(:last-child)": { paddingRight: "12px" } + }, + valueFooter: { + color: theme.palette.text.secondary, + fontSize: "80%" } }) ); @@ -50,6 +54,7 @@ export default function ResultParams({ label: string; description: string; value: number | string; + valueFooter?: string; }[]; simulationDuration: number; }) { @@ -58,7 +63,7 @@ export default function ResultParams({ * Keep the animation active only during the initial animation time, * but afterwards, deactivate to prevent the re-size ugly effect */ - const [isAnimationActive, setIsAnimationActive] = useState(true); + const [isAnimationActive, setIsAnimationActive] = useState(false); useEffect(() => { const timeout = setTimeout(() => { setIsAnimationActive(false); @@ -72,7 +77,7 @@ export default function ResultParams({ return (
- {resultFields.map(({ label, description, value }) => ( + {resultFields.map(({ label, description, value, valueFooter }) => ( @@ -82,7 +87,14 @@ export default function ResultParams({ {isAnimationActive ? ( ) : ( - {value} +
+ {value} + {valueFooter && ( + + {valueFooter} + + )} +
)}
diff --git a/src/SimulationInputParams.tsx b/src/SimulationInputParams.tsx index 038cb08..bde6ab2 100644 --- a/src/SimulationInputParams.tsx +++ b/src/SimulationInputParams.tsx @@ -1,9 +1,9 @@ import React, { useState, useEffect } from "react"; import { InputFieldInterface, CurveParamsInterface } from "./types"; -import InputParams from "./InputParams"; +import InputParamBig from "./InputParamBig"; import { parameterDescriptions } from "./parametersDescriptions"; -export default function CurveDesignInputParams({ +export default function SimulationInputParams({ curveParams, setCurveParams }: { @@ -25,7 +25,7 @@ export default function CurveDesignInputParams({ const inputFields: InputFieldInterface[] = [ { - label: `${parameterDescriptions.d0.name} (DAI)`, + label: `${parameterDescriptions.d0.name}`, description: parameterDescriptions.d0.text, value: d0, setter: setD0, @@ -35,13 +35,12 @@ export default function CurveDesignInputParams({ 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 + toNum: (n: string) => Math.floor(parseFloat(n) * 1e6) } ]; return ( - diff --git a/src/TextWithPopover.tsx b/src/TextWithPopover.tsx index 781e1e5..1c558de 100644 --- a/src/TextWithPopover.tsx +++ b/src/TextWithPopover.tsx @@ -8,7 +8,6 @@ const useStyles = makeStyles(theme => ({ container: { color: theme.palette.text.secondary, display: "flex", - marginLeft: "6px", fontSize: "0.9rem", cursor: "pointer", transition: "color ease 150ms", @@ -61,7 +60,7 @@ export default function TextWithPopover({ return (
- {content} + {content}