LedgerService

public class LedgerService : NSObject, CBPeripheralDelegate, CBCentralManagerDelegate

A 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.

Types / Constants

  • Instead of returning data, sometimes ledger returns a code to indicate that so far the message have been received successfully

    Declaration

    Swift

    public static let successCode: String
  • General Ledger error codes, pulled from the source, and some additional ones added for native swift issues

    See more

    Declaration

    Swift

    public enum GeneralErrorCodes : String, Error, Codable
  • Dedicated error codes pulled from the Ledger tezos app

    See more

    Declaration

    Swift

    public enum TezosAppErrorCodes : String, Error, Codable

Properties

  • 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

    Declaration

    Swift

    @Published
    public var partialSuccessMessageReceived: Bool { get set }
  • Public shared instace to avoid having multiple copies of the underlying JSContext created

    Declaration

    Swift

    public static let shared: LedgerService

Public functions

  • Start listening for ledger devices

    Declaration

    Swift

    public func listenForDevices() -> AnyPublisher<[String : String], KukaiError>

    Return Value

    Publisher with a dictionary of [UUID: deviceName] or an KukaiError

  • Stop listening for and reporting new ledger devices found

    Declaration

    Swift

    public func stopListening()
  • Connect to a ledger device by a given UUID

    Declaration

    Swift

    public func connectTo(uuid: String) -> AnyPublisher<Bool, KukaiError>

    Return Value

    Publisher which will indicate true / false, or return an KukaiError if it can’t connect to bluetooth

  • Disconnect from the current Ledger device

    Declaration

    Swift

    public func disconnectFromDevice()

    Return Value

    A Publisher with a boolean, or KukaiError if soemthing goes wrong

  • Get the UUID of the connected device

    Declaration

    Swift

    public func getConnectedDeviceUUID() -> String?

    Return Value

    a string if it can be found

  • Get a TZ address and public key from the current connected Ledger device

    Declaration

    Swift

    public func getAddress(forDerivationPath derivationPath: String = HD.defaultDerivationPath, curve: EllipticalCurve = .ed25519, verify: Bool) -> AnyPublisher<(address: String, publicKey: String), KukaiError>

    Parameters

    forDerivationPath

    Optional. The derivation path to use to extract the address from the underlying HD wallet

    curve

    Optional. The EllipticalCurve to use to extract the address

    verify

    Whether 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 Value

    A publisher which will return a tuple containing the address and publicKey, or an KukaiError

  • Get a TZ address and public key from the current connected Ledger device

    Declaration

    Swift

    public func getAddress(forDerivationPath derivationPath: String = HD.defaultDerivationPath, curve: EllipticalCurve = .ed25519, verify: Bool) async -> Result<(address: String, publicKey: String), KukaiError>

    Parameters

    forDerivationPath

    Optional. The derivation path to use to extract the address from the underlying HD wallet

    curve

    Optional. The EllipticalCurve to use to extract the address

    verify

    Whether 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 Value

    An async Result object, allowing code to be triggered via while loops more easily

  • Sign an operation payload with the underlying secret key, returning the signature

    Declaration

    Swift

    public func sign(hex: String, forDerivationPath derivationPath: String = HD.defaultDerivationPath, parse: Bool) -> AnyPublisher<String, KukaiError>

    Parameters

    hex

    An 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)

    forDerivationPath

    Optional. The derivation path to use to extract the address from the underlying HD wallet

    parse

    Ledger 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 Value

    A Publisher which will return a string containing the hex signature, or an KukaiError

Bluetooth

  • CBCentralManagerDelegate function, must be marked public because of protocol definition

    Declaration

    Swift

    public func centralManagerDidUpdateState(_ central: CBCentralManager)
  • CBCentralManagerDelegate function, must be marked public because of protocol definition

    Declaration

    Swift

    public func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber)
  • CBCentralManagerDelegate function, must be marked public because of protocol definition

    Declaration

    Swift

    public func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral)
  • CBCentralManagerDelegate function, must be marked public because of protocol definition

    Declaration

    Swift

    public func centralManager(_ central: CBCentralManager, didFailToConnect peripheral: CBPeripheral, error: Error?)
  • CBCentralManagerDelegate function, must be marked public because of protocol definition

    Declaration

    Swift

    public func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?)
  • Declaration

    Swift

    public func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: (any Error)?)
  • Declaration

    Swift

    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

    Declaration

    Swift

    public func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?)
  • CBCentralManagerDelegate function, must be marked public because of protocol definition

    Declaration

    Swift

    public func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?)
  • CBCentralManagerDelegate function, must be marked public because of protocol definition

    Declaration

    Swift

    public func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?)