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.

227 lines (209 loc) 7.81 kB
interface OpenOceanToken { address: string symbol: string decimals: number name: string icon?: string usd?: string chainId?: number } export class OpenOceanService { // private static readonly API_V1_URL = 'https://ethapi.openocean.finance/v1' //private static readonly API_V2_URL = 'https://ethapi.openocean.finance/v2' private static readonly API_V3_URL = 'https://open-api.openocean.finance/v3' private static readonly API_V4_URL = 'https://open-api.openocean.finance/v4' // Chain ID to OpenOcean chain name mapping private static readonly CHAIN_ID_MAP: Record<string | number, string> = { 1151111081099710: 'solana', // Solana mainnet } // Get OpenOcean supported chain name private static getChainName(chainId: string | number): string { return this.CHAIN_ID_MAP[chainId] || chainId.toString() } // Get API URL based on chain ID private static getApiUrl(chainId: string | number): string { // If chainId exists in CHAIN_ID_MAP, use V1 API const chainName = this.getChainName(chainId) return Object.keys(this.CHAIN_ID_MAP).includes(chainId.toString()) ? `${this.API_V4_URL}/${chainName}` : `${this.API_V4_URL}/${chainId}` } private static getSolanaAddress(chain: string, tokenAddress: string): string { if (chain === '1151111081099710' && tokenAddress === 'So11111111111111111111111111111111111111112') { return '11111111111111111111111111111111' } else if (chain === '1151111081099710' && tokenAddress === '11111111111111111111111111111111') { return 'So11111111111111111111111111111111111111112' } return tokenAddress } static async getQuote(params: { chain: string inTokenAddress: string inTokenSymbol: string outTokenAddress: string outTokenSymbol: string amount: string slippage?: string gasPrice?: string disabledDexIds?: string enabledDexIds?: string referrer?: string }) { const apiUrl = this.getApiUrl(params.chain) const slippage = (Number(params.slippage || 0.01)).toString(); const queryParams = new URLSearchParams({ quoteType: 'quote', inTokenSymbol: params.inTokenSymbol, inTokenAddress: params.inTokenAddress, outTokenSymbol: params.outTokenSymbol, outTokenAddress: params.outTokenAddress, amountDecimals: params.amount, slippage, gasPriceDecimals: params.gasPrice || '', disabledDexIds: params.disabledDexIds || '', enabledDexIds: params.enabledDexIds || '', referrer: params.referrer || '0x3487ef9f9b36547e43268b8f0e2349a226c70b53', }) const response = await fetch(`${apiUrl}/quote?${queryParams.toString()}`) return response.json() } static async getSwapQuote(params: { chain: string inTokenAddress: string inTokenSymbol: string outTokenAddress: string outTokenSymbol: string amount: string account: string slippage?: string gasPrice?: string disabledDexIds?: string enabledDexIds?: string referrer?: string, referrerFee?: string, referrerFeeShare?: string }) { const apiUrl = this.getApiUrl(params.chain) const slippage = (Number(params.slippage || 0.01)).toString(); const referrer: any = { referrer: params.referrer || '0x3487ef9f9b36547e43268b8f0e2349a226c70b53', } if (params.referrerFee) { referrer.referrerFee = Number(params.referrerFee); // referrer.referrerFeeShare = params.referrerFeeShare || '1500'; } const queryParams = new URLSearchParams({ quoteType: 'swap', inTokenSymbol: params.inTokenSymbol, inTokenAddress: this.getSolanaAddress(params.chain, params.inTokenAddress), outTokenSymbol: params.outTokenSymbol, outTokenAddress: this.getSolanaAddress(params.chain, params.outTokenAddress), amountDecimals: params.amount, account: params.account, slippage, gasPriceDecimals: params.gasPrice || '', disabledDexIds: params.disabledDexIds || '', enabledDexIds: params.enabledDexIds || '', ...referrer, }) // const isV1Api = Object.keys(this.CHAIN_ID_MAP).includes(params.chain.toString()) // const swapEndpoint = isV1Api ? 'swap-quote' : 'swap' const response = await fetch(`${apiUrl}/swap?${queryParams.toString()}`) return response.json() } static async getTokenList(chain: string) { const chainName = this.getChainName(chain) const response = await fetch(`${this.API_V4_URL}/${chainName}/tokenList`) const data = await response.json() if (data.code !== 200) { throw new Error('Failed to fetch token list') } return data.data.map((token: OpenOceanToken) => { let address = token.address // Convert Solana native token address if (chain === '1151111081099710' && address === 'So11111111111111111111111111111111111111112') { address = '11111111111111111111111111111111' } // Convert Base chain native token address if (address === '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE') { address = '0x0000000000000000000000000000000000000000' } return { address, symbol: token.symbol, decimals: token.decimals, name: token.name, logoURI: token.icon, priceUSD: token.usd, chainId: Number(chain), amount: 0n, featured: false, popular: false } }) } static async getDexList(chain: string) { const apiUrl = this.getApiUrl(chain) const response = await fetch(`${apiUrl}/dexList`) return response.json() } static async getGasPrice(chain: string) { if (!chain || chain === '1151111081099710') { return { data: { gasPrice: '1000000000000000000', }, } } // const apiUrl = this.getApiUrl(chain) const response = await fetch(`${this.API_V4_URL}/${chain}/gasPrice`) const { data } = await response.json() return data } static async getTokenInfo(chain: string, tokenAddress: string) { const chainName = this.getChainName(chain) // Check if the address is valid if (!tokenAddress || (!/^0x[a-fA-F0-9]{40}$/.test(tokenAddress) && !/^[1-9A-HJ-NP-Za-km-z]{32,44}$/.test(tokenAddress))) { throw new Error('Invalid token address') } const response = await fetch(`${this.API_V4_URL}/${chainName}/getTokenInfo?tokenAddress=${tokenAddress}`) const data = await response.json() if (!data || !data.address || !data.symbol || !data.decimals) { throw new Error('Failed to fetch token info') } return { address: data.address, symbol: data.symbol, decimals: data.decimals, name: data.name, logoURI: data.icon, priceUSD: data.usd, chainId: Number(chain), amount: 0n, featured: false, popular: false } } /** * Get prices for specified tokens * @param chain chain name, e.g. 'arbitrum' * @param tokenAddresses array of token addresses * @returns Promise<Record<string, string>> returns an object where key is token address and value is price */ static async getTokensPrice(chain: string, tokenAddresses: string[]): Promise<Record<string, string>> { const chainName = this.getChainName(chain) const tokenAddressesStr = tokenAddresses.join(',') const response = await fetch(`${this.API_V3_URL}/${chainName}/designated_tokenList?tokens=${tokenAddressesStr}`) const data = await response.json() if (data.code !== 200) { throw new Error('Failed to fetch token prices') } const prices = data.data.reduce((acc: Record<string, string>, token: OpenOceanToken) => { if (token.address && token.usd) { acc[token.address.toLowerCase()] = token.usd } return acc }, {}) return prices } }