@ethersproject/abstract-provider#TransactionResponse TypeScript Examples

The following examples show how to use @ethersproject/abstract-provider#TransactionResponse. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: PerpService.ts    From sakeperp-arbitrageur with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
async addMargin(
        trader: Wallet,
        exchangeAddr: string,
        marginToBeAdded: Big,
        overrides?: Overrides,
    ): Promise<TransactionResponse> {
        const sakePerp = await this.createSakePerp(trader)
        const tx = await sakePerp.functions.addMargin(
            exchangeAddr,
            { d: PerpService.toWei(marginToBeAdded) },
            {
                gasLimit: 1_500_000,
                ...overrides,
            },
        )
        this.log.jinfo({
            event: "AddMarginTxSent",
            params: {
                trader: trader.address,
                amm: exchangeAddr,
                marginToBeRemoved: +marginToBeAdded.toFixed(),
                txHash: tx.hash,
                gasPrice: tx.gasPrice.toString(),
                nonce: tx.nonce,
            },
        })
        return tx
    }
Example #2
Source File: TransactionManifest.ts    From mStable-apps with GNU Lesser General Public License v3.0 6 votes vote down vote up
async send(gasLimit?: BigNumber, gasPrice?: number): Promise<TransactionResponse> {
    let response: TransactionResponse

    try {
      const args = await this.addGasSettings(gasLimit, gasPrice)
      response = await this.contract.functions[this.fn](...args)
    } catch (error) {
      // MetaMask error messages are in a `data` property
      const txMessage = error.data?.message || error.message

      throw new Error(
        !txMessage || txMessage.includes('always failing transaction') || txMessage.includes('transaction may fail')
          ? 'Transaction failed - if this problem persists, contact mStable team.'
          : txMessage,
      )
    }

    if (!response?.hash) {
      throw new Error('Unable to send; missing transaction hash')
    }

    return response
  }
Example #3
Source File: PerpService.ts    From sakeperp-arbitrageur with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
async removeMargin(
        trader: Wallet,
        exchangeAddr: string,
        marginToBeRemoved: Big,
        overrides?: Overrides,
    ): Promise<TransactionResponse> {
        const sakePerp = await this.createSakePerp(trader)
        const tx = await sakePerp.functions.removeMargin(
            exchangeAddr,
            { d: PerpService.toWei(marginToBeRemoved) },
            {
                gasLimit: 1_500_000,
                ...overrides,
            },
        )
        this.log.jinfo({
            event: "RemoveMarginTxSent",
            params: {
                trader: trader.address,
                amm: exchangeAddr,
                marginToBeRemoved: +marginToBeRemoved.toFixed(),
                txHash: tx.hash,
                gasPrice: tx.gasPrice.toString(),
                nonce: tx.nonce,
            },
        })
        return tx
    }
Example #4
Source File: PerpService.ts    From sakeperp-arbitrageur with BSD 3-Clause "New" or "Revised" License 6 votes vote down vote up
async closePosition(
        trader: Wallet,
        exchangeAddr: string,
        exchangePair: string,
        minBaseAssetAmount: Big = Big(0),
        overrides?: Overrides,
    ): Promise<TransactionResponse> {
        const sakePerp = await this.createSakePerp(trader)
        const tx = await sakePerp.functions.closePosition(
            exchangeAddr,
            { d: PerpService.toWei(minBaseAssetAmount) },
            {
                gasLimit: 2_500_000,
                ...overrides,
            },
        )
        this.log.jinfo({
            event: "ClosePositionTxSent",
            params: {
                trader: trader.address,
                exchangePair: exchangePair,
                txHash: tx.hash,
                gasPrice: tx.gasPrice.toString(),
                nonce: tx.nonce,
            },
        })

        return tx
    }
Example #5
Source File: tune.ts    From nova with GNU Affero General Public License v3.0 6 votes vote down vote up
/**
 * Finds the optimal missing gas estimate for an execution manager based on a single exec tx.
 */
async function findOptimalMissingGasEstimate(
  L1_NovaExecutionManager: L1NovaExecutionManager,
  tx: Promise<TransactionResponse>
) {
  const { gasUsed, logs } = await (await tx).wait();

  // Since we accept a generic TransactionResponse we have to parse the logs manually.
  const execEvent = L1_NovaExecutionManager.interface.parseLog(logs[logs.length - 1]);

  // We are assuming that the current gas config was the same when the
  // tx was executed. Don't try to use tune on really old transactions!
  const currentGasConfig = await L1_NovaExecutionManager.gasConfig();

  const currentMissingGasEstimate = currentGasConfig.missingGasEstimate.toNumber();
  const underestimateAmount = gasUsed.toNumber() - execEvent.args.gasUsed.toNumber();

  return {
    currentGasConfig,
    currentMissingGasEstimate,
    optimalMissingGasEstimate: currentMissingGasEstimate + underestimateAmount,
  };
}
Example #6
Source File: snapshot.ts    From trident with GNU General Public License v3.0 6 votes vote down vote up
export async function snapshotGasCost(
  x:
    | TransactionResponse
    | Promise<TransactionResponse>
    | ContractTransaction
    | Promise<ContractTransaction>
    | TransactionReceipt
    | Promise<BigNumber>
    | BigNumber
    | Contract
    | Promise<Contract>
): Promise<void> {
  const resolved = await x;
  if ("deployTransaction" in resolved) {
    const receipt = await resolved.deployTransaction.wait();
    expect(receipt.gasUsed.toNumber()).toMatchSnapshot();
  } else if ("wait" in resolved) {
    const waited = await resolved.wait();
    expect(waited.gasUsed.toNumber()).toMatchSnapshot();
  } else if (BigNumber.isBigNumber(resolved)) {
    expect(resolved.toNumber()).toMatchSnapshot();
  }
}
Example #7
Source File: base-provider.ts    From bodhi.js with Apache License 2.0 5 votes vote down vote up
sendTransaction = async (signedTransaction: string | Promise<string>): Promise<TransactionResponse> => {
    await this.getNetwork();
    const hexTx = await Promise.resolve(signedTransaction).then((t) => hexlify(t));
    const tx = parseTransaction(await signedTransaction);

    if ((tx as any).confirmations === null || (tx as any).confirmations === undefined) {
      (tx as any).confirmations = 0;
    }

    try {
      const { extrinsic, transaction } = await this.prepareTransaction(hexTx);
      //@TODO
      // wait for tx in block
      const result = await sendTx(this.api, extrinsic);
      const blockHash = result.status.isInBlock ? result.status.asInBlock : result.status.asFinalized;
      const header = await this._getBlockHeader(blockHash.toHex());
      const blockNumber = header.number.toNumber();
      const hash = extrinsic.hash.toHex();

      return this._wrapTransaction(transaction, hash, blockNumber, blockHash.toHex());
    } catch (err) {
      const error = err as any;
      for (let pattern of ERROR_PATTERN) {
        const match = ((error.toString?.() || '') as string).match(pattern);
        if (match) {
          const errDetails = this.api.registry.findMetaError(new Uint8Array([parseInt(match[1]), parseInt(match[2])]));

          // error.message is readonly, so construct a new error object
          throw new Error(
            JSON.stringify({
              message: `${errDetails.section}.${errDetails.name}: ${errDetails.docs}`,
              transaction: tx,
              transactionHash: tx.hash
            })
          );
        }
      }

      error.transaction = tx;
      error.transactionHash = tx.hash;
      throw error;
    }
  };
Example #8
Source File: tune.ts    From nova with GNU Affero General Public License v3.0 5 votes vote down vote up
/**
 * Tunes an execution manager's missing gas estimate based on single exec tx.
 */
export async function tuneMissingGasEstimate(
  L1_NovaExecutionManager: L1NovaExecutionManager,
  tx: Promise<TransactionResponse>
) {
  const { currentGasConfig, currentMissingGasEstimate, optimalMissingGasEstimate } =
    await findOptimalMissingGasEstimate(L1_NovaExecutionManager, tx);

  console.log();

  const loader = ora({
    text: chalk.gray(
      `tuning missing gas estimate from ${chalk.magenta(
        currentMissingGasEstimate.toString()
      )} to ${chalk.magenta(optimalMissingGasEstimate.toString())}\n`
    ),
    color: "magenta",
    indent: 6,
  }).start();

  // Add a 500 gas buffer for a bit of leeway.
  const newMissingGasEstimate = optimalMissingGasEstimate + 500;

  // Update the missing gas estimate on-chain.
  await (
    await L1_NovaExecutionManager.updateGasConfig({
      ...currentGasConfig,
      missingGasEstimate: newMissingGasEstimate,
    })
  ).wait();

  loader.stopAndPersist({
    symbol: chalk.magenta("✓"),
    text: chalk.gray(
      `tuned missing gas estimate from ${chalk.magenta(
        currentMissingGasEstimate.toString()
      )} to ${chalk.magenta(optimalMissingGasEstimate.toString())}\n`
    ),
  });

  loader.indent = 0;
}
Example #9
Source File: EthService.ts    From sakeperp-arbitrageur with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
static async supervise(
        signer: Wallet,
        tx: TransactionResponse,
        timeout: number,
        retry = 3,
    ): Promise<TransactionReceipt> {
        return new Promise((resolve, reject) => {
            // Set timeout for sending cancellation tx at double the gas price
            const timeoutId = setTimeout(async () => {
                const cancelTx = await signer.sendTransaction({
                    to: signer.address,
                    value: 0,
                    gasPrice: tx.gasPrice.mul(2), // TODO Make configurable?
                    nonce: tx.nonce,
                })

                await EthService.log.warn(
                    JSON.stringify({
                        event: "txCancelling",
                        params: {
                            tx: tx.hash,
                            txGasPrice: tx.gasPrice.toString(),
                            cancelTx: cancelTx.hash,
                            cancelTxGasPrice: cancelTx.gasPrice.toString(),
                            nonce: cancelTx.nonce,
                        },
                    }),
                )

                // Yo dawg I heard you like cancelling tx so
                // we put a cancel in your cancel tx so you can supervise while you supervise
                if (retry > 0) {
                    await EthService.supervise(signer, cancelTx, timeout, retry - 1)
                } else {
                    await cancelTx.wait()
                }
                reject({
                    reason: "timeout",
                    tx: tx.hash,
                    cancelTx: cancelTx.hash,
                })
            }, timeout)

            // Otherwise, resolve normally if the original tx is confirmed
            tx.wait().then(result => {
                clearTimeout(timeoutId)
                resolve(result)
            })
        })
    }
Example #10
Source File: PerpService.ts    From sakeperp-arbitrageur with BSD 3-Clause "New" or "Revised" License 5 votes vote down vote up
async openPosition(
        trader: Wallet,
        exchangePair: string,
        exchangeAddr: string,
        side: Side,
        quoteAssetAmount: Big,
        leverage: Big,
        minBaseAssetAmount: Big = Big(0),
        overrides?: Overrides,
    ): Promise<TransactionResponse> {
        const sakePerp = await this.createSakePerp(trader)

        // if the tx gonna fail it will throw here
        // const gasEstimate = await sakePerp.estimateGas.openPosition(
        //     exchangeAddr,
        //     side.valueOf(),
        //     { d: PerpService.toWei(quoteAssetAmount) },
        //     { d: PerpService.toWei(leverage) },
        //     { d: PerpService.toWei(minBaseAssetAmount) },
        // )

        const tx = await sakePerp.functions.openPosition(
            exchangeAddr,
            side.valueOf(),
            { d: PerpService.toWei(quoteAssetAmount) },
            { d: PerpService.toWei(leverage) },
            { d: PerpService.toWei(minBaseAssetAmount) },
            {
                // add a margin for gas limit since its estimation was sometimes too tight
                // gasLimit: BigNumber.from(
                //     Big(gasEstimate.toString())
                //         .mul(Big(1.2))
                //         .toFixed(0),
                // ),
                gasLimit: 2_500_000,
                ...overrides,
            },
        )
        this.log.jinfo({
            event: "OpenPositionTxSent",
            params: {
                exchangePair: exchangePair,
                trader: trader.address,
                amm: exchangeAddr,
                side,
                quoteAssetAmount: +quoteAssetAmount,
                leverage: +leverage,
                minBaseAssetAmount: +minBaseAssetAmount,
                txHash: tx.hash,
                gasPrice: tx.gasPrice.toString(),
                nonce: tx.nonce,
            },
        })

        return tx
    }
Example #11
Source File: base-provider.ts    From bodhi.js with Apache License 2.0 5 votes vote down vote up
// Queries
  getTransaction = (txHash: string): Promise<TransactionResponse> =>
    throwNotImplemented('getTransaction (deprecated: please use getTransactionByHash)');
Example #12
Source File: transactionErrors.d.ts    From tx2uml with MIT License 5 votes vote down vote up
getTransactionError: (tx: TransactionResponse, receipt: TransactionReceipt, provider: Provider) => Promise<string>
Example #13
Source File: transactionErrors.ts    From tx2uml with MIT License 5 votes vote down vote up
getTransactionError = async (
    tx: TransactionResponse,
    receipt: TransactionReceipt,
    provider: Provider
): Promise<string> => {
    if (typeof tx !== "object") {
        throw TypeError(`tx argument ${tx} must be a transaction object`)
    }
    if (typeof receipt !== "object") {
        throw TypeError(
            `receipt argument ${receipt} must be a transaction receipt object`
        )
    }
    if (receipt.status) {
        throw TypeError(
            "Transaction did not fail. Can only read the revert reason from failed transactions"
        )
    }
    if (!receipt.transactionHash) {
        throw TypeError(`There is no transaction hash on the receipt object`)
    }
    if (receipt.gasUsed === tx.gasLimit) {
        throw Error("Transaction failed as it ran out of gas.")
    }

    let rawMessageData
    try {
        const result = await provider.call(
            {
                ...tx,
            },
            receipt.blockNumber
        )

        // Trim the 0x prefix
        rawMessageData = result.slice(2)
    } catch (e) {
        if (e.message.startsWith("Node error: ")) {
            // Trim "Node error: "
            const errorObjectStr = e.message.slice(12)
            // Parse the error object
            const errorObject = JSON.parse(errorObjectStr)

            if (!errorObject.data) {
                throw Error(
                    "Failed to parse data field error object:" + errorObjectStr
                )
            }

            if (errorObject.data.startsWith("Reverted 0x")) {
                // Trim "Reverted 0x" from the data field
                rawMessageData = errorObject.data.slice(11)
            } else if (errorObject.data.startsWith("0x")) {
                // Trim "0x" from the data field
                rawMessageData = errorObject.data.slice(2)
            } else {
                throw Error(
                    "Failed to parse data field of error object:" +
                        errorObjectStr
                )
            }
        } else {
            throw Error(
                "Failed to parse error message from Ethereum call: " + e.message
            )
        }
    }

    return parseReasonCode(rawMessageData)
}
Example #14
Source File: usePromiseTransaction.ts    From useDApp with MIT License 5 votes vote down vote up
export function usePromiseTransaction(chainId: number | undefined, options?: TransactionOptions) {
  const [state, setState] = useState<TransactionStatus>({ status: 'None' })
  const { addTransaction } = useTransactionsContext()
  const { addNotification } = useNotificationsContext()

  const resetState = useCallback(() => {
    setState({ status: 'None' })
  }, [setState])

  const promiseTransaction = useCallback(
    async (transactionPromise: Promise<TransactionResponse>) => {
      if (!chainId) return
      let transaction: TransactionResponse | undefined = undefined
      try {
        setState({ status: 'PendingSignature', chainId })

        transaction = await transactionPromise

        setState({ transaction, status: 'Mining', chainId })
        addTransaction({
          transaction: {
            ...transaction,
            chainId: chainId,
          },
          submittedAt: Date.now(),
          transactionName: options?.transactionName,
        })
        const receipt = await transaction.wait()
        setState({ receipt, transaction, status: 'Success', chainId })
        return receipt
      } catch (e: any) {
        const errorMessage = e.error?.data?.message ?? e.error?.message ?? e.reason ?? e.data?.message ?? e.message
        if (transaction) {
          const droppedAndReplaced = isDroppedAndReplaced(e)

          if (droppedAndReplaced) {
            const status: TransactionState = e.receipt.status === 0 ? 'Fail' : 'Success'
            const type = status === 'Fail' ? 'transactionFailed' : 'transactionSucceed'

            addNotification({
              notification: {
                type,
                submittedAt: Date.now(),
                transaction: e.replacement,
                receipt: e.receipt,
                transactionName: e.replacement?.transactionName,
                originalTransaction: transaction,
              },
              chainId,
            })

            setState({
              status,
              transaction: e.replacement,
              originalTransaction: transaction,
              receipt: e.receipt,
              errorMessage,
              chainId,
            })
          } else {
            setState({ status: 'Fail', transaction, receipt: e.receipt, errorMessage, chainId })
          }
        } else {
          setState({ status: 'Exception', errorMessage, chainId })
        }
        return undefined
      }
    },
    [chainId, setState, addTransaction, options]
  )

  return { promiseTransaction, state, resetState }
}
Example #15
Source File: transactionsReducer.test.ts    From useDApp with MIT License 5 votes vote down vote up
describe('transactionsReducer', () => {
  it('addTransaction', () => {
    const transaction: StoredTransaction = {
      transaction: { chainId: 1 } as TransactionResponse,
      submittedAt: 10,
    }

    expect(transactionReducer({}, { type: 'ADD_TRANSACTION', payload: transaction })).to.deep.eq({
      1: [transaction],
    })
  })

  it('correct order', () => {
    const initial: StoredTransaction = {
      transaction: { chainId: 1 } as TransactionResponse,
      submittedAt: 10,
    }
    const added: StoredTransaction = {
      transaction: { chainId: 1 } as TransactionResponse,
      submittedAt: 30,
    }

    const newState = transactionReducer({ 1: [initial] }, { type: 'ADD_TRANSACTION', payload: added })

    expect(newState).to.deep.eq({ 1: [added, initial] })
  })

  it('update transactions', () => {
    const initialTransactions: StoredTransaction[] = [
      { transaction: { chainId: 1 } as TransactionResponse, submittedAt: 10 },
      { transaction: { chainId: 1 } as TransactionResponse, submittedAt: 15 },
      { transaction: { chainId: 1 } as TransactionResponse, submittedAt: 20 },
    ]

    const newTransactions = initialTransactions.map((tx) => ({ ...tx, lastCheckedBlockNumber: 12 }))

    const newState = transactionReducer(
      { 1: initialTransactions },
      { type: 'UPDATE_TRANSACTIONS', chainId: 1, transactions: newTransactions }
    )

    expect(newState).to.deep.eq({ 1: newTransactions })
  })
})
Example #16
Source File: contractCallOutOfGasMock.ts    From useDApp with MIT License 5 votes vote down vote up
transferOutOfGasMock = (): TransactionResponse => {
  return {
    wait: (): Promise<TransactionReceipt> => {
      return Promise.reject({ reason: 'out of gas' })
    },
  } as TransactionResponse
}
Example #17
Source File: index.ts    From ethereum-sdk with MIT License 5 votes vote down vote up
constructor(private readonly tx: TransactionResponse) {}
Example #18
Source File: Provider.ts    From evm-provider.js with Apache License 2.0 5 votes vote down vote up
/**
   * Unimplemented, will always fail.
   */
  async getTransaction(transactionHash: string): Promise<TransactionResponse> {
    return this._fail('getTransaction');
  }
Example #19
Source File: Provider.ts    From evm-provider.js with Apache License 2.0 5 votes vote down vote up
/**
   * Unimplemented
   */
  async sendTransaction(
    signedTransaction: string | Promise<string>
  ): Promise<TransactionResponse> {
    return this._fail('sendTransaction');
  }
Example #20
Source File: MigrateV1Exchange.tsx    From sushiswap-exchange with GNU General Public License v3.0 4 votes vote down vote up
function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount: TokenAmount; token: Token }) {
  const { account, chainId } = useActiveWeb3React()
  const totalSupply = useTotalSupply(liquidityTokenAmount.token)
  const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
  const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)

  const [v2PairState, v2Pair] = usePair(chainId ? WETH[chainId] : undefined, token)
  const isFirstLiquidityProvider: boolean = v2PairState === PairState.NOT_EXISTS

  const v2SpotPrice = chainId && v2Pair ? v2Pair.reserveOf(token).divide(v2Pair.reserveOf(WETH[chainId])) : undefined

  const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
  const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)

  const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION

  const ethWorth: CurrencyAmount = exchangeETHBalance
    ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient)
    : CurrencyAmount.ether(ZERO)

  const tokenWorth: TokenAmount = exchangeTokenBalance
    ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
    : new TokenAmount(token, ZERO)

  const [approval, approve] = useApproveCallback(liquidityTokenAmount, MIGRATOR_ADDRESS)

  const v1SpotPrice =
    exchangeTokenBalance && exchangeETHBalance
      ? exchangeTokenBalance.divide(new Fraction(exchangeETHBalance.raw, WEI_DENOM))
      : null

  const priceDifferenceFraction: Fraction | undefined =
    v1SpotPrice && v2SpotPrice
      ? v1SpotPrice
          .divide(v2SpotPrice)
          .multiply('100')
          .subtract('100')
      : undefined

  const priceDifferenceAbs: Fraction | undefined = priceDifferenceFraction?.lessThan(ZERO)
    ? priceDifferenceFraction?.multiply('-1')
    : priceDifferenceFraction

  const minAmountETH: JSBI | undefined =
    v2SpotPrice && tokenWorth
      ? tokenWorth
          .divide(v2SpotPrice)
          .multiply(WEI_DENOM)
          .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
      : ethWorth?.numerator

  const minAmountToken: JSBI | undefined =
    v2SpotPrice && ethWorth
      ? ethWorth
          .multiply(v2SpotPrice)
          .multiply(JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(token.decimals)))
          .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
      : tokenWorth?.numerator

  const addTransaction = useTransactionAdder()
  const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)

  const migrator = useV2MigratorContract()
  const migrate = useCallback(() => {
    if (!minAmountToken || !minAmountETH || !migrator) return

    setConfirmingMigration(true)
    migrator
      .migrate(
        token.address,
        minAmountToken.toString(),
        minAmountETH.toString(),
        account,
        Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW
      )
      .then((response: TransactionResponse) => {
        ReactGA.event({
          category: 'Migrate',
          action: 'V1->V2',
          label: token?.symbol
        })

        addTransaction(response, {
          summary: `Migrate ${token.symbol} liquidity to V2`
        })
        setPendingMigrationHash(response.hash)
      })
      .catch(() => {
        setConfirmingMigration(false)
      })
  }, [minAmountToken, minAmountETH, migrator, token, account, addTransaction])

  const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)

  const largePriceDifference = !!priceDifferenceAbs && !priceDifferenceAbs.lessThan(JSBI.BigInt(5))

  const isSuccessfullyMigrated = !!pendingMigrationHash && noLiquidityTokens

  return (
    <AutoColumn gap="20px">
      <TYPE.body my={9} style={{ fontWeight: 400 }}>
        This tool will safely migrate your V1 liquidity to V2 with minimal price risk. The process is completely
        trustless thanks to the{' '}
        {chainId && (
          <ExternalLink href={getEtherscanLink(chainId, MIGRATOR_ADDRESS, 'address')}>
            <TYPE.blue display="inline">Uniswap migration contract↗</TYPE.blue>
          </ExternalLink>
        )}
        .
      </TYPE.body>

      {!isFirstLiquidityProvider && largePriceDifference ? (
        <YellowCard>
          <TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
            It{"'"}s best to deposit liquidity into SushiSwap LP Token at a price you believe is correct. If the V2 price seems
            incorrect, you can either make a swap to move the price or wait for someone else to do so.
          </TYPE.body>
          <AutoColumn gap="8px">
            <RowBetween>
              <TYPE.body>V1 Price:</TYPE.body>
              <TYPE.black>
                {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </TYPE.black>
            </RowBetween>
            <RowBetween>
              <div />
              <TYPE.black>
                {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </TYPE.black>
            </RowBetween>

            <RowBetween>
              <TYPE.body>V2 Price:</TYPE.body>
              <TYPE.black>
                {v2SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </TYPE.black>
            </RowBetween>
            <RowBetween>
              <div />
              <TYPE.black>
                {v2SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </TYPE.black>
            </RowBetween>

            <RowBetween>
              <TYPE.body color="inherit">Price Difference:</TYPE.body>
              <TYPE.black color="inherit">{priceDifferenceAbs?.toSignificant(4)}%</TYPE.black>
            </RowBetween>
          </AutoColumn>
        </YellowCard>
      ) : null}

      {isFirstLiquidityProvider && (
        <PinkCard>
          <TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
            You are the first liquidity provider for this pair on SushiSwap LP Token. Your liquidity will be migrated at the
            current V1 price. Your transaction cost also includes the gas to create the pool.
          </TYPE.body>

          <AutoColumn gap="8px">
            <RowBetween>
              <TYPE.body>V1 Price:</TYPE.body>
              <TYPE.black>
                {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </TYPE.black>
            </RowBetween>
            <RowBetween>
              <div />
              <TYPE.black>
                {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </TYPE.black>
            </RowBetween>
          </AutoColumn>
        </PinkCard>
      )}

      <LightCard>
        <V1LiquidityInfo
          token={token}
          liquidityTokenAmount={liquidityTokenAmount}
          tokenWorth={tokenWorth}
          ethWorth={ethWorth}
        />

        <div style={{ display: 'flex', marginTop: '1rem' }}>
          <AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}>
            <ButtonConfirmed
              confirmed={approval === ApprovalState.APPROVED}
              disabled={approval !== ApprovalState.NOT_APPROVED}
              onClick={approve}
            >
              {approval === ApprovalState.PENDING ? (
                <Dots>Approving</Dots>
              ) : approval === ApprovalState.APPROVED ? (
                'Approved'
              ) : (
                'Approve'
              )}
            </ButtonConfirmed>
          </AutoColumn>
          <AutoColumn gap="12px" style={{ flex: '1' }}>
            <ButtonConfirmed
              confirmed={isSuccessfullyMigrated}
              disabled={
                isSuccessfullyMigrated ||
                noLiquidityTokens ||
                isMigrationPending ||
                approval !== ApprovalState.APPROVED ||
                confirmingMigration
              }
              onClick={migrate}
            >
              {isSuccessfullyMigrated ? 'Success' : isMigrationPending ? <Dots>Migrating</Dots> : 'Migrate'}
            </ButtonConfirmed>
          </AutoColumn>
        </div>
      </LightCard>
      <TYPE.darkGray style={{ textAlign: 'center' }}>
        {`Your Uniswap V1 ${token.symbol}/ETH liquidity will become SushiSwap LP Token ${token.symbol}/ETH liquidity.`}
      </TYPE.darkGray>
    </AutoColumn>
  )
}
Example #21
Source File: Signer.ts    From evm-provider.js with Apache License 2.0 4 votes vote down vote up
/**
   *
   * @param transaction
   * @returns A promise that resolves to the transaction's response
   */
  async sendTransaction(
    _transaction: Deferrable<TransactionRequest>
  ): Promise<TransactionResponse> {
    this._checkProvider('sendTransaction');

    const signerAddress = await this.getSubstrateAddress();
    const evmAddress = await this.getAddress();

    // estimateResources requires the from parameter.
    // However, when creating the contract, there is no from parameter in the tx
    const transaction = {
      from: evmAddress,
      ..._transaction
    };

    const resources = await this.provider.estimateResources(transaction);

    // Multiply by 3.1
    const gasLimit: BigNumber = resources.gas.mul(31).div(10);
    let storageLimit: BigNumber;

    // If the storage limit is supplied, override it from the estimateResources
    if (transaction.customData) {
      if ('storageLimit' in transaction.customData) {
        storageLimit = transaction.customData.storageLimit;
        if (isNumber(storageLimit)) {
          storageLimit = BigNumber.from(storageLimit);
        }
      }
    } else {
      storageLimit = resources.storage.mul(31).div(10);
    }

    let totalLimit = await transaction.gasLimit;

    if (totalLimit === null || totalLimit === undefined) {
      totalLimit = gasLimit.add(storageLimit);
    }

    transaction.gasLimit = totalLimit;

    const tx = await this.populateTransaction(transaction);

    const data = tx.data;
    const from = tx.from;

    if (!data) {
      return logger.throwError('Request data not found');
    }

    if (!from) {
      return logger.throwError('Request from not found');
    }

    let extrinsic: SubmittableExtrinsic<'promise'>;

    // @TODO create contract
    if (!tx.to) {
      extrinsic = this.provider.api.tx.evm.create(
        tx.data,
        toBN(tx.value),
        toBN(gasLimit),
        toBN(storageLimit.isNegative() ? 0 : storageLimit)
      );
    } else {
      extrinsic = this.provider.api.tx.evm.call(
        tx.to,
        tx.data,
        toBN(tx.value),
        toBN(gasLimit),
        toBN(storageLimit.isNegative() ? 0 : storageLimit)
      );
    }

    await extrinsic.signAsync(signerAddress);

    return new Promise((resolve, reject) => {
      extrinsic
        .send((result: SubmittableResult) => {
          handleTxResponse(result, this.provider.api)
            .then(() => {
              resolve({
                hash: extrinsic.hash.toHex(),
                from: from || '',
                confirmations: 0,
                nonce: toBN(tx.nonce).toNumber(),
                gasLimit: BigNumber.from(tx.gasLimit || '0'),
                gasPrice: BigNumber.from(0),
                data: dataToString(data),
                value: BigNumber.from(tx.value || '0'),
                chainId: 13939,
                wait: (confirmations?: number): Promise<TransactionReceipt> => {
                  return this.provider._resolveTransactionReceipt(
                    extrinsic.hash.toHex(),
                    result.status.asInBlock.toHex(),
                    from
                  );
                }
              });
            })
            .catch(({ message, result }) => {
              reject(message);
            });
        })
        .catch((error) => {
          reject(error && error.message);
        });
    });
  }
Example #22
Source File: RemoveV1Exchange.tsx    From luaswap-interface with GNU General Public License v3.0 4 votes vote down vote up
function V1PairRemoval({
  exchangeContract,
  liquidityTokenAmount,
  token
}: {
  exchangeContract: Contract
  liquidityTokenAmount: TokenAmount
  token: Token
}) {
  const { chainId } = useActiveWeb3React()
  const totalSupply = useTotalSupply(liquidityTokenAmount.token)
  const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
  const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)

  const [confirmingRemoval, setConfirmingRemoval] = useState<boolean>(false)
  const [pendingRemovalHash, setPendingRemovalHash] = useState<string | null>(null)

  const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION

  const ethWorth: CurrencyAmount = exchangeETHBalance
    ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient)
    : CurrencyAmount.ether(ZERO)

  const tokenWorth: TokenAmount = exchangeTokenBalance
    ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
    : new TokenAmount(token, ZERO)

  const addTransaction = useTransactionAdder()
  const isRemovalPending = useIsTransactionPending(pendingRemovalHash ?? undefined)

  const remove = useCallback(() => {
    if (!liquidityTokenAmount) return

    setConfirmingRemoval(true)
    exchangeContract
      .removeLiquidity(
        liquidityTokenAmount.raw.toString(),
        1, // min_eth, this is safe because we're removing liquidity
        1, // min_tokens, this is safe because we're removing liquidity
        Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW
      )
      .then((response: TransactionResponse) => {
        ReactGA.event({
          category: 'Remove',
          action: 'V1',
          label: token?.symbol
        })

        addTransaction(response, {
          summary: `Remove ${chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH V1 liquidity`
        })
        setPendingRemovalHash(response.hash)
      })
      .catch((error: Error) => {
        console.error(error)
        setConfirmingRemoval(false)
      })
  }, [exchangeContract, liquidityTokenAmount, token, chainId, addTransaction])

  const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)

  const isSuccessfullyRemoved = !!pendingRemovalHash && noLiquidityTokens

  return (
    <AutoColumn gap="20px">
      <TYPE.body my={9} style={{ fontWeight: 400 }}>
        This tool will remove your V1 liquidity and send the underlying assets to your wallet.
      </TYPE.body>

      <LightCard>
        <V1LiquidityInfo
          token={token}
          liquidityTokenAmount={liquidityTokenAmount}
          tokenWorth={tokenWorth}
          ethWorth={ethWorth}
        />

        <div style={{ display: 'flex', marginTop: '1rem' }}>
          <ButtonConfirmed
            confirmed={isSuccessfullyRemoved}
            disabled={isSuccessfullyRemoved || noLiquidityTokens || isRemovalPending || confirmingRemoval}
            onClick={remove}
          >
            {isSuccessfullyRemoved ? 'Success' : isRemovalPending ? <Dots>Removing</Dots> : 'Remove'}
          </ButtonConfirmed>
        </div>
      </LightCard>
      <TYPE.darkGray style={{ textAlign: 'center' }}>
        {`Your LuaSwap V1 ${
          chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol
        }/ETH liquidity will be redeemed for underlying assets.`}
      </TYPE.darkGray>
    </AutoColumn>
  )
}
Example #23
Source File: Signer.ts    From bodhi.js with Apache License 2.0 4 votes vote down vote up
/**
   *
   * @param transaction
   * @returns A promise that resolves to the transaction's response
   */
  async sendTransaction(_transaction: Deferrable<TransactionRequest>): Promise<TransactionResponse> {
    this._checkProvider('sendTransaction');

    const signerAddress = await this.getSubstrateAddress();
    const evmAddress = await this.getAddress();

    // estimateResources requires the from parameter.
    // However, when creating the contract, there is no from parameter in the tx
    const transaction = {
      from: evmAddress,
      ..._transaction
    };

    const resources = await this.provider.estimateResources(transaction);

    let gasLimit: BigNumber;
    let storageLimit: BigNumber;

    let totalLimit = await transaction.gasLimit;

    if (totalLimit === null || totalLimit === undefined) {
      gasLimit = resources.gas;
      storageLimit = resources.storage;
      totalLimit = resources.gas.add(resources.storage);
    } else {
      const estimateTotalLimit = resources.gas.add(resources.storage);
      gasLimit = BigNumber.from(totalLimit).mul(resources.gas).div(estimateTotalLimit).add(1);
      storageLimit = BigNumber.from(totalLimit).mul(resources.storage).div(estimateTotalLimit).add(1);
    }

    transaction.gasLimit = totalLimit;

    const tx = await this.populateTransaction(transaction);

    const data = tx.data;
    const from = tx.from;

    if (!data) {
      return logger.throwError('Request data not found');
    }

    if (!from) {
      return logger.throwError('Request from not found');
    }

    let extrinsic: SubmittableExtrinsic<'promise'>;

    // @TODO create contract
    if (!tx.to) {
      extrinsic = this.provider.api.tx.evm.create(
        tx.data,
        toBN(tx.value),
        toBN(gasLimit),
        toBN(storageLimit.isNegative() ? 0 : storageLimit),
        tx.accessList || []
      );
    } else {
      extrinsic = this.provider.api.tx.evm.call(
        tx.to,
        tx.data,
        toBN(tx.value),
        toBN(gasLimit),
        toBN(storageLimit.isNegative() ? 0 : storageLimit),
        tx.accessList || []
      );
    }

    await extrinsic.signAsync(signerAddress);

    return new Promise((resolve, reject) => {
      extrinsic
        .send((result: SubmittableResult) => {
          handleTxResponse(result, this.provider.api)
            .then(() => {
              resolve({
                hash: extrinsic.hash.toHex(),
                from: from || '',
                confirmations: 0,
                nonce: toBN(tx.nonce).toNumber(),
                gasLimit: BigNumber.from(tx.gasLimit || '0'),
                gasPrice: BigNumber.from(1),
                data: dataToString(data),
                value: BigNumber.from(tx.value || '0'),
                chainId: +this.provider.api.consts.evmAccounts.chainId.toString(),
                wait: (confirmations?: number): Promise<TransactionReceipt> => {
                  const hex = result.status.isInBlock
                    ? result.status.asInBlock.toHex()
                    : result.status.asFinalized.toHex();
                  return this.provider.getTransactionReceiptAtBlock(extrinsic.hash.toHex(), hex);
                }
              });
            })
            .catch(reject);
        })
        .catch(reject);
    });
  }
Example #24
Source File: MigrateV1Exchange.tsx    From luaswap-interface with GNU General Public License v3.0 4 votes vote down vote up
function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount: TokenAmount; token: Token }) {
  const { account, chainId } = useActiveWeb3React()
  const totalSupply = useTotalSupply(liquidityTokenAmount.token)
  const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
  const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)

  const [v2PairState, v2Pair] = usePair(chainId ? WETH[chainId] : undefined, token)
  const isFirstLiquidityProvider: boolean = v2PairState === PairState.NOT_EXISTS

  const v2SpotPrice = chainId && v2Pair ? v2Pair.reserveOf(token).divide(v2Pair.reserveOf(WETH[chainId])) : undefined

  const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
  const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)

  const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION

  const ethWorth: CurrencyAmount = exchangeETHBalance
    ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient)
    : CurrencyAmount.ether(ZERO)

  const tokenWorth: TokenAmount = exchangeTokenBalance
    ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
    : new TokenAmount(token, ZERO)

  const [approval, approve] = useApproveCallback(liquidityTokenAmount, MIGRATOR_ADDRESS)

  const v1SpotPrice =
    exchangeTokenBalance && exchangeETHBalance
      ? exchangeTokenBalance.divide(new Fraction(exchangeETHBalance.raw, WEI_DENOM))
      : null

  const priceDifferenceFraction: Fraction | undefined =
    v1SpotPrice && v2SpotPrice
      ? v1SpotPrice
          .divide(v2SpotPrice)
          .multiply('100')
          .subtract('100')
      : undefined

  const priceDifferenceAbs: Fraction | undefined = priceDifferenceFraction?.lessThan(ZERO)
    ? priceDifferenceFraction?.multiply('-1')
    : priceDifferenceFraction

  const minAmountETH: JSBI | undefined =
    v2SpotPrice && tokenWorth
      ? tokenWorth
          .divide(v2SpotPrice)
          .multiply(WEI_DENOM)
          .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
      : ethWorth?.numerator

  const minAmountToken: JSBI | undefined =
    v2SpotPrice && ethWorth
      ? ethWorth
          .multiply(v2SpotPrice)
          .multiply(JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(token.decimals)))
          .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
      : tokenWorth?.numerator

  const addTransaction = useTransactionAdder()
  const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)

  const migrator = useV2MigratorContract()
  const migrate = useCallback(() => {
    if (!minAmountToken || !minAmountETH || !migrator) return

    setConfirmingMigration(true)
    migrator
      .migrate(
        token.address,
        minAmountToken.toString(),
        minAmountETH.toString(),
        account,
        Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW
      )
      .then((response: TransactionResponse) => {
        ReactGA.event({
          category: 'Migrate',
          action: 'V1->V2',
          label: token?.symbol
        })

        addTransaction(response, {
          summary: `Migrate ${token.symbol} liquidity to V2`
        })
        setPendingMigrationHash(response.hash)
      })
      .catch(() => {
        setConfirmingMigration(false)
      })
  }, [minAmountToken, minAmountETH, migrator, token, account, addTransaction])

  const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)

  const largePriceDifference = !!priceDifferenceAbs && !priceDifferenceAbs.lessThan(JSBI.BigInt(5))

  const isSuccessfullyMigrated = !!pendingMigrationHash && noLiquidityTokens

  return (
    <AutoColumn gap="20px">
      <TYPE.body my={9} style={{ fontWeight: 400 }}>
        This tool will safely migrate your V1 liquidity to V2 with minimal price risk. The process is completely
        trustless thanks to the{' '}
        {chainId && (
          <ExternalLink href={getEtherscanLink(chainId, MIGRATOR_ADDRESS, 'address')}>
            <TYPE.blue display="inline">LuaSwap migration contract↗</TYPE.blue>
          </ExternalLink>
        )}
        .
      </TYPE.body>

      {!isFirstLiquidityProvider && largePriceDifference ? (
        <YellowCard>
          <TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
            It{"'"}s best to deposit liquidity into LuaSwap V1 at a price you believe is correct. If the V2 price seems
            incorrect, you can either make a swap to move the price or wait for someone else to do so.
          </TYPE.body>
          <AutoColumn gap="8px">
            <RowBetween>
              <TYPE.body>V1 Price:</TYPE.body>
              <TYPE.black>
                {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </TYPE.black>
            </RowBetween>
            <RowBetween>
              <div />
              <TYPE.black>
                {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </TYPE.black>
            </RowBetween>

            <RowBetween>
              <TYPE.body>V2 Price:</TYPE.body>
              <TYPE.black>
                {v2SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </TYPE.black>
            </RowBetween>
            <RowBetween>
              <div />
              <TYPE.black>
                {v2SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </TYPE.black>
            </RowBetween>

            <RowBetween>
              <TYPE.body color="inherit">Price Difference:</TYPE.body>
              <TYPE.black color="inherit">{priceDifferenceAbs?.toSignificant(4)}%</TYPE.black>
            </RowBetween>
          </AutoColumn>
        </YellowCard>
      ) : null}

      {isFirstLiquidityProvider && (
        <PinkCard>
          <TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
            You are the first liquidity provider for this pair on LuaSwap V2. Your liquidity will be migrated at the
            current V1 price. Your transaction cost also includes the gas to create the pool.
          </TYPE.body>

          <AutoColumn gap="8px">
            <RowBetween>
              <TYPE.body>V1 Price:</TYPE.body>
              <TYPE.black>
                {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </TYPE.black>
            </RowBetween>
            <RowBetween>
              <div />
              <TYPE.black>
                {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </TYPE.black>
            </RowBetween>
          </AutoColumn>
        </PinkCard>
      )}

      <LightCard>
        <V1LiquidityInfo
          token={token}
          liquidityTokenAmount={liquidityTokenAmount}
          tokenWorth={tokenWorth}
          ethWorth={ethWorth}
        />

        <div style={{ display: 'flex', marginTop: '1rem' }}>
          <AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}>
            <ButtonConfirmed
              confirmed={approval === ApprovalState.APPROVED}
              disabled={approval !== ApprovalState.NOT_APPROVED}
              onClick={approve}
            >
              {approval === ApprovalState.PENDING ? (
                <Dots>Approving</Dots>
              ) : approval === ApprovalState.APPROVED ? (
                'Approved'
              ) : (
                'Approve'
              )}
            </ButtonConfirmed>
          </AutoColumn>
          <AutoColumn gap="12px" style={{ flex: '1' }}>
            <ButtonConfirmed
              confirmed={isSuccessfullyMigrated}
              disabled={
                isSuccessfullyMigrated ||
                noLiquidityTokens ||
                isMigrationPending ||
                approval !== ApprovalState.APPROVED ||
                confirmingMigration
              }
              onClick={migrate}
            >
              {isSuccessfullyMigrated ? 'Success' : isMigrationPending ? <Dots>Migrating</Dots> : 'Migrate'}
            </ButtonConfirmed>
          </AutoColumn>
        </div>
      </LightCard>
      <TYPE.darkGray style={{ textAlign: 'center' }}>
        {`Your LuaSwap V1 ${token.symbol}/ETH liquidity will become LuaSwap V2 ${token.symbol}/ETH liquidity.`}
      </TYPE.darkGray>
    </AutoColumn>
  )
}
Example #25
Source File: RemoveV1Exchange.tsx    From goose-frontend-amm with GNU General Public License v3.0 4 votes vote down vote up
function V1PairRemoval({
  exchangeContract,
  liquidityTokenAmount,
  token,
}: {
  exchangeContract: Contract
  liquidityTokenAmount: TokenAmount
  token: Token
}) {
  const { chainId } = useActiveWeb3React()
  const totalSupply = useTotalSupply(liquidityTokenAmount.token)
  const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
  const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)

  const [confirmingRemoval, setConfirmingRemoval] = useState<boolean>(false)
  const [pendingRemovalHash, setPendingRemovalHash] = useState<string | null>(null)

  const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION

  const ethWorth: CurrencyAmount = exchangeETHBalance
    ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient)
    : CurrencyAmount.ether(ZERO)

  const tokenWorth: TokenAmount = exchangeTokenBalance
    ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
    : new TokenAmount(token, ZERO)

  const addTransaction = useTransactionAdder()
  const isRemovalPending = useIsTransactionPending(pendingRemovalHash ?? undefined)

  const remove = useCallback(() => {
    if (!liquidityTokenAmount) return

    setConfirmingRemoval(true)
    exchangeContract
      .removeLiquidity(
        liquidityTokenAmount.raw.toString(),
        1, // min_eth, this is safe because we're removing liquidity
        1, // min_tokens, this is safe because we're removing liquidity
        Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW
      )
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: `Remove ${chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH V1 liquidity`,
        })
        setPendingRemovalHash(response.hash)
      })
      .catch((error: Error) => {
        console.error(error)
        setConfirmingRemoval(false)
      })
  }, [exchangeContract, liquidityTokenAmount, token, chainId, addTransaction])

  const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)

  const isSuccessfullyRemoved = !!pendingRemovalHash && noLiquidityTokens

  return (
    <AutoColumn gap="20px">
      <Body my={9} style={{ fontWeight: 400 }}>
        This tool will remove your V1 liquidity and send the underlying assets to your wallet.
      </Body>

      <LightCard>
        <V1LiquidityInfo
          token={token}
          liquidityTokenAmount={liquidityTokenAmount}
          tokenWorth={tokenWorth}
          ethWorth={ethWorth}
        />

        <div style={{ display: 'flex', marginTop: '1rem' }}>
          <Button
            variant={isSuccessfullyRemoved ? 'success' : 'primary'}
            disabled={isSuccessfullyRemoved || noLiquidityTokens || isRemovalPending || confirmingRemoval}
            onClick={remove}
          >
            {isSuccessfullyRemoved ? 'Success' : isRemovalPending ? <Dots>Removing</Dots> : 'Remove'}
          </Button>
        </div>
      </LightCard>
      <DarkGray style={{ textAlign: 'center' }}>
        {`Your Uniswap V1 ${
          chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol
        }/ETH liquidity will be redeemed for underlying assets.`}
      </DarkGray>
    </AutoColumn>
  )
}
Example #26
Source File: MigrateV1Exchange.tsx    From goose-frontend-amm with GNU General Public License v3.0 4 votes vote down vote up
function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount: TokenAmount; token: Token }) {
  const { account, chainId } = useActiveWeb3React()
  const totalSupply = useTotalSupply(liquidityTokenAmount.token)
  const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
  const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)

  const [v2PairState, v2Pair] = usePair(chainId ? WETH[chainId] : undefined, token)
  const isFirstLiquidityProvider: boolean = v2PairState === PairState.NOT_EXISTS

  const v2SpotPrice = chainId && v2Pair ? v2Pair.reserveOf(token).divide(v2Pair.reserveOf(WETH[chainId])) : undefined

  const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
  const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)

  const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION

  const ethWorth: CurrencyAmount = exchangeETHBalance
    ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient)
    : CurrencyAmount.ether(ZERO)

  const tokenWorth: TokenAmount = exchangeTokenBalance
    ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
    : new TokenAmount(token, ZERO)

  const [approval, approve] = useApproveCallback(liquidityTokenAmount, MIGRATOR_ADDRESS)

  const v1SpotPrice =
    exchangeTokenBalance && exchangeETHBalance
      ? exchangeTokenBalance.divide(new Fraction(exchangeETHBalance.raw, WEI_DENOM))
      : null

  const priceDifferenceFraction: Fraction | undefined =
    v1SpotPrice && v2SpotPrice ? v1SpotPrice.divide(v2SpotPrice).multiply('100').subtract('100') : undefined

  const priceDifferenceAbs: Fraction | undefined = priceDifferenceFraction?.lessThan(ZERO)
    ? priceDifferenceFraction?.multiply('-1')
    : priceDifferenceFraction

  const minAmountETH: JSBI | undefined =
    v2SpotPrice && tokenWorth
      ? tokenWorth.divide(v2SpotPrice).multiply(WEI_DENOM).multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
      : ethWorth?.numerator

  const minAmountToken: JSBI | undefined =
    v2SpotPrice && ethWorth
      ? ethWorth
          .multiply(v2SpotPrice)
          .multiply(JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(token.decimals)))
          .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
      : tokenWorth?.numerator

  const addTransaction = useTransactionAdder()
  const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)

  const migrator = useV2MigratorContract()
  const migrate = useCallback(() => {
    if (!minAmountToken || !minAmountETH || !migrator) return

    setConfirmingMigration(true)
    migrator
      .migrate(
        token.address,
        minAmountToken.toString(),
        minAmountETH.toString(),
        account,
        Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW
      )
      .then((response: TransactionResponse) => {
        addTransaction(response, {
          summary: `Migrate ${token.symbol} liquidity to V2`,
        })
        setPendingMigrationHash(response.hash)
      })
      .catch(() => {
        setConfirmingMigration(false)
      })
  }, [minAmountToken, minAmountETH, migrator, token, account, addTransaction])

  const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)

  const largePriceDifference = !!priceDifferenceAbs && !priceDifferenceAbs.lessThan(JSBI.BigInt(5))

  const isSuccessfullyMigrated = !!pendingMigrationHash && noLiquidityTokens

  return (
    <AutoColumn gap="20px">
      <Body my={9} style={{ fontWeight: 400 }}>
        This tool will safely migrate your V1 liquidity to V2 with minimal price risk. The process is completely
        trustless thanks to the{' '}
        {chainId && (
          <ExternalLink href={getEtherscanLink(chainId, MIGRATOR_ADDRESS, 'address')}>
            <Blue display="inline">Uniswap migration contract↗</Blue>
          </ExternalLink>
        )}
        .
      </Body>

      {!isFirstLiquidityProvider && largePriceDifference ? (
        <YellowCard>
          <Body style={{ marginBottom: 8, fontWeight: 400 }}>
            It&apos;s best to deposit liquidity into Uniswap V2 at a price you believe is correct. If the V2 price seems
            incorrect, you can either make a swap to move the price or wait for someone else to do so.
          </Body>
          <AutoColumn gap="8px">
            <RowBetween>
              <Body>V1 Price:</Body>
              <Black>
                {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </Black>
            </RowBetween>
            <RowBetween>
              <div />
              <Black>
                {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </Black>
            </RowBetween>

            <RowBetween>
              <Body>V2 Price:</Body>
              <Black>
                {v2SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </Black>
            </RowBetween>
            <RowBetween>
              <div />
              <Black>
                {v2SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </Black>
            </RowBetween>

            <RowBetween>
              <Body color="inherit">Price Difference:</Body>
              <Black color="inherit">{priceDifferenceAbs?.toSignificant(4)}%</Black>
            </RowBetween>
          </AutoColumn>
        </YellowCard>
      ) : null}

      {isFirstLiquidityProvider && (
        <PinkCard>
          <Body style={{ marginBottom: 8, fontWeight: 400 }}>
            You are the first liquidity provider for this pair on Uniswap V2. Your liquidity will be migrated at the
            current V1 price. Your transaction cost also includes the gas to create the pool.
          </Body>

          <AutoColumn gap="8px">
            <RowBetween>
              <Body>V1 Price:</Body>
              <Black>
                {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </Black>
            </RowBetween>
            <RowBetween>
              <div />
              <Black>
                {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </Black>
            </RowBetween>
          </AutoColumn>
        </PinkCard>
      )}

      <LightCard>
        <V1LiquidityInfo
          token={token}
          liquidityTokenAmount={liquidityTokenAmount}
          tokenWorth={tokenWorth}
          ethWorth={ethWorth}
        />

        <div style={{ display: 'flex', marginTop: '1rem' }}>
          <AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}>
            <Button
              variant={approval === ApprovalState.APPROVED ? 'success' : 'primary'}
              disabled={approval !== ApprovalState.NOT_APPROVED}
              onClick={approve}
            >
              {approval === ApprovalState.PENDING ? (
                <Dots>Approving</Dots>
              ) : approval === ApprovalState.APPROVED ? (
                'Approved'
              ) : (
                'Approve'
              )}
            </Button>
          </AutoColumn>
          <AutoColumn gap="12px" style={{ flex: '1' }}>
            <Button
              variant={isSuccessfullyMigrated ? 'success' : 'primary'}
              disabled={
                isSuccessfullyMigrated ||
                noLiquidityTokens ||
                isMigrationPending ||
                approval !== ApprovalState.APPROVED ||
                confirmingMigration
              }
              onClick={migrate}
            >
              {isSuccessfullyMigrated ? 'Success' : isMigrationPending ? <Dots>Migrating</Dots> : 'Migrate'}
            </Button>
          </AutoColumn>
        </div>
      </LightCard>
      <DarkDray style={{ textAlign: 'center' }}>
        {`Your Uniswap V1 ${token.symbol}/ETH liquidity will become Uniswap V2 ${token.symbol}/ETH liquidity.`}
      </DarkDray>
    </AutoColumn>
  )
}
Example #27
Source File: RemoveV1Exchange.tsx    From dyp with Do What The F*ck You Want To Public License 4 votes vote down vote up
function V1PairRemoval({
  exchangeContract,
  liquidityTokenAmount,
  token
}: {
  exchangeContract: Contract
  liquidityTokenAmount: TokenAmount
  token: Token
}) {
  const { chainId } = useActiveWeb3React()
  const totalSupply = useTotalSupply(liquidityTokenAmount.token)
  const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
  const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)

  const [confirmingRemoval, setConfirmingRemoval] = useState<boolean>(false)
  const [pendingRemovalHash, setPendingRemovalHash] = useState<string | null>(null)

  const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION

  const ethWorth: CurrencyAmount = exchangeETHBalance
    ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient)
    : CurrencyAmount.ether(ZERO)

  const tokenWorth: TokenAmount = exchangeTokenBalance
    ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
    : new TokenAmount(token, ZERO)

  const addTransaction = useTransactionAdder()
  const isRemovalPending = useIsTransactionPending(pendingRemovalHash ?? undefined)

  const remove = useCallback(() => {
    if (!liquidityTokenAmount) return

    setConfirmingRemoval(true)
    exchangeContract
      .removeLiquidity(
        liquidityTokenAmount.raw.toString(),
        1, // min_eth, this is safe because we're removing liquidity
        1, // min_tokens, this is safe because we're removing liquidity
        Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW
      )
      .then((response: TransactionResponse) => {
        ReactGA.event({
          category: 'Remove',
          action: 'V1',
          label: token?.symbol
        })

        addTransaction(response, {
          summary: `Remove ${chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH V1 liquidity`
        })
        setPendingRemovalHash(response.hash)
      })
      .catch((error: Error) => {
        console.error(error)
        setConfirmingRemoval(false)
      })
  }, [exchangeContract, liquidityTokenAmount, token, chainId, addTransaction])

  const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)

  const isSuccessfullyRemoved = !!pendingRemovalHash && noLiquidityTokens

  return (
    <AutoColumn gap="20px">
      <TYPE.body my={9} style={{ fontWeight: 400 }}>
        This tool will remove your V1 liquidity and send the underlying assets to your wallet.
      </TYPE.body>

      <LightCard>
        <V1LiquidityInfo
          token={token}
          liquidityTokenAmount={liquidityTokenAmount}
          tokenWorth={tokenWorth}
          ethWorth={ethWorth}
        />

        <div style={{ display: 'flex', marginTop: '1rem' }}>
          <ButtonConfirmed
            confirmed={isSuccessfullyRemoved}
            disabled={isSuccessfullyRemoved || noLiquidityTokens || isRemovalPending || confirmingRemoval}
            onClick={remove}
          >
            {isSuccessfullyRemoved ? 'Success' : isRemovalPending ? <Dots>Removing</Dots> : 'Remove'}
          </ButtonConfirmed>
        </div>
      </LightCard>
      <TYPE.darkGray style={{ textAlign: 'center' }}>
        {`Your Uniswap V1 ${
          chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol
        }/ETH liquidity will be redeemed for underlying assets.`}
      </TYPE.darkGray>
    </AutoColumn>
  )
}
Example #28
Source File: MigrateV1Exchange.tsx    From dyp with Do What The F*ck You Want To Public License 4 votes vote down vote up
function V1PairMigration({ liquidityTokenAmount, token }: { liquidityTokenAmount: TokenAmount; token: Token }) {
  const { account, chainId } = useActiveWeb3React()
  const totalSupply = useTotalSupply(liquidityTokenAmount.token)
  const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
  const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)

  const [v2PairState, v2Pair] = usePair(chainId ? WETH[chainId] : undefined, token)
  const isFirstLiquidityProvider: boolean = v2PairState === PairState.NOT_EXISTS

  const v2SpotPrice = chainId && v2Pair ? v2Pair.reserveOf(token).divide(v2Pair.reserveOf(WETH[chainId])) : undefined

  const [confirmingMigration, setConfirmingMigration] = useState<boolean>(false)
  const [pendingMigrationHash, setPendingMigrationHash] = useState<string | null>(null)

  const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION

  const ethWorth: CurrencyAmount = exchangeETHBalance
    ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient)
    : CurrencyAmount.ether(ZERO)

  const tokenWorth: TokenAmount = exchangeTokenBalance
    ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
    : new TokenAmount(token, ZERO)

  const [approval, approve] = useApproveCallback(liquidityTokenAmount, MIGRATOR_ADDRESS)

  const v1SpotPrice =
    exchangeTokenBalance && exchangeETHBalance
      ? exchangeTokenBalance.divide(new Fraction(exchangeETHBalance.raw, WEI_DENOM))
      : null

  const priceDifferenceFraction: Fraction | undefined =
    v1SpotPrice && v2SpotPrice
      ? v1SpotPrice
          .divide(v2SpotPrice)
          .multiply('100')
          .subtract('100')
      : undefined

  const priceDifferenceAbs: Fraction | undefined = priceDifferenceFraction?.lessThan(ZERO)
    ? priceDifferenceFraction?.multiply('-1')
    : priceDifferenceFraction

  const minAmountETH: JSBI | undefined =
    v2SpotPrice && tokenWorth
      ? tokenWorth
          .divide(v2SpotPrice)
          .multiply(WEI_DENOM)
          .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
      : ethWorth?.numerator

  const minAmountToken: JSBI | undefined =
    v2SpotPrice && ethWorth
      ? ethWorth
          .multiply(v2SpotPrice)
          .multiply(JSBI.exponentiate(JSBI.BigInt(10), JSBI.BigInt(token.decimals)))
          .multiply(ALLOWED_OUTPUT_MIN_PERCENT).quotient
      : tokenWorth?.numerator

  const addTransaction = useTransactionAdder()
  const isMigrationPending = useIsTransactionPending(pendingMigrationHash ?? undefined)

  const migrator = useV2MigratorContract()
  const migrate = useCallback(() => {
    if (!minAmountToken || !minAmountETH || !migrator) return

    setConfirmingMigration(true)
    migrator
      .migrate(
        token.address,
        minAmountToken.toString(),
        minAmountETH.toString(),
        account,
        Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW
      )
      .then((response: TransactionResponse) => {
        ReactGA.event({
          category: 'Migrate',
          action: 'V1->V2',
          label: token?.symbol
        })

        addTransaction(response, {
          summary: `Migrate ${token.symbol} liquidity to V2`
        })
        setPendingMigrationHash(response.hash)
      })
      .catch(() => {
        setConfirmingMigration(false)
      })
  }, [minAmountToken, minAmountETH, migrator, token, account, addTransaction])

  const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)

  const largePriceDifference = !!priceDifferenceAbs && !priceDifferenceAbs.lessThan(JSBI.BigInt(5))

  const isSuccessfullyMigrated = !!pendingMigrationHash && noLiquidityTokens

  return (
    <AutoColumn gap="20px">
      <TYPE.body my={9} style={{ fontWeight: 400 }}>
        This tool will safely migrate your V1 liquidity to V2 with minimal price risk. The process is completely
        trustless thanks to the{' '}
        {chainId && (
          <ExternalLink href={getEtherscanLink(chainId, MIGRATOR_ADDRESS, 'address')}>
            <TYPE.blue display="inline">Uniswap migration contract↗</TYPE.blue>
          </ExternalLink>
        )}
        .
      </TYPE.body>

      {!isFirstLiquidityProvider && largePriceDifference ? (
        <YellowCard>
          <TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
            It{"'"}s best to deposit liquidity into Uniswap V2 at a price you believe is correct. If the V2 price seems
            incorrect, you can either make a swap to move the price or wait for someone else to do so.
          </TYPE.body>
          <AutoColumn gap="8px">
            <RowBetween>
              <TYPE.body>V1 Price:</TYPE.body>
              <TYPE.black>
                {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </TYPE.black>
            </RowBetween>
            <RowBetween>
              <div />
              <TYPE.black>
                {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </TYPE.black>
            </RowBetween>

            <RowBetween>
              <TYPE.body>V2 Price:</TYPE.body>
              <TYPE.black>
                {v2SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </TYPE.black>
            </RowBetween>
            <RowBetween>
              <div />
              <TYPE.black>
                {v2SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </TYPE.black>
            </RowBetween>

            <RowBetween>
              <TYPE.body color="inherit">Price Difference:</TYPE.body>
              <TYPE.black color="inherit">{priceDifferenceAbs?.toSignificant(4)}%</TYPE.black>
            </RowBetween>
          </AutoColumn>
        </YellowCard>
      ) : null}

      {isFirstLiquidityProvider && (
        <PinkCard>
          <TYPE.body style={{ marginBottom: 8, fontWeight: 400 }}>
            You are the first liquidity provider for this pair on Uniswap V2. Your liquidity will be migrated at the
            current V1 price. Your transaction cost also includes the gas to create the pool.
          </TYPE.body>

          <AutoColumn gap="8px">
            <RowBetween>
              <TYPE.body>V1 Price:</TYPE.body>
              <TYPE.black>
                {v1SpotPrice?.toSignificant(6)} {token.symbol}/ETH
              </TYPE.black>
            </RowBetween>
            <RowBetween>
              <div />
              <TYPE.black>
                {v1SpotPrice?.invert()?.toSignificant(6)} ETH/{token.symbol}
              </TYPE.black>
            </RowBetween>
          </AutoColumn>
        </PinkCard>
      )}

      <LightCard>
        <V1LiquidityInfo
          token={token}
          liquidityTokenAmount={liquidityTokenAmount}
          tokenWorth={tokenWorth}
          ethWorth={ethWorth}
        />

        <div style={{ display: 'flex', marginTop: '1rem' }}>
          <AutoColumn gap="12px" style={{ flex: '1', marginRight: 12 }}>
            <ButtonConfirmed
              confirmed={approval === ApprovalState.APPROVED}
              disabled={approval !== ApprovalState.NOT_APPROVED}
              onClick={approve}
            >
              {approval === ApprovalState.PENDING ? (
                <Dots>Approving</Dots>
              ) : approval === ApprovalState.APPROVED ? (
                'Approved'
              ) : (
                'Approve'
              )}
            </ButtonConfirmed>
          </AutoColumn>
          <AutoColumn gap="12px" style={{ flex: '1' }}>
            <ButtonConfirmed
              confirmed={isSuccessfullyMigrated}
              disabled={
                isSuccessfullyMigrated ||
                noLiquidityTokens ||
                isMigrationPending ||
                approval !== ApprovalState.APPROVED ||
                confirmingMigration
              }
              onClick={migrate}
            >
              {isSuccessfullyMigrated ? 'Success' : isMigrationPending ? <Dots>Migrating</Dots> : 'Migrate'}
            </ButtonConfirmed>
          </AutoColumn>
        </div>
      </LightCard>
      <TYPE.darkGray style={{ textAlign: 'center' }}>
        {`Your Uniswap V1 ${token.symbol}/ETH liquidity will become Uniswap V2 ${token.symbol}/ETH liquidity.`}
      </TYPE.darkGray>
    </AutoColumn>
  )
}
Example #29
Source File: RemoveV1Exchange.tsx    From cheeseswap-interface with GNU General Public License v3.0 4 votes vote down vote up
function V1PairRemoval({
  exchangeContract,
  liquidityTokenAmount,
  token
}: {
  exchangeContract: Contract
  liquidityTokenAmount: TokenAmount
  token: Token
}) {
  const { chainId } = useActiveWeb3React()
  const totalSupply = useTotalSupply(liquidityTokenAmount.token)
  const exchangeETHBalance = useETHBalances([liquidityTokenAmount.token.address])?.[liquidityTokenAmount.token.address]
  const exchangeTokenBalance = useTokenBalance(liquidityTokenAmount.token.address, token)

  const [confirmingRemoval, setConfirmingRemoval] = useState<boolean>(false)
  const [pendingRemovalHash, setPendingRemovalHash] = useState<string | null>(null)

  const shareFraction: Fraction = totalSupply ? new Percent(liquidityTokenAmount.raw, totalSupply.raw) : ZERO_FRACTION

  const ethWorth: CurrencyAmount = exchangeETHBalance
    ? CurrencyAmount.ether(exchangeETHBalance.multiply(shareFraction).multiply(WEI_DENOM).quotient)
    : CurrencyAmount.ether(ZERO)

  const tokenWorth: TokenAmount = exchangeTokenBalance
    ? new TokenAmount(token, shareFraction.multiply(exchangeTokenBalance.raw).quotient)
    : new TokenAmount(token, ZERO)

  const addTransaction = useTransactionAdder()
  const isRemovalPending = useIsTransactionPending(pendingRemovalHash ?? undefined)

  const remove = useCallback(() => {
    if (!liquidityTokenAmount) return

    setConfirmingRemoval(true)
    exchangeContract
      .removeLiquidity(
        liquidityTokenAmount.raw.toString(),
        1, // min_eth, this is safe because we're removing liquidity
        1, // min_tokens, this is safe because we're removing liquidity
        Math.floor(new Date().getTime() / 1000) + DEFAULT_DEADLINE_FROM_NOW
      )
      .then((response: TransactionResponse) => {
        ReactGA.event({
          category: 'Remove',
          action: 'V1',
          label: token?.symbol
        })

        addTransaction(response, {
          summary: `Remove ${chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol}/ETH V1 liquidity`
        })
        setPendingRemovalHash(response.hash)
      })
      .catch((error: Error) => {
        console.error(error)
        setConfirmingRemoval(false)
      })
  }, [exchangeContract, liquidityTokenAmount, token, chainId, addTransaction])

  const noLiquidityTokens = !!liquidityTokenAmount && liquidityTokenAmount.equalTo(ZERO)

  const isSuccessfullyRemoved = !!pendingRemovalHash && noLiquidityTokens

  return (
    <AutoColumn gap="20px">
      <TYPE.body my={9} style={{ fontWeight: 600 }}>
        This tool will remove your V1 liquidity and send the underlying assets to your wallet.
      </TYPE.body>

      <LightCard>
        <V1LiquidityInfo
          token={token}
          liquidityTokenAmount={liquidityTokenAmount}
          tokenWorth={tokenWorth}
          ethWorth={ethWorth}
        />

        <div style={{ display: 'flex', marginTop: '1rem' }}>
          <ButtonConfirmed
            confirmed={isSuccessfullyRemoved}
            disabled={isSuccessfullyRemoved || noLiquidityTokens || isRemovalPending || confirmingRemoval}
            onClick={remove}
          >
            {isSuccessfullyRemoved ? 'Success' : isRemovalPending ? <Dots>Removing</Dots> : 'Remove'}
          </ButtonConfirmed>
        </div>
      </LightCard>
      <TYPE.darkGray style={{ textAlign: 'center' }}>
        {`Your Uniswap V1 ${
          chainId && token.equals(WETH[chainId]) ? 'WETH' : token.symbol
        }/ETH liquidity will be redeemed for underlying assets.`}
      </TYPE.darkGray>
    </AutoColumn>
  )
}