wallet-stack 1.0.0-alpha.134 → 1.0.0-alpha.135

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wallet-stack",
3
- "version": "1.0.0-alpha.134",
3
+ "version": "1.0.0-alpha.135",
4
4
  "author": "Valora Inc",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -10,7 +10,7 @@ import BackButton from 'src/components/BackButton'
10
10
  import { BottomSheetModalRefType } from 'src/components/BottomSheet'
11
11
  import Button, { BtnSizes } from 'src/components/Button'
12
12
  import FeeInfoBottomSheet from 'src/components/FeeInfoBottomSheet'
13
- import { FilterChip } from 'src/components/FilterChipsCarousel'
13
+ import { FilterChip, isNetworkChip } from 'src/components/FilterChipsCarousel'
14
14
  import InLineNotification, { NotificationVariant } from 'src/components/InLineNotification'
15
15
  import KeyboardAwareScrollView from 'src/components/KeyboardAwareScrollView'
16
16
  import { ReviewDetailsItem } from 'src/components/ReviewTransaction'
@@ -34,6 +34,7 @@ import { Spacing } from 'src/styles/styles'
34
34
  import { feeCurrenciesSelector } from 'src/tokens/selectors'
35
35
  import { TokenBalance } from 'src/tokens/slice'
36
36
  import { PreparedTransactionsResult } from 'src/viem/prepareTransactions'
37
+ import networkConfig from 'src/web3/networkConfig'
37
38
 
38
39
  export interface ProceedArgs {
39
40
  tokenAmount: BigNumber
@@ -113,9 +114,20 @@ export default function EnterAmount({
113
114
  }: Props) {
114
115
  const { t } = useTranslation()
115
116
  const insets = useSafeAreaInsets()
116
- const [token, setToken] = useState<TokenBalance>(() => defaultToken ?? tokens[0])
117
+ const [token, setToken] = useState<TokenBalance | undefined>(() => {
118
+ if (defaultToken) return defaultToken
119
+ const activeFilters = filterChips?.filter((chip) => chip.isSelected) ?? []
120
+ const selectableTokens = tokens.filter((t) =>
121
+ activeFilters.every((filter) =>
122
+ isNetworkChip(filter) ? filter.filterFn(t, filter.selectedNetworkIds) : filter.filterFn(t)
123
+ )
124
+ )
125
+ return selectableTokens[0]
126
+ })
117
127
  const [selectedPercentage, setSelectedPercentage] = useState<number | null>(null)
118
- const feeCurrencies = useSelector((state) => feeCurrenciesSelector(state, token.networkId))
128
+ const feeCurrencies = useSelector((state) =>
129
+ feeCurrenciesSelector(state, token?.networkId ?? networkConfig.defaultNetworkId)
130
+ )
119
131
  const networkFee = useNetworkFee(prepareTransactionsResult)
120
132
  const localCurrencySymbol = useSelector(getLocalCurrencySymbol) ?? LocalCurrencySymbol.USD
121
133
 
@@ -142,6 +154,8 @@ export default function EnterAmount({
142
154
  useEffect(() => {
143
155
  onClearPreparedTransactions()
144
156
 
157
+ if (!token) return
158
+
145
159
  const canRefresh =
146
160
  processedAmounts.token.bignum &&
147
161
  processedAmounts.token.bignum.gt(0) &&
@@ -158,9 +172,9 @@ export default function EnterAmount({
158
172
  const onOpenTokenPicker = () => {
159
173
  tokenBottomSheetRef.current?.snapToIndex(0)
160
174
  AppAnalytics.track(SendEvents.token_dropdown_opened, {
161
- currentTokenId: token.tokenId,
162
- currentTokenAddress: token.address,
163
- currentNetworkId: token.networkId,
175
+ currentTokenId: token?.tokenId ?? '',
176
+ currentTokenAddress: token?.address ?? null,
177
+ currentNetworkId: token?.networkId ?? null,
164
178
  })
165
179
  }
166
180
 
@@ -174,6 +188,7 @@ export default function EnterAmount({
174
188
  }
175
189
 
176
190
  const onSelectPercentageAmount = (percentage: number) => {
191
+ if (!token) return
177
192
  handleSelectPercentageAmount(percentage)
178
193
  setSelectedPercentage(percentage)
179
194
 
@@ -187,6 +202,7 @@ export default function EnterAmount({
187
202
  }
188
203
 
189
204
  const showLowerAmountError =
205
+ token &&
190
206
  processedAmounts.token.bignum &&
191
207
  !processedAmounts.token.bignum.lte(token.balance) &&
192
208
  !disableBalanceCheck
@@ -205,6 +221,7 @@ export default function EnterAmount({
205
221
  prepareTransactionsResult.transactions.length > 0
206
222
 
207
223
  const disabled =
224
+ !token ||
208
225
  disableProceed ||
209
226
  (disableBalanceCheck ? !!processedAmounts.token.bignum?.isZero() : !transactionIsPossible)
210
227
 
@@ -236,89 +253,95 @@ export default function EnterAmount({
236
253
  onOpenTokenPicker={tokenSelectionDisabled ? undefined : onOpenTokenPicker}
237
254
  />
238
255
 
239
- {prepareTransactionsResult?.type !== 'not-enough-balance-for-gas' && !!networkFee && (
240
- <View style={styles.feeContainer}>
241
- <ReviewDetailsItem
242
- approx
243
- testID="SendEnterAmount/NetworkFee"
244
- type="token-amount"
245
- label={t('networkFee')}
246
- tokenAmount={networkFee.amount}
247
- localAmount={networkFee.localAmount}
248
- tokenInfo={networkFee.token}
249
- localCurrencySymbol={localCurrencySymbol}
250
- onInfoPress={() => feeInfoBottomSheetRef.current?.snapToIndex(0)}
251
- />
256
+ {token &&
257
+ prepareTransactionsResult?.type !== 'not-enough-balance-for-gas' &&
258
+ !!networkFee && (
259
+ <View style={styles.feeContainer}>
260
+ <ReviewDetailsItem
261
+ approx
262
+ testID="SendEnterAmount/NetworkFee"
263
+ type="token-amount"
264
+ label={t('networkFee')}
265
+ tokenAmount={networkFee.amount}
266
+ localAmount={networkFee.localAmount}
267
+ tokenInfo={networkFee.token}
268
+ localCurrencySymbol={localCurrencySymbol}
269
+ onInfoPress={() => feeInfoBottomSheetRef.current?.snapToIndex(0)}
270
+ />
252
271
 
253
- <FeeInfoBottomSheet forwardedRef={feeInfoBottomSheetRef} networkFee={networkFee} />
254
- </View>
255
- )}
272
+ <FeeInfoBottomSheet forwardedRef={feeInfoBottomSheetRef} networkFee={networkFee} />
273
+ </View>
274
+ )}
256
275
  </View>
257
276
 
258
- {showLowerAmountError && (
259
- <InLineNotification
260
- variant={NotificationVariant.Warning}
261
- title={t('sendEnterAmountScreen.insufficientBalanceWarning.title', {
262
- tokenSymbol: token.symbol,
263
- })}
264
- description={t('sendEnterAmountScreen.insufficientBalanceWarning.description', {
265
- tokenSymbol: token.symbol,
266
- })}
267
- style={styles.warning}
268
- testID="SendEnterAmount/NotEnoughBalanceWarning"
269
- />
270
- )}
271
- {showMaxAmountWarning && (
272
- <InLineNotification
273
- variant={NotificationVariant.Warning}
274
- title={t('sendEnterAmountScreen.maxAmountWarning.title')}
275
- description={t('sendEnterAmountScreen.maxAmountWarning.description', {
276
- feeTokenSymbol: prepareTransactionsResult.feeCurrency.symbol,
277
- })}
278
- style={styles.warning}
279
- testID="SendEnterAmount/MaxAmountWarning"
280
- />
281
- )}
282
- {showNotEnoughBalanceForGasWarning && (
283
- <InLineNotification
284
- variant={NotificationVariant.Warning}
285
- title={t('sendEnterAmountScreen.notEnoughBalanceForGasWarning.title', {
286
- feeTokenSymbol: prepareTransactionsResult.feeCurrencies[0].symbol,
287
- })}
288
- description={t('sendEnterAmountScreen.notEnoughBalanceForGasWarning.description', {
289
- feeTokenSymbol: prepareTransactionsResult.feeCurrencies[0].symbol,
290
- })}
291
- style={styles.warning}
292
- testID="SendEnterAmount/NotEnoughForGasWarning"
293
- />
294
- )}
295
- {prepareTransactionError && (
296
- <InLineNotification
297
- variant={NotificationVariant.Error}
298
- title={t('sendEnterAmountScreen.prepareTransactionError.title')}
299
- description={t('sendEnterAmountScreen.prepareTransactionError.description')}
300
- style={styles.warning}
301
- testID="SendEnterAmount/PrepareTransactionError"
302
- />
303
- )}
277
+ {token && (
278
+ <>
279
+ {showLowerAmountError && (
280
+ <InLineNotification
281
+ variant={NotificationVariant.Warning}
282
+ title={t('sendEnterAmountScreen.insufficientBalanceWarning.title', {
283
+ tokenSymbol: token.symbol,
284
+ })}
285
+ description={t('sendEnterAmountScreen.insufficientBalanceWarning.description', {
286
+ tokenSymbol: token.symbol,
287
+ })}
288
+ style={styles.warning}
289
+ testID="SendEnterAmount/NotEnoughBalanceWarning"
290
+ />
291
+ )}
292
+ {showMaxAmountWarning && (
293
+ <InLineNotification
294
+ variant={NotificationVariant.Warning}
295
+ title={t('sendEnterAmountScreen.maxAmountWarning.title')}
296
+ description={t('sendEnterAmountScreen.maxAmountWarning.description', {
297
+ feeTokenSymbol: prepareTransactionsResult.feeCurrency.symbol,
298
+ })}
299
+ style={styles.warning}
300
+ testID="SendEnterAmount/MaxAmountWarning"
301
+ />
302
+ )}
303
+ {showNotEnoughBalanceForGasWarning && (
304
+ <InLineNotification
305
+ variant={NotificationVariant.Warning}
306
+ title={t('sendEnterAmountScreen.notEnoughBalanceForGasWarning.title', {
307
+ feeTokenSymbol: prepareTransactionsResult.feeCurrencies[0].symbol,
308
+ })}
309
+ description={t('sendEnterAmountScreen.notEnoughBalanceForGasWarning.description', {
310
+ feeTokenSymbol: prepareTransactionsResult.feeCurrencies[0].symbol,
311
+ })}
312
+ style={styles.warning}
313
+ testID="SendEnterAmount/NotEnoughForGasWarning"
314
+ />
315
+ )}
316
+ {prepareTransactionError && (
317
+ <InLineNotification
318
+ variant={NotificationVariant.Error}
319
+ title={t('sendEnterAmountScreen.prepareTransactionError.title')}
320
+ description={t('sendEnterAmountScreen.prepareTransactionError.description')}
321
+ style={styles.warning}
322
+ testID="SendEnterAmount/PrepareTransactionError"
323
+ />
324
+ )}
304
325
 
305
- {children}
326
+ {children}
306
327
 
307
- <EnterAmountOptions
308
- onPressAmount={onSelectPercentageAmount}
309
- selectedAmount={selectedPercentage}
310
- testID="SendEnterAmount/AmountOptions"
311
- />
328
+ <EnterAmountOptions
329
+ onPressAmount={onSelectPercentageAmount}
330
+ selectedAmount={selectedPercentage}
331
+ testID="SendEnterAmount/AmountOptions"
332
+ />
312
333
 
313
- <ProceedComponent
314
- tokenAmount={processedAmounts.token.bignum}
315
- localAmount={processedAmounts.local.bignum}
316
- token={token}
317
- amountEnteredIn={amountType}
318
- onPressProceed={onPressProceed}
319
- disabled={disabled}
320
- showLoading={prepareTransactionsLoading}
321
- />
334
+ <ProceedComponent
335
+ tokenAmount={processedAmounts.token.bignum}
336
+ localAmount={processedAmounts.local.bignum}
337
+ token={token}
338
+ amountEnteredIn={amountType}
339
+ onPressProceed={onPressProceed}
340
+ disabled={disabled}
341
+ showLoading={prepareTransactionsLoading}
342
+ />
343
+ </>
344
+ )}
322
345
  </KeyboardAwareScrollView>
323
346
  <TokenBottomSheet
324
347
  forwardedRef={tokenBottomSheetRef}
@@ -308,6 +308,33 @@ describe('SendEnterAmount', () => {
308
308
  expect(queryByText('sendEnterAmountScreen.miniPayFilterChip')).toBeFalsy()
309
309
  })
310
310
 
311
+ it('should not select a default token when isMiniPayRecipient is true and user has no MiniPay tokens', () => {
312
+ jest.mocked(getDynamicConfigParams).mockReturnValue({
313
+ miniPayTokenIds: ['celo-alfajores:0xNOT_HELD_BY_USER'],
314
+ })
315
+
316
+ const { getByText, queryByTestId, getByTestId } = render(
317
+ <Provider store={store}>
318
+ <MockedNavigator
319
+ component={SendEnterAmount}
320
+ params={{ ...params, isMiniPayRecipient: true }}
321
+ />
322
+ </Provider>
323
+ )
324
+
325
+ // "Select token" placeholder is rendered in place of the selected token
326
+ expect(getByText('tokenEnterAmount.selectToken')).toBeTruthy()
327
+ // Amount input, percentage options, and Review button are not rendered
328
+ expect(queryByTestId('SendEnterAmount/TokenAmountInput')).toBeNull()
329
+ expect(queryByTestId('SendEnterAmount/AmountOptions')).toBeNull()
330
+ expect(queryByTestId('SendEnterAmount/ReviewButton')).toBeNull()
331
+ // MiniPay filter chip is still present (inside the token picker)
332
+ expect(getByText('sendEnterAmountScreen.miniPayFilterChip')).toBeTruthy()
333
+ // Tapping the token row opens the picker
334
+ fireEvent.press(getByTestId('SendEnterAmount/TokenSelect'))
335
+ expect(getByTestId('TokenBottomSheet')).toBeTruthy()
336
+ })
337
+
311
338
  it('should include isMiniPayRecipient in send_amount_continue analytics', async () => {
312
339
  jest.mocked(usePrepareSendTransactions).mockReturnValue({
313
340
  prepareTransactionsResult: mockPrepareTransactionsResultPossible,