Skip to content

Commit

Permalink
fix: insufficient liquidity checks (#190)
Browse files Browse the repository at this point in the history
* Revert "Add Insufficient Liquidity warning when tokenIn total cannot be gathered"

This reverts commit 3dd5f92.

* Add extra insufficientLiquidityOut check:

    - calculateOut may have an amountOut cap of maxOut

* Show general insufficient liquidity warning on swap:

    - occurs when there are no ticks on the requested pair

* Abstract out SwapError type
  • Loading branch information
dib542 authored Nov 26, 2022
1 parent b36e0f4 commit 0f4682d
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 43 deletions.
4 changes: 2 additions & 2 deletions src/pages/Swap/Swap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ export default function Swap() {
{address ? (
hasFormData &&
hasSufficientFunds &&
!error?.insufficientLiquidity &&
!error?.insufficientLiquidityIn &&
!error?.insufficientLiquidityOut ? (
<button
Expand All @@ -312,8 +313,7 @@ export default function Swap() {
>
{orderType === 'limit' ? 'Place Limit Order' : 'Swap'}
</button>
) : error?.insufficientLiquidityIn ||
error?.insufficientLiquidityOut ? (
) : error?.insufficientLiquidity ? (
<button className="submit-button button-error" type="button">
Insufficient liquidity
</button>
Expand Down
43 changes: 23 additions & 20 deletions src/pages/Swap/hooks/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,36 @@ import {
import { RouterResult } from './index';
import { BigNumber } from 'bignumber.js';

export type SwapError = Error & {
insufficientLiquidity?: boolean;
insufficientLiquidityIn?: boolean;
insufficientLiquidityOut?: boolean;
};

// mock implementation of router (no hop)
export function router(
state: PairMap,
tokenA: string,
tokenB: string,
value0: string
): RouterResult {
let error: SwapError | false = false;

// find pair by searching both directions in the current state
// the pairs are sorted by the backend not here
const reverse = state[getPairID(tokenA, tokenB)];
const forward = state[getPairID(tokenB, tokenA)];
const exactPair = forward || reverse;
if (!exactPair) {
throw new Error('There are no ticks for the supplied token pair');
error = new Error('There are no ticks for the supplied token pair');
error.insufficientLiquidity = true;
throw error;
} else {
const sortedTicks = forward
? exactPair.poolsZeroToOne
: exactPair.poolsOneToZero;
const maxIn = sortedTicks.reduce((result, tick) => {
return result.plus(forward ? tick.reserve0 : tick.reserve1);
}, new BigNumber(0));
const amountIn = new BigNumber(value0);

let error:
| (Error & {
insufficientLiquidityIn?: boolean;
insufficientLiquidityOut?: boolean;
})
| false = false;

if (amountIn.isGreaterThan(maxIn)) {
error = new Error('Not enough tick liquidity found to match trade');
error.insufficientLiquidityIn = true;
}

try {
const amountOut = calculateOut({
tokenIn: tokenA,
Expand All @@ -56,6 +51,7 @@ export function router(
if (!error) {
error = new Error('Not enough tick liquidity found to match trade');
}
error.insufficientLiquidity = true;
error.insufficientLiquidityOut = true;
}

Expand Down Expand Up @@ -128,10 +124,10 @@ export function calculateOut({
amountOut = amountOut.plus(reservesOut);
if (amountLeft.isEqualTo(0)) return amountOut;
if (amountLeft.isLessThan(0)) {
const error: Error & {
insufficientLiquidityIn?: boolean;
insufficientLiquidityOut?: boolean;
} = new Error('Error while calculating amount out (negative amount)');
const error: SwapError = new Error(
'Error while calculating amount out (negative amount)'
);
error.insufficientLiquidity = true;
error.insufficientLiquidityIn = true;
throw error;
}
Expand All @@ -140,6 +136,13 @@ export function calculateOut({
}
}
}
// if there is still tokens left to be traded the liquidity must have been exhausted
if (amountLeft.isGreaterThan(0)) {
const error: SwapError = new Error('Could not swap all tokens given');
error.insufficientLiquidity = true;
error.insufficientLiquidityOut = true;
throw error;
}
return amountOut;
}

Expand Down
26 changes: 5 additions & 21 deletions src/pages/Swap/hooks/useRouter.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { useIndexerData, PairMap } from '../../../lib/web3/indexerProvider';
import { useEffect, useState } from 'react';
import { PairRequest, PairResult, RouterResult } from './index';
import { routerAsync, calculateFee } from './router';
import { routerAsync, calculateFee, SwapError } from './router';
import BigNumber from 'bignumber.js';

const cachedRequests: {
Expand Down Expand Up @@ -31,19 +31,11 @@ async function getRouterResult(
export function useRouterResult(pairRequest: PairRequest): {
data?: RouterResult;
isValidating: boolean;
error?: Error & {
insufficientLiquidityIn?: boolean;
insufficientLiquidityOut?: boolean;
};
error?: SwapError;
} {
const [data, setData] = useState<RouterResult>();
const [isValidating, setIsValidating] = useState(false);
const [error, setError] = useState<
Error & {
insufficientLiquidityIn?: boolean;
insufficientLiquidityOut?: boolean;
}
>();
const [error, setError] = useState<SwapError>();
const { data: pairs } = useIndexerData();

useEffect(() => {
Expand Down Expand Up @@ -89,12 +81,7 @@ export function useRouterResult(pairRequest: PairRequest): {
setIsValidating(false);
setData(result);
})
.catch(function (
err: Error & {
insufficientLiquidityIn?: boolean;
insufficientLiquidityOut?: boolean;
}
) {
.catch(function (err: SwapError) {
if (cancelled) return;
setIsValidating(false);
setError(err);
Expand Down Expand Up @@ -185,10 +172,7 @@ export function getRouterEstimates(
export function useRouterEstimates(pairRequest: PairRequest): {
data?: PairResult;
isValidating: boolean;
error?: Error & {
insufficientLiquidityIn?: boolean;
insufficientLiquidityOut?: boolean;
};
error?: SwapError;
} {
const { data, error, isValidating } = useRouterResult(pairRequest);
return { data: getRouterEstimates(pairRequest, data), isValidating, error };
Expand Down

0 comments on commit 0f4682d

Please sign in to comment.