Skip to content
Draft
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,39 @@ public class ManagedAccount {
blockHashHex = nil
}

// Convert input details
var inputs: [TxIO] = []
if ffiTx.input_details_count > 0, let inputsPtr = ffiTx.input_details {
for j in 0..<ffiTx.input_details_count {
let detail = inputsPtr.advanced(by: j).pointee
let address = detail.address != nil ? String(cString: detail.address) : ""
inputs.append(TxIO(address: address, amount: detail.value, isMine: false))
}
Comment on lines +100 to +107
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Locate FFI type declarations surfaced in repo sources/interfaces.
fd -i 'DashSDKFFI|ffi' .

# 2) Inspect symbols related to input details and ownership flags.
rg -n -C3 --iglob '*.h' --iglob '*.swiftinterface' --iglob '*.swift' \
  'FFIInputDetail|input_details|is_mine|isMine|role|address|value'

Repository: dashpay/platform

Length of output: 50374


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Read the ManagedAccount.swift file to see the context around lines 100-107
echo "=== ManagedAccount.swift lines 95-115 ==="
sed -n '95,115p' packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift

echo -e "\n=== Searching for FFIInputDetail definition ==="
# Search in FFI-related files for FFIInputDetail struct/type definition
rg -n 'struct FFIInputDetail|class FFIInputDetail|FFIInputDetail.*{' packages/swift-sdk/ --type swift --type c --type h -A 10 | head -100

echo -e "\n=== Searching for input_details and is_mine patterns ==="
# Look for is_mine or similar ownership fields in FFI bindings
rg -n 'is_mine|isMine' packages/swift-sdk/Sources/SwiftDashSDK/FFI/ --type swift --type h -B 2 -A 2

echo -e "\n=== Checking FFI header files for input detail structure ==="
# Search in header files for input detail field definitions
fd -e h packages/swift-sdk/Sources/SwiftDashSDK/FFI/ -exec grep -l "input_details" {} \;

Repository: dashpay/platform

Length of output: 1327


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Simpler search for FFIInputDetail
echo "=== Searching for FFIInputDetail definition ==="
rg 'FFIInputDetail' packages/swift-sdk/Sources/SwiftDashSDK/FFI/ --type swift --type h -B 2 -A 5

echo -e "\n=== Looking for input detail structure in header files ==="
find packages/swift-sdk -name "*.h" -type f | xargs grep -l "input_detail" 2>/dev/null || echo "No .h files with input_detail found"

echo -e "\n=== Searching for is_mine field in codebase ==="
rg 'is_mine|isMine' packages/swift-sdk/Sources/SwiftDashSDK/ --type swift -B 1 -A 1

echo -e "\n=== Looking at the full TxIO struct definition ==="
rg 'struct TxIO' packages/swift-sdk/Sources/SwiftDashSDK/ --type swift -A 10

echo -e "\n=== Check imports and FFI files ==="
ls -la packages/swift-sdk/Sources/SwiftDashSDK/FFI/

Repository: dashpay/platform

Length of output: 107


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Searching entire repo for FFIInputDetail ==="
rg 'FFIInputDetail' . --type swift --type h --type rust 2>/dev/null | head -30

echo -e "\n=== Looking for input_detail in FFI packages ==="
rg 'input_detail' packages/rs-sdk-ffi/ packages/rs-platform-wallet-ffi/ --type rust --type h 2>/dev/null | head -30

echo -e "\n=== Check ManagedAccount.swift for TxIO and isMine usage ==="
rg 'struct TxIO|class TxIO|var isMine' packages/swift-sdk/Sources/SwiftDashSDK/ --type swift -A 5 -B 1

echo -e "\n=== Check if similar patterns with ownership exist elsewhere ==="
rg 'isMine.*false|isMine.*true' packages/swift-sdk/ --type swift

echo -e "\n=== Look for transaction parsing to understand data available ==="
grep -r 'input_details_count\|output_details_count' packages/swift-sdk/ --include="*.swift" 2>/dev/null | head -20

Repository: dashpay/platform

Length of output: 108


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Read ManagedAccount.swift imports and beginning ==="
head -50 packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift

echo -e "\n=== Check for bridging header ==="
find packages/swift-sdk -name "*Bridging*" -o -name "*bridging*" 2>/dev/null | xargs cat 2>/dev/null

echo -e "\n=== Look at FFI module structure ==="
ls -la packages/swift-sdk/Sources/SwiftDashSDK/FFI/ 2>/dev/null || echo "FFI dir listing failed"

echo -e "\n=== Check if ffiTx is defined in module imports ==="
rg 'import.*FFI|typealias.*ffiTx' packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ --type swift -B 2 -A 2

echo -e "\n=== Look for WalletTransaction struct to understand input handling ==="
rg 'struct WalletTransaction|func getTransactions' packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift -A 20 | head -60

Repository: dashpay/platform

Length of output: 11111


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check output detail handling for comparison ==="
sed -n '112,130p' packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift

echo -e "\n=== Search for output isMine patterns ==="
rg 'outputs.append.*isMine' packages/swift-sdk/Sources/SwiftDashSDK/ --type swift -B 2 -A 2

echo -e "\n=== Look for Package.swift or dependency declarations ==="
find packages/swift-sdk -name "Package.swift" -o -name "*.podspec" | xargs cat 2>/dev/null | grep -A 20 "DashSDKFFI\|dependency" | head -40

echo -e "\n=== Check git for when DashSDKFFI was introduced ==="
git log --oneline --all -S "DashSDKFFI" -- packages/swift-sdk 2>/dev/null | head -10

echo -e "\n=== Search for any is_mine field usage anywhere in codebase ==="
rg 'is_mine|\.isMine\s*=' packages/swift-sdk/Sources/SwiftDashSDK/ --type swift

Repository: dashpay/platform

Length of output: 3455


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Search for FFI_OUTPUT_ROLE and similar input role constants ==="
rg 'FFI_OUTPUT_ROLE|FFI_INPUT_ROLE' packages/swift-sdk/ --type swift --type h

echo -e "\n=== Search for role field usage with input details ==="
rg 'input.*role|detail\.role' packages/swift-sdk/Sources/SwiftDashSDK/ --type swift -B 3 -A 3

echo -e "\n=== Look at the full getTransactions method around input/output handling ==="
sed -n '57,145p' packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift

Repository: dashpay/platform

Length of output: 4849


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check recent commits about input detail mapping ==="
git log --all --oneline --grep="input" -- packages/swift-sdk | head -15

echo -e "\n=== Check for any documentation about FFIInputDetail fields ==="
rg 'FFIInputDetail|input_detail.*struct' packages/ --type rust --type h -A 15 | head -80

echo -e "\n=== Look for rs-dashcore-ffi or FFI binding definitions ==="
find packages -name "*.rs" -path "*/ffi/*" | xargs grep -l "input_detail\|InputDetail" 2>/dev/null | head -5

echo -e "\n=== Check the Rust FFI crate for input/output detail definitions ==="
rg 'pub struct.*InputDetail|pub struct.*OutputDetail' packages/rs-sdk-ffi packages/rs-platform-wallet-ffi --type rust -A 10

Repository: dashpay/platform

Length of output: 180


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== List FFI framework contents ==="
find packages/swift-sdk -name "DashSDKFFI.xcframework" -o -name "*.modulemap" -o -name "module.modulemap" 2>/dev/null | head -10

echo -e "\n=== Check for input detail in rust bindings broadly ==="
find packages -type f -name "*.rs" | xargs grep -l "input.*detail\|struct.*Detail" 2>/dev/null | grep -E "(ffi|binding)" | head -10

echo -e "\n=== Search in rs-sdk-ffi directory ==="
find packages/rs-sdk-ffi -type f -name "*.rs" | head -20

echo -e "\n=== Look for input_detail in any rust file ==="
rg 'input.*detail|InputDetail' packages/ --type rust --max-count 20

echo -e "\n=== Check if there's C header with FFIInputDetail ==="
find packages -name "*.h" | xargs grep -l "input_detail\|InputDetail" 2>/dev/null

Repository: dashpay/platform

Length of output: 152


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Check if FFIInputDetail is mentioned anywhere or similar patterns ==="
rg 'FFIInputDetail|FFIOutputDetail' packages/swift-sdk/ --type swift -B 2 -A 2

echo -e "\n=== Check commit that added output role mapping ==="
git log --all -p --grep="output.*role\|FFI_OUTPUT_ROLE" -- packages/swift-sdk | head -100

echo -e "\n=== Check latest commits to ManagedAccount.swift ==="
git log --oneline -- packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift | head -20

echo -e "\n=== Look at the specific commit that added input_details mapping ==="
git show 6015040bd:packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift 2>/dev/null | grep -A 20 "Convert input details" | head -25

Repository: dashpay/platform

Length of output: 133


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "=== Try to find what fields are accessed on input/output detail ==="
grep -n 'detail\.' packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift | grep -E "input|output" -A 5 -B 5

echo -e "\n=== Look at commit history for this file ==="
git log --oneline -20 packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift

echo -e "\n=== Check if there are any TODOs or FIXMEs related to isMine ==="
rg 'TODO|FIXME|XXX' packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift -B 2 -A 2

echo -e "\n=== Look at TxIO struct definition to understand the field ==="
rg 'struct TxIO|class TxIO' packages/swift-sdk/Sources/SwiftDashSDK/ -A 10 --type swift

Repository: dashpay/platform

Length of output: 832


Map input ownership from FFIInputDetail role field, matching output detail handling

Line 106 hardcodes isMine: false for all inputs, which will misclassify wallet-owned inputs in outgoing and internal transactions. This breaks sender/owner UI logic and contradicts the output detail handling at line 116, which correctly checks detail.role == FFI_OUTPUT_ROLE_RECEIVED || detail.role == FFI_OUTPUT_ROLE_CHANGE.

Update input mapping to check the role field if available on FFIInputDetail, or document why inputs lack ownership metadata if the field is not exposed:

Current code (lines 105-107)
for j in 0..<ffiTx.input_details_count {
    let detail = inputsPtr.advanced(by: j).pointee
    let address = detail.address != nil ? String(cString: detail.address) : ""
    inputs.append(TxIO(address: address, amount: detail.value, isMine: false))
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/swift-sdk/Sources/SwiftDashSDK/KeyWallet/ManagedAccount.swift`
around lines 100 - 107, The input mapping currently hardcodes isMine: false in
ManagedAccount.swift when building inputs, causing misclassification; update the
loop that builds inputs (the section that iterates ffiTx.input_details and
appends TxIO) to inspect the FFIInputDetail.role field (analogous to the output
handling that checks detail.role == FFI_OUTPUT_ROLE_RECEIVED || detail.role ==
FFI_OUTPUT_ROLE_CHANGE) and set isMine = true when the role indicates the input
is wallet-owned, otherwise false; if FFIInputDetail does not expose a role
field, document that fact in a comment and request exposing ownership metadata
from the FFI so inputs can be classified properly.

}

// Convert output details
var outputs: [TxIO] = []
if ffiTx.output_details_count > 0, let outputsPtr = ffiTx.output_details {
for j in 0..<ffiTx.output_details_count {
let detail = outputsPtr.advanced(by: j).pointee
let address = detail.address != nil ? String(cString: detail.address) : ""
let isMine = detail.role == FFI_OUTPUT_ROLE_RECEIVED || detail.role == FFI_OUTPUT_ROLE_CHANGE
outputs.append(TxIO(address: address, amount: detail.value, isMine: isMine))
}
}

let transaction = WalletTransaction(
txid: txidHex,
netAmount: ffiTx.net_amount,
height: ffiTx.context.block_info.height,
blockHash: blockHashHex,
timestamp: ffiTx.context.block_info.timestamp,
fee: ffiTx.fee > 0 ? ffiTx.fee : nil,
inputs: inputs,
outputs: outputs,
txType: ffiTx.transaction_type.rawValue,
direction: ffiTx.direction.rawValue,
instantSendLocked: ffiTx.context.islock_data != nil && ffiTx.context.islock_len > 0
)

transactions.append(transaction)
Expand Down Expand Up @@ -156,6 +182,24 @@ public class ManagedAccount {
}
}

// MARK: - Transaction Input/Output

/// Transaction input/output record with decoded address
public struct TxIO {
/// Address (empty string for non-standard scripts)
public let address: String
/// Amount in satoshis
public let amount: UInt64
/// Whether this address belongs to the wallet
public let isMine: Bool

public init(address: String, amount: UInt64, isMine: Bool) {
self.address = address
self.amount = amount
self.isMine = isMine
}
}

// MARK: - Wallet Transaction

/// Information about a transaction from a managed account
Expand All @@ -172,6 +216,16 @@ public struct WalletTransaction: Identifiable {
public let timestamp: UInt32
/// Fee if known
public let fee: UInt64?
/// Transaction inputs with decoded addresses
public let inputs: [TxIO]
/// Transaction outputs with roles
public let outputs: [TxIO]
/// Transaction type (FFITransactionType raw value: 0=standard, 1=coinJoin, 8=coinbase, etc.)
public let txType: UInt32
/// Transaction direction (FFITransactionDirection raw value: 0=incoming, 1=outgoing, 2=internal, 3=coinJoin)
public let direction: UInt32
/// Whether this transaction has an InstantSend lock
public let instantSendLocked: Bool

public init(
txid: String,
Expand All @@ -180,13 +234,23 @@ public struct WalletTransaction: Identifiable {
blockHash: String?,
timestamp: UInt32,
fee: UInt64?,
inputs: [TxIO] = [],
outputs: [TxIO] = [],
txType: UInt32 = 0,
direction: UInt32 = 0,
instantSendLocked: Bool = false
) {
self.txid = txid
self.netAmount = netAmount
self.height = height
self.blockHash = blockHash
self.timestamp = timestamp
self.fee = fee
self.inputs = inputs
self.outputs = outputs
self.txType = txType
self.direction = direction
self.instantSendLocked = instantSendLocked
}

/// Transaction date
Expand Down
Loading