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": ""
}
}