Skip to main content

How features work and plug into the SDK

Let's dissect a simple feature in the Exodus SDK to understand how it works: the top-movers-monitor. This feature tracks assets that experienced the biggest gain or loss in value within a given time period.

Let's start at the top level, the feature definition. This defines all the pieces of the feature. At the time of writing, this feature exports:

  1. A public topMoversAtom. You can tell it's public because it has a public: true field in its definition. This means this atom is available as a dependency to all other features. Perhaps it should be private, but let's leave that for now.
  2. A public module that tracks top movers locally or remotely, depending on how the feature is instantiated. By default it computes it locally. Whichever module is used, it receives a config of { minChangePercent, fetchInterval }. The latter is most likely only relevant to the remote tracker.
  3. A plugin, which likely connects the feature to the greater application lifecycle. This is a common pattern in Exodus features.
  4. The feature definition does NOT export an 'api' node. This means there's no exodus.topMoversMonitor.xyz API.
  5. There's a redux module and one custom selector.

Let's now zoom into each component of the feature:

topMoversAtom

This atom is very simple. It's an in-memory atom that holds values of { gainers: [], losers: [] }. Making schemas for atom values more explicit is on our TODO list, but for now you can take a look at the data that the writer writes to the atom.

topMoversMonitor: local

The "local" topMoversMonitor is a small module that looks at data coming from three other atoms (ratesAtom, currencyAtom, availableAssetNamesAtom), exposes a start/stop pair of methods to observe/unobserve that data, and then a bunch of business logic to compute the top movers from those inputs. After that, it writes the result to the topMoversAtom

topMoversMonitor: remote

The "remote" topMoversMonitor is also very simple: it exposes a start/stop pair of methods to poll an API, has a bunch of business logic to compute the top movers from the response, and as the "local" one, writes the result to the topMoversAtom

topMoversLifecyclePlugin

This is a simple plugin that implements hooks to the greater application lifecycle:

  • onUnlock: when the user unlocks the application, it starts the topMoversMonitor, conditional on the topMovers feature flag being enabled. Note: most features aren't gated by feature flags.
  • onStart/onLoad: these make sure that data coming out of topMoversAtom gets reemitted to the port. When you call exodus.subscribe(({ type, payload }) => {}), you're getting events emitted to the port.
  • onStop: this supports a graceful shutdown of the feature when the application is stopped, by unobserving the topMoversAtom.

Redux module

Remember the event emitted in the topMoversLifecyclePlugin? To ingest the data from that event into redux, we declare a reducer for that event in the feature's redux module definition.

Looking at the redux module's id, we know that selectors will become available at selectors.topMovers.xyz, and based on the initialState, two selectors will be auto-generated for us: selectors.topMovers.loaded and selectors.topMovers.data. There's also a custom selector that filters down the gainers/losers by removed tokens, if those are available at runtime.