Skip to content

Commit

Permalink
Merge pull request #1341 from madfish-solutions/QUIPU-831-implement-3…
Browse files Browse the repository at this point in the history
…-route-for-swaps

Implement swaps with 3route
  • Loading branch information
keshan3262 authored Feb 23, 2023
2 parents fcc5893 + f8315e6 commit 2594447
Show file tree
Hide file tree
Showing 53 changed files with 1,110 additions and 277 deletions.
3 changes: 3 additions & 0 deletions .env.mainnet.prod
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ REACT_APP_POOLS_URL=wss://dexes-api-mainnet.prod.templewallet.com/
REACT_APP_FARMING_API_URL=https://staking-api-mainnet.prod.quipuswap.com
REACT_APP_STABLESWAP_API_URL=https://stableswap-api-mainnet.prod.quipuswap.com
REACT_APP_LIQUIDITY_API_URL=https://liquidity-api-mainnet.prod.quipuswap.com
REACT_APP_THREE_ROUTE_API_URL=https://quipuswap.3route.io
REACT_APP_THREE_ROUTE_API_AUTH_TOKEN=UXVpcHVzd2FwOhXazKpW5//4RC+Oa9TdpQrmLOwl284os85upVGa65uA
REACT_APP_THREE_ROUTE_CONTRACT_ADDRESS=KT1Tuta6vbpHhZ15ixsYD3qJdhnpEAuogLQ9

#sentry
REACT_APP_SENTRY_DSN=https://[email protected]/6676545
Expand Down
3 changes: 3 additions & 0 deletions .env.mainnet.stage
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ REACT_APP_POOLS_URL=wss://dexes-api-mainnet.stage.madfish.xyz/
REACT_APP_FARMING_API_URL=https://staking-api-mainnet.stage.madfish.xyz
REACT_APP_STABLESWAP_API_URL=https://stableswap-api-mainnet.stage.madfish.xyz
REACT_APP_LIQUIDITY_API_URL=https://liquidity-api-mainnet.stage.madfish.xyz
REACT_APP_THREE_ROUTE_API_URL=https://quipuswap.3route.io
REACT_APP_THREE_ROUTE_API_AUTH_TOKEN=UXVpcHVzd2FwOhXazKpW5//4RC+Oa9TdpQrmLOwl284os85upVGa65uA
REACT_APP_THREE_ROUTE_CONTRACT_ADDRESS=KT1Tuta6vbpHhZ15ixsYD3qJdhnpEAuogLQ9

#TZKT
REACT_APP_TZKT_API=https://api.ghostnet.tzkt.io/v1
3 changes: 3 additions & 0 deletions src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,6 @@ export const HOT_POOLS: Array<{ id: string; type: PoolType }> = [
// Coinflip
export const COINFLIP_CONTRACT_DECIMALS = 18;
export const COINFLIP_TOKEN_DECIMALS = 6;

// 3route
export const THREE_ROUTE_APP_ID = 3;
3 changes: 3 additions & 0 deletions src/config/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ export const USD_DECIMALS = 2;

export const MAX_ITEMS_PER_PAGE = 5;
export const MAX_HOPS_COUNT = 3;
export const PRICE_IMPACT_WARNING_THRESHOLD = 10;

export const DATA_TEST_ID_PROP_NAME = 'data-test-id';

export const NO_WITHDRAWAL_FEE_VALUE = 0;

Expand Down
3 changes: 3 additions & 0 deletions src/config/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ export const STABLESWAP_FACTORY_CONTRACT_ADDRESS = process.env.REACT_APP_STABLES
export const TEMPLEWALLET_API_URL = process.env.REACT_APP_TEMPLEWALLET_API_URL!;
export const METADATA_API = process.env.REACT_APP_METADATA_API_URL!;
export const DEX_POOL_URL = process.env.REACT_APP_POOLS_URL!;
export const THREE_ROUTE_API_URL = process.env.REACT_APP_THREE_ROUTE_API_URL;
export const THREE_ROUTE_API_AUTH_TOKEN = process.env.REACT_APP_THREE_ROUTE_API_AUTH_TOKEN;
export const THREE_ROUTE_CONTRACT_ADDRESS = process.env.REACT_APP_THREE_ROUTE_CONTRACT_ADDRESS;

export const FARMING_API_URL = process.env.REACT_APP_FARMING_API_URL!;
export const STABLESWAP_API_URL = process.env.REACT_APP_STABLESWAP_API_URL!;
Expand Down
1 change: 1 addition & 0 deletions src/modules/swap/api/backend/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './three-route';
53 changes: 53 additions & 0 deletions src/modules/swap/api/backend/three-route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import BigNumber from 'bignumber.js';

import { THREE_ROUTE_API_AUTH_TOKEN, THREE_ROUTE_API_URL } from '@config/environment';

import { ThreeRouteDex, ThreeRouteSwapResponse, ThreeRouteToken } from '../../types';

export class ThreeRouteBackendApi {
private static NUMBER_DISCRIMINATION_PREFIX = 'uniqueprefix';

private static jsonWithBigNumberParser(origJSON: string): ReturnType<typeof JSON['parse']> {
const stringedJSON = origJSON.replace(
/:\s*([-+Ee0-9.]+)/g,
`: "${ThreeRouteBackendApi.NUMBER_DISCRIMINATION_PREFIX}$1"`
);

return JSON.parse(stringedJSON, (_, value) => {
if (typeof value !== 'string' || !value.startsWith(ThreeRouteBackendApi.NUMBER_DISCRIMINATION_PREFIX)) {
return value;
}

value = value.slice('uniqueprefix'.length);

return new BigNumber(value);
});
}

private static async getThreeRouteResponse(path: string) {
const response = await fetch(`${THREE_ROUTE_API_URL}${path}`, {
headers: { Authorization: `Basic ${THREE_ROUTE_API_AUTH_TOKEN}` }
});
const rawJSON = await response.text();

return ThreeRouteBackendApi.jsonWithBigNumberParser(rawJSON);
}

static async getSwap(
inputTokenSymbol: string,
outputTokenSymbol: string,
realAmount: BigNumber
): Promise<ThreeRouteSwapResponse> {
return await ThreeRouteBackendApi.getThreeRouteResponse(
`/swap/${inputTokenSymbol}/${outputTokenSymbol}/${realAmount.toFixed()}`
);
}

static async getTokens(): Promise<ThreeRouteToken[]> {
return await ThreeRouteBackendApi.getThreeRouteResponse('/tokens');
}

static async getDexes(): Promise<ThreeRouteDex[]> {
return await ThreeRouteBackendApi.getThreeRouteResponse('/dexes');
}
}
2 changes: 2 additions & 0 deletions src/modules/swap/api/blockchain/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './no-mediators-swap';
export * from './three-route';
63 changes: 63 additions & 0 deletions src/modules/swap/api/blockchain/no-mediators-swap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { TezosToolkit } from '@taquito/taquito';
import { BigNumber } from 'bignumber.js';
import { getTradeOpParams, parseTransferParamsToParamsWithKind, Trade } from 'swap-router-sdk';

import { STABLESWAP_REFERRAL } from '@config/config';
import { QUIPUSWAP_REFERRAL_CODE } from '@config/constants';
import { estimateFee } from '@shared/api';

export class NoMediatorsSwapBlockchainApi {
static async getSwapTransferParams(
tezos: TezosToolkit,
accountPkh: string,
trade: Trade,
recipientPkh = accountPkh,
deadlineTimespan?: BigNumber
) {
return await getTradeOpParams(
trade,
accountPkh,
tezos,
STABLESWAP_REFERRAL,
recipientPkh,
deadlineTimespan?.toNumber(),
QUIPUSWAP_REFERRAL_CODE.toNumber()
);
}

static async estimateSwapFee(
tezos: TezosToolkit,
accountPkh: string,
trade: Trade,
recipientPkh = accountPkh,
deadlineTimespan?: BigNumber
) {
return await estimateFee(
tezos,
accountPkh,
await NoMediatorsSwapBlockchainApi.getSwapTransferParams(tezos, accountPkh, trade, recipientPkh, deadlineTimespan)
);
}

static async doSwap(
tezos: TezosToolkit,
accountPkh: string,
trade: Trade,
recipientPkh = accountPkh,
deadlineTimespan?: BigNumber
) {
const tradeTransferParams = await NoMediatorsSwapBlockchainApi.getSwapTransferParams(
tezos,
accountPkh,
trade,
recipientPkh,
deadlineTimespan
);

const walletParamsWithKind = tradeTransferParams.map(tradeTransferParam =>
parseTransferParamsToParamsWithKind(tradeTransferParam)
);

return await tezos.wallet.batch(walletParamsWithKind).send();
}
}
114 changes: 114 additions & 0 deletions src/modules/swap/api/blockchain/three-route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { TezosToolkit } from '@taquito/taquito';
import { BigNumber } from 'bignumber.js';
import { parseTransferParamsToParamsWithKind } from 'swap-router-sdk';

import { getApproveParams } from '@blockchain';
import { THREE_ROUTE_APP_ID } from '@config/config';
import { FIRST_INDEX, ZERO_AMOUNT } from '@config/constants';
import { THREE_ROUTE_CONTRACT_ADDRESS } from '@config/environment';
import { threeRouteTokenMatches } from '@modules/swap/helpers';
import { ThreeRouteSwapResponse, ThreeRouteToken } from '@modules/swap/types';
import { estimateFee } from '@shared/api';
import { getContract } from '@shared/dapp/get-storage-info';
import { decreaseByPercentage, toAtomic, isTezosToken } from '@shared/helpers';
import { Token } from '@shared/types';

export class ThreeRouteBlockchainApi {
static async getSwapTransferParams(
tezos: TezosToolkit,
accountPkh: string,
receiver: string,
inputToken: Token,
outputToken: Token,
threeRouteTokens: ThreeRouteToken[],
swap: ThreeRouteSwapResponse,
slippageTolerance: BigNumber
) {
const threeRouteContract = await getContract(tezos, THREE_ROUTE_CONTRACT_ADDRESS!);
const [threeRouteInputToken, threeRouteOutputToken] = [inputToken, outputToken].map(token =>
threeRouteTokens.find(threeRouteToken => threeRouteTokenMatches(threeRouteToken, token))
);
const atomicInputAmount = toAtomic(swap.input, inputToken);

const hops = swap.chains
.map(chain =>
chain.hops.map(({ dex: dex_id, forward }, index) => ({
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
code: (index === FIRST_INDEX ? 1 : 0) + (forward ? 2 : 0),
dex_id,
amount_opt: index === FIRST_INDEX ? toAtomic(chain.input, inputToken) : null
}))
)
.flat();
const tezAmount = isTezosToken(inputToken) ? swap.input.toNumber() : ZERO_AMOUNT;

return getApproveParams(tezos, THREE_ROUTE_CONTRACT_ADDRESS!, inputToken, accountPkh, atomicInputAmount, [
threeRouteContract.methods
.execute(
threeRouteInputToken!.id,
threeRouteOutputToken!.id,
toAtomic(decreaseByPercentage(swap.output, slippageTolerance), outputToken).integerValue(
BigNumber.ROUND_DOWN
),
receiver,
hops,
THREE_ROUTE_APP_ID
)
.toTransferParams({ amount: tezAmount, mutez: false })
]);
}

static async estimateSwapFee(
tezos: TezosToolkit,
accountPkh: string,
receiver: string,
inputToken: Token,
outputToken: Token,
threeRouteTokens: ThreeRouteToken[],
swap: ThreeRouteSwapResponse,
slippageTolerance: BigNumber
) {
return await estimateFee(
tezos,
accountPkh,
await ThreeRouteBlockchainApi.getSwapTransferParams(
tezos,
accountPkh,
receiver,
inputToken,
outputToken,
threeRouteTokens,
swap,
slippageTolerance
)
);
}

static async doSwap(
tezos: TezosToolkit,
accountPkh: string,
receiver: string,
inputToken: Token,
outputToken: Token,
threeRouteTokens: ThreeRouteToken[],
swap: ThreeRouteSwapResponse,
slippageTolerance: BigNumber
) {
const tradeTransferParams = await ThreeRouteBlockchainApi.getSwapTransferParams(
tezos,
accountPkh,
receiver,
inputToken,
outputToken,
threeRouteTokens,
swap,
slippageTolerance
);

const walletParamsWithKind = tradeTransferParams.map(tradeTransferParam =>
parseTransferParamsToParamsWithKind(tradeTransferParam)
);

return await tezos.wallet.batch(walletParamsWithKind).send();
}
}
2 changes: 2 additions & 0 deletions src/modules/swap/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './backend';
export * from './blockchain';
10 changes: 4 additions & 6 deletions src/modules/swap/components/swap-details/swap-details.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,25 @@
import { FC } from 'react';

import BigNumber from 'bignumber.js';
import cx from 'classnames';

import { HIDE_ANALYTICS } from '@config/config';
import { QUIPU_TOKEN, TEZOS_TOKEN } from '@config/tokens';
import { eQuipuSwapVideo } from '@config/youtube';
import {
Card,
DashPlug,
DetailsCardCell,
RateView,
StateCurrencyAmount,
StatePriceImpact,
Tabs,
YouTube
} from '@shared/components';
import { isEmptyArray } from '@shared/helpers';
import { useYoutubeTabs } from '@shared/hooks';
import { Nullable, Token, Undefined } from '@shared/types';
import styles from '@styles/CommonContainer.module.scss';
import { useTranslation } from '@translation';

import { DexPool } from '../../types';
import { Route } from '../route';
import { ViewPairAnlytics } from '../view-pair-analytics';

interface SwapDetailsProps {
Expand Down Expand Up @@ -80,6 +76,7 @@ export const SwapDetails: FC<SwapDetailsProps> = ({
<RateView rate={sellRate} inputToken={inputTokenWithFallback} outputToken={outputTokenWithFallback} />
</DetailsCardCell>

{/* TODO: restore after optimal calculation of buy price is implemented */}
{/* <DetailsCardCell
cellName={t('common|Buy Price')}
tooltipContent={t(
Expand Down Expand Up @@ -109,7 +106,8 @@ export const SwapDetails: FC<SwapDetailsProps> = ({
<StateCurrencyAmount isError={Boolean(feeError)} amount={fee} currency="TEZ" />
</DetailsCardCell>

<DetailsCardCell
{/* TODO: restore after UI for multiple routes is defined */}
{/* <DetailsCardCell
cellName={t('common|Route')}
tooltipContent={t(
"swap|When a direct swap is impossible (no liquidity pool for the pair exists yet) QuipuSwap's algorithm will conduct the swap in several transactions, picking the most beneficial chain of trades."
Expand All @@ -118,7 +116,7 @@ export const SwapDetails: FC<SwapDetailsProps> = ({
data-test-id="route"
>
{isEmptyArray(route ?? null) ? <DashPlug animation={!route} /> : <Route route={route!} />}
</DetailsCardCell>
</DetailsCardCell> */}

{!HIDE_ANALYTICS && (
<ViewPairAnlytics
Expand Down
5 changes: 5 additions & 0 deletions src/modules/swap/dto/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export * from './three-route-swap-chain.dto';
export * from './three-route-hop.dto';
export * from './three-route-swap-response.dto';
export * from './three-route-token.dto';
export * from './three-route-tokens-response.dto';
11 changes: 11 additions & 0 deletions src/modules/swap/dto/three-route-hop.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import BigNumber from 'bignumber.js';

import { Typed } from '@shared/decorators';

export class ThreeRouteHopDto {
@Typed()
dex: BigNumber;

@Typed()
forward: boolean;
}
16 changes: 16 additions & 0 deletions src/modules/swap/dto/three-route-swap-chain.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import BigNumber from 'bignumber.js';

import { Typed } from '@shared/decorators';

import { ThreeRouteHopDto } from './three-route-hop.dto';

export class ThreeRouteSwapChainDto {
@Typed()
input: BigNumber;

@Typed()
output: BigNumber;

@Typed({ type: ThreeRouteHopDto, isArray: true })
hops: ThreeRouteHopDto[];
}
16 changes: 16 additions & 0 deletions src/modules/swap/dto/three-route-swap-response.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import BigNumber from 'bignumber.js';

import { Typed } from '@shared/decorators';

import { ThreeRouteSwapChainDto } from './three-route-swap-chain.dto';

export class ThreeRouteSwapResponseDto {
@Typed()
input: BigNumber;

@Typed()
output: BigNumber;

@Typed({ type: ThreeRouteSwapChainDto, isArray: true })
chains: ThreeRouteSwapChainDto[];
}
Loading

0 comments on commit 2594447

Please sign in to comment.