diff --git a/Gemfile b/Gemfile
index 2bb45933be..f1642ccab5 100644
--- a/Gemfile
+++ b/Gemfile
@@ -5,6 +5,6 @@ ruby "3.1.6"
gem 'rubyzip', '2.3.2'
gem 'cocoapods', '>= 1.13', '!= 1.15.0', '!= 1.15.1'
gem 'activesupport', '>= 6.1.7.5', '!= 7.1.0'
-gem "fastlane", ">= 2.224.0"
+gem "fastlane", ">= 2.225.0"
plugins_path = File.join(File.dirname(__FILE__), 'fastlane', 'Pluginfile')
eval_gemfile(plugins_path) if File.exist?(plugins_path)
diff --git a/Gemfile.lock b/Gemfile.lock
index d3df06f3df..78274a7d7e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -24,20 +24,20 @@ GEM
artifactory (3.0.17)
atomos (0.1.3)
aws-eventstream (1.3.0)
- aws-partitions (1.991.0)
- aws-sdk-core (3.209.1)
+ aws-partitions (1.992.0)
+ aws-sdk-core (3.211.0)
aws-eventstream (~> 1, >= 1.3.0)
- aws-partitions (~> 1, >= 1.651.0)
+ aws-partitions (~> 1, >= 1.992.0)
aws-sigv4 (~> 1.9)
jmespath (~> 1, >= 1.6.1)
- aws-sdk-kms (1.94.0)
- aws-sdk-core (~> 3, >= 3.207.0)
+ aws-sdk-kms (1.95.0)
+ aws-sdk-core (~> 3, >= 3.210.0)
aws-sigv4 (~> 1.5)
- aws-sdk-s3 (1.168.0)
- aws-sdk-core (~> 3, >= 3.207.0)
+ aws-sdk-s3 (1.169.0)
+ aws-sdk-core (~> 3, >= 3.210.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.5)
- aws-sigv4 (1.10.0)
+ aws-sigv4 (1.10.1)
aws-eventstream (~> 1, >= 1.0.2)
babosa (1.0.4)
base64 (0.2.0)
@@ -126,7 +126,7 @@ GEM
faraday_middleware (1.2.1)
faraday (~> 1.0)
fastimage (2.3.1)
- fastlane (2.224.0)
+ fastlane (2.225.0)
CFPropertyList (>= 2.3, < 4.0.0)
addressable (>= 2.8, < 3.0.0)
artifactory (~> 3.0)
@@ -142,6 +142,7 @@ GEM
faraday-cookie_jar (~> 0.0.6)
faraday_middleware (~> 1.0)
fastimage (>= 2.1.0, < 3.0.0)
+ fastlane-sirp (>= 1.0.0)
gh_inspector (>= 1.1.2, < 2.0.0)
google-apis-androidpublisher_v3 (~> 0.3)
google-apis-playcustomapp_v1 (~> 0.1)
@@ -169,6 +170,8 @@ GEM
xcpretty-travis-formatter (>= 0.0.3, < 2.0.0)
fastlane-plugin-browserstack (0.3.3)
rest-client (~> 2.0, >= 2.0.2)
+ fastlane-sirp (1.0.0)
+ sysrandom (~> 1.0)
ffi (1.17.0)
fourflusher (2.3.1)
fuzzy_match (2.0.4)
@@ -266,6 +269,7 @@ GEM
simctl (1.6.10)
CFPropertyList
naturally
+ sysrandom (1.0.5)
terminal-notifier (2.0.0)
terminal-table (3.0.2)
unicode-display_width (>= 1.1.1, < 3)
@@ -299,7 +303,7 @@ PLATFORMS
DEPENDENCIES
activesupport (>= 6.1.7.5, != 7.1.0)
cocoapods (>= 1.13, != 1.15.1, != 1.15.0)
- fastlane (>= 2.224.0)
+ fastlane (>= 2.225.0)
fastlane-plugin-browserstack
rubyzip (= 2.3.2)
diff --git a/components/AddressInput.tsx b/components/AddressInput.tsx
index e0487c1f28..4a98fb4c2b 100644
--- a/components/AddressInput.tsx
+++ b/components/AddressInput.tsx
@@ -3,9 +3,6 @@ import { Image, Keyboard, Platform, StyleSheet, Text, TextInput, View } from 're
import { scanQrHelper } from '../helpers/scan-qr';
import loc from '../loc';
-import { useTheme } from './themes';
-import { showFilePickerAndReadFile, showImagePickerAndReadImage } from '../blue_modules/fs';
-import Clipboard from '@react-native-clipboard/clipboard';
import presentAlert from './Alert';
import ToolTipMenu from './TooltipMenu';
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index ab9b6b6444..6dd041fc28 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -1321,7 +1321,7 @@ PODS:
- React
- react-native-randombytes (3.6.1):
- React-Core
- - react-native-safe-area-context (4.11.0):
+ - react-native-safe-area-context (4.11.1):
- React-Core
- react-native-screen-capture (0.2.3):
- React
@@ -1660,7 +1660,7 @@ PODS:
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- Yoga
- - RNReanimated (3.15.5):
+ - RNReanimated (3.16.0):
- DoubleConversion
- glog
- hermes-engine
@@ -1680,10 +1680,10 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
- - RNReanimated/reanimated (= 3.15.5)
- - RNReanimated/worklets (= 3.15.5)
+ - RNReanimated/reanimated (= 3.16.0)
+ - RNReanimated/worklets (= 3.16.0)
- Yoga
- - RNReanimated/reanimated (3.15.5):
+ - RNReanimated/reanimated (3.16.0):
- DoubleConversion
- glog
- hermes-engine
@@ -1703,8 +1703,30 @@ PODS:
- ReactCodegen
- ReactCommon/turbomodule/bridging
- ReactCommon/turbomodule/core
+ - RNReanimated/reanimated/apple (= 3.16.0)
- Yoga
- - RNReanimated/worklets (3.15.5):
+ - RNReanimated/reanimated/apple (3.16.0):
+ - DoubleConversion
+ - glog
+ - hermes-engine
+ - RCT-Folly (= 2024.01.01.00)
+ - RCTRequired
+ - RCTTypeSafety
+ - React-Core
+ - React-debug
+ - React-Fabric
+ - React-featureflags
+ - React-graphics
+ - React-ImageManager
+ - React-NativeModulesApple
+ - React-RCTFabric
+ - React-rendererdebug
+ - React-utils
+ - ReactCodegen
+ - ReactCommon/turbomodule/bridging
+ - ReactCommon/turbomodule/core
+ - Yoga
+ - RNReanimated/worklets (3.16.0):
- DoubleConversion
- glog
- hermes-engine
@@ -1749,7 +1771,7 @@ PODS:
- Yoga
- RNShare (10.2.1):
- React-Core
- - RNSVG (15.7.1):
+ - RNSVG (15.8.0):
- React-Core
- RNVectorIcons (10.2.0):
- DoubleConversion
@@ -2164,7 +2186,7 @@ SPEC CHECKSUMS:
react-native-menu: c30eb7a85d7b04d51945f61ea8a8986ed366ac5c
react-native-qrcode-local-image: 35ccb306e4265bc5545f813e54cc830b5d75bcfc
react-native-randombytes: 421f1c7d48c0af8dbcd471b0324393ebf8fe7846
- react-native-safe-area-context: 851c62c48dce80ccaa5637b6aa5991a1bc36eca9
+ react-native-safe-area-context: 5141f11858b033636f1788b14f32eaba92cee810
react-native-screen-capture: 75db9b051c41fea47fa68665506e9257d4b1dadc
react-native-secure-key-store: 910e6df6bc33cb790aba6ee24bc7818df1fe5898
react-native-tcp-socket: 8c3e8bef909ab06c557eeb95363fe029391ff09d
@@ -2210,10 +2232,10 @@ SPEC CHECKSUMS:
RNQuickAction: 6d404a869dc872cde841ad3147416a670d13fa93
RNRate: ef3bcff84f39bb1d1e41c5593d3eea4aab2bd73a
RNReactNativeHapticFeedback: 0d591ea1e150f36cb96d868d4e8d77272243d78a
- RNReanimated: 625f9e7f53cba61d7b3436e8e6e209d1dd4e6e9b
+ RNReanimated: f6a10979b3701f8029c71dbfe35d0ff4328dce4c
RNScreens: 19719a9c326e925498ac3b2d35c4e50fe87afc06
RNShare: 0fad69ae2d71de9d1f7b9a43acf876886a6cb99c
- RNSVG: 4590aa95758149fa27c5c83e54a6a466349a1688
+ RNSVG: 8b1a777d54096b8c2a0fd38fc9d5a454332bbb4d
RNVectorIcons: 6382277afab3c54658e9d555ee0faa7a37827136
RNWatch: fd30ca40a5b5ef58dcbc195638e68219bc455236
SocketRocket: abac6f5de4d4d62d24e11868d7a2f427e0ef940d
diff --git a/package.json b/package.json
index 9f535f8a54..9e670c5b2c 100644
--- a/package.json
+++ b/package.json
@@ -111,7 +111,7 @@
"coinselect": "3.1.13",
"crypto-js": "4.2.0",
"dayjs": "1.11.13",
- "detox": "20.27.4",
+ "detox": "20.27.5",
"ecpairgrs": "2.0.1",
"ecurve": "1.0.6",
"electrum-client": "github:BlueWallet/rn-electrum-client#1bfe3cc",
diff --git a/screen/send/Confirm.tsx b/screen/send/Confirm.tsx
index 78feab8ee8..ab2b2a20bb 100644
--- a/screen/send/Confirm.tsx
+++ b/screen/send/Confirm.tsx
@@ -170,15 +170,31 @@ const Confirm: React.FC = () => {
return bitcoin.address.toOutputScript(recipients[0].address, bitcoin.networks.bitcoin);
};
- const send = async () => {
+ const handleSendTransaction = async () => {
dispatch({ type: ActionType.SET_BUTTON_DISABLED, payload: true });
dispatch({ type: ActionType.SET_LOADING, payload: true });
try {
- const txids2watch = [];
+ // Perform biometric authentication first
+ if (await isBiometricUseCapableAndEnabled()) {
+ if (!(await unlockWithBiometrics())) {
+ // Stop execution if biometric unlock fails
+ dispatch({ type: ActionType.SET_LOADING, payload: false });
+ dispatch({ type: ActionType.SET_BUTTON_DISABLED, payload: false });
+ return;
+ }
+ }
+
+ const txidsToWatch = [];
if (!state.isPayjoinEnabled) {
- await broadcast(tx);
+ // Only broadcast the transaction after biometrics pass
+ const result = await broadcastTransaction(tx);
+ if (!result) {
+ dispatch({ type: ActionType.SET_LOADING, payload: false });
+ dispatch({ type: ActionType.SET_BUTTON_DISABLED, payload: false });
+ return;
+ }
} else {
- const payJoinWallet = new PayjoinTransaction(psbt, (txHex: string) => broadcast(txHex), wallet as HDSegwitBech32Wallet);
+ const payJoinWallet = new PayjoinTransaction(psbt, (txHex: string) => broadcastTransaction(txHex), wallet as HDSegwitBech32Wallet);
const paymentScript = getPaymentScript();
if (!paymentScript) {
throw new Error('Invalid payment script');
@@ -191,15 +207,15 @@ const Confirm: React.FC = () => {
await payjoinClient.run();
const payjoinPsbt = payJoinWallet.getPayjoinPsbt();
if (payjoinPsbt) {
- const tx2watch = payjoinPsbt.extractTransaction();
- txids2watch.push(tx2watch.getId());
+ const txToWatch = payjoinPsbt.extractTransaction();
+ txidsToWatch.push(txToWatch.getId());
}
}
const txid = bitcoin.Transaction.fromHex(tx).getId();
- txids2watch.push(txid);
+ txidsToWatch.push(txid);
// @ts-ignore: Notifications has to be TSed
- // Notifications.majorTomToGroundControl([], [], txids2watch);
+ // Notifications.majorTomToGroundControl([], [], txidsToWatch);
let amount = 0;
for (const recipient of recipients) {
if (recipient.value) {
@@ -227,16 +243,10 @@ const Confirm: React.FC = () => {
}
};
- const broadcast = async (transaction: string) => {
+ const broadcastTransaction = async (transaction: string) => {
await BlueElectrum.ping();
await BlueElectrum.waitTillConnected();
- if (await isBiometricUseCapableAndEnabled()) {
- if (!(await unlockWithBiometrics())) {
- return;
- }
- }
-
const result = await wallet.broadcastTx(transaction);
if (!result) {
throw new Error(loc.errors.broadcast);
@@ -330,7 +340,7 @@ const Confirm: React.FC = () => {
{state.isLoading ? (
) : (
-
+
)}