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:
- 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 beprivate
, but let's leave that for now. - 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. - A plugin, which likely connects the feature to the greater application lifecycle. This is a common pattern in Exodus features.
- The feature definition does NOT export an 'api' node. This means there's no
exodus.topMoversMonitor.xyz
API. - 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 theport
. When you callexodus.subscribe(({ type, payload }) => {})
, you're getting events emitted to theport
. - 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.