Skip to content

LLC:CurrencyBridge

KVNLS edited this page Jul 17, 2023 · 4 revisions

CurrencyBridge: scan accounts with a device

Prerequisite: Currency.

CurrencyBridge offers a generic abstraction (for all crypto currencies) to add accounts with a Ledger device.

types

export interface CurrencyBridge {
  scanAccounts({
    currency: CryptoCurrency,
    deviceId: string,
    syncConfig: SyncConfig,
    scheme?: ?DerivationMode,
  }): Observable<ScanAccountEvent>;
  preload(currency: CryptoCurrency): Promise<Object>;
  hydrate(data: mixed, currency: CryptoCurrency): void;
  getPreloadStrategy?: (currency: CryptoCurrency) => PreloadStrategy;
}

getCurrencyBridge

getCurrencyBridge is the entrypoint to get a bridge instance for a given currency.

import { getCurrencyBridge } from "@ledgerhq/live-common/lib/bridge";

const bridge = getCurrencyBridge(cryptoCurrency);

Finding accounts with scanAccounts

Scans all available accounts with a Ledger device.

scanAccounts({
  currency: CryptoCurrency,
  deviceId: DeviceId,
  scheme?: ?DerivationMode,
  syncConfig: SyncConfig
}): Observable<ScanAccountEvent>;

This technically will derivate addresses/xpubs with the device and iterate on paths while there are accounts. To offer a simple experience for users, it unifies all possible derivation scheme and will emit accounts found in different contexts (for instance legacy, segwit, native segwit, derivation paths of MyEtherWallet,...)

  • currency: CryptoCurrency. The crypto currency to start accounts on.
  • deviceId: string. identify a device to use for scanning accounts.
  • syncConfig: SyncConfig. Configure user specifics like pagination,...
  • scheme: ?DerivationMode. Optionally filter a specific mode to filter.

It emits an observable of ScanAccountEvent, which at the moment is only one event:

export type ScanAccountEvent = {
  type: "discovered",
  account: Account,
};

The observable can be unsubscribed at any time.

preload, hydrate, getPreloadStrategy

Preload some currency data (e.g. tokens, delegators,...) required for live-common feature to correctly work.

preload(currency: CryptoCurrency): Promise<Object>;

returns a promise of a serializable object (aka can be JSON.stringifyed). It can fail if data was not able to load.

hydrate(data: mixed, currency: CryptoCurrency): void;

takes in parameter the same data that is returned by preload() (serialized form) and allows to reinject that preloaded data (typically coming from a cached) in a way that a preload() would be a noop that instantly resolved if the data was not "invalidated". The data coming in parameter however must be treated as unsafe (that's why it's mixed typed). Implementations must validate all fields and be backward compatible.

N.B. Both preload() and hydrate() implementation are expected to have side effects in live-common (with caches). That technically makes live-common a non-pure library.

In live-common lifecycle, Preload/Hydrate must have been resolved before doing scan accounts or using AccountBridge of the associated currency.

This allows to implement cache system and typically make Ledger Live resilient to network being down. The fact it's guaranteed that preload() was done as soon as accounts were loaded makes it possible to have data cached and hydrated without network.

getPreloadStrategy allows to override the way the preloaded data should be handled and kept in memory. It is not mandatory to be defined. It is an object that contains:

  • preloadMaxAge (default will be 5 minutes). 0 would disable any cache.

Behavior expectations

With ledger-live-common >=18.0.0 you can expect that:

  • if a cache exists on the user's side, it will first be used to hydrate() at boot. with the data that was preload()-ed in the past.
  • preload() will always get called independently of the cache/hydrate() status. It's up to bridge implementation to "do nothing" if there is nothing to preload.
  • hydrate() is always getting called after a preload()
  • that preload() will always be called before synchronisations and scanAccounts call.
  • if preload() or hydrate() can throw an exception and this is understood as a critical situation that makes any further action (sync, scan account) to fail (with the error returned to user). For instance, network is down.

Serialized usage

TO BE DOCUMENTED. usage in Ledger Live Desktop in context of serialization.

type ScanAccountEventRaw = {
  type: "discovered",
  account: AccountRaw,
};
Clone this wiki locally