Skip to content

Commit 3b5c70a

Browse files
committed
Merge branch 'release-into-master-merged'
2 parents 6fadbf9 + a172fc7 commit 3b5c70a

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1054
-237
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
## Unreleased
44
- Reduced support for BitBox01
55
- Fix a bug that would prevent the app to perform firmware upgrade when offline.
6-
- Upgraded to Qt 6.8.2, dropping support for macOS 11 and Ubuntu 20.04.
76

87
# 4.47.0
98
- Bundle BitBox02 firmware version v9.22.0
@@ -24,6 +23,7 @@
2423
- Create desktop shortcut by default on Windows during installation
2524
- Migrate from deprecated Walletconnect web3wallet to Reown, add Sepolia, Arbitrum, Base, and Optimism to supported chains
2625
- Added BTC Direct buy option
26+
- Upgraded to Qt 6.8.2, dropping support for macOS 11 and Ubuntu 20.04.
2727

2828
# 4.46.3
2929
- Fix camera access on linux

backend/accounts/transaction_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,14 @@ func TestOrderedTransactionsDeductedAmount(t *testing.T) {
277277
Amount: amount,
278278
Fee: &fee,
279279
},
280+
{
281+
// timestamp is nil, deductedAmount is still set
282+
Timestamp: nil,
283+
Height: 15,
284+
Type: TxTypeSend,
285+
Amount: amount,
286+
Fee: &fee,
287+
},
280288
}
281289

282290
orderedTxs := NewOrderedTransactions(txs)
@@ -285,4 +293,5 @@ func TestOrderedTransactionsDeductedAmount(t *testing.T) {
285293
requireAmountIsEqualTo(t, orderedTxs[1].DeductedAmount, 10)
286294
requireAmountIsEqualTo(t, orderedTxs[2].DeductedAmount, 0)
287295
requireAmountIsEqualTo(t, orderedTxs[3].DeductedAmount, 100)
296+
requireAmountIsEqualTo(t, orderedTxs[4].DeductedAmount, 110)
288297
}

backend/backend.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -645,7 +645,7 @@ func (backend *Backend) Start() <-chan interface{} {
645645

646646
backend.environment.OnAuthSettingChanged(backend.config.AppConfig().Backend.Authentication)
647647

648-
if backend.DefaultAppConfig().Backend.StartInTestnet {
648+
if backend.config.AppConfig().Backend.StartInTestnet {
649649
if err := backend.config.ModifyAppConfig(func(c *config.AppConfig) error { c.Backend.StartInTestnet = false; return nil }); err != nil {
650650
backend.log.WithError(err).Error("Can't set StartInTestnet to false")
651651
}

backend/coins/btc/handlers/handlers.go

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -121,14 +121,18 @@ func (handlers *Handlers) formatAmountAsJSON(amount coin.Amount, isFee bool) For
121121

122122
func (handlers *Handlers) formatAmountAtTimeAsJSON(amount coin.Amount, timeStamp *time.Time) FormattedAmount {
123123
accountCoin := handlers.account.Coin()
124-
conversions, estimated := coin.ConversionsAtTime(
125-
amount,
126-
handlers.account.Coin(),
127-
false,
128-
handlers.account.Config().RateUpdater,
129-
util.FormatBtcAsSat(handlers.account.Config().BtcCurrencyUnit),
130-
timeStamp,
131-
)
124+
conversions := map[string]string{}
125+
estimated := false
126+
if timeStamp != nil {
127+
conversions, estimated = coin.ConversionsAtTime(
128+
amount,
129+
handlers.account.Coin(),
130+
false,
131+
handlers.account.Config().RateUpdater,
132+
util.FormatBtcAsSat(handlers.account.Config().BtcCurrencyUnit),
133+
timeStamp,
134+
)
135+
}
132136
return FormattedAmount{
133137
Amount: accountCoin.FormatAmount(amount, false),
134138
Unit: accountCoin.GetFormatUnit(false),
@@ -195,12 +199,12 @@ func (handlers *Handlers) getTxInfoJSON(txInfo *accounts.TransactionData, detail
195199
timestamp = txInfo.CreatedTimestamp
196200
}
197201

198-
var deductedAmountAtTime FormattedAmount
202+
deductedAmountAtTime := handlers.formatAmountAtTimeAsJSON(txInfo.DeductedAmount, timestamp)
203+
199204
if timestamp != nil {
200205
t := timestamp.Format(time.RFC3339)
201206
formattedTime = &t
202207
amountAtTime = handlers.formatAmountAtTimeAsJSON(txInfo.Amount, timestamp)
203-
deductedAmountAtTime = handlers.formatAmountAtTimeAsJSON(txInfo.DeductedAmount, timestamp)
204208
}
205209

206210
addresses := []string{}

backend/exchanges/btcdirect.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func isRegionSupportedBtcDirect(region string) bool {
6161
func IsBtcDirectSupported(coinCode coin.Code) bool {
6262
supportedCoins := []coin.Code{
6363
coin.CodeBTC, coin.CodeTBTC, coin.CodeLTC, coin.CodeTLTC, coin.CodeETH, coin.CodeSEPETH,
64-
"eth-erc20-usdt", "eth-erc20-usdc", "eth-erc20-link"}
64+
"eth-erc20-usdc", "eth-erc20-link"}
6565

6666
coinSupported := slices.Contains(supportedCoins, coinCode)
6767

@@ -80,7 +80,7 @@ func BtcDirectDeals() *ExchangeDealsList {
8080
ExchangeName: BTCDirectName,
8181
Deals: []*ExchangeDeal{
8282
{
83-
Fee: 3, // 3%
83+
Fee: 3.9, // 3.9%
8484
Payment: CardPayment,
8585
IsFast: true,
8686
},

cmd/servewallet/main.go

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -103,18 +103,6 @@ func (webdevEnvironment) NativeLocale() string {
103103
if v == "" {
104104
v = os.Getenv("LANG")
105105
}
106-
// try macOS specific AppleLocale
107-
if v == "" && runtime.GOOS == "darwin" {
108-
cmd := exec.Command("defaults", "read", "-g", "AppleLocale") // may return something like en_US@rg=chzzzz
109-
out, err := cmd.Output()
110-
if err == nil {
111-
v = strings.Split(string(out), "@")[0]
112-
}
113-
}
114-
// If still empty, provide a default
115-
if v == "" {
116-
v = "en_US" // Default to English (United States)
117-
}
118106
// Strip charset from the LANG. It is unsupported by JS Date formatting
119107
// used in the frontend and breaks UI in unexpected ways.
120108
// We are always UTF-8 anyway.

frontends/qt/BitBox.pro

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ unix:!macx {
5959

6060
SOURCES += \
6161
main.cpp \
62-
filedialog.cpp
62+
filedialog.cpp \
63+
urlhandler.cpp
6364

64-
HEADERS += libserver.h webclass.h filedialog.h
65+
HEADERS += libserver.h webclass.h filedialog.h urlhandler.h
6566

6667
unix:macx {
6768
CONFIG += sdk_no_version_check

frontends/qt/main.cpp

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
#include <QSystemTrayIcon>
3939
#include <QMessageBox>
4040
#include <QtGlobal>
41+
#include <QtSystemDetection>
4142
#if defined(_WIN32)
4243
#if QT_VERSION_MAJOR >= 6
4344
#include <private/qguiapplication_p.h>
@@ -55,6 +56,7 @@
5556
#include "filedialog.h"
5657
#include "libserver.h"
5758
#include "webclass.h"
59+
#include "urlhandler.h"
5860

5961
#define APPNAME "BitBoxApp"
6062

@@ -85,25 +87,6 @@ class BitBoxApp : public SingleApplication
8587
Mode::User | Mode::SecondaryNotification | Mode::ExcludeAppVersion | Mode::ExcludeAppPath)
8688
{
8789
}
88-
89-
#if defined(Q_OS_MACOS)
90-
bool event(QEvent *event) override
91-
{
92-
if (event->type() == QEvent::FileOpen) {
93-
QFileOpenEvent* openEvent = static_cast<QFileOpenEvent*>(event);
94-
if (!openEvent->url().isEmpty()) {
95-
// This is only supported on macOS and is used to handle URIs that are opened with
96-
// the BitBoxApp, such as "aopp:..." links. The event is received and handled both
97-
// if the BitBoxApp is launched and also when it is already running, in which case
98-
// it is brought to the foreground automatically.
99-
100-
handleURI(openEvent->url().toString().toUtf8().constData());
101-
}
102-
}
103-
104-
return QApplication::event(event);
105-
}
106-
#endif
10790
};
10891

10992
class WebEnginePage : public QWebEnginePage {
@@ -318,6 +301,12 @@ int main(int argc, char *argv[])
318301
}
319302

320303
BitBoxApp a(argc, argv);
304+
// The URI scheme handler for aopp is handled via OS events on macOS. The other platforms invoke
305+
// the process with the uri as a command line param.
306+
#if defined(Q_OS_MACOS)
307+
UrlHandler url_handler;
308+
url_handler.setup();
309+
#endif
321310
// These three are part of the SingleApplication instance ID - if changed, the user should close
322311
// th existing app before launching the new one.
323312
// See https://github.com/BitBoxSwiss/SingleApplication/blob/c557da5d0cb63b8002c1ba99ec18f257620009b1/singleapplication_p.cpp#L135-L137

frontends/qt/resources/MacOS/entitlements.plist

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@
33
<plist version="1.0">
44
<dict>
55
<!-- needed for QtWebEngine, otherwise there is a gray screen at launch -->
6+
<!-- all these are copied from QtWebEngineCore.framework/Helpers/QtWebEngineProcess.app/Contents/Resources/QtWebEngineProcess.entitlements. -->
7+
<!-- see https://doc.qt.io/qt-6/qtwebengine-deploying.html#macos-specific-deployment-steps -->
68
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
79
<true/>
10+
<key>com.apple.security.cs.disable-library-validation</key>
11+
<true/>
12+
<key>com.apple.security.cs.allow-jit</key>
13+
<true/>
14+
<key>com.apple.security.cs.disable-executable-page-protection</key>
15+
<true/>
16+
817
<!-- needed for USB HID access, who knows why -->
918
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
1019
<true/>

frontends/qt/urlhandler.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#include "urlhandler.h"
16+
#include "libserver.h"
17+
18+
#include <iostream>
19+
#include <QDesktopServices>
20+
21+
UrlHandler::UrlHandler(QObject *parent) : QObject(parent) {
22+
}
23+
24+
void UrlHandler::setup() {
25+
// This is only supported on macOS and is used to handle URIs that are opened with
26+
// the BitBoxApp using "aopp:..." links. The event is received and handled both
27+
// if the BitBoxApp is launched and also when it is already running, in which case
28+
// it is brought to the foreground automatically.
29+
QDesktopServices::setUrlHandler("aopp", this, "handleUrlSlot");
30+
}
31+
32+
void UrlHandler::handleUrlSlot(const QUrl &url) {
33+
handleURI(url.toString().toUtf8().constData());
34+
}

frontends/qt/urlhandler.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// Copyright 2025 Shift Crypto AG
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#ifndef URLHANDLER_H
16+
#define URLHANDLER_H
17+
18+
#include <QObject>
19+
#include <QUrl>
20+
21+
class UrlHandler : public QObject {
22+
Q_OBJECT
23+
24+
public:
25+
UrlHandler(QObject *parent = nullptr);
26+
27+
void setup();
28+
29+
public slots:
30+
void handleUrlSlot(const QUrl &url);
31+
};
32+
33+
#endif // URLHANDLER_H
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
.amount {
2+
flex-shrink: 1;
3+
overflow: hidden;
4+
text-overflow: ellipsis;
5+
}
6+
17
.space {
28
margin-left: 0.33ch;
39
}

frontends/web/src/components/amount/amount.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const Amount = ({
110110
};
111111

112112
return (
113-
<span onClick={handleClick}>
113+
<span className={style.amount} onClick={handleClick}>
114114
<FormattedAmount
115115
amount={amount}
116116
unit={unit}

frontends/web/src/components/amount/conversion-amount.module.css

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,21 @@
77
align-items: baseline;
88
display: flex;
99
font-variant: tabular-nums;
10-
justify-content: end;
11-
gap: var(--space-eight);
10+
margin-left: auto;
11+
gap: 3px;
1212
}
1313

1414
.txPrefix,
1515
.txUnit {
1616
color: var(--color-secondary);
1717
font-size: 14px;
1818
line-height: 1.285714;
19+
white-space: nowrap;
1920
}
2021

2122
.txConversionAmount .txUnit {
2223
font-size: 12px;
24+
flex-shrink: 0;
2325
}
2426

2527
.txSmallInlineIcon img {

frontends/web/src/components/amount/conversion-amount.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ export const ConversionAmount = ({
5454

5555
return (
5656
<span className={styles.txConversionAmount}>
57-
{conversion && amountToShow ? (
57+
{(conversion || sendToSelf) && amountToShow ? (
5858
<>
5959
{sendToSelf && (
6060
<span className={styles.txSmallInlineIcon}>

frontends/web/src/components/guide/entry.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export const Entry = (props: TProps) => {
5555
<div className={[style.entryContent, shown ? style.expanded : ''].join(' ')}>
5656
{shown ? (
5757
<div className="flex-1">
58-
{entry?.text?.trim().split('\n').map((p, idx) => <p key={idx}>{p}</p>)}
58+
{entry.text.trim().split('\n').map((p, idx) => <p key={idx}>{p}</p>)}
5959
{entry.link && (
6060
<p>
6161
<A
Lines changed: 4 additions & 0 deletions
Loading

frontends/web/src/components/icon/icon.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ import statusInfoSVG from './assets/icons/icon-info.svg';
9292
import statusSuccessSVG from './assets/icons/icon-success.svg';
9393
import statusWarningSVG from './assets/icons/icon-warning.svg';
9494
import statusErrorSVG from './assets/icons/icon-error.svg';
95+
import plusSVG from './assets/icons/plus.svg';
9596
import style from './icon.module.css';
9697

9798
export const ExpandOpen = () => (
@@ -236,6 +237,7 @@ export const StatusSuccess = (props: ImgProps) => (<img src={statusSuccessSVG} d
236237
export const StatusInfo = (props: ImgProps) => (<img src={statusInfoSVG} draggable={false} {...props} />);
237238
export const StatusWarning = (props: ImgProps) => (<img src={statusWarningSVG} draggable={false} {...props} />);
238239
export const StatusError = (props: ImgProps) => (<img src={statusErrorSVG} draggable={false} {...props} />);
240+
export const Plus = (props: ImgProps) => (<img src={plusSVG} draggable={false} {...props} />);
239241
/**
240242
* @deprecated Alert is only used for BitBox01 use `Warning` icon instead
241243
*/

frontends/web/src/components/language/language.test.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -22,14 +22,7 @@ import { useTranslation } from 'react-i18next';
2222
import { TLanguagesList } from './types';
2323

2424
vi.mock('react-i18next', () => ({
25-
useTranslation: vi.fn(),
26-
hasResourceBundle: vi.fn(),
27-
addResourceBundle: vi.fn(),
28-
changeLanguage: vi.fn()
29-
}));
30-
31-
vi.mock('@/i18n/i18n', () => ({
32-
changei18nLanguage: vi.fn()
25+
useTranslation: vi.fn()
3326
}));
3427

3528
describe('components/language/language', () => {

0 commit comments

Comments
 (0)