Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improvements to Ton blockchain integration #5418

Merged
merged 1 commit into from
Nov 27, 2023
Merged
Show file tree
Hide file tree
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

Large diffs are not rendered by default.

Binary file not shown.

Large diffs are not rendered by default.

Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "[email protected]",
"idiom" : "universal",
"scale" : "3x"
}
],
"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.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
77 changes: 63 additions & 14 deletions UnstoppableWallet/UnstoppableWallet/Core/Adapters/TonAdapter.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
import Combine
import Foundation
import HdWalletKit
import HsToolKit
import MarketKit
import RxSwift
import TonKitKmm

class TonAdapter {
static let coinRate: Decimal = 1_000_000_000
private static let coinRate: Decimal = 1_000_000_000

private let tonKit: TonKit
private let ownAddress: String
private let transactionSource: TransactionSource
private let baseToken: Token
private let reachabilityManager = App.shared.reachabilityManager
private var cancellables = Set<AnyCancellable>()

private var adapterStarted = false
private var kitStarted = false

private let balanceStateSubject = PublishSubject<AdapterState>()
private(set) var balanceState: AdapterState {
didSet {
Expand Down Expand Up @@ -41,18 +46,26 @@ class TonAdapter {
transactionSource = wallet.transactionSource
self.baseToken = baseToken

guard let seed = wallet.account.type.mnemonicSeed else {
switch wallet.account.type {
case .mnemonic:
guard let seed = wallet.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)

tonKit = TonKitFactory(driverFactory: DriverFactory(), connectionManager: ConnectionManager()).create(seed: privateKey.raw.toKotlinByteArray(), walletId: wallet.account.id)
case let .tonAddress(address):
tonKit = TonKitFactory(driverFactory: DriverFactory(), connectionManager: ConnectionManager()).createWatch(address: address, walletId: wallet.account.id)
default:
throw AdapterError.unsupportedAccount
}

let hdWallet = HDWallet(seed: seed, coinType: 607, xPrivKey: 0, curve: .ed25519)
let privateKey = try hdWallet.privateKey(account: 0)

tonKit = TonKitFactory(driverFactory: DriverFactory()).create(seed: privateKey.raw.toKotlinByteArray(), walletId: wallet.account.id)
ownAddress = tonKit.receiveAddress

balanceState = Self.adapterState(kitSyncState: tonKit.balanceSyncState)
balanceData = Self.balanceData(kitBalance: tonKit.balance)
balanceData = BalanceData(available: Self.amount(kitAmount: tonKit.balance))
transactionsState = Self.adapterState(kitSyncState: tonKit.transactionsSyncState)

collect(tonKit.balanceSyncStatePublisher)
Expand All @@ -65,7 +78,7 @@ class TonAdapter {
collect(tonKit.balancePublisher)
.completeOnFailure()
.sink { [weak self] balance in
self?.balanceData = Self.balanceData(kitBalance: balance)
self?.balanceData = BalanceData(available: Self.amount(kitAmount: balance))
}
.store(in: &cancellables)

Expand All @@ -82,6 +95,24 @@ class TonAdapter {
self?.handle(tonTransactions: tonTransactions)
}
.store(in: &cancellables)

reachabilityManager.$isReachable
.sink { [weak self] isReachable in
self?.handle(isReachable: isReachable)
}
.store(in: &cancellables)
}

private func handle(isReachable: Bool) {
guard adapterStarted else {
return
}

if isReachable, !kitStarted {
startKit()
} else if !isReachable, kitStarted {
stopKit()
}
}

private func handle(tonTransactions: [TonTransaction]) {
Expand All @@ -98,12 +129,12 @@ class TonAdapter {
}
}

private static func balanceData(kitBalance: String?) -> BalanceData {
guard let kitBalance, let decimal = Decimal(string: kitBalance) else {
return BalanceData(available: 0)
static func amount(kitAmount: String) -> Decimal {
guard let decimal = Decimal(string: kitAmount) else {
return 0
}

return BalanceData(available: decimal / coinRate)
return decimal / coinRate
}

private func transactionRecord(tonTransaction tx: TonTransaction) -> TonTransactionRecord {
Expand Down Expand Up @@ -152,6 +183,16 @@ class TonAdapter {
tonTransactions.compactMap { self?.transactionRecord(tonTransaction: $0) }
}
}

private func startKit() {
tonKit.start()
kitStarted = true
}

private func stopKit() {
tonKit.stop()
kitStarted = false
}
}

extension TonAdapter: IBaseAdapter {
Expand All @@ -162,11 +203,19 @@ extension TonAdapter: IBaseAdapter {

extension TonAdapter: IAdapter {
func start() {
tonKit.start()
adapterStarted = true

if reachabilityManager.isReachable {
startKit()
}
}

func stop() {
tonKit.stop()
adapterStarted = false

if kitStarted {
stopKit()
}
}

func refresh() {}
Expand Down
2 changes: 1 addition & 1 deletion UnstoppableWallet/UnstoppableWallet/Core/App.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class App {
let systemInfoManager: SystemInfoManager

let pasteboardManager: PasteboardManager
let reachabilityManager: IReachabilityManager
let reachabilityManager: ReachabilityManager
let networkManager: NetworkManager

let accountManager: AccountManager
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ class AccountStorage {
}

type = .tronAddress(address: try! TronKit.Address(raw: data))
case .tonAddress:
guard let address = record.dataKey else {
return nil
}

type = .tonAddress(address: address)
case .hdExtendedKey:
guard let data = recoverData(id: id, typeName: typeName, keyName: .data) else {
return nil
Expand Down Expand Up @@ -115,6 +121,9 @@ class AccountStorage {
case .tronAddress(let address):
typeName = .tronAddress
dataKey = try store(data: address.raw, id: id, typeName: typeName, keyName: .data)
case .tonAddress(let address):
typeName = .tonAddress
dataKey = address
case .hdExtendedKey(let key):
typeName = .hdExtendedKey
dataKey = try store(data: key.serialized, id: id, typeName: typeName, keyName: .data)
Expand Down Expand Up @@ -157,6 +166,8 @@ class AccountStorage {
try keychainStorage.removeValue(for: secureKey(id: id, typeName: .hdExtendedKey, keyName: .data))
case .cex:
try keychainStorage.removeValue(for: secureKey(id: id, typeName: .cex, keyName: .data))
default:
()
}
}

Expand Down Expand Up @@ -241,6 +252,7 @@ extension AccountStorage {
case evmPrivateKey
case evmAddress = "address"
case tronAddress
case tonAddress
case hdExtendedKey
case cex
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ extension BlockchainType {
}
case .tronAddress:
return self == .tron
case .tonAddress:
return self == .ton
default:
return false
}
Expand Down
4 changes: 2 additions & 2 deletions UnstoppableWallet/UnstoppableWallet/Models/Account.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class Account: Identifiable {

var watchAccount: Bool {
switch type {
case .evmAddress, .tronAddress:
case .evmAddress, .tronAddress, .tonAddress:
return true
case let .hdExtendedKey(key):
switch key {
Expand Down Expand Up @@ -59,7 +59,7 @@ class Account: Identifiable {
var canBeBackedUp: Bool {
switch type {
case .mnemonic: return true
case .hdExtendedKey, .evmAddress, .tronAddress, .evmPrivateKey, .cex: return false
case .hdExtendedKey, .evmAddress, .tronAddress, .tonAddress, .evmPrivateKey, .cex: return false
}
}
}
Expand Down
23 changes: 22 additions & 1 deletion UnstoppableWallet/UnstoppableWallet/Models/AccountType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum AccountType {
case evmPrivateKey(data: Data)
case evmAddress(address: EvmKit.Address)
case tronAddress(address: TronKit.Address)
case tonAddress(address: String)
case hdExtendedKey(key: HDExtendedKey)
case cex(cexAccount: CexAccount)

Expand Down Expand Up @@ -44,6 +45,8 @@ enum AccountType {
privateData = address.hex.hs.data
case let .tronAddress(address):
privateData = address.hex.hs.data
case let .tonAddress(address):
privateData = address.hs.data
case let .hdExtendedKey(key):
privateData = key.serialized
case let .cex(cexAccount):
Expand Down Expand Up @@ -114,6 +117,11 @@ enum AccountType {
case (.tron, .native), (.tron, .eip20): return true
default: return false
}
case .tonAddress:
switch (token.blockchainType, token.type) {
case (.ton, .native): return true
default: return false
}
default:
return false
}
Expand Down Expand Up @@ -158,6 +166,8 @@ enum AccountType {
return "EVM Address"
case .tronAddress:
return "TRON Address"
case .tonAddress:
return "TON Address"
case let .hdExtendedKey(key):
switch key {
case .private:
Expand All @@ -183,6 +193,8 @@ enum AccountType {
return address.eip55.shortened
case let .tronAddress(address):
return address.base58.shortened
case let .tonAddress(address):
return address.shortened
default: return description
}
}
Expand Down Expand Up @@ -256,6 +268,8 @@ extension AccountType {
return (try? EvmKit.Address(hex: string)).map { AccountType.evmAddress(address: $0) }
case .tronAddress:
return (try? TronKit.Address(address: string)).map { AccountType.tronAddress(address: $0) }
case .tonAddress:
return AccountType.tonAddress(address: string)
case .cex:
guard let cexAccount = CexAccount.decode(uniqueId: string) else {
return nil
Expand All @@ -270,6 +284,7 @@ extension AccountType {
case evmPrivateKey = "private_key"
case evmAddress = "evm_address"
case tronAddress = "tron_address"
case tonAddress = "ton_address"
case hdExtendedKey = "hd_extended_key"
case cex

Expand All @@ -279,14 +294,15 @@ extension AccountType {
case .evmPrivateKey: self = .evmPrivateKey
case .evmAddress: self = .evmAddress
case .tronAddress: self = .tronAddress
case .tonAddress: self = .tonAddress
case .hdExtendedKey: self = .hdExtendedKey
case .cex: self = .cex
}
}

var isWatch: Bool {
switch self {
case .evmAddress, .tronAddress: return true
case .evmAddress, .tronAddress, .tonAddress: return true
default: return false
}
}
Expand All @@ -304,6 +320,8 @@ extension AccountType: Hashable {
return lhsAddress == rhsAddress
case let (.tronAddress(lhsAddress), .tronAddress(rhsAddress)):
return lhsAddress == rhsAddress
case let (.tonAddress(lhsAddress), .tonAddress(rhsAddress)):
return lhsAddress == rhsAddress
case let (.hdExtendedKey(lhsKey), .hdExtendedKey(rhsKey)):
return lhsKey == rhsKey
case let (.cex(lhsCexAccount), .cex(rhsCexAccount)):
Expand All @@ -328,6 +346,9 @@ extension AccountType: Hashable {
case let .tronAddress(address):
hasher.combine("tronAddress")
hasher.combine(address.raw)
case let .tonAddress(address):
hasher.combine("tonAddress")
hasher.combine(address)
case let .hdExtendedKey(key):
hasher.combine("hdExtendedKey")
hasher.combine(key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ class TonIncomingTransactionRecord: TonTransactionRecord {
let from: String

init(source: TransactionSource, transaction: TonTransaction, feeToken: Token, token: Token) {
let rawTonValue: Decimal = transaction.value_.flatMap { Decimal(string: $0) } ?? 0
let tonValue = rawTonValue / TonAdapter.coinRate
let tonValue: Decimal = transaction.value_.map { TonAdapter.amount(kitAmount: $0) } ?? 0
value = .coinValue(token: token, value: tonValue)
from = transaction.src ?? ""

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ class TonOutgoingTransactionRecord: TonTransactionRecord {
let sentToSelf: Bool

init(source: TransactionSource, transaction: TonTransaction, feeToken: Token, token: Token, sentToSelf: Bool) {
let rawTonValue: Decimal = transaction.value_.flatMap { Decimal(string: $0) } ?? 0
let tonValue = rawTonValue / TonAdapter.coinRate
let tonValue: Decimal = transaction.value_.map { TonAdapter.amount(kitAmount: $0) } ?? 0
value = .coinValue(token: token, value: Decimal(sign: .minus, exponent: tonValue.exponent, significand: tonValue.significand))
to = transaction.dest ?? ""
self.sentToSelf = sentToSelf
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import MarketKit
import TonKitKmm

class TonTransactionRecord: TransactionRecord {
let fee: TransactionValue
let fee: TransactionValue?

init(source: TransactionSource, transaction: TonTransaction, feeToken: Token) {
fee = .coinValue(token: feeToken, value: 0.00001) // todo
fee = transaction.fee.map { .coinValue(token: feeToken, value: TonAdapter.amount(kitAmount: $0)) }

super.init(
source: source,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ class ManageAccountViewModel {
switch account.type {
case .mnemonic: keyActions.append(contentsOf: [.recoveryPhrase, .privateKeys, .publicKeys])
case .evmPrivateKey: keyActions.append(contentsOf: [.privateKeys, .publicKeys])
case .evmAddress, .tronAddress: ()
case .evmAddress, .tronAddress, .tonAddress: ()
case let .hdExtendedKey(key):
switch key {
case .private: keyActions.append(contentsOf: [.privateKeys, .publicKeys])
Expand Down
Loading