Skip to content

Commit

Permalink
Add BitcoinSendHandler and BitcoinPreSendHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
esen committed May 2, 2024
1 parent 0c78460 commit d2455b9
Show file tree
Hide file tree
Showing 13 changed files with 230 additions and 34 deletions.
14 changes: 13 additions & 1 deletion UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2936,6 +2936,10 @@
D0DEFF0B2BD1257F004C9DF0 /* BitcoinTransactionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DEFF082BD1257E004C9DF0 /* BitcoinTransactionService.swift */; };
D0DEFF0C2BD1257F004C9DF0 /* BitcoinFeeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DEFF092BD1257F004C9DF0 /* BitcoinFeeData.swift */; };
D0DEFF0D2BD1257F004C9DF0 /* BitcoinFeeData.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0DEFF092BD1257F004C9DF0 /* BitcoinFeeData.swift */; };
D0E5E84F2BE22172005080A4 /* BitcoinSendHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5E84E2BE22172005080A4 /* BitcoinSendHandler.swift */; };
D0E5E8502BE22172005080A4 /* BitcoinSendHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5E84E2BE22172005080A4 /* BitcoinSendHandler.swift */; };
D0E5E8522BE260C8005080A4 /* BitcoinPreSendHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5E8512BE260C8005080A4 /* BitcoinPreSendHandler.swift */; };
D0E5E8532BE260C8005080A4 /* BitcoinPreSendHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E5E8512BE260C8005080A4 /* BitcoinPreSendHandler.swift */; };
D0E659BB2B875003000D8981 /* ResendBitcoinViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E659BA2B875003000D8981 /* ResendBitcoinViewModel.swift */; };
D0E659BC2B875003000D8981 /* ResendBitcoinViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = D0E659BA2B875003000D8981 /* ResendBitcoinViewModel.swift */; };
D0EC34DB2A4450B100BB308B /* HCaptcha in Frameworks */ = {isa = PBXBuildFile; productRef = D0EC34DA2A4450B100BB308B /* HCaptcha */; };
Expand Down Expand Up @@ -4789,6 +4793,8 @@
D0DEFF032BD1253B004C9DF0 /* BitcoinFeeSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitcoinFeeSettingsView.swift; sourceTree = "<group>"; };
D0DEFF082BD1257E004C9DF0 /* BitcoinTransactionService.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitcoinTransactionService.swift; sourceTree = "<group>"; };
D0DEFF092BD1257F004C9DF0 /* BitcoinFeeData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BitcoinFeeData.swift; sourceTree = "<group>"; };
D0E5E84E2BE22172005080A4 /* BitcoinSendHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinSendHandler.swift; sourceTree = "<group>"; };
D0E5E8512BE260C8005080A4 /* BitcoinPreSendHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BitcoinPreSendHandler.swift; sourceTree = "<group>"; };
D0E659BA2B875003000D8981 /* ResendBitcoinViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResendBitcoinViewModel.swift; sourceTree = "<group>"; };
D0EE557D2934B0D60027AAD3 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
D0EE557E2934B0D60027AAD3 /* pt-BR */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "pt-BR"; path = "pt-BR.lproj/Localizable.strings"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -9105,6 +9111,8 @@
11B357FD9D760D9671A3DF24 /* PreSendViewModel.swift */,
D3B73E262BDBC6120067429D /* IPreSendHandler.swift */,
D3B73E292BDBC61D0067429D /* EvmPreSendHandler.swift */,
D0E5E84E2BE22172005080A4 /* BitcoinSendHandler.swift */,
D0E5E8512BE260C8005080A4 /* BitcoinPreSendHandler.swift */,
);
path = SendNew;
sourceTree = "<group>";
Expand Down Expand Up @@ -10541,6 +10549,7 @@
2FA5D50BFA714AAF76AC536B /* Eip1559GasPriceService.swift in Sources */,
11B35963D09A27ACE74B3A52 /* PublicKeysModule.swift in Sources */,
11B35B1F63DBDEEF57043C97 /* PublicKeysService.swift in Sources */,
D0E5E8532BE260C8005080A4 /* BitcoinPreSendHandler.swift in Sources */,
11B3536008462BFF8CA245BE /* PublicKeysViewModel.swift in Sources */,
11B3509603841DEF2FC02F24 /* PublicKeysViewController.swift in Sources */,
11B358AE1CCD292DF2D2AC42 /* PrivateKeysViewModel.swift in Sources */,
Expand Down Expand Up @@ -10892,6 +10901,7 @@
11B35EBBFE0CA22D35BA4A94 /* KeychainManager.swift in Sources */,
11B3552937152C5A4169C018 /* PasscodeLockManager.swift in Sources */,
11B3558A626C2E58C2656FF6 /* KeychainStorage.swift in Sources */,
D0E5E8502BE22172005080A4 /* BitcoinSendHandler.swift in Sources */,
11B354908D10B00AF7FF0AA3 /* UserDefaultsStorage.swift in Sources */,
11B35993ADB991F644E5EE98 /* PasscodeLockState.swift in Sources */,
11B35F906F9708CFC86E53FB /* NoPasscodeViewController.swift in Sources */,
Expand Down Expand Up @@ -12065,6 +12075,7 @@
2FA5DDC853CB680F672C1B50 /* LegacyEvmFeeViewModel.swift in Sources */,
11B3504619330D3DB0E0ECD1 /* PublicKeysModule.swift in Sources */,
11B35E70BF95197591A052EF /* PublicKeysService.swift in Sources */,
D0E5E8522BE260C8005080A4 /* BitcoinPreSendHandler.swift in Sources */,
11B35281808DE30B8D717B73 /* PublicKeysViewModel.swift in Sources */,
11B3550E1FD46B02F0154CBC /* PublicKeysViewController.swift in Sources */,
6BAAF3472B9B245C00EFE5B2 /* ShimmerEffect.swift in Sources */,
Expand Down Expand Up @@ -12416,6 +12427,7 @@
11B3520B4589E8C57FD4774F /* ScanQrBlurView.swift in Sources */,
11B3542290A3C8B680AA1943 /* ScanQrAlertView.swift in Sources */,
11B35082449BDCDAF7CECC3E /* KeychainManager.swift in Sources */,
D0E5E84F2BE22172005080A4 /* BitcoinSendHandler.swift in Sources */,
11B35CAD54059FA55DF81972 /* PasscodeLockManager.swift in Sources */,
11B35051B85C51018A1C7A3A /* KeychainStorage.swift in Sources */,
11B35A81895CBF7E86C0C437 /* UserDefaultsStorage.swift in Sources */,
Expand Down Expand Up @@ -13355,7 +13367,7 @@
repositoryURL = "https://github.com/horizontalsystems/BitcoinCore.Swift";
requirement = {
kind = exactVersion;
version = 3.0.0;
version = 3.0.1;
};
};
6B55461F2A6E73190054B524 /* XCRemoteSwiftPackageReference "UIExtensions" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -316,12 +316,16 @@ extension BitcoinBaseAdapter {
abstractKit.unspentOutputs(filters: filters)
}

func send(params: SendParameters) throws {
_ = try abstractKit.send(params: params)
}

func sendSingle(params: SendParameters, logger: Logger) -> Single<Void> {
Single.create { [weak self] observer in
do {
if let adapter = self {
logger.debug("Sending to \(String(reflecting: adapter.abstractKit))", save: true)
_ = try adapter.abstractKit.send(params: params)
try adapter.send(params: params)
}
observer(.success(()))
} catch {
Expand Down
4 changes: 4 additions & 0 deletions UnstoppableWallet/UnstoppableWallet/Extensions/Token.swift
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,10 @@ extension Token {
Decimal(bigUInt: value, decimals: decimals) ?? 0
}

func decimalValue(value: Int) -> Decimal {
Decimal(sign: value >= 0 ? .plus : .minus, exponent: -decimals, significand: Decimal(value))
}

func fractionalMonetaryValue(value: Decimal) -> BigUInt {
BigUInt(value.hs.roundedString(decimal: decimals)) ?? 0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ class ThorChainMultiSwapBtcConfirmationQuote: BaseSendBtcData, IMultiSwapConfirm
let slippage: Decimal
let transactionError: Error?

init(swapQuote: ThorChainMultiSwapProvider.SwapQuote, recipient: Address?, slippage: Decimal, satoshiPerByte: Int?, sendInfo: SendInfo?, transactionError: Error?) {
init(swapQuote: ThorChainMultiSwapProvider.SwapQuote, recipient: Address?, slippage: Decimal, satoshiPerByte: Int?, fee: Decimal?, transactionError: Error?) {
self.swapQuote = swapQuote
self.recipient = recipient
self.slippage = slippage
self.transactionError = transactionError

super.init(satoshiPerByte: satoshiPerByte, sendInfo: sendInfo)
super.init(satoshiPerByte: satoshiPerByte, fee: fee)
}

var amountOut: Decimal {
swapQuote.expectedAmountOut
}

var feeData: FeeData? {
sendInfo.map { .bitcoin(bitcoinFeeData: BitcoinFeeData(sendInfo: $0)) }
fee.map { .bitcoin(bitcoinFeeData: BitcoinFeeData(fee: $0)) }
}

var canSwap: Bool {
satoshiPerByte != nil && sendInfo != nil
satoshiPerByte != nil && fee != nil
}

func cautions(baseToken: MarketKit.Token) -> [CautionNew] {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ class ThorChainMultiSwapProvider: IMultiSwapProvider {
recipient: storage.recipient(blockchainType: tokenIn.blockchainType),
slippage: slippage,
satoshiPerByte: satoshiPerByte,
sendInfo: sendInfo,
fee: sendInfo?.fee,
transactionError: transactionError
)
default:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,21 @@ import MarketKit

class BaseSendBtcData {
let satoshiPerByte: Int?
let sendInfo: SendInfo?
let fee: Decimal?

init(satoshiPerByte: Int?, sendInfo: SendInfo?) {
init(satoshiPerByte: Int?, fee: Decimal?) {
self.satoshiPerByte = satoshiPerByte
self.sendInfo = sendInfo
self.fee = fee
}

func amountData(feeToken: Token, currency: Currency, feeTokenRate: Decimal?) -> AmountData? {
guard let sendInfo else {
guard let fee else {
return nil
}

let amount = sendInfo.fee

return AmountData(
coinValue: CoinValue(kind: .token(token: feeToken), value: amount),
currencyValue: feeTokenRate.map { CurrencyValue(currency: currency, value: amount * $0) }
coinValue: CoinValue(kind: .token(token: feeToken), value: fee),
currencyValue: feeTokenRate.map { CurrencyValue(currency: currency, value: fee * $0) }
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import Foundation
import MarketKit

struct BitcoinFeeData {
let sendInfo: SendInfo
let fee: Decimal

init(sendInfo: SendInfo) {
self.sendInfo = sendInfo
init(fee: Decimal) {
self.fee = fee
}

func amountData(feeToken: Token, currency: Currency, feeTokenRate: Decimal?) -> AmountData? {
let amount = sendInfo.fee

let coinValue = CoinValue(kind: .token(token: feeToken), value: amount)
let currencyValue = feeTokenRate.map { CurrencyValue(currency: currency, value: amount * $0) }
let coinValue = CoinValue(kind: .token(token: feeToken), value: fee)
let currencyValue = feeTokenRate.map { CurrencyValue(currency: currency, value: fee * $0) }

return AmountData(coinValue: coinValue, currencyValue: currencyValue)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import BigInt
import BitcoinCore
import Foundation
import MarketKit

class BitcoinPreSendHandler {
private let token: Token
private let adapter: BitcoinBaseAdapter

init(token: Token, adapter: BitcoinBaseAdapter) {
self.token = token
self.adapter = adapter
}
}

extension BitcoinPreSendHandler: IPreSendHandler {
func sendData(amount: Decimal, address: String, memo: String?) -> SendData? {
let params = SendParameters(
address: address,
value: adapter.convertToSatoshi(value: amount),
memo: memo
)

return .bitcoin(token: token, params: params)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
import BitcoinCore
import Foundation
import MarketKit

class BitcoinSendHandler {
private let token: Token
private var params: SendParameters
private var adapter: BitcoinBaseAdapter

init(token: Token, params: SendParameters, adapter: BitcoinBaseAdapter) {
self.token = token
self.params = params
self.adapter = adapter
}
}

extension BitcoinSendHandler: ISendHandler {
var baseToken: MarketKit.Token {
token
}

var syncingText: String? {
nil
}

var expirationDuration: Int {
10
}

func sendData(transactionSettings: TransactionSettings?) async throws -> ISendData {
let satoshiPerByte = transactionSettings?.satoshiPerByte
var feeData: BitcoinFeeData?
var transactionError: Error?

if let satoshiPerByte {
params.feeRate = satoshiPerByte
do {
let sendInfo = try adapter.sendInfo(params: params)
feeData = .init(fee: sendInfo.fee)
} catch {
transactionError = error
}
}

return SendData(
token: token,
params: params,
transactionError: transactionError,
satoshiPerByte: satoshiPerByte,
feeData: feeData
)
}

func send(data _: ISendData) async throws {
try adapter.send(params: params)
}
}

extension BitcoinSendHandler {
class SendData: BaseSendBtcData, ISendData {
private let token: Token
private let params: SendParameters
private let transactionError: Error?

init(token: Token, params: SendParameters, transactionError: Error?, satoshiPerByte: Int?, feeData: BitcoinFeeData?) {
self.token = token
self.params = params
self.transactionError = transactionError

super.init(satoshiPerByte: satoshiPerByte, fee: feeData?.fee)
}

var feeData: FeeData? {
fee.map { .bitcoin(bitcoinFeeData: BitcoinFeeData(fee: $0)) }
}

var canSend: Bool {
fee != nil
}

var sendButtonTitle: String {
"send.confirmation.slide_to_send".localized
}

var sendingButtonTitle: String {
"send.confirmation.sending".localized
}

var sentButtonTitle: String {
"send.confirmation.sent".localized
}

var rateCoins: [MarketKit.Coin] {
[token.coin]
}

func cautions(baseToken: Token) -> [CautionNew] {
var cautions = [CautionNew]()

if let transactionError {
cautions.append(caution(transactionError: transactionError, feeToken: baseToken))
}

return cautions
}

func sections(baseToken: Token, currency: Currency, rates: [String: Decimal]) -> [[SendField]] {
guard let toAddress = params.address, let value = params.value else {
return []
}

let decimalValue = baseToken.decimalValue(value: value)
let coinValue = CoinValue(kind: .token(token: baseToken), value: -decimalValue)
let rate = rates[baseToken.coin.uid]

return [
[
.amount(
title: "send.confirmation.you_send".localized,
token: baseToken,
coinValueType: .regular(coinValue: coinValue),
currencyValue: rate.map { CurrencyValue(currency: currency, value: $0 * decimalValue) },
type: .neutral
),
.address(
title: "send.confirmation.to".localized,
value: toAddress,
blockchainType: baseToken.blockchainType
),
],
feeFields(feeToken: baseToken, currency: currency, feeTokenRate: rate),
]
}
}
}

extension BitcoinSendHandler {
static func instance(token: Token, params: SendParameters) -> BitcoinSendHandler? {
guard let adapter = App.shared.adapterManager.adapter(for: token) as? BitcoinBaseAdapter else {
return nil
}

return BitcoinSendHandler(
token: token,
params: params,
adapter: adapter
)
}
}
Loading

0 comments on commit d2455b9

Please sign in to comment.