Skip to content

Commit

Permalink
Merge branch 'onekey' into fix/coinselect
Browse files Browse the repository at this point in the history
  • Loading branch information
originalix authored Oct 24, 2023
2 parents 74d8836 + a7924cf commit 94bc6e7
Show file tree
Hide file tree
Showing 5 changed files with 225 additions and 7 deletions.
2 changes: 1 addition & 1 deletion packages/engine/src/vaults/impl/btc/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export type IEncodedTxBtc = {
outputs: Array<{
address: string;
value: string;
payload?: { isCharge?: boolean; bip44Path?: string };
payload?: { isCharge?: boolean; bip44Path?: string; opReturn?: string };
inscriptions?: NFTBTCAssetModel[];
}>;
totalFee: string;
Expand Down
137 changes: 132 additions & 5 deletions packages/kit/src/views/Swap/quoter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import BigNumber from 'bignumber.js';
import { getNetworkImpl } from '@onekeyhq/engine/src/managers/network';
import type { Token } from '@onekeyhq/engine/src/types/token';
import type { IEncodedTxAptos } from '@onekeyhq/engine/src/vaults/impl/apt/types';
import type { IEncodedTxBtc } from '@onekeyhq/engine/src/vaults/impl/btc/types';
import type { IEncodedTxEvm } from '@onekeyhq/engine/src/vaults/impl/evm/Vault';
import { IDecodedTxStatus } from '@onekeyhq/engine/src/vaults/types';
import { OnekeyNetwork } from '@onekeyhq/shared/src/config/networkIds';
import { IMPL_APTOS, IMPL_EVM } from '@onekeyhq/shared/src/engine/engineConsts';
import debugLogger from '@onekeyhq/shared/src/logger/debugLogger';

Expand All @@ -31,6 +33,7 @@ import { DeezyQuoter } from './deezy';
import { JupiterQuoter } from './jupiter';
import { SocketQuoter } from './socket';
import { SwftcQuoter } from './swftc';
import { ThorSwapQuoter } from './thorswap';

import type {
BuildTransactionParams,
Expand Down Expand Up @@ -62,6 +65,21 @@ type TransactionOrder = {
orderId: string;
};

type ThorswapOrder = {
fromAsset: string;
userAddress: string;
amountIn: string;
amountOut: string;
amountOutMin: string;
memo: string;
expiration: string;
tcVault: string;
};

type ThorSwapData = {
quoteId: string;
};

type EVMTransaction = {
to: string;
value: string;
Expand All @@ -84,6 +102,8 @@ type BuildTransactionHttpResponse = {
order?: TransactionOrder;
errMsg?: string;
result?: FetchQuoteHttpResult;
thor?: ThorSwapData;
thorOrder?: ThorswapOrder;
};

type FetchQuoteHttpParams = {
Expand Down Expand Up @@ -147,6 +167,8 @@ export class SwapQuoter {

private socket = new SocketQuoter();

private thor = new ThorSwapQuoter();

private deezy = new DeezyQuoter();

private quoters: Quoter[] = [
Expand All @@ -155,6 +177,7 @@ export class SwapQuoter {
this.jupiter,
this.swftc,
this.deezy,
this.thor,
];

transactionReceipts: Record<
Expand Down Expand Up @@ -209,6 +232,72 @@ export class SwapQuoter {
return result;
}

async convertThorswapOrderToTransaction(
params: BuildTransactionParams,
order: ThorswapOrder,
) {
const { tokenIn, networkIn, activeAccount, sellAmount } = params;
if (!sellAmount || !tokenIn) {
return;
}
if (!order.tcVault) {
throw new Error('failed to build transaction due to invalid tcVault');
}
const depositCoinAmt = new BigNumber(sellAmount)
.shiftedBy(-tokenIn.decimals)
.toFixed();
let result: TransactionData | undefined;
if (
!tokenIn.tokenIdOnNetwork &&
[
OnekeyNetwork.btc,
OnekeyNetwork.doge,
OnekeyNetwork.ltc,
OnekeyNetwork.bch,
].includes(networkIn.id)
) {
result = await backgroundApiProxy.engine.buildEncodedTxFromTransfer({
networkId: networkIn.id,
accountId: activeAccount.id,
transferInfo: {
from: activeAccount.address,
to: order.tcVault,
amount: depositCoinAmt,
opReturn: order.memo,
},
});
if (result) {
const btcResult = result as IEncodedTxBtc;
const isOpReturnEqualOrderMemo = btcResult.outputs.some(
(output) => output.payload?.opReturn === order.memo,
);
if (!isOpReturnEqualOrderMemo) {
throw new Error(
'failed to build transaction due to invalid opReturn',
);
}
}
return result;
}
if (
!tokenIn.tokenIdOnNetwork &&
[OnekeyNetwork.cosmoshub].includes(networkIn.id)
) {
result = await backgroundApiProxy.engine.buildEncodedTxFromTransfer({
networkId: networkIn.id,
accountId: activeAccount.id,
transferInfo: {
from: activeAccount.address,
to: order.tcVault,
amount: depositCoinAmt,
destinationTag: order.memo,
},
});
return result;
}
throw new Error('not support network');
}

async fetchLimitOrderQuote(params: ILimitOrderQuoteParams) {
const urlParams = convertLimitOrderParams(params) as
| FetchQuoteHttpParams
Expand Down Expand Up @@ -408,15 +497,14 @@ export class SwapQuoter {

urlParams.quoterType = quoterType;
urlParams.disableValidate = Boolean(params.disableValidate);
const serverEndPont =
const serverEndPoint =
await backgroundApiProxy.serviceSwap.getServerEndPoint();
const url = `${serverEndPont}/exchange/build_tx`;
const url = `${serverEndPoint}/exchange/build_tx`;

const res = await this.httpClient.post(url, urlParams);
const requestId = this.parseRequestId(res);

const data = res.data as BuildTransactionHttpResponse;

if (data.errMsg) {
throw new Error(data.errMsg);
}
Expand All @@ -433,16 +521,32 @@ export class SwapQuoter {
requestId,
};
}
return {
const result = {
data: {
...data.transaction,
from: params.activeAccount.address,
} as IEncodedTxEvm,
result: data.result,
requestId,
} as BuildTransactionResponse;
if (data.thor) {
result.attachment = {
thorswapQuoteId: data.thor.quoteId,
};
}
return result;
}
const result = {
data: data.transaction,
result: data.result,
requestId,
} as BuildTransactionResponse;
if (data.thor) {
result.attachment = {
thorswapQuoteId: data.thor.quoteId,
};
}
return { data: data.transaction, result: data.result, requestId };
return result;
}
if (data.order && data.result?.instantRate) {
const transaction = await this.convertOrderToTransaction(
Expand All @@ -463,6 +567,22 @@ export class SwapQuoter {
requestId,
};
}
if (data.thorOrder) {
const transaction = await this.convertThorswapOrderToTransaction(
params,
data.thorOrder,
);
const result: BuildTransactionResponse = {
data: transaction,
result: data.result,
};
if (data.thor) {
result.attachment = {
thorswapQuoteId: data.thor.quoteId,
};
}
return result;
}
return undefined;
}

Expand Down Expand Up @@ -631,6 +751,13 @@ export class SwapQuoter {
if (orderInfo) {
return orderInfo.receiveCoinAmt;
}
} else if (tx?.quoterType === 'Thorswap') {
const info = await this.thor.getTransactionInfo(tx);
if (info) {
const { legs } = info.result;
const lastLegs = legs[legs.length - 1];
return lastLegs.toAmount;
}
} else {
const historyTx = await this.getHistoryTx(tx);
const txid = historyTx?.decodedTx.txid || tx.hash;
Expand Down
83 changes: 83 additions & 0 deletions packages/kit/src/views/Swap/quoter/thorswap.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import axios from 'axios';

import backgroundApiProxy from '../../../background/instance/backgroundApiProxy';
import { QuoterType } from '../typings';

import type {
Quoter,
TransactionDetails,
TransactionProgress,
} from '../typings';

export class ThorSwapQuoter implements Quoter {
type: QuoterType = QuoterType.thorswap;

async getBaseUrl() {
const baseUrl = await backgroundApiProxy.serviceSwap.getServerEndPoint();
return `${baseUrl}/thorswap`;
}

async queryTransactionProgress(
tx: TransactionDetails,
): Promise<TransactionProgress> {
const { networkId, accountId, nonce, hash, attachment } = tx;
if (nonce !== undefined) {
const status =
await backgroundApiProxy.serviceHistory.queryTransactionNonceStatus({
networkId,
accountId,
nonce,
});
if (status === 'failed' || status === 'canceled') {
return { status };
}
}
if (hash && attachment?.thorswapQuoteId) {
const data = await this.getTransactionInfo(tx);
if (data && data.status === 'success') {
const { legs } = data.result;
const lastLegs = legs[legs.length - 1];
return { status: 'sucesss', destinationTransactionHash: lastLegs.hash };
}
return { status: 'pending' };
}
if (Date.now() - tx.addedTime > 60 * 60 * 1000 * 24) {
return { status: 'failed' };
}
return undefined;
}

async getTransactionInfo(tx: TransactionDetails) {
const { hash, attachment } = tx;
if (hash && attachment?.thorswapQuoteId) {
const baseUrl = await this.getBaseUrl();
const url = `${baseUrl}/transaction_status`;
const res = await axios.get(url, {
params: {
hash,
quoteId: attachment?.thorswapQuoteId,
},
});
const data = res.data as {
status: string;
done: boolean;
result: {
quoteId: string;
firstTransactionHash: string;
status: string;
isLending: boolean;
isStreamingSwap: false;
legs: {
chain: string;
hash: string;
fromAsset: string;
fromAmount: string;
toAsset: string;
toAmount: string;
}[];
};
};
return data;
}
}
}
7 changes: 7 additions & 0 deletions packages/kit/src/views/Swap/typings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ export enum QuoterType {
jupiter = 'jupiter',
onekey = 'onekey',
deezy = 'Deezy',
thorswap = 'Thorswap',
}

export type FieldType = 'INPUT' | 'OUTPUT';
Expand Down Expand Up @@ -237,6 +238,12 @@ export interface TransactionAttachment {
swftcReceiveCoinAmt?: string;
swftcReceiveCoinCode?: string;
socketUsedBridgeNames?: string[];

thorswapQuoteId?: string;
}

export interface ThorswapOrderReceipt {
quoteId: string;
}

export type BuildTransactionParams = FetchQuoteParams & {
Expand Down
3 changes: 2 additions & 1 deletion packages/kit/src/views/Swap/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,8 @@ export function convertParams(params: FetchQuoteParams) {
if (params.onChainSatsPerVbyte) {
urlParams.onChainSatsPerVbyte = params.onChainSatsPerVbyte;
}
urlParams.includes = '0x,1inch,jupiter,openocean,swftc,socket,mdex,Deezy';
urlParams.includes =
'0x,1inch,jupiter,openocean,swftc,socket,mdex,Deezy,Thorswap';
urlParams.noFilter = true;
return urlParams;
}
Expand Down

0 comments on commit 94bc6e7

Please sign in to comment.