diff --git a/cspell.json b/cspell.json index 069b320a239f..559697c3796f 100644 --- a/cspell.json +++ b/cspell.json @@ -289,6 +289,8 @@ "crwdns", "dodoapi", "getdodoroute", + "openocean", + "OPENOCEAN", "getx", "hookform", "idiv", diff --git a/packages/dashboard/src/pages/Labs/components/SwapSettingDialog.tsx b/packages/dashboard/src/pages/Labs/components/SwapSettingDialog.tsx index 2458bfd7d0c8..d75de60756ce 100644 --- a/packages/dashboard/src/pages/Labs/components/SwapSettingDialog.tsx +++ b/packages/dashboard/src/pages/Labs/components/SwapSettingDialog.tsx @@ -29,6 +29,7 @@ export default function SwapSettingDialog({ open, onClose }: SettingDialogProps) { label: 'Balancer', value: TradeProvider.BALANCER }, { label: 'DODO', value: TradeProvider.DODO }, { label: 'Bancor', value: TradeProvider.BANCOR }, + { label: 'OpenOcean', value: TradeProvider.OPENOCEAN }, ] const polygonOptions = [ @@ -36,6 +37,7 @@ export default function SwapSettingDialog({ open, onClose }: SettingDialogProps) { label: 'SushiSwap', value: TradeProvider.SUSHISWAP }, { label: '0x', value: TradeProvider.ZRX }, { label: 'DODO', value: TradeProvider.DODO }, + { label: 'OpenOcean', value: TradeProvider.OPENOCEAN }, ] const bscOptions = [ @@ -43,11 +45,18 @@ export default function SwapSettingDialog({ open, onClose }: SettingDialogProps) { label: 'SushiSwap', value: TradeProvider.SUSHISWAP }, { label: '0x', value: TradeProvider.ZRX }, { label: 'DODO', value: TradeProvider.DODO }, + { label: 'OpenOcean', value: TradeProvider.OPENOCEAN }, ] - const arbitrumOptions = [{ label: 'UniSwap V3', value: TradeProvider.UNISWAP_V3 }] + const arbitrumOptions = [ + { label: 'UniSwap V3', value: TradeProvider.UNISWAP_V3 }, + { label: 'OpenOcean', value: TradeProvider.OPENOCEAN }, + ] - const xDaiOptions = [{ label: 'SushiSwap', value: TradeProvider.SUSHISWAP }] + const xDaiOptions = [ + { label: 'SushiSwap', value: TradeProvider.SUSHISWAP }, + { label: 'OpenOcean', value: TradeProvider.OPENOCEAN }, + ] const fantomOptions = xDaiOptions const celoOptions = [{ label: 'SushiSwap', value: TradeProvider.SUSHISWAP }] diff --git a/packages/mask/src/plugins/Trader/SNSAdaptor/trader/TradeProviderIcon.tsx b/packages/mask/src/plugins/Trader/SNSAdaptor/trader/TradeProviderIcon.tsx index 1edffc16c22f..422b3e44e89d 100644 --- a/packages/mask/src/plugins/Trader/SNSAdaptor/trader/TradeProviderIcon.tsx +++ b/packages/mask/src/plugins/Trader/SNSAdaptor/trader/TradeProviderIcon.tsx @@ -8,6 +8,7 @@ import { SushiSwapIcon } from '../../../../resources/SushiSwapIcon' import { UniswapIcon } from '../../../../resources/UniswapIcon' import { ZRXIcon } from '../../../../resources/ZRXIcon' import { DODOIcon } from '../../../../resources/DODOIcon' +import { OpenOceanIcon } from '../../../../resources/OpenOceanIcon' import { resolveTradeProviderName } from '../../pipes' const quickswapIcon = new URL('../../../../resources/quickswap.png', import.meta.url).toString() @@ -59,6 +60,8 @@ export function TradeProviderIcon(props: TradeProviderIconProps) { return case TradeProvider.BANCOR: return + case TradeProvider.OPENOCEAN: + return default: unreachable(props.provider) } diff --git a/packages/mask/src/plugins/Trader/apis/index.ts b/packages/mask/src/plugins/Trader/apis/index.ts index 686bb84b62f4..ed4fa7e0fd63 100644 --- a/packages/mask/src/plugins/Trader/apis/index.ts +++ b/packages/mask/src/plugins/Trader/apis/index.ts @@ -6,3 +6,4 @@ export * from './blocks' export * from './LBP' export * from './dodo' export * from './bancor' +export * from './openocean' diff --git a/packages/mask/src/plugins/Trader/apis/openocean/index.ts b/packages/mask/src/plugins/Trader/apis/openocean/index.ts new file mode 100644 index 000000000000..92b8443b25eb --- /dev/null +++ b/packages/mask/src/plugins/Trader/apis/openocean/index.ts @@ -0,0 +1,39 @@ +import type { SwapOOData, SwapOORequest } from '../../types/openocean' +import { OPENOCEAN_BASE_URL } from '../../constants/openocean' +import { leftShift } from '@masknet/web3-shared-base' +import urlcat from 'urlcat' + +export async function swapOO(request: SwapOORequest): Promise { + const response = await fetch( + urlcat(OPENOCEAN_BASE_URL, `/${request.chainId}/swap`, { + inTokenSymbol: request.toToken?.symbol, + inTokenAddress: request.fromToken?.address, + outTokenSymbol: request.toToken?.symbol, + outTokenAddress: request.toToken?.address, + amount: request.fromAmount, + gasPrice: 5000000000, + slippage: request.slippage, + disabledDexIds: '', + account: request.userAddr, + }), + ) + const payload = await response.json() + const { data, outAmount, minOutAmount, to, value } = payload + const _resAmount = leftShift(outAmount, request.toToken.decimals).toNumber() + const _fromAmount = leftShift(request.fromAmount, request.fromToken.decimals).toNumber() + return { + data, + targetApproveAddr: request.fromToken.address, + targetDecimals: request.fromToken.decimals, + resAmount: _resAmount, + fromAmount: _fromAmount, + resPricePerFromToken: +(_fromAmount / _resAmount).toFixed(8), + resPricePerToToken: +(_resAmount / _fromAmount).toFixed(8), + to, + value, + slippage: request.slippage, + fromTokenSymbol: request.fromToken.symbol, + toTokenSymbol: request.toToken.symbol, + minOutAmount, + } as SwapOOData +} diff --git a/packages/mask/src/plugins/Trader/apis/trader/index.ts b/packages/mask/src/plugins/Trader/apis/trader/index.ts index 31a8a099dc16..b6a67b76bb5e 100644 --- a/packages/mask/src/plugins/Trader/apis/trader/index.ts +++ b/packages/mask/src/plugins/Trader/apis/trader/index.ts @@ -16,6 +16,7 @@ export async function getAvailableTraderProviders(chainId: ChainId) { TradeProvider.BALANCER, TradeProvider.DODO, TradeProvider.BANCOR, + TradeProvider.OPENOCEAN, ] case NetworkType.Polygon: return [ @@ -24,13 +25,20 @@ export async function getAvailableTraderProviders(chainId: ChainId) { TradeProvider.SUSHISWAP, TradeProvider.DODO, TradeProvider.ZRX, + TradeProvider.OPENOCEAN, ] case NetworkType.Binance: - return [TradeProvider.PANCAKESWAP, TradeProvider.SUSHISWAP, TradeProvider.DODO, TradeProvider.ZRX] + return [ + TradeProvider.PANCAKESWAP, + TradeProvider.SUSHISWAP, + TradeProvider.DODO, + TradeProvider.ZRX, + TradeProvider.OPENOCEAN, + ] case NetworkType.Arbitrum: - return [TradeProvider.UNISWAP_V3] + return [TradeProvider.UNISWAP_V3, TradeProvider.OPENOCEAN] case NetworkType.xDai: - return [TradeProvider.SUSHISWAP] + return [TradeProvider.SUSHISWAP, TradeProvider.OPENOCEAN] case NetworkType.Celo: return [TradeProvider.SUSHISWAP] case NetworkType.Fantom: diff --git a/packages/mask/src/plugins/Trader/constants/openocean.ts b/packages/mask/src/plugins/Trader/constants/openocean.ts new file mode 100644 index 000000000000..e3412435e2bc --- /dev/null +++ b/packages/mask/src/plugins/Trader/constants/openocean.ts @@ -0,0 +1,13 @@ +import { NetworkType } from '@masknet/web3-shared-evm' + +export const OPENOCEAN_BASE_URL = 'https://ethapi.openocean.finance/v2/' + +export const networkNames: Record = { + [NetworkType.Ethereum]: 'eth', + [NetworkType.Binance]: 'bsc', + [NetworkType.Polygon]: 'polygon', + [NetworkType.Arbitrum]: 'arbitrum', + [NetworkType.xDai]: 'xdai', + [NetworkType.Celo]: 'celo', + [NetworkType.Fantom]: 'fantom', +} diff --git a/packages/mask/src/plugins/Trader/pipes.ts b/packages/mask/src/plugins/Trader/pipes.ts index bf1681c1f8ad..d11709b6e30e 100644 --- a/packages/mask/src/plugins/Trader/pipes.ts +++ b/packages/mask/src/plugins/Trader/pipes.ts @@ -56,6 +56,7 @@ export const resolveTradeProviderName = createLookupTableResolver { throw new Error(`Unknown provider type: ${tradeProvider}`) @@ -102,6 +103,8 @@ export function resolveTradeProviderLink(tradeProvider: TradeProvider, networkTy return 'https://app.dodoex.io' case TradeProvider.BANCOR: return 'https://app.bancor.network/eth/swap' + case TradeProvider.OPENOCEAN: + return 'https://openocean.finance/classic' default: unreachable(tradeProvider) } @@ -157,6 +160,9 @@ export function resolveTradePairLink(tradeProvider: TradeProvider, address: stri case TradeProvider.BANCOR: // TODO - Bancor analytics should be available with V3 return `` + case TradeProvider.OPENOCEAN: + // TODO - OpenOcean + return `` default: unreachable(tradeProvider) } diff --git a/packages/mask/src/plugins/Trader/settings.ts b/packages/mask/src/plugins/Trader/settings.ts index ac704efe6710..13a929c1439d 100644 --- a/packages/mask/src/plugins/Trader/settings.ts +++ b/packages/mask/src/plugins/Trader/settings.ts @@ -103,6 +103,7 @@ const pancakeswapSettings = createInternalSettings(`${PLUGIN_ID}+tradePr const balancerSettings = createInternalSettings(`${PLUGIN_ID}+tradeProvider+balancer`, '') const dodoSettings = createInternalSettings(`${PLUGIN_ID}+tradeProvider+dodo`, '') const bancorSettings = createInternalSettings(`${PLUGIN_ID}+tradeProvider+bancor`, '') +const openoceanSettings = createInternalSettings(`${PLUGIN_ID}+tradeProvider+openocean`, '') /** * The general settings of specific tarde provider @@ -129,6 +130,8 @@ export function getCurrentTradeProviderGeneralSettings(tradeProvider: TradeProvi return dodoSettings case TradeProvider.BANCOR: return bancorSettings + case TradeProvider.OPENOCEAN: + return openoceanSettings default: unreachable(tradeProvider) } diff --git a/packages/mask/src/plugins/Trader/trader/dodo/useTradeGasLimit.ts b/packages/mask/src/plugins/Trader/trader/dodo/useTradeGasLimit.ts index 6173ce860c80..fbd80ff11a33 100644 --- a/packages/mask/src/plugins/Trader/trader/dodo/useTradeGasLimit.ts +++ b/packages/mask/src/plugins/Trader/trader/dodo/useTradeGasLimit.ts @@ -9,8 +9,7 @@ import type { AsyncState } from 'react-use/lib/useAsyncFn' export function useTradeGasLimit(tradeComputed: TradeComputed | null): AsyncState { const { targetChainId } = TargetChainIdContext.useContainer() - - const web3 = useWeb3(false, targetChainId) + const web3 = useWeb3(true, targetChainId) const account = useAccount() const config = useMemo(() => { if (!account || !tradeComputed?.trade_) return null diff --git a/packages/mask/src/plugins/Trader/trader/openocean/useSlippageTolerance.ts b/packages/mask/src/plugins/Trader/trader/openocean/useSlippageTolerance.ts new file mode 100644 index 000000000000..f54692e3703f --- /dev/null +++ b/packages/mask/src/plugins/Trader/trader/openocean/useSlippageTolerance.ts @@ -0,0 +1,9 @@ +import { clamp } from 'lodash-unified' +import { useValueRef } from '@masknet/shared' +import { SLIPPAGE_MAX, SLIPPAGE_MIN } from '../../constants' +import { currentSlippageSettings } from '../../settings' + +export function useSlippageTolerance() { + const slippage_ = useValueRef(currentSlippageSettings) + return clamp(slippage_, SLIPPAGE_MIN, SLIPPAGE_MAX) +} diff --git a/packages/mask/src/plugins/Trader/trader/openocean/useTrade.ts b/packages/mask/src/plugins/Trader/trader/openocean/useTrade.ts new file mode 100644 index 000000000000..960a788ce0eb --- /dev/null +++ b/packages/mask/src/plugins/Trader/trader/openocean/useTrade.ts @@ -0,0 +1,65 @@ +import { + FungibleTokenDetailed, + isNativeTokenAddress, + useAccount, + useBlockNumber, + useRPCConstants, + useTokenConstants, + useTraderConstants, +} from '@masknet/web3-shared-evm' +import { useAsyncRetry } from 'react-use' +import { PluginTraderRPC } from '../../messages' +import type { TradeStrategy } from '../../types' +import { TargetChainIdContext } from '../useTargetChainIdContext' +import { useSlippageTolerance } from './useSlippageTolerance' +import { first } from 'lodash-unified' + +export function useTrade( + strategy: TradeStrategy, + inputAmount: string, + outputAmount: string, + inputToken?: FungibleTokenDetailed, + outputToken?: FungibleTokenDetailed, +) { + const { NATIVE_TOKEN_ADDRESS } = useTokenConstants() + const blockNumber = useBlockNumber() + const slippage = useSlippageTolerance() + const { targetChainId } = TargetChainIdContext.useContainer() + const { RPC } = useRPCConstants(targetChainId) + const providerURL = first(RPC) + const { OPENOCEAN_ETH_ADDRESS } = useTraderConstants(targetChainId) + const account = useAccount() + + return useAsyncRetry(async () => { + if (!inputToken || !outputToken) return null + if (inputAmount === '0') return null + const sellToken = isNativeTokenAddress(inputToken.address) + ? { ...inputToken, address: OPENOCEAN_ETH_ADDRESS ?? '' } + : inputToken + const buyToken = isNativeTokenAddress(outputToken.address) + ? { ...outputToken, address: OPENOCEAN_ETH_ADDRESS ?? '' } + : outputToken + return PluginTraderRPC.swapOO({ + isNativeSellToken: isNativeTokenAddress(inputToken.address), + fromToken: sellToken, + toToken: buyToken, + fromAmount: inputAmount, + slippage, + userAddr: account, + rpc: providerURL, + chainId: targetChainId, + }) + }, [ + NATIVE_TOKEN_ADDRESS, + strategy, + inputAmount, + outputAmount, + inputToken?.address, + outputToken?.address, + slippage, + blockNumber, // refresh api each block + account, + providerURL, + targetChainId, + ]) +} diff --git a/packages/mask/src/plugins/Trader/trader/openocean/useTradeCallback.ts b/packages/mask/src/plugins/Trader/trader/openocean/useTradeCallback.ts new file mode 100644 index 000000000000..38d729d0a6bd --- /dev/null +++ b/packages/mask/src/plugins/Trader/trader/openocean/useTradeCallback.ts @@ -0,0 +1,89 @@ +import { useCallback, useMemo, useState } from 'react' +import stringify from 'json-stable-stringify' +import { pick } from 'lodash-unified' +import type { TransactionConfig } from 'web3-core' +import { + TransactionState, + TransactionStateType, + useAccount, + useChainId, + useWeb3, + GasOptionConfig, +} from '@masknet/web3-shared-evm' +import type { SwapOOSuccessResponse, TradeComputed } from '../../types' + +export function useTradeCallback( + tradeComputed: TradeComputed | null, + gasConfig?: GasOptionConfig, +) { + const web3 = useWeb3() + const account = useAccount() + const chainId = useChainId() + const [tradeState, setTradeState] = useState({ + type: TransactionStateType.UNKNOWN, + }) + + // compose transaction config + const config = useMemo(() => { + if (!account || !tradeComputed?.trade_) return null + return { + from: account, + ...pick(tradeComputed.trade_, ['to', 'data', 'value']), + } as TransactionConfig + }, [account, tradeComputed]) + + const tradeCallback = useCallback(async () => { + // validate config + if (!account || !config) { + setTradeState({ + type: TransactionStateType.UNKNOWN, + }) + return + } + + // start waiting for provider to confirm tx + setTradeState({ + type: TransactionStateType.WAIT_FOR_CONFIRMING, + }) + + // compose transaction config + const config_ = { + ...config, + gas: await web3.eth.estimateGas(config).catch((error) => { + setTradeState({ + type: TransactionStateType.FAILED, + error, + }) + throw error + }), + ...gasConfig, + } + + // send transaction and wait for hash + return new Promise((resolve, reject) => { + web3.eth.sendTransaction(config_, (error, hash) => { + if (error) { + setTradeState({ + type: TransactionStateType.FAILED, + error, + }) + reject(error) + } else { + setTradeState({ + type: TransactionStateType.HASH, + hash, + }) + resolve(hash) + } + }) + }) + }, [web3, account, chainId, stringify(config)]) + + const resetCallback = useCallback(() => { + setTradeState({ + type: TransactionStateType.UNKNOWN, + }) + }, []) + + return [tradeState, tradeCallback, resetCallback] as const +} diff --git a/packages/mask/src/plugins/Trader/trader/openocean/useTradeComputed.ts b/packages/mask/src/plugins/Trader/trader/openocean/useTradeComputed.ts new file mode 100644 index 000000000000..f91027c64583 --- /dev/null +++ b/packages/mask/src/plugins/Trader/trader/openocean/useTradeComputed.ts @@ -0,0 +1,38 @@ +import BigNumber from 'bignumber.js' +import { useMemo } from 'react' +import type { FungibleTokenDetailed } from '@masknet/web3-shared-evm' +import { ZERO, pow10 } from '@masknet/web3-shared-base' +import { SwapOOData, TradeComputed, TradeStrategy } from '../../types' + +export function useTradeComputed( + trade: SwapOOData | null, + strategy: TradeStrategy, + inputToken?: FungibleTokenDetailed, + outputToken?: FungibleTokenDetailed, +) { + return useMemo(() => { + if (!trade) return null + if (!inputToken || !outputToken) return null + const isExactIn = strategy === TradeStrategy.ExactIn + if (!isExactIn) return null + const inputAmount = new BigNumber(trade.fromAmount).multipliedBy(pow10(inputToken.decimals)).integerValue() + const executionPrice = new BigNumber(trade.resPricePerToToken) + const outputAmount = new BigNumber(trade.resAmount).multipliedBy(pow10(outputToken.decimals)).integerValue() + const priceImpact = new BigNumber(trade.priceImpact) + return { + strategy, + inputToken, + outputToken, + inputAmount, + outputAmount, + executionPrice, + fee: ZERO, + maximumSold: inputAmount, + minimumReceived: new BigNumber(trade.minOutAmount), + // minimumProtocolFee + priceImpact, + + trade_: { ...trade }, + } as TradeComputed + }, [trade, strategy, inputToken, outputToken]) +} diff --git a/packages/mask/src/plugins/Trader/trader/openocean/useTradeGasLimit.ts b/packages/mask/src/plugins/Trader/trader/openocean/useTradeGasLimit.ts new file mode 100644 index 000000000000..6173ce860c80 --- /dev/null +++ b/packages/mask/src/plugins/Trader/trader/openocean/useTradeGasLimit.ts @@ -0,0 +1,27 @@ +import type { SwapRouteData, TradeComputed } from '../../types' +import { useMemo } from 'react' +import { useAccount, useWeb3 } from '@masknet/web3-shared-evm' +import { TargetChainIdContext } from '../useTargetChainIdContext' +import { pick } from 'lodash-unified' +import type { TransactionConfig } from 'web3-core' +import { useAsync } from 'react-use' +import type { AsyncState } from 'react-use/lib/useAsyncFn' + +export function useTradeGasLimit(tradeComputed: TradeComputed | null): AsyncState { + const { targetChainId } = TargetChainIdContext.useContainer() + + const web3 = useWeb3(false, targetChainId) + const account = useAccount() + const config = useMemo(() => { + if (!account || !tradeComputed?.trade_) return null + return { + from: account, + ...pick(tradeComputed.trade_, ['to', 'data', 'value']), + } as TransactionConfig + }, [account, tradeComputed]) + + return useAsync(async () => { + if (!config) return 0 + return web3.eth.estimateGas(config) + }, [config, web3]) +} diff --git a/packages/mask/src/plugins/Trader/trader/useAllTradeComputed.ts b/packages/mask/src/plugins/Trader/trader/useAllTradeComputed.ts index f9e0745096b5..5e4400bde41e 100644 --- a/packages/mask/src/plugins/Trader/trader/useAllTradeComputed.ts +++ b/packages/mask/src/plugins/Trader/trader/useAllTradeComputed.ts @@ -18,10 +18,13 @@ import { useTradeGasLimit as useDODOTradeGasLimit } from './dodo/useTradeGasLimi import { useTrade as useBancorTrade } from './bancor/useTrade' import { useTradeComputed as useBancorTradeComputed } from './bancor/useTradeComputed' import { useTradeGasLimit as useBancorTradeGasLimit } from './bancor/useTradeGasLimit' +import { useTradeComputed as useOpenOceanTradeComputed } from './openocean/useTradeComputed' +import { useTrade as useOpenOceanTrade } from './openocean/useTrade' import { TradeProvider } from '@masknet/public-api' import { useAvailableTraderProviders } from '../trending/useAvailableTraderProviders' import { useNativeTradeGasLimit } from './useNativeTradeGasLimit' import { TargetChainIdContext } from './useTargetChainIdContext' +import type { TradeComputed, SwapRouteData } from '../types' export function useAllTradeComputed( inputAmount: string, @@ -174,6 +177,16 @@ export function useAllTradeComputed( const bancor = useBancorTradeComputed(bancor_.value ?? null, TradeStrategy.ExactIn, inputToken, outputToken) const bancorSwapEstimateGas = useBancorTradeGasLimit(bancor) + // openocean + const openocean_ = useOpenOceanTrade(TradeStrategy.ExactIn, inputAmount_, '0', inputToken, outputToken) + const openocean = useOpenOceanTradeComputed( + openocean_.value ?? null, + TradeStrategy.ExactIn, + inputToken, + outputToken, + ) + const openoceanSwapEstimateGas = useDODOTradeGasLimit(openocean as TradeComputed | null) + const allTradeResult = [ { provider: TradeProvider.UNISWAP_V2, ...uniswapV2_, value: uniswapV2, gas: uniswapV2EstimateGas }, { provider: TradeProvider.SUSHISWAP, ...sushiSwap_, value: sushiSwap, gas: sushiSwapEstimateGas }, @@ -185,6 +198,7 @@ export function useAllTradeComputed( { provider: TradeProvider.BALANCER, ...balancer_, value: balancer, gas: balancerSwapEstimateGas }, { provider: TradeProvider.DODO, ...dodo_, value: dodo, gas: dodoSwapEstimateGas }, { provider: TradeProvider.BANCOR, ...bancor_, value: bancor, gas: bancorSwapEstimateGas }, + { provider: TradeProvider.OPENOCEAN, ...openocean_, value: openocean, gas: openoceanSwapEstimateGas }, ] return nativeToken_.value diff --git a/packages/mask/src/plugins/Trader/trader/useGetTradeContext.ts b/packages/mask/src/plugins/Trader/trader/useGetTradeContext.ts index c0eb7e4df671..b9eea9226d02 100644 --- a/packages/mask/src/plugins/Trader/trader/useGetTradeContext.ts +++ b/packages/mask/src/plugins/Trader/trader/useGetTradeContext.ts @@ -113,6 +113,11 @@ export function useGetTradeContext(tradeProvider?: TradeProvider) { TYPE: tradeProvider, ROUTER_CONTRACT_ADDRESS: getTraderConstants(chainId).BANCOR_EXCHANGE_PROXY_ADDRESS, } + case TradeProvider.OPENOCEAN: + return { + TYPE: tradeProvider, + ROUTER_CONTRACT_ADDRESS: getTraderConstants(chainId).BANCOR_EXCHANGE_PROXY_ADDRESS, + } default: if (tradeProvider) unreachable(tradeProvider) return null diff --git a/packages/mask/src/plugins/Trader/trader/useTradeApproveComputed.ts b/packages/mask/src/plugins/Trader/trader/useTradeApproveComputed.ts index 3a64c6fb201f..8130abcb3e97 100644 --- a/packages/mask/src/plugins/Trader/trader/useTradeApproveComputed.ts +++ b/packages/mask/src/plugins/Trader/trader/useTradeApproveComputed.ts @@ -24,6 +24,8 @@ export function useTradeApproveComputed( approveAddress: (() => { if (context?.TYPE === TradeProvider.ZRX) return trade?.trade_ ? (trade.trade_ as SwapQuoteResponse).allowanceTarget : '' + if (context?.TYPE === TradeProvider.OPENOCEAN) + return trade?.trade_ ? (trade?.trade_ as SwapQuoteResponse).to : '' return context?.ROUTER_CONTRACT_ADDRESS ?? '' })(), } diff --git a/packages/mask/src/plugins/Trader/trader/useTradeCallback.ts b/packages/mask/src/plugins/Trader/trader/useTradeCallback.ts index 5994a0906bcd..3065bf857b6f 100644 --- a/packages/mask/src/plugins/Trader/trader/useTradeCallback.ts +++ b/packages/mask/src/plugins/Trader/trader/useTradeCallback.ts @@ -9,6 +9,7 @@ import type { SwapBancorRequest, SwapRouteSuccessResponse, TradeComputed, + SwapOOSuccessResponse, } from '../types' import { useTradeCallback as useNativeTokenWrapperCallback } from './native/useTradeCallback' import { useTradeCallback as useZrxCallback } from './0x/useTradeCallback' @@ -16,6 +17,7 @@ import { useTradeCallback as useUniswapCallback } from './uniswap/useTradeCallba import { useTradeCallback as useBalancerCallback } from './balancer/useTradeCallback' import { useTradeCallback as useDODOCallback } from './dodo/useTradeCallback' import { useTradeCallback as useBancorCallback } from './bancor/useTradeCallback' +import { useTradeCallback as useOpenOceanCallback } from './openocean/useTradeCallback' import { useExchangeProxyContract } from '../contracts/balancer/useExchangeProxyContract' import type { NativeTokenWrapper } from './native/useTradeComputed' import { isNativeTokenWrapper } from '../helpers' @@ -48,7 +50,9 @@ export function useTradeCallback( ? (tradeComputed as TradeComputed) : null const tradeComputedForBancor = !isNativeTokenWrapper_ ? (tradeComputed as TradeComputed) : null - + const tradeComputedForOpenOcean = !isNativeTokenWrapper_ + ? (tradeComputed as TradeComputed) + : null // uniswap like providers const uniswapV2Like = useUniswapCallback(tradeComputedForUniswapV2Like, provider, gasConfig) const uniswapV3Like = useUniswapCallback(tradeComputedForUniswapV3Like, provider, gasConfig) @@ -66,6 +70,10 @@ export function useTradeCallback( const zrx = useZrxCallback(provider === TradeProvider.ZRX ? tradeComputedForZRX : null, gasConfig) const dodo = useDODOCallback(provider === TradeProvider.DODO ? tradeComputedForDODO : null, gasConfig) const bancor = useBancorCallback(provider === TradeProvider.BANCOR ? tradeComputedForBancor : null, gasConfig) + const openocean = useOpenOceanCallback( + provider === TradeProvider.OPENOCEAN ? tradeComputedForOpenOcean : null, + gasConfig, + ) // the trade is an ETH-WETH pair const nativeTokenWrapper = useNativeTokenWrapperCallback( @@ -96,6 +104,8 @@ export function useTradeCallback( return dodo case TradeProvider.BANCOR: return bancor + case TradeProvider.OPENOCEAN: + return openocean default: if (provider) unreachable(provider) return [] diff --git a/packages/mask/src/plugins/Trader/trader/useTradeContext.ts b/packages/mask/src/plugins/Trader/trader/useTradeContext.ts index b5b6dfc7f350..6aa3cdfb1df1 100644 --- a/packages/mask/src/plugins/Trader/trader/useTradeContext.ts +++ b/packages/mask/src/plugins/Trader/trader/useTradeContext.ts @@ -114,6 +114,11 @@ export function useTradeContext(tradeProvider: TradeProvider) { TYPE: tradeProvider, ROUTER_CONTRACT_ADDRESS: getTraderConstants(chainId).BANCOR_EXCHANGE_PROXY_ADDRESS, } + case TradeProvider.OPENOCEAN: + return { + TYPE: tradeProvider, + ROUTER_CONTRACT_ADDRESS: getTraderConstants(chainId).OPENOCEAN_EXCHANGE_PROXY_ADDRESS, + } default: unreachable(tradeProvider) } diff --git a/packages/mask/src/plugins/Trader/types/index.ts b/packages/mask/src/plugins/Trader/types/index.ts index 776dc3316315..bcb516e55046 100644 --- a/packages/mask/src/plugins/Trader/types/index.ts +++ b/packages/mask/src/plugins/Trader/types/index.ts @@ -5,3 +5,4 @@ export * from './dodo' export * from './trader' export * from './trending' export * from './bancor' +export * from './openocean' diff --git a/packages/mask/src/plugins/Trader/types/openocean.ts b/packages/mask/src/plugins/Trader/types/openocean.ts new file mode 100644 index 000000000000..5875abdf78f8 --- /dev/null +++ b/packages/mask/src/plugins/Trader/types/openocean.ts @@ -0,0 +1,45 @@ +import type { FungibleTokenDetailed } from '@masknet/web3-shared-evm' + +export interface SwapOORequest { + isNativeSellToken: boolean + fromToken: FungibleTokenDetailed + toToken: FungibleTokenDetailed + fromAmount: string + slippage: number + userAddr?: string + rpc?: string + chainId: number +} + +export type SwapOOResponse = SwapOOSuccessResponse | SwapOOErrorResponse + +export interface SwapOOSuccessResponse { + code: number + status: 200 + data: SwapOOData +} + +export interface SwapOOErrorResponse { + code: number + status: number + data: string | undefined +} + +export interface SwapOOData { + data: string + priceImpact: number + resAmount: number + resCostGas: number + resPricePerFromToken: number + resPricePerToToken: number + targetApproveAddr: string + targetDecimals: number + to: string + useSource: string + fromAmount: number + value: string + slippage: number + fromTokenSymbol: string + toTokenSymbol: string + minOutAmount: number +} diff --git a/packages/mask/src/resources/OpenOceanIcon.tsx b/packages/mask/src/resources/OpenOceanIcon.tsx new file mode 100644 index 000000000000..8af955de587e --- /dev/null +++ b/packages/mask/src/resources/OpenOceanIcon.tsx @@ -0,0 +1,16 @@ +import { SvgIconProps, SvgIcon } from '@mui/material' + +const svg = ( + + + + + +) + +export function OpenOceanIcon(props: SvgIconProps) { + return {svg} +} diff --git a/packages/public-api/src/web.ts b/packages/public-api/src/web.ts index 6d2188798098..903942514359 100644 --- a/packages/public-api/src/web.ts +++ b/packages/public-api/src/web.ts @@ -212,6 +212,7 @@ export enum TradeProvider { DODO = 7, UNISWAP_V3 = 8, BANCOR = 9, + OPENOCEAN = 10, } /** Supported language settings */ export enum LanguageOptions { diff --git a/packages/web3-constants/evm/trader.json b/packages/web3-constants/evm/trader.json index bf22a080de1c..6341747d9713 100644 --- a/packages/web3-constants/evm/trader.json +++ b/packages/web3-constants/evm/trader.json @@ -510,5 +510,38 @@ "xDai": "", "Celo": "", "Fantom": "" + }, + "OPENOCEAN_ETH_ADDRESS": { + "Mainnet": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "Ropsten": "", + "Rinkeby": "", + "Kovan": "", + "Gorli": "", + "BSC": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "BSCT": "", + "Matic": "0x0000000000000000000000000000000000001010", + "Mumbai": "", + "Arbitrum": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "Arbitrum_Rinkeby": "", + "xDai": "0x0000000000000000000000000000000000000000", + "Celo": "", + "Fantom": "" + }, + "OPENOCEAN_EXCHANGE_PROXY_ADDRESS": { + "Mainnet": "0x6352a56caadC4F1E25CD6c75970Fa768A3304e64", + "Ropsten": "", + "Rinkeby": "", + "Kovan": "", + "Gorli": "", + "BSC": "0x6352a56caadC4F1E25CD6c75970Fa768A3304e64", + "BSCT": "", + "Matic": "0x6352a56caadc4f1e25cd6c75970fa768a3304e64", + "Mumbai": "", + "Arbitrum": "0x6352a56caadc4f1e25cd6c75970fa768a3304e64", + "Arbitrum_Rinkeby": "", + "xDai": "0x6352a56caadC4F1E25CD6c75970Fa768A3304e64", + "xDai": "", + "Celo": "", + "Fantom": "" } }