import React, { useState, useEffect, useMemo } from "react";
// Material UI
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import Container from "@material-ui/core/Container";
import Typography from "@material-ui/core/Typography";
import Box from "@material-ui/core/Box";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid";
import Button from "@material-ui/core/Button";
import Fade from "@material-ui/core/Fade";
// Components
import Header from "./Header";
import InputParams from "./InputParams";
import SupplyVsDemandChart from "./SupplyVsDemandChart";
import ResultParams from "./ResultParams";
import PriceSimulationChart from "./PriceSimulationChart";
import HelpText from "./HelpText";
// Utils
import { getLast, getAvg, pause } from "./utils";
import {
getInitialParams,
getPriceR,
getRandomDeltas,
getSlippage
} from "./math";
import { throttle } from "lodash";
// General styles
import "./app.css";
const headerOffset = 10;
const useStyles = makeStyles((theme: Theme) =>
createStyles({
mainContainer: {
"& > div:not(:last-child)": {
paddingBottom: theme.spacing(3)
},
"& > div": {
"& > div": {
paddingTop: "0 !important"
}
},
paddingBottom: theme.spacing(9)
},
paper: {
width: "100%",
height: "100%",
minHeight: 310,
backgroundColor: "#293640"
},
box: {
padding: theme.spacing(3, 3),
minHeight: 310
},
boxButton: {
padding: theme.spacing(3, 3)
},
boxHeader: {
padding: theme.spacing(3, 3),
height: theme.spacing(headerOffset),
display: "flex",
alignItems: "center",
borderBottom: "1px solid #313d47"
},
boxChart: {
width: "100%",
height: "100%",
minHeight: 310,
maxHeight: 350,
padding: theme.spacing(3, 3),
// Correct the chart excessive margins
paddingRight: "5px",
paddingLeft: "5px"
},
header: {
backgroundColor: "#0b1216",
color: "#f8f8f8",
textAlign: "center",
padding: theme.spacing(3, 0, 6 + headerOffset),
marginBottom: -theme.spacing(headerOffset)
},
button: {
background: "linear-gradient(290deg, #2ad179, #4ab47c)",
color: "white"
}
})
);
export default function App() {
const [curveParams, setCurveParams] = useState({
d0: 1e6, // Initial raise, d0 (DAI)
theta: 0.35, // fraction allocated to reserve (.)
p0: 0.1, // Hatch sale price p0 (DAI / token)
returnF: 3, // Return factor (.)
wFee: 0.05 // friction coefficient (.)
});
const { d0, theta, p0, returnF, wFee } = curveParams;
/**
* Throttle the curve update to prevent the expensive chart
* to re-render too often
*/
const setCurveParamsThrottle = useMemo(
() => throttle(setCurveParams, 1000),
[]
);
// Simulation results
const {
k, // Invariant power kappa (.)
R0, // Initial reserve (DAI)
S0, // initial supply of tokens (token)
V0 // invariant coef
} = getInitialParams({
d0,
theta,
p0,
returnF
});
const [priceTimeseries, setPriceTimeseries] = useState([0]);
const [withdrawFeeTimeseries, setWithdrawFeeTimeseries] = useState([0]);
const [totalReserve, setTotalReserve] = useState(R0);
const [withdrawCount, setWithdrawCount] = useState(0);
const [avgSlippage, setAvgSlippage] = useState(0);
const [avgTxSize] = useState(10000);
// Simulation state variables
const [simulationActive, setSimulationActive] = useState(false);
const [simulationRunning, setSimulationRunning] = useState(false);
// #### TEST: Immediate simulation
async function startSimulation() {
// If there's a simulation already active, clear it
clearSimulation();
await pause(0);
// Start simulation by setting it to active
setSimulationActive(true);
}
function clearSimulation() {
// Stop simulation
setSimulationActive(false);
// Clear simulation variables
setWithdrawCount(0);
setPriceTimeseries([0]);
setWithdrawFeeTimeseries([0]);
setAvgSlippage(0);
}
useEffect(() => {
let canContinueSimulation = true;
async function simulateRandomDelta() {
const R_t: number[] = [R0];
const p_t: number[] = [getPriceR({ R: R0, V0, k })];
const wFee_t: number[] = [0];
const slippage_t: number[] = [];
// Random walk
const numSteps = 100;
const updateEveryNth = 1;
// Compute the random deltas
const deltaR_t = getRandomDeltas({ numSteps, avgTxSize });
setSimulationRunning(true);
for (let i = 0; i < numSteps; i++) {
const deltaR = deltaR_t[i];
const R = getLast(R_t);
const R_next = R + deltaR;
R_t.push(R_next);
p_t.push(getPriceR({ R: R_next, V0, k }));
slippage_t.push(getSlippage({ R, deltaR, V0, k }));
// Consider withdraw fees on sales only
if (deltaR < 0) {
wFee_t.push(getLast(wFee_t) - deltaR * wFee);
setWithdrawCount(c => c + 1);
} else {
wFee_t.push(getLast(wFee_t));
}
// Stop the simulation if it's no longer active
if (!simulationActive || !canContinueSimulation) break;
if (i % updateEveryNth === 0) {
setPriceTimeseries(p_t);
setWithdrawFeeTimeseries(wFee_t);
setAvgSlippage(getAvg(slippage_t));
setTotalReserve(R_next);
// Make this run non-UI blocking
await pause(5);
}
}
setSimulationRunning(false);
}
if (simulationActive) simulateRandomDelta();
// Return an "unsubscribe" function that halts the run
return () => {
canContinueSimulation = false;
};
}, [simulationActive]);
const resultFields = [
{
label: `Average slippage (avg tx size ${avgTxSize} DAI)`,
value: +(100 * avgSlippage).toFixed(3) + "%"
},
{
label: `Capital collected from withdraw fees (${withdrawCount} txs)`,
value:
(+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() +
" DAI"
},
{
label: `Total reserve`,
value: (+totalReserve.toPrecision(3)).toLocaleString() + " DAI"
}
];
const classes = useStyles();
return (
<>
Curve Design
Description of the different parameters
Initial raise: Lorem ipsum
Allocation to project: Lorem ipsum
Initial token price: Lorem ipsum
Return factor: Lorem ipsum
Withdrawl fee: Lorem ipsum
}
/>
Preview
Preview of the token bonding curve}
/>
{simulationActive && (
Simulation
Some context about this simulation}
/>
Results
Explanation of what do this results mean}
/>
)}
>
);
}