Skip to content
Merged
35 changes: 35 additions & 0 deletions src/lib/yieldxyz/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
getYieldActionLabelKeys,
getYieldSuccessMessageKey,
isStakingYieldType,
resolveAssetSymbolForTx,
resolveYieldInputAssetIcon,
searchValidators,
searchYields,
Expand Down Expand Up @@ -49,6 +50,40 @@ describe('getTransactionButtonText', () => {
})
})

describe('resolveAssetSymbolForTx', () => {
it('should return output token for exit APPROVAL', () => {
expect(resolveAssetSymbolForTx('APPROVAL', 'exit', 'AVAX', 'sAVAX')).toBe('sAVAX')
expect(resolveAssetSymbolForTx('APPROVE', 'exit', 'AVAX', 'sAVAX')).toBe('sAVAX')
expect(resolveAssetSymbolForTx('APPROVAL', 'exit', 'ETH', 'rETH')).toBe('rETH')
expect(resolveAssetSymbolForTx('APPROVAL', 'exit', 'ETH', 'stETH')).toBe('stETH')
})

it('should return input token for non-APPROVAL exit types', () => {
expect(resolveAssetSymbolForTx('UNSTAKE', 'exit', 'AVAX', 'sAVAX')).toBe('AVAX')
expect(resolveAssetSymbolForTx('WITHDRAW', 'exit', 'USDT', 'cUSDTv3')).toBe('USDT')
expect(resolveAssetSymbolForTx('SWAP', 'exit', 'ETH', 'rETH')).toBe('ETH')
expect(resolveAssetSymbolForTx('EXIT', 'exit', 'ETH', 'stETH')).toBe('ETH')
})

it('should return input token for enter actions', () => {
expect(resolveAssetSymbolForTx('APPROVAL', 'enter', 'USDT', 'cUSDTv3')).toBe('USDT')
expect(resolveAssetSymbolForTx('STAKE', 'enter', 'ETH', 'stETH')).toBe('ETH')
expect(resolveAssetSymbolForTx('DEPOSIT', 'enter', 'USDT', 'cUSDTv3')).toBe('USDT')
})

it('should return input token for manage actions', () => {
expect(resolveAssetSymbolForTx('CLAIM', 'manage', 'ETH', 'stETH')).toBe('ETH')
})

it('should fallback to input token when output token is undefined', () => {
expect(resolveAssetSymbolForTx('APPROVAL', 'exit', 'ETH', undefined)).toBe('ETH')
})

it('should fallback to input token when tx type is undefined', () => {
expect(resolveAssetSymbolForTx(undefined, 'exit', 'ETH', 'stETH')).toBe('ETH')
})
})

describe('resolveYieldInputAssetIcon', () => {
it('should prefer inputToken assetId', () => {
const yieldItem = {
Expand Down
24 changes: 18 additions & 6 deletions src/lib/yieldxyz/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ const TX_TYPE_TO_LABELS: Record<string, TxTypeLabels> = {

type TerminologyKey = 'staking' | 'vault'

const assertNever = (value: never): never => {
throw new Error(`Unhandled yield type: ${value}`)
}

export const isStakingYieldType = (yieldType: YieldType): boolean => {
switch (yieldType) {
case 'staking':
Expand All @@ -79,8 +83,7 @@ export const isStakingYieldType = (yieldType: YieldType): boolean => {
case 'lending':
return false
default:
assertNever(yieldType)
return false
return assertNever(yieldType)
}
}

Expand Down Expand Up @@ -110,6 +113,19 @@ export const getTransactionButtonText = (
return 'Confirm'
}

const APPROVAL_TX_TYPES = new Set(['APPROVAL', 'APPROVE'])

export const resolveAssetSymbolForTx = (
txType: string | undefined,
action: 'enter' | 'exit' | 'manage',
assetSymbol: string,
outputTokenSymbol: string | undefined,
): string => {
if (action !== 'exit' || !outputTokenSymbol || !txType) return assetSymbol
if (APPROVAL_TX_TYPES.has(txType.toUpperCase())) return outputTokenSymbol
return assetSymbol
}

export const formatYieldTxTitle = (
title: string,
assetSymbol: string,
Expand Down Expand Up @@ -217,10 +233,6 @@ export type YieldActionLabelKeys = {
exit: string
}

const assertNever = (value: never): never => {
throw new Error(`Unhandled yield type: ${value}`)
}

/**
* Gets the appropriate translation keys for yield actions based on yield type.
*
Expand Down
39 changes: 31 additions & 8 deletions src/pages/Yields/hooks/useYieldTransactionFlow.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { useToast } from '@chakra-ui/react'
import type { AssetId, ChainId } from '@shapeshiftoss/caip'
import { cosmosChainId, ethChainId, fromAccountId, usdtAssetId } from '@shapeshiftoss/caip'
import {
CHAIN_NAMESPACE,
cosmosChainId,
ethChainId,
fromAccountId,
fromChainId,
usdtAssetId,
} from '@shapeshiftoss/caip'
import { assertGetViemClient } from '@shapeshiftoss/contracts'
import type { KnownChainIds } from '@shapeshiftoss/types'
import { useQuery, useQueryClient } from '@tanstack/react-query'
Expand All @@ -20,7 +27,11 @@ import type { CosmosStakeArgs } from '@/lib/yieldxyz/executeTransaction'
import { executeTransaction } from '@/lib/yieldxyz/executeTransaction'
import type { ActionDto, AugmentedYieldDto, TransactionDto } from '@/lib/yieldxyz/types'
import { ActionStatus as YieldActionStatus, TransactionStatus } from '@/lib/yieldxyz/types'
import { formatYieldTxTitle, getDefaultValidatorForYield } from '@/lib/yieldxyz/utils'
import {
formatYieldTxTitle,
getDefaultValidatorForYield,
resolveAssetSymbolForTx,
} from '@/lib/yieldxyz/utils'
import { useYieldAccount } from '@/pages/Yields/YieldAccountContext'
import { reactQueries } from '@/react-queries'
import { useAllowance } from '@/react-queries/hooks/useAllowance'
Expand Down Expand Up @@ -178,6 +189,12 @@ export const useYieldTransactionFlow = ({
const submitHashMutation = useSubmitYieldTransactionHash()

const inputTokenAssetId = useMemo(() => yieldItem?.inputTokens?.[0]?.assetId, [yieldItem])
const outputTokenSymbol = yieldItem?.outputToken?.symbol

const resolveSymbolForTx = useCallback(
(txType?: string) => resolveAssetSymbolForTx(txType, action, assetSymbol, outputTokenSymbol),
[action, assetSymbol, outputTokenSymbol],
)

const yieldChainId = yieldItem?.chainId
const { accountId: contextAccountId, accountNumber: contextAccountNumber } = useYieldAccount()
Expand Down Expand Up @@ -353,7 +370,7 @@ export const useYieldTransactionFlow = ({
.map((tx, i) => ({
title: formatYieldTxTitle(
tx.title || `Transaction ${i + 1}`,
assetSymbol,
resolveSymbolForTx(tx.type),
yieldItem?.mechanics.type,
tx.type,
),
Expand All @@ -368,7 +385,7 @@ export const useYieldTransactionFlow = ({
}, [
transactionSteps,
quoteData,
assetSymbol,
resolveSymbolForTx,
yieldItem?.mechanics.type,
isAllowanceCheckPending,
isUsdtResetRequired,
Expand Down Expand Up @@ -450,7 +467,7 @@ export const useYieldTransactionFlow = ({
typeMessagesMap[actionType] ??
formatYieldTxTitle(
tx.title || 'Transaction',
assetSymbol,
resolveSymbolForTx(tx.type),
yieldItem.mechanics.type,
tx.type,
),
Expand All @@ -461,7 +478,7 @@ export const useYieldTransactionFlow = ({
}),
)
},
[dispatch, yieldChainId, accountId, action, yieldItem, assetSymbol, amount],
[dispatch, yieldChainId, accountId, action, yieldItem, resolveSymbolForTx, amount],
)

const buildCosmosStakeArgs = useCallback((): CosmosStakeArgs | undefined => {
Expand Down Expand Up @@ -613,6 +630,12 @@ export const useYieldTransactionFlow = ({
queryClient.removeQueries({ queryKey: ['yieldxyz', 'quote'] })
setStep(ModalStep.Success)
} else {
const { chainNamespace } = fromChainId(yieldChainId)
if (chainNamespace === CHAIN_NAMESPACE.Evm) {
const publicClient = assertGetViemClient(yieldChainId)
await publicClient.waitForTransactionReceipt({ hash: txHash as Hash })
}

const confirmedAction = await waitForTransactionConfirmation(actionId, tx.id)
const nextTx = confirmedAction.transactions.find(
t => t.status === TransactionStatus.Created && t.stepIndex === yieldTxIndex + 1,
Expand Down Expand Up @@ -775,7 +798,7 @@ export const useYieldTransactionFlow = ({
...transactions.map((tx, i) => ({
title: formatYieldTxTitle(
tx.title || `Transaction ${i + 1}`,
assetSymbol,
resolveSymbolForTx(tx.type),
yieldItem?.mechanics.type,
tx.type,
),
Expand Down Expand Up @@ -818,7 +841,7 @@ export const useYieldTransactionFlow = ({
amount,
quoteError,
quoteData,
assetSymbol,
resolveSymbolForTx,
translate,
showErrorToast,
yieldItem?.mechanics.type,
Expand Down