Skip to content

Commit

Permalink
Piteas DEX (#593)
Browse files Browse the repository at this point in the history
  • Loading branch information
Doctor-Cyclone authored Mar 29, 2024
2 parents d9dfeae + 5f96932 commit d7366d4
Show file tree
Hide file tree
Showing 10 changed files with 334 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "rubic-sdk",
"version": "5.8.5",
"version": "5.9.0",
"description": "Simplify dApp creation",
"main": "lib/index.js",
"types": "lib/index.d.ts",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { DlnOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/aggregators/dln/dln-on-chain-provider';
import { PiteasProvider } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-provider';

import { LifiProvider } from '../providers/aggregators/lifi/lifi-provider';
import { OdosOnChainProvider } from '../providers/aggregators/odos/odos-on-chain-provider';
Expand All @@ -10,6 +11,7 @@ export const AGGREGATORS_ON_CHAIN = {
OPEN_OCEAN: OpenOceanProvider,
RANGO: RangoOnChainProvider,
ODOS: OdosOnChainProvider,
DLN: DlnOnChainProvider
DLN: DlnOnChainProvider,
PITEAS: PiteasProvider
// SYMBIOSIS: SymbiosisOnChainProvider
} as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { BLOCKCHAIN_NAME } from 'src/core/blockchain/models/blockchain-name';

export const piteasOnChainSupportedBlockchains = [BLOCKCHAIN_NAME.PULSECHAIN] as const;
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export const piteasRouterAddress = '0x6BF228eb7F8ad948d37deD07E595EfddfaAF88A6';
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
export interface PiteasQuoteRequestParams {
tokenInAddress: string;
tokenInChainId: number;
tokenOutAddress: string;
tokenOutChainId: number;
amount: string; // Amount with decimals
allowedSlippage: number;
account?: string; // Receiver address
}

export interface PiteasSuccessQuoteResponse {
destAmount: string;
gasUseEstimate: number;
methodParameters: PiteasMethodParameters;
}

export interface PiteasMethodParameters {
calldata: string;
value: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PiteasMethodParameters } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote';

import { EvmOnChainTradeStruct } from '../../../common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct';

export interface PiteasTradeStruct extends EvmOnChainTradeStruct {
methodParameters: PiteasMethodParameters;
gasUseEstimate: number;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Injector } from 'src/core/injector/injector';
import {
PiteasQuoteRequestParams,
PiteasSuccessQuoteResponse
} from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote';

export class PiteasApiService {
public static apiEndpoint = 'https://api.piteas.io';

public static fetchQuote(
quoteRequestParams: PiteasQuoteRequestParams
): Promise<PiteasSuccessQuoteResponse> {
return Injector.httpClient.get<PiteasSuccessQuoteResponse>(
`${PiteasApiService.apiEndpoint}/quote`,
{
params: { ...quoteRequestParams }
}
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
import BigNumber from 'bignumber.js';
import { RubicSdkError } from 'src/common/errors';
import { PriceToken, PriceTokenAmount } from 'src/common/tokens';
import { combineOptions } from 'src/common/utils/options';
import { BlockchainName, EvmBlockchainName } from 'src/core/blockchain/models/blockchain-name';
import { blockchainId } from 'src/core/blockchain/utils/blockchains-info/constants/blockchain-id';
import { Web3Pure } from 'src/core/blockchain/web3-pure/web3-pure';
import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address';
import { OnChainTradeError } from 'src/features/on-chain/calculation-manager/models/on-chain-trade-error';
import { piteasOnChainSupportedBlockchains } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-on-chain-supported-blockchains';
import { piteasRouterAddress } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-router-address';
import { PiteasQuoteRequestParams } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote';
import { PiteasTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-trade-struct';
import { PiteasApiService } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-api-service';
import { PiteasTrade } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-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 { AggregatorOnChainProvider } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-on-chain-provider-abstract';
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 { evmProviderDefaultOptions } from 'src/features/on-chain/calculation-manager/providers/dexes/common/on-chain-provider/evm-on-chain-provider/constants/evm-provider-default-options';

import { GasFeeInfo } from '../../common/on-chain-trade/evm-on-chain-trade/models/gas-fee-info';
import { getGasPriceInfo } from '../../common/utils/get-gas-price-info';

export class PiteasProvider extends AggregatorOnChainProvider {
private readonly defaultOptions: RequiredOnChainCalculationOptions = {
...evmProviderDefaultOptions,
deadlineMinutes: 20,
disableMultihops: false
};

public readonly tradeType = ON_CHAIN_TRADE_TYPE.PITEAS;

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

public async calculate(
from: PriceTokenAmount<EvmBlockchainName>,
toToken: PriceToken<EvmBlockchainName>,
options?: RequiredOnChainCalculationOptions
): Promise<OnChainTrade | OnChainTradeError> {
if (!this.isSupportedBlockchain(from.blockchain)) {
throw new RubicSdkError(`Piteas doesn't support ${from.blockchain} chain!`);
}

try {
const fromAddress =
options?.useProxy || this.defaultOptions.useProxy
? rubicProxyContractAddress[from.blockchain].gateway
: this.getWalletAddress(from.blockchain);

const fullOptions = combineOptions(options, {
...this.defaultOptions,
fromAddress
});

const quoteRequestParams: PiteasQuoteRequestParams = {
tokenInAddress: from.isNative ? 'PLS' : from.address,
tokenInChainId: blockchainId[from.blockchain],
tokenOutAddress: toToken.isNative ? 'PLS' : toToken.address,
tokenOutChainId: blockchainId[from.blockchain],
amount: from.stringWeiAmount,
allowedSlippage: 0.5,
...(options?.fromAddress && { account: options.fromAddress })
};

const { fromWithoutFee, proxyFeeInfo } = await this.handleProxyContract(from, {
...fullOptions
});

const { destAmount, gasUseEstimate, methodParameters } =
await PiteasApiService.fetchQuote(quoteRequestParams);

const to = new PriceTokenAmount({
...toToken.asStruct,
tokenAmount: Web3Pure.fromWei(destAmount, toToken.decimals)
});

const tradeStruct: PiteasTradeStruct = {
from,
to,
slippageTolerance: fullOptions.slippageTolerance,
path: [from, to],
gasFeeInfo: null,
useProxy: fullOptions.useProxy,
proxyFeeInfo,
fromWithoutFee,
withDeflation: fullOptions.withDeflation,
usedForCrossChain: fullOptions.usedForCrossChain,
methodParameters,
gasUseEstimate
};

const gasFeeInfo =
options?.gasCalculation === 'calculate'
? await this.getGasFeeInfo(tradeStruct, piteasRouterAddress)
: null;

return new PiteasTrade(
{
...tradeStruct,
gasFeeInfo
},
fullOptions.providerAddress,
quoteRequestParams
);
} catch (err) {
return {
type: ON_CHAIN_TRADE_TYPE.PITEAS,
error: err
};
}
}

protected async getGasFeeInfo(
tradeStruct: PiteasTradeStruct,
providerGateway: string
): Promise<GasFeeInfo | null> {
try {
const gasPriceInfo = await getGasPriceInfo(tradeStruct.from.blockchain);
const gasLimit =
(await PiteasTrade.getGasLimit(
tradeStruct,
providerGateway,
tradeStruct.methodParameters
)) || new BigNumber(tradeStruct.gasUseEstimate);
return getGasFeeInfo(gasLimit, gasPriceInfo);
} catch {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
import BigNumber from 'bignumber.js';
import { RubicSdkError } from 'src/common/errors';
import { parseError } from 'src/common/utils/errors';
import { EvmEncodeConfig } from 'src/core/blockchain/web3-pure/typed-web3-pure/evm-web3-pure/models/evm-encode-config';
import { Injector } from 'src/core/injector/injector';
import { EncodeTransactionOptions } from 'src/features/common/models/encode-transaction-options';
import { rubicProxyContractAddress } from 'src/features/cross-chain/calculation-manager/providers/common/constants/rubic-proxy-contract-address';
import { piteasRouterAddress } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/constants/piteas-router-address';
import {
PiteasMethodParameters,
PiteasQuoteRequestParams
} from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-quote';
import { PiteasTradeStruct } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/models/piteas-trade-struct';
import { PiteasApiService } from 'src/features/on-chain/calculation-manager/providers/aggregators/piteas/piteas-api-service';
import {
ON_CHAIN_TRADE_TYPE,
OnChainTradeType
} from 'src/features/on-chain/calculation-manager/providers/common/models/on-chain-trade-type';
import { AggregatorEvmOnChainTrade } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/aggregator-evm-on-chain-trade-abstract';
import { GetToAmountAndTxDataResponse } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-aggregator/models/aggregator-on-chain-types';
import { EvmOnChainTradeStruct } from 'src/features/on-chain/calculation-manager/providers/common/on-chain-trade/evm-on-chain-trade/models/evm-on-chain-trade-struct';

export class PiteasTrade extends AggregatorEvmOnChainTrade {
public static async getGasLimit(
tradeStruct: EvmOnChainTradeStruct,
providerGateway: string,
methodParameters: PiteasMethodParameters
): Promise<BigNumber | null> {
const fromBlockchain = tradeStruct.from.blockchain;
const walletAddress =
Injector.web3PrivateService.getWeb3PrivateByBlockchain(fromBlockchain).address;
if (!walletAddress) {
return null;
}

try {
const web3Public = Injector.web3PublicService.getWeb3Public(fromBlockchain);
const gasLimit = await web3Public.getEstimatedGasByData(
walletAddress,
providerGateway,
{
data: methodParameters.calldata,
value: methodParameters.value
}
);

if (!gasLimit?.isFinite()) {
return null;
}
return gasLimit;
} catch (err) {
console.debug(err);
return null;
}
}

public readonly type: OnChainTradeType = ON_CHAIN_TRADE_TYPE.PITEAS;

public readonly providerGateway = piteasRouterAddress;

private readonly quoteRequestParams: PiteasQuoteRequestParams;

protected get spenderAddress(): string {
return this.useProxy
? rubicProxyContractAddress[this.from.blockchain].gateway
: this.providerGateway;
}

public get dexContractAddress(): string {
return piteasRouterAddress;
}

constructor(
tradeStruct: PiteasTradeStruct,
providerAddress: string,
quoteRequestParams: PiteasQuoteRequestParams
) {
super(tradeStruct, providerAddress);

this.quoteRequestParams = quoteRequestParams;
}

public async encodeDirect(options: EncodeTransactionOptions): Promise<EvmEncodeConfig> {
await this.checkFromAddress(options.fromAddress, true);
await this.checkReceiverAddress(options.receiverAddress);

try {
const transactionData = await this.getTxConfigAndCheckAmount(
options.receiverAddress,
options.fromAddress,
options.directTransaction
);

const { gas, gasPrice } = this.getGasParams(options, {
gasLimit: transactionData.gas,
gasPrice: transactionData.gasPrice
});

return {
to: transactionData.to,
data: transactionData.data,
value: transactionData.value,
gas,
gasPrice
};
} catch (error) {
throw parseError(error);
}
}

public async getToAmountAndTxData(
receiverAddress?: string,
fromAddress?: string
): Promise<GetToAmountAndTxDataResponse> {
const account = receiverAddress || fromAddress;
try {
const { destAmount, gasUseEstimate, methodParameters } =
await PiteasApiService.fetchQuote({
...this.quoteRequestParams,
account
});

const tx: EvmEncodeConfig = {
to: piteasRouterAddress,
data: methodParameters.calldata,
value: methodParameters.value,
gas: gasUseEstimate.toString()
};

return {
tx,
toAmount: destAmount
};
} catch (error) {
if ('statusCode' in error && 'message' in error) {
throw new RubicSdkError(error.message);
}
throw error;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ export const ON_CHAIN_TRADE_TYPE = {
PEGASYS: 'PEGASYS',
PHOTON_SWAP: 'PHOTON_SWAP',
POLYDEX: 'POLYDEX',
PITEAS: 'PITEAS',

QUICK_SWAP: 'QUICK_SWAP',
QUICK_SWAP_V3: 'QUICK_SWAP_V3',
Expand Down

0 comments on commit d7366d4

Please sign in to comment.