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.

248 lines 9.16 kB
import { MAINNET_RELAY_API, convertViemChainToRelayChain, createClient, getClient, } from '@relayprotocol/relay-sdk'; import { formatUnits } from 'viem'; import { arbitrum, avalanche, base, berachain, blast, bsc, fantom, linea, mainnet, mantle, optimism, polygon, ronin, scroll, sonic, unichain, zksync, celo, monad, } from 'viem/chains'; import { CROSS_CHAIN_FEE_RECEIVER, MAINNET_NETWORKS, ZERO_ADDRESS, } from '../constants/index.js'; import { BaseSwapAdapter, NOT_SUPPORTED_CHAINS_PRICE_SERVICE, NonEvmChain, NewEvmChain, } from './BaseSwapAdapter.js'; import { defineChain } from 'viem'; import { ChainId } from '../../index.js'; const hyperEvm = defineChain({ id: 999, name: 'HyperEvm', network: 'hyperevm', nativeCurrency: { decimals: 18, name: 'HYPE', symbol: 'HYPE', }, rpcUrls: { default: { http: ['https://rpc.hyperliquid.xyz/evm'], }, public: { http: ['https://rpc.hyperliquid.xyz/evm'], }, }, blockExplorers: { default: { name: 'Explorer', url: 'https://hyperevmscan.io' }, }, }); const plasma = defineChain({ id: 9745, name: 'Plasma', blockTime: 1000, nativeCurrency: { name: 'Plasma', symbol: 'XPL', decimals: 18, }, rpcUrls: { default: { http: ['https://rpc.plasma.to'], }, }, blockExplorers: { default: { name: 'PlasmaScan', url: 'https://plasmascan.to', }, }, contracts: { multicall3: { address: '0xcA11bde05977b3631167028862bE2a173976CA11', blockCreated: 0, }, }, }); const SolanaChainId = 792703809; const BitcoinChainId = 8253038; const BitcoinAddress = 'bc1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqmql8k8'; const solanaChain = { id: SolanaChainId, name: 'Solana', displayName: 'Solana', vmType: 'svm', }; const bitcoinChain = { id: BitcoinChainId, name: 'Bitcoin', displayName: 'Bitcoin', vmType: 'bvm', }; export class RelayAdapter extends BaseSwapAdapter { constructor() { super(); createClient({ baseApiUrl: MAINNET_RELAY_API, source: 'openocean', chains: [ arbitrum, avalanche, base, berachain, blast, bsc, fantom, linea, mainnet, mantle, optimism, polygon, scroll, sonic, zksync, ronin, unichain, hyperEvm, plasma, celo, monad, ] .map(convertViemChainToRelayChain) .concat(solanaChain, bitcoinChain), }); } getName() { return 'Relay'; } getIcon() { return 'https://storage.googleapis.com/ks-setting-1d682dca/84e906bb-eaeb-45d3-a64c-2aa9c84eb3ea1747759080942.png'; } getSupportedChains() { return [NonEvmChain.Solana, NonEvmChain.Bitcoin, NewEvmChain.Plasma, NewEvmChain.Monad, ...MAINNET_NETWORKS.filter(chain => chain !== ChainId.FLR)]; // return [...MAINNET_NETWORKS] } getSupportedTokens(_sourceChain, _destChain) { return []; } async getQuote(params) { const evmFromToken = params.fromToken; const evmToToken = params.toToken; let currency = evmFromToken.isNative ? ZERO_ADDRESS : evmFromToken.address; let toCurrency = evmToToken.isNative ? ZERO_ADDRESS : evmToToken.address; let chainId = +params.fromChain; if (params.fromChain === NonEvmChain.Solana) { chainId = SolanaChainId; } else if (params.fromChain === NonEvmChain.Bitcoin) { chainId = BitcoinChainId; currency = BitcoinAddress; } let toChainId = +params.toChain; if (params.toChain === NonEvmChain.Solana) { toChainId = SolanaChainId; } else if (params.toChain === NonEvmChain.Bitcoin) { toChainId = BitcoinChainId; toCurrency = BitcoinAddress; } const quoteParams = { chainId: chainId, toChainId: toChainId, currency, toCurrency, amount: params.amount, tradeType: 'EXACT_INPUT', wallet: params.walletClient, recipient: params.recipient, user: params.sender, options: { appFees: [ { recipient: CROSS_CHAIN_FEE_RECEIVER, // recipient: // params.fromChain === NonEvmChain.Solana // ? CROSS_CHAIN_FEE_RECEIVER_SOLANA // : CROSS_CHAIN_FEE_RECEIVER, fee: params.feeBps.toString(), }, ], protocolVersion: 'preferV2', // includedSwapSources: ['open-ocean'], }, }; const resp = await getClient().actions.getQuote(quoteParams); const formattedOutputAmount = formatUnits(BigInt(resp.details?.currencyOut?.amount || '0'), params.toToken.decimals); const formattedInputAmount = formatUnits(BigInt(params.amount), params.fromToken.decimals); const inputUsd = NOT_SUPPORTED_CHAINS_PRICE_SERVICE.includes(params.fromChain) ? Number(resp.details?.currencyIn?.amountUsd || 0) : params.tokenInUsd * +formattedInputAmount; const outputUsd = NOT_SUPPORTED_CHAINS_PRICE_SERVICE.includes(params.toChain) ? Number(resp.details?.currencyOut?.amountUsd || 0) : params.tokenOutUsd * +formattedOutputAmount; if (params.walletClient) { params.walletClient = { account: { address: params.sender, }, }; } const protocolFee = resp.fees?.relayer?.amountUsd || 0; return { quoteParams: params, outputAmount: BigInt(resp.details?.currencyOut?.amount || '0'), formattedOutputAmount, inputUsd, outputUsd, priceImpact: !inputUsd || !outputUsd ? Number.NaN : ((inputUsd - outputUsd) * 100) / inputUsd, //rate: Number(resp.details?.rate || 0), rate: +formattedOutputAmount / +formattedInputAmount, gasFeeUsd: Number(resp.fees?.gas?.amountUsd || 0), timeEstimate: resp.details?.timeEstimate || 0, // Relay dont need to approve, we send token to contract directly contractAddress: ZERO_ADDRESS, rawQuote: resp, protocolFee: protocolFee, platformFeePercent: (params.feeBps * 100) / 10000, }; } async executeSwap({ quote }, walletClient) { return new Promise((resolve, reject) => { getClient() .actions.execute({ quote: quote.rawQuote, wallet: walletClient, onProgress: ({ currentStep }) => { if (currentStep?.id === 'deposit' && currentStep.requestId && currentStep.kind === 'transaction') { const txHash = currentStep.items?.[0]?.txHashes?.[0].txHash; if (txHash) { resolve({ sender: quote.quoteParams.sender, sourceTxHash: txHash, adapter: this.getName(), id: currentStep.requestId, sourceChain: quote.quoteParams.fromChain, targetChain: quote.quoteParams.toChain, inputAmount: quote.quoteParams.amount, outputAmount: quote.outputAmount.toString(), sourceToken: quote.quoteParams.fromToken, targetToken: quote.quoteParams.toToken, timestamp: new Date().getTime(), }); } } }, }) .catch((e) => { reject(e); // Make sure errors from execute are also caught }); }); } async getTransactionStatus(p) { const res = await fetch(`https://api.relay.link/intents/status/v2?requestId=${p.id}`).then((r) => r.json()); return { txHash: res.txHashes?.[0] || '', status: res.status === 'success' ? 'Success' : res.status === 'refund' ? 'Refunded' : res.status === 'failure' ? 'Failed' : 'Processing', }; } } //# sourceMappingURL=RelayAdapter.js.map