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

[Backend Issue 1] Migrate swap quote fetching to backend #4000

Open
wants to merge 12 commits into
base: subwallet-dev
Choose a base branch
from
2 changes: 2 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,5 @@ BITTENSOR_API_KEY_8=tao-abafdbad8
BITTENSOR_API_KEY_9=tao-abafdbad9
BITTENSOR_API_KEY_10=tao-abafdbad10
SIMPLE_SWAP_API_KEY=26c8c21
UNISWAP_API_KEY=D3D2BvnTo
SUBWALLET_API=http://localhost:3000/api
4 changes: 3 additions & 1 deletion .github/workflows/push-koni-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ jobs:
TRANSAK_API_KEY: ${{ secrets.TRANSAK_API_KEY }}
COINBASE_PAY_ID: ${{ secrets.COINBASE_PAY_ID }}
CHAINFLIP_BROKER_API: ${{ secrets.CHAINFLIP_BROKER_API }}
BITTENSOR_API_KEY_1: ${{ secrets.BITTENSOR_API_KEY_1 }}
BITTENSOR_API_KEY_1: ${{ secrets.BITTENSOR_API_KEY_1 }}
BITTENSOR_API_KEY_2: ${{ secrets.BITTENSOR_API_KEY_2 }}
BITTENSOR_API_KEY_3: ${{ secrets.BITTENSOR_API_KEY_3 }}
BITTENSOR_API_KEY_4: ${{ secrets.BITTENSOR_API_KEY_4 }}
Expand All @@ -58,6 +58,8 @@ jobs:
BITTENSOR_API_KEY_9: ${{ secrets.BITTENSOR_API_KEY_9 }}
BITTENSOR_API_KEY_10: ${{ secrets.BITTENSOR_API_KEY_10 }}
SIMPLE_SWAP_API_KEY: ${{ secrets.SIMPLE_SWAP_API_KEY }}
UNISWAP_API_KEY: ${{ secrets.UNISWAP_API_KEY }}
SUBWALLET_API: ${{ secrets.SUBWALLET_API }}
GH_RELEASE_FILES: master-build.zip,master-src.zip
COMMIT_MESSAGE: ${{ github.event.head_commit.message }}
REF_NAME: ${{ github.ref_name }}
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/push-master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
TRANSAK_API_KEY: ${{ secrets.TRANSAK_API_KEY }}
COINBASE_PAY_ID: ${{ secrets.COINBASE_PAY_ID }}
CHAINFLIP_BROKER_API: ${{ secrets.CHAINFLIP_BROKER_API }}
BITTENSOR_API_KEY_1: ${{ secrets.BITTENSOR_API_KEY_1 }}
BITTENSOR_API_KEY_1: ${{ secrets.BITTENSOR_API_KEY_1 }}
BITTENSOR_API_KEY_2: ${{ secrets.BITTENSOR_API_KEY_2 }}
BITTENSOR_API_KEY_3: ${{ secrets.BITTENSOR_API_KEY_3 }}
BITTENSOR_API_KEY_4: ${{ secrets.BITTENSOR_API_KEY_4 }}
Expand All @@ -39,6 +39,8 @@ jobs:
BITTENSOR_API_KEY_9: ${{ secrets.BITTENSOR_API_KEY_9 }}
BITTENSOR_API_KEY_10: ${{ secrets.BITTENSOR_API_KEY_10 }}
SIMPLE_SWAP_API_KEY: ${{ secrets.SIMPLE_SWAP_API_KEY }}
UNISWAP_API_KEY: ${{ secrets.UNISWAP_API_KEY }}
SUBWALLET_API: ${{ secrets.SUBWALLET_API }}
BRANCH_NAME: ${{ github.ref_name }}
run: |
yarn install --immutable | grep -v 'YN0013'
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/push-web-runner.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
TRANSAK_API_KEY: ${{ secrets.TRANSAK_API_KEY }}
COINBASE_PAY_ID: ${{ secrets.COINBASE_PAY_ID }}
CHAINFLIP_BROKER_API: ${{ secrets.CHAINFLIP_BROKER_API }}
BITTENSOR_API_KEY_1: ${{ secrets.BITTENSOR_API_KEY_1 }}
BITTENSOR_API_KEY_1: ${{ secrets.BITTENSOR_API_KEY_1 }}
BITTENSOR_API_KEY_2: ${{ secrets.BITTENSOR_API_KEY_2 }}
BITTENSOR_API_KEY_3: ${{ secrets.BITTENSOR_API_KEY_3 }}
BITTENSOR_API_KEY_4: ${{ secrets.BITTENSOR_API_KEY_4 }}
Expand All @@ -43,6 +43,8 @@ jobs:
BITTENSOR_API_KEY_9: ${{ secrets.BITTENSOR_API_KEY_9 }}
BITTENSOR_API_KEY_10: ${{ secrets.BITTENSOR_API_KEY_10 }}
SIMPLE_SWAP_API_KEY: ${{ secrets.SIMPLE_SWAP_API_KEY }}
UNISWAP_API_KEY: ${{ secrets.UNISWAP_API_KEY }}
SUBWALLET_API: ${{ secrets.SUBWALLET_API }}
BRANCH_NAME: master
run: |
yarn install --immutable | grep -v 'YN0013'
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/push-webapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ jobs:
COINBASE_PAY_ID: ${{ secrets.COINBASE_PAY_ID }}
NFT_MINTING_HOST: ${{ secrets.NFT_MINTING_HOST }}
CHAINFLIP_BROKER_API: ${{ secrets.CHAINFLIP_BROKER_API }}
BITTENSOR_API_KEY_1: ${{ secrets.BITTENSOR_API_KEY_1 }}
BITTENSOR_API_KEY_1: ${{ secrets.BITTENSOR_API_KEY_1 }}
BITTENSOR_API_KEY_2: ${{ secrets.BITTENSOR_API_KEY_2 }}
BITTENSOR_API_KEY_3: ${{ secrets.BITTENSOR_API_KEY_3 }}
BITTENSOR_API_KEY_4: ${{ secrets.BITTENSOR_API_KEY_4 }}
Expand All @@ -43,6 +43,8 @@ jobs:
BITTENSOR_API_KEY_9: ${{ secrets.BITTENSOR_API_KEY_9 }}
BITTENSOR_API_KEY_10: ${{ secrets.BITTENSOR_API_KEY_10 }}
SIMPLE_SWAP_API_KEY: ${{ secrets.SIMPLE_SWAP_API_KEY }}
UNISWAP_API_KEY: ${{ secrets.UNISWAP_API_KEY }}
SUBWALLET_API: ${{ secrets.SUBWALLET_API }}
BRANCH_NAME: ${{ github.ref_name }}
run: |
yarn install --immutable | grep -v 'YN0013'
Expand Down
2 changes: 1 addition & 1 deletion packages/extension-base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"@acala-network/api": "^5.0.2",
"@apollo/client": "^3.7.14",
"@azns/resolver-core": "^1.4.0",
"@chainflip/sdk": "^1.6.0",
"@equilab/api": "~1.14.25",
"@ethereumjs/common": "^4.1.0",
"@ethereumjs/tx": "^5.1.0",
Expand Down Expand Up @@ -61,6 +60,7 @@
"@subwallet/extension-dapp": "^1.3.15-0",
"@subwallet/extension-inject": "^1.3.15-0",
"@subwallet/keyring": "^0.1.8-beta.0",
"@subwallet/subwallet-api-sdk": "^1.3.12-1",
"@subwallet/ui-keyring": "^0.1.8-beta.0",
"@ton/core": "^0.56.3",
"@ton/crypto": "^3.2.0",
Expand Down
1 change: 1 addition & 0 deletions packages/extension-base/src/constants/environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ const PRODUCTION_BRANCHES = ['master', 'webapp', 'webapp-dev'];
const branchName = process.env.BRANCH_NAME || 'subwallet-dev';

export const isProductionMode = PRODUCTION_BRANCHES.indexOf(branchName) > -1;
export const BACKEND_API_URL = process.env.SUBWALLET_API || (isProductionMode ? '' : 'https://be-dev.subwallet.app/api'); // TODO: Update production URL
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { withErrorLog } from '@subwallet/extension-base/background/handlers/help
import { isSubscriptionRunning, unsubscribe } from '@subwallet/extension-base/background/handlers/subscriptions';
import { AddTokenRequestExternal, AmountData, APIItemState, ApiMap, AuthRequestV2, ChainStakingMetadata, ChainType, ConfirmationsQueue, ConfirmationsQueueTon, CrowdloanItem, CrowdloanJson, CurrencyType, EvmProviderErrorType, EvmSendTransactionParams, EvmSendTransactionRequest, EvmSignatureRequest, ExternalRequestPromise, ExternalRequestPromiseStatus, ExtrinsicType, MantaAuthorizationContext, MantaPayConfig, MantaPaySyncState, NftCollection, NftItem, NftJson, NominatorMetadata, RequestAccountExportPrivateKey, RequestConfirmationComplete, RequestConfirmationCompleteTon, RequestCrowdloanContributions, RequestSettingsType, ResponseAccountExportPrivateKey, ServiceInfo, SingleModeJson, StakingItem, StakingJson, StakingRewardItem, StakingRewardJson, StakingType, UiSettings } from '@subwallet/extension-base/background/KoniTypes';
import { RequestAuthorizeTab, RequestRpcSend, RequestRpcSubscribe, RequestRpcUnsubscribe, RequestSign, ResponseRpcListProviders, ResponseSigning } from '@subwallet/extension-base/background/types';
import { EnvConfig, MANTA_PAY_BALANCE_INTERVAL, REMIND_EXPORT_ACCOUNT } from '@subwallet/extension-base/constants';
import { BACKEND_API_URL, EnvConfig, MANTA_PAY_BALANCE_INTERVAL, REMIND_EXPORT_ACCOUNT } from '@subwallet/extension-base/constants';
import { convertErrorFormat, generateValidationProcess, PayloadValidated, ValidateStepFunction, validationAuthMiddleware, validationAuthWCMiddleware, validationConnectMiddleware, validationEvmDataTransactionMiddleware, validationEvmSignMessageMiddleware } from '@subwallet/extension-base/core/logic-validation';
import { BalanceService } from '@subwallet/extension-base/services/balance-service';
import { ServiceStatus } from '@subwallet/extension-base/services/base/types';
Expand Down Expand Up @@ -46,6 +46,7 @@ import { BalanceItem, BasicTxErrorType, CurrentAccountInfo, EvmFeeInfo, RequestC
import { isManifestV3, stripUrl, targetIsWeb } from '@subwallet/extension-base/utils';
import { createPromiseHandler } from '@subwallet/extension-base/utils/promise';
import { MetadataDef, ProviderMeta } from '@subwallet/extension-inject/types';
import subwalletApiSdk from '@subwallet/subwallet-api-sdk';
import { keyring } from '@subwallet/ui-keyring';
import BN from 'bn.js';
import { t } from 'i18next';
Expand Down Expand Up @@ -141,6 +142,9 @@ export default class KoniState {
private waitStarting: Promise<void> | null = null;

constructor (providers: Providers = {}) {
// Init subwallet api sdk
subwalletApiSdk.init(BACKEND_API_URL);

this.providers = providers;

this.eventService = new EventService();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
// Copyright 2019-2022 @subwallet/extension-base
// SPDX-License-Identifier: Apache-2.0

import { SwapError } from '@subwallet/extension-base/background/errors/SwapError';
import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
import { ChainType, ExtrinsicType } from '@subwallet/extension-base/background/KoniTypes';
import { _getEarlyAssetHubValidationError, _validateBalanceToSwapOnAssetHub, _validateSwapRecipient } from '@subwallet/extension-base/core/logic-validation/swap';
import { _validateBalanceToSwapOnAssetHub, _validateSwapRecipient } from '@subwallet/extension-base/core/logic-validation/swap';
import { BalanceService } from '@subwallet/extension-base/services/balance-service';
import { createXcmExtrinsic } from '@subwallet/extension-base/services/balance-service/transfer/xcm';
import { ChainService } from '@subwallet/extension-base/services/chain-service';
import { _getChainNativeTokenSlug, _isNativeToken } from '@subwallet/extension-base/services/chain-service/utils';
import { convertSwapRate, getSwapAlternativeAsset, SWAP_QUOTE_TIMEOUT_MAP } from '@subwallet/extension-base/services/swap-service/utils';
import { getSwapAlternativeAsset } from '@subwallet/extension-base/services/swap-service/utils';
import { BasicTxErrorType, RequestCrossChainTransfer, RuntimeDispatchInfo } from '@subwallet/extension-base/types';
import { BaseStepDetail, CommonFeeComponent, CommonOptimalPath, CommonStepFeeInfo, CommonStepType } from '@subwallet/extension-base/types/service-base';
import { AssetHubSwapEarlyValidation, OptimalSwapPathParams, SwapBaseTxData, SwapErrorType, SwapFeeType, SwapProviderId, SwapQuote, SwapRequest, SwapStepType, SwapSubmitParams, SwapSubmitStepData, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap';
import { BaseStepDetail, CommonOptimalPath, CommonStepFeeInfo, CommonStepType } from '@subwallet/extension-base/types/service-base';
import { OptimalSwapPathParams, SwapBaseTxData, SwapErrorType, SwapFeeType, SwapProviderId, SwapStepType, SwapSubmitParams, SwapSubmitStepData, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap';
import BigN from 'bignumber.js';

import { SwapBaseHandler, SwapBaseInterface } from '../base-handler';
import { AssetHubRouter } from './router';

const PAH_LOW_LIQUIDITY_THRESHOLD = 0.15;

export class AssetHubSwapHandler implements SwapBaseInterface {
private swapBaseHandler: SwapBaseHandler;
private readonly chain: string;
Expand Down Expand Up @@ -170,65 +167,6 @@ export class AssetHubSwapHandler implements SwapBaseInterface {
]);
}

async getSwapQuote (request: SwapRequest): Promise<SwapQuote | SwapError> {
const fromAsset = this.chainService.getAssetBySlug(request.pair.from);
const toAsset = this.chainService.getAssetBySlug(request.pair.to);
const fromChain = this.chainService.getChainInfoByKey(fromAsset.originChain);
const fromChainNativeTokenSlug = _getChainNativeTokenSlug(fromChain);

if (!this.isReady || !this.router) {
return new SwapError(SwapErrorType.UNKNOWN);
}

const earlyValidation = await this.validateSwapRequest(request);

if (earlyValidation.error) {
const metadata = earlyValidation.metadata;

return _getEarlyAssetHubValidationError(earlyValidation.error, metadata);
}

try {
const paths = this.router.buildPath(request.pair);
const amountOut = earlyValidation.metadata.toAmount;
const toAmount = new BigN(amountOut);
const minReceive = toAmount.times(1 - request.slippage).integerValue(BigN.ROUND_DOWN);
const extrinsic = await this.router.buildSwapExtrinsic(paths, request.address, request.fromAmount, minReceive.toString());
const paymentInfo = await extrinsic.paymentInfo(request.address);

const networkFee: CommonFeeComponent = {
tokenSlug: fromChainNativeTokenSlug,
amount: paymentInfo.partialFee.toString(),
feeType: SwapFeeType.NETWORK_FEE
};

const feeTokenOptions = [fromChainNativeTokenSlug];
const selectedFeeToken = fromChainNativeTokenSlug;
const priceImpactPct = earlyValidation.metadata.priceImpactPct || '0';

return {
pair: request.pair,
fromAmount: request.fromAmount,
toAmount: toAmount.toString(),
rate: convertSwapRate(earlyValidation.metadata.quoteRate, fromAsset, toAsset),
provider: this.providerInfo,
aliveUntil: +Date.now() + (SWAP_QUOTE_TIMEOUT_MAP[this.slug] || SWAP_QUOTE_TIMEOUT_MAP.default),
feeInfo: {
feeComponent: [networkFee],
defaultFeeToken: fromChainNativeTokenSlug,
feeOptions: feeTokenOptions, // TODO: enable fee options
selectedFeeToken
},
isLowLiquidity: Math.abs(parseFloat(priceImpactPct)) >= PAH_LOW_LIQUIDITY_THRESHOLD,
route: {
path: paths.map((asset) => asset.slug)
}
} as SwapQuote;
} catch (e) {
return new SwapError(SwapErrorType.ERROR_FETCHING_QUOTE);
}
}

public async handleXcmStep (params: SwapSubmitParams): Promise<SwapSubmitStepData> {
const pair = params.quote.pair;
const alternativeAssetSlug = getSwapAlternativeAsset(pair) as string;
Expand Down Expand Up @@ -415,12 +353,4 @@ export class AssetHubSwapHandler implements SwapBaseInterface {

return [];
}

validateSwapRequest (request: SwapRequest): Promise<AssetHubSwapEarlyValidation> {
if (!this.isReady || !this.router) {
throw new SwapError(SwapErrorType.ERROR_FETCHING_QUOTE);
}

return this.router.earlyValidateSwapValidation(request);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright 2019-2022 @subwallet/extension-base
// SPDX-License-Identifier: Apache-2.0

import { SwapError } from '@subwallet/extension-base/background/errors/SwapError';
import { TransactionError } from '@subwallet/extension-base/background/errors/TransactionError';
import { _validateBalanceToSwap, _validateSwapRecipient } from '@subwallet/extension-base/core/logic-validation/swap';
import { BalanceService } from '@subwallet/extension-base/services/balance-service';
Expand All @@ -10,20 +9,18 @@ import { _isNativeToken } from '@subwallet/extension-base/services/chain-service
import { getSwapAlternativeAsset } from '@subwallet/extension-base/services/swap-service/utils';
import { BasicTxErrorType } from '@subwallet/extension-base/types';
import { BaseStepDetail, CommonOptimalPath, CommonStepFeeInfo, DEFAULT_FIRST_STEP, MOCK_STEP_FEE } from '@subwallet/extension-base/types/service-base';
import { GenSwapStepFunc, OptimalSwapPathParams, SwapEarlyValidation, SwapErrorType, SwapFeeType, SwapProvider, SwapProviderId, SwapQuote, SwapRequest, SwapSubmitParams, SwapSubmitStepData, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap';
import { GenSwapStepFunc, OptimalSwapPathParams, SwapErrorType, SwapFeeType, SwapProvider, SwapProviderId, SwapSubmitParams, SwapSubmitStepData, ValidateSwapProcessParams } from '@subwallet/extension-base/types/swap';
import { formatNumber } from '@subwallet/extension-base/utils';
import BigNumber from 'bignumber.js';
import { t } from 'i18next';

export interface SwapBaseInterface {
providerSlug: SwapProviderId;

getSwapQuote: (request: SwapRequest) => Promise<SwapQuote | SwapError>;
generateOptimalProcess: (params: OptimalSwapPathParams) => Promise<CommonOptimalPath>;

getSubmitStep: (params: OptimalSwapPathParams) => Promise<[BaseStepDetail, CommonStepFeeInfo] | undefined>;

validateSwapRequest: (request: SwapRequest) => Promise<SwapEarlyValidation>;
validateSwapProcess: (params: ValidateSwapProcessParams) => Promise<TransactionError[]>;

handleSwapProcess: (params: SwapSubmitParams) => Promise<SwapSubmitStepData>;
Expand Down
Loading
Loading