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} /> )}
); }