Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
ead8a6a
feat: add usdt wallet and paykit payments
ben-kaufman Oct 7, 2026
18a5c4a
fix: preserve payment recovery and final authorization
ben-kaufman Oct 7, 2026
22f449c
chore: rename changelog fragment
ben-kaufman Oct 7, 2026
cc5edea
feat: add usdt bridge support actions
ben-kaufman Oct 7, 2026
32f5eb3
Make USDT sharing optional in companion authorization
ben-kaufman Oct 7, 2026
e70c293
feat: apply usdt wallet designs
ben-kaufman Oct 7, 2026
9492009
fix: align paykit pricing with selected payment methods
ben-kaufman Oct 7, 2026
6da0318
fix: align USDT and Paykit screens with design updates
ben-kaufman Oct 8, 2026
4c21984
chore: update bitkit core to 0.8.0-rc2
ben-kaufman Oct 8, 2026
8dd9198
docs: remove stale paykit version from usdt guide
ben-kaufman Oct 8, 2026
016b72e
fix: align usdt displays with design feedback
ben-kaufman Oct 9, 2026
377ee54
Preserve quoted payment terms and recovery across request flows
ben-kaufman Oct 12, 2026
82743ce
fix: preserve quote deadlines through payment retries
ben-kaufman Oct 12, 2026
a0cec0f
fix: use usdt colors for paid payment requests
ben-kaufman Oct 12, 2026
6d1dade
test: cover backing out of prepared usdt requests
ben-kaufman Oct 12, 2026
c9313f2
fix: route USDT payment links to send flow
ben-kaufman Oct 12, 2026
c01ea85
test: add Lightning fee review journey
ben-kaufman Oct 12, 2026
964b566
fix: distinguish usdt proofs and explain fee failures
ben-kaufman Oct 12, 2026
bc9700f
chore: use the public usdt gateway
ben-kaufman Oct 12, 2026
c18a8cc
fix: preserve reviewed payment terms
ben-kaufman Oct 12, 2026
b3c1f89
fix: reuse USDT core within the wallet session
ben-kaufman Oct 12, 2026
170bddb
fix: show completed deposit refunds accurately
ben-kaufman Oct 12, 2026
37af927
feat: align USDT send and activity designs
ben-kaufman Oct 12, 2026
5d6b5b7
docs: align USDT QA with current navigation
ben-kaufman Oct 12, 2026
270b254
fix: complete usdt design consistency
ben-kaufman Oct 12, 2026
f7e8240
fix(usdt): retain queued sends and clarify deposit history
ben-kaufman Oct 12, 2026
931f51a
docs(usdt): cover retained payments during backup outages
ben-kaufman Oct 12, 2026
b35dd08
fix: keep maximum usdt bridge debit visible
ben-kaufman Oct 12, 2026
2bad371
fix: preserve usdt payment links and clarify self sends
ben-kaufman Oct 12, 2026
4e2e5f9
fix: explain insufficient bridge amounts using Core rc5
ben-kaufman Oct 12, 2026
b216ae3
fix: show active incoming conversions as pending
ben-kaufman Oct 12, 2026
d5ac83f
fix: preserve grouped activity row identifiers
ben-kaufman Oct 13, 2026
90194c1
fix: reject usdt links during hardware manual entry
ben-kaufman Oct 13, 2026
d4f206c
fix: clarify subscription review and activity grouping
ben-kaufman Oct 13, 2026
4842452
fix: distinguish sent and received bridge amounts
ben-kaufman Oct 13, 2026
2a790d4
fix: preserve usdt flows after rebase
ben-kaufman Oct 13, 2026
a3c73e8
Implement USDT opt-in and updated Figma sheets
ben-kaufman Oct 13, 2026
ae666ab
fix: align USDT screens and payment recovery
ben-kaufman Oct 13, 2026
d7cc4a5
fix: respect usdt opt-in across payment flows
ben-kaufman Oct 13, 2026
9d8cb11
fix: align usdt review dividers
ben-kaufman Oct 13, 2026
2818c75
fix: align usdt receive keypad spacing
ben-kaufman Oct 13, 2026
6576f23
fix: preserve usdt subscription payment context
ben-kaufman Oct 13, 2026
e83bca0
feat: align payment requests and Earn with USDT designs
ben-kaufman Oct 13, 2026
323327b
Merge master security updates into USDT work
ben-kaufman Oct 13, 2026
cc4b354
fix: keep Earn disclosures visible on compact screens
ben-kaufman Oct 13, 2026
f53b894
fix: align subscription totals and use core rc6
ben-kaufman Oct 13, 2026
c5513d8
fix: preserve usdt consent and avoid redundant proof backups
ben-kaufman Oct 13, 2026
dfd9b56
fix: keep disabled usdt settings available while balance loads
ben-kaufman Oct 14, 2026
e325f9c
fix: preserve request amounts and suppress active send prompts
ben-kaufman Oct 14, 2026
a8aa07b
docs: clarify receive behavior after usdt restore
ben-kaufman Oct 14, 2026
05b5fa5
fix: preserve payment review and recovery across assets
ben-kaufman Oct 14, 2026
8622aee
Merge master into USDT work
ben-kaufman Oct 14, 2026
997852b
fix: format usd request amounts for the device locale
ben-kaufman Oct 14, 2026
a35f2a3
Match USDT bridge activity and refund designs
ben-kaufman Oct 14, 2026
810858a
Align USDT bridge typography and spacing with designs
ben-kaufman Oct 14, 2026
b522b8f
fix: handle usdt send errors and prompt dismissal
ben-kaufman Oct 14, 2026
f555905
fix: preserve usdt receipts and align deposit details
ben-kaufman Oct 14, 2026
397b355
merge master into usdt work
ben-kaufman Oct 14, 2026
c348e73
fix: localize usdt refund amounts
ben-kaufman Oct 14, 2026
99fbda1
test: verify localized usdt refund confirmation
ben-kaufman Oct 14, 2026
587c542
fix: split main navigation view for reliable compilation
ben-kaufman Oct 14, 2026
a33d891
fix: preserve receipt and refund presentation state
ben-kaufman Oct 14, 2026
4caa785
perf: reuse valid usdt payment quotes
ben-kaufman Oct 15, 2026
49033c7
fix: prefer funded usdt for quoted requests
ben-kaufman Oct 15, 2026
767789a
fix: preserve usdt recovery and payment details
ben-kaufman Oct 15, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion Bitkit.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,10 @@
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
USDT_BRIDGES_URL = "https://bitkit.to/v1/usdt/bridges";
USDT_BUNDLER_URL = "https://bitkit.to/v1/usdt/rpc";
USDT_DEPOSITS_URL = "https://bitkit.to/v1/usdt/deposits";
USDT_RPC_URL = "https://bitkit.to/v1/usdt/chain-rpc";
};
name = Debug;
};
Expand Down Expand Up @@ -1039,6 +1043,10 @@
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
USDT_BRIDGES_URL = "https://bitkit.to/v1/usdt/bridges";
USDT_BUNDLER_URL = "https://bitkit.to/v1/usdt/rpc";
USDT_DEPOSITS_URL = "https://bitkit.to/v1/usdt/deposits";
USDT_RPC_URL = "https://bitkit.to/v1/usdt/chain-rpc";
};
name = Release;
};
Expand Down Expand Up @@ -1226,7 +1234,7 @@
repositoryURL = "https://github.com/synonymdev/bitkit-core";
requirement = {
kind = exactVersion;
version = 0.5.18;
version = "0.8.0-rc6";
};
};
96E493A22C942FD1000E8BC2 /* XCRemoteSwiftPackageReference "swift-secp256k1" */ = {
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 21 additions & 6 deletions Bitkit/AppScene.swift
Original file line number Diff line number Diff line change
Expand Up @@ -402,10 +402,6 @@ struct AppScene: View {
PaykitFeatureFlags.enforceBuildAvailability()
ContactPaymentsService.enableAllPaymentOptions()

_app = StateObject(wrappedValue: AppViewModel(
sheetViewModel: sheetViewModel,
navigationViewModel: navigationViewModel
))
_sheets = StateObject(wrappedValue: sheetViewModel)
_navigation = StateObject(wrappedValue: navigationViewModel)
let feeEstimatesManager = FeeEstimatesManager()
Expand All @@ -414,6 +410,11 @@ struct AppScene: View {
sheetViewModel: sheetViewModel,
feeEstimatesManager: feeEstimatesManager
)
_app = StateObject(wrappedValue: AppViewModel(
sheetViewModel: sheetViewModel,
navigationViewModel: navigationViewModel,
usdtBalance: { walletVm.usdtWallet.balance }
))
_wallet = StateObject(wrappedValue: walletVm)
_currency = StateObject(wrappedValue: CurrencyViewModel())
_blocktank = StateObject(wrappedValue: BlocktankViewModel())
Expand Down Expand Up @@ -577,6 +578,7 @@ struct AppScene: View {
.environment(trezorViewModel)
.environment(jadeManager)
.environment(hwWalletManager)
.environment(wallet.usdtWallet)
.environment(calculatorInputManager)
.environment(paykitPaymentRequestManager)
}
Expand Down Expand Up @@ -658,6 +660,17 @@ struct AppScene: View {

private var appEventContent: some View {
paykitEventContent
.onChange(of: wallet.usdtWallet.hasFundsOrPendingTransfers, initial: true) { _, hasFundsOrPendingTransfers in
settings.protectUsdtFunds(hasFundsOrPendingTransfers)
}
.onChange(of: settings.usdtPreference) { _, _ in
PublicPaykitService.setCleanupPending(true)
Task {
await retryPendingPaykitEndpointRemoval()
let keys = contactsManager.contacts.map(\.publicKey)
await PrivatePaykitService.shared.refreshSavedContactEndpoints(for: keys, savedPublicKeys: keys, wallet: wallet)
}
}
.onChange(of: sheets.activeSheetConfiguration?.id) { _, activeSheetId in
guard activeSheetId == nil, !sheets.isReplacingSheet else { return }
scheduleNextIncomingPaykitItemPresentation()
Expand Down Expand Up @@ -1328,6 +1341,7 @@ struct AppScene: View {
paykitPaymentRequestManager.updateSavedPublicKeys(contactsManager.contacts.map(\.publicKey))
if mode == .full {
await PaykitPaymentProofService.shared.reconcile()
await PaykitUsdtPaymentService.shared.reconcile(wallet: wallet.usdtWallet)
}
guard !Task.isCancelled, !onlyWhileActive || isPaykitSceneActive else { return false }
guard let identity = pubkyProfile.publicKey else { return false }
Expand Down Expand Up @@ -1516,7 +1530,7 @@ struct AppScene: View {
}
return
}
guard case let .opened(paymentTarget, privatePaymentContext) = result else {
guard case let .opened(paymentTarget, privatePaymentContext, endpoints) = result else {
deferIncomingPaykitPaymentRequestPresentation(
request,
reason: result.incomingPaymentRequestFailureReason ?? .resolutionFailed
Expand All @@ -1527,7 +1541,8 @@ struct AppScene: View {
let contactPaymentContext = ContactPaymentContext(
publicKey: request.counterparty,
privatePaymentContext: privatePaymentContext,
incomingPaymentRequest: request
incomingPaymentRequest: request,
endpoints: endpoints
)
guard app.claimContactPaymentContext(contactPaymentContext) else { return }
preparation.paymentContext = contactPaymentContext
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"images": [
{
"idiom": "universal",
"scale": "1x"
},
{
"idiom": "universal",
"scale": "2x"
},
{
"idiom": "universal",
"scale": "3x",
"filename": "usdt-coins-received.png"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"images": [
{
"filename": "paper-plane-tilt.svg",
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
},
"properties": {
"preserves-vector-representation": true,
"template-rendering-intent": "template"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"images": [
{
"filename": "permission-folder.svg",
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
},
"properties": {
"preserves-vector-representation": true,
"template-rendering-intent": "template"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions Bitkit/Assets.xcassets/tether-circle.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images": [
{
"filename": "tether-circle.png",
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions Bitkit/Assets.xcassets/tether-coin.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images": [
{
"filename": "tether-coin.png",
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions Bitkit/Assets.xcassets/tether.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images": [
{
"filename": "tether.png",
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 12 additions & 0 deletions Bitkit/Assets.xcassets/usdt-network.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"images": [
{
"filename": "usdt-network.png",
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions Bitkit/Assets.xcassets/usdt-settings.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"images": [
{
"filename": "usdt-settings.svg",
"idiom": "universal"
}
],
"info": {
"author": "xcode",
"version": 1
},
"properties": {
"preserves-vector-representation": true,
"template-rendering-intent": "template"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
61 changes: 34 additions & 27 deletions Bitkit/Components/Activity/ActivityList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,11 @@ import BitkitCore
import SwiftUI

struct ActivityList: View {
@AppStorage(PaykitFeatureFlags.uiEnabledKey) private var isPaykitUIEnabled = PaykitFeatureFlags.uiEnabledByDefault

@Environment(UsdtWalletManager.self) private var usdt
@EnvironmentObject var activity: ActivityListViewModel
@EnvironmentObject var contactsManager: ContactsManager
@EnvironmentObject var feeEstimatesManager: FeeEstimatesManager

let viewType: ActivityViewType

private var isPaykitUIActive: Bool {
PaykitFeatureFlags.isUIAvailable && isPaykitUIEnabled
}

enum ActivityViewType {
case all
case lightning
Expand All @@ -22,31 +15,38 @@ struct ActivityList: View {

var body: some View {
let activities = getActivities()
let groupedItems = activity.groupActivities(activities)

if !groupedItems.isEmpty {
let rows = WalletActivity.grouped(WalletActivity.merged(activities, filteredUsdt))
Comment thread
ben-kaufman marked this conversation as resolved.
if !rows.isEmpty {
LazyVStack(alignment: .leading, spacing: 16) {
ForEach(Array(zip(groupedItems.indices, groupedItems)), id: \.1) { index, groupItem in
switch groupItem {
ForEach(Array(rows.enumerated()), id: \.element) { index, item in
switch item {
case let .header(title):
CaptionMText(title)
.frame(height: 34, alignment: .bottom)

case let .activity(item):
NavigationLink(value: Route.activityDetail(item)) {
ActivityRow(
item: item,
feeEstimates: feeEstimatesManager.estimates,
contact: isPaykitUIActive ? item.contact(in: contactsManager.contacts) : nil
)
}
.accessibilityIdentifier("Activity-\(index)")
CaptionMText(title).frame(height: 34, alignment: .bottom)
case let .activity(activity):
WalletActivityRow(item: activity).accessibilityIdentifier("Activity-\(index)")
}
}
}
} else {
BodyMText(t("wallet__activity_no"))
.padding()
BodyMText(t("wallet__activity_no")).padding()
}
}

private var filteredUsdt: [UsdtTransfer] {
guard viewType == .all, activity.selectedTags.isEmpty else { return [] }
return usdt.transfers.filter { transfer in
let matchesTab: Bool = switch activity.selectedTab {
case .all: true
case .sent: !transfer.isIncoming
case .received: transfer.isIncoming
case .other: false
}
let date = Date(timeIntervalSince1970: TimeInterval(transfer.timestamp))
let start = activity.startDate.map { Calendar.current.startOfDay(for: $0) }
let end = activity.endDate.flatMap { Calendar.current.date(byAdding: .day, value: 1, to: Calendar.current.startOfDay(for: $0)) }
let search = activity.searchText
return matchesTab && (start.map { date >= $0 } ?? true) && (end.map { date < $0 } ?? true) &&
transfer.matchesActivitySearch(search)
}
}

Expand All @@ -58,3 +58,10 @@ struct ActivityList: View {
}
}
}

extension UsdtTransfer {
func matchesActivitySearch(_ search: String) -> Bool {
search.isEmpty || search.caseInsensitiveCompare("USDT") == .orderedSame ||
recipient.localizedCaseInsensitiveContains(search) || txHash?.localizedCaseInsensitiveContains(search) == true
}
}
Loading
Loading