UNPKG

@openocean.finance/widget

Version:

Openocean Widget for cross-chain bridging and swapping. It will drive your multi-chain strategy and attract new users from everywhere.

173 lines 7.85 kB
import { ChainId } from '@openocean.finance/widget-sdk'; import { getPublicClient } from 'wagmi/actions'; import { formatUnits } from 'viem'; import { useConfig } from 'wagmi'; import { CROSS_CHAIN_FEE_RECEIVER, ETHER_ADDRESS } from '../constants/index.js'; import { BaseSwapAdapter, NOT_SUPPORTED_CHAINS_PRICE_SERVICE, } from './BaseSwapAdapter.js'; const XY_FINANCE_API = 'https://aggregator-api.xy.finance/v1'; export class XYFinanceAdapter extends BaseSwapAdapter { constructor() { super(); } getName() { return 'XYFinance'; } getIcon() { return 'https://xy.finance/img/favicon.ico'; } getSupportedChains() { return [ ChainId.ETH, ChainId.BSC, ChainId.POL, ChainId.AVA, ChainId.ARB, ChainId.OPT, ChainId.LNA, ChainId.BAS, ChainId.SCL, ChainId.BLS, ChainId.UNI, ]; } getSupportedTokens(_sourceChain, _destChain) { return []; } async getQuote(params) { const p = { srcChainId: params.fromChain, srcQuoteTokenAddress: params.fromToken.isNative ? ETHER_ADDRESS : params.fromToken.address, srcQuoteTokenAmount: params.amount, dstChainId: params.toChain, dstQuoteTokenAddress: params.toToken.isNative ? ETHER_ADDRESS : params.toToken.address, slippage: (params.slippage * 100) / 10000, srcSwapProviders: 'KyberSwap V1 DexAggregator', // bridgeProviders: 'yBridge', affiliate: CROSS_CHAIN_FEE_RECEIVER, //represents the fee you wish to collect. It is an integer between 0 and 100,000. In this range, 100,000 corresponds to 10%, 10,000 represents 1%, and so on in a similar fashion. commissionRate: (params.feeBps / 10000) * 1000000, }; // Convert the parameters object to URL query string const queryParams = new URLSearchParams(); for (const [key, value] of Object.entries(p)) { queryParams.append(key, String(value)); } const resp = await fetch(`${XY_FINANCE_API}/quote?${queryParams.toString()}`).then(res => res.json()); const r = resp?.routes?.sort((a, b) => { return +(BigInt(b.dstQuoteTokenAmount) - BigInt(a.dstQuoteTokenAmount)).toString(); })?.[0]; if (!r) { throw new Error('No route found'); } const formattedOutputAmount = formatUnits(BigInt(r.dstQuoteTokenAmount), params.toToken.decimals); const formattedInputAmount = formatUnits(BigInt(params.amount), params.fromToken.decimals); const tokenInUsd = params.tokenInUsd; const tokenOutUsd = params.tokenOutUsd; const inputUsd = NOT_SUPPORTED_CHAINS_PRICE_SERVICE.includes(params.fromChain) ? Number(r.srcQuoteTokenUsdValue) : tokenInUsd * +formattedInputAmount; const outputUsd = NOT_SUPPORTED_CHAINS_PRICE_SERVICE.includes(params.toChain) ? Number(r.dstQuoteTokenUsdValue) : tokenOutUsd * +formattedOutputAmount; return { quoteParams: params, outputAmount: BigInt(r.dstQuoteTokenAmount), formattedOutputAmount, inputUsd, outputUsd, priceImpact: !inputUsd || !outputUsd ? NaN : ((inputUsd - outputUsd) * 100) / inputUsd, rate: +formattedOutputAmount / +formattedInputAmount, gasFeeUsd: 0, timeEstimate: r.estimatedTransferTime, contractAddress: r.contractAddress, rawQuote: r, protocolFee: 0, platformFeePercent: (params.feeBps * 100) / 10000, }; } async executeSwap({ quote }, walletClient) { const account = walletClient.account?.address; if (!account) throw new Error('WalletClient account is not defined'); const fromToken = quote.quoteParams.fromToken; const toToken = quote.quoteParams.toToken; const buildSlippage = Math.floor(quote.quoteParams.slippage * 0.9) > 1 ? Math.floor(quote.quoteParams.slippage * 0.9) : quote.quoteParams.slippage; const p = { srcChainId: quote.quoteParams.fromChain, srcQuoteTokenAddress: fromToken.isNative ? ETHER_ADDRESS : fromToken.address, srcQuoteTokenAmount: quote.quoteParams.amount, dstChainId: quote.quoteParams.toChain, dstQuoteTokenAddress: toToken.isNative ? ETHER_ADDRESS : toToken.address, // slippage: quote.quoteParams.slippage, slippage: (buildSlippage * 100) / 10000, receiver: quote.quoteParams.recipient, bridgeProvider: quote.rawQuote.bridgeDescription.provider, srcBridgeTokenAddress: quote.rawQuote.bridgeDescription.srcBridgeTokenAddress, dstBridgeTokenAddress: quote.rawQuote.bridgeDescription.dstBridgeTokenAddress, affiliate: CROSS_CHAIN_FEE_RECEIVER, //represents the fee you wish to collect. It is an integer between 0 and 100,000. In this range, 100,000 corresponds to 10%, 10,000 represents 1%, and so on in a similar fashion. commissionRate: (quote.quoteParams.feeBps / 10000) * 1000000, ...(quote.rawQuote.srcSwapDescription ? { srcSwapProvider: quote.rawQuote.srcSwapDescription.provider } : {}), ...(quote.rawQuote.dstSwapDescription ? { dstSwapProvider: quote.rawQuote.dstSwapDescription?.provider, } : {}), }; // Convert the parameters object to URL query string const queryParams = new URLSearchParams(); for (const [key, value] of Object.entries(p)) { queryParams.append(key, String(value)); } const resp = await fetch(`${XY_FINANCE_API}/buildTx?${queryParams.toString()}`).then(res => res.json()); if (BigInt(resp.route.minReceiveAmount) < BigInt(quote.rawQuote.minReceiveAmount)) { throw new Error('Rate has changed'); } if (resp.tx) { const tx = await walletClient.sendTransaction({ chain: undefined, account, to: resp.tx.to, value: resp.tx.value, data: resp.tx.data, kzg: undefined, }); return { sender: quote.quoteParams.sender, id: tx, // specific id for each provider sourceTxHash: tx, adapter: this.getName(), sourceChain: quote.quoteParams.fromChain, targetChain: quote.quoteParams.toChain, inputAmount: quote.quoteParams.amount, outputAmount: quote.outputAmount.toString(), sourceToken: fromToken, targetToken: toToken, timestamp: new Date().getTime(), }; } throw new Error('No transaction found'); } async getTransactionStatus(p) { const publicClient = getPublicClient(useConfig(), { chainId: p.sourceChain, }); const receipt = await publicClient?.getTransactionReceipt({ hash: p.id, }); if (receipt.status === 'reverted') { return { txHash: '', status: 'Failed', }; } const res = await fetch(`${XY_FINANCE_API}/crossChainStatus?srcChainId=${p.sourceChain}&srcTxHash=${p.id}`).then(r => r.json()); return { txHash: res.tx || '', status: res.status === 'Done' ? 'Success' : res.status === 'Refunded' ? 'Refunded' : 'Processing', }; } } //# sourceMappingURL=XYFinanceAdapter.js.map