Skip to content

Commit

Permalink
feat(protocol-fees): arb1 protocol fee (#5055)
Browse files Browse the repository at this point in the history
* feat: feature flags can also be numbers

* feat: add arb1 stable coins

* feat: apply a/b testing fee to arb1

* chore: remove exports

* fix: fix getDoNotQueryStatusEndpoint only accepting a boolean

* feat: return fee even if not connected when percentage set to 100%

* feat: add token logos for arb1 stablecoins

* chore: added todo to remove gnosis chain feature flag

* refactor: use a set for quicker lookup instead of array

* chore: fix set usage

---------

Co-authored-by: Alexandr Kazachenko <[email protected]>
  • Loading branch information
alfetopito and shoom3301 authored Oct 31, 2024
1 parent 0fbb9b5 commit ed176c3
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ export function useOrderProgressBarV2Props(chainId: SupportedChainId, order: Ord
const showCancellationModal = useMemo(
// Sort of duplicate cancellation logic since ethflow on creating state don't have progress bar props
() => progressBarV2Props?.showCancellationModal || (order && getCancellation ? getCancellation(order) : null),
[progressBarV2Props?.showCancellationModal, order, getCancellation]
[progressBarV2Props?.showCancellationModal, order, getCancellation],
)
const surplusData = useGetSurplusData(order)
const receiverEnsName = useENS(order?.receiver).name || undefined
Expand All @@ -87,7 +87,7 @@ export function useOrderProgressBarV2Props(chainId: SupportedChainId, order: Ord
}

function useOrderBaseProgressBarV2Props(
params: UseOrderProgressBarPropsParams
params: UseOrderProgressBarPropsParams,
): UseOrderProgressBarV2Result | undefined {
const { activityDerivedState, chainId } = params

Expand Down Expand Up @@ -129,7 +129,7 @@ function useOrderBaseProgressBarV2Props(
const solversInfo = useSolversInfo(chainId)
const totalSolvers = Object.keys(solversInfo).length

const doNotQuery = getDoNotQueryStatusEndpoint(order, apiSolverCompetition, disableProgressBar)
const doNotQuery = getDoNotQueryStatusEndpoint(order, apiSolverCompetition, !!disableProgressBar)

// Local updaters of the respective atom
useBackendApiStatusUpdater(chainId, orderId, doNotQuery)
Expand All @@ -145,7 +145,7 @@ function useOrderBaseProgressBarV2Props(
backendApiStatus,
previousBackendApiStatus,
lastTimeChangedSteps,
previousStepName
previousStepName,
)
useCancellingOrderUpdater(orderId, isCancelling)
useCountdownStartUpdater(orderId, countdown, backendApiStatus)
Expand All @@ -156,7 +156,7 @@ function useOrderBaseProgressBarV2Props(
?.map((entry) => mergeSolverData(entry, solversInfo))
// Reverse it since backend returns the solutions ranked ascending. Winner is the last one.
.reverse(),
[apiSolverCompetition, solversInfo]
[apiSolverCompetition, solversInfo],
)

return useMemo(() => {
Expand Down Expand Up @@ -184,7 +184,7 @@ function useOrderBaseProgressBarV2Props(
function getDoNotQueryStatusEndpoint(
order: Order | undefined,
apiSolverCompetition: CompetitionOrderStatus['value'] | undefined,
disableProgressBar: boolean
disableProgressBar: boolean,
) {
return (
!!(
Expand Down Expand Up @@ -224,7 +224,7 @@ function useSetExecutingOrderProgressBarStepNameCallback() {
function useCountdownStartUpdater(
orderId: string,
countdown: OrderProgressBarState['countdown'],
backendApiStatus: OrderProgressBarState['backendApiStatus']
backendApiStatus: OrderProgressBarState['backendApiStatus'],
) {
const setCountdown = useSetExecutingOrderCountdownCallback()

Expand Down Expand Up @@ -259,7 +259,7 @@ function useProgressBarStepNameUpdater(
backendApiStatus: OrderProgressBarState['backendApiStatus'],
previousBackendApiStatus: OrderProgressBarState['previousBackendApiStatus'],
lastTimeChangedSteps: OrderProgressBarState['lastTimeChangedSteps'],
previousStepName: OrderProgressBarState['previousStepName']
previousStepName: OrderProgressBarState['previousStepName'],
) {
const setProgressBarStepName = useSetExecutingOrderProgressBarStepNameCallback()

Expand All @@ -273,7 +273,7 @@ function useProgressBarStepNameUpdater(
countdown,
backendApiStatus,
previousBackendApiStatus,
previousStepName
previousStepName,
)

// Update state with new step name
Expand Down Expand Up @@ -321,7 +321,7 @@ function getProgressBarStepName(
countdown: OrderProgressBarState['countdown'],
backendApiStatus: OrderProgressBarState['backendApiStatus'],
previousBackendApiStatus: OrderProgressBarState['previousBackendApiStatus'],
previousStepName: OrderProgressBarState['previousStepName']
previousStepName: OrderProgressBarState['previousStepName'],
): OrderProgressBarStepName {
if (isExpired) {
return 'expired'
Expand Down Expand Up @@ -391,7 +391,7 @@ function usePendingOrderStatus(chainId: SupportedChainId, orderId: string, doNot
return useSWR(
chainId && orderId && !doNotQuery ? ['getOrderCompetitionStatus', chainId, orderId] : null,
async ([, _chainId, _orderId]) => getOrderCompetitionStatus(_chainId, _orderId),
doNotQuery ? SWR_NO_REFRESH_OPTIONS : POOLING_SWR_OPTIONS
doNotQuery ? SWR_NO_REFRESH_OPTIONS : POOLING_SWR_OPTIONS,
).data
}

Expand All @@ -404,7 +404,7 @@ function usePendingOrderStatus(chainId: SupportedChainId, orderId: string, doNot
*/
function mergeSolverData(
solverCompetition: ApiSolverCompetition,
solversInfo: Record<string, SolverInfo>
solversInfo: Record<string, SolverInfo>,
): SolverCompetition {
// Backend has the prefix `-solve` on some solvers. We should discard that for now.
// In the future this prefix will be removed.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { atom } from 'jotai'

export const featureFlagsAtom = atom<Record<string, boolean>>({})
export const featureFlagsAtom = atom<Record<string, boolean | number>>({})
Original file line number Diff line number Diff line change
@@ -1,41 +1,69 @@
import { atom } from 'jotai'

import { GNOSIS_CHAIN_STABLECOINS } from '@cowprotocol/common-const'
import { STABLECOINS } from '@cowprotocol/common-const'
import { getCurrencyAddress, isInjectedWidget } from '@cowprotocol/common-utils'
import { SupportedChainId } from '@cowprotocol/cow-sdk'
import { walletInfoAtom } from '@cowprotocol/wallet'

import { derivedTradeStateAtom } from 'modules/trade'

import { featureFlagsAtom } from 'common/state/featureFlagsState'

import { VolumeFee } from '../types'

const COWSWAP_VOLUME_FEES: Record<SupportedChainId, VolumeFee | null> = {
[SupportedChainId.MAINNET]: null,
[SupportedChainId.SEPOLIA]: null,
[SupportedChainId.ARBITRUM_ONE]: null,
// Only Gnosis chain
[SupportedChainId.ARBITRUM_ONE]: {
bps: 10, // 0.1%
recipient: '0x451100Ffc88884bde4ce87adC8bB6c7Df7fACccd', // Arb1 Protocol fee safe
},
[SupportedChainId.GNOSIS_CHAIN]: {
bps: 10, // 0.1%
recipient: '0x6b3214fD11dc91De14718DeE98Ef59bCbFcfB432', // Gnosis Chain Protocol fee safe
},
}

export const cowSwapFeeAtom = atom((get) => {
const { chainId } = get(walletInfoAtom)
const { chainId, account } = get(walletInfoAtom)
const tradeState = get(derivedTradeStateAtom)
const featureFlags = get(featureFlagsAtom)

const { inputCurrency, outputCurrency } = tradeState || {}

// No widget mode
// Don't use it in the widget
if (isInjectedWidget()) return null

// Don't user it when the currencies are not set
if (!inputCurrency || !outputCurrency) return null

const isInputTokenStable = GNOSIS_CHAIN_STABLECOINS.includes(getCurrencyAddress(inputCurrency).toLowerCase())
const isOutputTokenStable = GNOSIS_CHAIN_STABLECOINS.includes(getCurrencyAddress(outputCurrency).toLowerCase())
// TODO: remove this feature flag in another PR
// Don't use it when isCowSwapFeeEnabled is not enabled
if (!featureFlags.isCowSwapFeeEnabled) return null

// Don't use it when on arb1 and shouldn't apply fee based on percentage
if (chainId === SupportedChainId.ARBITRUM_ONE && !shouldApplyFee(account, featureFlags.arb1CowSwapFeePercentage))
return null

const isInputTokenStable = STABLECOINS[chainId].has(getCurrencyAddress(inputCurrency).toLowerCase())
const isOutputTokenStable = STABLECOINS[chainId].has(getCurrencyAddress(outputCurrency).toLowerCase())

// No stable-stable trades
if (isInputTokenStable && isOutputTokenStable) return null

return COWSWAP_VOLUME_FEES[chainId]
})

function shouldApplyFee(account: string | undefined, percentage: number | boolean | undefined): boolean {
// Early exit for 100%, meaning should be enabled for everyone
if (percentage === 100) {
return true
}

// Falsy conditions
if (typeof percentage !== 'number' || !account || percentage < 0 || percentage > 100) {
return false
}

return BigInt(account) % 100n < percentage
}
Loading

0 comments on commit ed176c3

Please sign in to comment.