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: truefield 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.xyzAPI. - 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
topMoversAtomgets 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.