Skip to content

Commit

Permalink
refactor(earn): handle claims in withdraw saga
Browse files Browse the repository at this point in the history
  • Loading branch information
MuckT committed Nov 6, 2024
1 parent 7c5d529 commit 7f56a57
Show file tree
Hide file tree
Showing 6 changed files with 71 additions and 236 deletions.
27 changes: 10 additions & 17 deletions src/earn/EarnConfirmationScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import TokenDisplay from 'src/components/TokenDisplay'
import TokenIcon, { IconSize } from 'src/components/TokenIcon'
import { usePrepareEnterConfirmationScreenTransactions } from 'src/earn/hooks'
import { withdrawStatusSelector } from 'src/earn/selectors'
import { claimStart, withdrawStart } from 'src/earn/slice'
import { withdrawStart } from 'src/earn/slice'
import { EarnActiveMode } from 'src/earn/types'
import { getEarnPositionBalanceValues, isGasSubsidizedForNetwork } from 'src/earn/utils'
import { CICOFlow } from 'src/fiatExchanges/utils'
Expand Down Expand Up @@ -98,22 +98,15 @@ export default function EarnConfirmationScreen({ route }: Props) {
}

dispatch(
mode === 'claim-rewards'
? claimStart({
preparedTransactions: getSerializablePreparedTransactions(
prepareTransactionsResult.transactions
),
rewardsTokens,
pool,
})
: withdrawStart({
preparedTransactions: getSerializablePreparedTransactions(
prepareTransactionsResult.transactions
),
rewardsTokens,
pool,
amount: withdrawAmountInDepositToken.toString(),
})
withdrawStart({
preparedTransactions: getSerializablePreparedTransactions(
prepareTransactionsResult.transactions
),
rewardsTokens,
pool,
mode,
...(mode !== 'claim-rewards' && { amount: withdrawAmountInDepositToken.toString() }),
})
)

AppAnalytics.track(EarnEvents.earn_collect_earnings_press, {
Expand Down
243 changes: 58 additions & 185 deletions src/earn/saga.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,6 @@ import AppAnalytics from 'src/analytics/AppAnalytics'
import { EarnEvents } from 'src/analytics/Events'
import { EarnDepositTxsReceiptProperties } from 'src/analytics/Properties'
import {
claimCancel,
claimError,
claimStart,
claimSuccess,
depositCancel,
depositError,
depositStart,
Expand Down Expand Up @@ -312,15 +308,14 @@ export function* withdrawSubmitSaga(action: PayloadAction<WithdrawInfo>) {
preparedTransactions: serializablePreparedTransactions,
rewardsTokens,
amount,
mode,
} = action.payload
const tokenId = pool.dataProps.depositTokenId

const preparedTransactions = getPreparedTransactions(serializablePreparedTransactions)

const tokenInfo = yield* call(getTokenInfo, tokenId)

if (!tokenInfo) {
Logger.error(`${TAG}/withdrawSubmitSaga`, 'Token info not found for token id', tokenId)
Logger.error(`${TAG}/submitSaga`, 'Token info not found for token id', tokenId)
yield* put(withdrawError())
return
}
Expand All @@ -331,7 +326,8 @@ export function* withdrawSubmitSaga(action: PayloadAction<WithdrawInfo>) {
depositTokenId: tokenId,
networkId,
poolId: pool.positionId,
tokenAmount: amount ?? pool.balance,
// Exclude tokenAmount for claim-rewards mode
...(mode !== 'claim-rewards' && { tokenAmount: amount ?? pool.balance }),
providerId: pool.appId,
rewards: rewardsTokens.map(({ tokenId, balance }) => ({
tokenId,
Expand All @@ -341,92 +337,85 @@ export function* withdrawSubmitSaga(action: PayloadAction<WithdrawInfo>) {

try {
Logger.debug(
`${TAG}/withdrawSubmitSaga`,
`Starting withdraw for token ${tokenId}, total transactions: ${preparedTransactions.length}`
`${TAG}/submitSaga`,
`Starting ${mode} for token ${tokenId}, total transactions: ${preparedTransactions.length}`
)

const createWithdrawStandbyTxHandlers = []
const transactionHandlers: Array<
(transactionHash: string, feeCurrencyId?: string) => BaseStandbyTransaction
> = []

// Assumes the first tx is withdraw and the rest are claim rewards
const createWithdrawStandbyTxHandler = (
transactionHash: string,
feeCurrencyId?: string
): BaseStandbyTransaction => {
return {
if (mode !== 'claim-rewards') {
const createWithdrawStandbyTxHandler = (
transactionHash: string,
feeCurrencyId?: string
): BaseStandbyTransaction => ({
context: newTransactionContext(TAG, 'Earn/Withdraw'),
networkId,
type: TokenTransactionTypeV2.EarnWithdraw,
inAmount: {
value: amount ?? pool.balance,
tokenId,
},
outAmount: {
value: amount ?? pool.balance,
tokenId: pool.dataProps.withdrawTokenId,
},
inAmount: { value: amount ?? pool.balance, tokenId },
outAmount: { value: amount ?? pool.balance, tokenId: pool.dataProps.withdrawTokenId },
transactionHash,
feeCurrencyId,
providerId: pool.appId,
}
})
transactionHandlers.push(createWithdrawStandbyTxHandler)
}

createWithdrawStandbyTxHandlers.push(createWithdrawStandbyTxHandler)

rewardsTokens.forEach(({ balance, tokenId }, index) => {
const createClaimRewardStandbyTx = (
transactionHash: string,
feeCurrencyId?: string
): BaseStandbyTransaction => {
return {
context: newTransactionContext(TAG, `Earn/ClaimReward-${index + 1}`),
networkId,
amount: {
value: balance,
tokenId,
},
type: TokenTransactionTypeV2.EarnClaimReward,
transactionHash,
feeCurrencyId,
providerId: pool.appId,
}
}
): BaseStandbyTransaction => ({
context: newTransactionContext(TAG, `Earn/ClaimReward-${index + 1}`),
networkId,
amount: { value: balance, tokenId },
type: TokenTransactionTypeV2.EarnClaimReward,
transactionHash,
feeCurrencyId,
providerId: pool.appId,
})

if (!pool.dataProps.withdrawalIncludesClaim) {
createWithdrawStandbyTxHandlers.push(createClaimRewardStandbyTx)
if (mode === 'claim-rewards' || !pool.dataProps.withdrawalIncludesClaim) {
transactionHandlers.push(createClaimRewardStandbyTx)
}
})

AppAnalytics.track(EarnEvents.earn_withdraw_submit_start, commonAnalyticsProps)
const eventStart =
mode === 'claim-rewards'
? EarnEvents.earn_claim_submit_start
: EarnEvents.earn_withdraw_submit_start
AppAnalytics.track(eventStart, commonAnalyticsProps)

const txHashes = yield* call(
sendPreparedTransactions,
serializablePreparedTransactions,
networkId,
createWithdrawStandbyTxHandlers,
transactionHandlers,
isGasSubsidizedForNetwork(networkId)
)

Logger.debug(
`${TAG}/withdraw`,
'Successfully sent withdraw transaction(s) to the network',
`${TAG}/${mode}`,
`Successfully sent ${mode} transaction(s) to the network`,
txHashes
)

navigateHome()
submitted = true

// wait for the tx receipts, so that we can track them
Logger.debug(`${TAG}/withdrawSubmitSaga`, 'Waiting for transaction receipts')
// Wait for transaction receipts
Logger.debug(`${TAG}/submitSaga`, 'Waiting for transaction receipts')
const txReceipts = yield* all(
txHashes.map((txHash) => {
return call([publicClient[networkIdToNetwork[networkId]], 'waitForTransactionReceipt'], {
hash: txHash,
})
})
)

txReceipts.forEach((receipt, index) => {
Logger.debug(
`${TAG}/withdrawSubmitSaga`,
`${TAG}/submitSaga`,
`Received transaction receipt ${index + 1} of ${txReceipts.length}`,
receipt
)
Expand All @@ -440,146 +429,31 @@ export function* withdrawSubmitSaga(action: PayloadAction<WithdrawInfo>) {

yield* put(withdrawSuccess())
yield* put(fetchTokenBalances({ showLoading: false }))
AppAnalytics.track(EarnEvents.earn_withdraw_submit_success, commonAnalyticsProps)
const eventSuccess =
mode === 'withdraw'
? EarnEvents.earn_withdraw_submit_success
: EarnEvents.earn_claim_submit_success
AppAnalytics.track(eventSuccess, commonAnalyticsProps)
} catch (err) {
if (err === CANCELLED_PIN_INPUT) {
Logger.info(`${TAG}/withdrawSubmitSaga`, 'Transaction cancelled by user')
Logger.info(`${TAG}/submitSaga`, 'Transaction cancelled by user')
yield* put(withdrawCancel())
AppAnalytics.track(EarnEvents.earn_withdraw_submit_cancel, commonAnalyticsProps)
const eventCancel =
mode === 'claim-rewards'
? EarnEvents.earn_claim_submit_cancel
: EarnEvents.earn_withdraw_submit_cancel
AppAnalytics.track(eventCancel, commonAnalyticsProps)
return
}

const error = ensureError(err)
Logger.error(`${TAG}/withdrawSubmitSaga`, 'Error sending withdraw transaction', error)
Logger.error(`${TAG}/submitSaga`, `Error sending ${mode} transaction`, error)
yield* put(withdrawError())
AppAnalytics.track(EarnEvents.earn_withdraw_submit_error, {
...commonAnalyticsProps,
error: error.message,
})

if (!submitted) {
vibrateError()
}
}
}

export function* claimSubmitSaga(action: PayloadAction<WithdrawInfo>) {
const {
pool,
preparedTransactions: serializablePreparedTransactions,
rewardsTokens,
} = action.payload
const tokenId = pool.dataProps.depositTokenId

const preparedTransactions = getPreparedTransactions(serializablePreparedTransactions)

const tokenInfo = yield* call(getTokenInfo, tokenId)

if (!tokenInfo) {
Logger.error(`${TAG}/claimSubmitSaga`, 'Token info not found for token id', tokenId)
yield* put(claimError())
return
}

const networkId = tokenInfo.networkId
let submitted = false
const commonAnalyticsProps = {
depositTokenId: tokenId,
networkId,
poolId: pool.positionId,
providerId: pool.appId,
rewards: rewardsTokens.map(({ tokenId, balance }) => ({
tokenId,
amount: balance,
})),
}

try {
Logger.debug(
`${TAG}/claimSubmitSaga`,
`Starting claim for token ${tokenId}, total transactions: ${preparedTransactions.length}`
)

const createClaimRewardStandbyTxHandlers: any[] = []

rewardsTokens.forEach(({ balance, tokenId }, index) => {
const createClaimRewardStandbyTx = (
transactionHash: string,
feeCurrencyId?: string
): BaseStandbyTransaction => {
return {
context: newTransactionContext(TAG, `Earn/ClaimReward-${index + 1}`),
networkId,
amount: {
value: balance,
tokenId,
},
type: TokenTransactionTypeV2.EarnClaimReward,
transactionHash,
feeCurrencyId,
providerId: pool.appId,
}
}

createClaimRewardStandbyTxHandlers.push(createClaimRewardStandbyTx)
})

AppAnalytics.track(EarnEvents.earn_claim_submit_start, commonAnalyticsProps)

const txHashes = yield* call(
sendPreparedTransactions,
serializablePreparedTransactions,
networkId,
createClaimRewardStandbyTxHandlers,
isGasSubsidizedForNetwork(networkId)
)

Logger.debug(`${TAG}/claim`, 'Successfully sent claim transaction(s) to the network', txHashes)

navigateHome()
submitted = true

// wait for the tx receipts, so that we can track them
Logger.debug(`${TAG}/claimSubmitSaga`, 'Waiting for transaction receipts')
const txReceipts = yield* all(
txHashes.map((txHash) => {
return call([publicClient[networkIdToNetwork[networkId]], 'waitForTransactionReceipt'], {
hash: txHash,
})
})
)
txReceipts.forEach((receipt, index) => {
Logger.debug(
`${TAG}/claimSubmitSaga`,
`Received transaction receipt ${index + 1} of ${txReceipts.length}`,
receipt
)
})

txReceipts.forEach((receipt, index) => {
if (receipt.status !== 'success') {
throw new Error(`Transaction ${index + 1} reverted: ${receipt?.transactionHash}`)
}
})

yield* put(claimSuccess())
yield* put(fetchTokenBalances({ showLoading: false }))
AppAnalytics.track(EarnEvents.earn_claim_submit_success, commonAnalyticsProps)
} catch (err) {
if (err === CANCELLED_PIN_INPUT) {
Logger.info(`${TAG}/claimSubmitSaga`, 'Transaction cancelled by user')
yield* put(claimCancel())
AppAnalytics.track(EarnEvents.earn_claim_submit_cancel, commonAnalyticsProps)
return
}

const error = ensureError(err)
Logger.error(`${TAG}/claimSubmitSaga`, 'Error sending claim transaction', error)
yield* put(claimError())
AppAnalytics.track(EarnEvents.earn_claim_submit_error, {
...commonAnalyticsProps,
error: error.message,
})
const eventError =
mode === 'claim-rewards'
? EarnEvents.earn_claim_submit_error
: EarnEvents.earn_withdraw_submit_error
AppAnalytics.track(eventError, { ...commonAnalyticsProps, error: error.message })

if (!submitted) {
vibrateError()
Expand All @@ -590,5 +464,4 @@ export function* claimSubmitSaga(action: PayloadAction<WithdrawInfo>) {
export function* earnSaga() {
yield* takeLeading(depositStart.type, safely(depositSubmitSaga))
yield* takeLeading(withdrawStart.type, safely(withdrawSubmitSaga))
yield* takeLeading(claimStart.type, safely(claimSubmitSaga))
}
Loading

0 comments on commit 7f56a57

Please sign in to comment.