class Pair {
constructor(token0, token1, token0Reserve, token1Reserve) {
this.token0 = token0.toUpperCase();
this.token1 = token1.toUpperCase();
this.token0Reserve = token0Reserve;
this.token1Reserve = token1Reserve;
this.product = this.token0Reserve * this.token1Reserve;
}
static newFromPrice(token0, token1, token0Reserve, token0Price, token1Price) {
return new this(
token0,
token1,
token0Reserve,
(token0Reserve * token0Price) / token1Price
);
}
quote(amountA, reserveA, reserveB) {
assert(amountA > 0, 'INSUFFICIENT_AMOUNT');
assert(reserveA > 0 && reserveB > 0, 'INSUFFICIENT_LIQUIDITY');
return (amountA * reserveB) / reserveA;
}
getAmountOut(amountIn, reserveIn, reserveOut) {
assert(amountIn > 0, 'INSUFFICIENT_AMOUNT');
assert(reserveIn > 0 && reserveOut > 0, 'INSUFFICIENT_LIQUIDITY');
const amountInWithFee = amountIn * 0.997;
const numerator = amountInWithFee * reserveOut;
const denominator = reserveIn + amountInWithFee;
const amountOut = numerator / denominator;
return amountOut;
}
getAmountIn(amountOut, reserveIn, reserveOut) {
assert(amountOut > 0, 'INSUFFICIENT_AMOUNT');
assert(reserveIn > 0 && reserveOut > 0, 'INSUFFICIENT_LIQUIDITY');
const numerator = reserveIn * amountOut * 1000;
const denominator = (reserveOut - amountOut) * 997;
return numerator / denominator + 1;
}
priceImpact(amountIn, tokenInName) {
const pricePrior = this.quote(1, this.token0Reserve, this.token1Reserve);
let priceAfter = 0;
if (this.token0.toUpperCase() === tokenInName.toUpperCase()) {
const delta = this.getAmountOut(
amountIn,
this.token0Reserve,
this.token1Reserve
);
priceAfter = this.quote(
1,
this.token0Reserve,
this.token1Reserve - delta
);
}
if (this.token1.toUpperCase() === tokenInName.toUpperCase()) {
const delta = this.getAmountOut(
amountIn,
this.token1Reserve,
this.token0Reserve
);
priceAfter = this.quote(
1,
this.token0Reserve,
this.token1Reserve + delta
);
}
return (priceAfter - pricePrior) / pricePrior;
}
swapTokensForTokens(amountIn, tokenInName) {
if (this.token0 === tokenInName.toUpperCase() && amountIn > 0) {
const tokenOut = this.getAmountOut(
amountIn,
this.token0Reserve,
this.token1Reserve
);
this.token1Reserve -= tokenOut;
return tokenOut;
}
if (this.token1 === tokenInName.toUpperCase() && amountIn > 0) {
const tokenOut = this.getAmountOut(
amountIn,
this.token1Reserve,
this.token0Reserve
);
this.token0Reserve -= tokenOut;
return tokenOut;
}
return undefined;
}
changeLiquidityInToken(amount, name) {
if (this.token0 === name.toUpperCase() && this.token0Reserve + amount > 0) {
const oldToken0Reserve = this.token0Reserve;
this.token0Reserve = this.token0Reserve + amount;
const tokenMultiple = this.token0Reserve / oldToken0Reserve;
this.token1Reserve = this.token1Reserve * tokenMultiple;
this.product = this.token0Reserve * this.token1Reserve;
return true;
}
if (this.token0 === name.toUpperCase() && this.token1Reserve + amount > 0) {
const oldToken1Reserve = this.token1Reserve;
this.token1Reserve = this.token1Reserve + amount;
const tokenMultiple = this.token1Reserve / oldToken1Reserve;
this.token0Reserve = this.token0Reserve * tokenMultiple;
this.product = this.token0Reserve * this.token1Reserve;
return true;
}
return false;
}
spotPriceFor(name) {
const token0Price = this.token0Reserve / this.token1Reserve;
if (this.token0 === name.toUpperCase()) {
return token0Price;
}
if (this.token1 === name.toUpperCase()) {
return 1 / token0Price;
}
}
}