Skip to content

Commit

Permalink
feat: earn confirmation screen display (valora-inc#6144)
Browse files Browse the repository at this point in the history
### Description

Renames the `EarnCollectScreen.tsx` to `EarnConfirmationScreen.tsx` and
updates the screen to handle the display partial withdrawals. Submitting
the withdrawals will be in a follow-up PR & this screen is not used when
the feature gate `ALLOW_EARN_PARTIAL_WITHDRAWAL` is false.

### Screenshots

| iOS Enter Amount | iOS Confirmation |
| ---- | ---- |
|
![](https://github.com/user-attachments/assets/e26895a7-9541-4ea5-bafc-2a8cba479626)
|
![](https://github.com/user-attachments/assets/84408c87-155f-423f-a111-14938168a69c)
|

### Test plan

- [x] Tested locally on iOS
- [x] Tested locally on Android
- [x] Unit tests updated

### Related issues

- Part of ACT-1389

### Backwards compatibility

Yes

### Network scalability

N/A

---------

Co-authored-by: Finnian Jacobson-Schulte <140328381+finnian0826@users.noreply.github.com>
Co-authored-by: Jacob Waterman <jacobrwaterman@gmail.com>
  • Loading branch information
3 people authored and jophish committed Nov 12, 2024
1 parent eb9bcb4 commit 45cb247
Show file tree
Hide file tree
Showing 18 changed files with 307 additions and 136 deletions.
7 changes: 7 additions & 0 deletions locales/base/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -2650,12 +2650,19 @@
},
"collect": {
"title": "Congratulations! Review & collect your earnings",
"titleClaim": "You’re going to claim:",
"titleCollect": "You’re going to collect:",
"titleWithdraw": "You’re going to withdraw:",
"total": "TOTAL",
"plus": "PLUS",
"reward": "REWARD",
"rate": "Rate (est.)",
"apy": "{{apy}}% APY",
"fee": "Gas Fee",
"cta": "Collect Earnings",
"ctaWithdraw": "Withdraw",
"ctaReward": "Claim Reward",
"ctaExit": "Exit Pool",
"errorTitle": "Something went wrong",
"errorDescription": "Unable to fetch deposit details. Please try again.",
"noGasTitle": "You need more {{symbol}} to continue",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React from 'react'
import { Provider } from 'react-redux'
import AppAnalytics from 'src/analytics/AppAnalytics'
import { EarnEvents } from 'src/analytics/Events'
import EarnCollectScreen from 'src/earn/EarnCollectScreen'
import EarnConfirmationScreen from 'src/earn/EarnConfirmationScreen'
import { prepareWithdrawAndClaimTransactions } from 'src/earn/prepareTransactions'
import { withdrawStart } from 'src/earn/slice'
import { isGasSubsidizedForNetwork } from 'src/earn/utils'
Expand Down Expand Up @@ -89,7 +89,7 @@ const mockPreparedTransaction: PreparedTransactionsPossible = {
},
}

describe('EarnCollectScreen', () => {
describe('EarnConfirmationScreen', () => {
beforeEach(() => {
jest.clearAllMocks()

Expand All @@ -103,48 +103,98 @@ describe('EarnCollectScreen', () => {
store.clearActions()
})

it('renders total balance, rewards, apy and gas after fetching rewards and preparing tx', async () => {
it('renders total balance, rewards and gas after fetching rewards and preparing tx for exit', async () => {
const { getByText, getByTestId, queryByTestId } = render(
<Provider store={store}>
<MockedNavigator
component={EarnCollectScreen}
component={EarnConfirmationScreen}
params={{
pool: { ...mockEarnPositions[0], balance: '10.75' },
mode: 'Exit',
}}
/>
</Provider>
)

expect(getByText('earnFlow.collect.title')).toBeTruthy()
expect(getByText('earnFlow.collect.titleCollect')).toBeTruthy()
expect(getByText('earnFlow.collect.total')).toBeTruthy()
expect(getByTestId(`EarnCollect/${mockArbUsdcTokenId}/CryptoAmount`)).toHaveTextContent(
expect(getByTestId(`EarnConfirmation/${mockArbUsdcTokenId}/CryptoAmount`)).toHaveTextContent(
'11.83 USDC'
)
expect(getByTestId(`EarnCollect/${mockArbUsdcTokenId}/FiatAmount`)).toHaveTextContent('₱15.73')
expect(queryByTestId('EarnCollect/ApyLoading')).toBeFalsy()
expect(getByTestId('EarnCollect/GasLoading')).toBeTruthy()
expect(getByTestId('EarnCollectScreen/CTA')).toBeDisabled()
expect(getByTestId(`EarnConfirmation/${mockArbUsdcTokenId}/FiatAmount`)).toHaveTextContent(
'₱15.73'
)
expect(getByTestId('EarnConfirmation/GasLoading')).toBeTruthy()
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeDisabled()

expect(getByText('earnFlow.collect.plus')).toBeTruthy()
expect(getByTestId(`EarnCollect/${mockArbArbTokenId}/CryptoAmount`)).toHaveTextContent(
expect(getByText('earnFlow.collect.reward')).toBeTruthy()
expect(getByTestId(`EarnConfirmation/${mockArbArbTokenId}/CryptoAmount`)).toHaveTextContent(
'0.01 ARB'
)
expect(getByTestId(`EarnCollect/${mockArbArbTokenId}/FiatAmount`)).toHaveTextContent('₱0.016')
expect(getByText('earnFlow.collect.apy, {"apy":"1.92"}')).toBeTruthy()
expect(getByTestId(`EarnConfirmation/${mockArbArbTokenId}/FiatAmount`)).toHaveTextContent(
'₱0.016'
)

await waitFor(() => {
expect(queryByTestId('EarnConfirmation/GasLoading')).toBeFalsy()
})
expect(getByTestId('EarnConfirmation/GasFeeCryptoAmount')).toHaveTextContent('0.06 ETH')
expect(getByTestId('EarnConfirmation/GasFeeFiatAmount')).toHaveTextContent('₱119.70')
expect(queryByTestId('EarnConfirmation/GasSubsidized')).toBeFalsy()
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeEnabled()
expect(prepareWithdrawAndClaimTransactions).toHaveBeenCalledWith({
feeCurrencies: mockStoreBalancesToTokenBalances([mockTokenBalances[mockArbEthTokenId]]),
pool: { ...mockEarnPositions[0], balance: '10.75' },
rewardsPositions: [mockRewardsPositions[1]],
walletAddress: mockAccount.toLowerCase(),
hooksApiUrl: 'https://api.alfajores.valora.xyz/hooks-api',
amount: '10.75',
})
expect(store.getActions()).toEqual([])
})

it('renders total balance and gas after fetching rewards and preparing tx for partial withdrawal', async () => {
const { getByText, getByTestId, queryByTestId, queryByText } = render(
<Provider store={store}>
<MockedNavigator
component={EarnConfirmationScreen}
params={{
pool: { ...mockEarnPositions[0], balance: '10.75' },
mode: 'PartialWithdraw',
inputAmount: (10.75 * +mockEarnPositions[0].pricePerShare) / 2, // Input amount is half of the balance
}}
/>
</Provider>
)

expect(getByText('earnFlow.collect.titleWithdraw')).toBeTruthy()
expect(getByText('earnFlow.collect.total')).toBeTruthy()
expect(getByTestId(`EarnConfirmation/${mockArbUsdcTokenId}/CryptoAmount`)).toHaveTextContent(
'5.91 USDC'
)
expect(getByTestId(`EarnConfirmation/${mockArbUsdcTokenId}/FiatAmount`)).toHaveTextContent(
'₱7.86'
)
expect(getByTestId('EarnConfirmation/GasLoading')).toBeTruthy()
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeDisabled()

expect(queryByText('earnFlow.collect.reward')).toBeFalsy()

await waitFor(() => {
expect(queryByTestId('EarnCollect/GasLoading')).toBeFalsy()
expect(queryByTestId('EarnConfirmation/GasLoading')).toBeFalsy()
})
expect(getByTestId('EarnCollect/GasFeeCryptoAmount')).toHaveTextContent('0.06 ETH')
expect(getByTestId('EarnCollect/GasFeeFiatAmount')).toHaveTextContent('₱119.70')
expect(queryByTestId('EarnCollect/GasSubsidized')).toBeFalsy()
expect(getByTestId('EarnCollectScreen/CTA')).toBeEnabled()
expect(getByTestId('EarnConfirmation/GasFeeCryptoAmount')).toHaveTextContent('0.06 ETH')
expect(getByTestId('EarnConfirmation/GasFeeFiatAmount')).toHaveTextContent('₱119.70')
expect(queryByTestId('EarnConfirmation/GasSubsidized')).toBeFalsy()
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeEnabled()
// TODO(act-1389): update this test to make sure that reward positions are not included in partial withdrawals.
expect(prepareWithdrawAndClaimTransactions).toHaveBeenCalledWith({
feeCurrencies: mockStoreBalancesToTokenBalances([mockTokenBalances[mockArbEthTokenId]]),
pool: { ...mockEarnPositions[0], balance: '10.75' },
rewardsPositions: [mockRewardsPositions[1]],
walletAddress: mockAccount.toLowerCase(),
hooksApiUrl: 'https://api.alfajores.valora.xyz/hooks-api',
amount: '5.37500000000000045455',
})
expect(store.getActions()).toEqual([])
})
Expand All @@ -164,30 +214,33 @@ describe('EarnCollectScreen', () => {
})}
>
<MockedNavigator
component={EarnCollectScreen}
component={EarnConfirmationScreen}
params={{
pool: { ...mockEarnPositions[0], balance: '10.75' },
mode: 'PartialWithdraw',
}}
/>
</Provider>
)

expect(getByText('earnFlow.collect.title')).toBeTruthy()
expect(getByText('earnFlow.collect.titleWithdraw')).toBeTruthy()
expect(getByText('earnFlow.collect.total')).toBeTruthy()
expect(getByTestId(`EarnCollect/${mockArbUsdcTokenId}/CryptoAmount`)).toHaveTextContent(
expect(getByTestId(`EarnConfirmation/${mockArbUsdcTokenId}/CryptoAmount`)).toHaveTextContent(
'11.83 USDC'
)
expect(getByTestId(`EarnCollect/${mockArbUsdcTokenId}/FiatAmount`)).toHaveTextContent('₱15.73')
expect(getByTestId('EarnCollectScreen/CTA')).toBeDisabled()
expect(getByTestId(`EarnConfirmation/${mockArbUsdcTokenId}/FiatAmount`)).toHaveTextContent(
'₱15.73'
)
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeDisabled()

expect(queryByText('earnFlow.collect.plus')).toBeFalsy()
expect(queryByTestId(`EarnCollect/${mockArbArbTokenId}/CryptoAmount`)).toBeFalsy()
expect(queryByTestId(`EarnCollect/${mockArbArbTokenId}/FiatAmount`)).toBeFalsy()
expect(queryByText('earnFlow.collect.reward')).toBeFalsy()
expect(queryByTestId(`EarnConfirmation/${mockArbArbTokenId}/CryptoAmount`)).toBeFalsy()
expect(queryByTestId(`EarnConfirmation/${mockArbArbTokenId}/FiatAmount`)).toBeFalsy()
await waitFor(() => {
expect(queryByTestId('EarnCollectScreen/CTA')).toBeEnabled()
expect(queryByTestId('EarnConfirmationScreen/CTA')).toBeEnabled()
})
await waitFor(() => {
expect(queryByTestId('EarnCollect/GasLoading')).toBeFalsy()
expect(queryByTestId('EarnConfirmation/GasLoading')).toBeFalsy()
})
})

Expand All @@ -198,25 +251,26 @@ describe('EarnCollectScreen', () => {
const { getByText, getByTestId, queryByTestId } = render(
<Provider store={store}>
<MockedNavigator
component={EarnCollectScreen}
component={EarnConfirmationScreen}
params={{
pool: mockEarnPositions[0],
mode: 'PartialWithdraw',
}}
/>
</Provider>
)

expect(getByText('earnFlow.collect.title')).toBeTruthy()
expect(getByText('earnFlow.collect.titleWithdraw')).toBeTruthy()
expect(getByText('earnFlow.collect.total')).toBeTruthy()
expect(getByTestId('EarnCollect/GasLoading')).toBeTruthy()
expect(getByTestId('EarnCollectScreen/CTA')).toBeDisabled()
expect(getByTestId('EarnConfirmation/GasLoading')).toBeTruthy()
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeDisabled()

await waitFor(() => {
expect(queryByTestId('EarnCollect/GasLoading')).toBeFalsy()
expect(queryByTestId('EarnConfirmation/GasLoading')).toBeFalsy()
})
expect(getByText('earnFlow.collect.errorTitle')).toBeTruthy()
expect(getByTestId('EarnCollect/GasError')).toBeTruthy()
expect(getByTestId('EarnCollectScreen/CTA')).toBeDisabled()
expect(getByTestId('EarnConfirmation/GasError')).toBeTruthy()
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeDisabled()
})

it('disables cta if not enough balance for gas', async () => {
Expand All @@ -227,48 +281,51 @@ describe('EarnCollectScreen', () => {
const { getByText, getByTestId, queryByTestId } = render(
<Provider store={store}>
<MockedNavigator
component={EarnCollectScreen}
component={EarnConfirmationScreen}
params={{
pool: mockEarnPositions[0],
mode: 'PartialWithdraw',
}}
/>
</Provider>
)

expect(getByText('earnFlow.collect.title')).toBeTruthy()
expect(getByText('earnFlow.collect.titleWithdraw')).toBeTruthy()
expect(getByText('earnFlow.collect.total')).toBeTruthy()
expect(getByTestId('EarnCollect/GasLoading')).toBeTruthy()
expect(getByTestId('EarnCollectScreen/CTA')).toBeDisabled()
expect(getByTestId('EarnConfirmation/GasLoading')).toBeTruthy()
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeDisabled()

await waitFor(() => {
expect(queryByTestId('EarnCollect/GasLoading')).toBeFalsy()
expect(queryByTestId('EarnConfirmation/GasLoading')).toBeFalsy()
})
expect(getByTestId('EarnCollectScreen/CTA')).toBeDisabled()
expect(getByTestId('EarnCollect/GasError')).toBeTruthy()
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeDisabled()
expect(getByTestId('EarnConfirmation/GasError')).toBeTruthy()
})

it('pressing cta dispatches withdraw action and fires analytics event', async () => {
const { getByTestId } = render(
<Provider store={store}>
<MockedNavigator
component={EarnCollectScreen}
component={EarnConfirmationScreen}
params={{
pool: { ...mockEarnPositions[0], balance: '10.75' },
mode: 'PartialWithdraw',
}}
/>
</Provider>
)

await waitFor(() => {
expect(getByTestId('EarnCollectScreen/CTA')).toBeEnabled()
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeEnabled()
})

fireEvent.press(getByTestId('EarnCollectScreen/CTA'))
fireEvent.press(getByTestId('EarnConfirmationScreen/CTA'))

expect(store.getActions()).toEqual([
{
type: withdrawStart.type,
payload: {
amount: '11.825',
pool: { ...mockEarnPositions[0], balance: '10.75' },
preparedTransactions: getSerializablePreparedTransactions(
mockPreparedTransaction.transactions
Expand Down Expand Up @@ -299,20 +356,23 @@ describe('EarnCollectScreen', () => {
const { getByTestId, queryByTestId } = render(
<Provider store={store}>
<MockedNavigator
component={EarnCollectScreen}
component={EarnConfirmationScreen}
params={{
pool: mockEarnPositions[0],
mode: 'PartialWithdraw',
}}
/>
</Provider>
)

await waitFor(() => {
expect(queryByTestId('EarnCollect/GasLoading')).toBeFalsy()
expect(queryByTestId('EarnConfirmation/GasLoading')).toBeFalsy()
})

expect(getByTestId('EarnCollectScreen/CTA')).toBeDisabled()
expect(getByTestId('EarnCollectScreen/CTA')).toContainElement(getByTestId('Button/Loading'))
expect(getByTestId('EarnConfirmationScreen/CTA')).toBeDisabled()
expect(getByTestId('EarnConfirmationScreen/CTA')).toContainElement(
getByTestId('Button/Loading')
)
})

it('navigate and fire analytics on no gas CTA press', async () => {
Expand All @@ -324,16 +384,17 @@ describe('EarnCollectScreen', () => {
const { getByText, queryByTestId } = render(
<Provider store={store}>
<MockedNavigator
component={EarnCollectScreen}
component={EarnConfirmationScreen}
params={{
pool: mockEarnPositions[0],
mode: 'PartialWithdraw',
}}
/>
</Provider>
)

await waitFor(() => {
expect(queryByTestId('EarnCollect/RewardsLoading')).toBeFalsy()
expect(queryByTestId('EarnConfirmation/RewardsLoading')).toBeFalsy()
})

expect(
Expand Down Expand Up @@ -363,13 +424,34 @@ describe('EarnCollectScreen', () => {
const { getByTestId } = render(
<Provider store={store}>
<MockedNavigator
component={EarnCollectScreen}
component={EarnConfirmationScreen}
params={{
pool: mockEarnPositions[0],
mode: 'PartialWithdraw',
}}
/>
</Provider>
)
expect(getByTestId('EarnCollect/GasSubsidized')).toBeTruthy()
expect(getByTestId('EarnConfirmation/GasSubsidized')).toBeTruthy()
})

it.each([
['Claim', 'earnFlow.collect.titleClaim'],
['PartialWithdraw', 'earnFlow.collect.titleWithdraw'],
['Exit', 'earnFlow.collect.titleCollect'],
])('shows correct header text for %s', async (mode, expectedHeader) => {
const { getByText } = render(
<Provider store={store}>
<MockedNavigator
component={EarnConfirmationScreen}
params={{
pool: mockEarnPositions[0],
mode,
}}
/>
</Provider>
)

expect(getByText(expectedHeader)).toBeTruthy()
})
})
Loading

0 comments on commit 45cb247

Please sign in to comment.