UNPKG

@evolutionland/evolution-js

Version:

evolution evolution-js evolutionland evolution-js-sdk evolution-land metaverse

189 lines (188 loc) 10.1 kB
import { Token, Price, TokenAmount, InsufficientReservesError, InsufficientInputAmountError } from "../../libs/uniswap"; import invariant from "tiny-invariant"; import JSBI from "jsbi"; import { pack, keccak256 } from "@ethersproject/solidity"; import { getCreate2Address } from "@ethersproject/address"; import { sqrt, parseBigintIsh } from "./uniswapUtils"; import { MINIMUM_LIQUIDITY } from "../../libs/uniswap"; var ZERO = /*#__PURE__*/ JSBI.BigInt(0); var ONE = /*#__PURE__*/ JSBI.BigInt(1); var FIVE = /*#__PURE__*/ JSBI.BigInt(5); var _997 = /*#__PURE__*/ JSBI.BigInt(997); var _1000 = /*#__PURE__*/ JSBI.BigInt(1000); let PAIR_ADDRESS_CACHE = { // '0x2f6aE7fDbB7c0c613F7923Ddce3E5b71aFE71f78': { // '0xD4C2F962B8b94cdD2e0B2e8E765d39f32980a1c1': '0x5a5B56EE7F615Ca4f9676703f9aB8AD4C4954092' // } }; const FACTORY_ADDRESS = { 1: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", 3: "0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f", 44: "0x36ABF1A7851fBF9ae9D79dc3E39C1227068158B3", 56: "0xcA143Ce32Fe78f1f7019d7d551a6402fC5350c73", 97: "0x6725F303b657a9451d8BA641348b6761A6CC7a17", 128: "0xb0b670fc1F7724119963018DB0BfA86aDb22d941", 137: "0x5757371414417b8c6caad45baef941abc7d3ab32", 256: "0xA19a691EB6dE729758BFCef165e117C830483eF0", 80001: "0x6d7cdf98a49f968F6eB90eEb3D8189038760d439", }; // https://github.com/Uniswap/uniswap-v2-core/issues/102 // We can use create LP to calculate the hash using the generated lp contract bytescode. UniswapV2Pair contract const INIT_CODE_HASH = { 1: "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f", 3: "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f", 44: "0x2a3eebed007578fadb94fc416a8002f6bcef7ee2aae9680a5616e2fb4e407a51", 56: "0x00fb7f630766e6a796048ea87d01acd3068e8ff67d078148a3fa3f4a84f69bd5", 97: "0xd0d4c4cd0848c93cb4fd1f498d7013ee6bfb25783ea21593d5834f5d250ece66", 128: "0x2ad889f82040abccb2649ea6a874796c1601fb67f91a747a80e08860c73ddf24", 137: "0x96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f", 256: "0x6fcc083b512761e9f65d41be84dbc66f5afb698ad320a8b0e1e6d2d0e4d10930", 80001: "0x6fcc083b512761e9f65d41be84dbc66f5afb698ad320a8b0e1e6d2d0e4d10930", }; export class Pair { static getAddress(tokenA, tokenB) { var _a; const tokens = tokenA.sortsBefore(tokenB) ? [tokenA, tokenB] : [tokenB, tokenA]; // does safety checks if (((_a = PAIR_ADDRESS_CACHE === null || PAIR_ADDRESS_CACHE === void 0 ? void 0 : PAIR_ADDRESS_CACHE[tokens[0].address]) === null || _a === void 0 ? void 0 : _a[tokens[1].address]) === undefined) { PAIR_ADDRESS_CACHE = Object.assign(Object.assign({}, PAIR_ADDRESS_CACHE), { [tokens[0].address]: Object.assign(Object.assign({}, PAIR_ADDRESS_CACHE === null || PAIR_ADDRESS_CACHE === void 0 ? void 0 : PAIR_ADDRESS_CACHE[tokens[0].address]), { [tokens[1].address]: getCreate2Address(FACTORY_ADDRESS[tokens[0].chainId], keccak256(["bytes"], [pack(["address", "address"], [tokens[0].address, tokens[1].address])]), INIT_CODE_HASH[tokens[0].chainId]) }) }); } return PAIR_ADDRESS_CACHE[tokens[0].address][tokens[1].address]; } constructor(tokenAmountA, tokenAmountB) { const tokenAmounts = tokenAmountA.token.sortsBefore(tokenAmountB.token) // does safety checks ? [tokenAmountA, tokenAmountB] : [tokenAmountB, tokenAmountA]; this.liquidityToken = new Token(tokenAmounts[0].token.chainId, Pair.getAddress(tokenAmounts[0].token, tokenAmounts[1].token), 18, "UNI-V2", "Uniswap V2"); this.tokenAmounts = tokenAmounts; } /** * Returns true if the token is either token0 or token1 * @param token to check */ involvesToken(token) { return token.equals(this.token0) || token.equals(this.token1); } /** * Returns the current mid price of the pair in terms of token0, i.e. the ratio of reserve1 to reserve0 */ get token0Price() { return new Price(this.token0, this.token1, this.tokenAmounts[0].raw, this.tokenAmounts[1].raw); } /** * Returns the current mid price of the pair in terms of token1, i.e. the ratio of reserve0 to reserve1 */ get token1Price() { return new Price(this.token1, this.token0, this.tokenAmounts[1].raw, this.tokenAmounts[0].raw); } /** * Return the price of the given token in terms of the other token in the pair. * @param token token to return price of */ priceOf(token) { invariant(this.involvesToken(token), "TOKEN"); return token.equals(this.token0) ? this.token0Price : this.token1Price; } /** * Returns the chain ID of the tokens in the pair. */ get chainId() { return this.token0.chainId; } get token0() { return this.tokenAmounts[0].token; } get token1() { return this.tokenAmounts[1].token; } get reserve0() { return this.tokenAmounts[0]; } get reserve1() { return this.tokenAmounts[1]; } reserveOf(token) { invariant(this.involvesToken(token), "TOKEN"); return token.equals(this.token0) ? this.reserve0 : this.reserve1; } getOutputAmount(inputAmount) { invariant(this.involvesToken(inputAmount.token), "TOKEN"); if (JSBI.equal(this.reserve0.raw, ZERO) || JSBI.equal(this.reserve1.raw, ZERO)) { throw new InsufficientReservesError(); } const inputReserve = this.reserveOf(inputAmount.token); const outputReserve = this.reserveOf(inputAmount.token.equals(this.token0) ? this.token1 : this.token0); const inputAmountWithFee = JSBI.multiply(inputAmount.raw, _997); const numerator = JSBI.multiply(inputAmountWithFee, outputReserve.raw); const denominator = JSBI.add(JSBI.multiply(inputReserve.raw, _1000), inputAmountWithFee); const outputAmount = new TokenAmount(inputAmount.token.equals(this.token0) ? this.token1 : this.token0, JSBI.divide(numerator, denominator)); if (JSBI.equal(outputAmount.raw, ZERO)) { throw new InsufficientInputAmountError(); } return [outputAmount, new Pair(inputReserve.add(inputAmount), outputReserve.subtract(outputAmount))]; } getInputAmount(outputAmount) { invariant(this.involvesToken(outputAmount.token), "TOKEN"); if (JSBI.equal(this.reserve0.raw, ZERO) || JSBI.equal(this.reserve1.raw, ZERO) || JSBI.greaterThanOrEqual(outputAmount.raw, this.reserveOf(outputAmount.token).raw)) { throw new InsufficientReservesError(); } const outputReserve = this.reserveOf(outputAmount.token); const inputReserve = this.reserveOf(outputAmount.token.equals(this.token0) ? this.token1 : this.token0); const numerator = JSBI.multiply(JSBI.multiply(inputReserve.raw, outputAmount.raw), _1000); const denominator = JSBI.multiply(JSBI.subtract(outputReserve.raw, outputAmount.raw), _997); const inputAmount = new TokenAmount(outputAmount.token.equals(this.token0) ? this.token1 : this.token0, JSBI.add(JSBI.divide(numerator, denominator), ONE)); return [inputAmount, new Pair(inputReserve.add(inputAmount), outputReserve.subtract(outputAmount))]; } getLiquidityMinted(totalSupply, tokenAmountA, tokenAmountB) { invariant(totalSupply.token.equals(this.liquidityToken), "LIQUIDITY"); const tokenAmounts = tokenAmountA.token.sortsBefore(tokenAmountB.token) // does safety checks ? [tokenAmountA, tokenAmountB] : [tokenAmountB, tokenAmountA]; invariant(tokenAmounts[0].token.equals(this.token0) && tokenAmounts[1].token.equals(this.token1), "TOKEN"); let liquidity; if (JSBI.equal(totalSupply.raw, ZERO)) { liquidity = JSBI.subtract(sqrt(JSBI.multiply(tokenAmounts[0].raw, tokenAmounts[1].raw)), MINIMUM_LIQUIDITY); } else { const amount0 = JSBI.divide(JSBI.multiply(tokenAmounts[0].raw, totalSupply.raw), this.reserve0.raw); const amount1 = JSBI.divide(JSBI.multiply(tokenAmounts[1].raw, totalSupply.raw), this.reserve1.raw); liquidity = JSBI.lessThanOrEqual(amount0, amount1) ? amount0 : amount1; } if (!JSBI.greaterThan(liquidity, ZERO)) { throw new InsufficientInputAmountError(); } return new TokenAmount(this.liquidityToken, liquidity); } getLiquidityValue(token, totalSupply, liquidity, feeOn = false, kLast) { invariant(this.involvesToken(token), "TOKEN"); invariant(totalSupply.token.equals(this.liquidityToken), "TOTAL_SUPPLY"); invariant(liquidity.token.equals(this.liquidityToken), "LIQUIDITY"); invariant(JSBI.lessThanOrEqual(liquidity.raw, totalSupply.raw), "LIQUIDITY"); let totalSupplyAdjusted; if (!feeOn) { totalSupplyAdjusted = totalSupply; } else { invariant(!!kLast, "K_LAST"); const kLastParsed = parseBigintIsh(kLast); if (!JSBI.equal(kLastParsed, ZERO)) { const rootK = sqrt(JSBI.multiply(this.reserve0.raw, this.reserve1.raw)); const rootKLast = sqrt(kLastParsed); if (JSBI.greaterThan(rootK, rootKLast)) { const numerator = JSBI.multiply(totalSupply.raw, JSBI.subtract(rootK, rootKLast)); const denominator = JSBI.add(JSBI.multiply(rootK, FIVE), rootKLast); const feeLiquidity = JSBI.divide(numerator, denominator); totalSupplyAdjusted = totalSupply.add(new TokenAmount(this.liquidityToken, feeLiquidity)); } else { totalSupplyAdjusted = totalSupply; } } else { totalSupplyAdjusted = totalSupply; } } return new TokenAmount(token, JSBI.divide(JSBI.multiply(liquidity.raw, this.reserveOf(token).raw), totalSupplyAdjusted.raw)); } }