Merge branch 'master' of https://github.com/commons-stack/augmented-tbc-design
This commit is contained in:
commit
a0d6bf03af
239
src/App.tsx
239
src/App.tsx
|
|
@ -15,15 +15,27 @@ import SupplyVsDemandChart from "./SupplyVsDemandChart";
|
||||||
import ResultParams from "./ResultParams";
|
import ResultParams from "./ResultParams";
|
||||||
import PriceSimulationChart from "./PriceSimulationChart";
|
import PriceSimulationChart from "./PriceSimulationChart";
|
||||||
import HelpText from "./HelpText";
|
import HelpText from "./HelpText";
|
||||||
|
// Text content
|
||||||
|
import {
|
||||||
|
parameterDescriptions,
|
||||||
|
simulationParameterDescriptions,
|
||||||
|
resultParameterDescriptions
|
||||||
|
} from "./parametersDescriptions";
|
||||||
// Utils
|
// Utils
|
||||||
import { getLast, getAvg, pause } from "./utils";
|
import { getLast, getAvg, pause } from "./utils";
|
||||||
import {
|
import {
|
||||||
getInitialParams,
|
getInitialParams,
|
||||||
getPriceR,
|
getPriceR,
|
||||||
|
getMinPrice,
|
||||||
|
getS,
|
||||||
|
vest_tokens,
|
||||||
|
getR,
|
||||||
getSlippage,
|
getSlippage,
|
||||||
getTxDistribution,
|
getTxDistribution,
|
||||||
getDeltaR_priceGrowth,
|
getDeltaR_priceGrowth,
|
||||||
rv_U
|
rv_U,
|
||||||
|
getMedian,
|
||||||
|
getSum
|
||||||
} from "./math";
|
} from "./math";
|
||||||
import { throttle } from "lodash";
|
import { throttle } from "lodash";
|
||||||
// General styles
|
// General styles
|
||||||
|
|
@ -116,65 +128,17 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
descriptionTitle: {
|
descriptionTitle: {
|
||||||
fontWeight: theme.typography.fontWeightBold,
|
|
||||||
padding: theme.spacing(0.5)
|
padding: theme.spacing(0.5)
|
||||||
},
|
},
|
||||||
descriptionName: {
|
descriptionBody: {
|
||||||
fontWeight: theme.typography.fontWeightBold
|
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() {
|
export default function App() {
|
||||||
const [curveParams, setCurveParams] = useState({
|
const [curveParams, setCurveParams] = useState({
|
||||||
theta: 0.35, // fraction allocated to reserve (.)
|
theta: 0.35, // fraction allocated to reserve (.)
|
||||||
|
|
@ -199,7 +163,7 @@ export default function App() {
|
||||||
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
|
V0 // invariant coef
|
||||||
} = getInitialParams({
|
} = getInitialParams({
|
||||||
d0,
|
d0,
|
||||||
|
|
@ -210,6 +174,7 @@ export default function App() {
|
||||||
|
|
||||||
const [priceTimeseries, setPriceTimeseries] = useState([0]);
|
const [priceTimeseries, setPriceTimeseries] = useState([0]);
|
||||||
const [withdrawFeeTimeseries, setWithdrawFeeTimeseries] = useState([0]);
|
const [withdrawFeeTimeseries, setWithdrawFeeTimeseries] = useState([0]);
|
||||||
|
const [floorpriceTimeseries, setFloorpriceTimeseries] = useState([0]);
|
||||||
const [totalReserve, setTotalReserve] = useState(R0);
|
const [totalReserve, setTotalReserve] = useState(R0);
|
||||||
const [withdrawCount, setWithdrawCount] = useState(0);
|
const [withdrawCount, setWithdrawCount] = useState(0);
|
||||||
const [avgSlippage, setAvgSlippage] = useState(0);
|
const [avgSlippage, setAvgSlippage] = useState(0);
|
||||||
|
|
@ -248,48 +213,100 @@ export default function App() {
|
||||||
|
|
||||||
async function simulateRandomDelta() {
|
async function simulateRandomDelta() {
|
||||||
const R_t: number[] = [R0];
|
const R_t: number[] = [R0];
|
||||||
|
const S_t: number[] = [S0];
|
||||||
const p_t: number[] = [getPriceR({ R: R0, V0, k })];
|
const p_t: number[] = [getPriceR({ R: R0, V0, k })];
|
||||||
const wFee_t: number[] = [0];
|
const wFee_t: number[] = [0];
|
||||||
const slippage_t: number[] = [];
|
const slippage_t: number[] = [];
|
||||||
const avgTxSize_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
|
// Random walk
|
||||||
const numSteps = 52;
|
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
|
// numSteps = 52 take 8ms to run
|
||||||
setSimulationRunning(true);
|
setSimulationRunning(true);
|
||||||
for (let t = 0; t < numSteps; t++) {
|
for (let t = 0; t < numSteps; t++) {
|
||||||
const txsWeek = Math.ceil(t < 5 ? rv_U(0, 5) : rv_U(5, 2 * t));
|
const txsWeek = rv_U(5, 2 * t + 5);
|
||||||
const priceGrowth = rv_U(0.99, 1.03);
|
|
||||||
|
|
||||||
const R = getLast(R_t);
|
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 R_next = R + deltaR;
|
||||||
|
|
||||||
const txs = getTxDistribution({ sum: deltaR, num: txsWeek });
|
const txs = getTxDistribution({
|
||||||
|
sum: deltaR,
|
||||||
|
num: txsWeek,
|
||||||
|
spread: tx_spread
|
||||||
|
});
|
||||||
// Compute slippage
|
// Compute slippage
|
||||||
const slippage = getAvg(
|
const slippage_txs = txs.map(txR =>
|
||||||
txs.map(txR => getSlippage({ R, deltaR: txR, V0, k }))
|
getSlippage({ R, deltaR: txR, V0, k })
|
||||||
);
|
);
|
||||||
|
const slippage = getMedian(slippage_txs);
|
||||||
|
|
||||||
const txsWithdraw = txs.filter(tx => tx < 0);
|
const txsWithdraw = txs.filter(tx => tx < 0);
|
||||||
const wFees = -wFee * txsWithdraw.reduce((t, c) => t + c, 0);
|
const wFees = -wFee * getSum(txsWithdraw);
|
||||||
const _avgTxSize =
|
// txsWithdraw.reduce((t, c) => t + c, 0);
|
||||||
txs.reduce((t, c) => t + Math.abs(c), 0) / txs.length;
|
|
||||||
|
// 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);
|
R_t.push(R_next);
|
||||||
|
S_t.push(S_next);
|
||||||
|
H_t.push(H_next);
|
||||||
p_t.push(getPriceR({ R: R_next, V0, k }));
|
p_t.push(getPriceR({ R: R_next, V0, k }));
|
||||||
slippage_t.push(slippage);
|
slippage_t.push(slippage);
|
||||||
avgTxSize_t.push(_avgTxSize);
|
avgTxSize_t.push(_avgTxSize);
|
||||||
wFee_t.push(getLast(wFee_t) + wFees);
|
wFee_t.push(getLast(wFee_t) + wFees);
|
||||||
|
|
||||||
|
floorprice_t.push(floorprice_next);
|
||||||
setWithdrawCount(c => c + txsWithdraw.length);
|
setWithdrawCount(c => c + txsWithdraw.length);
|
||||||
|
|
||||||
// Stop the simulation if it's no longer active
|
// Stop the simulation if it's no longer active
|
||||||
if (!simulationActive || !canContinueSimulation) break;
|
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);
|
setPriceTimeseries(p_t);
|
||||||
setWithdrawFeeTimeseries(wFee_t);
|
setWithdrawFeeTimeseries(wFee_t);
|
||||||
|
setFloorpriceTimeseries(floorprice_t);
|
||||||
setAvgSlippage(getAvg(slippage_t));
|
setAvgSlippage(getAvg(slippage_t));
|
||||||
setAvgTxSize(getAvg(avgTxSize_t));
|
setAvgTxSize(getAvg(avgTxSize_t));
|
||||||
setTotalReserve(getLast(R_t));
|
setTotalReserve(getLast(R_t));
|
||||||
|
|
@ -307,14 +324,17 @@ export default function App() {
|
||||||
const resultFields = [
|
const resultFields = [
|
||||||
{
|
{
|
||||||
label: `Total reserve`,
|
label: `Total reserve`,
|
||||||
|
description: resultParameterDescriptions.totalReserve.text,
|
||||||
value: (+totalReserve.toPrecision(3)).toLocaleString() + " DAI"
|
value: (+totalReserve.toPrecision(3)).toLocaleString() + " DAI"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: `Funds generated from initial hatch`,
|
label: `Funds generated from initial hatch`,
|
||||||
|
description: resultParameterDescriptions.initialHatchFunds.text,
|
||||||
value: Math.round(d0 * theta).toLocaleString() + " DAI"
|
value: Math.round(d0 * theta).toLocaleString() + " DAI"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: `Funds generated from exit tributes (${withdrawCount} txs)`,
|
label: `Funds generated from exit tributes (${withdrawCount} txs)`,
|
||||||
|
description: resultParameterDescriptions.exitTributes.text,
|
||||||
value:
|
value:
|
||||||
(+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() +
|
(+getLast(withdrawFeeTimeseries).toPrecision(3)).toLocaleString() +
|
||||||
" DAI"
|
" DAI"
|
||||||
|
|
@ -323,6 +343,7 @@ export default function App() {
|
||||||
label: `Average slippage (avg tx size ${Math.round(
|
label: `Average slippage (avg tx size ${Math.round(
|
||||||
avgTxSize
|
avgTxSize
|
||||||
).toLocaleString()} DAI)`,
|
).toLocaleString()} DAI)`,
|
||||||
|
description: resultParameterDescriptions.slippage.text,
|
||||||
value: +(100 * avgSlippage).toFixed(3) + "%"
|
value: +(100 * avgSlippage).toFixed(3) + "%"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
@ -353,15 +374,21 @@ export default function App() {
|
||||||
</div>
|
</div>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{parameterDescriptions.map(({ name, text }) => (
|
{[
|
||||||
|
parameterDescriptions.theta,
|
||||||
|
parameterDescriptions.p0,
|
||||||
|
parameterDescriptions.p1,
|
||||||
|
parameterDescriptions.wFee,
|
||||||
|
parameterDescriptions.d0
|
||||||
|
].map(({ name, text }) => (
|
||||||
<tr key={name}>
|
<tr key={name}>
|
||||||
<td>
|
<td>
|
||||||
<Typography className={classes.descriptionName}>
|
<Typography>{name}</Typography>
|
||||||
{name}
|
|
||||||
</Typography>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Typography>{text}</Typography>
|
<Typography className={classes.descriptionBody}>
|
||||||
|
{text}
|
||||||
|
</Typography>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
))}
|
))}
|
||||||
|
|
@ -398,13 +425,16 @@ export default function App() {
|
||||||
<Typography variant="h6">Preview</Typography>
|
<Typography variant="h6">Preview</Typography>
|
||||||
<HelpText
|
<HelpText
|
||||||
text={
|
text={
|
||||||
<Typography>
|
<div className={classes.descriptionPadding}>
|
||||||
Visualization of the token bonding curve analytic function
|
<Typography className={classes.descriptionBody}>
|
||||||
on a specific range of reserve [0, 4 * R0]. This result is
|
Visualization of the token bonding curve analytic
|
||||||
deterministic given the current set of parameters and will
|
function on a specific range of reserve [0, 4 * R0].
|
||||||
never change regardes of the campaign performance, it only
|
This result is deterministic given the current set of
|
||||||
shows how the price will react to reserve changes.
|
parameters and will never change regardes of the
|
||||||
</Typography>
|
campaign performance, it only shows how the price will
|
||||||
|
react to reserve changes.
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
@ -449,14 +479,40 @@ export default function App() {
|
||||||
<Typography variant="h6">Simulation</Typography>
|
<Typography variant="h6">Simulation</Typography>
|
||||||
<HelpText
|
<HelpText
|
||||||
text={
|
text={
|
||||||
<Typography>
|
<div className={classes.descriptionContainer}>
|
||||||
This chart shows a 52 week simulation of discrete
|
<div className={classes.descriptionPadding}>
|
||||||
transactions interacting with the token bonding curve.
|
<Typography className={classes.descriptionBody}>
|
||||||
Each transaction adds or substract reserve to the
|
This chart shows a 52 week simulation of discrete
|
||||||
system, modifying the price over time. The frequency,
|
transactions interacting with the token bonding
|
||||||
size and direction of each transaction is computed
|
curve. Each transaction adds or substract reserve
|
||||||
from a set of bounded random functions.
|
to the system, modifying the price over time. The
|
||||||
</Typography>
|
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>
|
</Box>
|
||||||
|
|
@ -465,6 +521,7 @@ export default function App() {
|
||||||
<PriceSimulationChart
|
<PriceSimulationChart
|
||||||
priceTimeseries={priceTimeseries}
|
priceTimeseries={priceTimeseries}
|
||||||
withdrawFeeTimeseries={withdrawFeeTimeseries}
|
withdrawFeeTimeseries={withdrawFeeTimeseries}
|
||||||
|
floorpriceTimeseries={floorpriceTimeseries}
|
||||||
p0={p0}
|
p0={p0}
|
||||||
p1={p1}
|
p1={p1}
|
||||||
/>
|
/>
|
||||||
|
|
@ -486,18 +543,18 @@ export default function App() {
|
||||||
</div>
|
</div>
|
||||||
<table>
|
<table>
|
||||||
<tbody>
|
<tbody>
|
||||||
{resultParameterDescriptions.map(
|
{Object.values(resultParameterDescriptions).map(
|
||||||
({ name, text }) => (
|
({ name, text }) => (
|
||||||
<tr key={name}>
|
<tr key={name}>
|
||||||
<td>
|
<td>
|
||||||
<Typography
|
<Typography>{name}</Typography>
|
||||||
className={classes.descriptionName}
|
|
||||||
>
|
|
||||||
{name}
|
|
||||||
</Typography>
|
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<Typography>{text}</Typography>
|
<Typography
|
||||||
|
className={classes.descriptionBody}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</Typography>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { InputFieldInterface, CurveParamsInterface } from "./types";
|
import { InputFieldInterface, CurveParamsInterface } from "./types";
|
||||||
import InputParams from "./InputParams";
|
import InputParams from "./InputParams";
|
||||||
|
import { parameterDescriptions } from "./parametersDescriptions";
|
||||||
|
|
||||||
export default function CurveDesignInputParams({
|
export default function CurveDesignInputParams({
|
||||||
curveParams,
|
curveParams,
|
||||||
|
|
@ -42,6 +43,7 @@ export default function CurveDesignInputParams({
|
||||||
const inputFields: InputFieldInterface[] = [
|
const inputFields: InputFieldInterface[] = [
|
||||||
{
|
{
|
||||||
label: "Allocation to funding pool",
|
label: "Allocation to funding pool",
|
||||||
|
description: parameterDescriptions.theta.text,
|
||||||
value: theta,
|
value: theta,
|
||||||
setter: setTheta,
|
setter: setTheta,
|
||||||
min: 0,
|
min: 0,
|
||||||
|
|
@ -54,6 +56,7 @@ export default function CurveDesignInputParams({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Hatch price (DAI/token)",
|
label: "Hatch price (DAI/token)",
|
||||||
|
description: parameterDescriptions.p0.text,
|
||||||
value: p0,
|
value: p0,
|
||||||
setter: _setP0,
|
setter: _setP0,
|
||||||
min: 0.01,
|
min: 0.01,
|
||||||
|
|
@ -65,6 +68,7 @@ export default function CurveDesignInputParams({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Post-hatch price (DAI/token)",
|
label: "Post-hatch price (DAI/token)",
|
||||||
|
description: parameterDescriptions.p1.text,
|
||||||
value: p1,
|
value: p1,
|
||||||
setter: setP1,
|
setter: setP1,
|
||||||
min: p0 || 0.1,
|
min: p0 || 0.1,
|
||||||
|
|
@ -76,6 +80,7 @@ export default function CurveDesignInputParams({
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Exit tribute",
|
label: "Exit tribute",
|
||||||
|
description: parameterDescriptions.wFee.text,
|
||||||
value: wFee,
|
value: wFee,
|
||||||
setter: setWFee,
|
setter: setWFee,
|
||||||
min: 0,
|
min: 0,
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,11 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
|
||||||
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";
|
import { InputFieldInterface } from "./types";
|
||||||
import PrettoSlider from "./PrettoSlider";
|
import PrettoSlider from "./PrettoSlider";
|
||||||
|
import TextWithPopover from "./TextWithPopover";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
|
@ -81,6 +81,7 @@ export default function InputParams({
|
||||||
{inputFields.map(
|
{inputFields.map(
|
||||||
({
|
({
|
||||||
label,
|
label,
|
||||||
|
description,
|
||||||
value,
|
value,
|
||||||
setter,
|
setter,
|
||||||
min,
|
min,
|
||||||
|
|
@ -103,9 +104,7 @@ export default function InputParams({
|
||||||
return (
|
return (
|
||||||
<Grid key={label} container spacing={0} className={classes.listBox}>
|
<Grid key={label} container spacing={0} className={classes.listBox}>
|
||||||
<Grid item xs={6} className={classes.leftContainer}>
|
<Grid item xs={6} className={classes.leftContainer}>
|
||||||
<Typography id={label} gutterBottom>
|
<TextWithPopover content={label} popoverText={description} />
|
||||||
{label}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={2} className={classes.centerContainer}>
|
<Grid item xs={2} className={classes.centerContainer}>
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ import { linspace } from "./utils";
|
||||||
const keyHorizontal = "x";
|
const keyHorizontal = "x";
|
||||||
const keyVerticalLeft = "Price (DAI/token)";
|
const keyVerticalLeft = "Price (DAI/token)";
|
||||||
const keyVerticalRight = "Total exit tributes (DAI)";
|
const keyVerticalRight = "Total exit tributes (DAI)";
|
||||||
|
const keyVerticalLeft2 = "Floor price (DAI/token)";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
|
@ -32,11 +33,13 @@ const useStyles = makeStyles((theme: Theme) =>
|
||||||
function PriceSimulationChart({
|
function PriceSimulationChart({
|
||||||
priceTimeseries,
|
priceTimeseries,
|
||||||
withdrawFeeTimeseries,
|
withdrawFeeTimeseries,
|
||||||
|
floorpriceTimeseries,
|
||||||
p0,
|
p0,
|
||||||
p1
|
p1
|
||||||
}: {
|
}: {
|
||||||
priceTimeseries: number[];
|
priceTimeseries: number[];
|
||||||
withdrawFeeTimeseries: number[];
|
withdrawFeeTimeseries: number[];
|
||||||
|
floorpriceTimeseries: number[];
|
||||||
p0: number;
|
p0: number;
|
||||||
p1: number;
|
p1: number;
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -51,6 +54,7 @@ function PriceSimulationChart({
|
||||||
data.push({
|
data.push({
|
||||||
[keyHorizontal]: t,
|
[keyHorizontal]: t,
|
||||||
[keyVerticalLeft]: priceTimeseries[t] || 0,
|
[keyVerticalLeft]: priceTimeseries[t] || 0,
|
||||||
|
[keyVerticalLeft2]: floorpriceTimeseries[t] || 0,
|
||||||
[keyVerticalRight]: withdrawFeeTimeseries[t] || 0
|
[keyVerticalRight]: withdrawFeeTimeseries[t] || 0
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -70,8 +74,8 @@ function PriceSimulationChart({
|
||||||
const { textAnchor, viewBox, text } = props;
|
const { textAnchor, viewBox, text } = props;
|
||||||
return (
|
return (
|
||||||
<text
|
<text
|
||||||
x={viewBox.x + 10}
|
x={viewBox.x + 8}
|
||||||
y={viewBox.y - 10}
|
y={viewBox.y + 17}
|
||||||
fill={theme.palette.text.secondary}
|
fill={theme.palette.text.secondary}
|
||||||
textAnchor={textAnchor}
|
textAnchor={textAnchor}
|
||||||
>
|
>
|
||||||
|
|
@ -83,10 +87,12 @@ function PriceSimulationChart({
|
||||||
function CustomTooltip({ active, payload, label }: any) {
|
function CustomTooltip({ active, payload, label }: any) {
|
||||||
if (active) {
|
if (active) {
|
||||||
const price = payload[0].value;
|
const price = payload[0].value;
|
||||||
const exit = payload[1].value;
|
const floor = payload[1].value;
|
||||||
|
const exit = payload[2].value;
|
||||||
const weekNum = label;
|
const weekNum = label;
|
||||||
const toolTipData: string[][] = [
|
const toolTipData: string[][] = [
|
||||||
["Price", price.toFixed(2), "DAI/tk"],
|
["Price", price.toFixed(2), "DAI/tk"],
|
||||||
|
["Floor", floor.toFixed(2), "DAI/tk"],
|
||||||
["Exit t.", formatter(exit), "DAI"],
|
["Exit t.", formatter(exit), "DAI"],
|
||||||
["Week", weekNum, ""]
|
["Week", weekNum, ""]
|
||||||
];
|
];
|
||||||
|
|
@ -148,12 +154,10 @@ function PriceSimulationChart({
|
||||||
{/* Capital collected from withdraw fees - AXIS */}
|
{/* Capital collected from withdraw fees - AXIS */}
|
||||||
<YAxis
|
<YAxis
|
||||||
yAxisId="right"
|
yAxisId="right"
|
||||||
// domain={[
|
domain={[0, +(2 * withdrawFeeTimeseries.slice(-1)[0]).toPrecision(1)]}
|
||||||
// Math.floor(Math.min(...withdrawFeeTimeseries)),
|
|
||||||
// Math.ceil(Math.max(...withdrawFeeTimeseries))
|
|
||||||
// ]}
|
|
||||||
orientation="right"
|
orientation="right"
|
||||||
tick={{ fill: theme.palette.text.secondary }}
|
tick={{ fill: theme.palette.text.secondary }}
|
||||||
|
tickFormatter={formatter}
|
||||||
stroke={theme.palette.text.secondary}
|
stroke={theme.palette.text.secondary}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
@ -166,7 +170,20 @@ function PriceSimulationChart({
|
||||||
dataKey={keyVerticalLeft}
|
dataKey={keyVerticalLeft}
|
||||||
stroke={theme.palette.primary.main}
|
stroke={theme.palette.primary.main}
|
||||||
fill={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
|
<ReferenceLine
|
||||||
y={p0}
|
y={p0}
|
||||||
yAxisId="left"
|
yAxisId="left"
|
||||||
|
|
@ -188,9 +205,10 @@ function PriceSimulationChart({
|
||||||
yAxisId="right"
|
yAxisId="right"
|
||||||
type="monotone"
|
type="monotone"
|
||||||
dataKey={keyVerticalRight}
|
dataKey={keyVerticalRight}
|
||||||
stroke={theme.palette.secondary.dark}
|
stroke={"#0085ff"}
|
||||||
fill={theme.palette.secondary.dark}
|
fill={theme.palette.secondary.dark}
|
||||||
fillOpacity="0.8"
|
fillOpacity={0.5}
|
||||||
|
strokeWidth={2}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* <ReferenceLine
|
{/* <ReferenceLine
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import React from "react";
|
||||||
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
|
import TextWithPopover from "./TextWithPopover";
|
||||||
|
|
||||||
const useStyles = makeStyles((theme: Theme) =>
|
const useStyles = makeStyles((theme: Theme) =>
|
||||||
createStyles({
|
createStyles({
|
||||||
|
|
@ -45,6 +46,7 @@ export default function ResultParams({
|
||||||
}: {
|
}: {
|
||||||
resultFields: {
|
resultFields: {
|
||||||
label: string;
|
label: string;
|
||||||
|
description: string;
|
||||||
value: number | string;
|
value: number | string;
|
||||||
}[];
|
}[];
|
||||||
}) {
|
}) {
|
||||||
|
|
@ -52,12 +54,10 @@ export default function ResultParams({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.listBoxContainer}>
|
<div className={classes.listBoxContainer}>
|
||||||
{resultFields.map(({ label, value }) => (
|
{resultFields.map(({ label, description, value }) => (
|
||||||
<Grid key={label} container spacing={0} className={classes.listBox}>
|
<Grid key={label} container spacing={0} className={classes.listBox}>
|
||||||
<Grid item xs={8} className={classes.leftContainer}>
|
<Grid item xs={8} className={classes.leftContainer}>
|
||||||
<Typography id={label} gutterBottom>
|
<TextWithPopover content={label} popoverText={description} />
|
||||||
{label}
|
|
||||||
</Typography>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid item xs={4} className={classes.centerContainer}>
|
<Grid item xs={4} className={classes.centerContainer}>
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { InputFieldInterface, CurveParamsInterface } from "./types";
|
import { InputFieldInterface, CurveParamsInterface } from "./types";
|
||||||
import InputParams from "./InputParams";
|
import InputParams from "./InputParams";
|
||||||
|
import { parameterDescriptions } from "./parametersDescriptions";
|
||||||
|
|
||||||
export default function CurveDesignInputParams({
|
export default function CurveDesignInputParams({
|
||||||
curveParams,
|
curveParams,
|
||||||
|
|
@ -25,6 +26,7 @@ export default function CurveDesignInputParams({
|
||||||
const inputFields: InputFieldInterface[] = [
|
const inputFields: InputFieldInterface[] = [
|
||||||
{
|
{
|
||||||
label: "Initial raise (DAI)",
|
label: "Initial raise (DAI)",
|
||||||
|
description: parameterDescriptions.d0.text,
|
||||||
value: d0,
|
value: d0,
|
||||||
setter: setD0,
|
setter: setD0,
|
||||||
min: 0.1e6,
|
min: 0.1e6,
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,8 @@ function SupplyVsDemandChart({
|
||||||
dataKey={keyVertical}
|
dataKey={keyVertical}
|
||||||
stroke={theme.palette.primary.main}
|
stroke={theme.palette.primary.main}
|
||||||
fill={theme.palette.primary.main}
|
fill={theme.palette.primary.main}
|
||||||
|
fillOpacity={0.3}
|
||||||
|
strokeWidth={2}
|
||||||
/>
|
/>
|
||||||
<ReferenceLine
|
<ReferenceLine
|
||||||
x={R0_round}
|
x={R0_round}
|
||||||
|
|
|
||||||
|
|
@ -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>
|
||||||
|
);
|
||||||
|
}
|
||||||
81
src/math.ts
81
src/math.ts
|
|
@ -23,6 +23,37 @@ export function getInitialParams({
|
||||||
return { k, R0, S0, V0 };
|
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`
|
* 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
|
* Demo: https://codepen.io/anon/pen/mNqJjv?editors=0010#0
|
||||||
* Very quick: < 10ms for 10000 txs
|
* 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 mean = sum / num;
|
||||||
const off = mean * 4;
|
const off = mean * spread;
|
||||||
const x: number[] = [];
|
const x: number[] = [];
|
||||||
for (let i = 0; i < num; i++) {
|
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;
|
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
|
* Random variable uniformly distributed
|
||||||
|
|
@ -111,3 +172,15 @@ function randn_bm(min: number, max: number) {
|
||||||
num += min; // offset to min
|
num += min; // offset to min
|
||||||
return num;
|
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);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
export interface InputFieldInterface {
|
export interface InputFieldInterface {
|
||||||
label: string;
|
label: string;
|
||||||
|
description: string;
|
||||||
value: number;
|
value: number;
|
||||||
setter(newValue: any): void;
|
setter(newValue: any): void;
|
||||||
min: number;
|
min: number;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue