wallet-stack 1.0.0-alpha.127 → 1.0.0-alpha.129

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.
Files changed (65) hide show
  1. package/locales/de/translation.json +2 -1
  2. package/locales/en-US/translation.json +2 -1
  3. package/locales/es-419/translation.json +2 -1
  4. package/locales/fr-FR/translation.json +2 -1
  5. package/locales/it-IT/translation.json +2 -1
  6. package/locales/pl-PL/translation.json +2 -1
  7. package/locales/pt-BR/translation.json +2 -1
  8. package/locales/ru-RU/translation.json +2 -1
  9. package/locales/th-TH/translation.json +2 -1
  10. package/locales/tr-TR/translation.json +2 -1
  11. package/locales/uk-UA/translation.json +2 -1
  12. package/locales/vi-VN/translation.json +2 -1
  13. package/locales/zh-CN/translation.json +2 -1
  14. package/metro-config.js +1 -1
  15. package/package.json +18 -13
  16. package/src/account/LicenseDisclaimer.txt +4 -4
  17. package/src/account/ProfileSubmenu.test.tsx +1 -1
  18. package/src/account/SecuritySubmenu.test.tsx +2 -2
  19. package/src/account/saga.test.ts +1 -1
  20. package/src/analytics/Properties.tsx +1 -0
  21. package/src/app/actions.ts +1 -1
  22. package/src/app/reducers.ts +1 -1
  23. package/src/app/saga.test.ts +1 -1
  24. package/src/app/saga.ts +1 -1
  25. package/src/backup/BackupPhrase.test.tsx +1 -1
  26. package/src/components/WebView.tsx +2 -2
  27. package/src/config.ts +1 -1
  28. package/src/fiatExchanges/BidaliScreen.test.tsx +1 -1
  29. package/src/fiatExchanges/BidaliScreen.tsx +1 -1
  30. package/src/fiatExchanges/ExternalExchanges.tsx +3 -2
  31. package/src/fiatExchanges/Spend.tsx +3 -2
  32. package/src/fiatconnect/LinkAccountScreen.tsx +3 -2
  33. package/src/fiatconnect/ReviewScreen.tsx +3 -2
  34. package/src/fiatconnect/TransferStatusScreen.tsx +4 -3
  35. package/src/i18n/i18n.test.ts +1 -1
  36. package/src/i18n/otaTranslations.test.ts +1 -1
  37. package/src/i18n/otaTranslations.ts +1 -1
  38. package/src/keylessBackup/KeylessBackupIntro.tsx +3 -2
  39. package/src/navigator/types.tsx +1 -0
  40. package/src/nfts/NftsLoadError.tsx +3 -2
  41. package/src/onboarding/registration/EnableBiometry.test.tsx +1 -1
  42. package/src/onboarding/registration/EnableBiometry.tsx +1 -1
  43. package/src/onboarding/steps.test.ts +1 -1
  44. package/src/onboarding/steps.ts +1 -1
  45. package/src/pincode/PincodeLock.test.tsx +1 -1
  46. package/src/pincode/PincodeSet.tsx +1 -1
  47. package/src/pincode/authentication.test.ts +1 -1
  48. package/src/pincode/authentication.ts +1 -1
  49. package/src/qrcode/utils.ts +1 -1
  50. package/src/send/EnterAmount.tsx +4 -0
  51. package/src/send/SendEnterAmount.test.tsx +108 -1
  52. package/src/send/SendEnterAmount.tsx +18 -9
  53. package/src/send/useSendFilterChips.ts +46 -0
  54. package/src/statsig/constants.ts +6 -0
  55. package/src/statsig/types.ts +1 -0
  56. package/src/storage/keychain.tsx +1 -1
  57. package/src/utils/Logger.test.ts +1 -1
  58. package/src/utils/Logger.ts +1 -1
  59. package/src/utils/accountChecker.test.ts +1 -1
  60. package/src/utils/aes.ts +1 -1
  61. package/src/utils/random.ts +1 -1
  62. package/src/verify/VerificationCodeInputScreen.test.tsx +1 -1
  63. package/src/verify/VerificationStartScreen.test.tsx +1 -1
  64. package/src/verify/hooks.test.tsx +1 -1
  65. package/src/webview/WebViewScreen.tsx +1 -1
@@ -710,7 +710,8 @@
710
710
  "title": "Lade deine Freunde ein und erhalte bis zu 12 cUSD",
711
711
  "body": "Sobald sie ihre Nummer verknüpfen und Guthaben zu {{appName}} hinzufügen, erhaltet ihr beide 0,50 cUSD."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Per SMS einladen",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Per SMS einladen",
715
716
  "shareMessage": "Hey! Melde dich bei {{appName}} wallet an. Ich nutze sie, um meine Ersparnisse in USD und Krypto zu vermehren, und du kannst sie in Sekundenschnelle mit nur einer Telefonnummer einrichten: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Invite friends and get up to 12 cUSD",
711
711
  "body": "Once they connect their number and add funds to {{appName}}, you’ll both receive 0.50 cUSD."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Invite with SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Invite with SMS",
715
716
  "shareMessage": "Hey! Sign up to {{appName}} wallet. I use it to grow my savings in USD and crypto, and you can set it up in seconds with just a phone number: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Invita a amigos y consigue hasta 12 cUSD",
711
711
  "body": "Una vez que conecten su número y agreguen fondos a {{appName}}, ambos recibirán 0,50 cUSD."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Invitar por SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Invitar por SMS",
715
716
  "shareMessage": "Hola, regístrate en la cartera {{appName}} . Yo la utilizo para hacer crecer mis ahorros en USD y criptomonedas, y tú puedes configurarla en segundos con sólo un número de teléfono: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Invitez des amis et obtenez jusqu'à 12 cUSD",
711
711
  "body": "Une fois que votre contact aura relié son numéro et ajouté des fonds à {{appName}}, vous recevrez tous les deux 0,50 cUSD."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Inviter par SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Inviter par SMS",
715
716
  "shareMessage": "Hé ! Inscris-toi à {{appName}} wallet. Je l'utilise pour faire fructifier mes économies en USD et en crypto, et tu peux le configurer en quelques secondes avec un simple numéro de téléphone : {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Invita gli amici e guadagni fino a 12 cUSD",
711
711
  "body": "Dopo che avranno collegato il loro numero e avranno depositato dei fondi su {{appName}}, riceverete entrambi 0,50 cUSD."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Invita con SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Invita con SMS",
715
716
  "shareMessage": "Ehi! Iscriviti a {{appName}} wallet. Io lo uso per far crescere i miei risparmi in USD e criptovalute, e tu puoi configurarlo in pochi secondi con un semplice numero di telefono: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Zaproś znajomych i zdobądź do 12 cUSD",
711
711
  "body": "Gdy połączą swój numer i dodadzą środki do {{appName}}, otrzymacie po 0,50 cUSD."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Zaproś przez SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Zaproś przez SMS",
715
716
  "shareMessage": "Zarejestruj się w portfelu {{appName}} . Używam go do powiększania moich oszczędności w USD i kryptowalutach, a możesz go skonfigurować w kilka sekund, podając tylko numer telefonu: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Indique um amigo e ganhe USDT",
711
711
  "body": "Depois que a pessoa conectar seu número à {{appName}}, vocês dois receberão 1 USDT."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Convidar com SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Convidar com SMS",
715
716
  "shareMessage": "Ei, inscreva-se na carteira {{appName}} . Eu a uso para aumentar minhas economias em dólares e criptomoedas, e você pode configurá-la em segundos com apenas um número de telefone: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Пригласите друзей и получите до 12 cUSD",
711
711
  "body": "Как только они подключат свои номера и внесут средства в {{appName}}, вы получите по 0,50 cUSD."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Пригласить с помощью SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Пригласить с помощью SMS",
715
716
  "shareMessage": "Привет! Зарегистрируйся на кошельке {{appName}} . Я использую его для приумножения своих сбережений в долларах и криптовалютах, а ты можешь настроить его за считанные секунды, указав лишь номер телефона: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "เชิญเพื่อนและรับสูงสุด 12 cUSD",
711
711
  "body": "เมื่อเพื่อนเชื่อมต่อเบอร์และเพิ่มเงินทุนลงใน {{appName}} คุณทั้งคู่จะได้รับ 0.50 cUSD"
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "เชิญด้วย SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "เชิญด้วย SMS",
715
716
  "shareMessage": "Hey! Sign up to {{appName}} wallet. I use it to grow my savings in USD and crypto, and you can set it up in seconds with just a phone number: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Arkadaşlarınızı davet edin ve 12 cUSD'ye kadar kazanın",
711
711
  "body": "Numaralarını bağladıklarında ve {{appName}} uygulamasına fon eklediklerinde her ikiniz de 0,50 cUSD alacaksınız."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "SMS ile davet et",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "SMS ile davet et",
715
716
  "shareMessage": "Hey! {{appName}} cüzdanına kaydolun. Birikimlerimi USD ve kripto olarak artırmak için kullanıyorum ve siz de sadece bir telefon numarasıyla saniyeler içinde kurabilirsiniz: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Запрошуйте друзів і отримуйте до 12 cUSD",
711
711
  "body": "Щойно вони під’єднають свій номер і покладуть кошти у {{appName}}, кожен із вас отримає по 0,50 cUSD."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Запросити з SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Запросити з SMS",
715
716
  "shareMessage": "Привіт! Підписуйся на гаманець {{appName}} . Я використовую його для примноження своїх заощаджень у доларах та криптовалюті, а ви можете створити його за лічені секунди, маючи лише номер телефону: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "Giới thiệu bạn bè và nhận USDT",
711
711
  "body": "Khi họ kết nối số điện thoại, cả hai bạn sẽ nhận được 1 USDT mỗi người."
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "Mời bằng SMS",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "Mời bằng SMS",
715
716
  "shareMessage": "Hey! Sign up to {{appName}} wallet. I use it to grow my savings in USD and crypto, and you can set it up in seconds with just a phone number: {{shareUrl}}."
716
717
  },
@@ -710,7 +710,8 @@
710
710
  "title": "邀请朋友,最多可获得 12 cUSD",
711
711
  "body": "他们连接了自己的号码并将资金添加到 {{appName}} 后,你们都将收到 0.50 cUSD。"
712
712
  },
713
- "inviteWithSMS": {
713
+ "inviteWithSMS": "通过短信邀请",
714
+ "inviteWithSmsMessage": {
714
715
  "title": "通过短信邀请",
715
716
  "shareMessage": "嘿!注册 {{appName}} 钱包。我用它来增加我的美元和加密货币储蓄,你只需一个电话号码就可以在几秒钟内设置好: {{shareUrl}}。"
716
717
  },
package/metro-config.js CHANGED
@@ -18,7 +18,7 @@ function getDefaultConfig(...args) {
18
18
  // This is the crypto module we want to use moving forward (unless something better comes up).
19
19
  // It is implemented natively using OpenSSL.
20
20
  crypto: require.resolve('react-native-quick-crypto'),
21
- fs: require.resolve('@divvi/react-native-fs'),
21
+ fs: require.resolve('@valora/react-native-fs'),
22
22
  stream: require.resolve('readable-stream'),
23
23
  buffer: require.resolve('@craftzdog/react-native-buffer'),
24
24
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wallet-stack",
3
- "version": "1.0.0-alpha.127",
3
+ "version": "1.0.0-alpha.129",
4
4
  "author": "Valora Inc",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -60,11 +60,7 @@
60
60
  "test:update-root-state-schema": "./scripts/update_root_state_schema.sh"
61
61
  },
62
62
  "peerDependencies": {
63
- "@divvi/cookies": "^6.2.3",
64
- "@divvi/react-native-fs": "^2.20.1",
65
- "@divvi/react-native-keychain": "^10.0.0-divvi.1",
66
63
  "@google-cloud/recaptcha-enterprise-react-native": "^18.8.0",
67
- "@interaxyz/react-native-webview": "^13.13.4",
68
64
  "@react-native-async-storage/async-storage": "^2.2.0",
69
65
  "@react-native-clipboard/clipboard": "^1.16.3",
70
66
  "@react-native-community/netinfo": "^11.4.1",
@@ -88,7 +84,11 @@
88
84
  "@segment/sovran-react-native": "^1.1.3",
89
85
  "@sentry/react-native": "^6.20.0",
90
86
  "@statsig/react-native-bindings": "^3.17.1",
91
- "@walletconnect/react-native-compat": "^2.21.9",
87
+ "@valora/react-native-cookies": "^6.2.3",
88
+ "@valora/react-native-fs": "^2.20.1",
89
+ "@valora/react-native-keychain": "^10.0.0-valora.1",
90
+ "@valora/react-native-webview": "^13.13.4",
91
+ "@walletconnect/react-native-compat": "2.21.9",
92
92
  "expo-camera": "~16.1.11",
93
93
  "expo-splash-screen": "~0.30.10",
94
94
  "lottie-react-native": "^5.1.6",
@@ -107,9 +107,11 @@
107
107
  "react-native-launch-arguments": "^4.0.2",
108
108
  "react-native-linear-gradient": "^2.8.3",
109
109
  "react-native-localize": "^3.3.0",
110
+ "react-native-nitro-modules": ">=0.29.1",
110
111
  "react-native-pager-view": "^6.7.1",
111
112
  "react-native-permissions": "^4.1.5",
112
- "react-native-quick-crypto": "0.7.7",
113
+ "react-native-quick-base64": ">=2.1.0",
114
+ "react-native-quick-crypto": "^1.0.16",
113
115
  "react-native-reanimated": "~3.17.4",
114
116
  "react-native-restart": "^0.0.27",
115
117
  "react-native-safe-area-context": "^5.6.1",
@@ -123,8 +125,8 @@
123
125
  "dependencies": {
124
126
  "@badrap/result": "~0.2.13",
125
127
  "@crowdin/ota-client": "^2.0.1",
126
- "@fiatconnect/fiatconnect-sdk": "^0.5.74",
127
- "@fiatconnect/fiatconnect-types": "^13.3.11",
128
+ "@fiatconnect/fiatconnect-sdk": "^0.5.163",
129
+ "@fiatconnect/fiatconnect-types": "^13.3.50",
128
130
  "@gorhom/bottom-sheet": "^5.1.1",
129
131
  "@interaxyz/react-navigation-bottom-sheet": "^0.3.2",
130
132
  "@json-rpc-tools/utils": "^1.7.6",
@@ -142,7 +144,7 @@
142
144
  "@types/lodash": "^4.17.15",
143
145
  "@types/seedrandom": "^3.0.8",
144
146
  "@types/uuid": "^10.0.0",
145
- "@walletconnect/utils": "^2.21.9",
147
+ "@walletconnect/utils": "2.21.9",
146
148
  "bignumber.js": "^9.1.2",
147
149
  "country-data": "^0.0.31",
148
150
  "date-fns": "^4.1.0",
@@ -160,16 +162,19 @@
160
162
  "io-ts": "2.2.22",
161
163
  "is-ip": "^3.1.0",
162
164
  "jwt-decode": "^4.0.0",
163
- "lodash": "^4.17.21",
165
+ "lodash": "^4.17.23",
164
166
  "mixpanel-react-native": "^3.2.1",
165
167
  "permissionless": "^0.2.57",
166
168
  "react-async-hook": "^4.0.0",
167
169
  "react-i18next": "^15.4.1",
168
170
  "react-native-android-open-settings": "^1.3.0",
169
171
  "react-native-modal": "^14.0.0-rc.1",
172
+ "react-native-nitro-modules": ">=0.29.1",
170
173
  "react-native-picker-select": "^9.3.1",
171
174
  "react-native-platform-touchable": "^1.1.1",
172
175
  "react-native-qrcode-svg": "^6.3.12",
176
+ "react-native-quick-base64": ">=2.1.0",
177
+ "react-native-quick-crypto": "^1.0.16",
173
178
  "react-native-skeleton-placeholder": "^5.2.4",
174
179
  "react-native-url-polyfill": "^2.0.0",
175
180
  "react-redux": "^9.2.0",
@@ -198,8 +203,8 @@
198
203
  "@types/react-native-video": "^5.0.15",
199
204
  "@types/react-test-renderer": "^19.0.0",
200
205
  "@types/redux-mock-store": "^1.0.6",
201
- "@walletconnect/types": "^2.21.9",
202
- "ajv": "^8.10.0",
206
+ "@walletconnect/types": "2.21.9",
207
+ "ajv": "^8.18.0",
203
208
  "babel-jest": "^29.2.1",
204
209
  "jest": "^29.6.2",
205
210
  "jest-fetch-mock": "^3.0.3",
@@ -390,7 +390,7 @@ SOFTWARE.
390
390
 
391
391
  -----
392
392
 
393
- The following software may be included in this product: @divvi/cookies. A copy of the source code may be downloaded from git+https://github.com/divvixyz/cookies.git. This software contains the following license and notice below:
393
+ The following software may be included in this product: @valora/react-native-cookies. A copy of the source code may be downloaded from git+https://github.com/valora-xyz/react-native-cookies.git. This software contains the following license and notice below:
394
394
 
395
395
  MIT License
396
396
 
@@ -416,7 +416,7 @@ SOFTWARE.
416
416
 
417
417
  -----
418
418
 
419
- The following software may be included in this product: @divvi/react-native-fs, react-native-fs. A copy of the source code may be downloaded from git+https@https://github.com/divvixyz/react-native-fs.git (@divvi/react-native-fs), git@github.com:itinance/react-native-fs.git (react-native-fs). This software contains the following license and notice below:
419
+ The following software may be included in this product: @valora/react-native-fs, react-native-fs. A copy of the source code may be downloaded from git+https@https://github.com/valora-xyz/react-native-fs.git (@valora/react-native-fs), git@github.com:itinance/react-native-fs.git (react-native-fs). This software contains the following license and notice below:
420
420
 
421
421
  The MIT License (MIT)
422
422
 
@@ -442,7 +442,7 @@ SOFTWARE.
442
442
 
443
443
  -----
444
444
 
445
- The following software may be included in this product: @divvi/react-native-keychain, react-native-animatable. A copy of the source code may be downloaded from git+https://github.com/mobilestack-xyz/react-native-keychain.git (@divvi/react-native-keychain), git://github.com/oblador/react-native-animatable.git (react-native-animatable). This software contains the following license and notice below:
445
+ The following software may be included in this product: @valora/react-native-keychain, react-native-animatable. A copy of the source code may be downloaded from git+https://github.com/valora-xyz/react-native-keychain.git (@valora/react-native-keychain), git://github.com/oblador/react-native-animatable.git (react-native-animatable). This software contains the following license and notice below:
446
446
 
447
447
  The MIT License (MIT)
448
448
 
@@ -631,7 +631,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
631
631
 
632
632
  -----
633
633
 
634
- The following software may be included in this product: @interaxyz/react-native-webview, @react-native-community/netinfo, @react-native-masked-view/masked-view, @react-native-picker/picker. A copy of the source code may be downloaded from git+https://github.com/interaxyz/react-native-webview.git (@interaxyz/react-native-webview), https://github.com/react-native-netinfo/react-native-netinfo.git (@react-native-community/netinfo), git+https://github.com/react-native-masked-view/masked-view.git (@react-native-masked-view/masked-view), https://github.com/react-native-picker/picker.git (@react-native-picker/picker). This software contains the following license and notice below:
634
+ The following software may be included in this product: @valora/react-native-webview, @react-native-community/netinfo, @react-native-masked-view/masked-view, @react-native-picker/picker. A copy of the source code may be downloaded from git+https://github.com/valora-xyz/react-native-webview.git (@valora/react-native-webview), https://github.com/react-native-netinfo/react-native-netinfo.git (@react-native-community/netinfo), git+https://github.com/react-native-masked-view/masked-view.git (@react-native-masked-view/masked-view), https://github.com/react-native-picker/picker.git (@react-native-picker/picker). This software contains the following license and notice below:
635
635
 
636
636
  MIT License
637
637
 
@@ -1,5 +1,5 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
2
1
  import { act, fireEvent, render, waitFor } from '@testing-library/react-native'
2
+ import * as Keychain from '@valora/react-native-keychain'
3
3
  import { FetchMock } from 'jest-fetch-mock/types'
4
4
  import * as React from 'react'
5
5
  import 'react-native'
@@ -1,6 +1,6 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
2
- import { BIOMETRY_TYPE } from '@divvi/react-native-keychain'
3
1
  import { act, fireEvent, render, waitFor } from '@testing-library/react-native'
2
+ import * as Keychain from '@valora/react-native-keychain'
3
+ import { BIOMETRY_TYPE } from '@valora/react-native-keychain'
4
4
  import { FetchMock } from 'jest-fetch-mock/types'
5
5
  import * as React from 'react'
6
6
  import 'react-native'
@@ -1,4 +1,4 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
1
+ import * as Keychain from '@valora/react-native-keychain'
2
2
  import { FetchMock } from 'jest-fetch-mock/types'
3
3
  import { expectSaga } from 'redux-saga-test-plan'
4
4
  import * as matchers from 'redux-saga-test-plan/matchers'
@@ -493,6 +493,7 @@ interface SendEventsProperties {
493
493
  amountEnteredIn: AmountEnteredIn
494
494
  tokenId: string | null
495
495
  networkId: string | null
496
+ isMiniPayRecipient: boolean
496
497
  }
497
498
  [SendEvents.send_confirm_back]: undefined
498
499
  [SendEvents.send_confirm_send]:
@@ -1,4 +1,4 @@
1
- import { BIOMETRY_TYPE } from '@divvi/react-native-keychain'
1
+ import { BIOMETRY_TYPE } from '@valora/react-native-keychain'
2
2
  import { Screens } from 'src/navigator/Screens'
3
3
 
4
4
  // https://facebook.github.io/react-native/docs/appstate
@@ -1,4 +1,4 @@
1
- import { BIOMETRY_TYPE } from '@divvi/react-native-keychain'
1
+ import { BIOMETRY_TYPE } from '@valora/react-native-keychain'
2
2
  import { Platform } from 'react-native'
3
3
  import { Actions, ActionTypes, AppState } from 'src/app/actions'
4
4
  import { DEEP_LINK_URL_SCHEME } from 'src/config'
@@ -1,4 +1,4 @@
1
- import { BIOMETRY_TYPE } from '@divvi/react-native-keychain'
1
+ import { BIOMETRY_TYPE } from '@valora/react-native-keychain'
2
2
  import * as RNLocalize from 'react-native-localize'
3
3
  import { expectSaga } from 'redux-saga-test-plan'
4
4
  import * as matchers from 'redux-saga-test-plan/matchers'
package/src/app/saga.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
1
+ import * as Keychain from '@valora/react-native-keychain'
2
2
  import locales from 'locales'
3
3
  import { AppState, Platform } from 'react-native'
4
4
  import DeviceInfo from 'react-native-device-info'
@@ -1,5 +1,5 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
2
1
  import { fireEvent, render } from '@testing-library/react-native'
2
+ import * as Keychain from '@valora/react-native-keychain'
3
3
  import * as React from 'react'
4
4
  import 'react-native'
5
5
  import { Provider } from 'react-redux'
@@ -1,5 +1,5 @@
1
- import { WebView as RNWebView, WebViewProps } from '@interaxyz/react-native-webview'
2
- import { WebViewErrorEvent } from '@interaxyz/react-native-webview/lib/WebViewTypes'
1
+ import { WebView as RNWebView, WebViewProps } from '@valora/react-native-webview'
2
+ import { WebViewErrorEvent } from '@valora/react-native-webview/lib/WebViewTypes'
3
3
  import React from 'react'
4
4
  import { Platform, StyleSheet } from 'react-native'
5
5
  import Logger from 'src/utils/Logger'
package/src/config.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { CachesDirectoryPath } from '@divvi/react-native-fs'
2
1
  import { Network } from '@fiatconnect/fiatconnect-types'
2
+ import { CachesDirectoryPath } from '@valora/react-native-fs'
3
3
  import Config from 'react-native-config'
4
4
  import { SpendMerchant } from 'src/fiatExchanges/Spend'
5
5
  import { LoggerLevel } from 'src/utils/LoggerLevels'
@@ -1,5 +1,5 @@
1
- import { WebView } from '@interaxyz/react-native-webview'
2
1
  import { render } from '@testing-library/react-native'
2
+ import { WebView } from '@valora/react-native-webview'
3
3
  import * as React from 'react'
4
4
  import { Provider } from 'react-redux'
5
5
  import { getAppConfig } from 'src/appConfig'
@@ -1,5 +1,5 @@
1
- import { WebViewMessageEvent } from '@interaxyz/react-native-webview'
2
1
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
2
+ import { WebViewMessageEvent } from '@valora/react-native-webview'
3
3
  import React, { useEffect, useMemo, useRef, useState } from 'react'
4
4
  import { ActivityIndicator, StyleSheet } from 'react-native'
5
5
  import { SafeAreaView } from 'react-native-safe-area-context'
@@ -1,7 +1,8 @@
1
1
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
2
2
  import React from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
- import { SafeAreaView, ScrollView, StyleSheet, Text, View } from 'react-native'
4
+ import { ScrollView, StyleSheet, Text, View } from 'react-native'
5
+ import { SafeAreaView } from 'react-native-safe-area-context'
5
6
  import { FiatExchangeEvents } from 'src/analytics/Events'
6
7
  import AppAnalytics from 'src/analytics/AppAnalytics'
7
8
  import BackButton from 'src/components/BackButton'
@@ -55,7 +56,7 @@ function ExternalExchanges({ route }: Props) {
55
56
  }
56
57
 
57
58
  return (
58
- <SafeAreaView style={styles.container}>
59
+ <SafeAreaView style={styles.container} edges={['bottom']}>
59
60
  <Text style={styles.pleaseSelectExchange}>
60
61
  {t('youCanTransferOut', {
61
62
  digitalAsset: tokenInfo?.symbol,
@@ -1,7 +1,8 @@
1
1
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
2
2
  import React from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
- import { SafeAreaView, ScrollView, StyleSheet, Text, View } from 'react-native'
4
+ import { ScrollView, StyleSheet, Text, View } from 'react-native'
5
+ import { SafeAreaView } from 'react-native-safe-area-context'
5
6
  import AppAnalytics from 'src/analytics/AppAnalytics'
6
7
  import { FiatExchangeEvents } from 'src/analytics/Events'
7
8
  import BackButton from 'src/components/BackButton'
@@ -54,7 +55,7 @@ function Spend(props: Props) {
54
55
 
55
56
  return (
56
57
  <ScrollView style={styles.container}>
57
- <SafeAreaView>
58
+ <SafeAreaView edges={['bottom']}>
58
59
  <Text style={styles.pleaseSelectProvider}>{t('useBalanceWithMerchants')}</Text>
59
60
  <View>
60
61
  {merchants
@@ -3,7 +3,8 @@ import { RouteProp } from '@react-navigation/native'
3
3
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
4
4
  import React from 'react'
5
5
  import { Trans, useTranslation } from 'react-i18next'
6
- import { SafeAreaView, StyleSheet, Text } from 'react-native'
6
+ import { StyleSheet, Text } from 'react-native'
7
+ import { SafeAreaView } from 'react-native-safe-area-context'
7
8
  import AppAnalytics from 'src/analytics/AppAnalytics'
8
9
  import { FiatExchangeEvents } from 'src/analytics/Events'
9
10
  import BackButton from 'src/components/BackButton'
@@ -105,7 +106,7 @@ function LinkAccountSection(props: {
105
106
  }
106
107
 
107
108
  return (
108
- <SafeAreaView style={styles.content}>
109
+ <SafeAreaView style={styles.content} edges={['bottom']}>
109
110
  <Text style={styles.title}>{t(bodyTitle)}</Text>
110
111
  <Text testID="descriptionText" style={styles.description}>
111
112
  <Trans i18nKey={description} values={{ providerName: quote.getProviderName() }}>
@@ -5,7 +5,8 @@ import BigNumber from 'bignumber.js'
5
5
  import React, { useEffect, useLayoutEffect, useMemo, useState } from 'react'
6
6
  import { useAsync } from 'react-async-hook'
7
7
  import { useTranslation } from 'react-i18next'
8
- import { ActivityIndicator, BackHandler, SafeAreaView, StyleSheet, Text, View } from 'react-native'
8
+ import { ActivityIndicator, BackHandler, StyleSheet, Text, View } from 'react-native'
9
+ import { SafeAreaView } from 'react-native-safe-area-context'
9
10
  import AppAnalytics from 'src/analytics/AppAnalytics'
10
11
  import { FiatExchangeEvents } from 'src/analytics/Events'
11
12
  import BackButton from 'src/components/BackButton'
@@ -251,7 +252,7 @@ export default function FiatConnectReviewScreen({ route, navigation }: Props) {
251
252
  }
252
253
 
253
254
  return (
254
- <SafeAreaView style={styles.content}>
255
+ <SafeAreaView style={styles.content} edges={['bottom']}>
255
256
  <Dialog
256
257
  testID="expiredQuoteDialog"
257
258
  isVisible={showingExpiredQuoteDialog}
@@ -1,7 +1,8 @@
1
1
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
2
2
  import React, { useEffect, useState, type JSX } from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
- import { ActivityIndicator, SafeAreaView, StyleSheet, Text, View } from 'react-native'
4
+ import { ActivityIndicator, StyleSheet, Text, View } from 'react-native'
5
+ import { SafeAreaView } from 'react-native-safe-area-context'
5
6
  import AppAnalytics from 'src/analytics/AppAnalytics'
6
7
  import { FiatExchangeEvents } from 'src/analytics/Events'
7
8
  import BackButton from 'src/components/BackButton'
@@ -269,7 +270,7 @@ export default function FiatConnectTransferStatusScreen({ route, navigation }: P
269
270
  ),
270
271
  })
271
272
  return (
272
- <SafeAreaView style={styles.content}>
273
+ <SafeAreaView style={styles.content} edges={['bottom']}>
273
274
  <FailureSection flow={flow} normalizedQuote={normalizedQuote} fiatAccount={fiatAccount} />
274
275
  </SafeAreaView>
275
276
  )
@@ -282,7 +283,7 @@ export default function FiatConnectTransferStatusScreen({ route, navigation }: P
282
283
  // intentionally falls thru since TxProcessing and Completed use the same component
283
284
  case SendingTransferStatus.TxProcessing:
284
285
  return (
285
- <SafeAreaView style={styles.content}>
286
+ <SafeAreaView style={styles.content} edges={['bottom']}>
286
287
  <SuccessOrProcessingSection
287
288
  status={fiatConnectTransfer.status}
288
289
  flow={flow}
@@ -1,6 +1,6 @@
1
1
  import i18next from 'i18next'
2
2
 
3
- jest.mock('@divvi/react-native-fs', () => {
3
+ jest.mock('@valora/react-native-fs', () => {
4
4
  return {
5
5
  exists: jest.fn().mockResolvedValue(true),
6
6
  readFile: jest.fn().mockResolvedValue('{"en-US":{"someKey":"Hello!"}}'),
@@ -1,4 +1,4 @@
1
- import * as RNFS from '@divvi/react-native-fs'
1
+ import * as RNFS from '@valora/react-native-fs'
2
2
  import { OTA_TRANSLATIONS_FILEPATH } from 'src/config'
3
3
  import { getOtaTranslations, saveOtaTranslations } from 'src/i18n/otaTranslations'
4
4
 
@@ -1,4 +1,4 @@
1
- import * as RNFS from '@divvi/react-native-fs'
1
+ import * as RNFS from '@valora/react-native-fs'
2
2
  import { Resource } from 'i18next'
3
3
  import { OTA_TRANSLATIONS_FILEPATH } from 'src/config'
4
4
 
@@ -1,7 +1,8 @@
1
1
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
2
2
  import React from 'react'
3
3
  import { Trans, useTranslation } from 'react-i18next'
4
- import { SafeAreaView, ScrollView, StyleSheet, Text, View } from 'react-native'
4
+ import { ScrollView, StyleSheet, Text, View } from 'react-native'
5
+ import { SafeAreaView } from 'react-native-safe-area-context'
5
6
  import AppAnalytics from 'src/analytics/AppAnalytics'
6
7
  import { KeylessBackupEvents } from 'src/analytics/Events'
7
8
  import BackButton from 'src/components/BackButton'
@@ -33,7 +34,7 @@ function KeylessBackupIntro({ route }: Props) {
33
34
  const { t } = useTranslation()
34
35
 
35
36
  return (
36
- <SafeAreaView style={styles.container}>
37
+ <SafeAreaView style={styles.container} edges={['bottom']}>
37
38
  <ScrollView style={styles.scrollContainer}>
38
39
  {isSetup && <Text style={styles.title}>{t('keylessBackupIntro.setup.title')}</Text>}
39
40
  <Text
@@ -42,6 +42,7 @@ type SendEnterAmountParams = {
42
42
  origin: SendOrigin
43
43
  forceTokenId?: boolean
44
44
  defaultTokenIdOverride?: string
45
+ isMiniPayRecipient?: boolean
45
46
  }
46
47
 
47
48
  interface ValidateRecipientParams {
@@ -1,6 +1,7 @@
1
1
  import React, { useEffect } from 'react'
2
2
  import { Trans, useTranslation } from 'react-i18next'
3
- import { SafeAreaView, ScrollView, StyleSheet, Text, View } from 'react-native'
3
+ import { ScrollView, StyleSheet, Text, View } from 'react-native'
4
+ import { SafeAreaView } from 'react-native-safe-area-context'
4
5
  import AppAnalytics from 'src/analytics/AppAnalytics'
5
6
  import { NftEvents } from 'src/analytics/Events'
6
7
  import Touchable from 'src/components/Touchable'
@@ -31,7 +32,7 @@ export default function NftsLoadError({ testID }: Props) {
31
32
  }, [])
32
33
 
33
34
  return (
34
- <SafeAreaView style={styles.safeArea} testID={testID}>
35
+ <SafeAreaView style={styles.safeArea} testID={testID} edges={['bottom']}>
35
36
  <ScrollView
36
37
  contentContainerStyle={styles.contentContainerStyle}
37
38
  style={styles.scrollContainer}
@@ -1,5 +1,5 @@
1
- import { BIOMETRY_TYPE } from '@divvi/react-native-keychain'
2
1
  import { act, fireEvent, render } from '@testing-library/react-native'
2
+ import { BIOMETRY_TYPE } from '@valora/react-native-keychain'
3
3
  import * as React from 'react'
4
4
  import { Provider } from 'react-redux'
5
5
  import { setPincodeSuccess } from 'src/account/actions'
@@ -1,6 +1,6 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
2
1
  import { useHeaderHeight } from '@react-navigation/elements'
3
2
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
3
+ import * as Keychain from '@valora/react-native-keychain'
4
4
  import React, { useEffect, useLayoutEffect, type JSX } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
6
  import { Image, ScrollView, StyleSheet, Text, View } from 'react-native'
@@ -1,4 +1,4 @@
1
- import { BIOMETRY_TYPE } from '@divvi/react-native-keychain'
1
+ import { BIOMETRY_TYPE } from '@valora/react-native-keychain'
2
2
  import { initializeAccount } from 'src/account/actions'
3
3
  import { KeylessBackupFlow } from 'src/keylessBackup/types'
4
4
  import { navigate, navigateClearingStack, popToScreen } from 'src/navigator/NavigationService'
@@ -1,4 +1,4 @@
1
- import { BIOMETRY_TYPE } from '@divvi/react-native-keychain'
1
+ import { BIOMETRY_TYPE } from '@valora/react-native-keychain'
2
2
  import { createSelector } from 'reselect'
3
3
  import { initializeAccount } from 'src/account/actions'
4
4
  import {
@@ -1,5 +1,5 @@
1
- import { BIOMETRY_TYPE } from '@divvi/react-native-keychain'
2
1
  import { act, fireEvent, render, waitFor } from '@testing-library/react-native'
2
+ import { BIOMETRY_TYPE } from '@valora/react-native-keychain'
3
3
  import * as React from 'react'
4
4
  import { Provider } from 'react-redux'
5
5
  import { PincodeType } from 'src/account/reducer'
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * This is a reactnavigation SCREEN, which we use to set a PIN.
3
3
  */
4
- import { BIOMETRY_TYPE } from '@divvi/react-native-keychain'
5
4
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
5
+ import { BIOMETRY_TYPE } from '@valora/react-native-keychain'
6
6
  import * as React from 'react'
7
7
  import { WithTranslation } from 'react-i18next'
8
8
  import { StyleSheet } from 'react-native'
@@ -1,4 +1,4 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
1
+ import * as Keychain from '@valora/react-native-keychain'
2
2
  import { expectSaga } from 'redux-saga-test-plan'
3
3
  import { select } from 'redux-saga/effects'
4
4
  import { PincodeType } from 'src/account/reducer'
@@ -5,7 +5,7 @@
5
5
  * The password is a combination of the two. It is used for unlocking the account in the keychain
6
6
  */
7
7
 
8
- import * as Keychain from '@divvi/react-native-keychain'
8
+ import * as Keychain from '@valora/react-native-keychain'
9
9
  import crypto from 'crypto'
10
10
  import { PincodeType } from 'src/account/reducer'
11
11
  import { pincodeTypeSelector } from 'src/account/selectors'
@@ -1,4 +1,4 @@
1
- import * as RNFS from '@divvi/react-native-fs'
1
+ import * as RNFS from '@valora/react-native-fs'
2
2
  import { useMemo } from 'react'
3
3
  import Share from 'react-native-share'
4
4
  import { showError, showMessage } from 'src/alert/actions'
@@ -10,6 +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
14
  import InLineNotification, { NotificationVariant } from 'src/components/InLineNotification'
14
15
  import KeyboardAwareScrollView from 'src/components/KeyboardAwareScrollView'
15
16
  import { ReviewDetailsItem } from 'src/components/ReviewTransaction'
@@ -66,6 +67,7 @@ interface Props {
66
67
  children?: React.ReactNode
67
68
  ProceedComponent: ComponentType<ProceedComponentProps>
68
69
  disableBalanceCheck?: boolean
70
+ filterChips?: FilterChip<TokenBalance>[]
69
71
  }
70
72
 
71
73
  export const SendProceed = ({
@@ -107,6 +109,7 @@ export default function EnterAmount({
107
109
  children,
108
110
  ProceedComponent,
109
111
  disableBalanceCheck = false,
112
+ filterChips,
110
113
  }: Props) {
111
114
  const { t } = useTranslation()
112
115
  const insets = useSafeAreaInsets()
@@ -325,6 +328,7 @@ export default function EnterAmount({
325
328
  tokens={tokens}
326
329
  title={t('sendEnterAmountScreen.selectToken')}
327
330
  titleStyle={styles.title}
331
+ filterChips={filterChips}
328
332
  />
329
333
  </SafeAreaView>
330
334
  )
@@ -1,4 +1,4 @@
1
- import { fireEvent, render, waitFor } from '@testing-library/react-native'
1
+ import { fireEvent, render, waitFor, within } from '@testing-library/react-native'
2
2
  import BigNumber from 'bignumber.js'
3
3
  import React from 'react'
4
4
  import { Provider } from 'react-redux'
@@ -10,6 +10,7 @@ import { Screens } from 'src/navigator/Screens'
10
10
  import { RecipientType } from 'src/recipients/recipient'
11
11
  import SendEnterAmount from 'src/send/SendEnterAmount'
12
12
  import { usePrepareSendTransactions } from 'src/send/usePrepareSendTransactions'
13
+ import { getDynamicConfigParams } from 'src/statsig'
13
14
  import { getSerializablePreparedTransactionsPossible } from 'src/viem/preparedTransactionSerialization'
14
15
  import { PreparedTransactionsPossible } from 'src/viem/prepareTransactions'
15
16
  import MockedNavigator from 'test/MockedNavigator'
@@ -29,6 +30,10 @@ import {
29
30
  jest.mock('src/statsig')
30
31
  jest.mock('src/send/usePrepareSendTransactions')
31
32
 
33
+ jest.mocked(getDynamicConfigParams).mockReturnValue({
34
+ miniPayTokenIds: [],
35
+ })
36
+
32
37
  const mockPrepareTransactionsResultPossible: PreparedTransactionsPossible = {
33
38
  type: 'possible',
34
39
  transactions: [
@@ -164,6 +169,7 @@ describe('SendEnterAmount', () => {
164
169
  underlyingTokenAddress: mockCeloAddress,
165
170
  underlyingTokenSymbol: 'CELO',
166
171
  amountEnteredIn: 'token',
172
+ isMiniPayRecipient: false,
167
173
  })
168
174
  expect(navigate).toHaveBeenCalledWith(Screens.SendConfirmation, {
169
175
  origin: params.origin,
@@ -232,4 +238,105 @@ describe('SendEnterAmount', () => {
232
238
  expect(getByTestId('SendEnterAmount/TokenSelect')).toHaveTextContent('cUSD', { exact: false })
233
239
  expect(getByTestId('SendEnterAmount/TokenSelect')).toBeDisabled()
234
240
  })
241
+
242
+ describe('MiniPay filter', () => {
243
+ const miniPayTokenIds = [mockCusdTokenId, mockUSDCTokenId]
244
+
245
+ beforeEach(() => {
246
+ jest.mocked(getDynamicConfigParams).mockReturnValue({
247
+ miniPayTokenIds,
248
+ })
249
+ })
250
+
251
+ it('should show MiniPay chip pre-selected and only MiniPay tokens when isMiniPayRecipient is true', () => {
252
+ const { getAllByTestId, getByText } = render(
253
+ <Provider store={store}>
254
+ <MockedNavigator
255
+ component={SendEnterAmount}
256
+ params={{ ...params, isMiniPayRecipient: true }}
257
+ />
258
+ </Provider>
259
+ )
260
+
261
+ expect(getByText('MiniPay')).toBeTruthy()
262
+
263
+ const tokenBottomSheet = getAllByTestId('TokenBottomSheet')[0]
264
+ const tokens = within(tokenBottomSheet).getAllByTestId('TokenBalanceItem')
265
+ // only cUSD visible (USDC is on a different network and filtered by selector)
266
+ expect(tokens).toHaveLength(1)
267
+ expect(tokens[0]).toHaveTextContent('cUSD', { exact: false })
268
+ })
269
+
270
+ it('should select default token from MiniPay list', () => {
271
+ const { getByTestId } = render(
272
+ <Provider store={store}>
273
+ <MockedNavigator
274
+ component={SendEnterAmount}
275
+ params={{ ...params, isMiniPayRecipient: true }}
276
+ />
277
+ </Provider>
278
+ )
279
+
280
+ // cUSD is the only MiniPay token with balance, not CELO (which is excluded)
281
+ expect(getByTestId('SendEnterAmount/TokenSelect')).toHaveTextContent('cUSD', { exact: false })
282
+ })
283
+
284
+ it('should show all tokens when MiniPay chip is toggled off', () => {
285
+ const { getAllByTestId, getByText } = render(
286
+ <Provider store={store}>
287
+ <MockedNavigator
288
+ component={SendEnterAmount}
289
+ params={{ ...params, isMiniPayRecipient: true }}
290
+ />
291
+ </Provider>
292
+ )
293
+
294
+ fireEvent.press(getByText('MiniPay'))
295
+
296
+ const tokenBottomSheet = getAllByTestId('TokenBottomSheet')[0]
297
+ const tokens = within(tokenBottomSheet).getAllByTestId('TokenBalanceItem')
298
+ expect(tokens).toHaveLength(3)
299
+ })
300
+
301
+ it('should not show MiniPay chip when isMiniPayRecipient is not set', () => {
302
+ const { queryByText } = render(
303
+ <Provider store={store}>
304
+ <MockedNavigator component={SendEnterAmount} params={params} />
305
+ </Provider>
306
+ )
307
+
308
+ expect(queryByText('MiniPay')).toBeFalsy()
309
+ })
310
+
311
+ it('should include isMiniPayRecipient in send_amount_continue analytics', async () => {
312
+ jest.mocked(usePrepareSendTransactions).mockReturnValue({
313
+ prepareTransactionsResult: mockPrepareTransactionsResultPossible,
314
+ prepareTransactionLoading: false,
315
+ refreshPreparedTransactions: jest.fn(),
316
+ clearPreparedTransactions: jest.fn(),
317
+ prepareTransactionError: undefined,
318
+ })
319
+ const { getByTestId, getByText } = render(
320
+ <Provider store={store}>
321
+ <MockedNavigator
322
+ component={SendEnterAmount}
323
+ params={{ ...params, isMiniPayRecipient: true }}
324
+ />
325
+ </Provider>
326
+ )
327
+
328
+ fireEvent.changeText(getByTestId('SendEnterAmount/TokenAmountInput'), '8')
329
+
330
+ await waitFor(() => expect(getByText('review')).not.toBeDisabled())
331
+ fireEvent.press(getByText('review'))
332
+
333
+ await waitFor(() => expect(AppAnalytics.track).toHaveBeenCalledTimes(1))
334
+ expect(AppAnalytics.track).toHaveBeenCalledWith(
335
+ SendEvents.send_amount_continue,
336
+ expect.objectContaining({
337
+ isMiniPayRecipient: true,
338
+ })
339
+ )
340
+ })
341
+ })
235
342
  })
@@ -1,6 +1,6 @@
1
1
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
2
2
  import BigNumber from 'bignumber.js'
3
- import React, { useMemo } from 'react'
3
+ import React from 'react'
4
4
  import AppAnalytics from 'src/analytics/AppAnalytics'
5
5
  import { SendEvents } from 'src/analytics/Events'
6
6
  import { getLocalCurrencyCode, usdToLocalCurrencyRateSelector } from 'src/localCurrency/selectors'
@@ -11,6 +11,7 @@ import { useSelector } from 'src/redux/hooks'
11
11
  import EnterAmount, { ProceedArgs, SendProceed } from 'src/send/EnterAmount'
12
12
  import { lastUsedTokenIdSelector } from 'src/send/selectors'
13
13
  import { usePrepareSendTransactions } from 'src/send/usePrepareSendTransactions'
14
+ import useSendFilterChips from 'src/send/useSendFilterChips'
14
15
  import { sortedTokensWithBalanceOrShowZeroBalanceSelector } from 'src/tokens/selectors'
15
16
  import { TokenBalance } from 'src/tokens/slice'
16
17
  import Logger from 'src/utils/Logger'
@@ -22,18 +23,24 @@ type Props = NativeStackScreenProps<StackParamList, Screens.SendEnterAmount>
22
23
  const TAG = 'SendEnterAmount'
23
24
 
24
25
  function SendEnterAmount({ route }: Props) {
25
- const { defaultTokenIdOverride, origin, recipient, isFromScan, forceTokenId } = route.params
26
+ const {
27
+ defaultTokenIdOverride,
28
+ origin,
29
+ recipient,
30
+ isFromScan,
31
+ forceTokenId,
32
+ isMiniPayRecipient,
33
+ } = route.params
26
34
  // explicitly allow zero state tokens to be shown for exploration purposes for
27
35
  // new users with no balance
28
36
  const tokens = useSelector(sortedTokensWithBalanceOrShowZeroBalanceSelector)
29
37
  const lastUsedTokenId = useSelector(lastUsedTokenIdSelector)
30
-
31
- const defaultToken = useMemo(() => {
32
- const defaultToken = tokens.find((token) => token.tokenId === defaultTokenIdOverride)
33
- const lastUsedToken = tokens.find((token) => token.tokenId === lastUsedTokenId)
34
-
35
- return defaultToken ?? lastUsedToken ?? tokens[0]
36
- }, [tokens, defaultTokenIdOverride, lastUsedTokenId])
38
+ const { filterChips, defaultToken } = useSendFilterChips({
39
+ isMiniPayRecipient,
40
+ tokens,
41
+ defaultTokenIdOverride,
42
+ lastUsedTokenId,
43
+ })
37
44
 
38
45
  const localCurrencyCode = useSelector(getLocalCurrencyCode)
39
46
  const localCurrencyExchangeRate = useSelector(usdToLocalCurrencyRateSelector)
@@ -72,6 +79,7 @@ function SendEnterAmount({ route }: Props) {
72
79
  amountEnteredIn,
73
80
  tokenId: token.tokenId,
74
81
  networkId: token.networkId,
82
+ isMiniPayRecipient: isMiniPayRecipient ?? false,
75
83
  })
76
84
  }
77
85
 
@@ -116,6 +124,7 @@ function SendEnterAmount({ route }: Props) {
116
124
  tokenSelectionDisabled={!!forceTokenId}
117
125
  onPressProceed={handleReviewSend}
118
126
  ProceedComponent={SendProceed}
127
+ filterChips={filterChips}
119
128
  />
120
129
  )
121
130
  }
@@ -0,0 +1,46 @@
1
+ import { BooleanFilterChip } from 'src/components/FilterChipsCarousel'
2
+ import { getDynamicConfigParams } from 'src/statsig'
3
+ import { DynamicConfigs } from 'src/statsig/constants'
4
+ import { StatsigDynamicConfigs } from 'src/statsig/types'
5
+ import { TokenBalance } from 'src/tokens/slice'
6
+
7
+ export default function useSendFilterChips({
8
+ isMiniPayRecipient,
9
+ tokens,
10
+ defaultTokenIdOverride,
11
+ lastUsedTokenId,
12
+ }: {
13
+ isMiniPayRecipient?: boolean
14
+ tokens: TokenBalance[]
15
+ defaultTokenIdOverride?: string
16
+ lastUsedTokenId?: string | null
17
+ }): {
18
+ filterChips: BooleanFilterChip<TokenBalance>[]
19
+ defaultToken: TokenBalance | undefined
20
+ } {
21
+ const { miniPayTokenIds: configTokenIds } = getDynamicConfigParams(
22
+ DynamicConfigs[StatsigDynamicConfigs.SEND_CONFIG]
23
+ )
24
+ const miniPayTokenIds = isMiniPayRecipient && configTokenIds.length > 0 ? configTokenIds : null
25
+
26
+ const filterChips: BooleanFilterChip<TokenBalance>[] = miniPayTokenIds
27
+ ? [
28
+ {
29
+ id: 'minipay',
30
+ name: 'MiniPay',
31
+ filterFn: (token: TokenBalance) => miniPayTokenIds.includes(token.tokenId),
32
+ isSelected: true,
33
+ },
34
+ ]
35
+ : []
36
+
37
+ const eligibleTokens = miniPayTokenIds
38
+ ? tokens.filter((token) => miniPayTokenIds.includes(token.tokenId))
39
+ : tokens
40
+ const defaultToken =
41
+ eligibleTokens.find((token) => token.tokenId === defaultTokenIdOverride) ??
42
+ eligibleTokens.find((token) => token.tokenId === lastUsedTokenId) ??
43
+ eligibleTokens[0]
44
+
45
+ return { filterChips, defaultToken }
46
+ }
@@ -164,6 +164,12 @@ export const DynamicConfigs = {
164
164
  inviteRewardsVersion: 'none',
165
165
  },
166
166
  },
167
+ [StatsigDynamicConfigs.SEND_CONFIG]: {
168
+ configName: StatsigDynamicConfigs.SEND_CONFIG,
169
+ defaultValues: {
170
+ miniPayTokenIds: [] as string[],
171
+ },
172
+ },
167
173
  } satisfies {
168
174
  [key in StatsigDynamicConfigs]: {
169
175
  configName: key
@@ -11,6 +11,7 @@ export enum StatsigDynamicConfigs {
11
11
  DEMO_MODE_CONFIG = 'demo_mode_config',
12
12
  FIAT_CONNECT_CONFIG = 'fiat_connect_config',
13
13
  INVITE_REWARDS_CONFIG = 'invite_rewards_config',
14
+ SEND_CONFIG = 'send_config',
14
15
  }
15
16
 
16
17
  export enum StatsigFeatureGates {
@@ -1,4 +1,4 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
1
+ import * as Keychain from '@valora/react-native-keychain'
2
2
  import Logger from 'src/utils/Logger'
3
3
  import { ensureError } from 'src/utils/ensureError'
4
4
 
@@ -25,7 +25,7 @@ const mockData = [
25
25
  },
26
26
  ]
27
27
 
28
- jest.mock('@divvi/react-native-fs', () => {
28
+ jest.mock('@valora/react-native-fs', () => {
29
29
  return {
30
30
  exists: jest.fn(),
31
31
  mkdir: jest.fn(),
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable no-console */
2
- import * as RNFS from '@divvi/react-native-fs'
3
2
  import * as Sentry from '@sentry/react-native'
4
3
  import { SeverityLevel } from '@sentry/types'
4
+ import * as RNFS from '@valora/react-native-fs'
5
5
  import { format } from 'date-fns'
6
6
  import { Platform } from 'react-native'
7
7
  import Toast from 'react-native-simple-toast'
@@ -1,5 +1,5 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
2
1
  import * as Sentry from '@sentry/react-native'
2
+ import * as Keychain from '@valora/react-native-keychain'
3
3
  import AppAnalytics from 'src/analytics/AppAnalytics'
4
4
  import { resetStateOnInvalidStoredAccount } from 'src/utils/accountChecker'
5
5
  import { clearStoredAccounts } from 'src/web3/KeychainAccounts'
package/src/utils/aes.ts CHANGED
@@ -19,7 +19,7 @@ export function aesEncrypt(plainText: string, secret: string) {
19
19
  return Buffer.concat([
20
20
  Buffer.from('Salted__', 'utf8'),
21
21
  salt,
22
- cipher.update(plainText),
22
+ cipher.update(plainText, 'utf8'),
23
23
  cipher.final(),
24
24
  ]).toString('base64')
25
25
  }
@@ -26,7 +26,7 @@ export function shuffle(array: any[], seed: string) {
26
26
 
27
27
  export function calculateSha256Hash(input: string) {
28
28
  const hash = crypto.createHash('sha256')
29
- hash.update(input)
29
+ hash.update(input, 'utf8')
30
30
  return hash.digest('hex')
31
31
  }
32
32
 
@@ -1,5 +1,5 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
2
1
  import { act, fireEvent, render, waitFor, within } from '@testing-library/react-native'
2
+ import * as Keychain from '@valora/react-native-keychain'
3
3
  import { FetchMock } from 'jest-fetch-mock/types'
4
4
  import MockDate from 'mockdate'
5
5
  import React from 'react'
@@ -1,5 +1,5 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
2
1
  import { fireEvent, render, waitFor, within } from '@testing-library/react-native'
2
+ import * as Keychain from '@valora/react-native-keychain'
3
3
  import React from 'react'
4
4
  import { Provider } from 'react-redux'
5
5
  import { navigate } from 'src/navigator/NavigationService'
@@ -1,5 +1,5 @@
1
- import * as Keychain from '@divvi/react-native-keychain'
2
1
  import { act, fireEvent, render } from '@testing-library/react-native'
2
+ import * as Keychain from '@valora/react-native-keychain'
3
3
  import { FetchMock } from 'jest-fetch-mock/types'
4
4
  import React from 'react'
5
5
  import { Text } from 'react-native'
@@ -1,6 +1,6 @@
1
- import { ShouldStartLoadRequest } from '@interaxyz/react-native-webview/lib/WebViewTypes'
2
1
  import { useHeaderHeight } from '@react-navigation/elements'
3
2
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
3
+ import { ShouldStartLoadRequest } from '@valora/react-native-webview/lib/WebViewTypes'
4
4
  import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react'
5
5
  import { useTranslation } from 'react-i18next'
6
6
  import {