This commit is contained in:
Zargham 2019-08-11 11:59:05 +02:00
commit a0d6bf03af
11 changed files with 431 additions and 112 deletions

View File

@ -15,15 +15,27 @@ import SupplyVsDemandChart from "./SupplyVsDemandChart";
import ResultParams from "./ResultParams";
import PriceSimulationChart from "./PriceSimulationChart";
import HelpText from "./HelpText";
// Text content
import {
parameterDescriptions,
simulationParameterDescriptions,
resultParameterDescriptions
} from "./parametersDescriptions";
// Utils
import { getLast, getAvg, pause } from "./utils";
import {
getInitialParams,
getPriceR,
getMinPrice,
getS,
vest_tokens,
getR,
getSlippage,
getTxDistribution,
getDeltaR_priceGrowth,
rv_U
rv_U,
getMedian,
getSum
} from "./math";
import { throttle } from "lodash";
// General styles
@ -116,65 +128,17 @@ const useStyles = makeStyles((theme: Theme) =>
}
},
descriptionTitle: {
fontWeight: theme.typography.fontWeightBold,
padding: theme.spacing(0.5)
},
descriptionName: {
fontWeight: theme.typography.fontWeightBold
descriptionBody: {
color: "#dbdfe4"
},
descriptionPadding: {
padding: theme.spacing(0.5)
}
})
);
const parameterDescriptions = [
{
name: "Initial raise",
text: "Total funds raised in the hatch period of the ABC launch"
},
{
name: "Allocation to funding pool",
text:
"The percentage of the funds raised in the Hatch sale that go directly into the project funding pool to compensate future work done in the project"
},
{
name: "Hatch price",
text:
"The price paid per 'ABC token' by community members involved in hatching the project"
},
{
name: "Post-hatch price",
text:
"The price of the 'ABC token' when the curve enters the open phase and is live for public participation"
},
{
name: "Exit tribute",
text:
"The percentage of funds that are diverted to the project funding pool from community members who exit funds from the project by burning 'ABC tokens' in exchange for collateral"
}
];
const resultParameterDescriptions = [
{
name: "Total reserve",
text:
"Total DAI in the smart contract reserve at the end of the simulated period"
},
{
name: "Funds generated from initial hatch",
text:
"Fraction of the funds (theta) raised during the hatch that go directly to the cause"
},
{
name: "Funds generated from exit tributes",
text:
"Cumulative amount of exit tributes collected from only exit /sell transactions"
},
{
name: "Average slippage",
text:
"Average of the slippage of each transaction occured during the simulation period"
}
];
export default function App() {
const [curveParams, setCurveParams] = useState({
theta: 0.35, // fraction allocated to reserve (.)
@ -199,7 +163,7 @@ export default function App() {
const {
k, // Invariant power kappa (.)
R0, // Initial reserve (DAI)
// S0, // initial supply of tokens (token)
S0, // initial supply of tokens (token)
V0 // invariant coef
} = getInitialParams({
d0,
@ -210,6 +174,7 @@ export default function App() {
const [priceTimeseries, setPriceTimeseries] = useState([0]);
const [withdrawFeeTimeseries, setWithdrawFeeTimeseries] = useState([0]);
const [floorpriceTimeseries, setFloorpriceTimeseries] = useState([0]);
const [totalReserve, setTotalReserve] = useState(R0);
const [withdrawCount, setWithdrawCount] = useState(0);
const [avgSlippage, setAvgSlippage] = useState(0);
@ -248,48 +213,100 @@ export default function App() {
async function simulateRandomDelta() {
const R_t: number[] = [R0];
const S_t: number[] = [S0];
const p_t: number[] = [getPriceR({ R: R0, V0, k })];
const wFee_t: number[] = [0];
const slippage_t: number[] = [];
const avgTxSize_t: number[] = [];
// hatchers tokens = S0[section added by Z]
const H_t: number[] = [S0]; // total hatcher tokens not vested
const floorprice_t: number[] = []; // initially the price is the floor as all tokens are hatcher tokens
// Random walk
const numSteps = 52;
const u_min = 0.97;
const u_max = 1.04;
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
// percentage of the hatch tokens which vest per week(since that is our timescale in the sim)
// numSteps = 52 take 8ms to run
setSimulationRunning(true);
for (let t = 0; t < numSteps; t++) {
const txsWeek = Math.ceil(t < 5 ? rv_U(0, 5) : rv_U(5, 2 * t));
const priceGrowth = rv_U(0.99, 1.03);
const txsWeek = rv_U(5, 2 * t + 5);
const R = getLast(R_t);
const deltaR = getDeltaR_priceGrowth({ R, k, priceGrowth });
const S = getLast(S_t);
const H = getLast(H_t);
// enforce the effects of the unvested tokens not being burnable
let u_lower;
if (H > S) {
u_lower = 1;
} else {
// compute the reserve if all that supply is burned
const R_ratio = getR({ S: S - H, V0, k }) / R;
u_lower = Math.max(1 - R_ratio, u_min);
}
const priceGrowth = rv_U(u_lower, u_max);
const deltaR = getDeltaR_priceGrowth({ R, k, priceGrowth });
const R_next = R + deltaR;
const txs = getTxDistribution({ sum: deltaR, num: txsWeek });
const txs = getTxDistribution({
sum: deltaR,
num: txsWeek,
spread: tx_spread
});
// Compute slippage
const slippage = getAvg(
txs.map(txR => getSlippage({ R, deltaR: txR, V0, k }))
const slippage_txs = txs.map(txR =>
getSlippage({ R, deltaR: txR, V0, k })
);
const slippage = getMedian(slippage_txs);
const txsWithdraw = txs.filter(tx => tx < 0);
const wFees = -wFee * txsWithdraw.reduce((t, c) => t + c, 0);
const _avgTxSize =
txs.reduce((t, c) => t + Math.abs(c), 0) / txs.length;
const wFees = -wFee * getSum(txsWithdraw);
// txsWithdraw.reduce((t, c) => t + c, 0);
// Vest
const delta_H = vest_tokens({ week: t, H, halflife, cliff });
const H_next = H - delta_H;
// find floor price
const S_next = getS({ R, V0, k });
const floorprice_next = getMinPrice({
S: S_next,
H: S0 - H_next,
V0,
k
});
const _avgTxSize = getMedian(txsWithdraw);
R_t.push(R_next);
S_t.push(S_next);
H_t.push(H_next);
p_t.push(getPriceR({ R: R_next, V0, k }));
slippage_t.push(slippage);
avgTxSize_t.push(_avgTxSize);
wFee_t.push(getLast(wFee_t) + wFees);
floorprice_t.push(floorprice_next);
setWithdrawCount(c => c + txsWithdraw.length);
// Stop the simulation if it's no longer active
if (!simulationActive || !canContinueSimulation) break;
}
// floorprice_t is missing one data point
floorprice_t[floorprice_t.length] = floorprice_t[floorprice_t.length - 1];
setPriceTimeseries(p_t);
setWithdrawFeeTimeseries(wFee_t);
setFloorpriceTimeseries(floorprice_t);
setAvgSlippage(getAvg(slippage_t));
setAvgTxSize(getAvg(avgTxSize_t));
setTotalReserve(getLast(R_t));
@ -307,14 +324,17 @@ export default function App() {
const resultFields = [
{
label: `Total reserve`,
description: resultParameterDescriptions.totalReserve.text,
value: (+totalReserve.toPrecision(3)).toLocaleString() + " DAI"
},
{
label: `Funds generated from initial hatch`,
description: resultParameterDescriptions.initialHatchFunds.text,
value: Math.round(d0 * theta).toLocaleString() + " DAI"
},
{
label: `Funds generated from exit tributes (${withdrawCount} txs)`,
description: resultParameterDescriptions.exitTributes.text,
value:
(+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() +
" DAI"
@ -323,6 +343,7 @@ export default function App() {
label: `Average slippage (avg tx size ${Math.round(
avgTxSize
).toLocaleString()} DAI)`,
description: resultParameterDescriptions.slippage.text,
value: +(100 * avgSlippage).toFixed(3) + "%"
}
];
@ -353,15 +374,21 @@ export default function App() {
</div>
<table>
<tbody>
{parameterDescriptions.map(({ name, text }) => (
{[
parameterDescriptions.theta,
parameterDescriptions.p0,
parameterDescriptions.p1,
parameterDescriptions.wFee,
parameterDescriptions.d0
].map(({ name, text }) => (
<tr key={name}>
<td>
<Typography className={classes.descriptionName}>
{name}
</Typography>
<Typography>{name}</Typography>
</td>
<td>
<Typography>{text}</Typography>
<Typography className={classes.descriptionBody}>
{text}
</Typography>
</td>
</tr>
))}
@ -398,13 +425,16 @@ export default function App() {
<Typography variant="h6">Preview</Typography>
<HelpText
text={
<Typography>
Visualization of the token bonding curve analytic function
on a specific range of reserve [0, 4 * R0]. This result is
deterministic given the current set of parameters and will
never change regardes of the campaign performance, it only
shows how the price will react to reserve changes.
</Typography>
<div className={classes.descriptionPadding}>
<Typography className={classes.descriptionBody}>
Visualization of the token bonding curve analytic
function on a specific range of reserve [0, 4 * R0].
This result is deterministic given the current set of
parameters and will never change regardes of the
campaign performance, it only shows how the price will
react to reserve changes.
</Typography>
</div>
}
/>
</Box>
@ -449,14 +479,40 @@ export default function App() {
<Typography variant="h6">Simulation</Typography>
<HelpText
text={
<Typography>
This chart shows a 52 week simulation of discrete
transactions interacting with the token bonding curve.
Each transaction adds or substract reserve to the
system, modifying the price over time. The frequency,
size and direction of each transaction is computed
from a set of bounded random functions.
</Typography>
<div className={classes.descriptionContainer}>
<div className={classes.descriptionPadding}>
<Typography className={classes.descriptionBody}>
This chart shows a 52 week simulation of discrete
transactions interacting with the token bonding
curve. Each transaction adds or substract reserve
to the system, modifying the price over time. The
frequency, size and direction of each transaction
is computed from a set of bounded random
functions.
</Typography>
</div>
<table>
<tbody>
{Object.values(
simulationParameterDescriptions
).map(({ name, text }) => (
<tr key={name}>
<td>
<Typography>{name}</Typography>
</td>
<td>
<Typography
className={classes.descriptionBody}
>
{text}
</Typography>
</td>
</tr>
))}
</tbody>
</table>
</div>
}
/>
</Box>
@ -465,6 +521,7 @@ export default function App() {
<PriceSimulationChart
priceTimeseries={priceTimeseries}
withdrawFeeTimeseries={withdrawFeeTimeseries}
floorpriceTimeseries={floorpriceTimeseries}
p0={p0}
p1={p1}
/>
@ -486,18 +543,18 @@ export default function App() {
</div>
<table>
<tbody>
{resultParameterDescriptions.map(
{Object.values(resultParameterDescriptions).map(
({ name, text }) => (
<tr key={name}>
<td>
<Typography
className={classes.descriptionName}
>
{name}
</Typography>
<Typography>{name}</Typography>
</td>
<td>
<Typography>{text}</Typography>
<Typography
className={classes.descriptionBody}
>
{text}
</Typography>
</td>
</tr>
)

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react";
import { InputFieldInterface, CurveParamsInterface } from "./types";
import InputParams from "./InputParams";
import { parameterDescriptions } from "./parametersDescriptions";
export default function CurveDesignInputParams({
curveParams,
@ -42,6 +43,7 @@ export default function CurveDesignInputParams({
const inputFields: InputFieldInterface[] = [
{
label: "Allocation to funding pool",
description: parameterDescriptions.theta.text,
value: theta,
setter: setTheta,
min: 0,
@ -54,6 +56,7 @@ export default function CurveDesignInputParams({
},
{
label: "Hatch price (DAI/token)",
description: parameterDescriptions.p0.text,
value: p0,
setter: _setP0,
min: 0.01,
@ -65,6 +68,7 @@ export default function CurveDesignInputParams({
},
{
label: "Post-hatch price (DAI/token)",
description: parameterDescriptions.p1.text,
value: p1,
setter: setP1,
min: p0 || 0.1,
@ -76,6 +80,7 @@ export default function CurveDesignInputParams({
},
{
label: "Exit tribute",
description: parameterDescriptions.wFee.text,
value: wFee,
setter: setWFee,
min: 0,

View File

@ -1,11 +1,11 @@
import React from "react";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import Grid from "@material-ui/core/Grid";
import TextField from "@material-ui/core/TextField";
import NumberFormat from "react-number-format";
import { InputFieldInterface } from "./types";
import PrettoSlider from "./PrettoSlider";
import TextWithPopover from "./TextWithPopover";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@ -81,6 +81,7 @@ export default function InputParams({
{inputFields.map(
({
label,
description,
value,
setter,
min,
@ -103,9 +104,7 @@ export default function InputParams({
return (
<Grid key={label} container spacing={0} className={classes.listBox}>
<Grid item xs={6} className={classes.leftContainer}>
<Typography id={label} gutterBottom>
{label}
</Typography>
<TextWithPopover content={label} popoverText={description} />
</Grid>
<Grid item xs={2} className={classes.centerContainer}>

View File

@ -17,6 +17,7 @@ import { linspace } from "./utils";
const keyHorizontal = "x";
const keyVerticalLeft = "Price (DAI/token)";
const keyVerticalRight = "Total exit tributes (DAI)";
const keyVerticalLeft2 = "Floor price (DAI/token)";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@ -32,11 +33,13 @@ const useStyles = makeStyles((theme: Theme) =>
function PriceSimulationChart({
priceTimeseries,
withdrawFeeTimeseries,
floorpriceTimeseries,
p0,
p1
}: {
priceTimeseries: number[];
withdrawFeeTimeseries: number[];
floorpriceTimeseries: number[];
p0: number;
p1: number;
}) {
@ -51,6 +54,7 @@ function PriceSimulationChart({
data.push({
[keyHorizontal]: t,
[keyVerticalLeft]: priceTimeseries[t] || 0,
[keyVerticalLeft2]: floorpriceTimeseries[t] || 0,
[keyVerticalRight]: withdrawFeeTimeseries[t] || 0
});
}
@ -70,8 +74,8 @@ function PriceSimulationChart({
const { textAnchor, viewBox, text } = props;
return (
<text
x={viewBox.x + 10}
y={viewBox.y - 10}
x={viewBox.x + 8}
y={viewBox.y + 17}
fill={theme.palette.text.secondary}
textAnchor={textAnchor}
>
@ -83,10 +87,12 @@ function PriceSimulationChart({
function CustomTooltip({ active, payload, label }: any) {
if (active) {
const price = payload[0].value;
const exit = payload[1].value;
const floor = payload[1].value;
const exit = payload[2].value;
const weekNum = label;
const toolTipData: string[][] = [
["Price", price.toFixed(2), "DAI/tk"],
["Floor", floor.toFixed(2), "DAI/tk"],
["Exit t.", formatter(exit), "DAI"],
["Week", weekNum, ""]
];
@ -148,12 +154,10 @@ function PriceSimulationChart({
{/* Capital collected from withdraw fees - AXIS */}
<YAxis
yAxisId="right"
// domain={[
// Math.floor(Math.min(...withdrawFeeTimeseries)),
// Math.ceil(Math.max(...withdrawFeeTimeseries))
// ]}
domain={[0, +(2 * withdrawFeeTimeseries.slice(-1)[0]).toPrecision(1)]}
orientation="right"
tick={{ fill: theme.palette.text.secondary }}
tickFormatter={formatter}
stroke={theme.palette.text.secondary}
/>
@ -166,7 +170,20 @@ function PriceSimulationChart({
dataKey={keyVerticalLeft}
stroke={theme.palette.primary.main}
fill={theme.palette.primary.main}
fillOpacity={0.3}
strokeWidth={2}
/>
<Area
isAnimationActive={false}
yAxisId="left"
type="monotone"
dataKey={keyVerticalLeft2}
stroke={"#adcd2e"}
fill={"#adcd2e"}
fillOpacity={0.05}
strokeWidth={2}
/>
<ReferenceLine
y={p0}
yAxisId="left"
@ -188,9 +205,10 @@ function PriceSimulationChart({
yAxisId="right"
type="monotone"
dataKey={keyVerticalRight}
stroke={theme.palette.secondary.dark}
stroke={"#0085ff"}
fill={theme.palette.secondary.dark}
fillOpacity="0.8"
fillOpacity={0.5}
strokeWidth={2}
/>
{/* <ReferenceLine

View File

@ -2,6 +2,7 @@ import React from "react";
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography";
import Grid from "@material-ui/core/Grid";
import TextWithPopover from "./TextWithPopover";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@ -45,6 +46,7 @@ export default function ResultParams({
}: {
resultFields: {
label: string;
description: string;
value: number | string;
}[];
}) {
@ -52,12 +54,10 @@ export default function ResultParams({
return (
<div className={classes.listBoxContainer}>
{resultFields.map(({ label, value }) => (
{resultFields.map(({ label, description, value }) => (
<Grid key={label} container spacing={0} className={classes.listBox}>
<Grid item xs={8} className={classes.leftContainer}>
<Typography id={label} gutterBottom>
{label}
</Typography>
<TextWithPopover content={label} popoverText={description} />
</Grid>
<Grid item xs={4} className={classes.centerContainer}>

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect } from "react";
import { InputFieldInterface, CurveParamsInterface } from "./types";
import InputParams from "./InputParams";
import { parameterDescriptions } from "./parametersDescriptions";
export default function CurveDesignInputParams({
curveParams,
@ -25,6 +26,7 @@ export default function CurveDesignInputParams({
const inputFields: InputFieldInterface[] = [
{
label: "Initial raise (DAI)",
description: parameterDescriptions.d0.text,
value: d0,
setter: setD0,
min: 0.1e6,

View File

@ -186,6 +186,8 @@ function SupplyVsDemandChart({
dataKey={keyVertical}
stroke={theme.palette.primary.main}
fill={theme.palette.primary.main}
fillOpacity={0.3}
strokeWidth={2}
/>
<ReferenceLine
x={R0_round}

93
src/TextWithPopover.tsx Normal file
View File

@ -0,0 +1,93 @@
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";
const useStyles = makeStyles(theme => ({
container: {
color: theme.palette.text.secondary,
display: "flex",
marginLeft: "6px",
fontSize: "0.9rem",
cursor: "pointer",
transition: "color ease 150ms",
"&:hover": {
color: "#c3c9d0"
}
},
popoverContainer: {
padding: theme.spacing(2),
"& > p:not(:last-child)": {
paddingBottom: theme.spacing(1),
marginBottom: theme.spacing(1),
borderBottom: "1px solid #3f5463"
}
},
paper: {
backgroundColor: "#384b59",
maxWidth: theme.breakpoints.values.md * 0.9,
[`@media screen and (max-width: ${theme.breakpoints.values.md}px)`]: {
maxWidth: "90vw"
},
padding: theme.spacing(0.5)
},
descriptionBody: {
color: "#dbdfe4"
}
}));
export default function TextWithPopover({
content,
popoverText
}: {
content: string;
popoverText: string;
}) {
const classes = useStyles();
const [anchorEl, setAnchorEl] = React.useState(null);
function handleClick(event: any) {
setAnchorEl(event.currentTarget);
}
function handleClose() {
setAnchorEl(null);
}
const open = Boolean(anchorEl);
const id = open ? "simple-popover" : undefined;
return (
<div className={classes.container}>
<div aria-describedby={id} onClick={handleClick}>
<Typography gutterBottom>{content}</Typography>
</div>
<Popover
PaperProps={{
className: classes.paper
}}
id={id}
open={open}
anchorEl={anchorEl}
onClose={handleClose}
onClick={handleClose}
anchorOrigin={{
vertical: "bottom",
horizontal: "center"
}}
transformOrigin={{
vertical: "top",
horizontal: "center"
}}
>
<Box className={classes.popoverContainer}>
<Typography>{content}</Typography>
<Typography className={classes.descriptionBody}>
{popoverText}
</Typography>
</Box>
</Popover>
</div>
);
}

View File

@ -23,6 +23,37 @@ export function getInitialParams({
return { k, R0, S0, V0 };
}
export function getR({ S, V0, k }: { S: number; V0: number; k: number }) {
return S ** k / V0;
}
export function getS({ R, V0, k }: { R: number; V0: number; k: number }) {
return (V0 * R) ** (1 / k);
}
// compute the price if all that supply is burned
export function getMinPrice({
S,
H,
V0,
k
}: {
S: number;
H: number;
V0: number;
k: number;
}) {
if (S === H) {
const myR = getR({ S, V0, k });
const myP = getPriceR({ R: myR, V0, k }); // numerical precision make complex numbers just suppress it
return Math.abs(myP);
} else {
// compute the reserve if all that supply is burned
const minR = getR({ S: S - H, V0, k });
return getPriceR({ R: minR, V0, k });
}
}
/**
* Computes the price at a specific reserve `R`
*/
@ -75,17 +106,47 @@ export function getDeltaR_priceGrowth({
* Demo: https://codepen.io/anon/pen/mNqJjv?editors=0010#0
* Very quick: < 10ms for 10000 txs
*/
export function getTxDistribution({ sum, num }: { sum: number; num: number }) {
export function getTxDistribution({
sum,
num,
spread
}: {
sum: number;
num: number;
spread: number;
}) {
const mean = sum / num;
const off = mean * 4;
const off = mean * spread;
const x: number[] = [];
for (let i = 0; i < num; i++) {
x[i] = randn_bm(mean - off, mean + off);
x.push(randn_bm(mean - off, mean + off));
}
return x;
}
// Minor utils
export function vest_tokens({
week,
H, // unvested_hatch_tokens
halflife,
cliff
}: {
week: number;
H: number;
halflife: number;
cliff: number;
}) {
// check cliff
if (week < cliff) {
return 0;
} else {
// rate of release given half - life
const vest_fraction = 0.5 ** (1 / halflife);
// number of tokens that vest in this week
return H * (1 - vest_fraction);
}
}
// Statistics utils
/**
* Random variable uniformly distributed
@ -111,3 +172,15 @@ function randn_bm(min: number, max: number) {
num += min; // offset to min
return num;
}
// Array utils
export function getMedian(arr: number[]) {
const mid = Math.floor(arr.length / 2);
const nums = [...arr].sort((a, b) => a - b);
return arr.length % 2 !== 0 ? nums[mid] : (nums[mid - 1] + nums[mid]) / 2;
}
export function getSum(arr: number[]) {
return arr.reduce((a, b) => a + b, 0);
}

View File

@ -0,0 +1,69 @@
export interface DescriptionObject {
[key: string]: { name: string; text: string };
}
export const parameterDescriptions: DescriptionObject = {
theta: {
name: "Allocation to funding pool",
text:
"The percentage of the funds raised in the Hatch sale that go directly into the project funding pool to compensate future work done in the project"
},
p0: {
name: "Hatch price",
text:
"The price paid per 'ABC token' by community members involved in hatching the project"
},
p1: {
name: "Post-hatch price",
text:
"The price of the 'ABC token' when the curve enters the open phase and is live for public participation"
},
wFee: {
name: "Exit tribute",
text:
"The percentage of funds that are diverted to the project funding pool from community members who exit funds from the project by burning 'ABC tokens' in exchange for collateral"
},
d0: {
name: "Initial raise",
text: "Total funds raised in the hatch period of the ABC launch"
}
};
export const simulationParameterDescriptions: DescriptionObject = {
price: {
name: "Price",
text: "Price of the token over time."
},
floorPrice: {
name: "Floor price",
text:
"Lower bound of the price guaranteed by the vesting of hatch tokens. It decreases over time as more hatch tokens are allowed to be traded"
},
exitTributes: {
name: "Total exit tributes",
text:
"Cumulative sum of exit tributes collected from only exit /sell transactions"
}
};
export const resultParameterDescriptions: DescriptionObject = {
totalReserve: {
name: "Total reserve",
text:
"Total DAI in the smart contract reserve at the end of the simulated period"
},
initialHatchFunds: {
name: "Funds generated from initial hatch",
text:
"Fraction of the funds (theta) raised during the hatch that go directly to the cause (analytic result)"
},
exitTributes: {
name: "Funds generated from exit tributes",
text: simulationParameterDescriptions.exitTributes.text
},
slippage: {
name: "Average slippage",
text:
"Average of the slippage of each transaction occured during the simulation period"
}
};

View File

@ -1,5 +1,6 @@
export interface InputFieldInterface {
label: string;
description: string;
value: number;
setter(newValue: any): void;
min: number;