The state of the user's wallet, consisting of their master seed, along with any accounts derived from that seed, should be stored locally in a safe manner. As this requires the use of
localStorage, all data should be encrypted.
Presently, this challenge is being addressed by using the user's password (specified when creating their master seed) to encrypt/decrypt the mnemonic seed, as well as unlocking the state of their wallet. The accounts in the state are being persisted via redux-persist, with an ecryption transform that handles the encrypting and decrypting of all data stored in
The mnemonic is stored separately from the accounts data. In
anoma-apps/packages/anoma-lib/lib/types/mnemonic.rs implementation of
Mnemonic, we provide the ability to specify a password allowing us to retrieve a storage value of the mnemonic, which is encrypted before saving to
localStorage. When the wallet is locked, the user must provide a password, which is validated by attempting to decrypt the stored mnemonic. If successful, the password is used to either generate an encrypted Redux persistence layer, or decrypt the existing one, restoring the user's wallet state.
redux-persist gives us the ability to specify which sub-sections of the state should be persisted. Presently, this is only enabled for any derived account data. From the persisted store, we can establish a
persistor, which can be passed into a
PersistGate component that will only display its children once the state is retrieved and decrypted from storage.
If we wanted to export the state of the user's accounts, this would be trivial, and simply a matter of exporting a JSON file containing the
JSON.stringifyed version of their accounts state. Some work would need to be done in order to restore the data into Redux, however.
localStorage state is stored in one of three places, depending on your environment:
persist:anoma-wallet-local- Local ledger
This allows us to keep our wallet state in sync with multiple ledgers while testing.
The user should have the ability to save the state of their accounts in their wallet to a JSON file. It is relatively trivial to take a snapshot of the accounts state once the user is authenticated.
Technically, this will likely involve a process by which, following the upload of the file and successful parsing, the existing
persist:anoma-wallet storage is cleared, and when the store is initialized, we pass the parsed accounts state in to
configureStore by way of the
preloadedState parameter. This will only happen once, and on subsequent calls to the
makeStore function, it should hydrate from the encrypted value in local storage.
Refer to the following to see how our present
makeStore Redux store factory functions:
This method currently accepts a
secretKey as required by the
encryptTransform, and checks the environment variables
NODE_ENV to determine where the store gets saved in
localStorage. This is mostly useful for local testing where you may want to switch between connecting to a local ledger or a testnet, and want to keep your local stores in sync with both.
As a secret is required to unlock the persisted store, this store must be instantiated dynamically once a password is entered and validated. In the current implementation of the wallet, any routes that will make use of the Redux store are loaded asynchronously. When they are loaded, the store is initialized with the user's password (which is passed in through the Context API in React, separate from the Redux state).