Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add gmx on-chain provider #719

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { PiteasProvider } from 'src/features/on-chain/calculation-manager/provid
import { XyDexProvider } from 'src/features/on-chain/calculation-manager/providers/aggregators/xy-dex/xy-dex-provider';
import { ZrxProvider } from 'src/features/on-chain/calculation-manager/providers/aggregators/zrx/zrx-provider';

import { GMXOnChainProvider } from '../providers/aggregators/gmx/gmx-on-chain-provider';
import { NativeRouterProvider } from '../providers/aggregators/native-router/native-router-provider';
import { OdosOnChainProvider } from '../providers/aggregators/odos/odos-on-chain-provider';
import { OkuSwapOnChainProvider } from '../providers/aggregators/okuswap/okuswap-on-chain-provider';
Expand All @@ -26,6 +27,7 @@ export const AGGREGATORS_ON_CHAIN = {
ONE_INCH: OneInchProvider,
ZETA_SWAP: ZetaSwapProvider,
NATIVE_ROUTER: NativeRouterProvider,
SQUIDROUTER: SquidRouterOnChainProvider
SQUIDROUTER: SquidRouterOnChainProvider,
GMX: GMXOnChainProvider
// SYMBIOSIS: SymbiosisOnChainProvider
} as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Injector } from 'src/core/injector/injector';

type HttpParam = {
[param: string]: string | number | boolean | readonly (string | number | boolean)[];
};

export class OpenOceanApiService {
public static getQuote<T extends HttpParam, D>(
params: T,
url: string,
apiKey?: string
): Promise<D> {
return Injector.httpClient.get<D>(url, {
params: {
...params
},
headers: {
...(apiKey && { apiKey: apiKey })
}
});
}

public static getSupportedTokenList<T>(url: string, apiKey?: string): Promise<T> {
return Injector.httpClient.get<T>(url, {
headers: {
...(apiKey && { apiKey: apiKey })
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { PriceToken, PriceTokenAmount } from 'src/common/tokens';
import { nativeTokensList } from 'src/common/tokens/constants/native-tokens';
import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure';

import { ON_CHAIN_TRADE_TYPE } from '../../common/models/on-chain-trade-type';
import { GasFeeInfo } from '../../common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info';
import { OpenOceanApiService } from '../common/open-ocean/open-ocean-api-service';
import { X_API_KEY } from '../open-ocean/constants/api-key';
import { ARBITRUM_GAS_PRICE } from '../open-ocean/constants/arbitrum-gas-price';
import { openOceanApiUrl } from '../open-ocean/constants/get-open-ocean-api-url';
import { openOceanBlockchainName } from '../open-ocean/constants/open-ocean-blockchain';
import { OpenoceanOnChainSupportedBlockchain } from '../open-ocean/constants/open-ocean-on-chain-supported-blockchain';
import { OpenOceanQuoteResponse } from '../open-ocean/models/open-ocean-quote-response';
import { OpenOceanTradeStruct } from '../open-ocean/models/open-ocean-trade-struct';
import { OpenOceanProvider } from '../open-ocean/open-ocean-provider';
import { GMXOnChainTrade } from './gmx-on-chain-trade';
import { GMXQuoteRequest } from './models/gmx-quote-request';

export class GMXOnChainProvider extends OpenOceanProvider {
public override readonly tradeType = ON_CHAIN_TRADE_TYPE.GMX;

protected createTradeInstance(
openOceanTradeStruct: OpenOceanTradeStruct,
gasFeeInfo: GasFeeInfo | null,
providerAddress: string
): GMXOnChainTrade {
return new GMXOnChainTrade(
{
...openOceanTradeStruct,
gasFeeInfo
},
providerAddress
);
}

protected override async getQuote(
from: PriceTokenAmount,
toToken: PriceToken,
_slippage: number,
isArbitrum: boolean,
gasPrice: string
): Promise<OpenOceanQuoteResponse> {
const blockchain = from.blockchain as OpenoceanOnChainSupportedBlockchain;
const apiUrl = openOceanApiUrl.gmxQuote(openOceanBlockchainName[blockchain]);

const quoteRequestParams: GMXQuoteRequest = {
inTokenAddress: this.getTokenAddress(from),
outTokenAddress: this.getTokenAddress(toToken),
amount: from.tokenAmount.toString(),
gasPrice: isArbitrum
? ARBITRUM_GAS_PRICE
: Web3Pure.fromWei(gasPrice, nativeTokensList[from.blockchain].decimals)
.multipliedBy(10 ** 9)
.toString()
};

return OpenOceanApiService.getQuote<GMXQuoteRequest, OpenOceanQuoteResponse>(
quoteRequestParams,
apiUrl,
X_API_KEY
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { nativeTokensList } from 'src/common/tokens/constants/native-tokens';
import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure';

import { ON_CHAIN_TRADE_TYPE } from '../../common/models/on-chain-trade-type';
import { OpenOceanApiService } from '../common/open-ocean/open-ocean-api-service';
import { X_API_KEY } from '../open-ocean/constants/api-key';
import { ARBITRUM_GAS_PRICE } from '../open-ocean/constants/arbitrum-gas-price';
import { openOceanApiUrl } from '../open-ocean/constants/get-open-ocean-api-url';
import { openOceanBlockchainName } from '../open-ocean/constants/open-ocean-blockchain';
import { OpenoceanOnChainSupportedBlockchain } from '../open-ocean/constants/open-ocean-on-chain-supported-blockchain';
import { OpenoceanSwapQuoteResponse } from '../open-ocean/models/open-cean-swap-quote-response';
import { OpenOceanTrade } from '../open-ocean/open-ocean-trade';
import { GMXSwapQuoteRequest } from './models/gmx-quote-request';

export class GMXOnChainTrade extends OpenOceanTrade {
public override readonly type = ON_CHAIN_TRADE_TYPE.GMX;

protected override getSwapQuote(
isArbitrum: boolean,
gasPrice: string,
account: string
): Promise<OpenoceanSwapQuoteResponse> {
const swapQuoteParams: GMXSwapQuoteRequest = {
inTokenAddress: this.getTokenAddress(this.from),
outTokenAddress: this.getTokenAddress(this.to),
amount: this.fromWithoutFee.tokenAmount.toString(),
gasPrice: isArbitrum
? ARBITRUM_GAS_PRICE
: Web3Pure.fromWei(gasPrice, nativeTokensList[this.from.blockchain].decimals)
.multipliedBy(10 ** 9)
.toString(),
slippage: this.slippageTolerance * 100,
account,
referrer: '0x429A3A1a2623DFb520f1D93F64F38c0738418F1f'
};

const apiUrl = openOceanApiUrl.gmxSwapQuote(
openOceanBlockchainName[this.from.blockchain as OpenoceanOnChainSupportedBlockchain]
);

return OpenOceanApiService.getQuote<GMXSwapQuoteRequest, OpenoceanSwapQuoteResponse>(
swapQuoteParams,
apiUrl,
X_API_KEY
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface GMXQuoteRequest {
amount: string;
inTokenAddress: string;
outTokenAddress: string;
gasPrice: string;
[key: string]: string | number;
}

export type GMXSwapQuoteRequest = GMXQuoteRequest & {
slippage: number;
account: string;
referrer: string;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const X_API_KEY = 'sndfje3u4b3fnNSDNFUSDNVSunw345842hrnfd3b4nt4';
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export const openOceanApiUrl = {
tokenList: (chain: string) => `https://x-api.rubic.exchange/oo/api/token_list/${chain}`,
quote: (chain: string) => `https://x-api.rubic.exchange/oo/api/v3/${chain}/quote`,
swapQuote: (chain: string) => `https://x-api.rubic.exchange/oo/api/v3/${chain}/swap_quote`
swapQuote: (chain: string) => `https://x-api.rubic.exchange/oo/api/v3/${chain}/swap_quote`,
gmxQuote: (chain: string) => `https://open-api.openocean.finance/v3/${chain}/gmx_quote`,
gmxSwapQuote: (chain: string) => `https://open-api.openocean.finance/v3/${chain}/gmx_swap_quote`
};
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { OpenOceanQuoteRequest } from './open-ocean-quote-response';

export interface OpenoceanSwapQuoteResponse {
code: number;
data: {
Expand All @@ -8,3 +10,8 @@ export interface OpenoceanSwapQuoteResponse {
};
error?: string;
}

export type OpenOceanSwapQuoteRequest = OpenOceanQuoteRequest & {
account: string;
referrer: string;
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,13 @@ export interface OpenOceanQuoteResponse {
estimatedGas: number;
};
}

export interface OpenOceanQuoteRequest {
chain: string;
inTokenAddress: string;
outTokenAddress: string;
amount: string;
slippage: number;
gasPrice: string;
[key: string]: string | number;
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,51 @@ import {
OpenoceanOnChainSupportedBlockchain,
openoceanOnChainSupportedBlockchains
} from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/constants/open-ocean-on-chain-supported-blockchain';
import { OpenOceanQuoteResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-quote-response';
import {
OpenOceanQuoteRequest,
OpenOceanQuoteResponse
} from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-quote-response';
import { OpenOceanTokenListResponse } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-token-list-response';
import { OpenOceanTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/models/open-ocean-trade-struct';
import { OpenOceanTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/open-ocean/open-ocean-trade';
import { RequiredOnChainCalculationOptions } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-calculation-options';
import { ON_CHAIN_TRADE_TYPE } from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type';
import {
ON_CHAIN_TRADE_TYPE,
OnChainTradeType
} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type';
import { GasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info';
import { OnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/on-chain-trade';
import { getGasFeeInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-fee-info';
import { getGasPriceInfo } from 'src/features/on-chain/calculation-manager/providers/common/utils/get-gas-price-info';

import { AggregatorOnChainProvider } from '../../common/on-chain-aggregator/aggregator-on-chain-provider-abstract';
import { OpenOceanApiService } from '../common/open-ocean/open-ocean-api-service';
import { X_API_KEY } from './constants/api-key';
import { ARBITRUM_GAS_PRICE } from './constants/arbitrum-gas-price';

export class OpenOceanProvider extends AggregatorOnChainProvider {
public readonly tradeType = ON_CHAIN_TRADE_TYPE.OPEN_OCEAN;
public readonly tradeType: OnChainTradeType = ON_CHAIN_TRADE_TYPE.OPEN_OCEAN;

public static readonly nativeAddress = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE';

protected isSupportedBlockchain(blockchain: BlockchainName): boolean {
return openoceanOnChainSupportedBlockchains.some(item => item === blockchain);
}

protected createTradeInstance(
openOceanTradeStruct: OpenOceanTradeStruct,
gasFeeInfo: GasFeeInfo | null,
providerAddress: string
): OpenOceanTrade {
return new OpenOceanTrade(
{
...openOceanTradeStruct,
gasFeeInfo
},
providerAddress
);
}

public async calculate(
from: PriceTokenAmount<EvmBlockchainName>,
toToken: PriceToken<EvmBlockchainName>,
Expand All @@ -57,23 +79,9 @@ export class OpenOceanProvider extends AggregatorOnChainProvider {
.getWeb3Public(blockchain)
.getGasPrice();
const isArbitrum = blockchain === BLOCKCHAIN_NAME.ARBITRUM;
const apiUrl = openOceanApiUrl.quote(openOceanBlockchainName[blockchain]);

const quoteResponse = await pTimeout(
Injector.httpClient.get<OpenOceanQuoteResponse>(apiUrl, {
headers: { apikey: 'sndfje3u4b3fnNSDNFUSDNVSunw345842hrnfd3b4nt4' },
params: {
chain: openOceanBlockchainName[blockchain],
inTokenAddress: this.getTokenAddress(fromWithoutFee),
outTokenAddress: this.getTokenAddress(toToken),
amount: fromWithoutFee.tokenAmount.toString(),
slippage: options.slippageTolerance! * 100,
gasPrice: isArbitrum
? ARBITRUM_GAS_PRICE
: Web3Pure.fromWei(gasPrice, nativeTokensList[from.blockchain].decimals)
.multipliedBy(10 ** 9)
.toString()
}
}),
this.getQuote(from, toToken, options.slippageTolerance, isArbitrum, gasPrice),
7_000
);

Expand Down Expand Up @@ -110,11 +118,9 @@ export class OpenOceanProvider extends AggregatorOnChainProvider {
? await this.getGasFeeInfo(openOceanTradeStruct)
: null;

return new OpenOceanTrade(
{
...openOceanTradeStruct,
gasFeeInfo
},
return this.createTradeInstance(
openOceanTradeStruct,
gasFeeInfo,
options.providerAddress
);
} catch (error) {
Expand All @@ -125,7 +131,7 @@ export class OpenOceanProvider extends AggregatorOnChainProvider {
}
}

private getTokenAddress(token: PriceToken): string {
protected getTokenAddress(token: PriceToken): string {
if (token.isNative) {
if (token.blockchain === BLOCKCHAIN_NAME.METIS) {
return '0xdeaddeaddeaddeaddeaddeaddeaddeaddead0000';
Expand All @@ -148,14 +154,47 @@ export class OpenOceanProvider extends AggregatorOnChainProvider {
}
}

protected async getQuote(
from: PriceTokenAmount,
toToken: PriceToken,
slippage: number,
isArbitrum: boolean,
gasPrice: string
): Promise<OpenOceanQuoteResponse> {
const blockchain = from.blockchain as OpenoceanOnChainSupportedBlockchain;
const apiUrl = openOceanApiUrl.quote(openOceanBlockchainName[blockchain]);

const quoteRequestParams: OpenOceanQuoteRequest = {
chain: openOceanBlockchainName[blockchain],
inTokenAddress: this.getTokenAddress(from),
outTokenAddress: this.getTokenAddress(toToken),
amount: from.tokenAmount.toString(),
slippage: slippage * 100,
gasPrice: isArbitrum
? ARBITRUM_GAS_PRICE
: Web3Pure.fromWei(gasPrice, nativeTokensList[from.blockchain].decimals)
.multipliedBy(10 ** 9)
.toString()
};

return OpenOceanApiService.getQuote<OpenOceanQuoteRequest, OpenOceanQuoteResponse>(
quoteRequestParams,
apiUrl,
X_API_KEY
);
}

private async checkIsSupportedTokens(from: PriceTokenAmount, to: PriceToken): Promise<void> {
const apiUrl = openOceanApiUrl.tokenList(
openOceanBlockchainName[from.blockchain as OpenoceanOnChainSupportedBlockchain]
);
const tokenListResponse = await Injector.httpClient.get<OpenOceanTokenListResponse>(
apiUrl,
{ headers: { apikey: 'sndfje3u4b3fnNSDNFUSDNVSunw345842hrnfd3b4nt4' } }
);

const tokenListResponse =
await OpenOceanApiService.getSupportedTokenList<OpenOceanTokenListResponse>(
apiUrl,
X_API_KEY
);

const tokens = tokenListResponse?.data?.map(token => token.address.toLocaleLowerCase());
const isSupportedTokens =
Boolean(tokens.length) &&
Expand Down
Loading
Loading