From a63a33ad8eb2d562edbe36f8d57dbeff9e3807f9 Mon Sep 17 00:00:00 2001 From: EA Date: Mon, 30 Sep 2024 16:11:04 +0600 Subject: [PATCH] Integrate new send workflow for Ton blockchain --- .../project.pbxproj | 2 +- .../Core/Adapters/JettonAdapter.swift | 19 +++------ .../Core/Adapters/TonAdapter.swift | 9 +--- .../Core/Managers/TonKitManager.swift | 41 +++++++++++-------- .../UnstoppableWallet/Core/Protocols.swift | 4 +- .../Modules/SendNew/TonSendHandler.swift | 35 ++++++++++------ 6 files changed, 57 insertions(+), 53 deletions(-) diff --git a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj index 6e97b1c24a..d4efe14e9f 100644 --- a/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj +++ b/UnstoppableWallet/UnstoppableWallet.xcodeproj/project.pbxproj @@ -12835,7 +12835,7 @@ repositoryURL = "https://github.com/horizontalsystems/TonKit.Swift"; requirement = { kind = exactVersion; - version = 1.0.9; + version = 1.0.10; }; }; 6BF66DD82BA1A73300963242 /* XCRemoteSwiftPackageReference "ObjectMapper" */ = { diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/JettonAdapter.swift b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/JettonAdapter.swift index 5bf50cf54d..fe62bdfb71 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/JettonAdapter.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/JettonAdapter.swift @@ -120,21 +120,12 @@ extension JettonAdapter: IDepositAdapter { } extension JettonAdapter: ISendTonAdapter { - func estimateFee(recipient: FriendlyAddress, amount: TonAdapter.SendAmount, comment: String?) async throws -> Decimal { + func transferData(recipient: FriendlyAddress, amount: TonAdapter.SendAmount, comment: String?) throws -> TransferData { guard let jettonBalance else { - throw EstimateError.noWalletAddress + throw SendError.noJettonBalance } - let kitFee = try await tonKit.estimateFee(jettonWallet: jettonBalance.walletAddress, recipient: recipient, amount: sendAmount(jettonBalance: jettonBalance, amount: amount), comment: comment) - return TonAdapter.amount(kitAmount: kitFee) - } - - func send(recipient: FriendlyAddress, amount: TonAdapter.SendAmount, comment: String?) async throws { - guard let jettonBalance else { - throw EstimateError.noWalletAddress - } - - try await tonKit.send(jettonWallet: jettonBalance.walletAddress, recipient: recipient, amount: sendAmount(jettonBalance: jettonBalance, amount: amount), comment: comment) + return try tonKit.transferData(jettonAddress: jettonBalance.jettonAddress, recipient: recipient, amount: sendAmount(jettonBalance: jettonBalance, amount: amount), comment: comment) } private func sendAmount(jettonBalance: JettonBalance, amount: TonAdapter.SendAmount) throws -> BigUInt { @@ -152,8 +143,8 @@ extension JettonAdapter: ISendTonAdapter { } extension JettonAdapter { - enum EstimateError: Error { - case noWalletAddress + enum SendError: Error { + case noJettonBalance } enum AmountError: Error { diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/TonAdapter.swift b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/TonAdapter.swift index 8914771d23..537d6a888a 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Adapters/TonAdapter.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Adapters/TonAdapter.swift @@ -88,13 +88,8 @@ extension TonAdapter: IDepositAdapter { } extension TonAdapter: ISendTonAdapter { - func estimateFee(recipient: FriendlyAddress, amount: SendAmount, comment: String?) async throws -> Decimal { - let kitFee = try await tonKit.estimateFee(recipient: recipient, amount: sendAmount(amount: amount), comment: comment) - return Self.amount(kitAmount: kitFee) - } - - func send(recipient: FriendlyAddress, amount: SendAmount, comment: String?) async throws { - try await tonKit.send(recipient: recipient, amount: sendAmount(amount: amount), comment: comment) + func transferData(recipient: FriendlyAddress, amount: SendAmount, comment: String?) throws -> TransferData { + try tonKit.transferData(recipient: recipient, amount: sendAmount(amount: amount), comment: comment) } private func sendAmount(amount: SendAmount) throws -> Kit.SendAmount { diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Managers/TonKitManager.swift b/UnstoppableWallet/UnstoppableWallet/Core/Managers/TonKitManager.swift index 5af38e6145..6445afd198 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Managers/TonKitManager.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Managers/TonKitManager.swift @@ -29,31 +29,21 @@ class TonKitManager { return _tonKit } - let type: TonKit.Kit.WalletType + let address: TonSwift.Address switch account.type { case .mnemonic: - guard let seed = account.type.mnemonicSeed else { - throw AdapterError.unsupportedAccount - } - - let hdWallet = HDWallet(seed: seed, coinType: 607, xPrivKey: 0, curve: .ed25519) - let privateKey = try hdWallet.privateKey(account: 0) - let privateRaw = Data(privateKey.raw.bytes) - let pair = try TweetNacl.NaclSign.KeyPair.keyPair(fromSeed: privateRaw) - let keyPair = KeyPair(publicKey: .init(data: pair.publicKey), privateKey: .init(data: pair.secretKey)) - - type = .full(keyPair) - case let .tonAddress(address): - let tonAddress = try TonSwift.Address.parse(address) - type = .watch(tonAddress) + let (publicKey, _) = try Self.keyPair(accountType: account.type) + let contract = Self.contract(publicKey: publicKey) + address = try contract.address() + case let .tonAddress(_address): + address = try TonSwift.Address.parse(_address) default: throw AdapterError.unsupportedAccount } let tonKit = try TonKit.Kit.instance( - type: type, - walletVersion: .v4, + address: address, network: .mainNet, walletId: account.id, minLogLevel: .error @@ -168,6 +158,23 @@ extension TonKitManager { } } +extension TonKitManager { + static func contract(publicKey: Data) -> WalletContract { + WalletV4R2(publicKey: publicKey) + } + + static func keyPair(accountType: AccountType) throws -> (publicKey: Data, secretKey: Data) { + guard let seed = accountType.mnemonicSeed else { + throw AdapterError.unsupportedAccount + } + + let hdWallet = HDWallet(seed: seed, coinType: 607, xPrivKey: 0, curve: .ed25519) + let privateKey = try hdWallet.privateKey(account: 0) + let privateRaw = Data(privateKey.raw.bytes) + return try TweetNacl.NaclSign.KeyPair.keyPair(fromSeed: privateRaw) + } +} + extension Jetton { var tokenType: TokenType { .jetton(address: address.toString(bounceable: true)) diff --git a/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift b/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift index b9024e3a00..0ac62b4b4f 100644 --- a/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift +++ b/UnstoppableWallet/UnstoppableWallet/Core/Protocols.swift @@ -8,6 +8,7 @@ import HsToolKit import MarketKit import RxSwift import ThemeKit +import TonKit import TonSwift import TronKit import UIKit @@ -102,8 +103,7 @@ protocol ISendTronAdapter { } protocol ISendTonAdapter { - func estimateFee(recipient: FriendlyAddress, amount: TonAdapter.SendAmount, comment: String?) async throws -> Decimal - func send(recipient: FriendlyAddress, amount: TonAdapter.SendAmount, comment: String?) async throws + func transferData(recipient: FriendlyAddress, amount: TonAdapter.SendAmount, comment: String?) throws -> TransferData } protocol IErc20Adapter { diff --git a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/TonSendHandler.swift b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/TonSendHandler.swift index d80c12a0b4..cbac90b501 100644 --- a/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/TonSendHandler.swift +++ b/UnstoppableWallet/UnstoppableWallet/Modules/SendNew/TonSendHandler.swift @@ -6,6 +6,8 @@ import TonSwift class TonSendHandler { private let tonKit: TonKit.Kit + private let contract: WalletContract + private let secretKey: Data private let token: Token let baseToken: Token private let adapter: ISendTonAdapter & IBalanceAdapter @@ -13,8 +15,10 @@ class TonSendHandler { private let address: FriendlyAddress private let memo: String? - init(tonKit: TonKit.Kit, token: Token, baseToken: Token, adapter: ISendTonAdapter & IBalanceAdapter, amount: Decimal, address: FriendlyAddress, memo: String?) { + init(tonKit: TonKit.Kit, contract: WalletContract, secretKey: Data, token: Token, baseToken: Token, adapter: ISendTonAdapter & IBalanceAdapter, amount: Decimal, address: FriendlyAddress, memo: String?) { self.tonKit = tonKit + self.contract = contract + self.secretKey = secretKey self.token = token self.baseToken = baseToken self.adapter = adapter @@ -32,6 +36,7 @@ extension TonSendHandler: ISendHandler { func sendData(transactionSettings _: TransactionSettings?) async throws -> ISendData { var fee: Decimal? var transactionError: Error? + var transferData: TransferData? let tonBalance = TonAdapter.amount(kitAmount: tonKit.account?.balance) var sendAmount: TonAdapter.SendAmount = .amount(value: amount) @@ -43,8 +48,12 @@ extension TonSendHandler: ISendHandler { var finalAmount = amount do { - let estimatedFee = try await adapter.estimateFee(recipient: address, amount: sendAmount, comment: memo) + let _transferData = try adapter.transferData(recipient: address, amount: sendAmount, comment: memo) + let result = try await TonKit.Kit.emulate(transferData: _transferData, contract: contract, network: .mainNet) + let estimatedFee = TonAdapter.amount(kitAmount: result.totalFee) + fee = estimatedFee + transferData = _transferData if token.type.isNative { switch sendAmount { @@ -75,20 +84,16 @@ extension TonSendHandler: ISendHandler { memo: memo, fee: fee, transactionError: transactionError, - sendAmount: sendAmount + transferData: transferData ) } func send(data: ISendData) async throws { - guard let data = data as? SendData else { + guard let data = data as? SendData, let transferData = data.transferData else { throw SendError.invalidData } - _ = try await adapter.send( - recipient: data.address, - amount: data.sendAmount, - comment: data.memo - ) + try await TonKit.Kit.send(transferData: transferData, contract: contract, secretKey: secretKey, network: .mainNet) } } @@ -100,16 +105,16 @@ extension TonSendHandler { let memo: String? private let fee: Decimal? private let transactionError: Error? - let sendAmount: TonAdapter.SendAmount + let transferData: TransferData? - init(token: Token, amount: Decimal, address: FriendlyAddress, memo: String?, fee: Decimal?, transactionError: Error?, sendAmount: TonAdapter.SendAmount) { + init(token: Token, amount: Decimal, address: FriendlyAddress, memo: String?, fee: Decimal?, transactionError: Error?, transferData: TransferData?) { self.token = token self.amount = amount self.address = address self.memo = memo self.fee = fee self.transactionError = transactionError - self.sendAmount = sendAmount + self.transferData = transferData } var feeData: FeeData? { @@ -233,8 +238,14 @@ extension TonSendHandler { return nil } + guard let account = App.shared.accountManager.activeAccount, let (publicKey, secretKey) = try? TonKitManager.keyPair(accountType: account.type) else { + return nil + } + return TonSendHandler( tonKit: tonKit, + contract: TonKitManager.contract(publicKey: publicKey), + secretKey: secretKey, token: token, baseToken: baseToken, adapter: adapter,