LedgerService
public class LedgerService : NSObject, CBPeripheralDelegate, CBCentralManagerDelegateA service class to wrap up all the complicated interactions with CoreBluetooth and the modified version of ledgerjs, needed to communicate with a Ledger Nano X.
Ledger only provide a ReactNative module for third parties to integrate with. The architecture of the module also makes it very difficult to integrate with native mobile (if it can be packaged up) as it relies heavily on long observable chains passing through many classes and functions. To overcome this, I copied the base logic from multiple ledgerjs classes into a single typescript file and split the functions up into more of a utility style class, where each function returns a result, that must be passed into another function. This allowed the creation of a swift class to sit in the middle of these functions and decide what to do with the responses.
The modified typescript can be found in this file (under a fork of the main repo) https://github.com/simonmcl/ledgerjs/blob/native-mobile/packages/hw-app-tezos/src/NativeMobileTezos.ts . The containing package also includes a webpack file, which will package up the typescript and its dependencies into mobile friendly JS file, which needs to be included in the swift project. Usage of the JS can be seen below.
NOTE: this modified typescript is Tezos only as I was unable to find a way to simply subclass their Transport class, to produce a re-usable
NativeMobile transport. The changes required modifiying the app and other class logic which became impossible to refactor back into the project, without rewriting everything.
- 
                  
                  Instead of returning data, sometimes ledger returns a code to indicate that so far the message have been received successfully DeclarationSwift public static let successCode: String
- 
                  
                  General Ledger error codes, pulled from the source, and some additional ones added for native swift issues See moreDeclarationSwift public enum GeneralErrorCodes : String, Error, Codable
- 
                  
                  Dedicated error codes pulled from the Ledger tezos app See moreDeclarationSwift public enum TezosAppErrorCodes : String, Error, Codable
- 
                  
                  Be notified when the ledger device returns a success message, part way through the process. This can be useful to indicate to users that the request has succeed, but s waiting on input on the Ledger device to continue DeclarationSwift @Published public var partialSuccessMessageReceived: Bool { get set }
- 
                  
                  Public shared instace to avoid having multiple copies of the underlying JSContextcreatedDeclarationSwift public static let shared: LedgerService
- 
                  
                  Start listening for ledger devices DeclarationSwift public func listenForDevices() -> AnyPublisher<[String : String], KukaiError>Return ValuePublisher with a dictionary of [UUID: deviceName]or anKukaiError
- 
                  
                  Stop listening for and reporting new ledger devices found DeclarationSwift public func stopListening()
- 
                  
                  Connect to a ledger device by a given UUID DeclarationSwift public func connectTo(uuid: String) -> AnyPublisher<Bool, KukaiError>Return ValuePublisher which will indicate true / false, or return an KukaiErrorif it can’t connect to bluetooth
- 
                  
                  Disconnect from the current Ledger device DeclarationSwift public func disconnectFromDevice()Return ValueA Publisher with a boolean, or KukaiErrorif soemthing goes wrong
- 
                  
                  Get the UUID of the connected device DeclarationSwift public func getConnectedDeviceUUID() -> String?Return Valuea string if it can be found 
- 
                  
                  Get a TZ address and public key from the current connected Ledger device DeclarationSwift public func getAddress(forDerivationPath derivationPath: String = HD.defaultDerivationPath, curve: EllipticalCurve = .ed25519, verify: Bool) -> AnyPublisher<(address: String, publicKey: String), KukaiError>ParametersforDerivationPathOptional. The derivation path to use to extract the address from the underlying HD wallet curveOptional. The EllipticalCurveto use to extract the addressverifyWhether or not to ask the ledger device to prompt the user to show them what the TZ address should be, to ensure the mobile matches Return ValueA publisher which will return a tuple containing the address and publicKey, or an KukaiError
- 
                  getAddress(forDerivationPath:Asynchronouscurve: verify: ) Get a TZ address and public key from the current connected Ledger device DeclarationSwift public func getAddress(forDerivationPath derivationPath: String = HD.defaultDerivationPath, curve: EllipticalCurve = .ed25519, verify: Bool) async -> Result<(address: String, publicKey: String), KukaiError>ParametersforDerivationPathOptional. The derivation path to use to extract the address from the underlying HD wallet curveOptional. The EllipticalCurveto use to extract the addressverifyWhether or not to ask the ledger device to prompt the user to show them what the TZ address should be, to ensure the mobile matches Return ValueAn async Resultobject, allowing code to be triggered via while loops more easily
- 
                  
                  Sign an operation payload with the underlying secret key, returning the signature DeclarationSwift public func sign(hex: String, forDerivationPath derivationPath: String = HD.defaultDerivationPath, parse: Bool) -> AnyPublisher<String, KukaiError>ParametershexAn operation converted to JSON, forged and watermarked, converted to a hex string. (Note: there are some issues with the ledger app signing batch transactions. May simply return no result at all. Can’t run REVEAL and TRANSACTION together for example) forDerivationPathOptional. The derivation path to use to extract the address from the underlying HD wallet parseLedger can parse non-hashed (blake2b) hex data and display operation data to user (e.g. transfer 1 XTZ to TZ1abc, for fee: 0.001). There are many limitations around what can be parsed. Frequnetly it will require passing in false Return ValueA Publisher which will return a string containing the hex signature, or an KukaiError
- 
                  
                  CBCentralManagerDelegate function, must be marked public because of protocol definition DeclarationSwift public func centralManagerDidUpdateState(_ central: CBCentralManager)
- 
                  
                  CBCentralManagerDelegate function, must be marked public because of protocol definition DeclarationSwift public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
- 
                  
                  CBCentralManagerDelegate function, must be marked public because of protocol definition DeclarationSwift public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
- 
                  
                  CBCentralManagerDelegate function, must be marked public because of protocol definition DeclarationSwift public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?)
- 
                  
                  CBCentralManagerDelegate function, must be marked public because of protocol definition DeclarationSwift public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)
- 
                  
                  DeclarationSwift public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: (any Error)?)
- 
                  
                  DeclarationSwift public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, timestamp: CFAbsoluteTime, isReconnecting: Bool, error: (any Error)?)
- 
                  
                  CBCentralManagerDelegate function, must be marked public because of protocol definition DeclarationSwift public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
- 
                  
                  CBCentralManagerDelegate function, must be marked public because of protocol definition DeclarationSwift public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?)
- 
                  
                  CBCentralManagerDelegate function, must be marked public because of protocol definition DeclarationSwift public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)
 Install in Dash
Install in Dash