SDK Initialization
The Fireblocks Non-Custodial Wallet (NCW) SDK should be initialized only after Device ID value creation and assignment to a user.
Generate Device ID
The Device ID is a logical identifier that represents an entity that stores a user key share and can participate in MPC operations for a given wallet. It is a unique identifier per SDK instance.
Note
NCW SDK exposes a static method to generate a random
deviceId
-fireblocksNCW.generateDeviceId()
The code below will be changed to use this method in the near future.
const DEVICE_ID_KEY = "DEMO_APP:deviceId";
export const generateDeviceId = () => crypto.randomUUID();
export const getDeviceId = () => {
return localStorage.getItem(DEVICE_ID_KEY);
};
export const getOrCreateDeviceId = () => {
const deviceId = getDeviceId();
if (deviceId) {
return deviceId;
}
const uuid = generateDeviceId();
setDeviceId(uuid);
return uuid;
};
export const setDeviceId = (deviceId: string) => {
localStorage.setItem(DEVICE_ID_KEY, deviceId);
};
SDK Initialization
When initializing the SDK it is required to pass the deviceId, storage provider, message handler, and optionally the event handler. In this section, we are going to cover how each was implemented in our demo application. The full code is in: /src/AppStore.ts
Outgoing Message Handler
MessageHandler is the implementation of the outgoing message handling object for the asynchronous MPC messaging flow. For more information, please visit Outgoing/Incoming Message Handling guide:
...
const messagesHandler: IMessagesHandler = {
handleOutgoingMessage: (message: string) => {
if (!apiService) {
throw new Error("apiService is not initialized");
}
return apiService.sendMessage(deviceId, message);
},
};
...
Incoming Message Handler
Incoming messages change
Beginning with [email protected], incoming messages and the required implementation to fetch them continuously will no longer be required. This process will be facilitated by the SDKs.
In order to see an example of handling incoming messages on mobile, check out these open-source demo Fireblocks published:
Storage Provider
The storage provider is being called by the NCW SDK to access the MPC keys stored on the end user's device.
In this demo application, we have implemented password-protected storage that extends the built-in BrowserLocalStorageProvider and encrypts the key share and stores in the local storage of the browser:
import { md } from "node-forge";
import {
ISecureStorageProvider,
BrowserLocalStorageProvider,
decryptAesGCM,
encryptAesGCM,
} from "@fireblocks/ncw-js-sdk";
export type GetUserPasswordFunc = () => Promise<string>;
/// This secure storage implementations creates an encryption key on-demand based on a user password
export class PasswordEncryptedLocalStorage extends BrowserLocalStorageProvider implements ISecureStorageProvider {
private encKey: string | null = null;
constructor(
private deviceId: string,
private getPassword: GetUserPasswordFunc,
) {
super(`secure-${deviceId}`);
}
public async unlock(): Promise<void> {
this.encKey = await this._generateEncryptionKey();
}
public async lock(): Promise<void> {
this.encKey = null;
}
public async get(key: string): Promise<string | null> {
if (!this.encKey) {
throw new Error("Storage locked");
}
const encryptedData = await super.get(key);
if (!encryptedData) {
return null;
}
return decryptAesGCM(encryptedData, this.encKey, this.deviceId);
}
public async set(key: string, data: string): Promise<void> {
if (!this.encKey) {
throw new Error("Storage locked");
}
const encryptedData = await encryptAesGCM(data, this.encKey, this.deviceId);
await super.set(key, encryptedData);
}
private async _generateEncryptionKey(): Promise<string> {
let key = await this.getPassword();
const md5 = md.md5.create();
for (let i = 0; i < 1000; ++i) {
md5.update(key);
key = md5.digest().toHex();
}
return key;
}
}
This secure storage provider is being passed to the NCW SDK initialization call:
...
const secureStorageProvider = new PasswordEncryptedLocalStorage(deviceId, () => {
const password = prompt("Enter password", "");
return Promise.resolve(password || "");
});
...
Event Handler
The event handler is optional. We have implemented it in our demo app to get the MPC Key generation status in order to present it to the end user:
...
const eventsHandler: IEventsHandler = {
handleEvent: (event: TEvent) => {
switch (event.type) {
case "key_descriptor_changed":
const keysStatus: Record<TMPCAlgorithm, IKeyDescriptor> =
get().keysStatus ?? ({} as Record<TMPCAlgorithm, IKeyDescriptor>);
keysStatus[event.keyDescriptor.algorithm] = event.keyDescriptor;
set((state) => ({ ...state, keysStatus }));
break;
}
},
};
...
Initialize SDK
And finally, let's initialize our NCW SDK instance with all the required values:
...
const fireblocksNCW = await FireblocksNCW.initialize({
env: ENV_CONFIG.NCW_SDK_ENV,
deviceId,
messagesHandler,
eventsHandler,
secureStorageProvider,
logger: new ConsoleLogger(),
});
...
Note that we are passing here a few additional values:
- Optional:
env
- the Fireblocks environment that we are working on. Default issandbox
. - Optional:
logger
- your implementation of a logging mechanism.
Updated 10 months ago