199 lines
5.8 KiB
TypeScript
199 lines
5.8 KiB
TypeScript
import { FC, useEffect, useRef } from "react";
|
|
|
|
const drawArrow = (ctx: CanvasRenderingContext2D, x1: number, y1: number, x2: number, y2: number) => {
|
|
const [dx, dy] = [x1 - x2, y1 - y2];
|
|
const norm = Math.sqrt(dx * dx + dy * dy);
|
|
const [udx, udy] = [dx / norm, dy / norm];
|
|
const size = norm / 7;
|
|
ctx.beginPath();
|
|
ctx.moveTo(x1, y1);
|
|
ctx.lineTo(x2, y2);
|
|
ctx.stroke();
|
|
ctx.moveTo(x2, y2);
|
|
ctx.lineTo(x2 + udx * size - udy * size, y2 + udx * size + udy * size);
|
|
ctx.moveTo(x2, y2);
|
|
ctx.lineTo(x2 + udx * size + udy * size, y2 - udx * size + udy * size);
|
|
ctx.stroke();
|
|
};
|
|
|
|
export interface ICurveProps {
|
|
ethReserve: number;
|
|
tokenReserve: number;
|
|
addingEth: number;
|
|
addingToken: number;
|
|
width: number;
|
|
height: number;
|
|
}
|
|
|
|
export const Curve: FC<ICurveProps> = (props: ICurveProps) => {
|
|
const ref = useRef<HTMLCanvasElement>(null);
|
|
|
|
useEffect(() => {
|
|
const canvas = ref.current;
|
|
if (!canvas) {
|
|
return;
|
|
}
|
|
|
|
const textSize = 12;
|
|
|
|
const width = canvas.width;
|
|
const height = canvas.height;
|
|
|
|
if (props.ethReserve && props.tokenReserve) {
|
|
const k = props.ethReserve * props.tokenReserve;
|
|
|
|
const ctx = canvas.getContext("2d");
|
|
if (ctx == null) {
|
|
return;
|
|
}
|
|
ctx.clearRect(0, 0, width, height);
|
|
|
|
let maxX = k / (props.ethReserve / 4);
|
|
let minX = 0;
|
|
|
|
if (props.addingEth || props.addingToken) {
|
|
maxX = k / (props.ethReserve * 0.4);
|
|
//maxX = k/(props.ethReserve*0.8)
|
|
minX = k / Math.max(0, 500 - props.ethReserve);
|
|
}
|
|
|
|
const maxY = (maxX * height) / width;
|
|
const minY = (minX * height) / width;
|
|
|
|
const plotX = (x: number) => {
|
|
return ((x - minX) / (maxX - minX)) * width;
|
|
};
|
|
|
|
const plotY = (y: number) => {
|
|
return height - ((y - minY) / (maxY - minY)) * height;
|
|
};
|
|
ctx.strokeStyle = "#000000";
|
|
ctx.fillStyle = "#000000";
|
|
ctx.font = textSize + "px Arial";
|
|
// +Y axis
|
|
ctx.beginPath();
|
|
ctx.moveTo(plotX(minX), plotY(0));
|
|
ctx.lineTo(plotX(minX), plotY(maxY));
|
|
ctx.stroke();
|
|
// +X axis
|
|
ctx.beginPath();
|
|
ctx.moveTo(plotX(0), plotY(minY));
|
|
ctx.lineTo(plotX(maxX), plotY(minY));
|
|
ctx.stroke();
|
|
|
|
ctx.lineWidth = 2;
|
|
ctx.beginPath();
|
|
let first = true;
|
|
for (let x = minX; x <= maxX; x += maxX / width) {
|
|
/////
|
|
const y = k / x;
|
|
/////
|
|
if (first) {
|
|
ctx.moveTo(plotX(x), plotY(y));
|
|
first = false;
|
|
} else {
|
|
ctx.lineTo(plotX(x), plotY(y));
|
|
}
|
|
}
|
|
ctx.stroke();
|
|
|
|
ctx.lineWidth = 1;
|
|
|
|
if (props.addingEth) {
|
|
const newEthReserve = props.ethReserve + parseFloat(props.addingEth.toString());
|
|
|
|
ctx.fillStyle = "#bbbbbb";
|
|
ctx.beginPath();
|
|
ctx.arc(plotX(newEthReserve), plotY(k / newEthReserve), 5, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
|
|
ctx.strokeStyle = "#009900";
|
|
drawArrow(
|
|
ctx,
|
|
plotX(props.ethReserve),
|
|
plotY(props.tokenReserve),
|
|
plotX(newEthReserve),
|
|
plotY(props.tokenReserve),
|
|
);
|
|
|
|
ctx.fillStyle = "#000000";
|
|
ctx.fillText(
|
|
"" + props.addingEth + " ETH input",
|
|
plotX(props.ethReserve) + textSize,
|
|
plotY(props.tokenReserve) - textSize,
|
|
);
|
|
|
|
ctx.strokeStyle = "#990000";
|
|
drawArrow(ctx, plotX(newEthReserve), plotY(props.tokenReserve), plotX(newEthReserve), plotY(k / newEthReserve));
|
|
|
|
const amountGained = Math.round((10000 * (props.addingEth * props.tokenReserve)) / newEthReserve) / 10000;
|
|
ctx.fillStyle = "#000000";
|
|
ctx.fillText(
|
|
"" + amountGained + " 🎈 output (-0.3% fee)",
|
|
plotX(newEthReserve) + textSize,
|
|
plotY(k / newEthReserve),
|
|
);
|
|
} else if (props.addingToken) {
|
|
const newTokenReserve = props.tokenReserve + parseFloat(props.addingToken.toString());
|
|
|
|
ctx.fillStyle = "#bbbbbb";
|
|
ctx.beginPath();
|
|
ctx.arc(plotX(k / newTokenReserve), plotY(newTokenReserve), 5, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
|
|
//console.log("newTokenReserve",newTokenReserve)
|
|
ctx.strokeStyle = "#990000";
|
|
drawArrow(
|
|
ctx,
|
|
plotX(props.ethReserve),
|
|
plotY(props.tokenReserve),
|
|
plotX(props.ethReserve),
|
|
plotY(newTokenReserve),
|
|
);
|
|
|
|
ctx.fillStyle = "#000000";
|
|
ctx.fillText(
|
|
"" + props.addingToken + " 🎈 input",
|
|
plotX(props.ethReserve) + textSize,
|
|
plotY(props.tokenReserve),
|
|
);
|
|
|
|
ctx.strokeStyle = "#009900";
|
|
drawArrow(
|
|
ctx,
|
|
plotX(props.ethReserve),
|
|
plotY(newTokenReserve),
|
|
plotX(k / newTokenReserve),
|
|
plotY(newTokenReserve),
|
|
);
|
|
|
|
const amountGained = Math.round((10000 * (props.addingToken * props.ethReserve)) / newTokenReserve) / 10000;
|
|
//console.log("amountGained",amountGained)
|
|
ctx.fillStyle = "#000000";
|
|
ctx.fillText(
|
|
"" + amountGained + " ETH output (-0.3% fee)",
|
|
plotX(k / newTokenReserve) + textSize,
|
|
plotY(newTokenReserve) - textSize,
|
|
);
|
|
}
|
|
|
|
ctx.fillStyle = "#0000FF";
|
|
ctx.beginPath();
|
|
ctx.arc(plotX(props.ethReserve), plotY(props.tokenReserve), 5, 0, 2 * Math.PI);
|
|
ctx.fill();
|
|
}
|
|
}, [props]);
|
|
|
|
return (
|
|
<div style={{ position: "relative", width: props.width, height: props.height }}>
|
|
<canvas style={{ position: "absolute", left: 0, top: 0 }} ref={ref} width={props.width} height={props.height} />
|
|
<div style={{ position: "absolute", left: "20%", bottom: -20 }}>-- ETH Reserve --{">"}</div>
|
|
<div
|
|
style={{ position: "absolute", left: -20, bottom: "20%", transform: "rotate(-90deg)", transformOrigin: "0 0" }}
|
|
>
|
|
-- Token Reserve --{">"}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|